@absolutejs/manifest

中文文档
0.10.0发布于 2026/08/23MITTypeScriptES Module零依赖近 30 天下载 2.4万

The AbsoluteJS package manifest contract. Every @absolutejs/* package exports a typed manifest (settings schema, env requirements, adapter slots, wiring recipes, AI tools) from its ./manifest subpath; this package is the contract those manifests are writt

原始使用文档
文档目录7 节
  1. Agent action authorization (contract 2)
  2. No-code integration role
  3. Why TypeBox
  4. Authoring a manifest
  5. Package plumbing
  6. Consuming manifests
  7. Adapter slots

@absolutejs/manifest

Version 0.4 makes remote tools secure by construction. Contract-2 manifests declare enough policy metadata for a host or no-code control plane to explain, approve, lease, execute, audit, and where possible compensate every action. Contract-1 manifests still load for catalog and upgrade tooling, but their tools are deliberately omitted from AI and MCP bridges.

Agent action authorization (contract 2)

Contract 2 adds semantic tool effects and enforcement requirements. These are policy inputs, not model hints:

send_email: tool.runtime({
  authorization: {
    approval: "policy",
    audience: "owner",
    destinationFields: ["to"],
    effects: ["send", "external-network"],
    idempotency: { mode: "host" },
    requiredScopes: ["email:send"],
    reversible: false,
  },
  // input, handler, description…
});

Bridges fail closed: tools are omitted unless the manifest is contract 2, the tool has valid authorization metadata, its runtime or workspace capabilities are available, and the host supplies ToolBindings.enforce. The enforcer gets deep-frozen, validated, defaulted arguments, their canonical SHA-256 digest, the package/tool identity, and a single-use execution closure. The closure expires when enforcement returns, closing the approve-now/execute-different- input gap. A host can wrap approval, authorization, idempotency leasing, execution, and receipt recording around that exact closure.

inspectManifestSecurity returns per-tool posture and actionable issue codes for catalogs and no-code UIs. It checks scopes, public exposure, destinations, resource and spend bindings, idempotency, reversibility/compensation, destructive hints, read-only hints, and every referenced input field.

The AbsoluteJS package manifest contract. Every @absolutejs/* package exports a typed manifest from its ./manifest subpath describing what the package is, what it needs, how it wires into an app, and what AI tools it offers. This package is the contract those manifests are written against — plus the bridges that turn any manifest into an AI tool map (@absolutejs/ai) or a remote MCP tool registry (@absolutejs/mcp), so anyone can build AI tooling or MCP servers on top of the ecosystem's manifests, not just AbsoluteJS's own products.

MIT licensed. Sole peer dependency: @sinclair/typebox.

No-code integration role

Packages explicitly describe how a no-code host may integrate them:

  • integration: { mode: "recipe" } owns executable wiring and must publish at least one wiring recipe.
  • integration: { mode: "adapter" } fills a named ecosystem slot and must publish at least one implements entry.
  • integration: { mode: "code-first", description: "…" } remains searchable and explainable, but the host must not pretend it can automatically assemble production-specific stores, policies, signers, callbacks, or identities.

Older manifests without this field remain valid for compatibility. Consumers may retain their historical inference for those packages, but current manifests should declare the role explicitly.

Why TypeBox

A TypeBox schema is simultaneously a TypeScript type (via Static<>) and a plain JSON Schema object at runtime. That makes it the single source of truth for a tool's input: the same schema types the handler at compile time and is handed verbatim to AI providers (input_schema) and MCP (inputSchema). No hand-written JSON Schema, no drift.

Authoring a manifest

// src/manifest.ts of @absolutejs/dispatch
import { Type } from "@sinclair/typebox";
import { defineManifest, toolFactory } from "@absolutejs/manifest";
import type { Dispatcher, DispatcherOptions } from "./types";

const tool = toolFactory<Dispatcher>();

export const manifest = defineManifest<DispatcherOptions, Dispatcher>()({
  contract: 2,
  identity: {
    category: "messaging",
    name: "@absolutejs/dispatch",
    tagline: "Send email, texts, and push notifications from your site.",
  },
  settings: Type.Object({
    defaultFrom: Type.Optional(
      Type.Object(
        { email: Type.Optional(Type.String({ format: "email" })) },
        { title: "Default sender" },
      ),
    ),
  }),
  slots: {
    email: {
      configPath: "email",
      contract: "dispatch/email-adapter",
      description: "Email transport",
      known: ["@absolutejs/dispatch-resend", "@absolutejs/dispatch-postmark"],
    },
  },
  tools: {
    send_email: tool.runtime({
      annotations: { openWorldHint: true },
      authorization: {
        approval: "policy",
        audience: "owner",
        destinationFields: ["to"],
        effects: ["send", "external-network"],
        idempotency: { mode: "host" },
        requiredScopes: ["email:send"],
        reversible: false,
      },
      description: "Send a transactional email through the configured adapter.",
      handler: async (input, dispatcher) => {
        const result = await dispatcher.email(input);

        return `sent via ${result.provider}`;
      },
      input: Type.Object({
        subject: Type.String(),
        text: Type.String(),
        to: Type.String({ format: "email" }),
      }),
    }),
  },
  wiring: [
    {
      id: "default",
      server: {
        code: "const dispatcher = createDispatcher({ email: ${slot.email}, ...${settings} });",
        imports: [
          { from: "@absolutejs/dispatch", names: ["createDispatcher"] },
        ],
        placement: "module-scope",
      },
      title: "Create the dispatcher",
    },
  ],
});

defineManifest<TConfig, TRuntime>() is the drift-breaker: the settings schema is checked against the package's real exported config type. Rename a config key without updating the manifest and the package's own tsc fails at this module. Type safety survives upgrades because it is enforced where the types live.

Server wiring uses placement to preserve lifecycle semantics: server-boundary is mounted before the first route (error capture, request context, and other hooks that must observe every handler); server-plugin joins the normal plugin chain; module-scope creates top-level resources; and server-factory is reserved for host factory composition. Client recipes use client-entry.

Package plumbing

// package.json
{
  "absolutejs": {
    "manifestContract": 2,
  },
  "exports": {
    "./manifest": {
      "types": "./dist/manifest.d.ts",
      "import": "./dist/manifest.js",
    },
    "./manifest.json": "./dist/manifest.json",
  },
  "scripts": {
    "build": "… && absolute-manifest emit",
  },
}

absolute-manifest emit validates the manifest (schema, tool-key naming, preset values, package.json agreement, and peer dependency relationships) and writes dist/manifest.json — the serializable projection (handlers stripped) for consumers that can't execute package code. It is derived, never hand-authored, so the two forms cannot diverge. absolute-manifest scaffold generates a starter src/manifest.ts.

Standard package metadata is the only dependency contract. Put host-owned runtimes in peerDependencies, optionality in peerDependenciesMeta, and the version used by tests in devDependencies. Configure externals in the package's actual build command or build API. No parallel AbsoluteJS dependency map is required.

absolute-manifest verify-package checks derivable peer relationships, such as rejecting a peer duplicated in dependencies. absolute-manifest verify-tree [directory] recursively applies the same check while excluding generated and dependency directories.

Consuming manifests

import { loadManifest, toAIToolMap, toMcpToolRegistry } from '@absolutejs/manifest';

const result = await loadManifest('@absolutejs/dispatch');
if (!result.ok) throw new Error(result.details);

// AI tool loop (@absolutejs/ai)
const enforce = async (request, execute) => {
    await agency.authorizeAndLease(request);
    const toolResult = await execute();
    await agency.recordReceipt(request, toolResult);

    return toolResult;
};
const tools = toAIToolMap(result.manifest, { enforce, runtime: dispatcher });
streamAIToSSE({ tools, ... });

// Remote MCP server (@absolutejs/mcp)
new Elysia().use(mcpServer({
    path: '/mcp',
    tools: () => toMcpToolRegistry(result.manifest, { enforce, runtime: dispatcher })
}));

Bridges validate and default every call before enforcement and fail closed. Contract-1 tools, unguarded tools, missing runtimes, unavailable workspace capabilities, and hosts without an enforcer never appear in the registry.

Handlers receive nothing ambient — no process.env, no host secrets. A runtime tool gets the package instance the host constructed; a workspace tool gets the host's jailed Workspace (read/glob/write/exec, each granted explicitly). Hosts running untrusted manifests should additionally compose @absolutejs/ai's hardenUntrustedTool over the bridged map.

Adapter slots

Core packages declare slots (contract: 'dispatch/email-adapter'); adapter packages declare implements entries with the same contract id. Consumers resolve them by scanning installed manifests, so publishing a new vendor adapter lights up every consumer without a core release. Reserved ids and the frozen wiring placeholder grammar live in CONTRACTS.md.

版本与发布记录

@absolutejs/manifest 共发布 17 个版本,首次发布于 2026/07/13;过去 12 个月发布了 17 个版本。

版本发布日期距今
0.10.0latest2026/08/232 个月前
0.9.02026/08/202 个月前
0.8.02026/07/273 个月前
0.7.32026/07/273 个月前
0.7.22026/07/253 个月前
0.7.12026/07/253 个月前

关于 @absolutejs/manifest 的常见问题

@absolutejs/manifest 是什么?

The AbsoluteJS package manifest contract. Every @absolutejs/* package exports a typed manifest (settings schema, env requirements, adapter slots, wiring recipes, AI tools) from its ./manifest subpath; this package is the contract those manifests are writt

如何安装 @absolutejs/manifest?

在项目目录运行 npm install @absolutejs/manifest,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:typebox(>=1.3.0 <2)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 absolute-manifest,可以用 npx absolute-manifest 直接运行。

@absolutejs/manifest 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@absolutejs/manifest 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@absolutejs/manifest 可以商用吗?

@absolutejs/manifest 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@absolutejs/manifest 的下载量有多少?

根据 npm 官方下载统计,@absolutejs/manifest 在 2026/10/05 – 2026/11/03 这 30 天内被下载 24,461 次,日均约 815 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@absolutejs/manifest 还在维护吗?

最新版本 0.10.0 发布于 2026/08/23(2 个月前);至今共发布 17 个版本,其中过去 12 个月发布了 17 个;首次发布于 2026/07/13。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。