@prismer/runtime 1.9.0 → 1.9.6

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/README.md +363 -229
  3. package/dist/cli.cjs +10269 -0
  4. package/dist/cli.cjs.map +1 -0
  5. package/dist/cli.js +10257 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.cjs +10603 -0
  8. package/dist/index.cjs.map +1 -0
  9. package/dist/index.d.cts +1643 -0
  10. package/dist/index.d.ts +1496 -1916
  11. package/dist/index.js +9702 -9177
  12. package/dist/index.js.map +1 -1
  13. package/package.json +31 -33
  14. package/dist/adapter-registry-EMZFKFYK.mjs +0 -8
  15. package/dist/adapter-registry-EMZFKFYK.mjs.map +0 -1
  16. package/dist/artifacts-uploader-N2HNBSTW.mjs +0 -159
  17. package/dist/artifacts-uploader-N2HNBSTW.mjs.map +0 -1
  18. package/dist/auto-register-M4YSPJS6.mjs +0 -204
  19. package/dist/auto-register-M4YSPJS6.mjs.map +0 -1
  20. package/dist/bin/prismer.js +0 -14980
  21. package/dist/chunk-BJTO5JO5.mjs +0 -11
  22. package/dist/chunk-BJTO5JO5.mjs.map +0 -1
  23. package/dist/chunk-JIEDFDVI.mjs +0 -148
  24. package/dist/chunk-JIEDFDVI.mjs.map +0 -1
  25. package/dist/chunk-NDNX2G6O.mjs +0 -68
  26. package/dist/chunk-NDNX2G6O.mjs.map +0 -1
  27. package/dist/chunk-POWV475F.mjs +0 -45
  28. package/dist/chunk-POWV475F.mjs.map +0 -1
  29. package/dist/chunk-VTFKZAUY.mjs +0 -68
  30. package/dist/chunk-VTFKZAUY.mjs.map +0 -1
  31. package/dist/dispatch-mux-MW4HHICS.mjs +0 -8
  32. package/dist/dispatch-mux-MW4HHICS.mjs.map +0 -1
  33. package/dist/dispatch-rpc-J7H6KC2V.mjs +0 -78
  34. package/dist/dispatch-rpc-J7H6KC2V.mjs.map +0 -1
  35. package/dist/fs-rpc-OSHJZYK6.mjs +0 -58
  36. package/dist/fs-rpc-OSHJZYK6.mjs.map +0 -1
  37. package/dist/heartbeat-loop-H2LAI3V5.mjs +0 -95
  38. package/dist/heartbeat-loop-H2LAI3V5.mjs.map +0 -1
  39. package/dist/index.d.mts +0 -2063
  40. package/dist/index.mjs +0 -8807
  41. package/dist/index.mjs.map +0 -1
  42. package/dist/mode-b-DXJ7FJAL.mjs +0 -152
  43. package/dist/mode-b-DXJ7FJAL.mjs.map +0 -1
  44. package/dist/registry-ZYU2HDFL.mjs +0 -12
  45. package/dist/registry-ZYU2HDFL.mjs.map +0 -1
  46. /package/{dist → assets}/icon +0 -0
  47. /package/{dist → assets}/smallicon +0 -0
