@agentproto/cli 0.1.0-alpha.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cli.mjs +1848 -256
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +391 -41
- package/dist/index.mjs.map +1 -1
- package/dist/registry/builtins.d.ts +14 -0
- package/dist/registry/builtins.mjs +329 -0
- package/dist/registry/builtins.mjs.map +1 -0
- package/dist/registry/manifest.d.ts +88 -0
- package/dist/registry/manifest.mjs +42 -0
- package/dist/registry/manifest.mjs.map +1 -0
- package/dist/registry/plugins.d.ts +23 -0
- package/dist/registry/plugins.mjs +201 -0
- package/dist/registry/plugins.mjs.map +1 -0
- package/dist/registry/runtime.d.ts +70 -0
- package/dist/registry/runtime.mjs +52 -0
- package/dist/registry/runtime.mjs.map +1 -0
- package/dist/util/credentials.d.ts +71 -0
- package/dist/util/credentials.mjs +88 -0
- package/dist/util/credentials.mjs.map +1 -0
- package/package.json +35 -7
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { pathToFileURL } from 'url';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @agentproto/cli v0.1.0-alpha
|
|
10
|
+
* The `agentproto` binary — install / run / serve AIP-45 agent CLIs.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
var PLUGIN_MANIFEST_SCHEMA = "agentproto/plugin/v1";
|
|
14
|
+
var AdapterEntrySchema = z.object({
|
|
15
|
+
/** The `kind` string the manifest's substrate/dispatcher/etc. block uses. */
|
|
16
|
+
kind: z.string().min(1),
|
|
17
|
+
/**
|
|
18
|
+
* Path to the entry module, relative to the plugin package root.
|
|
19
|
+
* Resolved via the plugin's `package.json#main`/`exports` (i.e. you
|
|
20
|
+
* can use a subpath like `./dist/substrates.mjs` or an export name
|
|
21
|
+
* like `.` if the plugin re-exports everything from its root).
|
|
22
|
+
*/
|
|
23
|
+
entry: z.string().min(1),
|
|
24
|
+
/** Named export inside `entry` — the factory function. */
|
|
25
|
+
export: z.string().min(1),
|
|
26
|
+
/** Free-form description; shown by `agentproto plugins show`. */
|
|
27
|
+
description: z.string().optional()
|
|
28
|
+
}).loose();
|
|
29
|
+
var SubstrateEntrySchema = AdapterEntrySchema.extend({
|
|
30
|
+
/**
|
|
31
|
+
* Free-form capability tags. Not gated by the kernel yet, but
|
|
32
|
+
* surfaced by `agentproto plugins show` so users can see what the
|
|
33
|
+
* substrate claims to support (mentions, reactions, visibility, …).
|
|
34
|
+
*/
|
|
35
|
+
capabilities: z.array(z.string()).optional()
|
|
36
|
+
});
|
|
37
|
+
var PluginManifestSchema = z.object({
|
|
38
|
+
schema: z.literal(PLUGIN_MANIFEST_SCHEMA),
|
|
39
|
+
substrates: z.array(SubstrateEntrySchema).default([]),
|
|
40
|
+
dispatchers: z.array(AdapterEntrySchema).default([]),
|
|
41
|
+
executors: z.array(AdapterEntrySchema).default([]),
|
|
42
|
+
stateStores: z.array(AdapterEntrySchema).default([])
|
|
43
|
+
}).loose();
|
|
44
|
+
|
|
45
|
+
// src/registry/runtime.ts
|
|
46
|
+
var substrates = /* @__PURE__ */ new Map();
|
|
47
|
+
var dispatchers = /* @__PURE__ */ new Map();
|
|
48
|
+
var executors = /* @__PURE__ */ new Map();
|
|
49
|
+
var stateStores = /* @__PURE__ */ new Map();
|
|
50
|
+
function registerSubstrate(kind, factory) {
|
|
51
|
+
substrates.set(kind, factory);
|
|
52
|
+
}
|
|
53
|
+
function registerDispatcher(kind, factory) {
|
|
54
|
+
dispatchers.set(kind, factory);
|
|
55
|
+
}
|
|
56
|
+
function registerExecutor(kind, factory) {
|
|
57
|
+
executors.set(kind, factory);
|
|
58
|
+
}
|
|
59
|
+
function registerStateStore(kind, factory) {
|
|
60
|
+
stateStores.set(kind, factory);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/registry/manifest-loader.ts
|
|
64
|
+
async function loadPluginFromManifest(pluginId) {
|
|
65
|
+
const result = await readPluginManifest(pluginId);
|
|
66
|
+
if (!result) return null;
|
|
67
|
+
const { manifest, packageRoot } = result;
|
|
68
|
+
for (const sub of manifest.substrates) {
|
|
69
|
+
const factory = await importFactory(
|
|
70
|
+
packageRoot,
|
|
71
|
+
pluginId,
|
|
72
|
+
sub.entry,
|
|
73
|
+
sub.export
|
|
74
|
+
);
|
|
75
|
+
registerSubstrate(sub.kind, factory);
|
|
76
|
+
}
|
|
77
|
+
for (const dis of manifest.dispatchers) {
|
|
78
|
+
const factory = await importFactory(
|
|
79
|
+
packageRoot,
|
|
80
|
+
pluginId,
|
|
81
|
+
dis.entry,
|
|
82
|
+
dis.export
|
|
83
|
+
);
|
|
84
|
+
registerDispatcher(dis.kind, factory);
|
|
85
|
+
}
|
|
86
|
+
for (const exe of manifest.executors) {
|
|
87
|
+
const factory = await importFactory(
|
|
88
|
+
packageRoot,
|
|
89
|
+
pluginId,
|
|
90
|
+
exe.entry,
|
|
91
|
+
exe.export
|
|
92
|
+
);
|
|
93
|
+
registerExecutor(exe.kind, factory);
|
|
94
|
+
}
|
|
95
|
+
for (const st of manifest.stateStores) {
|
|
96
|
+
const factory = await importFactory(
|
|
97
|
+
packageRoot,
|
|
98
|
+
pluginId,
|
|
99
|
+
st.entry,
|
|
100
|
+
st.export
|
|
101
|
+
);
|
|
102
|
+
registerStateStore(st.kind, factory);
|
|
103
|
+
}
|
|
104
|
+
return manifest;
|
|
105
|
+
}
|
|
106
|
+
async function readPluginManifest(pluginId) {
|
|
107
|
+
const packageJsonPath = resolvePluginPackageJson(pluginId);
|
|
108
|
+
if (!packageJsonPath) return null;
|
|
109
|
+
const packageRoot = dirname(packageJsonPath);
|
|
110
|
+
const standalonePath = join(packageRoot, "agentproto.json");
|
|
111
|
+
const standalone = await readJsonIfExists(standalonePath);
|
|
112
|
+
if (standalone !== void 0) {
|
|
113
|
+
return {
|
|
114
|
+
manifest: PluginManifestSchema.parse(standalone),
|
|
115
|
+
packageRoot
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const pkgJson = await readJsonIfExists(packageJsonPath);
|
|
119
|
+
if (pkgJson && typeof pkgJson === "object" && pkgJson.agentproto) {
|
|
120
|
+
return {
|
|
121
|
+
manifest: PluginManifestSchema.parse(pkgJson.agentproto),
|
|
122
|
+
packageRoot
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
async function importFactory(packageRoot, pluginId, entry, exportName) {
|
|
128
|
+
const abs = entry.startsWith(".") ? join(packageRoot, entry) : entry;
|
|
129
|
+
const url = entry.startsWith(".") ? pathToFileURL(abs).href : entry;
|
|
130
|
+
const mod = await import(url);
|
|
131
|
+
const exported = mod[exportName];
|
|
132
|
+
if (typeof exported !== "function") {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`agentproto plugin '${pluginId}': manifest entry '${entry}' has no callable export '${exportName}' (got ${typeof exported}).`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return exported;
|
|
138
|
+
}
|
|
139
|
+
function resolvePluginPackageJson(pluginId) {
|
|
140
|
+
const candidates = [
|
|
141
|
+
import.meta.url,
|
|
142
|
+
pathToFileURL(join(process.cwd(), "package.json")).href
|
|
143
|
+
];
|
|
144
|
+
for (const root of candidates) {
|
|
145
|
+
try {
|
|
146
|
+
return createRequire(root).resolve(`${pluginId}/package.json`);
|
|
147
|
+
} catch {
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
async function readJsonIfExists(path) {
|
|
153
|
+
try {
|
|
154
|
+
const raw = await readFile(path, "utf8");
|
|
155
|
+
return JSON.parse(raw);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
if (err.code === "ENOENT") return void 0;
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/registry/plugins.ts
|
|
163
|
+
async function loadPluginsFromConfig() {
|
|
164
|
+
const path = configPath();
|
|
165
|
+
try {
|
|
166
|
+
const raw = await readFile(path, "utf8");
|
|
167
|
+
const parsed = JSON.parse(raw);
|
|
168
|
+
return parsed.plugins ?? [];
|
|
169
|
+
} catch (err) {
|
|
170
|
+
if (err.code === "ENOENT") return [];
|
|
171
|
+
process.stderr.write(
|
|
172
|
+
`agentproto: failed to read ${path} \u2014 ${err instanceof Error ? err.message : String(err)}
|
|
173
|
+
`
|
|
174
|
+
);
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function loadPlugins(moduleIds) {
|
|
179
|
+
for (const id of moduleIds) {
|
|
180
|
+
try {
|
|
181
|
+
const manifest = await loadPluginFromManifest(id);
|
|
182
|
+
if (manifest === null) {
|
|
183
|
+
await import(id);
|
|
184
|
+
}
|
|
185
|
+
} catch (err) {
|
|
186
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
187
|
+
process.stderr.write(
|
|
188
|
+
`agentproto: plugin '${id}' failed to load \u2014 ${msg}
|
|
189
|
+
`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function configPath() {
|
|
195
|
+
const base = process.env["AGENTPROTO_HOME"] ?? join(homedir(), ".agentproto");
|
|
196
|
+
return join(base, "config.json");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export { loadPlugins, loadPluginsFromConfig };
|
|
200
|
+
//# sourceMappingURL=plugins.mjs.map
|
|
201
|
+
//# sourceMappingURL=plugins.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/registry/manifest.ts","../../src/registry/runtime.ts","../../src/registry/manifest-loader.ts","../../src/registry/plugins.ts"],"names":["readFile","join"],"mappings":"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","file":"plugins.mjs","sourcesContent":["/**\n * Plugin manifest — `agentproto/plugin/v1`.\n *\n * Plugins declare what they provide in either:\n * - their `package.json` under the `agentproto` key, OR\n * - a standalone `agentproto.json` next to their `package.json`.\n *\n * The CLI reads this manifest, dynamic-imports each adapter entry,\n * and registers it with the runtime registry. Plugins don't need\n * side-effect imports any more — they just export their factory\n * functions and let the manifest do the wiring.\n *\n * Example (in @guilde/agentproto-bridge's package.json):\n *\n * {\n * \"name\": \"@guilde/agentproto-bridge\",\n * \"agentproto\": {\n * \"schema\": \"agentproto/plugin/v1\",\n * \"substrates\": [\n * {\n * \"kind\": \"guilde-mcp\",\n * \"entry\": \"./dist/index.mjs\",\n * \"export\": \"guildeMcpSubstrateFactory\",\n * \"capabilities\": [\"mentions\", \"reactions\", \"identity\"],\n * \"description\": \"Reads/writes turns through Guilde's MCP server.\"\n * }\n * ],\n * \"executors\": [\n * {\n * \"kind\": \"db-operator\",\n * \"entry\": \"./dist/index.mjs\",\n * \"export\": \"dbOperatorExecutorFactory\",\n * \"description\": \"Delegates to Mastra operators via run_operator.\"\n * }\n * ]\n * }\n * }\n */\n\nimport { z } from \"zod\"\n\nexport const PLUGIN_MANIFEST_SCHEMA = \"agentproto/plugin/v1\" as const\n\nconst AdapterEntrySchema = z\n .object({\n /** The `kind` string the manifest's substrate/dispatcher/etc. block uses. */\n kind: z.string().min(1),\n /**\n * Path to the entry module, relative to the plugin package root.\n * Resolved via the plugin's `package.json#main`/`exports` (i.e. you\n * can use a subpath like `./dist/substrates.mjs` or an export name\n * like `.` if the plugin re-exports everything from its root).\n */\n entry: z.string().min(1),\n /** Named export inside `entry` — the factory function. */\n export: z.string().min(1),\n /** Free-form description; shown by `agentproto plugins show`. */\n description: z.string().optional(),\n })\n .loose()\n\nconst SubstrateEntrySchema = AdapterEntrySchema.extend({\n /**\n * Free-form capability tags. Not gated by the kernel yet, but\n * surfaced by `agentproto plugins show` so users can see what the\n * substrate claims to support (mentions, reactions, visibility, …).\n */\n capabilities: z.array(z.string()).optional(),\n})\n\nexport const PluginManifestSchema = z\n .object({\n schema: z.literal(PLUGIN_MANIFEST_SCHEMA),\n substrates: z.array(SubstrateEntrySchema).default([]),\n dispatchers: z.array(AdapterEntrySchema).default([]),\n executors: z.array(AdapterEntrySchema).default([]),\n stateStores: z.array(AdapterEntrySchema).default([]),\n })\n .loose()\n\nexport type PluginManifest = z.infer<typeof PluginManifestSchema>\nexport type AdapterEntry = z.infer<typeof AdapterEntrySchema>\nexport type SubstrateEntry = z.infer<typeof SubstrateEntrySchema>\n","/**\n * MultiAgentRuntime adapter registry — the seam third-party packages\n * use to plug substrates, dispatchers, participant executors, and\n * state stores into `agentproto run-swarm` without forking the CLI.\n *\n * A plugin module exports nothing required by name — it just imports\n * `register*` helpers from `@agentproto/cli/registry/runtime` and\n * calls them at module load. The CLI discovers plugins via:\n *\n * 1. `--plugin <module-id>` flags on `run-swarm`\n * 2. The `plugins[]` array in `~/.agentproto/config.json`\n * 3. Auto-registered built-ins (file substrate, mention dispatcher,\n * fs state, agent-cli participant) — registered by\n * `registerBuiltins()` at startup.\n *\n * Manifest `kind` strings are resolved through the registry: the kind\n * is the lookup key; the factory builds the concrete adapter from the\n * (loose) manifest config + shared context.\n */\n\nimport type {\n Dispatcher,\n ParticipantExecutor,\n StateStore,\n Substrate,\n} from \"@agentproto/agent-runtime\"\n\n// ── Context passed to every factory ──\n\n/**\n * Shared context every factory receives. Carries the manifest's base\n * directory (for resolving relative paths declared in adapter configs)\n * and a `cleanup` collector so adapters that hold disposable resources\n * (MCP clients, sockets, child processes) can register teardown callbacks\n * that the CLI runs on shutdown.\n */\nexport interface AdapterContext {\n /** Absolute directory of the manifest file. */\n readonly baseDir: string\n /** Register a teardown callback to run when the swarm shuts down. */\n registerCleanup(fn: () => Promise<void> | void): void\n}\n\n// ── Loose config shape ──\n\n/**\n * Manifest adapter blocks are loose: `{ kind: string, ...host-extension }`.\n * Each factory pulls its own typed fields off the config and validates\n * them inline.\n */\nexport interface AdapterConfig {\n readonly kind: string\n readonly [extension: string]: unknown\n}\n\n// ── Factory signatures ──\n\nexport type SubstrateFactory = (\n config: AdapterConfig,\n ctx: AdapterContext\n) => Promise<Substrate> | Substrate\n\nexport type DispatcherFactory = (\n config: AdapterConfig,\n ctx: AdapterContext\n) => Promise<Dispatcher> | Dispatcher\n\nexport type ExecutorFactory = (\n config: AdapterConfig,\n ctx: AdapterContext\n) => Promise<ParticipantExecutor> | ParticipantExecutor\n\nexport type StateStoreFactory = (\n config: AdapterConfig,\n ctx: AdapterContext\n) => Promise<StateStore> | StateStore\n\n// ── Registry state ──\n\nconst substrates = new Map<string, SubstrateFactory>()\nconst dispatchers = new Map<string, DispatcherFactory>()\nconst executors = new Map<string, ExecutorFactory>()\nconst stateStores = new Map<string, StateStoreFactory>()\n\n// ── Public registration API ──\n\nexport function registerSubstrate(kind: string, factory: SubstrateFactory): void {\n substrates.set(kind, factory)\n}\n\nexport function registerDispatcher(\n kind: string,\n factory: DispatcherFactory\n): void {\n dispatchers.set(kind, factory)\n}\n\nexport function registerExecutor(kind: string, factory: ExecutorFactory): void {\n executors.set(kind, factory)\n}\n\nexport function registerStateStore(\n kind: string,\n factory: StateStoreFactory\n): void {\n stateStores.set(kind, factory)\n}\n\n// ── Lookup API (used by run-swarm wiring) ──\n\nexport function getSubstrateFactory(kind: string): SubstrateFactory | undefined {\n return substrates.get(kind)\n}\n\nexport function getDispatcherFactory(\n kind: string\n): DispatcherFactory | undefined {\n return dispatchers.get(kind)\n}\n\nexport function getExecutorFactory(kind: string): ExecutorFactory | undefined {\n return executors.get(kind)\n}\n\nexport function getStateStoreFactory(\n kind: string\n): StateStoreFactory | undefined {\n return stateStores.get(kind)\n}\n\nexport function listRegisteredKinds(): {\n substrates: readonly string[]\n dispatchers: readonly string[]\n executors: readonly string[]\n stateStores: readonly string[]\n} {\n return {\n substrates: [...substrates.keys()],\n dispatchers: [...dispatchers.keys()],\n executors: [...executors.keys()],\n stateStores: [...stateStores.keys()],\n }\n}\n\n/**\n * Test-only: drop every registration. Plugin registrations persist for\n * the lifetime of the process, so tests that load + unload plugins use\n * this to start from a clean slate.\n */\nexport function _resetRegistryForTests(): void {\n substrates.clear()\n dispatchers.clear()\n executors.clear()\n stateStores.clear()\n}\n","/**\n * Manifest-based plugin wiring.\n *\n * Given a plugin package id, locate its plugin manifest, validate it,\n * and register every declared adapter (substrate, dispatcher,\n * executor, state store) with the runtime registry. The plugin\n * doesn't need to call register* itself — the manifest declares what\n * it provides, this loader handles the wiring.\n *\n * Discovery order, per plugin:\n * 1. `<package-root>/agentproto.json`\n * 2. `<package-root>/package.json` → `.agentproto` block\n *\n * Returns the manifest (so callers like `agentproto plugins show` can\n * print it) or `null` if neither location declares one. A `null`\n * return is not an error — the caller may fall back to side-effect\n * imports for legacy plugins.\n */\n\nimport { readFile } from \"node:fs/promises\"\nimport { dirname, join } from \"node:path\"\nimport { createRequire } from \"node:module\"\nimport { pathToFileURL } from \"node:url\"\nimport {\n PluginManifestSchema,\n type PluginManifest,\n} from \"./manifest.js\"\nimport {\n registerDispatcher,\n registerExecutor,\n registerStateStore,\n registerSubstrate,\n type DispatcherFactory,\n type ExecutorFactory,\n type StateStoreFactory,\n type SubstrateFactory,\n} from \"./runtime.js\"\n\n/**\n * Load and register every adapter declared in a plugin's manifest.\n * Returns the manifest on success, `null` if the plugin doesn't ship\n * one (so the caller can fall back to side-effect import).\n */\nexport async function loadPluginFromManifest(\n pluginId: string\n): Promise<PluginManifest | null> {\n const result = await readPluginManifest(pluginId)\n if (!result) return null\n const { manifest, packageRoot } = result\n\n for (const sub of manifest.substrates) {\n const factory = await importFactory<SubstrateFactory>(\n packageRoot,\n pluginId,\n sub.entry,\n sub.export\n )\n registerSubstrate(sub.kind, factory)\n }\n for (const dis of manifest.dispatchers) {\n const factory = await importFactory<DispatcherFactory>(\n packageRoot,\n pluginId,\n dis.entry,\n dis.export\n )\n registerDispatcher(dis.kind, factory)\n }\n for (const exe of manifest.executors) {\n const factory = await importFactory<ExecutorFactory>(\n packageRoot,\n pluginId,\n exe.entry,\n exe.export\n )\n registerExecutor(exe.kind, factory)\n }\n for (const st of manifest.stateStores) {\n const factory = await importFactory<StateStoreFactory>(\n packageRoot,\n pluginId,\n st.entry,\n st.export\n )\n registerStateStore(st.kind, factory)\n }\n\n return manifest\n}\n\n/**\n * Read + validate a plugin's manifest without registering anything.\n * Used by `agentproto plugins show` to introspect installed plugins.\n */\nexport async function readPluginManifest(\n pluginId: string\n): Promise<{ manifest: PluginManifest; packageRoot: string } | null> {\n const packageJsonPath = resolvePluginPackageJson(pluginId)\n if (!packageJsonPath) return null\n\n const packageRoot = dirname(packageJsonPath)\n\n // 1. Try standalone agentproto.json first.\n const standalonePath = join(packageRoot, \"agentproto.json\")\n const standalone = await readJsonIfExists(standalonePath)\n if (standalone !== undefined) {\n return {\n manifest: PluginManifestSchema.parse(standalone),\n packageRoot,\n }\n }\n\n // 2. Fall back to package.json#agentproto.\n const pkgJson = (await readJsonIfExists(packageJsonPath)) as\n | { agentproto?: unknown }\n | undefined\n if (pkgJson && typeof pkgJson === \"object\" && pkgJson.agentproto) {\n return {\n manifest: PluginManifestSchema.parse(pkgJson.agentproto),\n packageRoot,\n }\n }\n\n return null\n}\n\nasync function importFactory<TFactory>(\n packageRoot: string,\n pluginId: string,\n entry: string,\n exportName: string\n): Promise<TFactory> {\n // Resolve the entry path relative to the package root. We dynamic-\n // import via file:// URL so an absolute path works without going\n // through Node's package resolver (which would need the export to\n // be declared in `exports`).\n const abs = entry.startsWith(\".\") ? join(packageRoot, entry) : entry\n const url = entry.startsWith(\".\") ? pathToFileURL(abs).href : entry\n const mod = (await import(url)) as Record<string, unknown>\n const exported = mod[exportName]\n if (typeof exported !== \"function\") {\n throw new Error(\n `agentproto plugin '${pluginId}': manifest entry '${entry}' has no callable export '${exportName}' (got ${typeof exported}).`\n )\n }\n return exported as TFactory\n}\n\nfunction resolvePluginPackageJson(pluginId: string): string | null {\n // Try the cli's own resolution first (global install + monorepo\n // workspace deps), then the user's cwd (locally-installed plugins).\n // `require.resolve` needs `createRequire` since we're an ESM bundle.\n const candidates = [\n import.meta.url,\n pathToFileURL(join(process.cwd(), \"package.json\")).href,\n ]\n for (const root of candidates) {\n try {\n return createRequire(root).resolve(`${pluginId}/package.json`)\n } catch {\n // try next root\n }\n }\n return null\n}\n\nasync function readJsonIfExists(path: string): Promise<unknown> {\n try {\n const raw = await readFile(path, \"utf8\")\n return JSON.parse(raw)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return undefined\n throw err\n }\n}\n","/**\n * Plugin loader. For each plugin id:\n *\n * 1. Try manifest-based wiring — read the plugin's\n * `package.json#agentproto` (or standalone `agentproto.json`),\n * validate the `agentproto/plugin/v1` schema, dynamic-import\n * each declared adapter entry, register with the runtime\n * registry.\n * 2. Fall back to side-effect import — older plugins that just\n * `registerSubstrate(...)` at module load are still supported.\n *\n * Discovery sources for plugin ids:\n * - `--plugin <module-id>` flag(s) on the verb\n * - `plugins[]` array in `~/.agentproto/config.json`\n *\n * Failures: a plugin that throws on load is reported on stderr and\n * the load skips it. The CLI continues — a single bad plugin\n * shouldn't take down the swarm.\n */\n\nimport { readFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\nimport { loadPluginFromManifest } from \"./manifest-loader.js\"\n\ninterface AgentprotoConfig {\n plugins?: readonly string[]\n}\n\nexport async function loadPluginsFromConfig(): Promise<readonly string[]> {\n const path = configPath()\n try {\n const raw = await readFile(path, \"utf8\")\n const parsed = JSON.parse(raw) as AgentprotoConfig\n return parsed.plugins ?? []\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return []\n process.stderr.write(\n `agentproto: failed to read ${path} — ${err instanceof Error ? err.message : String(err)}\\n`\n )\n return []\n }\n}\n\nexport async function loadPlugins(moduleIds: readonly string[]): Promise<void> {\n for (const id of moduleIds) {\n try {\n const manifest = await loadPluginFromManifest(id)\n if (manifest === null) {\n // No manifest declared → side-effect import for legacy plugins.\n await import(id)\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n process.stderr.write(\n `agentproto: plugin '${id}' failed to load — ${msg}\\n`\n )\n }\n }\n}\n\nfunction configPath(): string {\n const base = process.env[\"AGENTPROTO_HOME\"] ?? join(homedir(), \".agentproto\")\n return join(base, \"config.json\")\n}\n"]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Dispatcher, ParticipantExecutor, StateStore, Substrate } from '@agentproto/agent-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MultiAgentRuntime adapter registry — the seam third-party packages
|
|
5
|
+
* use to plug substrates, dispatchers, participant executors, and
|
|
6
|
+
* state stores into `agentproto run-swarm` without forking the CLI.
|
|
7
|
+
*
|
|
8
|
+
* A plugin module exports nothing required by name — it just imports
|
|
9
|
+
* `register*` helpers from `@agentproto/cli/registry/runtime` and
|
|
10
|
+
* calls them at module load. The CLI discovers plugins via:
|
|
11
|
+
*
|
|
12
|
+
* 1. `--plugin <module-id>` flags on `run-swarm`
|
|
13
|
+
* 2. The `plugins[]` array in `~/.agentproto/config.json`
|
|
14
|
+
* 3. Auto-registered built-ins (file substrate, mention dispatcher,
|
|
15
|
+
* fs state, agent-cli participant) — registered by
|
|
16
|
+
* `registerBuiltins()` at startup.
|
|
17
|
+
*
|
|
18
|
+
* Manifest `kind` strings are resolved through the registry: the kind
|
|
19
|
+
* is the lookup key; the factory builds the concrete adapter from the
|
|
20
|
+
* (loose) manifest config + shared context.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Shared context every factory receives. Carries the manifest's base
|
|
25
|
+
* directory (for resolving relative paths declared in adapter configs)
|
|
26
|
+
* and a `cleanup` collector so adapters that hold disposable resources
|
|
27
|
+
* (MCP clients, sockets, child processes) can register teardown callbacks
|
|
28
|
+
* that the CLI runs on shutdown.
|
|
29
|
+
*/
|
|
30
|
+
interface AdapterContext {
|
|
31
|
+
/** Absolute directory of the manifest file. */
|
|
32
|
+
readonly baseDir: string;
|
|
33
|
+
/** Register a teardown callback to run when the swarm shuts down. */
|
|
34
|
+
registerCleanup(fn: () => Promise<void> | void): void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Manifest adapter blocks are loose: `{ kind: string, ...host-extension }`.
|
|
38
|
+
* Each factory pulls its own typed fields off the config and validates
|
|
39
|
+
* them inline.
|
|
40
|
+
*/
|
|
41
|
+
interface AdapterConfig {
|
|
42
|
+
readonly kind: string;
|
|
43
|
+
readonly [extension: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
type SubstrateFactory = (config: AdapterConfig, ctx: AdapterContext) => Promise<Substrate> | Substrate;
|
|
46
|
+
type DispatcherFactory = (config: AdapterConfig, ctx: AdapterContext) => Promise<Dispatcher> | Dispatcher;
|
|
47
|
+
type ExecutorFactory = (config: AdapterConfig, ctx: AdapterContext) => Promise<ParticipantExecutor> | ParticipantExecutor;
|
|
48
|
+
type StateStoreFactory = (config: AdapterConfig, ctx: AdapterContext) => Promise<StateStore> | StateStore;
|
|
49
|
+
declare function registerSubstrate(kind: string, factory: SubstrateFactory): void;
|
|
50
|
+
declare function registerDispatcher(kind: string, factory: DispatcherFactory): void;
|
|
51
|
+
declare function registerExecutor(kind: string, factory: ExecutorFactory): void;
|
|
52
|
+
declare function registerStateStore(kind: string, factory: StateStoreFactory): void;
|
|
53
|
+
declare function getSubstrateFactory(kind: string): SubstrateFactory | undefined;
|
|
54
|
+
declare function getDispatcherFactory(kind: string): DispatcherFactory | undefined;
|
|
55
|
+
declare function getExecutorFactory(kind: string): ExecutorFactory | undefined;
|
|
56
|
+
declare function getStateStoreFactory(kind: string): StateStoreFactory | undefined;
|
|
57
|
+
declare function listRegisteredKinds(): {
|
|
58
|
+
substrates: readonly string[];
|
|
59
|
+
dispatchers: readonly string[];
|
|
60
|
+
executors: readonly string[];
|
|
61
|
+
stateStores: readonly string[];
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Test-only: drop every registration. Plugin registrations persist for
|
|
65
|
+
* the lifetime of the process, so tests that load + unload plugins use
|
|
66
|
+
* this to start from a clean slate.
|
|
67
|
+
*/
|
|
68
|
+
declare function _resetRegistryForTests(): void;
|
|
69
|
+
|
|
70
|
+
export { type AdapterConfig, type AdapterContext, type DispatcherFactory, type ExecutorFactory, type StateStoreFactory, type SubstrateFactory, _resetRegistryForTests, getDispatcherFactory, getExecutorFactory, getStateStoreFactory, getSubstrateFactory, listRegisteredKinds, registerDispatcher, registerExecutor, registerStateStore, registerSubstrate };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentproto/cli v0.1.0-alpha
|
|
3
|
+
* The `agentproto` binary — install / run / serve AIP-45 agent CLIs.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// src/registry/runtime.ts
|
|
7
|
+
var substrates = /* @__PURE__ */ new Map();
|
|
8
|
+
var dispatchers = /* @__PURE__ */ new Map();
|
|
9
|
+
var executors = /* @__PURE__ */ new Map();
|
|
10
|
+
var stateStores = /* @__PURE__ */ new Map();
|
|
11
|
+
function registerSubstrate(kind, factory) {
|
|
12
|
+
substrates.set(kind, factory);
|
|
13
|
+
}
|
|
14
|
+
function registerDispatcher(kind, factory) {
|
|
15
|
+
dispatchers.set(kind, factory);
|
|
16
|
+
}
|
|
17
|
+
function registerExecutor(kind, factory) {
|
|
18
|
+
executors.set(kind, factory);
|
|
19
|
+
}
|
|
20
|
+
function registerStateStore(kind, factory) {
|
|
21
|
+
stateStores.set(kind, factory);
|
|
22
|
+
}
|
|
23
|
+
function getSubstrateFactory(kind) {
|
|
24
|
+
return substrates.get(kind);
|
|
25
|
+
}
|
|
26
|
+
function getDispatcherFactory(kind) {
|
|
27
|
+
return dispatchers.get(kind);
|
|
28
|
+
}
|
|
29
|
+
function getExecutorFactory(kind) {
|
|
30
|
+
return executors.get(kind);
|
|
31
|
+
}
|
|
32
|
+
function getStateStoreFactory(kind) {
|
|
33
|
+
return stateStores.get(kind);
|
|
34
|
+
}
|
|
35
|
+
function listRegisteredKinds() {
|
|
36
|
+
return {
|
|
37
|
+
substrates: [...substrates.keys()],
|
|
38
|
+
dispatchers: [...dispatchers.keys()],
|
|
39
|
+
executors: [...executors.keys()],
|
|
40
|
+
stateStores: [...stateStores.keys()]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function _resetRegistryForTests() {
|
|
44
|
+
substrates.clear();
|
|
45
|
+
dispatchers.clear();
|
|
46
|
+
executors.clear();
|
|
47
|
+
stateStores.clear();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { _resetRegistryForTests, getDispatcherFactory, getExecutorFactory, getStateStoreFactory, getSubstrateFactory, listRegisteredKinds, registerDispatcher, registerExecutor, registerStateStore, registerSubstrate };
|
|
51
|
+
//# sourceMappingURL=runtime.mjs.map
|
|
52
|
+
//# sourceMappingURL=runtime.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/registry/runtime.ts"],"names":[],"mappings":";;;;;;AA+EA,IAAM,UAAA,uBAAiB,GAAA,EAA8B;AACrD,IAAM,WAAA,uBAAkB,GAAA,EAA+B;AACvD,IAAM,SAAA,uBAAgB,GAAA,EAA6B;AACnD,IAAM,WAAA,uBAAkB,GAAA,EAA+B;AAIhD,SAAS,iBAAA,CAAkB,MAAc,OAAA,EAAiC;AAC/E,EAAA,UAAA,CAAW,GAAA,CAAI,MAAM,OAAO,CAAA;AAC9B;AAEO,SAAS,kBAAA,CACd,MACA,OAAA,EACM;AACN,EAAA,WAAA,CAAY,GAAA,CAAI,MAAM,OAAO,CAAA;AAC/B;AAEO,SAAS,gBAAA,CAAiB,MAAc,OAAA,EAAgC;AAC7E,EAAA,SAAA,CAAU,GAAA,CAAI,MAAM,OAAO,CAAA;AAC7B;AAEO,SAAS,kBAAA,CACd,MACA,OAAA,EACM;AACN,EAAA,WAAA,CAAY,GAAA,CAAI,MAAM,OAAO,CAAA;AAC/B;AAIO,SAAS,oBAAoB,IAAA,EAA4C;AAC9E,EAAA,OAAO,UAAA,CAAW,IAAI,IAAI,CAAA;AAC5B;AAEO,SAAS,qBACd,IAAA,EAC+B;AAC/B,EAAA,OAAO,WAAA,CAAY,IAAI,IAAI,CAAA;AAC7B;AAEO,SAAS,mBAAmB,IAAA,EAA2C;AAC5E,EAAA,OAAO,SAAA,CAAU,IAAI,IAAI,CAAA;AAC3B;AAEO,SAAS,qBACd,IAAA,EAC+B;AAC/B,EAAA,OAAO,WAAA,CAAY,IAAI,IAAI,CAAA;AAC7B;AAEO,SAAS,mBAAA,GAKd;AACA,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,CAAC,GAAG,UAAA,CAAW,MAAM,CAAA;AAAA,IACjC,WAAA,EAAa,CAAC,GAAG,WAAA,CAAY,MAAM,CAAA;AAAA,IACnC,SAAA,EAAW,CAAC,GAAG,SAAA,CAAU,MAAM,CAAA;AAAA,IAC/B,WAAA,EAAa,CAAC,GAAG,WAAA,CAAY,MAAM;AAAA,GACrC;AACF;AAOO,SAAS,sBAAA,GAA+B;AAC7C,EAAA,UAAA,CAAW,KAAA,EAAM;AACjB,EAAA,WAAA,CAAY,KAAA,EAAM;AAClB,EAAA,SAAA,CAAU,KAAA,EAAM;AAChB,EAAA,WAAA,CAAY,KAAA,EAAM;AACpB","file":"runtime.mjs","sourcesContent":["/**\n * MultiAgentRuntime adapter registry — the seam third-party packages\n * use to plug substrates, dispatchers, participant executors, and\n * state stores into `agentproto run-swarm` without forking the CLI.\n *\n * A plugin module exports nothing required by name — it just imports\n * `register*` helpers from `@agentproto/cli/registry/runtime` and\n * calls them at module load. The CLI discovers plugins via:\n *\n * 1. `--plugin <module-id>` flags on `run-swarm`\n * 2. The `plugins[]` array in `~/.agentproto/config.json`\n * 3. Auto-registered built-ins (file substrate, mention dispatcher,\n * fs state, agent-cli participant) — registered by\n * `registerBuiltins()` at startup.\n *\n * Manifest `kind` strings are resolved through the registry: the kind\n * is the lookup key; the factory builds the concrete adapter from the\n * (loose) manifest config + shared context.\n */\n\nimport type {\n Dispatcher,\n ParticipantExecutor,\n StateStore,\n Substrate,\n} from \"@agentproto/agent-runtime\"\n\n// ── Context passed to every factory ──\n\n/**\n * Shared context every factory receives. Carries the manifest's base\n * directory (for resolving relative paths declared in adapter configs)\n * and a `cleanup` collector so adapters that hold disposable resources\n * (MCP clients, sockets, child processes) can register teardown callbacks\n * that the CLI runs on shutdown.\n */\nexport interface AdapterContext {\n /** Absolute directory of the manifest file. */\n readonly baseDir: string\n /** Register a teardown callback to run when the swarm shuts down. */\n registerCleanup(fn: () => Promise<void> | void): void\n}\n\n// ── Loose config shape ──\n\n/**\n * Manifest adapter blocks are loose: `{ kind: string, ...host-extension }`.\n * Each factory pulls its own typed fields off the config and validates\n * them inline.\n */\nexport interface AdapterConfig {\n readonly kind: string\n readonly [extension: string]: unknown\n}\n\n// ── Factory signatures ──\n\nexport type SubstrateFactory = (\n config: AdapterConfig,\n ctx: AdapterContext\n) => Promise<Substrate> | Substrate\n\nexport type DispatcherFactory = (\n config: AdapterConfig,\n ctx: AdapterContext\n) => Promise<Dispatcher> | Dispatcher\n\nexport type ExecutorFactory = (\n config: AdapterConfig,\n ctx: AdapterContext\n) => Promise<ParticipantExecutor> | ParticipantExecutor\n\nexport type StateStoreFactory = (\n config: AdapterConfig,\n ctx: AdapterContext\n) => Promise<StateStore> | StateStore\n\n// ── Registry state ──\n\nconst substrates = new Map<string, SubstrateFactory>()\nconst dispatchers = new Map<string, DispatcherFactory>()\nconst executors = new Map<string, ExecutorFactory>()\nconst stateStores = new Map<string, StateStoreFactory>()\n\n// ── Public registration API ──\n\nexport function registerSubstrate(kind: string, factory: SubstrateFactory): void {\n substrates.set(kind, factory)\n}\n\nexport function registerDispatcher(\n kind: string,\n factory: DispatcherFactory\n): void {\n dispatchers.set(kind, factory)\n}\n\nexport function registerExecutor(kind: string, factory: ExecutorFactory): void {\n executors.set(kind, factory)\n}\n\nexport function registerStateStore(\n kind: string,\n factory: StateStoreFactory\n): void {\n stateStores.set(kind, factory)\n}\n\n// ── Lookup API (used by run-swarm wiring) ──\n\nexport function getSubstrateFactory(kind: string): SubstrateFactory | undefined {\n return substrates.get(kind)\n}\n\nexport function getDispatcherFactory(\n kind: string\n): DispatcherFactory | undefined {\n return dispatchers.get(kind)\n}\n\nexport function getExecutorFactory(kind: string): ExecutorFactory | undefined {\n return executors.get(kind)\n}\n\nexport function getStateStoreFactory(\n kind: string\n): StateStoreFactory | undefined {\n return stateStores.get(kind)\n}\n\nexport function listRegisteredKinds(): {\n substrates: readonly string[]\n dispatchers: readonly string[]\n executors: readonly string[]\n stateStores: readonly string[]\n} {\n return {\n substrates: [...substrates.keys()],\n dispatchers: [...dispatchers.keys()],\n executors: [...executors.keys()],\n stateStores: [...stateStores.keys()],\n }\n}\n\n/**\n * Test-only: drop every registration. Plugin registrations persist for\n * the lifetime of the process, so tests that load + unload plugins use\n * this to start from a clean slate.\n */\nexport function _resetRegistryForTests(): void {\n substrates.clear()\n dispatchers.clear()\n executors.clear()\n stateStores.clear()\n}\n"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential store for `agentproto auth login`.
|
|
3
|
+
*
|
|
4
|
+
* Format on disk (JSON, mode 0600):
|
|
5
|
+
*
|
|
6
|
+
* {
|
|
7
|
+
* "version": 1,
|
|
8
|
+
* "hosts": {
|
|
9
|
+
* "wss://guilde.work/api/v1/agentproto/tunnel": {
|
|
10
|
+
* "token": "eyJ…",
|
|
11
|
+
* "tokenType": "Bearer",
|
|
12
|
+
* "expiresAt": "2026-08-08T12:34:56.000Z",
|
|
13
|
+
* "refreshToken": "rt_…", // optional
|
|
14
|
+
* "scope": "tunnel:connect",
|
|
15
|
+
* "subject": "user_abc",
|
|
16
|
+
* "obtainedAt": "2026-05-10T08:21:11.000Z",
|
|
17
|
+
* "deviceLabel": "jeremy@laptop"
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* One file per *user*, multiple hosts under the same file. Hosts are
|
|
23
|
+
* keyed by the URL the user passed to `agentproto auth login --host`,
|
|
24
|
+
* stripped of trailing slash. `agentproto serve --connect <url>` looks
|
|
25
|
+
* up the same key when no `--token` is passed.
|
|
26
|
+
*
|
|
27
|
+
* Storage location:
|
|
28
|
+
* - `$AGENTPROTO_HOME/credentials.json`, falling back to
|
|
29
|
+
* - `~/.agentproto/credentials.json`
|
|
30
|
+
*
|
|
31
|
+
* The file holds bearer tokens, so it's chmod'd 0600 on every write.
|
|
32
|
+
* On Windows we rely on the per-user profile path being already
|
|
33
|
+
* private and skip chmod. Never log the token contents.
|
|
34
|
+
*/
|
|
35
|
+
interface HostCredential {
|
|
36
|
+
/** Bearer token. Bearer-form is the only one supported for v1. */
|
|
37
|
+
token: string;
|
|
38
|
+
/** Always "Bearer" today; reserved for future schemes. */
|
|
39
|
+
tokenType: "Bearer";
|
|
40
|
+
/** ISO-8601. May be in the past — callers must check `isExpired`. */
|
|
41
|
+
expiresAt: string;
|
|
42
|
+
/** OAuth 2.0 refresh token. Present when the host issues one. */
|
|
43
|
+
refreshToken?: string;
|
|
44
|
+
/** Space-separated scopes the token was issued with. */
|
|
45
|
+
scope?: string;
|
|
46
|
+
/** Human-readable subject — usually the user id, surfaced in `auth status`. */
|
|
47
|
+
subject?: string;
|
|
48
|
+
/** ISO-8601 of when this credential was minted. */
|
|
49
|
+
obtainedAt: string;
|
|
50
|
+
/** Friendly device label shown on the host's Machine-tokens page. */
|
|
51
|
+
deviceLabel?: string;
|
|
52
|
+
/** Opaque revocation hint surfaced by the host (e.g. JTI). The CLI
|
|
53
|
+
* passes it back on `auth logout` so the host can revoke the right
|
|
54
|
+
* row server-side, not just delete the local copy. */
|
|
55
|
+
revocationId?: string;
|
|
56
|
+
}
|
|
57
|
+
interface CredentialsFile {
|
|
58
|
+
version: 1;
|
|
59
|
+
hosts: Record<string, HostCredential>;
|
|
60
|
+
}
|
|
61
|
+
declare function credentialsPath(): string;
|
|
62
|
+
declare function normaliseHost(host: string): string;
|
|
63
|
+
declare function loadCredentials(): Promise<CredentialsFile>;
|
|
64
|
+
declare function saveCredentials(file: CredentialsFile): Promise<void>;
|
|
65
|
+
declare function readHost(host: string): Promise<HostCredential | null>;
|
|
66
|
+
declare function writeHost(host: string, cred: HostCredential): Promise<void>;
|
|
67
|
+
declare function deleteHost(host: string): Promise<HostCredential | null>;
|
|
68
|
+
declare function isExpired(cred: HostCredential, gracePeriodMs?: number): boolean;
|
|
69
|
+
declare function formatExpiry(cred: HostCredential): string;
|
|
70
|
+
|
|
71
|
+
export { type HostCredential, credentialsPath, deleteHost, formatExpiry, isExpired, loadCredentials, normaliseHost, readHost, saveCredentials, writeHost };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readFile, mkdir, writeFile, chmod, unlink } from 'fs/promises';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @agentproto/cli v0.1.0-alpha
|
|
7
|
+
* The `agentproto` binary — install / run / serve AIP-45 agent CLIs.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var FILE_VERSION = 1;
|
|
11
|
+
function credentialsPath() {
|
|
12
|
+
const base = process.env["AGENTPROTO_HOME"] ?? join(homedir(), ".agentproto");
|
|
13
|
+
return join(base, "credentials.json");
|
|
14
|
+
}
|
|
15
|
+
function normaliseHost(host) {
|
|
16
|
+
return host.replace(/\/+$/, "");
|
|
17
|
+
}
|
|
18
|
+
async function loadCredentials() {
|
|
19
|
+
const path = credentialsPath();
|
|
20
|
+
try {
|
|
21
|
+
const raw = await readFile(path, "utf8");
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
if (parsed.version !== FILE_VERSION) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`credentials.json: unknown version ${parsed.version}; expected ${FILE_VERSION}. Delete the file and re-run \`agentproto auth login\`.`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.code === "ENOENT") {
|
|
31
|
+
return { version: FILE_VERSION, hosts: {} };
|
|
32
|
+
}
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function saveCredentials(file) {
|
|
37
|
+
const path = credentialsPath();
|
|
38
|
+
await mkdir(dirname(path), { recursive: true });
|
|
39
|
+
await writeFile(path, JSON.stringify(file, null, 2));
|
|
40
|
+
await chmod(path, 384).catch(() => {
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async function readHost(host) {
|
|
44
|
+
const f = await loadCredentials();
|
|
45
|
+
return f.hosts[normaliseHost(host)] ?? null;
|
|
46
|
+
}
|
|
47
|
+
async function writeHost(host, cred) {
|
|
48
|
+
const f = await loadCredentials();
|
|
49
|
+
f.hosts[normaliseHost(host)] = cred;
|
|
50
|
+
await saveCredentials(f);
|
|
51
|
+
}
|
|
52
|
+
async function deleteHost(host) {
|
|
53
|
+
const f = await loadCredentials();
|
|
54
|
+
const key = normaliseHost(host);
|
|
55
|
+
const prev = f.hosts[key] ?? null;
|
|
56
|
+
if (prev) {
|
|
57
|
+
delete f.hosts[key];
|
|
58
|
+
if (Object.keys(f.hosts).length === 0) {
|
|
59
|
+
await unlink(credentialsPath()).catch(() => {
|
|
60
|
+
});
|
|
61
|
+
} else {
|
|
62
|
+
await saveCredentials(f);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return prev;
|
|
66
|
+
}
|
|
67
|
+
function isExpired(cred, gracePeriodMs = 3e4) {
|
|
68
|
+
const exp = Date.parse(cred.expiresAt);
|
|
69
|
+
if (!Number.isFinite(exp)) return false;
|
|
70
|
+
return exp - gracePeriodMs <= Date.now();
|
|
71
|
+
}
|
|
72
|
+
function formatExpiry(cred) {
|
|
73
|
+
const exp = new Date(cred.expiresAt);
|
|
74
|
+
if (Number.isNaN(exp.getTime())) return "unknown";
|
|
75
|
+
const ms = exp.getTime() - Date.now();
|
|
76
|
+
if (ms < 0) return `expired ${formatRelative(-ms)} ago`;
|
|
77
|
+
return `expires in ${formatRelative(ms)}`;
|
|
78
|
+
}
|
|
79
|
+
function formatRelative(ms) {
|
|
80
|
+
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
81
|
+
if (ms < 36e5) return `${Math.round(ms / 6e4)}m`;
|
|
82
|
+
if (ms < 864e5) return `${Math.round(ms / 36e5)}h`;
|
|
83
|
+
return `${Math.round(ms / 864e5)}d`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { credentialsPath, deleteHost, formatExpiry, isExpired, loadCredentials, normaliseHost, readHost, saveCredentials, writeHost };
|
|
87
|
+
//# sourceMappingURL=credentials.mjs.map
|
|
88
|
+
//# sourceMappingURL=credentials.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/credentials.ts"],"names":[],"mappings":"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","file":"credentials.mjs","sourcesContent":["/**\n * Credential store for `agentproto auth login`.\n *\n * Format on disk (JSON, mode 0600):\n *\n * {\n * \"version\": 1,\n * \"hosts\": {\n * \"wss://guilde.work/api/v1/agentproto/tunnel\": {\n * \"token\": \"eyJ…\",\n * \"tokenType\": \"Bearer\",\n * \"expiresAt\": \"2026-08-08T12:34:56.000Z\",\n * \"refreshToken\": \"rt_…\", // optional\n * \"scope\": \"tunnel:connect\",\n * \"subject\": \"user_abc\",\n * \"obtainedAt\": \"2026-05-10T08:21:11.000Z\",\n * \"deviceLabel\": \"jeremy@laptop\"\n * }\n * }\n * }\n *\n * One file per *user*, multiple hosts under the same file. Hosts are\n * keyed by the URL the user passed to `agentproto auth login --host`,\n * stripped of trailing slash. `agentproto serve --connect <url>` looks\n * up the same key when no `--token` is passed.\n *\n * Storage location:\n * - `$AGENTPROTO_HOME/credentials.json`, falling back to\n * - `~/.agentproto/credentials.json`\n *\n * The file holds bearer tokens, so it's chmod'd 0600 on every write.\n * On Windows we rely on the per-user profile path being already\n * private and skip chmod. Never log the token contents.\n */\n\nimport { mkdir, readFile, writeFile, chmod, unlink } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { join, dirname } from \"node:path\"\n\nexport interface HostCredential {\n /** Bearer token. Bearer-form is the only one supported for v1. */\n token: string\n /** Always \"Bearer\" today; reserved for future schemes. */\n tokenType: \"Bearer\"\n /** ISO-8601. May be in the past — callers must check `isExpired`. */\n expiresAt: string\n /** OAuth 2.0 refresh token. Present when the host issues one. */\n refreshToken?: string\n /** Space-separated scopes the token was issued with. */\n scope?: string\n /** Human-readable subject — usually the user id, surfaced in `auth status`. */\n subject?: string\n /** ISO-8601 of when this credential was minted. */\n obtainedAt: string\n /** Friendly device label shown on the host's Machine-tokens page. */\n deviceLabel?: string\n /** Opaque revocation hint surfaced by the host (e.g. JTI). The CLI\n * passes it back on `auth logout` so the host can revoke the right\n * row server-side, not just delete the local copy. */\n revocationId?: string\n}\n\ninterface CredentialsFile {\n version: 1\n hosts: Record<string, HostCredential>\n}\n\nconst FILE_VERSION = 1 as const\n\nexport function credentialsPath(): string {\n const base = process.env[\"AGENTPROTO_HOME\"] ?? join(homedir(), \".agentproto\")\n return join(base, \"credentials.json\")\n}\n\nexport function normaliseHost(host: string): string {\n return host.replace(/\\/+$/, \"\")\n}\n\nexport async function loadCredentials(): Promise<CredentialsFile> {\n const path = credentialsPath()\n try {\n const raw = await readFile(path, \"utf8\")\n const parsed = JSON.parse(raw) as CredentialsFile\n if (parsed.version !== FILE_VERSION) {\n throw new Error(\n `credentials.json: unknown version ${parsed.version}; expected ${FILE_VERSION}. ` +\n `Delete the file and re-run \\`agentproto auth login\\`.`\n )\n }\n return parsed\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { version: FILE_VERSION, hosts: {} }\n }\n throw err\n }\n}\n\nexport async function saveCredentials(file: CredentialsFile): Promise<void> {\n const path = credentialsPath()\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, JSON.stringify(file, null, 2))\n await chmod(path, 0o600).catch(() => {\n // Windows + some sandboxed FSes (e.g. WSL on a Windows mount) don't\n // honour chmod. The file lives under the user profile in those\n // cases, which is already user-private; not worth refusing.\n })\n}\n\nexport async function readHost(host: string): Promise<HostCredential | null> {\n const f = await loadCredentials()\n return f.hosts[normaliseHost(host)] ?? null\n}\n\nexport async function writeHost(\n host: string,\n cred: HostCredential\n): Promise<void> {\n const f = await loadCredentials()\n f.hosts[normaliseHost(host)] = cred\n await saveCredentials(f)\n}\n\nexport async function deleteHost(host: string): Promise<HostCredential | null> {\n const f = await loadCredentials()\n const key = normaliseHost(host)\n const prev = f.hosts[key] ?? null\n if (prev) {\n delete f.hosts[key]\n if (Object.keys(f.hosts).length === 0) {\n // Empty file is a valid state (`auth login` re-creates), but\n // unlinking is cleaner — `auth status` then prints \"no credentials\".\n await unlink(credentialsPath()).catch(() => {})\n } else {\n await saveCredentials(f)\n }\n }\n return prev\n}\n\nexport function isExpired(cred: HostCredential, gracePeriodMs = 30_000): boolean {\n const exp = Date.parse(cred.expiresAt)\n if (!Number.isFinite(exp)) return false // unparseable expiry → trust the host\n return exp - gracePeriodMs <= Date.now()\n}\n\nexport function formatExpiry(cred: HostCredential): string {\n const exp = new Date(cred.expiresAt)\n if (Number.isNaN(exp.getTime())) return \"unknown\"\n const ms = exp.getTime() - Date.now()\n if (ms < 0) return `expired ${formatRelative(-ms)} ago`\n return `expires in ${formatRelative(ms)}`\n}\n\nfunction formatRelative(ms: number): string {\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`\n if (ms < 86_400_000) return `${Math.round(ms / 3_600_000)}h`\n return `${Math.round(ms / 86_400_000)}d`\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentproto/cli",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "@agentproto/cli — the `agentproto` binary. Install AIP-45 agent CLI adapters, run them locally, or expose them over a tunnel as a long-running daemon. Reference host for hermes / claude-code / opencode / gemini-cli / goose, all driven through @agentproto/driver-agent-cli.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agentproto",
|
|
@@ -36,7 +36,33 @@
|
|
|
36
36
|
"types": "./dist/index.d.ts",
|
|
37
37
|
"import": "./dist/index.mjs",
|
|
38
38
|
"default": "./dist/index.mjs"
|
|
39
|
-
}
|
|
39
|
+
},
|
|
40
|
+
"./registry/runtime": {
|
|
41
|
+
"types": "./dist/registry/runtime.d.ts",
|
|
42
|
+
"import": "./dist/registry/runtime.mjs",
|
|
43
|
+
"default": "./dist/registry/runtime.mjs"
|
|
44
|
+
},
|
|
45
|
+
"./registry/builtins": {
|
|
46
|
+
"types": "./dist/registry/builtins.d.ts",
|
|
47
|
+
"import": "./dist/registry/builtins.mjs",
|
|
48
|
+
"default": "./dist/registry/builtins.mjs"
|
|
49
|
+
},
|
|
50
|
+
"./registry/plugins": {
|
|
51
|
+
"types": "./dist/registry/plugins.d.ts",
|
|
52
|
+
"import": "./dist/registry/plugins.mjs",
|
|
53
|
+
"default": "./dist/registry/plugins.mjs"
|
|
54
|
+
},
|
|
55
|
+
"./registry/manifest": {
|
|
56
|
+
"types": "./dist/registry/manifest.d.ts",
|
|
57
|
+
"import": "./dist/registry/manifest.mjs",
|
|
58
|
+
"default": "./dist/registry/manifest.mjs"
|
|
59
|
+
},
|
|
60
|
+
"./util/credentials": {
|
|
61
|
+
"types": "./dist/util/credentials.d.ts",
|
|
62
|
+
"import": "./dist/util/credentials.mjs",
|
|
63
|
+
"default": "./dist/util/credentials.mjs"
|
|
64
|
+
},
|
|
65
|
+
"./package.json": "./package.json"
|
|
40
66
|
},
|
|
41
67
|
"files": [
|
|
42
68
|
"dist",
|
|
@@ -47,8 +73,9 @@
|
|
|
47
73
|
"gray-matter": "^4.0.3",
|
|
48
74
|
"ws": "^8.20.0",
|
|
49
75
|
"zod": "^4.4.3",
|
|
50
|
-
"@agentproto/
|
|
51
|
-
"@agentproto/acp": "0.1.0
|
|
76
|
+
"@agentproto/runtime-profile-standard": "0.1.0",
|
|
77
|
+
"@agentproto/acp": "0.1.0",
|
|
78
|
+
"@agentproto/driver-agent-cli": "0.1.0"
|
|
52
79
|
},
|
|
53
80
|
"optionalDependencies": {
|
|
54
81
|
"node-pty": "^1.0.0"
|
|
@@ -59,10 +86,11 @@
|
|
|
59
86
|
"tsup": "^8.5.1",
|
|
60
87
|
"typescript": "^5.9.3",
|
|
61
88
|
"vitest": "^3.2.4",
|
|
62
|
-
"@agentproto/adapter-claude-code": "0.1.0-alpha.0",
|
|
63
89
|
"@agentproto/adapter-hermes": "0.1.0-alpha.0",
|
|
64
|
-
"@agentproto/
|
|
65
|
-
"@agentproto/runtime": "0.1.0-alpha.0"
|
|
90
|
+
"@agentproto/adapter-claude-code": "0.1.0-alpha.0",
|
|
91
|
+
"@agentproto/agent-runtime": "0.1.0-alpha.0",
|
|
92
|
+
"@agentproto/runtime": "0.1.0-alpha.0",
|
|
93
|
+
"@agentproto/tooling": "0.1.0-alpha.0"
|
|
66
94
|
},
|
|
67
95
|
"engines": {
|
|
68
96
|
"node": ">=20.9.0"
|