package/package.json CHANGED
@@ -1,56 +1,54 @@
1
1
  {
2
2
  "name": "@prismer/runtime",
3
- "version": "1.9.0",
4
- "description": "Prismer Cloud v1.9.0 daemon runtime: agent supervisor, local HTTP API, event bus, CLI",
3
+ "version": "1.9.6",
4
+ "description": "Prismer Cloud daemon runtime TS-only adapter host for hosted IM agents",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
+ "module": "dist/index.js",
7
8
  "types": "dist/index.d.ts",
8
- "bin": {
9
- "prismer": "./dist/bin/prismer.js"
9
+ "engines": {
10
+ "node": ">=20"
10
11
  },
11
12
  "files": [
12
13
  "dist",
13
- "README.md"
14
+ "assets",
15
+ "README.md",
16
+ "CHANGELOG.md"
14
17
  ],
15
- "engines": {
16
- "node": ">=20"
17
- },
18
- "scripts": {
19
- "build": "tsup && mkdir -p dist && cp ../icon dist/icon && cp ../smallicon dist/smallicon",
20
- "postinstall": "node -e \"const fs=require('fs');const cp=require('child_process');if(fs.existsSync('dist/bin/prismer.js')) cp.spawnSync(process.execPath,['dist/bin/prismer.js','setup','--postinstall'],{stdio:'inherit'});\" || true",
21
- "typecheck": "tsc --noEmit",
22
- "test": "vitest run",
23
- "clean": "rm -rf dist"
18
+ "bin": {
19
+ "prismer": "./dist/cli.js"
24
20
  },
25
21
  "exports": {
26
22
  ".": {
27
23
  "types": "./dist/index.d.ts",
28
- "import": "./dist/index.mjs",
24
+ "import": "./dist/index.js",
29
25
  "require": "./dist/index.js"
30
26
  }
31
27
  },
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "typecheck": "tsc --noEmit",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "clean": "rm -rf dist"
34
+ },
32
35
  "dependencies": {
33
- "@prismer/sandbox-runtime": "^1.9.0",
34
- "@prismer/sdk": "^1.9.0",
35
- "@prismer/wire": "^0.1.0",
36
36
  "@iarna/toml": "^2.2.5",
37
- "better-sqlite3": "^11.0.0",
38
- "js-yaml": "^4.1.0",
39
- "picocolors": "^1.1.1",
40
- "@clack/prompts": "^0.9.1",
37
+ "better-sqlite3": "^11.8.0",
41
38
  "commander": "^12.1.0",
42
39
  "qrcode": "^1.5.4",
43
- "ws": "^8.18.0"
40
+ "ws": "^8.19.0",
41
+ "yaml": "^2.8.2",
42
+ "zod": "^3.23.8"
44
43
  },
45
44
  "devDependencies": {
46
- "@types/better-sqlite3": "^7.6.0",
47
- "@types/js-yaml": "^4.0.9",
48
- "@types/node": "^20.0.0",
45
+ "@types/better-sqlite3": "^7.6.12",
46
+ "@types/node": "^20.19.37",
49
47
  "@types/qrcode": "^1.5.5",
50
- "@types/ws": "^8.5.13",
51
- "tsup": "^8.0.0",
52
- "typescript": "^5.0.0",
53
- "vitest": "^1.0.0"
48
+ "@types/ws": "^8.18.1",
49
+ "tsup": "^8.5.1",
50
+ "typescript": "5.9.3",
51
+ "vitest": "^2.1.0"
54
52
  },
55
53
  "publishConfig": {
56
54
  "registry": "https://registry.npmjs.org/",
@@ -61,12 +59,12 @@
61
59
  "url": "https://github.com/Prismer-AI/PrismerCloud.git",
62
60
  "directory": "sdk/prismer-cloud/runtime"
63
61
  },
64
- "author": "Prismer",
65
62
  "license": "MIT",
66
63
  "keywords": [
67
64
  "prismer",
68
65
  "daemon",
69
66
  "agent",
70
- "runtime"
67
+ "runtime",
68
+ "adapter"
71
69
  ]
72
70
  }
@@ -1,8 +0,0 @@
1
- import {
2
- AdapterRegistry
3
- } from "./chunk-VTFKZAUY.mjs";
4
- import "./chunk-BJTO5JO5.mjs";
5
- export {
6
- AdapterRegistry
7
- };
8
- //# sourceMappingURL=adapter-registry-EMZFKFYK.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,159 +0,0 @@
1
- import "./chunk-BJTO5JO5.mjs";
2
-
3
- // src/artifacts-uploader.ts
4
- import * as fs from "fs";
5
- import * as path from "path";
6
- var MAX_SIMPLE_UPLOAD_BYTES = 10 * 1024 * 1024;
7
- var ArtifactUploader = class {
8
- apiKey;
9
- cloudApiBase;
10
- fetchImpl;
11
- constructor(opts) {
12
- if (!opts.apiKey) throw new Error("ArtifactUploader: apiKey required");
13
- if (!opts.cloudApiBase) throw new Error("ArtifactUploader: cloudApiBase required");
14
- this.apiKey = opts.apiKey;
15
- this.cloudApiBase = opts.cloudApiBase.replace(/\/+$/, "");
16
- this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;
17
- if (typeof this.fetchImpl !== "function") {
18
- throw new Error("ArtifactUploader: fetch is not available \u2014 pass fetchImpl");
19
- }
20
- }
21
- /**
22
- * Upload one artifact for the given task. Returns:
23
- * - `{ ok:true, uploadId, cdnUrl }` on success.
24
- * - `{ ok:false, error }` on any failure — never throws.
25
- */
26
- async upload(taskId, artifactPath, options = {}) {
27
- if (!taskId) return { ok: false, error: "taskId required" };
28
- if (!artifactPath) return { ok: false, error: "artifactPath required" };
29
- let buffer;
30
- let stat;
31
- try {
32
- stat = fs.statSync(artifactPath);
33
- if (!stat.isFile()) {
34
- return { ok: false, error: `artifact is not a file: ${artifactPath}` };
35
- }
36
- if (stat.size > MAX_SIMPLE_UPLOAD_BYTES) {
37
- return { ok: false, error: `artifact_too_large:${stat.size}` };
38
- }
39
- buffer = await fs.promises.readFile(artifactPath);
40
- } catch (err) {
41
- return { ok: false, error: `read_failed: ${err.message}` };
42
- }
43
- const fileName = options.fileName ?? path.basename(artifactPath);
44
- const mimeType = options.mimeType ?? guessMime(fileName);
45
- let presigned;
46
- try {
47
- const res = await this.fetchImpl(`${this.cloudApiBase}/api/im/artifacts/presign`, {
48
- method: "POST",
49
- headers: {
50
- "Content-Type": "application/json",
51
- Authorization: `Bearer ${this.apiKey}`
52
- },
53
- body: JSON.stringify({
54
- taskId,
55
- fileName,
56
- fileSize: stat.size,
57
- mimeType
58
- })
59
- });
60
- if (!res.ok) {
61
- const text = await safeText(res);
62
- return { ok: false, error: `presign_${res.status}: ${text}` };
63
- }
64
- const body = await res.json();
65
- if (!body.ok || !body.data) {
66
- return { ok: false, error: `presign_invalid_response: ${body.error ?? "no data"}` };
67
- }
68
- presigned = body.data;
69
- } catch (err) {
70
- return { ok: false, error: `presign_threw: ${err.message}` };
71
- }
72
- try {
73
- const form = new FormData();
74
- for (const [k, v] of Object.entries(presigned.fields ?? {})) {
75
- form.append(k, v);
76
- }
77
- const uploadUrl = presigned.url.startsWith("http") ? presigned.url : `${this.cloudApiBase}${presigned.url}`;
78
- const blob = new Blob([buffer], { type: mimeType });
79
- form.append("file", blob, fileName);
80
- const headers = {};
81
- if (!presigned.url.startsWith("http")) {
82
- headers["Authorization"] = `Bearer ${this.apiKey}`;
83
- }
84
- const res = await this.fetchImpl(uploadUrl, {
85
- method: "POST",
86
- headers,
87
- body: form
88
- });
89
- if (!res.ok) {
90
- const text = await safeText(res);
91
- return { ok: false, error: `upload_${res.status}: ${text}` };
92
- }
93
- } catch (err) {
94
- return { ok: false, error: `upload_threw: ${err.message}` };
95
- }
96
- try {
97
- const res = await this.fetchImpl(`${this.cloudApiBase}/api/im/artifacts/confirm`, {
98
- method: "POST",
99
- headers: {
100
- "Content-Type": "application/json",
101
- Authorization: `Bearer ${this.apiKey}`
102
- },
103
- body: JSON.stringify({ uploadId: presigned.uploadId })
104
- });
105
- if (!res.ok) {
106
- const text = await safeText(res);
107
- return { ok: false, error: `confirm_${res.status}: ${text}` };
108
- }
109
- const body = await res.json();
110
- if (!body.ok || !body.data) {
111
- return { ok: false, error: `confirm_invalid_response: ${body.error ?? "no data"}` };
112
- }
113
- return { ok: true, uploadId: body.data.uploadId, cdnUrl: body.data.cdnUrl };
114
- } catch (err) {
115
- return { ok: false, error: `confirm_threw: ${err.message}` };
116
- }
117
- }
118
- };
119
- async function safeText(res) {
120
- try {
121
- return (await res.text()).slice(0, 500);
122
- } catch {
123
- return "<no body>";
124
- }
125
- }
126
- function guessMime(fileName) {
127
- const ext = path.extname(fileName).toLowerCase();
128
- switch (ext) {
129
- case ".png":
130
- return "image/png";
131
- case ".jpg":
132
- case ".jpeg":
133
- return "image/jpeg";
134
- case ".gif":
135
- return "image/gif";
136
- case ".webp":
137
- return "image/webp";
138
- case ".pdf":
139
- return "application/pdf";
140
- case ".json":
141
- return "application/json";
142
- case ".txt":
143
- return "text/plain";
144
- case ".md":
145
- return "text/markdown";
146
- case ".zip":
147
- return "application/zip";
148
- case ".html":
149
- return "text/html";
150
- case ".csv":
151
- return "text/csv";
152
- default:
153
- return "application/octet-stream";
154
- }
155
- }
156
- export {
157
- ArtifactUploader
158
- };
159
- //# sourceMappingURL=artifacts-uploader-N2HNBSTW.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/artifacts-uploader.ts"],"sourcesContent":["/**\n * Prismer Runtime — Artifact Uploader (v1.9.x Task 2).\n *\n * After an adapter returns from `task.dispatch` with `artifacts: [{path, …}]`,\n * the daemon calls `ArtifactUploader.upload(taskId, path)` for each artifact.\n *\n * The uploader is intentionally defensive — failures at any step return a\n * structured `{ ok:false, error }` rather than throwing, so they never block\n * the rpc.response back to the cloud. The cloud already sees the artifact\n * paths in the dispatch result; the upload is best-effort enrichment that\n * lets iOS Library show downloadable files instead of agent-local paths.\n *\n * Three-step flow:\n * 1. POST /api/im/artifacts/presign → { uploadId, url, fields }\n * 2. POST <url> (multipart form) → S3 (or dev-upload) accepts bytes\n * 3. POST /api/im/artifacts/confirm → { uploadId, cdnUrl, … }\n *\n * The presign envelope includes both a presigned-POST `url` (S3) AND a set\n * of form fields that must be sent verbatim. We post them as multipart/form-data\n * with `file` last (S3 spec requirement).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * Cloud-side simple-upload limit (`FileService.presign` enforces 10 MiB). We\n * mirror it client-side so the daemon doesn't buffer hundreds of MB into\n * memory just to be rejected by presign — a 500MB log dump from an adapter\n * would OOM the daemon long before the cloud got a chance to say no.\n */\nconst MAX_SIMPLE_UPLOAD_BYTES = 10 * 1024 * 1024;\n\nexport interface ArtifactUploaderOptions {\n /** Cloud API key (sk-prismer-…) for the calling user/agent. */\n apiKey: string;\n /** Cloud API base URL, e.g. \"https://prismer.cloud\" or \"https://cloud.prismer.dev\". */\n cloudApiBase: string;\n /** Optional fetch impl override — useful for tests. Defaults to globalThis.fetch. */\n fetchImpl?: typeof fetch;\n}\n\nexport interface ArtifactUploadOptions {\n /** Override the file name sent in the presign request. Defaults to basename(path). */\n fileName?: string;\n /** Override the MIME type. Defaults to application/octet-stream when unknown. */\n mimeType?: string;\n}\n\nexport type ArtifactUploadResult =\n | { ok: true; uploadId: string; cdnUrl: string }\n | { ok: false; error: string };\n\ninterface PresignResponse {\n uploadId: string;\n url: string;\n fields: Record<string, string>;\n expiresAt: string;\n taskId: string;\n}\n\ninterface ConfirmResponse {\n uploadId: string;\n cdnUrl: string;\n fileName: string;\n fileSize: number;\n mimeType: string;\n sha256: string | null;\n cost: number;\n}\n\nexport class ArtifactUploader {\n private readonly apiKey: string;\n private readonly cloudApiBase: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(opts: ArtifactUploaderOptions) {\n if (!opts.apiKey) throw new Error('ArtifactUploader: apiKey required');\n if (!opts.cloudApiBase) throw new Error('ArtifactUploader: cloudApiBase required');\n this.apiKey = opts.apiKey;\n this.cloudApiBase = opts.cloudApiBase.replace(/\\/+$/, '');\n this.fetchImpl = opts.fetchImpl ?? (globalThis.fetch as typeof fetch);\n if (typeof this.fetchImpl !== 'function') {\n throw new Error('ArtifactUploader: fetch is not available — pass fetchImpl');\n }\n }\n\n /**\n * Upload one artifact for the given task. Returns:\n * - `{ ok:true, uploadId, cdnUrl }` on success.\n * - `{ ok:false, error }` on any failure — never throws.\n */\n async upload(\n taskId: string,\n artifactPath: string,\n options: ArtifactUploadOptions = {},\n ): Promise<ArtifactUploadResult> {\n if (!taskId) return { ok: false, error: 'taskId required' };\n if (!artifactPath) return { ok: false, error: 'artifactPath required' };\n\n // 0. Stat the file (and read into memory — artifacts in v1.9 are <= 10MB\n // per the simple-upload limit; multipart streaming is Sprint C+).\n //\n // We stat FIRST and refuse oversized files before the read, so a 500MB\n // adapter dump can't OOM the daemon. The cloud presign enforces the\n // same limit, but only after we've buffered the whole file.\n let buffer: Buffer;\n let stat: fs.Stats;\n try {\n stat = fs.statSync(artifactPath);\n if (!stat.isFile()) {\n return { ok: false, error: `artifact is not a file: ${artifactPath}` };\n }\n if (stat.size > MAX_SIMPLE_UPLOAD_BYTES) {\n return { ok: false, error: `artifact_too_large:${stat.size}` };\n }\n // Async read to keep the event loop responsive for other tasks the\n // daemon is concurrently dispatching.\n buffer = await fs.promises.readFile(artifactPath);\n } catch (err) {\n return { ok: false, error: `read_failed: ${(err as Error).message}` };\n }\n\n const fileName = options.fileName ?? path.basename(artifactPath);\n const mimeType = options.mimeType ?? guessMime(fileName);\n\n // 1. Presign.\n let presigned: PresignResponse;\n try {\n const res = await this.fetchImpl(`${this.cloudApiBase}/api/im/artifacts/presign`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n taskId,\n fileName,\n fileSize: stat.size,\n mimeType,\n }),\n });\n if (!res.ok) {\n const text = await safeText(res);\n return { ok: false, error: `presign_${res.status}: ${text}` };\n }\n const body = (await res.json()) as { ok?: boolean; data?: PresignResponse; error?: string };\n if (!body.ok || !body.data) {\n return { ok: false, error: `presign_invalid_response: ${body.error ?? 'no data'}` };\n }\n presigned = body.data;\n } catch (err) {\n return { ok: false, error: `presign_threw: ${(err as Error).message}` };\n }\n\n // 2. POST the file to the presigned URL. The fields object is sent first\n // as form fields, then `file` last (S3 presigned-POST requirement).\n try {\n const form = new FormData();\n for (const [k, v] of Object.entries(presigned.fields ?? {})) {\n form.append(k, v);\n }\n // Resolve the upload URL: presign returns a relative path in dev mode\n // (e.g. `/api/im/files/dev-upload/<id>`). Make it absolute against\n // cloudApiBase so the same flow works for dev + S3.\n const uploadUrl = presigned.url.startsWith('http')\n ? presigned.url\n : `${this.cloudApiBase}${presigned.url}`;\n\n // `Buffer` is BodyInit-compatible via Blob (works on Node 20+).\n const blob = new Blob([buffer], { type: mimeType });\n form.append('file', blob, fileName);\n\n // Dev-upload route requires an Authorization header. S3 presigned POST\n // does NOT — including it is harmless because S3 ignores unknown\n // headers, but we omit the auth header for non-relative URLs to avoid\n // accidental signature interference.\n const headers: Record<string, string> = {};\n if (!presigned.url.startsWith('http')) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n const res = await this.fetchImpl(uploadUrl, {\n method: 'POST',\n headers,\n body: form,\n });\n if (!res.ok) {\n const text = await safeText(res);\n return { ok: false, error: `upload_${res.status}: ${text}` };\n }\n } catch (err) {\n return { ok: false, error: `upload_threw: ${(err as Error).message}` };\n }\n\n // 3. Confirm.\n try {\n const res = await this.fetchImpl(`${this.cloudApiBase}/api/im/artifacts/confirm`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ uploadId: presigned.uploadId }),\n });\n if (!res.ok) {\n const text = await safeText(res);\n return { ok: false, error: `confirm_${res.status}: ${text}` };\n }\n const body = (await res.json()) as { ok?: boolean; data?: ConfirmResponse; error?: string };\n if (!body.ok || !body.data) {\n return { ok: false, error: `confirm_invalid_response: ${body.error ?? 'no data'}` };\n }\n return { ok: true, uploadId: body.data.uploadId, cdnUrl: body.data.cdnUrl };\n } catch (err) {\n return { ok: false, error: `confirm_threw: ${(err as Error).message}` };\n }\n }\n}\n\n// ─── Helpers ────────────────────────────────────────────\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return (await res.text()).slice(0, 500);\n } catch {\n return '<no body>';\n }\n}\n\n/** Minimal MIME guess — covers the common artifact extensions. */\nfunction guessMime(fileName: string): string {\n const ext = path.extname(fileName).toLowerCase();\n switch (ext) {\n case '.png':\n return 'image/png';\n case '.jpg':\n case '.jpeg':\n return 'image/jpeg';\n case '.gif':\n return 'image/gif';\n case '.webp':\n return 'image/webp';\n case '.pdf':\n return 'application/pdf';\n case '.json':\n return 'application/json';\n case '.txt':\n return 'text/plain';\n case '.md':\n return 'text/markdown';\n case '.zip':\n return 'application/zip';\n case '.html':\n return 'text/html';\n case '.csv':\n return 'text/csv';\n default:\n return 'application/octet-stream';\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,204 +0,0 @@
1
- import {
2
- AGENT_CATALOG
3
- } from "./chunk-JIEDFDVI.mjs";
4
- import "./chunk-BJTO5JO5.mjs";
5
-
6
- // src/adapters/cli-adapter.ts
7
- import { spawn } from "child_process";
8
- function createCliAdapter(config) {
9
- const promptVia = config.promptVia ?? "last-arg";
10
- const timeoutMs = config.timeoutMs ?? 5 * 60 * 1e3;
11
- return {
12
- name: config.name,
13
- tiersSupported: config.tiersSupported,
14
- capabilityTags: config.capabilityTags,
15
- metadata: { binary: config.binary, promptVia },
16
- async dispatch(input) {
17
- return await runCliOnce(config, input, { promptVia, timeoutMs });
18
- },
19
- async health() {
20
- return { healthy: true };
21
- },
22
- async reset(_agentName) {
23
- return { ok: true, state: "stateless_noop" };
24
- }
25
- };
26
- }
27
- function runCliOnce(config, input, runOpts) {
28
- return new Promise((resolve) => {
29
- const args = [...config.baseArgs ?? []];
30
- if (runOpts.promptVia === "last-arg") {
31
- args.push(input.prompt);
32
- }
33
- const spawnOpts = {
34
- cwd: config.cwd,
35
- env: { ...process.env, ...config.env ?? {} },
36
- stdio: ["pipe", "pipe", "pipe"]
37
- };
38
- let child;
39
- try {
40
- child = spawn(config.binary, args, spawnOpts);
41
- } catch (err) {
42
- resolve({ ok: false, error: `spawn_failed:${err.message}` });
43
- return;
44
- }
45
- if (runOpts.promptVia === "stdin") {
46
- child.stdin?.end(input.prompt, "utf8");
47
- } else if (child.stdin) {
48
- child.stdin.end();
49
- }
50
- let stdoutBuf = "";
51
- let stderrBuf = "";
52
- const MAX_BUF = 8 * 1024 * 1024;
53
- child.stdout?.on("data", (chunk) => {
54
- if (stdoutBuf.length < MAX_BUF) stdoutBuf += chunk.toString();
55
- });
56
- child.stderr?.on("data", (chunk) => {
57
- if (stderrBuf.length < MAX_BUF) stderrBuf += chunk.toString();
58
- });
59
- let timedOut = false;
60
- const timer = setTimeout(() => {
61
- timedOut = true;
62
- try {
63
- child.kill("SIGTERM");
64
- } catch {
65
- }
66
- }, runOpts.timeoutMs);
67
- if (typeof timer.unref === "function") {
68
- timer.unref();
69
- }
70
- child.on("error", (err) => {
71
- clearTimeout(timer);
72
- resolve({ ok: false, error: `spawn_error:${err.message}`, output: stderrBuf || void 0 });
73
- });
74
- child.on("exit", (code, signal) => {
75
- clearTimeout(timer);
76
- if (timedOut) {
77
- resolve({
78
- ok: false,
79
- error: `timeout:${runOpts.timeoutMs}ms`,
80
- output: stdoutBuf || void 0,
81
- metadata: { stderr: stderrBuf, signal }
82
- });
83
- return;
84
- }
85
- if (code === 0) {
86
- resolve({ ok: true, output: stdoutBuf, metadata: { stderr: stderrBuf, exitCode: 0 } });
87
- } else {
88
- resolve({
89
- ok: false,
90
- error: `nonzero_exit:${code ?? signal ?? "unknown"}`,
91
- output: stdoutBuf || void 0,
92
- metadata: { stderr: stderrBuf, exitCode: code, signal }
93
- });
94
- }
95
- });
96
- });
97
- }
98
-
99
- // src/adapters/auto-register.ts
100
- var PER_ADAPTER_DEFAULTS = {
101
- hermes: { promptVia: "stdin" }
102
- };
103
- var defaultHermesModeBResolver = async (registry) => {
104
- let mod;
105
- try {
106
- const specifier = "@prismer/adapter-hermes";
107
- mod = await import(specifier);
108
- } catch (err) {
109
- const code = err?.code;
110
- if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
111
- return null;
112
- }
113
- return {
114
- installed: false,
115
- reason: `import_failed:${err.message}`
116
- };
117
- }
118
- try {
119
- const res = await mod.autoRegisterHermes(registry);
120
- return {
121
- installed: res.installed,
122
- loopbackUrl: res.loopbackUrl,
123
- reason: res.reason
124
- };
125
- } catch (err) {
126
- return {
127
- installed: false,
128
- reason: `probe_failed:${err.message}`
129
- };
130
- }
131
- };
132
- async function autoRegisterAdapters(registry, opts = {}) {
133
- const catalog = opts.catalog ?? AGENT_CATALOG;
134
- const result = { registered: [], skipped: [] };
135
- for (const entry of catalog) {
136
- if (registry.has(entry.name)) {
137
- result.skipped.push({ name: entry.name, reason: "already_registered" });
138
- continue;
139
- }
140
- let binary;
141
- if (opts.forceBinaryFor && opts.forceBinaryFor[entry.name]) {
142
- binary = opts.forceBinaryFor[entry.name];
143
- } else if (opts.skipDetection) {
144
- binary = entry.upstreamBinary;
145
- } else {
146
- try {
147
- const detected = await entry.detect();
148
- if (!detected.found || !detected.binaryPath) {
149
- result.skipped.push({ name: entry.name, reason: "not_installed" });
150
- continue;
151
- }
152
- binary = detected.binaryPath;
153
- } catch (err) {
154
- result.skipped.push({ name: entry.name, reason: `detect_failed:${err.message}` });
155
- continue;
156
- }
157
- }
158
- const baseConfig = {
159
- name: entry.name,
160
- binary,
161
- tiersSupported: entry.tiersSupported,
162
- capabilityTags: entry.capabilityTags,
163
- ...PER_ADAPTER_DEFAULTS[entry.name] ?? {},
164
- ...opts.configOverrides?.[entry.name] ?? {}
165
- };
166
- const adapter = createCliAdapter(baseConfig);
167
- try {
168
- registry.register(adapter);
169
- result.registered.push({ name: entry.name, binary, source: "cli_shim" });
170
- } catch (err) {
171
- result.skipped.push({ name: entry.name, reason: `register_failed:${err.message}` });
172
- }
173
- }
174
- const hermesEntryIdx = result.registered.findIndex((r) => r.name === "hermes");
175
- if (hermesEntryIdx >= 0 && !opts.skipHermesModeB) {
176
- const resolve = opts.resolveHermesModeB ?? defaultHermesModeBResolver;
177
- try {
178
- const probe = await resolve(registry);
179
- if (probe === null) {
180
- console.log(
181
- "[AutoRegister] @prismer/adapter-hermes not installed; Hermes stays on CLI shim"
182
- );
183
- } else if (probe.installed) {
184
- result.registered[hermesEntryIdx].source = "mode_b";
185
- console.log(
186
- `[AutoRegister] Hermes upgraded to Mode B at ${probe.loopbackUrl ?? "loopback"}`
187
- );
188
- } else {
189
- console.log(
190
- `[AutoRegister] Hermes Mode B unavailable (${probe.reason ?? "unknown"}); keeping CLI shim`
191
- );
192
- }
193
- } catch (err) {
194
- console.warn(
195
- `[AutoRegister] Hermes Mode B probe threw: ${err.message}; keeping CLI shim`
196
- );
197
- }
198
- }
199
- return result;
200
- }
201
- export {
202
- autoRegisterAdapters
203
- };
204
- //# sourceMappingURL=auto-register-M4YSPJS6.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/cli-adapter.ts","../src/adapters/auto-register.ts"],"sourcesContent":["/**\n * Prismer Runtime — Generic CLI-spawn adapter factory (Sprint C1/C2)\n *\n * Most agent CLIs (Claude Code, Codex, OpenClaw, Hermes) expose\n * roughly the same interface: `binary <flags> <prompt>` produces output\n * on stdout. This factory builds an AdapterImpl by capturing those\n * conventions in a single, testable place.\n *\n * Each per-adapter file under this directory specializes the shape of\n * the spawn (flag set, env, working directory, stdin vs argv prompt)\n * and registers itself with the central AdapterRegistry on daemon\n * startup via auto-register.ts.\n *\n * What this factory does NOT do (yet):\n * - Streaming output. dispatch() resolves once the child exits;\n * incremental token streams need the cloud relay opcode 0x01\n * bridge, which lands in a follow-up sprint.\n * - Permission gating. The PARA Tier 5 approval gate runs before\n * dispatch is called, not inside it.\n */\n\nimport { spawn, type SpawnOptions } from 'node:child_process';\nimport type { AdapterImpl, AdapterDispatchInput, AdapterDispatchResult } from '../adapter-registry.js';\n\nexport interface CliAdapterConfig {\n /** Catalog name — used as the adapter id. */\n name: string;\n /** Path to the binary. Resolved once at registration time. */\n binary: string;\n /** Base flags prepended before the prompt. */\n baseArgs?: string[];\n /** PARA tiers this adapter can host. */\n tiersSupported: number[];\n /** Capability tags this adapter can satisfy. */\n capabilityTags: string[];\n /** How to pass the prompt to the binary. */\n promptVia?: 'stdin' | 'last-arg';\n /** Soft timeout in ms. Defaults to 5 minutes. */\n timeoutMs?: number;\n /** Working directory for the child process. */\n cwd?: string;\n /** Extra env vars to inject. */\n env?: Record<string, string>;\n}\n\nexport function createCliAdapter(config: CliAdapterConfig): AdapterImpl {\n const promptVia = config.promptVia ?? 'last-arg';\n const timeoutMs = config.timeoutMs ?? 5 * 60 * 1000;\n\n return {\n name: config.name,\n tiersSupported: config.tiersSupported,\n capabilityTags: config.capabilityTags,\n metadata: { binary: config.binary, promptVia },\n async dispatch(input: AdapterDispatchInput): Promise<AdapterDispatchResult> {\n return await runCliOnce(config, input, { promptVia, timeoutMs });\n },\n async health() {\n // Cheap probe — the binary path was resolved at registration time,\n // so just confirm we can still find it. Heavier probes would slow\n // every health request and hide a stalled adapter behind a passing\n // probe; we leave deeper liveness checks to the agent itself.\n return { healthy: true };\n },\n async reset(_agentName?: string) {\n // Each `dispatch` spawns a fresh child process; there is no persistent\n // per-agent state to clear. Report stateless_noop so the caller can\n // ack cleanly. v1.9.x agent_restart semantic — see\n // adapter-registry.ts::AdapterImpl.reset for the contract.\n return { ok: true, state: 'stateless_noop' };\n },\n };\n}\n\ninterface RunOpts {\n promptVia: 'stdin' | 'last-arg';\n timeoutMs: number;\n}\n\nfunction runCliOnce(\n config: CliAdapterConfig,\n input: AdapterDispatchInput,\n runOpts: RunOpts,\n): Promise<AdapterDispatchResult> {\n return new Promise((resolve) => {\n const args = [...(config.baseArgs ?? [])];\n if (runOpts.promptVia === 'last-arg') {\n args.push(input.prompt);\n }\n\n const spawnOpts: SpawnOptions = {\n cwd: config.cwd,\n env: { ...process.env, ...(config.env ?? {}) },\n stdio: ['pipe', 'pipe', 'pipe'],\n };\n\n let child;\n try {\n child = spawn(config.binary, args, spawnOpts);\n } catch (err) {\n resolve({ ok: false, error: `spawn_failed:${(err as Error).message}` });\n return;\n }\n\n if (runOpts.promptVia === 'stdin') {\n child.stdin?.end(input.prompt, 'utf8');\n } else if (child.stdin) {\n child.stdin.end();\n }\n\n let stdoutBuf = '';\n let stderrBuf = '';\n const MAX_BUF = 8 * 1024 * 1024; // 8 MiB cap so a runaway log doesn't OOM the daemon\n\n child.stdout?.on('data', (chunk) => {\n if (stdoutBuf.length < MAX_BUF) stdoutBuf += chunk.toString();\n });\n child.stderr?.on('data', (chunk) => {\n if (stderrBuf.length < MAX_BUF) stderrBuf += chunk.toString();\n });\n\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n try {\n child.kill('SIGTERM');\n } catch {\n // child may have already exited\n }\n }, runOpts.timeoutMs);\n if (typeof (timer as unknown as { unref: () => void }).unref === 'function') {\n (timer as unknown as { unref: () => void }).unref();\n }\n\n child.on('error', (err) => {\n clearTimeout(timer);\n resolve({ ok: false, error: `spawn_error:${err.message}`, output: stderrBuf || undefined });\n });\n\n child.on('exit', (code, signal) => {\n clearTimeout(timer);\n if (timedOut) {\n resolve({\n ok: false,\n error: `timeout:${runOpts.timeoutMs}ms`,\n output: stdoutBuf || undefined,\n metadata: { stderr: stderrBuf, signal },\n });\n return;\n }\n if (code === 0) {\n resolve({ ok: true, output: stdoutBuf, metadata: { stderr: stderrBuf, exitCode: 0 } });\n } else {\n resolve({\n ok: false,\n error: `nonzero_exit:${code ?? signal ?? 'unknown'}`,\n output: stdoutBuf || undefined,\n metadata: { stderr: stderrBuf, exitCode: code, signal },\n });\n }\n });\n });\n}\n","/**\n * Prismer Runtime — Auto-register installed adapters (Sprint C1/C2).\n *\n * On daemon startup, walk the agent catalog, detect which agents are\n * actually installed on this machine, and register an AdapterImpl\n * (built via createCliAdapter) for each. This means cloud's TaskRouter\n * can immediately dispatch to whatever the user has installed without\n * any per-adapter wiring code in daemon-runner.\n *\n * Returns the list of registered adapter names (not just descriptors)\n * so the caller can log which ones came online.\n *\n * Per-adapter overrides\n * ClaudeCode is the reference shape — see the per-adapter map below\n * for any divergences (different CLI flag conventions, prompt path).\n * Adapter teams that need richer integration should publish their\n * own AdapterImpl module + register on init; this auto-registrar\n * then yields to the user-supplied implementation.\n *\n * Hermes Mode B upgrade\n * Hermes is special-cased: after the CLI shim is registered we opt-in\n * to probe the local Hermes HTTP loopback (`@prismer/adapter-hermes`)\n * and, if reachable, swap the CLI shim for the Mode B adapter. The\n * `@prismer/adapter-hermes` dependency is resolved via dynamic import\n * and is intentionally NOT listed in this package's dependencies —\n * users opt in by installing it alongside @prismer/runtime. The\n * fallback chain is: Mode B (if reachable) → CLI shim → nothing.\n * Swap failures never break CLI-shim registration.\n */\n\nimport { AGENT_CATALOG } from '../agents/registry.js';\nimport type { AdapterRegistry, AdapterImpl } from '../adapter-registry.js';\nimport { createCliAdapter, type CliAdapterConfig } from './cli-adapter.js';\n\n/**\n * Result of probing `@prismer/adapter-hermes` for a reachable Mode B\n * endpoint. `installed: true` means a Mode B AdapterImpl was registered\n * (replacing any CLI shim); anything else is a no-op.\n */\nexport interface HermesModeBProbeResult {\n installed: boolean;\n loopbackUrl?: string;\n reason?: string;\n}\n\n/**\n * Seam used to resolve + invoke `@prismer/adapter-hermes`. Production\n * code defaults to a dynamic-import-based resolver; tests inject their\n * own to exercise the hit / miss / not-installed branches without\n * depending on a live Hermes instance or the real package being on disk.\n */\nexport type HermesModeBResolver = (\n registry: AdapterRegistry,\n) => Promise<HermesModeBProbeResult | null>;\n\nexport interface AutoRegisterOptions {\n /** When true, skip detection and assume every catalog entry is installed.\n * Useful for tests + local dev where you want the registration path\n * to run end-to-end without binaries being present. */\n skipDetection?: boolean;\n /** Override the catalog (for tests). Defaults to AGENT_CATALOG. */\n catalog?: typeof AGENT_CATALOG;\n /** Map of adapter-name → spawn config overrides. Useful when an\n * adapter ships its own dispatch shape (different flags, stdin, etc.). */\n configOverrides?: Record<string, Partial<CliAdapterConfig>>;\n /** Override binary detection result entirely (tests). */\n forceBinaryFor?: Record<string, string>;\n /** Disable the Hermes Mode B upgrade probe — force the CLI shim. Default false. */\n skipHermesModeB?: boolean;\n /** Injectable probe for Hermes Mode B. Defaults to dynamic-importing\n * `@prismer/adapter-hermes` and calling its `autoRegisterHermes()`. */\n resolveHermesModeB?: HermesModeBResolver;\n}\n\nexport interface RegisteredEntry {\n name: string;\n binary: string;\n /** Which code path produced this registration. Defaults to \"cli_shim\"\n * for adapters registered by the generic catalog path; \"mode_b\" means\n * a per-adapter module (currently only Hermes) replaced the shim. */\n source?: 'cli_shim' | 'mode_b';\n}\n\nexport interface AutoRegisterResult {\n registered: RegisteredEntry[];\n skipped: Array<{ name: string; reason: string }>;\n}\n\n/**\n * Per-adapter quirks. Default behavior (`promptVia: 'last-arg'`) works\n * for Claude Code + Codex + OpenClaw out of the box. Hermes is a Python\n * server and prefers stdin; the contract is documented separately\n * (Sprint C3) — this map applies to the local CLI shim only.\n */\nconst PER_ADAPTER_DEFAULTS: Record<string, Partial<CliAdapterConfig>> = {\n hermes: { promptVia: 'stdin' },\n};\n\n/**\n * Default resolver: dynamic-import `@prismer/adapter-hermes` and invoke\n * its autoRegisterHermes(). Returns `null` when the package isn't\n * installed (`ERR_MODULE_NOT_FOUND` / `MODULE_NOT_FOUND`). Any other\n * failure is surfaced as `{ installed: false, reason }` so the caller\n * can log it but keep the CLI shim.\n */\n// Shape of the bit of `@prismer/adapter-hermes` we consume. Kept as a\n// local type so this package doesn't need a compile-time dependency on\n// the adapter (it's dynamic-imported at runtime and not listed in deps).\ninterface HermesAdapterModule {\n autoRegisterHermes(\n registry: unknown,\n opts?: unknown,\n ): Promise<{\n installed: boolean;\n loopbackUrl?: string;\n reason?: string;\n }>;\n}\n\nconst defaultHermesModeBResolver: HermesModeBResolver = async (registry) => {\n let mod: HermesAdapterModule;\n try {\n // NOTE: @prismer/adapter-hermes is NOT listed in this package's deps —\n // users install it separately if they want Hermes Mode B. A missing\n // module here is the expected common case, not an error. The\n // specifier goes through a variable so bundlers don't try to resolve\n // it eagerly at build time.\n const specifier = '@prismer/adapter-hermes';\n mod = (await import(specifier)) as HermesAdapterModule;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {\n return null;\n }\n return {\n installed: false,\n reason: `import_failed:${(err as Error).message}`,\n };\n }\n\n try {\n const res = await mod.autoRegisterHermes(registry);\n return {\n installed: res.installed,\n loopbackUrl: res.loopbackUrl,\n reason: res.reason,\n };\n } catch (err) {\n return {\n installed: false,\n reason: `probe_failed:${(err as Error).message}`,\n };\n }\n};\n\nexport async function autoRegisterAdapters(\n registry: AdapterRegistry,\n opts: AutoRegisterOptions = {},\n): Promise<AutoRegisterResult> {\n const catalog = opts.catalog ?? AGENT_CATALOG;\n const result: AutoRegisterResult = { registered: [], skipped: [] };\n\n for (const entry of catalog) {\n // If the adapter is already registered (e.g. an out-of-band module\n // beat us to it), respect that — don't replace user code with a stub.\n if (registry.has(entry.name)) {\n result.skipped.push({ name: entry.name, reason: 'already_registered' });\n continue;\n }\n\n let binary: string | undefined;\n if (opts.forceBinaryFor && opts.forceBinaryFor[entry.name]) {\n binary = opts.forceBinaryFor[entry.name];\n } else if (opts.skipDetection) {\n // Fake it — use the catalog's `upstreamBinary` as if `which` returned it.\n binary = entry.upstreamBinary;\n } else {\n try {\n const detected = await entry.detect();\n if (!detected.found || !detected.binaryPath) {\n result.skipped.push({ name: entry.name, reason: 'not_installed' });\n continue;\n }\n binary = detected.binaryPath;\n } catch (err) {\n result.skipped.push({ name: entry.name, reason: `detect_failed:${(err as Error).message}` });\n continue;\n }\n }\n\n const baseConfig: CliAdapterConfig = {\n name: entry.name,\n binary,\n tiersSupported: entry.tiersSupported,\n capabilityTags: entry.capabilityTags,\n ...(PER_ADAPTER_DEFAULTS[entry.name] ?? {}),\n ...(opts.configOverrides?.[entry.name] ?? {}),\n };\n\n const adapter: AdapterImpl = createCliAdapter(baseConfig);\n try {\n registry.register(adapter);\n result.registered.push({ name: entry.name, binary, source: 'cli_shim' });\n } catch (err) {\n result.skipped.push({ name: entry.name, reason: `register_failed:${(err as Error).message}` });\n }\n }\n\n // --- Hermes Mode B upgrade probe ---------------------------------\n // The CLI shim above is the always-available fallback. If the user\n // installed @prismer/adapter-hermes AND has a reachable Hermes gateway,\n // swap the shim for the Mode B adapter. Any failure (package missing,\n // probe timeout, network error, etc.) keeps the CLI shim intact.\n const hermesEntryIdx = result.registered.findIndex((r) => r.name === 'hermes');\n if (hermesEntryIdx >= 0 && !opts.skipHermesModeB) {\n const resolve = opts.resolveHermesModeB ?? defaultHermesModeBResolver;\n try {\n const probe = await resolve(registry);\n if (probe === null) {\n console.log(\n '[AutoRegister] @prismer/adapter-hermes not installed; Hermes stays on CLI shim',\n );\n } else if (probe.installed) {\n result.registered[hermesEntryIdx].source = 'mode_b';\n console.log(\n `[AutoRegister] Hermes upgraded to Mode B at ${probe.loopbackUrl ?? 'loopback'}`,\n );\n } else {\n console.log(\n `[AutoRegister] Hermes Mode B unavailable (${probe.reason ?? 'unknown'}); keeping CLI shim`,\n );\n }\n } catch (err) {\n // Absolute last-resort guard — the default resolver already\n // catches its own errors, so this only trips if a custom resolver\n // throws. Never let it fail the whole auto-register call.\n console.warn(\n `[AutoRegister] Hermes Mode B probe threw: ${(err as Error).message}; keeping CLI shim`,\n );\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}