@prismer/runtime 1.9.6 → 1.9.21
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/CHANGELOG.md +56 -0
- package/dist/cli.cjs +1644 -928
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1469 -751
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1663 -953
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -1
- package/dist/index.d.ts +90 -1
- package/dist/index.js +2136 -1420
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,118 +30,711 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
30
|
-
// src/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
CloudClient: () => CloudClient,
|
|
37
|
-
CloudError: () => CloudError,
|
|
38
|
-
ConfigSchema: () => ConfigSchema,
|
|
39
|
-
LocalServer: () => LocalServer,
|
|
40
|
-
ParseClaimController: () => ParseClaimController,
|
|
41
|
-
Runner: () => Runner,
|
|
42
|
-
ServicePool: () => ServicePool,
|
|
43
|
-
SyncQueue: () => SyncQueue,
|
|
44
|
-
SyncWorker: () => SyncWorker,
|
|
45
|
-
TARGET_SCHEMA_VERSION: () => TARGET_SCHEMA_VERSION,
|
|
46
|
-
UriResolver: () => UriResolver,
|
|
47
|
-
WS_CLOSE: () => WS_CLOSE,
|
|
48
|
-
WorkspaceMirror: () => WorkspaceMirror,
|
|
49
|
-
WsClient: () => WsClient,
|
|
50
|
-
buildProgram: () => buildProgram,
|
|
51
|
-
claudeCodeAdapter: () => claudeCodeAdapter,
|
|
52
|
-
codexAdapter: () => codexAdapter,
|
|
53
|
-
composePrompt: () => composePrompt,
|
|
54
|
-
configExists: () => configExists,
|
|
55
|
-
currentSchemaVersion: () => currentSchemaVersion,
|
|
56
|
-
deriveWsUrl: () => deriveWsUrl,
|
|
57
|
-
envelope: () => envelope,
|
|
58
|
-
getRoleTemplate: () => getRoleTemplate,
|
|
59
|
-
handleDispatch: () => handleDispatch,
|
|
60
|
-
hermesAdapter: () => hermesAdapter,
|
|
61
|
-
isDaemonId: () => isDaemonId,
|
|
62
|
-
listRoleTemplates: () => listRoleTemplates,
|
|
63
|
-
loadConfig: () => loadConfig,
|
|
64
|
-
newDaemonId: () => newDaemonId,
|
|
65
|
-
nextBackoffMs: () => nextBackoffMs,
|
|
66
|
-
openLocalDb: () => openLocalDb,
|
|
67
|
-
pair: () => pair,
|
|
68
|
-
parseCodexOutput: () => parseCodexOutput,
|
|
69
|
-
parseUris: () => parseUris,
|
|
70
|
-
resolvePaths: () => resolvePaths,
|
|
71
|
-
runCli: () => runCli,
|
|
72
|
-
runMigrations: () => runMigrations,
|
|
73
|
-
saveConfig: () => saveConfig
|
|
74
|
-
});
|
|
75
|
-
module.exports = __toCommonJS(src_exports);
|
|
76
|
-
|
|
77
|
-
// src/adapters/registry.ts
|
|
78
|
-
var AdapterRegistry = class {
|
|
79
|
-
adapters = /* @__PURE__ */ new Map();
|
|
80
|
-
register(adapter) {
|
|
81
|
-
if (!adapter.name || typeof adapter.name !== "string") {
|
|
82
|
-
throw new Error("AdapterRegistry.register: adapter.name required");
|
|
83
|
-
}
|
|
84
|
-
const hasDispatch = typeof adapter.dispatch === "function";
|
|
85
|
-
const hasEnsureService = typeof adapter.ensureService === "function";
|
|
86
|
-
if (!hasDispatch && !hasEnsureService) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
`AdapterRegistry.register: adapter "${adapter.name}" must implement dispatch() (interactive) or ensureService() (long-running)`
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
this.adapters.set(adapter.name, adapter);
|
|
92
|
-
}
|
|
93
|
-
unregister(name) {
|
|
94
|
-
return this.adapters.delete(name);
|
|
95
|
-
}
|
|
96
|
-
has(name) {
|
|
97
|
-
return this.adapters.has(name);
|
|
98
|
-
}
|
|
99
|
-
get(name) {
|
|
100
|
-
return this.adapters.get(name);
|
|
33
|
+
// src/cli/ui.ts
|
|
34
|
+
function thisDirname() {
|
|
35
|
+
try {
|
|
36
|
+
return path6.dirname((0, import_node_url.fileURLToPath)(import_meta2.url));
|
|
37
|
+
} catch {
|
|
38
|
+
return process.cwd();
|
|
101
39
|
}
|
|
102
|
-
|
|
103
|
-
|
|
40
|
+
}
|
|
41
|
+
function findIconPath(size = "big") {
|
|
42
|
+
const name = size === "big" ? "icon" : "smallicon";
|
|
43
|
+
const here = thisDirname();
|
|
44
|
+
const candidates = [
|
|
45
|
+
// npm-installed: node_modules/@prismer/runtime/dist/cli.js → ../assets
|
|
46
|
+
path6.resolve(here, "../assets", name),
|
|
47
|
+
// alternate dist layout (sub-bundle): dist/bin/cli.js → ../../assets
|
|
48
|
+
path6.resolve(here, "../../assets", name),
|
|
49
|
+
// source/typecheck: src/cli/ui.ts → ../../assets
|
|
50
|
+
path6.resolve(here, "../../assets", name),
|
|
51
|
+
// dev mode: cwd happens to be runtime root
|
|
52
|
+
path6.resolve(process.cwd(), "assets", name),
|
|
53
|
+
path6.resolve(process.cwd(), "sdk/prismer-cloud/runtime/assets", name)
|
|
54
|
+
];
|
|
55
|
+
for (const candidate of candidates) {
|
|
56
|
+
try {
|
|
57
|
+
if (fs4.existsSync(candidate)) return candidate;
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
104
60
|
}
|
|
105
|
-
|
|
106
|
-
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function getUI() {
|
|
64
|
+
if (!_ui) _ui = new UI();
|
|
65
|
+
return _ui;
|
|
66
|
+
}
|
|
67
|
+
function setUI(ui) {
|
|
68
|
+
_ui = ui;
|
|
69
|
+
}
|
|
70
|
+
function applyCommonFlags(argv) {
|
|
71
|
+
let mode = "pretty";
|
|
72
|
+
const isTTY = process.stdout.isTTY === true;
|
|
73
|
+
const noColorEnv = Boolean(process.env["NO_COLOR"]);
|
|
74
|
+
let color2 = isTTY && !noColorEnv;
|
|
75
|
+
const rest = [];
|
|
76
|
+
for (const arg of argv) {
|
|
77
|
+
switch (arg) {
|
|
78
|
+
case "--no-color":
|
|
79
|
+
color2 = false;
|
|
80
|
+
break;
|
|
81
|
+
case "--color":
|
|
82
|
+
color2 = true;
|
|
83
|
+
break;
|
|
84
|
+
case "--json":
|
|
85
|
+
case "--pretty-json":
|
|
86
|
+
mode = "json";
|
|
87
|
+
if (arg === "--json") rest.push(arg);
|
|
88
|
+
break;
|
|
89
|
+
case "--quiet":
|
|
90
|
+
mode = "quiet";
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
rest.push(arg);
|
|
94
|
+
}
|
|
107
95
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
96
|
+
return { mode, color: color2, restArgv: rest };
|
|
97
|
+
}
|
|
98
|
+
var fs4, path6, import_node_url, import_meta2, BRAILLE_FRAMES, COMPACT_BANNER, UI, _ui;
|
|
99
|
+
var init_ui = __esm({
|
|
100
|
+
"src/cli/ui.ts"() {
|
|
101
|
+
"use strict";
|
|
102
|
+
fs4 = __toESM(require("fs"), 1);
|
|
103
|
+
path6 = __toESM(require("path"), 1);
|
|
104
|
+
import_node_url = require("url");
|
|
105
|
+
import_meta2 = {};
|
|
106
|
+
BRAILLE_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
107
|
+
COMPACT_BANNER = ["\u25C7 PRISMER", " Runtime CLI"];
|
|
108
|
+
UI = class {
|
|
109
|
+
mode;
|
|
110
|
+
colorEnabled;
|
|
111
|
+
stream;
|
|
112
|
+
errStream;
|
|
113
|
+
constructor(opts) {
|
|
114
|
+
this.mode = opts?.mode ?? "pretty";
|
|
115
|
+
this.stream = opts?.stream ?? process.stdout;
|
|
116
|
+
this.errStream = opts?.errStream ?? process.stderr;
|
|
117
|
+
if (opts?.color !== void 0) {
|
|
118
|
+
this.colorEnabled = opts.color;
|
|
119
|
+
} else {
|
|
120
|
+
const isTTY = this.stream.isTTY === true;
|
|
121
|
+
const noColor = Boolean(process.env["NO_COLOR"]);
|
|
122
|
+
this.colorEnabled = isTTY && !noColor;
|
|
123
|
+
}
|
|
121
124
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
// ---- Internal color helpers ----
|
|
126
|
+
ansi(open, close, text) {
|
|
127
|
+
if (!this.colorEnabled) return text;
|
|
128
|
+
return `\x1B[${open}m${text}\x1B[${close}m`;
|
|
126
129
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
green(t) {
|
|
131
|
+
return this.ansi(32, 39, t);
|
|
132
|
+
}
|
|
133
|
+
red(t) {
|
|
134
|
+
return this.ansi(31, 39, t);
|
|
135
|
+
}
|
|
136
|
+
yellow(t) {
|
|
137
|
+
return this.ansi(33, 39, t);
|
|
138
|
+
}
|
|
139
|
+
cyan(t) {
|
|
140
|
+
return this.ansi(36, 39, t);
|
|
141
|
+
}
|
|
142
|
+
dim(t) {
|
|
143
|
+
return this.ansi(2, 22, t);
|
|
144
|
+
}
|
|
145
|
+
bold(t) {
|
|
146
|
+
return this.ansi(1, 22, t);
|
|
147
|
+
}
|
|
148
|
+
gray(t) {
|
|
149
|
+
return this.ansi(90, 39, t);
|
|
150
|
+
}
|
|
151
|
+
brandMark() {
|
|
152
|
+
return this.cyan("\u25C7");
|
|
153
|
+
}
|
|
154
|
+
colorBrandLine(line) {
|
|
155
|
+
let out = "";
|
|
156
|
+
for (const ch of line) {
|
|
157
|
+
if (ch === "\u2592") {
|
|
158
|
+
out += this.cyan(ch);
|
|
159
|
+
} else if (ch === "\u2593") {
|
|
160
|
+
out += this.dim(ch);
|
|
161
|
+
} else {
|
|
162
|
+
out += ch;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
// ---- Core write helpers ----
|
|
168
|
+
write(text) {
|
|
169
|
+
this.stream.write(text);
|
|
170
|
+
}
|
|
171
|
+
writeErr(text) {
|
|
172
|
+
this.errStream.write(text);
|
|
173
|
+
}
|
|
174
|
+
// ---- Level 1: Header ----
|
|
175
|
+
header(text) {
|
|
176
|
+
if (this.mode === "json") return;
|
|
177
|
+
const prefix = text.startsWith("Prismer") ? this.brandMark() + " " : "";
|
|
178
|
+
this.write(prefix + this.bold(text) + "\n");
|
|
179
|
+
}
|
|
180
|
+
smallHeader(subtitle) {
|
|
181
|
+
if (this.mode === "json" || this.mode === "quiet") return;
|
|
182
|
+
const iconPath = findIconPath("small");
|
|
183
|
+
if (iconPath !== null) {
|
|
184
|
+
try {
|
|
185
|
+
const raw = fs4.readFileSync(iconPath, "utf-8").replace(/\n+$/, "");
|
|
186
|
+
for (const line of raw.split("\n")) {
|
|
187
|
+
this.write(this.cyan(line) + "\n");
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
this.write(this.brandMark() + " " + this.bold("Prismer") + "\n");
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
this.write(this.brandMark() + " " + this.bold("Prismer") + "\n");
|
|
194
|
+
}
|
|
195
|
+
if (subtitle !== void 0 && subtitle.length > 0) {
|
|
196
|
+
this.write(this.dim(" " + subtitle) + "\n");
|
|
197
|
+
}
|
|
198
|
+
this.blank();
|
|
199
|
+
}
|
|
200
|
+
banner(subtitle, opts) {
|
|
201
|
+
if (this.mode === "json" || this.mode === "quiet") return;
|
|
202
|
+
const envColumns = process.env["COLUMNS"] !== void 0 ? parseInt(process.env["COLUMNS"], 10) : NaN;
|
|
203
|
+
const width = this.stream.columns ?? process.stdout.columns ?? (Number.isFinite(envColumns) ? envColumns : 80);
|
|
204
|
+
const iconPath = findIconPath("big");
|
|
205
|
+
const shouldUseFull = opts?.full === true || width >= 120;
|
|
206
|
+
if (shouldUseFull && iconPath !== null) {
|
|
207
|
+
try {
|
|
208
|
+
const raw = fs4.readFileSync(iconPath, "utf-8");
|
|
209
|
+
const lines = raw.split("\n");
|
|
210
|
+
for (const line of lines) {
|
|
211
|
+
const brandedLine = line.replace("Prismer Cloud SDK", "Prismer Runtime CLI");
|
|
212
|
+
const stripped = brandedLine.trimEnd();
|
|
213
|
+
if (stripped.length === 0) {
|
|
214
|
+
this.write("\n");
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const clipped = stripped.length >= width ? stripped.slice(0, Math.max(width - 1, 1)) : stripped;
|
|
218
|
+
this.write(this.colorBrandLine(clipped) + "\n");
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
this.write(this.cyan(COMPACT_BANNER[0] ?? "\u25C7 PRISMER") + "\n");
|
|
222
|
+
this.write(this.dim(COMPACT_BANNER[1] ?? " Runtime CLI") + "\n");
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
this.write(this.cyan(COMPACT_BANNER[0] ?? "\u25C7 PRISMER") + "\n");
|
|
226
|
+
this.write(this.dim(COMPACT_BANNER[1] ?? " Runtime CLI") + "\n");
|
|
227
|
+
}
|
|
228
|
+
if (subtitle !== void 0 && subtitle.length > 0) {
|
|
229
|
+
this.write(this.dim(" " + subtitle) + "\n");
|
|
230
|
+
}
|
|
231
|
+
this.blank();
|
|
232
|
+
}
|
|
233
|
+
// ---- Level 2: Primary data ----
|
|
234
|
+
blank() {
|
|
235
|
+
if (this.mode === "json") return;
|
|
236
|
+
this.write("\n");
|
|
237
|
+
}
|
|
238
|
+
line(text) {
|
|
239
|
+
if (this.mode === "json") return;
|
|
240
|
+
this.write(text + "\n");
|
|
241
|
+
}
|
|
242
|
+
info(text) {
|
|
243
|
+
this.line(text);
|
|
244
|
+
}
|
|
245
|
+
// ---- Level 3: Secondary ----
|
|
246
|
+
secondary(text, indent = 2) {
|
|
247
|
+
if (this.mode === "json") return;
|
|
248
|
+
this.write(" ".repeat(indent) + this.dim(text) + "\n");
|
|
249
|
+
}
|
|
250
|
+
// ---- Level 4: Action tips ----
|
|
251
|
+
tip(text) {
|
|
252
|
+
if (this.mode === "json") return;
|
|
253
|
+
this.write(this.cyan("Tip:") + " " + text + "\n");
|
|
254
|
+
}
|
|
255
|
+
next(text) {
|
|
256
|
+
if (this.mode === "json") return;
|
|
257
|
+
this.write(this.cyan("Next:") + " " + text + "\n");
|
|
258
|
+
}
|
|
259
|
+
// ---- Level 5: Status indicators ----
|
|
260
|
+
ok(text, detail) {
|
|
261
|
+
if (this.mode === "json") return;
|
|
262
|
+
const suffix = detail ? " " + this.dim(detail) : "";
|
|
263
|
+
this.write(" " + this.green("\u2713") + " " + text + suffix + "\n");
|
|
264
|
+
}
|
|
265
|
+
success(text, detail) {
|
|
266
|
+
this.ok(text, detail);
|
|
267
|
+
}
|
|
268
|
+
fail(text, detail) {
|
|
269
|
+
if (this.mode === "json") return;
|
|
270
|
+
const suffix = detail ? " " + this.dim(detail) : "";
|
|
271
|
+
this.write(" " + this.red("\u2717") + " " + text + suffix + "\n");
|
|
272
|
+
}
|
|
273
|
+
online(text) {
|
|
274
|
+
if (this.mode === "json") return;
|
|
275
|
+
this.write(" " + this.green("\u25CF") + " " + text + "\n");
|
|
276
|
+
}
|
|
277
|
+
offline(text) {
|
|
278
|
+
if (this.mode === "json") return;
|
|
279
|
+
this.write(" " + this.gray("\u25CB") + " " + text + "\n");
|
|
280
|
+
}
|
|
281
|
+
notInstalled(text) {
|
|
282
|
+
if (this.mode === "json") return;
|
|
283
|
+
this.write(" " + this.dim("\xB7") + " " + this.dim(text) + "\n");
|
|
284
|
+
}
|
|
285
|
+
pending(text) {
|
|
286
|
+
if (this.mode === "json") return;
|
|
287
|
+
this.write(" " + this.yellow("\u27F3") + " " + text + "\n");
|
|
288
|
+
}
|
|
289
|
+
warn(text, detail) {
|
|
290
|
+
if (this.mode === "json") return;
|
|
291
|
+
const suffix = detail ? " " + this.dim(detail) : "";
|
|
292
|
+
this.write(" " + this.yellow("!") + " " + text + suffix + "\n");
|
|
293
|
+
}
|
|
294
|
+
// ---- Level 6: Error block ----
|
|
295
|
+
error(what, cause, fix) {
|
|
296
|
+
if (this.mode === "json") return;
|
|
297
|
+
this.writeErr(this.red("\u2717") + " " + what + "\n");
|
|
298
|
+
if (cause !== void 0) {
|
|
299
|
+
this.writeErr(" " + this.dim("Cause:") + " " + this.dim(cause) + "\n");
|
|
300
|
+
}
|
|
301
|
+
if (fix !== void 0) {
|
|
302
|
+
this.writeErr(" " + this.cyan("Fix:") + " " + fix + "\n");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
table(rowsOrOpts, maybeOpts) {
|
|
306
|
+
if (this.mode === "json") return;
|
|
307
|
+
const rows = Array.isArray(rowsOrOpts) ? rowsOrOpts : rowsOrOpts.rows;
|
|
308
|
+
const opts = Array.isArray(rowsOrOpts) ? maybeOpts : { columns: rowsOrOpts.columns, maxWidth: rowsOrOpts.maxWidth };
|
|
309
|
+
if (!opts) throw new Error("table() requires columns");
|
|
310
|
+
const maxWidth = opts.maxWidth ?? (process.stdout.columns || 80);
|
|
311
|
+
const cols = opts.columns;
|
|
312
|
+
const widths = cols.map((col) => col.length);
|
|
313
|
+
for (const row of rows) {
|
|
314
|
+
cols.forEach((col, i) => {
|
|
315
|
+
const val = row[col] ?? "";
|
|
316
|
+
const w = widths[i] ?? 0;
|
|
317
|
+
if (val.length > w) widths[i] = val.length;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
const totalWidth = widths.reduce((a, b) => a + b, 0) + (cols.length - 1) * 2 + 2;
|
|
321
|
+
if (totalWidth > maxWidth) {
|
|
322
|
+
for (let i = 0; i < rows.length; i++) {
|
|
323
|
+
const row = rows[i];
|
|
324
|
+
if (!row) continue;
|
|
325
|
+
for (const col of cols) {
|
|
326
|
+
const val = row[col] ?? "";
|
|
327
|
+
this.write(" " + this.bold(col + ":") + " " + val + "\n");
|
|
328
|
+
}
|
|
329
|
+
if (i < rows.length - 1) this.write("\n");
|
|
330
|
+
}
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const header2 = cols.map((col, i) => col.toUpperCase().padEnd(widths[i] ?? col.length)).join(" ");
|
|
334
|
+
this.write(" " + this.dim(header2) + "\n");
|
|
335
|
+
for (const row of rows) {
|
|
336
|
+
const line = cols.map((col, i) => (row[col] ?? "").padEnd(widths[i] ?? col.length)).join(" ");
|
|
337
|
+
this.write(" " + line + "\n");
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// ---- Spinner ----
|
|
341
|
+
spinner(text) {
|
|
342
|
+
if (this.mode === "quiet" || this.mode === "json") {
|
|
343
|
+
return {
|
|
344
|
+
update() {
|
|
345
|
+
},
|
|
346
|
+
stop() {
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
const isTTY = this.stream.isTTY === true;
|
|
351
|
+
if (!isTTY || !this.colorEnabled) {
|
|
352
|
+
this.write(" " + this.yellow("\u27F3") + " " + text + "\n");
|
|
353
|
+
return {
|
|
354
|
+
update: (t) => {
|
|
355
|
+
this.write(" " + this.yellow("\u27F3") + " " + t + "\n");
|
|
356
|
+
},
|
|
357
|
+
stop: (final) => {
|
|
358
|
+
if (final) this.write(" " + this.green("\u2713") + " " + final + "\n");
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
let current = text;
|
|
363
|
+
let frameIdx = 0;
|
|
364
|
+
let stopped = false;
|
|
365
|
+
const write = this.write.bind(this);
|
|
366
|
+
const colorFn = this.yellow.bind(this);
|
|
367
|
+
const greenFn = this.green.bind(this);
|
|
368
|
+
function renderFrame() {
|
|
369
|
+
const frame = BRAILLE_FRAMES[frameIdx % BRAILLE_FRAMES.length] ?? "\u280B";
|
|
370
|
+
const line = " " + colorFn(frame) + " " + current;
|
|
371
|
+
write("\r" + line);
|
|
372
|
+
frameIdx++;
|
|
373
|
+
}
|
|
374
|
+
renderFrame();
|
|
375
|
+
const timer = setInterval(renderFrame, 80);
|
|
376
|
+
return {
|
|
377
|
+
update(t) {
|
|
378
|
+
if (stopped) return;
|
|
379
|
+
current = t;
|
|
380
|
+
},
|
|
381
|
+
stop(final) {
|
|
382
|
+
if (stopped) return;
|
|
383
|
+
stopped = true;
|
|
384
|
+
clearInterval(timer);
|
|
385
|
+
write("\r\x1B[2K");
|
|
386
|
+
if (final) write(" " + greenFn("\u2713") + " " + final + "\n");
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
// ---- Progress bar ----
|
|
391
|
+
progress(text, total) {
|
|
392
|
+
if (this.mode === "quiet" || this.mode === "json") {
|
|
393
|
+
return {
|
|
394
|
+
update() {
|
|
395
|
+
},
|
|
396
|
+
stop() {
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const isTTY = this.stream.isTTY === true;
|
|
401
|
+
const start = Date.now();
|
|
402
|
+
const write = this.write.bind(this);
|
|
403
|
+
const colorFn = this.cyan.bind(this);
|
|
404
|
+
const dimFn = this.dim.bind(this);
|
|
405
|
+
const greenFn = this.green.bind(this);
|
|
406
|
+
let last = 0;
|
|
407
|
+
let lastDetail = "";
|
|
408
|
+
let stopped = false;
|
|
409
|
+
const render = () => {
|
|
410
|
+
if (stopped) return;
|
|
411
|
+
const frac = total > 0 ? Math.min(1, Math.max(0, last / total)) : 0;
|
|
412
|
+
const pct = Math.floor(frac * 100);
|
|
413
|
+
const width = 20;
|
|
414
|
+
const filled = Math.floor(frac * width);
|
|
415
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
|
|
416
|
+
const elapsed = (Date.now() - start) / 1e3;
|
|
417
|
+
const eta = frac > 0.01 ? Math.max(0, elapsed / frac - elapsed) : 0;
|
|
418
|
+
const etaStr = frac >= 1 ? "" : ` \xB7 ${eta < 1 ? "<1s" : Math.round(eta) + "s"} left`;
|
|
419
|
+
const detailStr = lastDetail ? ` \xB7 ${lastDetail}` : "";
|
|
420
|
+
const line = ` ${text} [${colorFn(bar)}] ${String(pct).padStart(3)}%${detailStr}${dimFn(etaStr)}`;
|
|
421
|
+
if (isTTY && this.colorEnabled) {
|
|
422
|
+
write("\r\x1B[2K" + line);
|
|
423
|
+
} else {
|
|
424
|
+
write(line + "\n");
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
render();
|
|
428
|
+
return {
|
|
429
|
+
update: (current, detail) => {
|
|
430
|
+
if (stopped) return;
|
|
431
|
+
last = current;
|
|
432
|
+
if (detail !== void 0) lastDetail = detail;
|
|
433
|
+
render();
|
|
434
|
+
},
|
|
435
|
+
stop: (final) => {
|
|
436
|
+
if (stopped) return;
|
|
437
|
+
stopped = true;
|
|
438
|
+
if (isTTY && this.colorEnabled) write("\r\x1B[2K");
|
|
439
|
+
if (final) write(" " + greenFn("\u2713") + " " + final + "\n");
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
// ---- JSON output ----
|
|
444
|
+
json(payload, opts) {
|
|
445
|
+
const indent = opts?.pretty ? 2 : void 0;
|
|
446
|
+
this.write(JSON.stringify(payload, null, indent) + "\n");
|
|
447
|
+
}
|
|
448
|
+
result(pretty, jsonPayload) {
|
|
449
|
+
if (this.mode === "pretty") {
|
|
450
|
+
pretty();
|
|
451
|
+
} else {
|
|
452
|
+
this.json(jsonPayload);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
_ui = null;
|
|
130
457
|
}
|
|
131
|
-
};
|
|
458
|
+
});
|
|
132
459
|
|
|
133
|
-
// src/
|
|
134
|
-
var
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
460
|
+
// src/cli/util.ts
|
|
461
|
+
var util_exports = {};
|
|
462
|
+
__export(util_exports, {
|
|
463
|
+
DEFAULT_CLOUD_BASE_URL: () => DEFAULT_CLOUD_BASE_URL,
|
|
464
|
+
clearPidFile: () => clearPidFile,
|
|
465
|
+
color: () => color,
|
|
466
|
+
exitWithError: () => exitWithError,
|
|
467
|
+
fail: () => fail2,
|
|
468
|
+
header: () => header,
|
|
469
|
+
info: () => info,
|
|
470
|
+
normalizeCloudUrl: () => normalizeCloudUrl,
|
|
471
|
+
ok: () => ok,
|
|
472
|
+
pidAlive: () => pidAlive,
|
|
473
|
+
pidFilePath: () => pidFilePath,
|
|
474
|
+
printBanner: () => printBanner,
|
|
475
|
+
printJson: () => printJson,
|
|
476
|
+
readPidFile: () => readPidFile,
|
|
477
|
+
runAction: () => runAction,
|
|
478
|
+
table: () => table,
|
|
479
|
+
tip: () => tip,
|
|
480
|
+
warn: () => warn,
|
|
481
|
+
writePidFile: () => writePidFile
|
|
482
|
+
});
|
|
483
|
+
function color(kind, text) {
|
|
484
|
+
if (process.env.NO_COLOR === "1" || process.env.NO_COLOR === "true") return text;
|
|
485
|
+
return `${ANSI[kind]}${text}${ANSI.reset}`;
|
|
486
|
+
}
|
|
487
|
+
function printJson(v) {
|
|
488
|
+
getUI().json(v, { pretty: true });
|
|
489
|
+
}
|
|
490
|
+
function exitWithError(message, opts) {
|
|
491
|
+
const o = typeof opts === "number" ? { exitCode: opts } : opts ?? {};
|
|
492
|
+
const exitCode = o.exitCode ?? 1;
|
|
493
|
+
const ui = getUI();
|
|
494
|
+
if (ui.mode === "json") {
|
|
495
|
+
const payload = {
|
|
496
|
+
ok: false,
|
|
497
|
+
error: { code: o.code ?? "cli_error", message },
|
|
498
|
+
...o.details ? { details: o.details } : {}
|
|
499
|
+
};
|
|
500
|
+
ui.json(payload, { pretty: true });
|
|
501
|
+
} else {
|
|
502
|
+
process.stderr.write(`Error: ${message}
|
|
503
|
+
`);
|
|
504
|
+
}
|
|
505
|
+
process.exit(exitCode);
|
|
506
|
+
}
|
|
507
|
+
function normalizeCloudUrl(input) {
|
|
508
|
+
const raw = input.trim();
|
|
509
|
+
if (!raw) throw new Error("Cloud URL is empty.");
|
|
510
|
+
if (/^[a-z][a-z0-9+\-.]*:\/\//i.test(raw)) {
|
|
511
|
+
let parsed;
|
|
512
|
+
try {
|
|
513
|
+
parsed = new URL(raw);
|
|
514
|
+
} catch {
|
|
515
|
+
throw new Error(`Invalid --cloud URL: ${raw}`);
|
|
516
|
+
}
|
|
517
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
518
|
+
throw new Error(`Invalid --cloud URL scheme: ${parsed.protocol} (expected http:// or https://)`);
|
|
519
|
+
}
|
|
520
|
+
return raw.replace(/\/$/, "");
|
|
521
|
+
}
|
|
522
|
+
if (!/^[A-Za-z0-9.\-_:[\]/]+$/.test(raw)) {
|
|
523
|
+
throw new Error(`Invalid --cloud URL: ${raw} (must be http://\u2026 or https://\u2026 or host:port)`);
|
|
524
|
+
}
|
|
525
|
+
const candidate = `http://${raw}`;
|
|
526
|
+
try {
|
|
527
|
+
new URL(candidate);
|
|
528
|
+
} catch {
|
|
529
|
+
throw new Error(`Invalid --cloud URL: ${raw}`);
|
|
530
|
+
}
|
|
531
|
+
return candidate.replace(/\/$/, "");
|
|
532
|
+
}
|
|
533
|
+
function runAction(fn, opts = {}) {
|
|
534
|
+
return async (...args) => {
|
|
535
|
+
try {
|
|
536
|
+
await fn(...args);
|
|
537
|
+
} catch (err) {
|
|
538
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
539
|
+
const message = opts.sanitize ? opts.sanitize(raw) : raw;
|
|
540
|
+
exitWithError(message, { code: opts.code });
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function printBanner(opts = {}) {
|
|
545
|
+
const ui = getUI();
|
|
546
|
+
if (opts.compact) {
|
|
547
|
+
ui.smallHeader("Runtime CLI v1.9.7");
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
ui.banner("Runtime CLI v1.9.7", { full: true });
|
|
551
|
+
}
|
|
552
|
+
function ok(label, detail) {
|
|
553
|
+
getUI().ok(label, detail);
|
|
554
|
+
}
|
|
555
|
+
function warn(label, detail) {
|
|
556
|
+
getUI().warn(label, detail);
|
|
557
|
+
}
|
|
558
|
+
function fail2(label, detail) {
|
|
559
|
+
getUI().fail(label, detail);
|
|
560
|
+
}
|
|
561
|
+
function tip(command, detail) {
|
|
562
|
+
const text = detail ? `${command} ${detail}` : command;
|
|
563
|
+
getUI().tip(text);
|
|
564
|
+
}
|
|
565
|
+
function info(message) {
|
|
566
|
+
getUI().info(message);
|
|
567
|
+
}
|
|
568
|
+
function header(title) {
|
|
569
|
+
getUI().header(title);
|
|
570
|
+
getUI().blank();
|
|
571
|
+
}
|
|
572
|
+
function table(rows, columns) {
|
|
573
|
+
getUI().table(rows, { columns });
|
|
574
|
+
}
|
|
575
|
+
function pidFilePath(paths) {
|
|
576
|
+
return (0, import_node_path8.join)(paths.root, "daemon.pid");
|
|
577
|
+
}
|
|
578
|
+
function writePidFile(paths, pid) {
|
|
579
|
+
(0, import_node_fs12.writeFileSync)(pidFilePath(paths), `${pid}
|
|
580
|
+
`, "utf8");
|
|
581
|
+
}
|
|
582
|
+
function readPidFile(paths) {
|
|
583
|
+
const p = pidFilePath(paths);
|
|
584
|
+
if (!(0, import_node_fs12.existsSync)(p)) return void 0;
|
|
585
|
+
const raw = (0, import_node_fs12.readFileSync)(p, "utf8").trim();
|
|
586
|
+
const pid = Number.parseInt(raw, 10);
|
|
587
|
+
return Number.isFinite(pid) ? pid : void 0;
|
|
588
|
+
}
|
|
589
|
+
function clearPidFile(paths) {
|
|
590
|
+
const p = pidFilePath(paths);
|
|
591
|
+
if ((0, import_node_fs12.existsSync)(p)) {
|
|
592
|
+
try {
|
|
593
|
+
(0, import_node_fs12.unlinkSync)(p);
|
|
594
|
+
} catch {
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function pidAlive(pid) {
|
|
599
|
+
try {
|
|
600
|
+
process.kill(pid, 0);
|
|
601
|
+
return true;
|
|
602
|
+
} catch {
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
var import_node_fs12, import_node_path8, DEFAULT_CLOUD_BASE_URL, ANSI;
|
|
607
|
+
var init_util = __esm({
|
|
608
|
+
"src/cli/util.ts"() {
|
|
609
|
+
"use strict";
|
|
610
|
+
import_node_fs12 = require("fs");
|
|
611
|
+
import_node_path8 = require("path");
|
|
612
|
+
init_ui();
|
|
613
|
+
DEFAULT_CLOUD_BASE_URL = "https://prismer.cloud";
|
|
614
|
+
ANSI = {
|
|
615
|
+
reset: "\x1B[0m",
|
|
616
|
+
bold: "\x1B[1m",
|
|
617
|
+
dim: "\x1B[2m",
|
|
618
|
+
cyan: "\x1B[36m",
|
|
619
|
+
green: "\x1B[32m",
|
|
620
|
+
yellow: "\x1B[33m",
|
|
621
|
+
red: "\x1B[31m"
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// src/index.ts
|
|
627
|
+
var src_exports = {};
|
|
628
|
+
__export(src_exports, {
|
|
629
|
+
AdapterRegistry: () => AdapterRegistry,
|
|
630
|
+
AssetCache: () => AssetCache,
|
|
631
|
+
BUILTIN_ROLE_TEMPLATES: () => BUILTIN_ROLE_TEMPLATES,
|
|
632
|
+
CloudClient: () => CloudClient,
|
|
633
|
+
CloudError: () => CloudError,
|
|
634
|
+
ConfigSchema: () => ConfigSchema,
|
|
635
|
+
LocalServer: () => LocalServer,
|
|
636
|
+
ParseClaimController: () => ParseClaimController,
|
|
637
|
+
Runner: () => Runner,
|
|
638
|
+
ServicePool: () => ServicePool,
|
|
639
|
+
SyncQueue: () => SyncQueue,
|
|
640
|
+
SyncWorker: () => SyncWorker,
|
|
641
|
+
TARGET_SCHEMA_VERSION: () => TARGET_SCHEMA_VERSION,
|
|
642
|
+
UriResolver: () => UriResolver,
|
|
643
|
+
WS_CLOSE: () => WS_CLOSE,
|
|
644
|
+
WorkspaceMirror: () => WorkspaceMirror,
|
|
645
|
+
WsClient: () => WsClient,
|
|
646
|
+
buildProgram: () => buildProgram,
|
|
647
|
+
claudeCodeAdapter: () => claudeCodeAdapter,
|
|
648
|
+
codexAdapter: () => codexAdapter,
|
|
649
|
+
composePrompt: () => composePrompt,
|
|
650
|
+
configExists: () => configExists,
|
|
651
|
+
currentSchemaVersion: () => currentSchemaVersion,
|
|
652
|
+
deriveWsUrl: () => deriveWsUrl,
|
|
653
|
+
envelope: () => envelope,
|
|
654
|
+
getRoleTemplate: () => getRoleTemplate,
|
|
655
|
+
handleDispatch: () => handleDispatch,
|
|
656
|
+
hermesAdapter: () => hermesAdapter,
|
|
657
|
+
isDaemonId: () => isDaemonId,
|
|
658
|
+
listRoleTemplates: () => listRoleTemplates,
|
|
659
|
+
loadConfig: () => loadConfig,
|
|
660
|
+
newDaemonId: () => newDaemonId,
|
|
661
|
+
nextBackoffMs: () => nextBackoffMs,
|
|
662
|
+
openLocalDb: () => openLocalDb,
|
|
663
|
+
pair: () => pair,
|
|
664
|
+
parseCodexOutput: () => parseCodexOutput,
|
|
665
|
+
parseUris: () => parseUris,
|
|
666
|
+
resolvePaths: () => resolvePaths,
|
|
667
|
+
runCli: () => runCli,
|
|
668
|
+
runMigrations: () => runMigrations,
|
|
669
|
+
saveConfig: () => saveConfig
|
|
670
|
+
});
|
|
671
|
+
module.exports = __toCommonJS(src_exports);
|
|
672
|
+
|
|
673
|
+
// src/adapters/registry.ts
|
|
674
|
+
var AdapterRegistry = class {
|
|
675
|
+
adapters = /* @__PURE__ */ new Map();
|
|
676
|
+
register(adapter) {
|
|
677
|
+
if (!adapter.name || typeof adapter.name !== "string") {
|
|
678
|
+
throw new Error("AdapterRegistry.register: adapter.name required");
|
|
679
|
+
}
|
|
680
|
+
const hasDispatch = typeof adapter.dispatch === "function";
|
|
681
|
+
const hasEnsureService = typeof adapter.ensureService === "function";
|
|
682
|
+
if (!hasDispatch && !hasEnsureService) {
|
|
683
|
+
throw new Error(
|
|
684
|
+
`AdapterRegistry.register: adapter "${adapter.name}" must implement dispatch() (interactive) or ensureService() (long-running)`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
this.adapters.set(adapter.name, adapter);
|
|
688
|
+
}
|
|
689
|
+
unregister(name) {
|
|
690
|
+
return this.adapters.delete(name);
|
|
691
|
+
}
|
|
692
|
+
has(name) {
|
|
693
|
+
return this.adapters.has(name);
|
|
694
|
+
}
|
|
695
|
+
get(name) {
|
|
696
|
+
return this.adapters.get(name);
|
|
697
|
+
}
|
|
698
|
+
list() {
|
|
699
|
+
return Array.from(this.adapters.values());
|
|
700
|
+
}
|
|
701
|
+
size() {
|
|
702
|
+
return this.adapters.size;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Find adapters that satisfy a capability tag.
|
|
706
|
+
*
|
|
707
|
+
* Match rules (deterministic, sorted by name):
|
|
708
|
+
* - exact: adapter declared the tag verbatim
|
|
709
|
+
* - wildcard: adapter declared `code.*` → matches `code.write`
|
|
710
|
+
*/
|
|
711
|
+
findByCapability(capability) {
|
|
712
|
+
const matches2 = [];
|
|
713
|
+
for (const adapter of this.adapters.values()) {
|
|
714
|
+
if (adapter.capabilities.includes(capability)) {
|
|
715
|
+
matches2.push(adapter);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (adapter.capabilities.some(
|
|
719
|
+
(tag) => tag.endsWith(".*") && capability.startsWith(tag.slice(0, -1))
|
|
720
|
+
)) {
|
|
721
|
+
matches2.push(adapter);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
matches2.sort((a, b) => a.name.localeCompare(b.name));
|
|
725
|
+
return matches2;
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// src/adapters/hermes/index.ts
|
|
730
|
+
var import_node_child_process = require("child_process");
|
|
731
|
+
var import_node_fs = require("fs");
|
|
732
|
+
var import_node_os = require("os");
|
|
733
|
+
var import_node_path = require("path");
|
|
734
|
+
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
735
|
+
var YAML = __toESM(require("yaml"), 1);
|
|
736
|
+
var import_zod = require("zod");
|
|
737
|
+
var import_meta = {};
|
|
142
738
|
var PRISMER_IM_SKILL_NAME = "prismer-im-collab";
|
|
143
739
|
var PRISMER_IM_SKILL_CONTENT = `# Prismer IM Collaboration
|
|
144
740
|
|
|
@@ -242,7 +838,7 @@ var HermesProfileConfigSchema = import_zod.z.object({
|
|
|
242
838
|
*/
|
|
243
839
|
prismerMcpServerPath: import_zod.z.string().optional(),
|
|
244
840
|
/** Model sent to Prismer's /api/v1/chat/completions endpoint. */
|
|
245
|
-
model: import_zod.z.string().min(1).default("us-kimi-k2.
|
|
841
|
+
model: import_zod.z.string().min(1).default("us-kimi-k2.6"),
|
|
246
842
|
/** Named custom provider written into Hermes config.yaml. */
|
|
247
843
|
prismerProviderName: import_zod.z.string().min(1).default("prismer"),
|
|
248
844
|
/** Override cloud provider base. Defaults to PRISMER_BASE_URL + /api/v1. */
|
|
@@ -268,7 +864,9 @@ var HermesProfileConfigSchema = import_zod.z.object({
|
|
|
268
864
|
* surface but the local source tree contains hermes_cli/kanban_db.py.
|
|
269
865
|
*/
|
|
270
866
|
hermesSourceDir: import_zod.z.string().optional(),
|
|
271
|
-
nativeMirrorTimeoutMs: import_zod.z.number().int().positive().default(2e3)
|
|
867
|
+
nativeMirrorTimeoutMs: import_zod.z.number().int().positive().default(2e3),
|
|
868
|
+
/** Task authority level: executor (default) or orchestrator. */
|
|
869
|
+
taskAuthority: import_zod.z.enum(["executor", "orchestrator"]).optional().default("executor")
|
|
272
870
|
});
|
|
273
871
|
var hermesAdapter = {
|
|
274
872
|
name: "hermes",
|
|
@@ -753,13 +1351,13 @@ function resolvePrismerMcpServerPath(config) {
|
|
|
753
1351
|
}
|
|
754
1352
|
try {
|
|
755
1353
|
const { fileURLToPath: fileURLToPath2 } = require("url");
|
|
756
|
-
const { join:
|
|
1354
|
+
const { join: join15, dirname: dirname8 } = require("path");
|
|
757
1355
|
const here = dirname8(fileURLToPath2(import_meta.url));
|
|
758
|
-
const candidate =
|
|
1356
|
+
const candidate = join15(here, "../../mcp/dist/index.js");
|
|
759
1357
|
if ((0, import_node_fs.existsSync)(candidate)) return candidate;
|
|
760
|
-
const candidate2 =
|
|
1358
|
+
const candidate2 = join15(here, "../../../mcp/dist/index.js");
|
|
761
1359
|
if ((0, import_node_fs.existsSync)(candidate2)) return candidate2;
|
|
762
|
-
const candidate3 =
|
|
1360
|
+
const candidate3 = join15(here, "../../../../mcp/dist/index.js");
|
|
763
1361
|
if ((0, import_node_fs.existsSync)(candidate3)) return candidate3;
|
|
764
1362
|
} catch {
|
|
765
1363
|
}
|
|
@@ -1181,7 +1779,7 @@ var WsClient = class extends import_node_events.EventEmitter {
|
|
|
1181
1779
|
var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
|
|
1182
1780
|
var import_node_fs2 = require("fs");
|
|
1183
1781
|
var import_node_path2 = require("path");
|
|
1184
|
-
var SCHEMA_VERSION =
|
|
1782
|
+
var SCHEMA_VERSION = 3;
|
|
1185
1783
|
var MIGRATIONS = [
|
|
1186
1784
|
{
|
|
1187
1785
|
version: 1,
|
|
@@ -1281,6 +1879,32 @@ var MIGRATIONS = [
|
|
|
1281
1879
|
);
|
|
1282
1880
|
CREATE INDEX IF NOT EXISTS idx_files_hash ON workspace_files_mirror (content_hash);
|
|
1283
1881
|
`
|
|
1882
|
+
},
|
|
1883
|
+
{
|
|
1884
|
+
version: 3,
|
|
1885
|
+
up: `
|
|
1886
|
+
-- Asset metadata index (#filename reference resolution \u2014 daemon/asset/metadata-index.ts)
|
|
1887
|
+
CREATE TABLE IF NOT EXISTS asset_metadata_index (
|
|
1888
|
+
workspace_id TEXT NOT NULL,
|
|
1889
|
+
asset_id TEXT NOT NULL,
|
|
1890
|
+
content_hash TEXT NOT NULL,
|
|
1891
|
+
filename TEXT,
|
|
1892
|
+
folder_path TEXT,
|
|
1893
|
+
mime TEXT NOT NULL,
|
|
1894
|
+
kind TEXT NOT NULL,
|
|
1895
|
+
size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
1896
|
+
description TEXT,
|
|
1897
|
+
asset_index_seq INTEGER NOT NULL,
|
|
1898
|
+
updated_at INTEGER NOT NULL,
|
|
1899
|
+
PRIMARY KEY (workspace_id, asset_id)
|
|
1900
|
+
);
|
|
1901
|
+
|
|
1902
|
+
CREATE INDEX IF NOT EXISTS idx_asset_meta_filename
|
|
1903
|
+
ON asset_metadata_index(workspace_id, filename);
|
|
1904
|
+
|
|
1905
|
+
CREATE INDEX IF NOT EXISTS idx_asset_meta_seq
|
|
1906
|
+
ON asset_metadata_index(workspace_id, asset_index_seq);
|
|
1907
|
+
`
|
|
1284
1908
|
}
|
|
1285
1909
|
];
|
|
1286
1910
|
function runSql(db, sql) {
|
|
@@ -1456,7 +2080,7 @@ var product_manager_default = {
|
|
|
1456
2080
|
description: "Writes PRDs, defines requirements, verifies implementations",
|
|
1457
2081
|
applicableAdapters: ["hermes", "openclaw", "claude-code"],
|
|
1458
2082
|
configSchema: {
|
|
1459
|
-
model: "
|
|
2083
|
+
model: "us-kimi-k2.6",
|
|
1460
2084
|
systemPrompt: "\u4F60\u662F\u4E00\u4F4D\u8D44\u6DF1\u4EA7\u54C1\u7ECF\u7406\u3002\u4F60\u7684\u4EFB\u52A1\uFF1A\u2460\u5199\u6E05\u6670\u7684 PRD\uFF08\u7528\u6237\u6545\u4E8B + \u9A8C\u6536\u6807\u51C6 + \u8FB9\u754C\u6761\u4EF6\uFF09\u2461\u8BC4\u5BA1\u5DE5\u7A0B\u5B9E\u73B0\uFF08\u6838\u5BF9\u9A8C\u6536\u6807\u51C6 + \u7ED9\u53CD\u9988\uFF09\u2462\u5728\u7FA4\u804A\u91CC @engineer \u89E6\u53D1\u5B9E\u73B0\uFF0C@verifier \u89E6\u53D1\u9A8C\u6536\u3002\n\n\u7FA4\u804A\u534F\u4F5C\u7EA6\u5B9A\uFF1A\n- \u4F60\u7684\u8F93\u51FA PRD \u5E94\u8BE5\u4E0A\u4F20\u4E3A workspace file `docs/PROJECT-prd.md`\n- \u4F60\u5B8C\u6210 PRD \u540E\uFF0C\u53D1\u6D88\u606F @engineer-name \u8BA9\u4ED6\u5B9E\u73B0\uFF0C\u9644 prismer://file/<wsId>/docs/PROJECT-prd.md \u94FE\u63A5\n- \u6536\u5230 engineer \u5B8C\u6210\u6D88\u606F\u540E\uFF0C\u4E3B\u52A8 review\uFF0C\u5199\u53CD\u9988\u5230\u7FA4\u91CC",
|
|
1461
2085
|
allowedTools: ["Read", "Write", "WebSearch"],
|
|
1462
2086
|
maxTokens: 8e3
|
|
@@ -1470,7 +2094,7 @@ var engineer_default = {
|
|
|
1470
2094
|
description: "Implements features per PRD, writes code, runs tests",
|
|
1471
2095
|
applicableAdapters: ["hermes", "openclaw", "claude-code"],
|
|
1472
2096
|
configSchema: {
|
|
1473
|
-
model: "
|
|
2097
|
+
model: "us-kimi-k2.6",
|
|
1474
2098
|
systemPrompt: "\u4F60\u662F\u8D44\u6DF1\u5DE5\u7A0B\u5E08\u3002\u4F60\u7684\u4EFB\u52A1\uFF1A\u2460\u6839\u636E PRD \u5B9E\u73B0 feature \u2461\u5199\u6D4B\u8BD5\u8986\u76D6 \u2462\u628A\u4EE3\u7801\u4E0A\u4F20\u4E3A workspace file \u540E\u5728\u7FA4\u804A\u6C47\u62A5\u3002\n\n\u7FA4\u804A\u534F\u4F5C\u7EA6\u5B9A\uFF1A\n- \u6536\u5230 PRD \u540E\u5148 Read prismer://file \u94FE\u63A5\u62C9\u5230\u7684\u672C\u5730\u8DEF\u5F84\n- \u5B9E\u73B0\u5B8C\u6BD5\u540E\u7528 daemon \u4E0A\u4F20 src/<feature>.* \u5230 workspace_files\n- \u5728\u7FA4\u804A\u56DE\u590D @pm-name\uFF0C\u9644\u5B9E\u73B0\u7684 prismer://file URI",
|
|
1475
2099
|
allowedTools: ["Read", "Write", "Edit", "Bash", "Grep"],
|
|
1476
2100
|
maxTokens: 16e3
|
|
@@ -1484,7 +2108,7 @@ var ceo_default = {
|
|
|
1484
2108
|
description: "Sets strategic direction, asks tough questions, makes go/no-go calls",
|
|
1485
2109
|
applicableAdapters: ["hermes", "openclaw", "claude-code"],
|
|
1486
2110
|
configSchema: {
|
|
1487
|
-
model: "
|
|
2111
|
+
model: "us-kimi-k2.6",
|
|
1488
2112
|
systemPrompt: "\u4F60\u662F CEO\u3002\u4F60\u7684\u4EFB\u52A1\uFF1A\u2460\u57FA\u4E8E\u5E02\u573A\u53CD\u9988\u548C\u6570\u636E\u505A\u6218\u7565\u51B3\u7B56 \u2461\u8BC4\u5BA1 PRD \u662F\u5426\u5BF9\u9F50\u6218\u7565 \u2462\u5728\u6267\u884C\u4E2D\u63D0\u51FA\u5C16\u9510\u7684'\u4E3A\u4EC0\u4E48'\u95EE\u9898\u3002",
|
|
1489
2113
|
allowedTools: ["Read", "WebSearch"],
|
|
1490
2114
|
maxTokens: 4e3
|
|
@@ -1498,7 +2122,7 @@ var researcher_default = {
|
|
|
1498
2122
|
description: "Investigates topics, gathers sources, writes research memos with citations",
|
|
1499
2123
|
applicableAdapters: ["hermes", "openclaw", "claude-code"],
|
|
1500
2124
|
configSchema: {
|
|
1501
|
-
model: "
|
|
2125
|
+
model: "us-kimi-k2.6",
|
|
1502
2126
|
systemPrompt: "\u4F60\u662F\u4E00\u4F4D\u8D44\u6DF1\u7814\u7A76\u5458\u3002\u4EFB\u52A1\uFF1A\u2460\u4F9D\u636E\u95EE\u9898\u5236\u5B9A\u8C03\u7814\u63D0\u7EB2 \u2461\u6293\u53D6/\u9605\u8BFB\u8D44\u6599\u5E76\u63D0\u53D6\u8981\u70B9 \u2462\u4EA7\u51FA\u5E26\u5F15\u7528\u7684\u7814\u7A76\u5907\u5FD8\u5F55\u3002\n\n\u7FA4\u804A\u534F\u4F5C\u7EA6\u5B9A\uFF1A\n- \u7814\u7A76\u4EA7\u51FA\u4E0A\u4F20\u4E3A workspace file `research/<topic>.md`\n- \u5B8C\u6210\u540E\u5728\u7FA4\u804A\u56DE\u590D @pm-name\uFF0C\u9644 prismer://file/<wsId>/research/<topic>.md\n- \u5F15\u7528\u5FC5\u987B\u7ED9\u51FA\u539F\u59CB\u94FE\u63A5\u6216 prismer://asset URI\uFF1B\u4E0D\u5F97\u4F2A\u9020\u6765\u6E90",
|
|
1503
2127
|
allowedTools: ["Read", "Write", "WebSearch", "WebFetch"],
|
|
1504
2128
|
maxTokens: 12e3
|
|
@@ -1608,10 +2232,11 @@ function deriveWsUrl(httpBase) {
|
|
|
1608
2232
|
}
|
|
1609
2233
|
|
|
1610
2234
|
// src/daemon-id.ts
|
|
1611
|
-
var
|
|
2235
|
+
var import_node_os3 = require("os");
|
|
1612
2236
|
var PREFIX = "daemon-";
|
|
1613
2237
|
function newDaemonId() {
|
|
1614
|
-
|
|
2238
|
+
const host = (0, import_node_os3.hostname)().replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "unknown";
|
|
2239
|
+
return `${PREFIX}${host}`;
|
|
1615
2240
|
}
|
|
1616
2241
|
function isDaemonId(s) {
|
|
1617
2242
|
return s.startsWith(PREFIX) && s.length > PREFIX.length;
|
|
@@ -2332,6 +2957,14 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
2332
2957
|
sendReply(deps.ws, reply, requestId);
|
|
2333
2958
|
return reply;
|
|
2334
2959
|
}
|
|
2960
|
+
let hashRefResult = { text: payload.prompt, resolutions: [] };
|
|
2961
|
+
if (deps.assetMetadataIndexes && profile.workspaceId) {
|
|
2962
|
+
const assetIndex = deps.assetMetadataIndexes.get(profile.workspaceId);
|
|
2963
|
+
if (assetIndex) {
|
|
2964
|
+
hashRefResult = await resolveHashRefs(payload.prompt, assetIndex, deps.cloud);
|
|
2965
|
+
payload.prompt = hashRefResult.text;
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2335
2968
|
const rewrittenPrompt = await deps.uriResolver.rewrite(payload.prompt, { pin: true });
|
|
2336
2969
|
resolvedHashes.push(...rewrittenPrompt.resolvedHashes);
|
|
2337
2970
|
let rewrittenContext = [];
|
|
@@ -2546,6 +3179,84 @@ function isTextLikeMime(mime) {
|
|
|
2546
3179
|
if (m.endsWith("+json") || m.endsWith("+xml") || m.endsWith("+csv")) return true;
|
|
2547
3180
|
return false;
|
|
2548
3181
|
}
|
|
3182
|
+
var HASH_REF_RE = /(?:^|\s)#([^\s#]+)/g;
|
|
3183
|
+
var HEX_COLOR_RE = /^[0-9a-fA-F]{3,8}$/;
|
|
3184
|
+
var FILE_EXT_RE = /\.[a-zA-Z0-9]{1,10}$/;
|
|
3185
|
+
var TRAILING_PUNCT_RE = /[,.;:!?)\]}'"]+$/;
|
|
3186
|
+
async function resolveHashRefs(prompt, assetIndex, cloud) {
|
|
3187
|
+
const resolutions = [];
|
|
3188
|
+
const candidates = [];
|
|
3189
|
+
let match;
|
|
3190
|
+
const re = new RegExp(HASH_REF_RE.source, "g");
|
|
3191
|
+
while ((match = re.exec(prompt)) !== null) {
|
|
3192
|
+
const refName = match[1];
|
|
3193
|
+
const leading = match[0].startsWith("#") ? 0 : 1;
|
|
3194
|
+
const start = match.index + leading;
|
|
3195
|
+
const end = match.index + match[0].length;
|
|
3196
|
+
if (HEX_COLOR_RE.test(refName)) continue;
|
|
3197
|
+
let cleanRef = refName;
|
|
3198
|
+
let stripped = "";
|
|
3199
|
+
const punctMatch = TRAILING_PUNCT_RE.exec(cleanRef);
|
|
3200
|
+
if (punctMatch) {
|
|
3201
|
+
stripped = punctMatch[0];
|
|
3202
|
+
cleanRef = cleanRef.slice(0, -stripped.length);
|
|
3203
|
+
}
|
|
3204
|
+
if (!cleanRef) continue;
|
|
3205
|
+
if (HEX_COLOR_RE.test(cleanRef)) continue;
|
|
3206
|
+
const hasExtension = FILE_EXT_RE.test(cleanRef);
|
|
3207
|
+
candidates.push({ ref: cleanRef, start, end: end - stripped.length, hasExtension });
|
|
3208
|
+
}
|
|
3209
|
+
if (candidates.length === 0) {
|
|
3210
|
+
return { text: prompt, resolutions: [] };
|
|
3211
|
+
}
|
|
3212
|
+
const allFilenames = candidates.map((c) => c.ref);
|
|
3213
|
+
const localResults = assetIndex.resolveByFilenames(allFilenames);
|
|
3214
|
+
const needsCloud = candidates.filter(
|
|
3215
|
+
(c) => c.hasExtension && !localResults.has(c.ref)
|
|
3216
|
+
);
|
|
3217
|
+
const cloudResults = /* @__PURE__ */ new Map();
|
|
3218
|
+
if (needsCloud.length > 0) {
|
|
3219
|
+
await Promise.allSettled(
|
|
3220
|
+
needsCloud.map(async (c) => {
|
|
3221
|
+
try {
|
|
3222
|
+
const items = await cloud.get(
|
|
3223
|
+
`/api/im/assets?workspaceId=${encodeURIComponent(assetIndex.workspaceId)}&q=${encodeURIComponent(c.ref)}&limit=1`
|
|
3224
|
+
);
|
|
3225
|
+
if (Array.isArray(items) && items.length > 0) {
|
|
3226
|
+
const item = items[0];
|
|
3227
|
+
cloudResults.set(c.ref, item.contentHash);
|
|
3228
|
+
}
|
|
3229
|
+
} catch {
|
|
3230
|
+
}
|
|
3231
|
+
})
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
for (const c of candidates) {
|
|
3235
|
+
const local = localResults.get(c.ref);
|
|
3236
|
+
if (local) {
|
|
3237
|
+
resolutions.push({
|
|
3238
|
+
ref: c.ref,
|
|
3239
|
+
start: c.start,
|
|
3240
|
+
end: c.end,
|
|
3241
|
+
resolvedUri: `prismer://workspace/${encodeURIComponent(assetIndex.workspaceId)}/asset/${local.contentHash}`
|
|
3242
|
+
});
|
|
3243
|
+
} else if (c.hasExtension) {
|
|
3244
|
+
const cloudHash = cloudResults.get(c.ref);
|
|
3245
|
+
resolutions.push({
|
|
3246
|
+
ref: c.ref,
|
|
3247
|
+
start: c.start,
|
|
3248
|
+
end: c.end,
|
|
3249
|
+
resolvedUri: cloudHash ? `prismer://workspace/${encodeURIComponent(assetIndex.workspaceId)}/asset/${cloudHash}` : void 0
|
|
3250
|
+
});
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
let result = prompt;
|
|
3254
|
+
const sorted = [...resolutions].filter((r) => r.resolvedUri).sort((a, b) => b.start - a.start);
|
|
3255
|
+
for (const r of sorted) {
|
|
3256
|
+
result = result.slice(0, r.start) + r.resolvedUri + result.slice(r.end);
|
|
3257
|
+
}
|
|
3258
|
+
return { text: result, resolutions };
|
|
3259
|
+
}
|
|
2549
3260
|
async function resolveAssetRefs(refs, cache) {
|
|
2550
3261
|
const out = { promptBlocks: [], observability: [], pinnedHashes: [] };
|
|
2551
3262
|
if (!refs || refs.length === 0) return out;
|
|
@@ -2618,8 +3329,8 @@ async function resolveAssetRefs(refs, cache) {
|
|
|
2618
3329
|
return out;
|
|
2619
3330
|
}
|
|
2620
3331
|
function formatInlineAssetBlock(ref, mime, body, strategy) {
|
|
2621
|
-
const
|
|
2622
|
-
return `${
|
|
3332
|
+
const header2 = `[Attached file] id=${ref.assetId} mime=${mime ?? "unknown"}${strategy === "inline-text-truncated" ? " (truncated)" : ""}`;
|
|
3333
|
+
return `${header2}
|
|
2623
3334
|
---
|
|
2624
3335
|
${body}
|
|
2625
3336
|
---`;
|
|
@@ -2934,7 +3645,7 @@ var ServicePool = class {
|
|
|
2934
3645
|
|
|
2935
3646
|
// src/daemon/local-server.ts
|
|
2936
3647
|
var import_node_http = require("http");
|
|
2937
|
-
var
|
|
3648
|
+
var import_node_crypto = require("crypto");
|
|
2938
3649
|
var import_node_fs7 = require("fs");
|
|
2939
3650
|
var path2 = __toESM(require("path"), 1);
|
|
2940
3651
|
var LocalServer = class {
|
|
@@ -2968,20 +3679,35 @@ var LocalServer = class {
|
|
|
2968
3679
|
respond(res, 204, null);
|
|
2969
3680
|
return;
|
|
2970
3681
|
}
|
|
3682
|
+
const handlers = [];
|
|
2971
3683
|
if (this.opts.attachMemory) {
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
});
|
|
2980
|
-
});
|
|
3684
|
+
handlers.push({ name: "memory", fn: this.opts.attachMemory });
|
|
3685
|
+
}
|
|
3686
|
+
if (this.opts.attachAsset) {
|
|
3687
|
+
handlers.push({ name: "asset", fn: this.opts.attachAsset });
|
|
3688
|
+
}
|
|
3689
|
+
if (handlers.length > 0) {
|
|
3690
|
+
void this.runHandlers(req, res, handlers, 0);
|
|
2981
3691
|
return;
|
|
2982
3692
|
}
|
|
2983
3693
|
this.routeStandard(req, res);
|
|
2984
3694
|
}
|
|
3695
|
+
async runHandlers(req, res, handlers, idx) {
|
|
3696
|
+
if (idx >= handlers.length) {
|
|
3697
|
+
this.routeStandard(req, res);
|
|
3698
|
+
return;
|
|
3699
|
+
}
|
|
3700
|
+
try {
|
|
3701
|
+
const handled = await handlers[idx].fn(req, res);
|
|
3702
|
+
if (handled) return;
|
|
3703
|
+
await this.runHandlers(req, res, handlers, idx + 1);
|
|
3704
|
+
} catch (err) {
|
|
3705
|
+
respond(res, 500, {
|
|
3706
|
+
error: `attach_${handlers[idx].name}_threw`,
|
|
3707
|
+
message: err instanceof Error ? err.message : String(err)
|
|
3708
|
+
});
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
2985
3711
|
routeStandard(req, res) {
|
|
2986
3712
|
const url = req.url ?? "/";
|
|
2987
3713
|
if (req.method === "GET" && url === "/healthz") {
|
|
@@ -2996,7 +3722,8 @@ var LocalServer = class {
|
|
|
2996
3722
|
wsConnected: state.wsConnected,
|
|
2997
3723
|
hostedAgents: state.hostedAgents,
|
|
2998
3724
|
observability: state.observability,
|
|
2999
|
-
memoryReady: this.opts.attachMemory != null
|
|
3725
|
+
memoryReady: this.opts.attachMemory != null,
|
|
3726
|
+
assetReady: this.opts.attachAsset != null
|
|
3000
3727
|
});
|
|
3001
3728
|
return;
|
|
3002
3729
|
}
|
|
@@ -3148,7 +3875,7 @@ var LocalServer = class {
|
|
|
3148
3875
|
respond(res, 400, { error: "missing_taskId" });
|
|
3149
3876
|
return;
|
|
3150
3877
|
}
|
|
3151
|
-
const runId = (0,
|
|
3878
|
+
const runId = (0, import_node_crypto.randomUUID)();
|
|
3152
3879
|
try {
|
|
3153
3880
|
this.opts.onDispatch?.(payload, runId);
|
|
3154
3881
|
} catch (err) {
|
|
@@ -3226,7 +3953,7 @@ async function walkAndDigest(root, current) {
|
|
|
3226
3953
|
}
|
|
3227
3954
|
if (!st.isFile()) continue;
|
|
3228
3955
|
const buf = await import_node_fs7.promises.readFile(full);
|
|
3229
|
-
const sha2562 = (0,
|
|
3956
|
+
const sha2562 = (0, import_node_crypto.createHash)("sha256").update(buf).digest("hex");
|
|
3230
3957
|
out.push({
|
|
3231
3958
|
path: rel,
|
|
3232
3959
|
sha256: sha2562,
|
|
@@ -3239,8 +3966,8 @@ async function walkAndDigest(root, current) {
|
|
|
3239
3966
|
|
|
3240
3967
|
// src/daemon/runner.ts
|
|
3241
3968
|
var import_node_events3 = require("events");
|
|
3242
|
-
var
|
|
3243
|
-
var
|
|
3969
|
+
var import_node_fs11 = require("fs");
|
|
3970
|
+
var import_node_os4 = require("os");
|
|
3244
3971
|
|
|
3245
3972
|
// src/adapters/claude-code/index.ts
|
|
3246
3973
|
var import_node_child_process2 = require("child_process");
|
|
@@ -3891,7 +4618,7 @@ var path4 = __toESM(require("path"), 1);
|
|
|
3891
4618
|
var import_better_sqlite33 = __toESM(require("better-sqlite3"), 1);
|
|
3892
4619
|
var fs2 = __toESM(require("fs"), 1);
|
|
3893
4620
|
var path3 = __toESM(require("path"), 1);
|
|
3894
|
-
var
|
|
4621
|
+
var import_node_crypto2 = require("crypto");
|
|
3895
4622
|
|
|
3896
4623
|
// src/daemon/memory/crypto.ts
|
|
3897
4624
|
function sealPlaintext(content) {
|
|
@@ -4093,16 +4820,17 @@ var MemoryStore = class {
|
|
|
4093
4820
|
const existing = db.prepare(
|
|
4094
4821
|
"SELECT id, version, createdAt FROM memory_pages WHERE workspaceId = ? AND path = ?"
|
|
4095
4822
|
).get(this.opts.workspaceId, input.path);
|
|
4096
|
-
const pageId = existing?.id ?? `page_${(0,
|
|
4823
|
+
const pageId = existing?.id ?? `page_${(0, import_node_crypto2.randomUUID)().replace(/-/g, "").slice(0, 22)}`;
|
|
4097
4824
|
const newVersion = (existing?.version ?? 0) + 1;
|
|
4098
4825
|
const createdAt = existing?.createdAt ?? now;
|
|
4826
|
+
const staleFlag = input.stale ? 1 : 0;
|
|
4099
4827
|
const insertPage = db.prepare(`
|
|
4100
4828
|
INSERT INTO memory_pages (
|
|
4101
4829
|
id, workspaceId, path, title, description, contentHash, version,
|
|
4102
4830
|
pageType, visibilityKind, visibilityImUserId, encrypted, stale,
|
|
4103
4831
|
archivedAt, sourceAssetId, sourceRefsJson, syncStatus,
|
|
4104
4832
|
createdAt, updatedAt
|
|
4105
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0,
|
|
4833
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, NULL, ?, ?, 'local-only', ?, ?)
|
|
4106
4834
|
ON CONFLICT(workspaceId, path) DO UPDATE SET
|
|
4107
4835
|
title = excluded.title,
|
|
4108
4836
|
description = excluded.description,
|
|
@@ -4113,6 +4841,7 @@ var MemoryStore = class {
|
|
|
4113
4841
|
visibilityImUserId = excluded.visibilityImUserId,
|
|
4114
4842
|
sourceAssetId = excluded.sourceAssetId,
|
|
4115
4843
|
sourceRefsJson = excluded.sourceRefsJson,
|
|
4844
|
+
stale = excluded.stale,
|
|
4116
4845
|
updatedAt = excluded.updatedAt
|
|
4117
4846
|
`);
|
|
4118
4847
|
const insertVersion = db.prepare(`
|
|
@@ -4140,6 +4869,7 @@ var MemoryStore = class {
|
|
|
4140
4869
|
input.pageType ?? "leaf",
|
|
4141
4870
|
visibility.kind,
|
|
4142
4871
|
visibilityImUserId,
|
|
4872
|
+
staleFlag,
|
|
4143
4873
|
input.sourceAssetId ?? null,
|
|
4144
4874
|
sourceRefsJson,
|
|
4145
4875
|
createdAt,
|
|
@@ -4220,6 +4950,16 @@ var MemoryStore = class {
|
|
|
4220
4950
|
dbPath: this.opts.dbPath
|
|
4221
4951
|
};
|
|
4222
4952
|
}
|
|
4953
|
+
/**
|
|
4954
|
+
* Record sync cursor for incremental sync. Used by cloud-sync.ts to
|
|
4955
|
+
* persist the high-water mark for future cursor-based catch-up.
|
|
4956
|
+
*/
|
|
4957
|
+
recordCursor(workspaceId, cursor) {
|
|
4958
|
+
const now = Date.now();
|
|
4959
|
+
this.requireDb().prepare(
|
|
4960
|
+
`INSERT OR REPLACE INTO memory_inbox_cursor (workspaceId, cursor, updatedAt) VALUES (?, ?, ?)`
|
|
4961
|
+
).run(workspaceId, cursor, now);
|
|
4962
|
+
}
|
|
4223
4963
|
/**
|
|
4224
4964
|
* Internal accessor for outbox.ts — outbox writes its own table within the
|
|
4225
4965
|
* same DB. Returning the live Database handle keeps outbox transactions
|
|
@@ -4264,7 +5004,7 @@ var MemoryStore = class {
|
|
|
4264
5004
|
}
|
|
4265
5005
|
};
|
|
4266
5006
|
function sha256(s) {
|
|
4267
|
-
return (0,
|
|
5007
|
+
return (0, import_node_crypto2.createHash)("sha256").update(s, "utf8").digest("hex");
|
|
4268
5008
|
}
|
|
4269
5009
|
|
|
4270
5010
|
// src/daemon/memory/search.ts
|
|
@@ -4370,7 +5110,7 @@ function clamp(n, lo, hi) {
|
|
|
4370
5110
|
}
|
|
4371
5111
|
|
|
4372
5112
|
// src/daemon/memory/outbox.ts
|
|
4373
|
-
var
|
|
5113
|
+
var import_node_crypto3 = require("crypto");
|
|
4374
5114
|
|
|
4375
5115
|
// src/daemon/memory/envelope.ts
|
|
4376
5116
|
var import_zod6 = require("zod");
|
|
@@ -4503,7 +5243,7 @@ var MemoryOutbox = class {
|
|
|
4503
5243
|
const now = Date.now();
|
|
4504
5244
|
const parsed = MemoryOutboxEnvelope.safeParse(event);
|
|
4505
5245
|
if (!parsed.success) {
|
|
4506
|
-
const dlId = `dl_${(0,
|
|
5246
|
+
const dlId = `dl_${(0, import_node_crypto3.randomUUID)()}`;
|
|
4507
5247
|
db.prepare(
|
|
4508
5248
|
`INSERT INTO memory_outbox_dead_letter (id, eventType, rawJson, errorJson, createdAt)
|
|
4509
5249
|
VALUES (?, ?, ?, ?, ?)`
|
|
@@ -4520,7 +5260,7 @@ var MemoryOutbox = class {
|
|
|
4520
5260
|
if (validated.eventType === "memory.feedback") {
|
|
4521
5261
|
const check = validateFeedbackTarget(validated);
|
|
4522
5262
|
if (!check.ok) {
|
|
4523
|
-
const dlId = `dl_${(0,
|
|
5263
|
+
const dlId = `dl_${(0, import_node_crypto3.randomUUID)()}`;
|
|
4524
5264
|
db.prepare(
|
|
4525
5265
|
`INSERT INTO memory_outbox_dead_letter (id, eventType, rawJson, errorJson, createdAt)
|
|
4526
5266
|
VALUES (?, ?, ?, ?, ?)`
|
|
@@ -4534,7 +5274,7 @@ var MemoryOutbox = class {
|
|
|
4534
5274
|
return { id: dlId, deadLetter: true };
|
|
4535
5275
|
}
|
|
4536
5276
|
}
|
|
4537
|
-
const rowId = `out_${(0,
|
|
5277
|
+
const rowId = `out_${(0, import_node_crypto3.randomUUID)()}`;
|
|
4538
5278
|
try {
|
|
4539
5279
|
db.prepare(
|
|
4540
5280
|
`INSERT INTO memory_outbox (id, eventType, envelopeJson, idempotencyKey, status, createdAt)
|
|
@@ -4870,6 +5610,84 @@ function defaultLog() {
|
|
|
4870
5610
|
};
|
|
4871
5611
|
}
|
|
4872
5612
|
|
|
5613
|
+
// src/daemon/memory/cloud-sync.ts
|
|
5614
|
+
var LOG = "[CloudMemorySync]";
|
|
5615
|
+
var CLOUD_PAGE_LIMIT = 300;
|
|
5616
|
+
async function initialSyncFromCloud(runtime, cloud, workspaceId) {
|
|
5617
|
+
const slot = runtime.peek(workspaceId);
|
|
5618
|
+
if (!slot) {
|
|
5619
|
+
console.log(`${LOG} No store for workspace=${workspaceId} \u2014 skipping`);
|
|
5620
|
+
return { pulled: 0, skipped: 0 };
|
|
5621
|
+
}
|
|
5622
|
+
const cursorRow = slot.store.rawDb().prepare("SELECT cursor FROM memory_inbox_cursor WHERE workspaceId = ?").get(workspaceId);
|
|
5623
|
+
if (cursorRow) {
|
|
5624
|
+
console.log(`${LOG} Workspace=${workspaceId} already synced (cursor: ${cursorRow.cursor.slice(0, 20)}...) \u2014 skip`);
|
|
5625
|
+
return { pulled: 0, skipped: 0 };
|
|
5626
|
+
}
|
|
5627
|
+
console.log(`${LOG} Fetching cloud pages for workspace=${workspaceId}...`);
|
|
5628
|
+
const resp = await cloud.request(
|
|
5629
|
+
"GET",
|
|
5630
|
+
`/api/im/memory/pages?workspaceId=${encodeURIComponent(workspaceId)}&limit=${CLOUD_PAGE_LIMIT}&stale=all`,
|
|
5631
|
+
{ timeoutMs: 15e3 }
|
|
5632
|
+
);
|
|
5633
|
+
if (!resp.ok) {
|
|
5634
|
+
console.warn(
|
|
5635
|
+
`${LOG} Cloud GET /memory/pages returned ${resp.status}: ${resp.error?.message ?? "unknown"}`
|
|
5636
|
+
);
|
|
5637
|
+
return { pulled: 0, skipped: 0 };
|
|
5638
|
+
}
|
|
5639
|
+
const envelope2 = resp.data;
|
|
5640
|
+
if (!envelope2 || !envelope2.ok) {
|
|
5641
|
+
console.log(`${LOG} Cloud returned non-ok envelope for workspace=${workspaceId}`);
|
|
5642
|
+
return { pulled: 0, skipped: 0 };
|
|
5643
|
+
}
|
|
5644
|
+
const pages = envelope2.data;
|
|
5645
|
+
if (!pages || !Array.isArray(pages) || pages.length === 0) {
|
|
5646
|
+
console.log(`${LOG} No cloud pages to sync for workspace=${workspaceId}`);
|
|
5647
|
+
return { pulled: 0, skipped: 0 };
|
|
5648
|
+
}
|
|
5649
|
+
let pulled = 0;
|
|
5650
|
+
let skipped = 0;
|
|
5651
|
+
for (const page of pages) {
|
|
5652
|
+
let content = page.content ?? "";
|
|
5653
|
+
if (!content) {
|
|
5654
|
+
try {
|
|
5655
|
+
const detailResp = await cloud.request(
|
|
5656
|
+
"GET",
|
|
5657
|
+
`/api/im/memory/pages/${encodeURIComponent(page.id)}?workspaceId=${encodeURIComponent(workspaceId)}`,
|
|
5658
|
+
{ timeoutMs: 5e3 }
|
|
5659
|
+
);
|
|
5660
|
+
if (detailResp.ok && detailResp.data?.data?.content) {
|
|
5661
|
+
content = detailResp.data.data.content;
|
|
5662
|
+
}
|
|
5663
|
+
} catch {
|
|
5664
|
+
}
|
|
5665
|
+
}
|
|
5666
|
+
const visibility = page.visibility === "agent" ? { kind: "agent", imUserId: "" } : { kind: "workspace" };
|
|
5667
|
+
try {
|
|
5668
|
+
slot.store.write({
|
|
5669
|
+
workspaceId,
|
|
5670
|
+
path: page.path,
|
|
5671
|
+
title: page.title ?? void 0,
|
|
5672
|
+
content: content || "",
|
|
5673
|
+
pageType: page.pageType || "leaf",
|
|
5674
|
+
visibility,
|
|
5675
|
+
actorImUserId: "cloud-sync",
|
|
5676
|
+
actorKind: "agent"
|
|
5677
|
+
});
|
|
5678
|
+
pulled++;
|
|
5679
|
+
} catch (err) {
|
|
5680
|
+
console.warn(`${LOG} write failed for ${page.path}:`, err.message);
|
|
5681
|
+
skipped++;
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
slot.store.recordCursor(workspaceId, `synced:${Date.now()}`);
|
|
5685
|
+
console.log(
|
|
5686
|
+
`${LOG} Synced ${pulled} pages${skipped ? `, ${skipped} skipped` : ""} for workspace=${workspaceId}`
|
|
5687
|
+
);
|
|
5688
|
+
return { pulled, skipped };
|
|
5689
|
+
}
|
|
5690
|
+
|
|
4873
5691
|
// src/daemon/memory/runner-wiring.ts
|
|
4874
5692
|
function attachMemoryRunner(opts) {
|
|
4875
5693
|
const runtime = new MemoryRuntime({ baseDir: opts.baseDir, deviceId: opts.deviceId });
|
|
@@ -4900,6 +5718,24 @@ function attachMemoryRunner(opts) {
|
|
|
4900
5718
|
}
|
|
4901
5719
|
};
|
|
4902
5720
|
}
|
|
5721
|
+
async function syncMemoryFromCloud(wiring, cloud, workspaceIds) {
|
|
5722
|
+
const uniqueIds = [...new Set(workspaceIds.filter(Boolean))];
|
|
5723
|
+
if (uniqueIds.length === 0) return;
|
|
5724
|
+
console.log(`[MemorySync] Initial cloud-to-local sync for ${uniqueIds.length} workspace(s)...`);
|
|
5725
|
+
for (const wsId of uniqueIds) {
|
|
5726
|
+
try {
|
|
5727
|
+
wiring.runtime.resolve(wsId);
|
|
5728
|
+
const result = await initialSyncFromCloud(wiring.runtime, cloud, wsId);
|
|
5729
|
+
if (result.pulled > 0 || result.skipped > 0) {
|
|
5730
|
+
console.log(
|
|
5731
|
+
`[MemorySync] workspace=${wsId}: ${result.pulled} pulled, ${result.skipped} skipped`
|
|
5732
|
+
);
|
|
5733
|
+
}
|
|
5734
|
+
} catch (err) {
|
|
5735
|
+
console.error(`[MemorySync] workspace=${wsId} failed:`, err.message);
|
|
5736
|
+
}
|
|
5737
|
+
}
|
|
5738
|
+
}
|
|
4903
5739
|
|
|
4904
5740
|
// src/daemon/memory/fork/select-memories.ts
|
|
4905
5741
|
var SELECT_MEMORIES_SYSTEM_PROMPT = [
|
|
@@ -5018,7 +5854,7 @@ function clamp2(n, lo, hi) {
|
|
|
5018
5854
|
}
|
|
5019
5855
|
|
|
5020
5856
|
// src/daemon/memory/fork/tracing.ts
|
|
5021
|
-
var
|
|
5857
|
+
var import_node_crypto4 = require("crypto");
|
|
5022
5858
|
|
|
5023
5859
|
// src/daemon/memory/fork/runner.ts
|
|
5024
5860
|
var DEFAULT_SNIPPET_MAX_BYTES = 500;
|
|
@@ -5307,8 +6143,235 @@ function parsePrismerUri(uri) {
|
|
|
5307
6143
|
return { workspaceId, path: path7 };
|
|
5308
6144
|
}
|
|
5309
6145
|
|
|
5310
|
-
// src/daemon/
|
|
6146
|
+
// src/daemon/asset/metadata-index.ts
|
|
5311
6147
|
var import_node_fs8 = require("fs");
|
|
6148
|
+
var import_node_path6 = require("path");
|
|
6149
|
+
var DEFAULT_LIMIT = 8;
|
|
6150
|
+
var PULL_PAGE_SIZE = 500;
|
|
6151
|
+
var THROTTLE_MS = 3e4;
|
|
6152
|
+
function rowToMetadata(row) {
|
|
6153
|
+
return {
|
|
6154
|
+
assetId: row.asset_id,
|
|
6155
|
+
contentHash: row.content_hash,
|
|
6156
|
+
filename: row.filename,
|
|
6157
|
+
folderPath: row.folder_path,
|
|
6158
|
+
mime: row.mime,
|
|
6159
|
+
kind: row.kind,
|
|
6160
|
+
sizeBytes: row.size_bytes,
|
|
6161
|
+
description: row.description,
|
|
6162
|
+
assetIndexSeq: row.asset_index_seq
|
|
6163
|
+
};
|
|
6164
|
+
}
|
|
6165
|
+
var AssetMetadataIndex = class {
|
|
6166
|
+
db;
|
|
6167
|
+
cloud;
|
|
6168
|
+
/** Workspace ID — exposed for prismer:// URI construction. */
|
|
6169
|
+
workspaceId;
|
|
6170
|
+
cursorPath;
|
|
6171
|
+
_lastSyncMs = 0;
|
|
6172
|
+
constructor(opts) {
|
|
6173
|
+
this.db = opts.db;
|
|
6174
|
+
this.cloud = opts.cloud;
|
|
6175
|
+
this.workspaceId = opts.workspaceId;
|
|
6176
|
+
if (!(0, import_node_fs8.existsSync)(opts.workspaceStateDir)) {
|
|
6177
|
+
(0, import_node_fs8.mkdirSync)(opts.workspaceStateDir, { recursive: true });
|
|
6178
|
+
}
|
|
6179
|
+
this.cursorPath = (0, import_node_path6.join)(opts.workspaceStateDir, "asset-metadata-cursor.json");
|
|
6180
|
+
}
|
|
6181
|
+
/** Persisted cursor for this workspace, or 0 on first run / corrupted file. */
|
|
6182
|
+
readCursor() {
|
|
6183
|
+
if (!(0, import_node_fs8.existsSync)(this.cursorPath)) return 0;
|
|
6184
|
+
try {
|
|
6185
|
+
const parsed = JSON.parse((0, import_node_fs8.readFileSync)(this.cursorPath, "utf8"));
|
|
6186
|
+
if (parsed.workspaceId !== this.workspaceId) return 0;
|
|
6187
|
+
return parsed.cursor;
|
|
6188
|
+
} catch {
|
|
6189
|
+
return 0;
|
|
6190
|
+
}
|
|
6191
|
+
}
|
|
6192
|
+
writeCursor(cursor) {
|
|
6193
|
+
const payload = {
|
|
6194
|
+
workspaceId: this.workspaceId,
|
|
6195
|
+
cursor,
|
|
6196
|
+
writtenAt: Date.now()
|
|
6197
|
+
};
|
|
6198
|
+
(0, import_node_fs8.writeFileSync)(this.cursorPath, JSON.stringify(payload, null, 2));
|
|
6199
|
+
}
|
|
6200
|
+
/**
|
|
6201
|
+
* Pull incremental asset metadata changes since the persisted cursor and
|
|
6202
|
+
* upsert into the local index. Newer rows overwrite older ones by
|
|
6203
|
+
* (workspace_id, asset_id) primary key.
|
|
6204
|
+
*
|
|
6205
|
+
* Returns the count of items applied + the new cursor. Throttled: if called
|
|
6206
|
+
* within 30s of the last successful pull, returns immediately.
|
|
6207
|
+
*/
|
|
6208
|
+
async pullDelta(opts) {
|
|
6209
|
+
const now = Date.now();
|
|
6210
|
+
if (now - this._lastSyncMs < THROTTLE_MS) {
|
|
6211
|
+
return { applied: 0, cursor: this.readCursor() };
|
|
6212
|
+
}
|
|
6213
|
+
const since = this.readCursor();
|
|
6214
|
+
const sinceParam = since > 0 ? `&since=${since}` : "";
|
|
6215
|
+
const envelope2 = await this.cloud.get(
|
|
6216
|
+
`/api/im/assets/index?workspaceId=${encodeURIComponent(this.workspaceId)}&limit=${PULL_PAGE_SIZE}${sinceParam}`,
|
|
6217
|
+
{ signal: opts?.signal }
|
|
6218
|
+
);
|
|
6219
|
+
const upsert = this.db.prepare(`
|
|
6220
|
+
INSERT INTO asset_metadata_index
|
|
6221
|
+
(workspace_id, asset_id, content_hash, filename, folder_path, mime, kind, size_bytes, description, asset_index_seq, updated_at)
|
|
6222
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6223
|
+
ON CONFLICT(workspace_id, asset_id) DO UPDATE SET
|
|
6224
|
+
content_hash = excluded.content_hash,
|
|
6225
|
+
filename = excluded.filename,
|
|
6226
|
+
folder_path = excluded.folder_path,
|
|
6227
|
+
mime = excluded.mime,
|
|
6228
|
+
kind = excluded.kind,
|
|
6229
|
+
size_bytes = excluded.size_bytes,
|
|
6230
|
+
description = excluded.description,
|
|
6231
|
+
asset_index_seq = excluded.asset_index_seq,
|
|
6232
|
+
updated_at = excluded.updated_at
|
|
6233
|
+
`);
|
|
6234
|
+
const nowTs = Date.now();
|
|
6235
|
+
let applied = 0;
|
|
6236
|
+
const tx = this.db.transaction((items) => {
|
|
6237
|
+
for (const item of items) {
|
|
6238
|
+
upsert.run(
|
|
6239
|
+
this.workspaceId,
|
|
6240
|
+
item.assetId,
|
|
6241
|
+
item.contentHash,
|
|
6242
|
+
item.filename ?? null,
|
|
6243
|
+
item.folderPath ?? null,
|
|
6244
|
+
item.mime,
|
|
6245
|
+
item.kind,
|
|
6246
|
+
item.sizeBytes,
|
|
6247
|
+
item.description ?? null,
|
|
6248
|
+
item.assetIndexSeq,
|
|
6249
|
+
nowTs
|
|
6250
|
+
);
|
|
6251
|
+
applied += 1;
|
|
6252
|
+
}
|
|
6253
|
+
});
|
|
6254
|
+
try {
|
|
6255
|
+
tx(envelope2.items);
|
|
6256
|
+
this.writeCursor(envelope2.cursor);
|
|
6257
|
+
} catch (err) {
|
|
6258
|
+
throw err;
|
|
6259
|
+
}
|
|
6260
|
+
this._lastSyncMs = now;
|
|
6261
|
+
return { applied, cursor: envelope2.cursor };
|
|
6262
|
+
}
|
|
6263
|
+
/**
|
|
6264
|
+
* Search local index by filename or description substring.
|
|
6265
|
+
* Escapes LIKE wildcards (% and _). Default limit 8.
|
|
6266
|
+
*/
|
|
6267
|
+
search(query, limit) {
|
|
6268
|
+
const escaped = query.replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
6269
|
+
const pattern = `%${escaped}%`;
|
|
6270
|
+
const limitVal = Math.min(Math.max(limit ?? DEFAULT_LIMIT, 1), 200);
|
|
6271
|
+
const rows = this.db.prepare(
|
|
6272
|
+
`SELECT * FROM asset_metadata_index
|
|
6273
|
+
WHERE workspace_id = ?
|
|
6274
|
+
AND (filename LIKE ? ESCAPE '\\' OR description LIKE ? ESCAPE '\\')
|
|
6275
|
+
ORDER BY asset_index_seq DESC
|
|
6276
|
+
LIMIT ?`
|
|
6277
|
+
).all(this.workspaceId, pattern, pattern, limitVal);
|
|
6278
|
+
return rows.map(rowToMetadata);
|
|
6279
|
+
}
|
|
6280
|
+
/** Exact match on filename column. Returns undefined if not indexed. */
|
|
6281
|
+
resolveByFilename(filename) {
|
|
6282
|
+
const row = this.db.prepare("SELECT * FROM asset_metadata_index WHERE workspace_id = ? AND filename = ?").get(this.workspaceId, filename);
|
|
6283
|
+
return row ? rowToMetadata(row) : void 0;
|
|
6284
|
+
}
|
|
6285
|
+
/** Batch exact match — returns Map for O(1) access. */
|
|
6286
|
+
resolveByFilenames(filenames) {
|
|
6287
|
+
if (filenames.length === 0) return /* @__PURE__ */ new Map();
|
|
6288
|
+
const placeholders = filenames.map(() => "?").join(",");
|
|
6289
|
+
const params = [this.workspaceId, ...filenames];
|
|
6290
|
+
const rows = this.db.prepare(
|
|
6291
|
+
`SELECT * FROM asset_metadata_index
|
|
6292
|
+
WHERE workspace_id = ? AND filename IN (${placeholders})`
|
|
6293
|
+
).all(...params);
|
|
6294
|
+
const map = /* @__PURE__ */ new Map();
|
|
6295
|
+
for (const row of rows) {
|
|
6296
|
+
if (row.filename) map.set(row.filename, rowToMetadata(row));
|
|
6297
|
+
}
|
|
6298
|
+
return map;
|
|
6299
|
+
}
|
|
6300
|
+
};
|
|
6301
|
+
|
|
6302
|
+
// src/daemon/asset/rpc.ts
|
|
6303
|
+
var ASSET_PATH_PREFIX = "/local/asset/";
|
|
6304
|
+
function attachAssetRpc(opts) {
|
|
6305
|
+
return async (req, res) => {
|
|
6306
|
+
const url = req.url ?? "/";
|
|
6307
|
+
if (!url.startsWith(ASSET_PATH_PREFIX)) return false;
|
|
6308
|
+
const [pathOnly = ""] = url.split("?", 2);
|
|
6309
|
+
const subpath = pathOnly.slice(ASSET_PATH_PREFIX.length);
|
|
6310
|
+
const method = req.method ?? "GET";
|
|
6311
|
+
try {
|
|
6312
|
+
if (method === "POST" && subpath === "search") {
|
|
6313
|
+
const body = await readJson3(req);
|
|
6314
|
+
return handleSearch2(opts.resolveIndex, body, res);
|
|
6315
|
+
}
|
|
6316
|
+
respond3(res, 404, { error: "asset_route_not_found", path: url });
|
|
6317
|
+
return true;
|
|
6318
|
+
} catch (err) {
|
|
6319
|
+
respond3(res, 500, {
|
|
6320
|
+
error: "asset_rpc_failed",
|
|
6321
|
+
message: err instanceof Error ? err.message : String(err)
|
|
6322
|
+
});
|
|
6323
|
+
return true;
|
|
6324
|
+
}
|
|
6325
|
+
};
|
|
6326
|
+
}
|
|
6327
|
+
function handleSearch2(resolveIndex, body, res) {
|
|
6328
|
+
if (!body || typeof body !== "object") {
|
|
6329
|
+
return respond4002(res, "request body must be a JSON object");
|
|
6330
|
+
}
|
|
6331
|
+
const b = body;
|
|
6332
|
+
if (typeof b.workspaceId !== "string" || !b.workspaceId) {
|
|
6333
|
+
return respond4002(res, "workspaceId is required (string)");
|
|
6334
|
+
}
|
|
6335
|
+
if (typeof b.query !== "string" || !b.query.trim()) {
|
|
6336
|
+
return respond4002(res, "query is required (non-empty string)");
|
|
6337
|
+
}
|
|
6338
|
+
const index = resolveIndex(b.workspaceId);
|
|
6339
|
+
if (!index) {
|
|
6340
|
+
respond3(res, 404, {
|
|
6341
|
+
error: "workspace_index_not_found",
|
|
6342
|
+
workspaceId: b.workspaceId,
|
|
6343
|
+
message: "No asset metadata index for this workspace. Ensure the daemon has synced asset metadata."
|
|
6344
|
+
});
|
|
6345
|
+
return true;
|
|
6346
|
+
}
|
|
6347
|
+
const limit = typeof b.limit === "number" && b.limit > 0 ? b.limit : void 0;
|
|
6348
|
+
const items = index.search(b.query.trim(), limit);
|
|
6349
|
+
respond3(res, 200, { items });
|
|
6350
|
+
return true;
|
|
6351
|
+
}
|
|
6352
|
+
function respond3(res, status, body) {
|
|
6353
|
+
res.statusCode = status;
|
|
6354
|
+
res.setHeader("Content-Type", "application/json");
|
|
6355
|
+
res.end(JSON.stringify(body));
|
|
6356
|
+
}
|
|
6357
|
+
function respond4002(res, message) {
|
|
6358
|
+
respond3(res, 400, { error: "invalid_request", message });
|
|
6359
|
+
return true;
|
|
6360
|
+
}
|
|
6361
|
+
async function readJson3(req) {
|
|
6362
|
+
let raw = "";
|
|
6363
|
+
req.setEncoding("utf8");
|
|
6364
|
+
for await (const chunk of req) raw += chunk;
|
|
6365
|
+
if (!raw) return {};
|
|
6366
|
+
try {
|
|
6367
|
+
return JSON.parse(raw);
|
|
6368
|
+
} catch {
|
|
6369
|
+
throw new Error("invalid_json");
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
|
|
6373
|
+
// src/daemon/outbox-watcher.ts
|
|
6374
|
+
var import_node_fs9 = require("fs");
|
|
5312
6375
|
var path5 = __toESM(require("path"), 1);
|
|
5313
6376
|
var DEFAULT_INTERVAL_MS = 2e3;
|
|
5314
6377
|
var RESERVED_SUBDIR = "_uploaded";
|
|
@@ -5476,7 +6539,7 @@ var OutboxWatcher = class {
|
|
|
5476
6539
|
async scanDir(dir, kind, task) {
|
|
5477
6540
|
let entries = [];
|
|
5478
6541
|
try {
|
|
5479
|
-
entries = await
|
|
6542
|
+
entries = await import_node_fs9.promises.readdir(dir);
|
|
5480
6543
|
} catch (err) {
|
|
5481
6544
|
const code = err.code;
|
|
5482
6545
|
if (code === "ENOENT") return;
|
|
@@ -5489,7 +6552,7 @@ var OutboxWatcher = class {
|
|
|
5489
6552
|
const full = path5.join(dir, name);
|
|
5490
6553
|
let st;
|
|
5491
6554
|
try {
|
|
5492
|
-
st = await
|
|
6555
|
+
st = await import_node_fs9.promises.stat(full);
|
|
5493
6556
|
} catch {
|
|
5494
6557
|
continue;
|
|
5495
6558
|
}
|
|
@@ -5514,7 +6577,7 @@ var OutboxWatcher = class {
|
|
|
5514
6577
|
this.log("warn", `skip ${filePath}: no active taskId (no dispatch/handoff received yet)`);
|
|
5515
6578
|
return;
|
|
5516
6579
|
}
|
|
5517
|
-
const bytes = await
|
|
6580
|
+
const bytes = await import_node_fs9.promises.readFile(filePath);
|
|
5518
6581
|
const fileName = path5.basename(filePath);
|
|
5519
6582
|
const blob = new Blob([new Uint8Array(bytes)]);
|
|
5520
6583
|
const form = new FormData();
|
|
@@ -5564,8 +6627,8 @@ var OutboxWatcher = class {
|
|
|
5564
6627
|
|
|
5565
6628
|
// src/daemon/shell-executor.ts
|
|
5566
6629
|
var import_node_child_process4 = require("child_process");
|
|
5567
|
-
var
|
|
5568
|
-
var
|
|
6630
|
+
var import_node_fs10 = require("fs");
|
|
6631
|
+
var import_node_path7 = require("path");
|
|
5569
6632
|
var DEFAULT_OUTPUT_LIMIT = 256 * 1024;
|
|
5570
6633
|
var DEFAULT_TIMEOUT = 6e4;
|
|
5571
6634
|
var HARD_MAX_TIMEOUT = 30 * 6e4;
|
|
@@ -5601,7 +6664,7 @@ async function executeShellDispatch(payload, deps) {
|
|
|
5601
6664
|
const command = readCommand(payload, execution);
|
|
5602
6665
|
if (!command.trim()) return fail(payload.taskId, "shell_command_required", "Shell command is required");
|
|
5603
6666
|
const cwd = resolveCwd(execution.cwd, deps.config.defaultCwd);
|
|
5604
|
-
if (!(0,
|
|
6667
|
+
if (!(0, import_node_fs10.existsSync)(cwd)) return fail(payload.taskId, "shell_cwd_missing", `cwd does not exist: ${cwd}`);
|
|
5605
6668
|
const timeoutMs = Math.min(
|
|
5606
6669
|
typeof payload.timeoutMs === "number" && payload.timeoutMs > 0 ? payload.timeoutMs : deps.config.maxTimeoutMs,
|
|
5607
6670
|
deps.config.maxTimeoutMs
|
|
@@ -5703,8 +6766,8 @@ function readCommand(payload, execution) {
|
|
|
5703
6766
|
return payload.prompt;
|
|
5704
6767
|
}
|
|
5705
6768
|
function resolveCwd(raw, fallback) {
|
|
5706
|
-
if (typeof raw !== "string" || raw.trim() === "") return (0,
|
|
5707
|
-
return (0,
|
|
6769
|
+
if (typeof raw !== "string" || raw.trim() === "") return (0, import_node_path7.resolve)(fallback);
|
|
6770
|
+
return (0, import_node_path7.resolve)(raw);
|
|
5708
6771
|
}
|
|
5709
6772
|
function clampNumber(value, fallback, min, max) {
|
|
5710
6773
|
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
@@ -5753,6 +6816,7 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
5753
6816
|
localServer;
|
|
5754
6817
|
outboxWatcher;
|
|
5755
6818
|
memoryWiring;
|
|
6819
|
+
assetMetadataIndexes = /* @__PURE__ */ new Map();
|
|
5756
6820
|
state = "idle";
|
|
5757
6821
|
startedAt = 0;
|
|
5758
6822
|
workspaceId = "";
|
|
@@ -5769,6 +6833,9 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
5769
6833
|
this.paths = this.opts.paths ?? resolvePaths();
|
|
5770
6834
|
this.config = this.opts.configOverride ?? loadConfig(this.paths);
|
|
5771
6835
|
this.shellConfig = resolveShellConfig(this.config.shell);
|
|
6836
|
+
if (process.env.PRISMER_WORKSPACE_ID) {
|
|
6837
|
+
this.workspaceId = process.env.PRISMER_WORKSPACE_ID;
|
|
6838
|
+
}
|
|
5772
6839
|
process.env.PRISMER_BASE_URL = this.config.cloud_api_base;
|
|
5773
6840
|
process.env.PRISMER_API_KEY = this.config.api_key;
|
|
5774
6841
|
this.db = openLocalDb(this.paths.localDb);
|
|
@@ -5806,6 +6873,16 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
5806
6873
|
baseDir: `${this.paths.root}/memory`,
|
|
5807
6874
|
deviceId: this.config.daemon_id
|
|
5808
6875
|
});
|
|
6876
|
+
if (this.memoryWiring && this.workspaceId) {
|
|
6877
|
+
syncMemoryFromCloud(this.memoryWiring, this.cloud, [this.workspaceId]).catch(
|
|
6878
|
+
(err) => console.error("[Daemon] Initial memory sync failed:", err.message)
|
|
6879
|
+
);
|
|
6880
|
+
}
|
|
6881
|
+
if (this.workspaceId) {
|
|
6882
|
+
this.syncAssetMetadata(this.workspaceId).catch(
|
|
6883
|
+
(err) => console.error("[Daemon] Initial asset metadata sync failed:", err.message)
|
|
6884
|
+
);
|
|
6885
|
+
}
|
|
5809
6886
|
const containerId = process.env.PRISMER_CONTAINER_ID;
|
|
5810
6887
|
const isContainer = !!containerId || process.env.PRISMER_RUNTIME_MODE === "container";
|
|
5811
6888
|
this.outboxWatcher = new OutboxWatcher({
|
|
@@ -5853,7 +6930,10 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
5853
6930
|
// wiring step so it could be reviewed alongside the host-adapter
|
|
5854
6931
|
// consumer (Hermes T2-B), which is what surfaces these routes to
|
|
5855
6932
|
// an actual agent process.
|
|
5856
|
-
attachMemory: this.memoryWiring ? attachMemoryRpc({ runtime: this.memoryWiring.runtime }) : void 0
|
|
6933
|
+
attachMemory: this.memoryWiring ? attachMemoryRpc({ runtime: this.memoryWiring.runtime }) : void 0,
|
|
6934
|
+
attachAsset: attachAssetRpc({
|
|
6935
|
+
resolveIndex: (workspaceId) => this.assetMetadataIndexes.get(workspaceId)
|
|
6936
|
+
})
|
|
5857
6937
|
});
|
|
5858
6938
|
await this.localServer.start();
|
|
5859
6939
|
}
|
|
@@ -6049,10 +7129,10 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
6049
7129
|
const rawJson = process.env.PRISMER_HOSTED_AGENT_JSON;
|
|
6050
7130
|
let raw;
|
|
6051
7131
|
if (rawFile) {
|
|
6052
|
-
if (!(0,
|
|
7132
|
+
if (!(0, import_node_fs11.existsSync)(rawFile)) {
|
|
6053
7133
|
throw new Error(`PRISMER_HOSTED_AGENT_FILE not found: ${rawFile}`);
|
|
6054
7134
|
}
|
|
6055
|
-
raw = (0,
|
|
7135
|
+
raw = (0, import_node_fs11.readFileSync)(rawFile, "utf8");
|
|
6056
7136
|
} else if (rawJson) {
|
|
6057
7137
|
raw = rawJson;
|
|
6058
7138
|
}
|
|
@@ -6148,7 +7228,7 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
6148
7228
|
const payload = {
|
|
6149
7229
|
daemonId: this.config.daemon_id,
|
|
6150
7230
|
daemonVersion: this.opts.daemonVersion ?? "0.0.0",
|
|
6151
|
-
platform: (0,
|
|
7231
|
+
platform: (0, import_node_os4.platform)() === "win32" ? "win32" : (0, import_node_os4.platform)() === "linux" ? "linux" : "darwin",
|
|
6152
7232
|
agents: Array.from(this.hostedAgents.values()).map((a) => ({
|
|
6153
7233
|
imUserId: a.imUserId,
|
|
6154
7234
|
name: a.name,
|
|
@@ -6182,12 +7262,23 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
6182
7262
|
case "workspace_file.changed":
|
|
6183
7263
|
this.onWorkspaceFileChanged(msg.payload);
|
|
6184
7264
|
return;
|
|
7265
|
+
case "asset.changed":
|
|
7266
|
+
void this.onAssetChanged(msg.payload);
|
|
7267
|
+
return;
|
|
6185
7268
|
default:
|
|
6186
7269
|
this.emit("unknown-message", msg);
|
|
6187
7270
|
}
|
|
6188
7271
|
}
|
|
6189
7272
|
async onHostAcked(payload) {
|
|
6190
7273
|
this.workspaceId = payload.workspaceId;
|
|
7274
|
+
if (this.memoryWiring && this.workspaceId) {
|
|
7275
|
+
syncMemoryFromCloud(this.memoryWiring, this.cloud, [this.workspaceId]).catch(
|
|
7276
|
+
(err) => console.error("[Daemon] Initial memory sync failed:", err.message)
|
|
7277
|
+
);
|
|
7278
|
+
}
|
|
7279
|
+
this.syncAssetMetadata(this.workspaceId).catch(
|
|
7280
|
+
(err) => console.error("[Daemon] Asset metadata sync failed:", err.message)
|
|
7281
|
+
);
|
|
6191
7282
|
for (const id of payload.profilesToSync) {
|
|
6192
7283
|
try {
|
|
6193
7284
|
await this.syncProfileFromCloud(id);
|
|
@@ -6247,6 +7338,7 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
6247
7338
|
paths: this.paths,
|
|
6248
7339
|
signal: ctrl.signal,
|
|
6249
7340
|
ensureService: (profile, adapter) => this.servicePool.ensureService(profile, adapter),
|
|
7341
|
+
assetMetadataIndexes: this.assetMetadataIndexes,
|
|
6250
7342
|
onProgress: () => {
|
|
6251
7343
|
const running = this.runningTasks.get(payload.taskId);
|
|
6252
7344
|
if (running) running.lastProgressAt = Date.now();
|
|
@@ -6347,6 +7439,19 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
6347
7439
|
this.emit("sync-error", err);
|
|
6348
7440
|
}
|
|
6349
7441
|
}
|
|
7442
|
+
async onAssetChanged(payload) {
|
|
7443
|
+
if (!payload.workspaceId) return;
|
|
7444
|
+
const index = this.assetMetadataIndexes.get(payload.workspaceId);
|
|
7445
|
+
if (!index) return;
|
|
7446
|
+
try {
|
|
7447
|
+
const result = await index.pullDelta();
|
|
7448
|
+
if (result.applied > 0) {
|
|
7449
|
+
console.log(`[Daemon] asset.changed workspace=${payload.workspaceId} applied=${result.applied}`);
|
|
7450
|
+
}
|
|
7451
|
+
} catch (err) {
|
|
7452
|
+
console.error(`[Daemon] asset.changed pullDelta failed workspace=${payload.workspaceId}:`, err.message);
|
|
7453
|
+
}
|
|
7454
|
+
}
|
|
6350
7455
|
onWorkspaceFileChanged(payload) {
|
|
6351
7456
|
if (payload.operation === "delete") {
|
|
6352
7457
|
this.db.prepare("DELETE FROM workspace_files_mirror WHERE workspace_id = ? AND path = ?").run(payload.workspaceId, payload.path);
|
|
@@ -6382,6 +7487,28 @@ var Runner = class extends import_node_events3.EventEmitter {
|
|
|
6382
7487
|
}
|
|
6383
7488
|
};
|
|
6384
7489
|
}
|
|
7490
|
+
/**
|
|
7491
|
+
* Ensure an AssetMetadataIndex exists for the given workspace and pull
|
|
7492
|
+
* delta from cloud. Idempotent — creates the index on first call, reuses
|
|
7493
|
+
* it on subsequent calls. Same cursor-catch-up semantics as WorkspaceMirror.
|
|
7494
|
+
*/
|
|
7495
|
+
async syncAssetMetadata(workspaceId) {
|
|
7496
|
+
let index = this.assetMetadataIndexes.get(workspaceId);
|
|
7497
|
+
if (!index) {
|
|
7498
|
+
const stateDir = `${this.paths.root}/${workspaceId}`;
|
|
7499
|
+
index = new AssetMetadataIndex({
|
|
7500
|
+
db: this.db,
|
|
7501
|
+
cloud: this.cloud,
|
|
7502
|
+
workspaceId,
|
|
7503
|
+
workspaceStateDir: stateDir
|
|
7504
|
+
});
|
|
7505
|
+
this.assetMetadataIndexes.set(workspaceId, index);
|
|
7506
|
+
}
|
|
7507
|
+
const result = await index.pullDelta();
|
|
7508
|
+
if (result.applied > 0) {
|
|
7509
|
+
console.log(`[AssetMeta] workspace=${workspaceId}: ${result.applied} applied, cursor=${result.cursor}`);
|
|
7510
|
+
}
|
|
7511
|
+
}
|
|
6385
7512
|
/**
|
|
6386
7513
|
* SyncWorker FlushFn — pushes local writes to cloud.
|
|
6387
7514
|
*
|
|
@@ -6505,715 +7632,165 @@ function validateStaticHostedAgent(raw) {
|
|
|
6505
7632
|
if (!raw || typeof raw !== "object") throw new Error("binding must be a JSON object");
|
|
6506
7633
|
const obj = raw;
|
|
6507
7634
|
const profile = obj.profile;
|
|
6508
|
-
const capabilities = obj.capabilities;
|
|
6509
|
-
if (typeof obj.workspaceId !== "string" || obj.workspaceId.length === 0) throw new Error("workspaceId is required");
|
|
6510
|
-
if (typeof obj.imUserId !== "string" || obj.imUserId.length === 0) throw new Error("imUserId is required");
|
|
6511
|
-
if (typeof obj.name !== "string" || obj.name.length === 0) throw new Error("name is required");
|
|
6512
|
-
if (typeof obj.adapterName !== "string" || obj.adapterName.length === 0) throw new Error("adapterName is required");
|
|
6513
|
-
if (!Array.isArray(capabilities) || capabilities.some((v) => typeof v !== "string")) {
|
|
6514
|
-
throw new Error("capabilities must be a string array");
|
|
6515
|
-
}
|
|
6516
|
-
if (!profile || typeof profile !== "object") throw new Error("profile is required");
|
|
6517
|
-
if (typeof profile.id !== "string" || profile.id.length === 0) throw new Error("profile.id is required");
|
|
6518
|
-
if (typeof profile.name !== "string" || profile.name.length === 0) throw new Error("profile.name is required");
|
|
6519
|
-
if (typeof profile.adapterName !== "string" || profile.adapterName.length === 0) {
|
|
6520
|
-
throw new Error("profile.adapterName is required");
|
|
6521
|
-
}
|
|
6522
|
-
if (profile.config !== void 0 && (!profile.config || typeof profile.config !== "object" || Array.isArray(profile.config))) {
|
|
6523
|
-
throw new Error("profile.config must be a JSON object");
|
|
6524
|
-
}
|
|
6525
|
-
return {
|
|
6526
|
-
workspaceId: obj.workspaceId,
|
|
6527
|
-
imUserId: obj.imUserId,
|
|
6528
|
-
name: obj.name,
|
|
6529
|
-
adapterName: obj.adapterName,
|
|
6530
|
-
capabilities,
|
|
6531
|
-
profile: {
|
|
6532
|
-
id: profile.id,
|
|
6533
|
-
name: profile.name,
|
|
6534
|
-
adapterName: profile.adapterName,
|
|
6535
|
-
config: profile.config ?? {},
|
|
6536
|
-
version: typeof profile.version === "number" && Number.isFinite(profile.version) ? profile.version : 1
|
|
6537
|
-
}
|
|
6538
|
-
};
|
|
6539
|
-
}
|
|
6540
|
-
function safeJsonParse(raw) {
|
|
6541
|
-
try {
|
|
6542
|
-
return JSON.parse(raw);
|
|
6543
|
-
} catch {
|
|
6544
|
-
return raw;
|
|
6545
|
-
}
|
|
6546
|
-
}
|
|
6547
|
-
|
|
6548
|
-
// src/pair.ts
|
|
6549
|
-
var import_node_crypto6 = require("crypto");
|
|
6550
|
-
var import_node_os4 = require("os");
|
|
6551
|
-
var import_promises = require("timers/promises");
|
|
6552
|
-
var import_qrcode = __toESM(require("qrcode"), 1);
|
|
6553
|
-
async function pair(opts) {
|
|
6554
|
-
const paths = opts.paths ?? resolvePaths();
|
|
6555
|
-
if (configExists(paths) && !opts.force) {
|
|
6556
|
-
throw new Error(
|
|
6557
|
-
`Config already exists at ${paths.configFile}. Pass --force to overwrite, or run \`prismer status\` to inspect.`
|
|
6558
|
-
);
|
|
6559
|
-
}
|
|
6560
|
-
const isLocalOnly = opts.isLocalOnly ?? (() => process.env.LOCAL_ONLY === "1");
|
|
6561
|
-
const localOnlyMode = !!opts.asUserEmail;
|
|
6562
|
-
if (localOnlyMode && !isLocalOnly()) {
|
|
6563
|
-
throw new Error(
|
|
6564
|
-
"pair: --as-user requires LOCAL_ONLY=1. Without that gate, this would skip mobile approval and silently mint a key for the named user."
|
|
6565
|
-
);
|
|
6566
|
-
}
|
|
6567
|
-
const { publicKey } = (0, import_node_crypto6.generateKeyPairSync)("ed25519");
|
|
6568
|
-
const devicePub = publicKey.export({ format: "der", type: "spki" }).toString("base64");
|
|
6569
|
-
const cloud = new CloudClient({
|
|
6570
|
-
baseUrl: opts.cloudBaseUrl,
|
|
6571
|
-
apiKey: "pending",
|
|
6572
|
-
// not used: we pass auth:false
|
|
6573
|
-
fetchImpl: opts.fetchImpl
|
|
6574
|
-
});
|
|
6575
|
-
const offerRes = await cloud.request(
|
|
6576
|
-
"POST",
|
|
6577
|
-
"/api/im/pair/offer",
|
|
6578
|
-
{
|
|
6579
|
-
auth: false,
|
|
6580
|
-
body: { devicePub, deviceName: opts.deviceName ?? (0, import_node_os4.hostname)() }
|
|
6581
|
-
}
|
|
6582
|
-
);
|
|
6583
|
-
if (!offerRes.ok) {
|
|
6584
|
-
throw new Error(`pair: offer failed (${offerRes.status}): ${offerRes.error?.message}`);
|
|
6585
|
-
}
|
|
6586
|
-
const offer = unwrapEnvelope(offerRes.data);
|
|
6587
|
-
if (!offer.nonce || !offer.qrUrl) {
|
|
6588
|
-
throw new Error("pair: cloud returned no nonce/qrUrl");
|
|
6589
|
-
}
|
|
6590
|
-
if (localOnlyMode) {
|
|
6591
|
-
const approveRes = await cloud.request(
|
|
6592
|
-
"POST",
|
|
6593
|
-
"/api/im/pair/local-only-approve",
|
|
6594
|
-
{
|
|
6595
|
-
auth: false,
|
|
6596
|
-
body: { nonce: offer.nonce, asUserEmail: opts.asUserEmail }
|
|
6597
|
-
}
|
|
6598
|
-
);
|
|
6599
|
-
if (!approveRes.ok) {
|
|
6600
|
-
throw new Error(
|
|
6601
|
-
`pair: local-only-approve failed (${approveRes.status}): ${approveRes.error?.message ?? "unknown"}`
|
|
6602
|
-
);
|
|
6603
|
-
}
|
|
6604
|
-
process.stdout.write(`[pair] LOCAL_ONLY approved as ${opts.asUserEmail} \u2014 no QR shown
|
|
6605
|
-
`);
|
|
6606
|
-
} else {
|
|
6607
|
-
const qrAscii = await import_qrcode.default.toString(offer.qrUrl, { type: "terminal", small: true });
|
|
6608
|
-
process.stdout.write(qrAscii);
|
|
6609
|
-
process.stdout.write(`
|
|
6610
|
-
Scan with Lumin to approve, or open: ${offer.qrUrl}
|
|
6611
|
-
|
|
6612
|
-
`);
|
|
6613
|
-
opts.onQrReady?.(offer.qrUrl);
|
|
6614
|
-
}
|
|
6615
|
-
const pollPath = `/api/im/pair/poll/${encodeURIComponent(offer.nonce)}?devicePub=${encodeURIComponent(devicePub)}`;
|
|
6616
|
-
const maxAttempts = opts.maxPollAttempts ?? 60;
|
|
6617
|
-
const pollIntervalMs = opts.pollIntervalMs ?? 5e3;
|
|
6618
|
-
for (let i = 0; i < maxAttempts; i += 1) {
|
|
6619
|
-
if (i > 0 || !localOnlyMode) {
|
|
6620
|
-
await (0, import_promises.setTimeout)(pollIntervalMs);
|
|
6621
|
-
}
|
|
6622
|
-
const res = await cloud.request(
|
|
6623
|
-
"GET",
|
|
6624
|
-
pollPath,
|
|
6625
|
-
{ auth: false }
|
|
6626
|
-
);
|
|
6627
|
-
if (res.ok) {
|
|
6628
|
-
const body = unwrapEnvelope(res.data);
|
|
6629
|
-
if (body.apiKey) {
|
|
6630
|
-
const config = {
|
|
6631
|
-
api_key: body.apiKey,
|
|
6632
|
-
cloud_api_base: opts.cloudBaseUrl,
|
|
6633
|
-
daemon_id: newDaemonId()
|
|
6634
|
-
};
|
|
6635
|
-
saveConfig(config, paths);
|
|
6636
|
-
return { config, paths };
|
|
6637
|
-
}
|
|
6638
|
-
}
|
|
6639
|
-
if (res.status === 404 || res.status === 202) {
|
|
6640
|
-
continue;
|
|
6641
|
-
}
|
|
6642
|
-
if (res.status >= 400 && res.status !== 404) {
|
|
6643
|
-
throw new Error(`pair: poll failed (${res.status}): ${res.error?.message}`);
|
|
6644
|
-
}
|
|
6645
|
-
}
|
|
6646
|
-
throw new Error("pair: timed out waiting for approval (5 min)");
|
|
6647
|
-
}
|
|
6648
|
-
function unwrapEnvelope(raw) {
|
|
6649
|
-
if (raw && typeof raw === "object" && "ok" in raw) {
|
|
6650
|
-
const env = raw;
|
|
6651
|
-
if (env.ok && env.data) return env.data;
|
|
6652
|
-
}
|
|
6653
|
-
return raw;
|
|
6654
|
-
}
|
|
6655
|
-
|
|
6656
|
-
// src/cli/index.ts
|
|
6657
|
-
var import_commander18 = require("commander");
|
|
6658
|
-
|
|
6659
|
-
// src/cli/commands/adapter.ts
|
|
6660
|
-
var import_node_child_process5 = require("child_process");
|
|
6661
|
-
var import_node_fs12 = require("fs");
|
|
6662
|
-
var import_node_os5 = require("os");
|
|
6663
|
-
var import_node_path8 = require("path");
|
|
6664
|
-
var import_commander = require("commander");
|
|
6665
|
-
|
|
6666
|
-
// src/cli/util.ts
|
|
6667
|
-
var import_node_fs11 = require("fs");
|
|
6668
|
-
var import_node_path7 = require("path");
|
|
6669
|
-
|
|
6670
|
-
// src/cli/ui.ts
|
|
6671
|
-
var fs4 = __toESM(require("fs"), 1);
|
|
6672
|
-
var path6 = __toESM(require("path"), 1);
|
|
6673
|
-
var import_node_url = require("url");
|
|
6674
|
-
var import_meta2 = {};
|
|
6675
|
-
var BRAILLE_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
6676
|
-
var COMPACT_BANNER = ["\u25C7 PRISMER", " Runtime CLI"];
|
|
6677
|
-
function thisDirname() {
|
|
6678
|
-
try {
|
|
6679
|
-
return path6.dirname((0, import_node_url.fileURLToPath)(import_meta2.url));
|
|
6680
|
-
} catch {
|
|
6681
|
-
return process.cwd();
|
|
6682
|
-
}
|
|
6683
|
-
}
|
|
6684
|
-
function findIconPath(size = "big") {
|
|
6685
|
-
const name = size === "big" ? "icon" : "smallicon";
|
|
6686
|
-
const here = thisDirname();
|
|
6687
|
-
const candidates = [
|
|
6688
|
-
// npm-installed: node_modules/@prismer/runtime/dist/cli.js → ../assets
|
|
6689
|
-
path6.resolve(here, "../assets", name),
|
|
6690
|
-
// alternate dist layout (sub-bundle): dist/bin/cli.js → ../../assets
|
|
6691
|
-
path6.resolve(here, "../../assets", name),
|
|
6692
|
-
// source/typecheck: src/cli/ui.ts → ../../assets
|
|
6693
|
-
path6.resolve(here, "../../assets", name),
|
|
6694
|
-
// dev mode: cwd happens to be runtime root
|
|
6695
|
-
path6.resolve(process.cwd(), "assets", name),
|
|
6696
|
-
path6.resolve(process.cwd(), "sdk/prismer-cloud/runtime/assets", name)
|
|
6697
|
-
];
|
|
6698
|
-
for (const candidate of candidates) {
|
|
6699
|
-
try {
|
|
6700
|
-
if (fs4.existsSync(candidate)) return candidate;
|
|
6701
|
-
} catch {
|
|
6702
|
-
}
|
|
6703
|
-
}
|
|
6704
|
-
return null;
|
|
6705
|
-
}
|
|
6706
|
-
var UI = class {
|
|
6707
|
-
mode;
|
|
6708
|
-
colorEnabled;
|
|
6709
|
-
stream;
|
|
6710
|
-
errStream;
|
|
6711
|
-
constructor(opts) {
|
|
6712
|
-
this.mode = opts?.mode ?? "pretty";
|
|
6713
|
-
this.stream = opts?.stream ?? process.stdout;
|
|
6714
|
-
this.errStream = opts?.errStream ?? process.stderr;
|
|
6715
|
-
if (opts?.color !== void 0) {
|
|
6716
|
-
this.colorEnabled = opts.color;
|
|
6717
|
-
} else {
|
|
6718
|
-
const isTTY = this.stream.isTTY === true;
|
|
6719
|
-
const noColor = Boolean(process.env["NO_COLOR"]);
|
|
6720
|
-
this.colorEnabled = isTTY && !noColor;
|
|
6721
|
-
}
|
|
6722
|
-
}
|
|
6723
|
-
// ---- Internal color helpers ----
|
|
6724
|
-
ansi(open, close, text) {
|
|
6725
|
-
if (!this.colorEnabled) return text;
|
|
6726
|
-
return `\x1B[${open}m${text}\x1B[${close}m`;
|
|
6727
|
-
}
|
|
6728
|
-
green(t) {
|
|
6729
|
-
return this.ansi(32, 39, t);
|
|
6730
|
-
}
|
|
6731
|
-
red(t) {
|
|
6732
|
-
return this.ansi(31, 39, t);
|
|
6733
|
-
}
|
|
6734
|
-
yellow(t) {
|
|
6735
|
-
return this.ansi(33, 39, t);
|
|
6736
|
-
}
|
|
6737
|
-
cyan(t) {
|
|
6738
|
-
return this.ansi(36, 39, t);
|
|
6739
|
-
}
|
|
6740
|
-
dim(t) {
|
|
6741
|
-
return this.ansi(2, 22, t);
|
|
6742
|
-
}
|
|
6743
|
-
bold(t) {
|
|
6744
|
-
return this.ansi(1, 22, t);
|
|
6745
|
-
}
|
|
6746
|
-
gray(t) {
|
|
6747
|
-
return this.ansi(90, 39, t);
|
|
6748
|
-
}
|
|
6749
|
-
brandMark() {
|
|
6750
|
-
return this.cyan("\u25C7");
|
|
6751
|
-
}
|
|
6752
|
-
colorBrandLine(line) {
|
|
6753
|
-
let out = "";
|
|
6754
|
-
for (const ch of line) {
|
|
6755
|
-
if (ch === "\u2592") {
|
|
6756
|
-
out += this.cyan(ch);
|
|
6757
|
-
} else if (ch === "\u2593") {
|
|
6758
|
-
out += this.dim(ch);
|
|
6759
|
-
} else {
|
|
6760
|
-
out += ch;
|
|
6761
|
-
}
|
|
6762
|
-
}
|
|
6763
|
-
return out;
|
|
6764
|
-
}
|
|
6765
|
-
// ---- Core write helpers ----
|
|
6766
|
-
write(text) {
|
|
6767
|
-
this.stream.write(text);
|
|
6768
|
-
}
|
|
6769
|
-
writeErr(text) {
|
|
6770
|
-
this.errStream.write(text);
|
|
6771
|
-
}
|
|
6772
|
-
// ---- Level 1: Header ----
|
|
6773
|
-
header(text) {
|
|
6774
|
-
if (this.mode === "json") return;
|
|
6775
|
-
const prefix = text.startsWith("Prismer") ? this.brandMark() + " " : "";
|
|
6776
|
-
this.write(prefix + this.bold(text) + "\n");
|
|
6777
|
-
}
|
|
6778
|
-
smallHeader(subtitle) {
|
|
6779
|
-
if (this.mode === "json" || this.mode === "quiet") return;
|
|
6780
|
-
const iconPath = findIconPath("small");
|
|
6781
|
-
if (iconPath !== null) {
|
|
6782
|
-
try {
|
|
6783
|
-
const raw = fs4.readFileSync(iconPath, "utf-8").replace(/\n+$/, "");
|
|
6784
|
-
for (const line of raw.split("\n")) {
|
|
6785
|
-
this.write(this.cyan(line) + "\n");
|
|
6786
|
-
}
|
|
6787
|
-
} catch {
|
|
6788
|
-
this.write(this.brandMark() + " " + this.bold("Prismer") + "\n");
|
|
6789
|
-
}
|
|
6790
|
-
} else {
|
|
6791
|
-
this.write(this.brandMark() + " " + this.bold("Prismer") + "\n");
|
|
6792
|
-
}
|
|
6793
|
-
if (subtitle !== void 0 && subtitle.length > 0) {
|
|
6794
|
-
this.write(this.dim(" " + subtitle) + "\n");
|
|
6795
|
-
}
|
|
6796
|
-
this.blank();
|
|
6797
|
-
}
|
|
6798
|
-
banner(subtitle, opts) {
|
|
6799
|
-
if (this.mode === "json" || this.mode === "quiet") return;
|
|
6800
|
-
const envColumns = process.env["COLUMNS"] !== void 0 ? parseInt(process.env["COLUMNS"], 10) : NaN;
|
|
6801
|
-
const width = this.stream.columns ?? process.stdout.columns ?? (Number.isFinite(envColumns) ? envColumns : 80);
|
|
6802
|
-
const iconPath = findIconPath("big");
|
|
6803
|
-
const shouldUseFull = opts?.full === true || width >= 120;
|
|
6804
|
-
if (shouldUseFull && iconPath !== null) {
|
|
6805
|
-
try {
|
|
6806
|
-
const raw = fs4.readFileSync(iconPath, "utf-8");
|
|
6807
|
-
const lines = raw.split("\n");
|
|
6808
|
-
for (const line of lines) {
|
|
6809
|
-
const brandedLine = line.replace("Prismer Cloud SDK", "Prismer Runtime CLI");
|
|
6810
|
-
const stripped = brandedLine.trimEnd();
|
|
6811
|
-
if (stripped.length === 0) {
|
|
6812
|
-
this.write("\n");
|
|
6813
|
-
continue;
|
|
6814
|
-
}
|
|
6815
|
-
const clipped = stripped.length >= width ? stripped.slice(0, Math.max(width - 1, 1)) : stripped;
|
|
6816
|
-
this.write(this.colorBrandLine(clipped) + "\n");
|
|
6817
|
-
}
|
|
6818
|
-
} catch {
|
|
6819
|
-
this.write(this.cyan(COMPACT_BANNER[0] ?? "\u25C7 PRISMER") + "\n");
|
|
6820
|
-
this.write(this.dim(COMPACT_BANNER[1] ?? " Runtime CLI") + "\n");
|
|
6821
|
-
}
|
|
6822
|
-
} else {
|
|
6823
|
-
this.write(this.cyan(COMPACT_BANNER[0] ?? "\u25C7 PRISMER") + "\n");
|
|
6824
|
-
this.write(this.dim(COMPACT_BANNER[1] ?? " Runtime CLI") + "\n");
|
|
6825
|
-
}
|
|
6826
|
-
if (subtitle !== void 0 && subtitle.length > 0) {
|
|
6827
|
-
this.write(this.dim(" " + subtitle) + "\n");
|
|
6828
|
-
}
|
|
6829
|
-
this.blank();
|
|
6830
|
-
}
|
|
6831
|
-
// ---- Level 2: Primary data ----
|
|
6832
|
-
blank() {
|
|
6833
|
-
if (this.mode === "json") return;
|
|
6834
|
-
this.write("\n");
|
|
6835
|
-
}
|
|
6836
|
-
line(text) {
|
|
6837
|
-
if (this.mode === "json") return;
|
|
6838
|
-
this.write(text + "\n");
|
|
6839
|
-
}
|
|
6840
|
-
info(text) {
|
|
6841
|
-
this.line(text);
|
|
6842
|
-
}
|
|
6843
|
-
// ---- Level 3: Secondary ----
|
|
6844
|
-
secondary(text, indent = 2) {
|
|
6845
|
-
if (this.mode === "json") return;
|
|
6846
|
-
this.write(" ".repeat(indent) + this.dim(text) + "\n");
|
|
6847
|
-
}
|
|
6848
|
-
// ---- Level 4: Action tips ----
|
|
6849
|
-
tip(text) {
|
|
6850
|
-
if (this.mode === "json") return;
|
|
6851
|
-
this.write(this.cyan("Tip:") + " " + text + "\n");
|
|
6852
|
-
}
|
|
6853
|
-
next(text) {
|
|
6854
|
-
if (this.mode === "json") return;
|
|
6855
|
-
this.write(this.cyan("Next:") + " " + text + "\n");
|
|
6856
|
-
}
|
|
6857
|
-
// ---- Level 5: Status indicators ----
|
|
6858
|
-
ok(text, detail) {
|
|
6859
|
-
if (this.mode === "json") return;
|
|
6860
|
-
const suffix = detail ? " " + this.dim(detail) : "";
|
|
6861
|
-
this.write(" " + this.green("\u2713") + " " + text + suffix + "\n");
|
|
6862
|
-
}
|
|
6863
|
-
success(text, detail) {
|
|
6864
|
-
this.ok(text, detail);
|
|
6865
|
-
}
|
|
6866
|
-
fail(text, detail) {
|
|
6867
|
-
if (this.mode === "json") return;
|
|
6868
|
-
const suffix = detail ? " " + this.dim(detail) : "";
|
|
6869
|
-
this.write(" " + this.red("\u2717") + " " + text + suffix + "\n");
|
|
6870
|
-
}
|
|
6871
|
-
online(text) {
|
|
6872
|
-
if (this.mode === "json") return;
|
|
6873
|
-
this.write(" " + this.green("\u25CF") + " " + text + "\n");
|
|
6874
|
-
}
|
|
6875
|
-
offline(text) {
|
|
6876
|
-
if (this.mode === "json") return;
|
|
6877
|
-
this.write(" " + this.gray("\u25CB") + " " + text + "\n");
|
|
6878
|
-
}
|
|
6879
|
-
notInstalled(text) {
|
|
6880
|
-
if (this.mode === "json") return;
|
|
6881
|
-
this.write(" " + this.dim("\xB7") + " " + this.dim(text) + "\n");
|
|
6882
|
-
}
|
|
6883
|
-
pending(text) {
|
|
6884
|
-
if (this.mode === "json") return;
|
|
6885
|
-
this.write(" " + this.yellow("\u27F3") + " " + text + "\n");
|
|
6886
|
-
}
|
|
6887
|
-
warn(text, detail) {
|
|
6888
|
-
if (this.mode === "json") return;
|
|
6889
|
-
const suffix = detail ? " " + this.dim(detail) : "";
|
|
6890
|
-
this.write(" " + this.yellow("!") + " " + text + suffix + "\n");
|
|
6891
|
-
}
|
|
6892
|
-
// ---- Level 6: Error block ----
|
|
6893
|
-
error(what, cause, fix) {
|
|
6894
|
-
if (this.mode === "json") return;
|
|
6895
|
-
this.writeErr(this.red("\u2717") + " " + what + "\n");
|
|
6896
|
-
if (cause !== void 0) {
|
|
6897
|
-
this.writeErr(" " + this.dim("Cause:") + " " + this.dim(cause) + "\n");
|
|
6898
|
-
}
|
|
6899
|
-
if (fix !== void 0) {
|
|
6900
|
-
this.writeErr(" " + this.cyan("Fix:") + " " + fix + "\n");
|
|
6901
|
-
}
|
|
6902
|
-
}
|
|
6903
|
-
table(rowsOrOpts, maybeOpts) {
|
|
6904
|
-
if (this.mode === "json") return;
|
|
6905
|
-
const rows = Array.isArray(rowsOrOpts) ? rowsOrOpts : rowsOrOpts.rows;
|
|
6906
|
-
const opts = Array.isArray(rowsOrOpts) ? maybeOpts : { columns: rowsOrOpts.columns, maxWidth: rowsOrOpts.maxWidth };
|
|
6907
|
-
if (!opts) throw new Error("table() requires columns");
|
|
6908
|
-
const maxWidth = opts.maxWidth ?? (process.stdout.columns || 80);
|
|
6909
|
-
const cols = opts.columns;
|
|
6910
|
-
const widths = cols.map((col) => col.length);
|
|
6911
|
-
for (const row of rows) {
|
|
6912
|
-
cols.forEach((col, i) => {
|
|
6913
|
-
const val = row[col] ?? "";
|
|
6914
|
-
const w = widths[i] ?? 0;
|
|
6915
|
-
if (val.length > w) widths[i] = val.length;
|
|
6916
|
-
});
|
|
6917
|
-
}
|
|
6918
|
-
const totalWidth = widths.reduce((a, b) => a + b, 0) + (cols.length - 1) * 2 + 2;
|
|
6919
|
-
if (totalWidth > maxWidth) {
|
|
6920
|
-
for (let i = 0; i < rows.length; i++) {
|
|
6921
|
-
const row = rows[i];
|
|
6922
|
-
if (!row) continue;
|
|
6923
|
-
for (const col of cols) {
|
|
6924
|
-
const val = row[col] ?? "";
|
|
6925
|
-
this.write(" " + this.bold(col + ":") + " " + val + "\n");
|
|
6926
|
-
}
|
|
6927
|
-
if (i < rows.length - 1) this.write("\n");
|
|
6928
|
-
}
|
|
6929
|
-
return;
|
|
6930
|
-
}
|
|
6931
|
-
const header = cols.map((col, i) => col.toUpperCase().padEnd(widths[i] ?? col.length)).join(" ");
|
|
6932
|
-
this.write(" " + this.dim(header) + "\n");
|
|
6933
|
-
for (const row of rows) {
|
|
6934
|
-
const line = cols.map((col, i) => (row[col] ?? "").padEnd(widths[i] ?? col.length)).join(" ");
|
|
6935
|
-
this.write(" " + line + "\n");
|
|
6936
|
-
}
|
|
6937
|
-
}
|
|
6938
|
-
// ---- Spinner ----
|
|
6939
|
-
spinner(text) {
|
|
6940
|
-
if (this.mode === "quiet" || this.mode === "json") {
|
|
6941
|
-
return {
|
|
6942
|
-
update() {
|
|
6943
|
-
},
|
|
6944
|
-
stop() {
|
|
6945
|
-
}
|
|
6946
|
-
};
|
|
6947
|
-
}
|
|
6948
|
-
const isTTY = this.stream.isTTY === true;
|
|
6949
|
-
if (!isTTY || !this.colorEnabled) {
|
|
6950
|
-
this.write(" " + this.yellow("\u27F3") + " " + text + "\n");
|
|
6951
|
-
return {
|
|
6952
|
-
update: (t) => {
|
|
6953
|
-
this.write(" " + this.yellow("\u27F3") + " " + t + "\n");
|
|
6954
|
-
},
|
|
6955
|
-
stop: (final) => {
|
|
6956
|
-
if (final) this.write(" " + this.green("\u2713") + " " + final + "\n");
|
|
6957
|
-
}
|
|
6958
|
-
};
|
|
6959
|
-
}
|
|
6960
|
-
let current = text;
|
|
6961
|
-
let frameIdx = 0;
|
|
6962
|
-
let stopped = false;
|
|
6963
|
-
const write = this.write.bind(this);
|
|
6964
|
-
const colorFn = this.yellow.bind(this);
|
|
6965
|
-
const greenFn = this.green.bind(this);
|
|
6966
|
-
function renderFrame() {
|
|
6967
|
-
const frame = BRAILLE_FRAMES[frameIdx % BRAILLE_FRAMES.length] ?? "\u280B";
|
|
6968
|
-
const line = " " + colorFn(frame) + " " + current;
|
|
6969
|
-
write("\r" + line);
|
|
6970
|
-
frameIdx++;
|
|
6971
|
-
}
|
|
6972
|
-
renderFrame();
|
|
6973
|
-
const timer = setInterval(renderFrame, 80);
|
|
6974
|
-
return {
|
|
6975
|
-
update(t) {
|
|
6976
|
-
if (stopped) return;
|
|
6977
|
-
current = t;
|
|
6978
|
-
},
|
|
6979
|
-
stop(final) {
|
|
6980
|
-
if (stopped) return;
|
|
6981
|
-
stopped = true;
|
|
6982
|
-
clearInterval(timer);
|
|
6983
|
-
write("\r\x1B[2K");
|
|
6984
|
-
if (final) write(" " + greenFn("\u2713") + " " + final + "\n");
|
|
6985
|
-
}
|
|
6986
|
-
};
|
|
6987
|
-
}
|
|
6988
|
-
// ---- Progress bar ----
|
|
6989
|
-
progress(text, total) {
|
|
6990
|
-
if (this.mode === "quiet" || this.mode === "json") {
|
|
6991
|
-
return {
|
|
6992
|
-
update() {
|
|
6993
|
-
},
|
|
6994
|
-
stop() {
|
|
6995
|
-
}
|
|
6996
|
-
};
|
|
6997
|
-
}
|
|
6998
|
-
const isTTY = this.stream.isTTY === true;
|
|
6999
|
-
const start = Date.now();
|
|
7000
|
-
const write = this.write.bind(this);
|
|
7001
|
-
const colorFn = this.cyan.bind(this);
|
|
7002
|
-
const dimFn = this.dim.bind(this);
|
|
7003
|
-
const greenFn = this.green.bind(this);
|
|
7004
|
-
let last = 0;
|
|
7005
|
-
let lastDetail = "";
|
|
7006
|
-
let stopped = false;
|
|
7007
|
-
const render = () => {
|
|
7008
|
-
if (stopped) return;
|
|
7009
|
-
const frac = total > 0 ? Math.min(1, Math.max(0, last / total)) : 0;
|
|
7010
|
-
const pct = Math.floor(frac * 100);
|
|
7011
|
-
const width = 20;
|
|
7012
|
-
const filled = Math.floor(frac * width);
|
|
7013
|
-
const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
|
|
7014
|
-
const elapsed = (Date.now() - start) / 1e3;
|
|
7015
|
-
const eta = frac > 0.01 ? Math.max(0, elapsed / frac - elapsed) : 0;
|
|
7016
|
-
const etaStr = frac >= 1 ? "" : ` \xB7 ${eta < 1 ? "<1s" : Math.round(eta) + "s"} left`;
|
|
7017
|
-
const detailStr = lastDetail ? ` \xB7 ${lastDetail}` : "";
|
|
7018
|
-
const line = ` ${text} [${colorFn(bar)}] ${String(pct).padStart(3)}%${detailStr}${dimFn(etaStr)}`;
|
|
7019
|
-
if (isTTY && this.colorEnabled) {
|
|
7020
|
-
write("\r\x1B[2K" + line);
|
|
7021
|
-
} else {
|
|
7022
|
-
write(line + "\n");
|
|
7023
|
-
}
|
|
7024
|
-
};
|
|
7025
|
-
render();
|
|
7026
|
-
return {
|
|
7027
|
-
update: (current, detail) => {
|
|
7028
|
-
if (stopped) return;
|
|
7029
|
-
last = current;
|
|
7030
|
-
if (detail !== void 0) lastDetail = detail;
|
|
7031
|
-
render();
|
|
7032
|
-
},
|
|
7033
|
-
stop: (final) => {
|
|
7034
|
-
if (stopped) return;
|
|
7035
|
-
stopped = true;
|
|
7036
|
-
if (isTTY && this.colorEnabled) write("\r\x1B[2K");
|
|
7037
|
-
if (final) write(" " + greenFn("\u2713") + " " + final + "\n");
|
|
7038
|
-
}
|
|
7039
|
-
};
|
|
7040
|
-
}
|
|
7041
|
-
// ---- JSON output ----
|
|
7042
|
-
json(payload, opts) {
|
|
7043
|
-
const indent = opts?.pretty ? 2 : void 0;
|
|
7044
|
-
this.write(JSON.stringify(payload, null, indent) + "\n");
|
|
7045
|
-
}
|
|
7046
|
-
result(pretty, jsonPayload) {
|
|
7047
|
-
if (this.mode === "pretty") {
|
|
7048
|
-
pretty();
|
|
7049
|
-
} else {
|
|
7050
|
-
this.json(jsonPayload);
|
|
7051
|
-
}
|
|
7052
|
-
}
|
|
7053
|
-
};
|
|
7054
|
-
var _ui = null;
|
|
7055
|
-
function getUI() {
|
|
7056
|
-
if (!_ui) _ui = new UI();
|
|
7057
|
-
return _ui;
|
|
7058
|
-
}
|
|
7059
|
-
function setUI(ui) {
|
|
7060
|
-
_ui = ui;
|
|
7061
|
-
}
|
|
7062
|
-
function applyCommonFlags(argv) {
|
|
7063
|
-
let mode = "pretty";
|
|
7064
|
-
const isTTY = process.stdout.isTTY === true;
|
|
7065
|
-
const noColorEnv = Boolean(process.env["NO_COLOR"]);
|
|
7066
|
-
let color2 = isTTY && !noColorEnv;
|
|
7067
|
-
const rest = [];
|
|
7068
|
-
for (const arg of argv) {
|
|
7069
|
-
switch (arg) {
|
|
7070
|
-
case "--no-color":
|
|
7071
|
-
color2 = false;
|
|
7072
|
-
break;
|
|
7073
|
-
case "--color":
|
|
7074
|
-
color2 = true;
|
|
7075
|
-
break;
|
|
7076
|
-
case "--json":
|
|
7077
|
-
case "--pretty-json":
|
|
7078
|
-
mode = "json";
|
|
7079
|
-
if (arg === "--json") rest.push(arg);
|
|
7080
|
-
break;
|
|
7081
|
-
case "--quiet":
|
|
7082
|
-
mode = "quiet";
|
|
7083
|
-
break;
|
|
7084
|
-
default:
|
|
7085
|
-
rest.push(arg);
|
|
7086
|
-
}
|
|
7087
|
-
}
|
|
7088
|
-
return { mode, color: color2, restArgv: rest };
|
|
7089
|
-
}
|
|
7090
|
-
|
|
7091
|
-
// src/cli/util.ts
|
|
7092
|
-
var DEFAULT_CLOUD_BASE_URL = "https://prismer.cloud";
|
|
7093
|
-
var ANSI = {
|
|
7094
|
-
reset: "\x1B[0m",
|
|
7095
|
-
bold: "\x1B[1m",
|
|
7096
|
-
dim: "\x1B[2m",
|
|
7097
|
-
cyan: "\x1B[36m",
|
|
7098
|
-
green: "\x1B[32m",
|
|
7099
|
-
yellow: "\x1B[33m",
|
|
7100
|
-
red: "\x1B[31m"
|
|
7101
|
-
};
|
|
7102
|
-
function color(kind, text) {
|
|
7103
|
-
if (process.env.NO_COLOR === "1" || process.env.NO_COLOR === "true") return text;
|
|
7104
|
-
return `${ANSI[kind]}${text}${ANSI.reset}`;
|
|
7105
|
-
}
|
|
7106
|
-
function printJson(v) {
|
|
7107
|
-
getUI().json(v, { pretty: true });
|
|
7108
|
-
}
|
|
7109
|
-
function exitWithError(message, opts) {
|
|
7110
|
-
const o = typeof opts === "number" ? { exitCode: opts } : opts ?? {};
|
|
7111
|
-
const exitCode = o.exitCode ?? 1;
|
|
7112
|
-
const ui = getUI();
|
|
7113
|
-
if (ui.mode === "json") {
|
|
7114
|
-
const payload = {
|
|
7115
|
-
ok: false,
|
|
7116
|
-
error: { code: o.code ?? "cli_error", message },
|
|
7117
|
-
...o.details ? { details: o.details } : {}
|
|
7118
|
-
};
|
|
7119
|
-
ui.json(payload, { pretty: true });
|
|
7120
|
-
} else {
|
|
7121
|
-
process.stderr.write(`Error: ${message}
|
|
7122
|
-
`);
|
|
7123
|
-
}
|
|
7124
|
-
process.exit(exitCode);
|
|
7125
|
-
}
|
|
7126
|
-
function normalizeCloudUrl(input) {
|
|
7127
|
-
const raw = input.trim();
|
|
7128
|
-
if (!raw) throw new Error("Cloud URL is empty.");
|
|
7129
|
-
if (/^[a-z][a-z0-9+\-.]*:\/\//i.test(raw)) {
|
|
7130
|
-
let parsed;
|
|
7131
|
-
try {
|
|
7132
|
-
parsed = new URL(raw);
|
|
7133
|
-
} catch {
|
|
7134
|
-
throw new Error(`Invalid --cloud URL: ${raw}`);
|
|
7135
|
-
}
|
|
7136
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
7137
|
-
throw new Error(`Invalid --cloud URL scheme: ${parsed.protocol} (expected http:// or https://)`);
|
|
7138
|
-
}
|
|
7139
|
-
return raw.replace(/\/$/, "");
|
|
7635
|
+
const capabilities = obj.capabilities;
|
|
7636
|
+
if (typeof obj.workspaceId !== "string" || obj.workspaceId.length === 0) throw new Error("workspaceId is required");
|
|
7637
|
+
if (typeof obj.imUserId !== "string" || obj.imUserId.length === 0) throw new Error("imUserId is required");
|
|
7638
|
+
if (typeof obj.name !== "string" || obj.name.length === 0) throw new Error("name is required");
|
|
7639
|
+
if (typeof obj.adapterName !== "string" || obj.adapterName.length === 0) throw new Error("adapterName is required");
|
|
7640
|
+
if (!Array.isArray(capabilities) || capabilities.some((v) => typeof v !== "string")) {
|
|
7641
|
+
throw new Error("capabilities must be a string array");
|
|
7140
7642
|
}
|
|
7141
|
-
if (
|
|
7142
|
-
|
|
7643
|
+
if (!profile || typeof profile !== "object") throw new Error("profile is required");
|
|
7644
|
+
if (typeof profile.id !== "string" || profile.id.length === 0) throw new Error("profile.id is required");
|
|
7645
|
+
if (typeof profile.name !== "string" || profile.name.length === 0) throw new Error("profile.name is required");
|
|
7646
|
+
if (typeof profile.adapterName !== "string" || profile.adapterName.length === 0) {
|
|
7647
|
+
throw new Error("profile.adapterName is required");
|
|
7143
7648
|
}
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
new URL(candidate);
|
|
7147
|
-
} catch {
|
|
7148
|
-
throw new Error(`Invalid --cloud URL: ${raw}`);
|
|
7649
|
+
if (profile.config !== void 0 && (!profile.config || typeof profile.config !== "object" || Array.isArray(profile.config))) {
|
|
7650
|
+
throw new Error("profile.config must be a JSON object");
|
|
7149
7651
|
}
|
|
7150
|
-
return
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7652
|
+
return {
|
|
7653
|
+
workspaceId: obj.workspaceId,
|
|
7654
|
+
imUserId: obj.imUserId,
|
|
7655
|
+
name: obj.name,
|
|
7656
|
+
adapterName: obj.adapterName,
|
|
7657
|
+
capabilities,
|
|
7658
|
+
profile: {
|
|
7659
|
+
id: profile.id,
|
|
7660
|
+
name: profile.name,
|
|
7661
|
+
adapterName: profile.adapterName,
|
|
7662
|
+
config: profile.config ?? {},
|
|
7663
|
+
version: typeof profile.version === "number" && Number.isFinite(profile.version) ? profile.version : 1
|
|
7160
7664
|
}
|
|
7161
7665
|
};
|
|
7162
7666
|
}
|
|
7163
|
-
function
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
return;
|
|
7667
|
+
function safeJsonParse(raw) {
|
|
7668
|
+
try {
|
|
7669
|
+
return JSON.parse(raw);
|
|
7670
|
+
} catch {
|
|
7671
|
+
return raw;
|
|
7168
7672
|
}
|
|
7169
|
-
ui.banner("Runtime CLI v1.9.3", { full: true });
|
|
7170
|
-
}
|
|
7171
|
-
function ok(label, detail) {
|
|
7172
|
-
getUI().ok(label, detail);
|
|
7173
|
-
}
|
|
7174
|
-
function warn(label, detail) {
|
|
7175
|
-
getUI().warn(label, detail);
|
|
7176
|
-
}
|
|
7177
|
-
function fail2(label, detail) {
|
|
7178
|
-
getUI().fail(label, detail);
|
|
7179
|
-
}
|
|
7180
|
-
function tip(command, detail) {
|
|
7181
|
-
const text = detail ? `${command} ${detail}` : command;
|
|
7182
|
-
getUI().tip(text);
|
|
7183
|
-
}
|
|
7184
|
-
function pidFilePath(paths) {
|
|
7185
|
-
return (0, import_node_path7.join)(paths.root, "daemon.pid");
|
|
7186
|
-
}
|
|
7187
|
-
function writePidFile(paths, pid) {
|
|
7188
|
-
(0, import_node_fs11.writeFileSync)(pidFilePath(paths), `${pid}
|
|
7189
|
-
`, "utf8");
|
|
7190
|
-
}
|
|
7191
|
-
function readPidFile(paths) {
|
|
7192
|
-
const p = pidFilePath(paths);
|
|
7193
|
-
if (!(0, import_node_fs11.existsSync)(p)) return void 0;
|
|
7194
|
-
const raw = (0, import_node_fs11.readFileSync)(p, "utf8").trim();
|
|
7195
|
-
const pid = Number.parseInt(raw, 10);
|
|
7196
|
-
return Number.isFinite(pid) ? pid : void 0;
|
|
7197
7673
|
}
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7674
|
+
|
|
7675
|
+
// src/pair.ts
|
|
7676
|
+
var import_node_crypto5 = require("crypto");
|
|
7677
|
+
var import_node_os5 = require("os");
|
|
7678
|
+
var import_promises = require("timers/promises");
|
|
7679
|
+
var import_qrcode = __toESM(require("qrcode"), 1);
|
|
7680
|
+
async function pair(opts) {
|
|
7681
|
+
const paths = opts.paths ?? resolvePaths();
|
|
7682
|
+
if (configExists(paths) && !opts.force) {
|
|
7683
|
+
throw new Error(
|
|
7684
|
+
`Config already exists at ${paths.configFile}. Pass --force to overwrite, or run \`prismer status\` to inspect.`
|
|
7685
|
+
);
|
|
7686
|
+
}
|
|
7687
|
+
const isLocalOnly = opts.isLocalOnly ?? (() => process.env.LOCAL_ONLY === "1");
|
|
7688
|
+
const localOnlyMode = !!opts.asUserEmail;
|
|
7689
|
+
if (localOnlyMode && !isLocalOnly()) {
|
|
7690
|
+
throw new Error(
|
|
7691
|
+
"pair: --as-user requires LOCAL_ONLY=1. Without that gate, this would skip mobile approval and silently mint a key for the named user."
|
|
7692
|
+
);
|
|
7693
|
+
}
|
|
7694
|
+
const { publicKey } = (0, import_node_crypto5.generateKeyPairSync)("ed25519");
|
|
7695
|
+
const devicePub = publicKey.export({ format: "der", type: "spki" }).toString("base64");
|
|
7696
|
+
const cloud = new CloudClient({
|
|
7697
|
+
baseUrl: opts.cloudBaseUrl,
|
|
7698
|
+
apiKey: "pending",
|
|
7699
|
+
// not used: we pass auth:false
|
|
7700
|
+
fetchImpl: opts.fetchImpl
|
|
7701
|
+
});
|
|
7702
|
+
const offerRes = await cloud.request(
|
|
7703
|
+
"POST",
|
|
7704
|
+
"/api/im/pair/offer",
|
|
7705
|
+
{
|
|
7706
|
+
auth: false,
|
|
7707
|
+
body: { devicePub, deviceName: opts.deviceName ?? (0, import_node_os5.hostname)() }
|
|
7708
|
+
}
|
|
7709
|
+
);
|
|
7710
|
+
if (!offerRes.ok) {
|
|
7711
|
+
throw new Error(`pair: offer failed (${offerRes.status}): ${offerRes.error?.message}`);
|
|
7712
|
+
}
|
|
7713
|
+
const offer = unwrapEnvelope(offerRes.data);
|
|
7714
|
+
if (!offer.nonce || !offer.qrUrl) {
|
|
7715
|
+
throw new Error("pair: cloud returned no nonce/qrUrl");
|
|
7716
|
+
}
|
|
7717
|
+
if (localOnlyMode) {
|
|
7718
|
+
const approveRes = await cloud.request(
|
|
7719
|
+
"POST",
|
|
7720
|
+
"/api/im/pair/local-only-approve",
|
|
7721
|
+
{
|
|
7722
|
+
auth: false,
|
|
7723
|
+
body: { nonce: offer.nonce, asUserEmail: opts.asUserEmail }
|
|
7724
|
+
}
|
|
7725
|
+
);
|
|
7726
|
+
if (!approveRes.ok) {
|
|
7727
|
+
throw new Error(
|
|
7728
|
+
`pair: local-only-approve failed (${approveRes.status}): ${approveRes.error?.message ?? "unknown"}`
|
|
7729
|
+
);
|
|
7730
|
+
}
|
|
7731
|
+
process.stdout.write(`[pair] LOCAL_ONLY approved as ${opts.asUserEmail} \u2014 no QR shown
|
|
7732
|
+
`);
|
|
7733
|
+
} else {
|
|
7734
|
+
const qrAscii = await import_qrcode.default.toString(offer.qrUrl, { type: "terminal", small: true });
|
|
7735
|
+
process.stdout.write(qrAscii);
|
|
7736
|
+
process.stdout.write(`
|
|
7737
|
+
Scan with Lumin to approve, or open: ${offer.qrUrl}
|
|
7738
|
+
|
|
7739
|
+
`);
|
|
7740
|
+
opts.onQrReady?.(offer.qrUrl);
|
|
7741
|
+
}
|
|
7742
|
+
const pollPath = `/api/im/pair/poll/${encodeURIComponent(offer.nonce)}?devicePub=${encodeURIComponent(devicePub)}`;
|
|
7743
|
+
const maxAttempts = opts.maxPollAttempts ?? 60;
|
|
7744
|
+
const pollIntervalMs = opts.pollIntervalMs ?? 5e3;
|
|
7745
|
+
for (let i = 0; i < maxAttempts; i += 1) {
|
|
7746
|
+
if (i > 0 || !localOnlyMode) {
|
|
7747
|
+
await (0, import_promises.setTimeout)(pollIntervalMs);
|
|
7748
|
+
}
|
|
7749
|
+
const res = await cloud.request(
|
|
7750
|
+
"GET",
|
|
7751
|
+
pollPath,
|
|
7752
|
+
{ auth: false }
|
|
7753
|
+
);
|
|
7754
|
+
if (res.ok) {
|
|
7755
|
+
const body = unwrapEnvelope(res.data);
|
|
7756
|
+
if (body.apiKey) {
|
|
7757
|
+
const config = {
|
|
7758
|
+
api_key: body.apiKey,
|
|
7759
|
+
cloud_api_base: opts.cloudBaseUrl,
|
|
7760
|
+
daemon_id: newDaemonId()
|
|
7761
|
+
};
|
|
7762
|
+
saveConfig(config, paths);
|
|
7763
|
+
return { config, paths };
|
|
7764
|
+
}
|
|
7765
|
+
}
|
|
7766
|
+
if (res.status === 404 || res.status === 202) {
|
|
7767
|
+
continue;
|
|
7768
|
+
}
|
|
7769
|
+
if (res.status >= 400 && res.status !== 404) {
|
|
7770
|
+
throw new Error(`pair: poll failed (${res.status}): ${res.error?.message}`);
|
|
7204
7771
|
}
|
|
7205
7772
|
}
|
|
7773
|
+
throw new Error("pair: timed out waiting for approval (5 min)");
|
|
7206
7774
|
}
|
|
7207
|
-
function
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
return
|
|
7211
|
-
} catch {
|
|
7212
|
-
return false;
|
|
7775
|
+
function unwrapEnvelope(raw) {
|
|
7776
|
+
if (raw && typeof raw === "object" && "ok" in raw) {
|
|
7777
|
+
const env = raw;
|
|
7778
|
+
if (env.ok && env.data) return env.data;
|
|
7213
7779
|
}
|
|
7780
|
+
return raw;
|
|
7214
7781
|
}
|
|
7215
7782
|
|
|
7783
|
+
// src/cli/index.ts
|
|
7784
|
+
var import_commander18 = require("commander");
|
|
7785
|
+
|
|
7216
7786
|
// src/cli/commands/adapter.ts
|
|
7787
|
+
var import_node_child_process5 = require("child_process");
|
|
7788
|
+
var import_node_fs13 = require("fs");
|
|
7789
|
+
var import_node_os6 = require("os");
|
|
7790
|
+
var import_node_path9 = require("path");
|
|
7791
|
+
var import_commander = require("commander");
|
|
7792
|
+
init_util();
|
|
7793
|
+
init_ui();
|
|
7217
7794
|
var BUILTIN_ADAPTERS = [hermesAdapter, claudeCodeAdapter, openclawAdapter, codexAdapter];
|
|
7218
7795
|
var INSTALL_SPECS = {
|
|
7219
7796
|
"claude-code": {
|
|
@@ -7223,7 +7800,7 @@ var INSTALL_SPECS = {
|
|
|
7223
7800
|
binary: "claude",
|
|
7224
7801
|
hint: "Set ANTHROPIC_API_KEY in your shell profile, or rely on Claude Code OAuth login. Run `claude login` to sign in.",
|
|
7225
7802
|
authHints: ["ANTHROPIC_API_KEY or Claude Code OAuth login (`claude login`)"],
|
|
7226
|
-
hookTarget: { path: (0,
|
|
7803
|
+
hookTarget: { path: (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".claude", "hooks.json"), kind: "json-file" }
|
|
7227
7804
|
},
|
|
7228
7805
|
openclaw: {
|
|
7229
7806
|
name: "openclaw",
|
|
@@ -7232,7 +7809,7 @@ var INSTALL_SPECS = {
|
|
|
7232
7809
|
binary: "openclaw",
|
|
7233
7810
|
hint: "OpenClaw runs as a gateway. Configure ~/.openclaw/openclaw.json and start it with `openclaw gateway` before tasks dispatch.",
|
|
7234
7811
|
authHints: ["~/.openclaw/openclaw.json gateway.auth.bearerTokens", "Prismer daemon api_key"],
|
|
7235
|
-
hookTarget: { path: (0,
|
|
7812
|
+
hookTarget: { path: (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".openclaw", "hooks"), kind: "directory" }
|
|
7236
7813
|
},
|
|
7237
7814
|
codex: {
|
|
7238
7815
|
name: "codex",
|
|
@@ -7241,7 +7818,7 @@ var INSTALL_SPECS = {
|
|
|
7241
7818
|
binary: "codex",
|
|
7242
7819
|
hint: "Set OPENAI_API_KEY in your shell profile. Verify with `codex --help`.",
|
|
7243
7820
|
authHints: ["OPENAI_API_KEY or Codex CLI account login"],
|
|
7244
|
-
hookTarget: { path: (0,
|
|
7821
|
+
hookTarget: { path: (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".codex", "hooks.json"), kind: "json-file" }
|
|
7245
7822
|
},
|
|
7246
7823
|
hermes: {
|
|
7247
7824
|
name: "hermes",
|
|
@@ -7250,7 +7827,7 @@ var INSTALL_SPECS = {
|
|
|
7250
7827
|
binary: "hermes",
|
|
7251
7828
|
hint: "Hermes runs as a long-lived HTTP gateway in your own Python venv. Start with `hermes -p <profile> gateway` after configuring ~/.hermes/.env.",
|
|
7252
7829
|
authHints: ["~/.hermes/.env API_SERVER_KEY", "Hermes profile provider credentials"],
|
|
7253
|
-
hookTarget: { path: (0,
|
|
7830
|
+
hookTarget: { path: (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".hermes", "hooks.json"), kind: "json-file" }
|
|
7254
7831
|
}
|
|
7255
7832
|
};
|
|
7256
7833
|
function buildAdapterCommand() {
|
|
@@ -7465,15 +8042,15 @@ function runHooks(spec, opts) {
|
|
|
7465
8042
|
}
|
|
7466
8043
|
let backup;
|
|
7467
8044
|
if (planned.kind === "directory") {
|
|
7468
|
-
(0,
|
|
7469
|
-
const markerPath = (0,
|
|
8045
|
+
(0, import_node_fs13.mkdirSync)(planned.path, { recursive: true });
|
|
8046
|
+
const markerPath = (0, import_node_path9.join)(planned.path, "prismer.json");
|
|
7470
8047
|
backup = backupIfExists(markerPath);
|
|
7471
|
-
(0,
|
|
8048
|
+
(0, import_node_fs13.writeFileSync)(markerPath, JSON.stringify(prismerHookMarker(spec), null, 2) + "\n", "utf8");
|
|
7472
8049
|
} else {
|
|
7473
|
-
(0,
|
|
8050
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path9.dirname)(planned.path), { recursive: true });
|
|
7474
8051
|
backup = backupIfExists(planned.path);
|
|
7475
8052
|
const merged = mergeHookJson(planned.path, spec);
|
|
7476
|
-
(0,
|
|
8053
|
+
(0, import_node_fs13.writeFileSync)(planned.path, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
7477
8054
|
}
|
|
7478
8055
|
return {
|
|
7479
8056
|
ok: true,
|
|
@@ -7540,23 +8117,23 @@ function inspectAuthEnv(spec) {
|
|
|
7540
8117
|
hints: spec.authHints ?? []
|
|
7541
8118
|
};
|
|
7542
8119
|
case "hermes": {
|
|
7543
|
-
const envFile = (0,
|
|
8120
|
+
const envFile = (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".hermes", ".env");
|
|
7544
8121
|
return {
|
|
7545
|
-
ok: (0,
|
|
8122
|
+
ok: (0, import_node_fs13.existsSync)(envFile) || Boolean(process.env.HERMES_API_KEY || process.env.API_SERVER_KEY),
|
|
7546
8123
|
present: [
|
|
7547
8124
|
...["HERMES_API_KEY", "API_SERVER_KEY"].filter((k) => Boolean(process.env[k])),
|
|
7548
|
-
...(0,
|
|
8125
|
+
...(0, import_node_fs13.existsSync)(envFile) ? [envFile] : []
|
|
7549
8126
|
],
|
|
7550
8127
|
hints: spec.authHints ?? []
|
|
7551
8128
|
};
|
|
7552
8129
|
}
|
|
7553
8130
|
case "openclaw": {
|
|
7554
|
-
const cfgFile = (0,
|
|
8131
|
+
const cfgFile = (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".openclaw", "openclaw.json");
|
|
7555
8132
|
return {
|
|
7556
|
-
ok: (0,
|
|
8133
|
+
ok: (0, import_node_fs13.existsSync)(cfgFile) || Boolean(process.env.OPENCLAW_API_KEY),
|
|
7557
8134
|
present: [
|
|
7558
8135
|
...["OPENCLAW_API_KEY"].filter((k) => Boolean(process.env[k])),
|
|
7559
|
-
...(0,
|
|
8136
|
+
...(0, import_node_fs13.existsSync)(cfgFile) ? [cfgFile] : []
|
|
7560
8137
|
],
|
|
7561
8138
|
hints: spec.authHints ?? []
|
|
7562
8139
|
};
|
|
@@ -7571,32 +8148,32 @@ function inspectHook(spec) {
|
|
|
7571
8148
|
return { supported: false, markerPresent: false };
|
|
7572
8149
|
}
|
|
7573
8150
|
if (spec.name === "openclaw") {
|
|
7574
|
-
const jsonPath = (0,
|
|
7575
|
-
const markerPath = (0,
|
|
7576
|
-
const jsonMarkerPresent = (0,
|
|
7577
|
-
const dirMarkerPresent = (0,
|
|
8151
|
+
const jsonPath = (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".openclaw", "hooks.json");
|
|
8152
|
+
const markerPath = (0, import_node_path9.join)(target.path, "prismer.json");
|
|
8153
|
+
const jsonMarkerPresent = (0, import_node_fs13.existsSync)(jsonPath) && fileContainsPrismerMarker(jsonPath);
|
|
8154
|
+
const dirMarkerPresent = (0, import_node_fs13.existsSync)(markerPath) && fileContainsPrismerMarker(markerPath);
|
|
7578
8155
|
return {
|
|
7579
8156
|
supported: true,
|
|
7580
8157
|
kind: "json-file-or-directory",
|
|
7581
8158
|
path: target.path,
|
|
7582
8159
|
jsonPath,
|
|
7583
|
-
exists: (0,
|
|
8160
|
+
exists: (0, import_node_fs13.existsSync)(target.path) || (0, import_node_fs13.existsSync)(jsonPath),
|
|
7584
8161
|
markerPath,
|
|
7585
8162
|
markerPresent: jsonMarkerPresent || dirMarkerPresent
|
|
7586
8163
|
};
|
|
7587
8164
|
}
|
|
7588
8165
|
if (target.kind === "directory") {
|
|
7589
|
-
const markerPath = (0,
|
|
8166
|
+
const markerPath = (0, import_node_path9.join)(target.path, "prismer.json");
|
|
7590
8167
|
return {
|
|
7591
8168
|
supported: true,
|
|
7592
8169
|
kind: target.kind,
|
|
7593
8170
|
path: target.path,
|
|
7594
|
-
exists: (0,
|
|
8171
|
+
exists: (0, import_node_fs13.existsSync)(target.path),
|
|
7595
8172
|
markerPath,
|
|
7596
|
-
markerPresent: (0,
|
|
8173
|
+
markerPresent: (0, import_node_fs13.existsSync)(markerPath) && fileContainsPrismerMarker(markerPath)
|
|
7597
8174
|
};
|
|
7598
8175
|
}
|
|
7599
|
-
const exists = (0,
|
|
8176
|
+
const exists = (0, import_node_fs13.existsSync)(target.path);
|
|
7600
8177
|
if (!exists) {
|
|
7601
8178
|
return {
|
|
7602
8179
|
supported: true,
|
|
@@ -7632,14 +8209,14 @@ function prismerHookMarker(spec) {
|
|
|
7632
8209
|
}
|
|
7633
8210
|
function mergeHookJson(path7, spec) {
|
|
7634
8211
|
const marker = prismerHookMarker(spec);
|
|
7635
|
-
if (!(0,
|
|
8212
|
+
if (!(0, import_node_fs13.existsSync)(path7)) {
|
|
7636
8213
|
return {
|
|
7637
8214
|
prismer: marker
|
|
7638
8215
|
};
|
|
7639
8216
|
}
|
|
7640
8217
|
let parsed;
|
|
7641
8218
|
try {
|
|
7642
|
-
parsed = JSON.parse((0,
|
|
8219
|
+
parsed = JSON.parse((0, import_node_fs13.readFileSync)(path7, "utf8"));
|
|
7643
8220
|
} catch (err) {
|
|
7644
8221
|
throw new Error(`Cannot parse ${path7} as JSON: ${err.message}`);
|
|
7645
8222
|
}
|
|
@@ -7663,17 +8240,17 @@ function mergeHookJson(path7, spec) {
|
|
|
7663
8240
|
return next;
|
|
7664
8241
|
}
|
|
7665
8242
|
function backupIfExists(path7) {
|
|
7666
|
-
if (!(0,
|
|
8243
|
+
if (!(0, import_node_fs13.existsSync)(path7)) return null;
|
|
7667
8244
|
const suffix = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7668
8245
|
const backup = `${path7}.bak.${suffix}`;
|
|
7669
|
-
const stat = (0,
|
|
8246
|
+
const stat = (0, import_node_fs13.statSync)(path7);
|
|
7670
8247
|
if (stat.isDirectory()) return null;
|
|
7671
|
-
(0,
|
|
8248
|
+
(0, import_node_fs13.copyFileSync)(path7, backup);
|
|
7672
8249
|
return backup;
|
|
7673
8250
|
}
|
|
7674
8251
|
function fileContainsPrismerMarker(path7) {
|
|
7675
8252
|
try {
|
|
7676
|
-
return (0,
|
|
8253
|
+
return (0, import_node_fs13.readFileSync)(path7, "utf8").includes("prismer-daemon-runtime");
|
|
7677
8254
|
} catch {
|
|
7678
8255
|
return false;
|
|
7679
8256
|
}
|
|
@@ -7736,6 +8313,8 @@ function clearInstallInConfig(name) {
|
|
|
7736
8313
|
// src/cli/commands/agent.ts
|
|
7737
8314
|
var import_commander2 = require("commander");
|
|
7738
8315
|
var import_node_child_process6 = require("child_process");
|
|
8316
|
+
init_util();
|
|
8317
|
+
init_ui();
|
|
7739
8318
|
var ADAPTER_BINARY = {
|
|
7740
8319
|
"claude-code": "claude",
|
|
7741
8320
|
codex: "codex",
|
|
@@ -8115,8 +8694,10 @@ function whichBinary2(bin) {
|
|
|
8115
8694
|
|
|
8116
8695
|
// src/cli/commands/asset.ts
|
|
8117
8696
|
var import_commander3 = require("commander");
|
|
8118
|
-
var
|
|
8119
|
-
var
|
|
8697
|
+
var import_node_fs14 = require("fs");
|
|
8698
|
+
var import_node_path10 = require("path");
|
|
8699
|
+
init_util();
|
|
8700
|
+
init_ui();
|
|
8120
8701
|
function buildAssetCommand() {
|
|
8121
8702
|
const cmd = new import_commander3.Command("asset").description("Inspect IM assets");
|
|
8122
8703
|
cmd.command("list").description("List assets for a workspace, optionally filtered by task").option("--workspace-id <id>", "Workspace id").option("--task-id <id>", "Task id filter").option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
|
|
@@ -8187,9 +8768,9 @@ function mkCloud() {
|
|
|
8187
8768
|
return new CloudClient({ baseUrl: cfg.cloud_api_base, apiKey: cfg.api_key });
|
|
8188
8769
|
}
|
|
8189
8770
|
async function uploadAsset(file, opts) {
|
|
8190
|
-
if (!(0,
|
|
8771
|
+
if (!(0, import_node_fs14.existsSync)(file)) exitWithError(`file not found: ${file}`);
|
|
8191
8772
|
const cfg = loadConfig(resolvePaths());
|
|
8192
|
-
const bytes = (0,
|
|
8773
|
+
const bytes = (0, import_node_fs14.readFileSync)(file);
|
|
8193
8774
|
const metadata = parseMetadata(opts.metadata);
|
|
8194
8775
|
if (opts.taskId && metadata.taskId === void 0) metadata.taskId = opts.taskId;
|
|
8195
8776
|
if (opts.containerId && metadata.containerId === void 0) metadata.containerId = opts.containerId;
|
|
@@ -8199,7 +8780,7 @@ async function uploadAsset(file, opts) {
|
|
|
8199
8780
|
if (opts.agentId) form.set("sourceAgentImUserId", opts.agentId);
|
|
8200
8781
|
if (opts.taskId) form.set("sourceTaskId", opts.taskId);
|
|
8201
8782
|
if (Object.keys(metadata).length > 0) form.set("metadata", JSON.stringify(metadata));
|
|
8202
|
-
form.set("file", new Blob([bytes], { type: opts.mime ?? "application/octet-stream" }), (0,
|
|
8783
|
+
form.set("file", new Blob([bytes], { type: opts.mime ?? "application/octet-stream" }), (0, import_node_path10.basename)(file));
|
|
8203
8784
|
const url = `${cfg.cloud_api_base.replace(/\/$/, "")}/api/im/assets`;
|
|
8204
8785
|
let res;
|
|
8205
8786
|
try {
|
|
@@ -8225,7 +8806,7 @@ async function downloadAsset(assetId, outPath) {
|
|
|
8225
8806
|
exitWithError(`asset download failed (${res.status}): ${errorMessage(body)}`);
|
|
8226
8807
|
}
|
|
8227
8808
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
8228
|
-
(0,
|
|
8809
|
+
(0, import_node_fs14.writeFileSync)(outPath, bytes);
|
|
8229
8810
|
}
|
|
8230
8811
|
function parseMetadata(raw) {
|
|
8231
8812
|
if (!raw) return {};
|
|
@@ -8313,6 +8894,7 @@ function stringField(obj, key) {
|
|
|
8313
8894
|
|
|
8314
8895
|
// src/cli/commands/banner.ts
|
|
8315
8896
|
var import_commander4 = require("commander");
|
|
8897
|
+
init_util();
|
|
8316
8898
|
function buildBannerCommand() {
|
|
8317
8899
|
return new import_commander4.Command("banner").description("Show the Prismer runtime CLI banner").option("--compact", "Show a single-line banner").option("--json", "Accept --json for global flag compatibility (banner is suppressed in JSON mode)").action((opts) => {
|
|
8318
8900
|
printBanner({ compact: opts.compact });
|
|
@@ -8321,6 +8903,8 @@ function buildBannerCommand() {
|
|
|
8321
8903
|
|
|
8322
8904
|
// src/cli/commands/chat.ts
|
|
8323
8905
|
var import_commander5 = require("commander");
|
|
8906
|
+
init_util();
|
|
8907
|
+
init_ui();
|
|
8324
8908
|
function buildChatCommand() {
|
|
8325
8909
|
const cmd = new import_commander5.Command("chat").description("Use IM chat and group APIs");
|
|
8326
8910
|
cmd.command("me").description("Show the current IM identity").option("--json", "Print raw JSON response").action(async (opts) => {
|
|
@@ -8495,6 +9079,8 @@ function sanitizeError(message) {
|
|
|
8495
9079
|
|
|
8496
9080
|
// src/cli/commands/config.ts
|
|
8497
9081
|
var import_commander6 = require("commander");
|
|
9082
|
+
init_util();
|
|
9083
|
+
init_ui();
|
|
8498
9084
|
var SETTABLE_KEYS = ["cloud_api_base", "api_key", "daemon_id"];
|
|
8499
9085
|
function redactApiKey(key) {
|
|
8500
9086
|
if (!key.startsWith("sk-prismer-")) return "***";
|
|
@@ -8590,6 +9176,8 @@ function buildConfigCommand() {
|
|
|
8590
9176
|
|
|
8591
9177
|
// src/cli/commands/cookbook.ts
|
|
8592
9178
|
var import_commander7 = require("commander");
|
|
9179
|
+
init_util();
|
|
9180
|
+
init_ui();
|
|
8593
9181
|
function buildCookbookCommand() {
|
|
8594
9182
|
const cmd = new import_commander7.Command("cookbook").description("Run CLI-only 54release MVP regression suites");
|
|
8595
9183
|
cmd.command("run").description("Run one or more cookbook smoke suites using the configured API key").option("--suite <name>", "status|im|task|group|asset|sandbox|all, comma-separated", "all").option("--workspace-id <id>", "Workspace id for workspace-scoped suites").option("--agent-id <id>", "Agent IM user id for task create smoke").option("--group-id <id>", "Group/conversation id for group message history smoke").option("--sandbox-id <id>", "Sandbox id for sandbox status smoke").option("--prompt <text>", "Prompt for optional task create smoke").option("--timeout-ms <ms>", "Request/task timeout", parsePositiveInt2, 6e4).option("--strict", "Treat skipped optional checks as failure").option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
|
|
@@ -8847,9 +9435,11 @@ function parsePositiveInt2(value) {
|
|
|
8847
9435
|
// src/cli/commands/daemon.ts
|
|
8848
9436
|
var import_commander8 = require("commander");
|
|
8849
9437
|
var import_node_child_process7 = require("child_process");
|
|
8850
|
-
var
|
|
9438
|
+
var import_node_fs15 = require("fs");
|
|
8851
9439
|
var import_promises2 = require("timers/promises");
|
|
8852
|
-
var
|
|
9440
|
+
var import_node_path11 = require("path");
|
|
9441
|
+
init_util();
|
|
9442
|
+
init_ui();
|
|
8853
9443
|
function buildDaemonCommand() {
|
|
8854
9444
|
const cmd = new import_commander8.Command("daemon").description("Manage the prismer daemon process");
|
|
8855
9445
|
cmd.command("start").description("Start the daemon in the background (use --foreground for Docker/systemd)").option("--port <port>", "Local server port (default 3210)", (v) => Number.parseInt(v, 10)).option("--no-local-server", "Skip starting the local 127.0.0.1 server").option("--foreground", "Run in the foreground instead of daemonizing").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
@@ -8921,9 +9511,9 @@ function buildDaemonCommand() {
|
|
|
8921
9511
|
if (existingPid && pidAlive(existingPid)) {
|
|
8922
9512
|
exitWithError(`Daemon already running (pid ${existingPid}). Use \`prismer daemon stop\` first.`);
|
|
8923
9513
|
}
|
|
8924
|
-
if (!(0,
|
|
8925
|
-
const logFile = (0,
|
|
8926
|
-
const fd = (0,
|
|
9514
|
+
if (!(0, import_node_fs15.existsSync)(paths.logsDir)) (0, import_node_fs15.mkdirSync)(paths.logsDir, { recursive: true });
|
|
9515
|
+
const logFile = (0, import_node_path11.join)(paths.logsDir, "daemon.log");
|
|
9516
|
+
const fd = (0, import_node_fs15.openSync)(logFile, "a");
|
|
8927
9517
|
const args = [process.argv[1], "daemon", "run"];
|
|
8928
9518
|
if (opts.port) args.push("--port", String(opts.port));
|
|
8929
9519
|
if (opts.localServer === false) args.push("--no-local-server");
|
|
@@ -9006,8 +9596,8 @@ function buildDaemonCommand() {
|
|
|
9006
9596
|
});
|
|
9007
9597
|
cmd.command("logs").description("Show daemon logs").option("--tail <n>", "Number of lines to show", (v) => Number.parseInt(v, 10), 80).option("--follow", "Follow log output").option("--json", "Accept --json for global flag compatibility (raw log bytes are streamed)").action(async (opts) => {
|
|
9008
9598
|
const paths = resolvePaths();
|
|
9009
|
-
const logFile = (0,
|
|
9010
|
-
if (!(0,
|
|
9599
|
+
const logFile = (0, import_node_path11.join)(paths.logsDir, "daemon.log");
|
|
9600
|
+
if (!(0, import_node_fs15.existsSync)(logFile)) {
|
|
9011
9601
|
exitWithError(`No daemon log found at ${logFile}. Start the daemon with \`prismer daemon start\`.`);
|
|
9012
9602
|
}
|
|
9013
9603
|
const lines = Math.max(1, opts.tail);
|
|
@@ -9048,13 +9638,13 @@ async function tailFromEnd(path7, lines) {
|
|
|
9048
9638
|
}
|
|
9049
9639
|
}
|
|
9050
9640
|
async function followFile(path7) {
|
|
9051
|
-
let offset = (0,
|
|
9641
|
+
let offset = (0, import_node_fs15.statSync)(path7).size;
|
|
9052
9642
|
for (; ; ) {
|
|
9053
9643
|
await (0, import_promises2.setTimeout)(1e3);
|
|
9054
|
-
const size = (0,
|
|
9644
|
+
const size = (0, import_node_fs15.statSync)(path7).size;
|
|
9055
9645
|
if (size < offset) offset = 0;
|
|
9056
9646
|
if (size === offset) continue;
|
|
9057
|
-
const stream = (0,
|
|
9647
|
+
const stream = (0, import_node_fs15.createReadStream)(path7, { start: offset, end: size - 1, encoding: "utf8" });
|
|
9058
9648
|
for await (const chunk of stream) process.stdout.write(chunk);
|
|
9059
9649
|
offset = size;
|
|
9060
9650
|
}
|
|
@@ -9062,15 +9652,16 @@ async function followFile(path7) {
|
|
|
9062
9652
|
|
|
9063
9653
|
// src/cli/commands/events.ts
|
|
9064
9654
|
var import_commander9 = require("commander");
|
|
9065
|
-
var
|
|
9066
|
-
var
|
|
9067
|
-
var
|
|
9655
|
+
var import_node_fs16 = require("fs");
|
|
9656
|
+
var import_node_os7 = require("os");
|
|
9657
|
+
var import_node_path12 = require("path");
|
|
9068
9658
|
var import_node_readline = require("readline");
|
|
9069
|
-
|
|
9659
|
+
init_util();
|
|
9660
|
+
var DEFAULT_LIMIT2 = 50;
|
|
9070
9661
|
function buildEventsCommand() {
|
|
9071
9662
|
return addEventOptions(new import_commander9.Command("events").description("Read local PARA events from ~/.prismer/para/events.jsonl")).action(async (opts) => {
|
|
9072
9663
|
const file = eventsPath();
|
|
9073
|
-
if (!(0,
|
|
9664
|
+
if (!(0, import_node_fs16.existsSync)(file)) {
|
|
9074
9665
|
printJson(unavailable(file));
|
|
9075
9666
|
process.exitCode = 1;
|
|
9076
9667
|
return;
|
|
@@ -9087,7 +9678,7 @@ function buildEventsCommand() {
|
|
|
9087
9678
|
function buildEventsStatsCommand() {
|
|
9088
9679
|
return addEventOptions(new import_commander9.Command("events:stats").description("Summarize local PARA events from ~/.prismer/para/events.jsonl")).action(async (opts) => {
|
|
9089
9680
|
const file = eventsPath();
|
|
9090
|
-
if (!(0,
|
|
9681
|
+
if (!(0, import_node_fs16.existsSync)(file)) {
|
|
9091
9682
|
printJson(unavailable(file));
|
|
9092
9683
|
process.exitCode = 1;
|
|
9093
9684
|
return;
|
|
@@ -9102,10 +9693,10 @@ function buildEventsStatsCommand() {
|
|
|
9102
9693
|
});
|
|
9103
9694
|
}
|
|
9104
9695
|
function addEventOptions(cmd) {
|
|
9105
|
-
return cmd.option("--limit <n>", "Max events to return/read", parsePositiveInt3,
|
|
9696
|
+
return cmd.option("--limit <n>", "Max events to return/read", parsePositiveInt3, DEFAULT_LIMIT2).option("--agent-id <id>", "Filter by agent id").option("--session-id <id>", "Filter by session id").option("--family <name>", "Filter by event family").option("--type <name>", "Filter by event type").option("--json", "Output JSON (default)");
|
|
9106
9697
|
}
|
|
9107
9698
|
function eventsPath() {
|
|
9108
|
-
return (0,
|
|
9699
|
+
return (0, import_node_path12.join)(process.env.PRISMER_HOME ?? (0, import_node_path12.join)((0, import_node_os7.homedir)(), ".prismer"), "para", "events.jsonl");
|
|
9109
9700
|
}
|
|
9110
9701
|
function unavailable(file) {
|
|
9111
9702
|
return {
|
|
@@ -9121,7 +9712,7 @@ function unavailable(file) {
|
|
|
9121
9712
|
async function readEvents(file, filters) {
|
|
9122
9713
|
const out = [];
|
|
9123
9714
|
const rl = (0, import_node_readline.createInterface)({
|
|
9124
|
-
input: (0,
|
|
9715
|
+
input: (0, import_node_fs16.createReadStream)(file, { encoding: "utf8" }),
|
|
9125
9716
|
crlfDelay: Infinity
|
|
9126
9717
|
});
|
|
9127
9718
|
for await (const line of rl) {
|
|
@@ -9173,7 +9764,7 @@ function bump(map, key) {
|
|
|
9173
9764
|
}
|
|
9174
9765
|
function normalizeFilters(opts) {
|
|
9175
9766
|
return {
|
|
9176
|
-
limit: Math.max(1, Math.min(1e4, Number.isFinite(opts.limit) ? opts.limit :
|
|
9767
|
+
limit: Math.max(1, Math.min(1e4, Number.isFinite(opts.limit) ? opts.limit : DEFAULT_LIMIT2)),
|
|
9177
9768
|
agentId: opts.agentId,
|
|
9178
9769
|
sessionId: opts.sessionId,
|
|
9179
9770
|
family: opts.family,
|
|
@@ -9185,13 +9776,14 @@ function cleanFilters(filters) {
|
|
|
9185
9776
|
}
|
|
9186
9777
|
function parsePositiveInt3(v) {
|
|
9187
9778
|
const n = Number.parseInt(v, 10);
|
|
9188
|
-
return Number.isFinite(n) && n > 0 ? n :
|
|
9779
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_LIMIT2;
|
|
9189
9780
|
}
|
|
9190
9781
|
|
|
9191
9782
|
// src/cli/commands/memory.ts
|
|
9192
9783
|
var import_better_sqlite34 = __toESM(require("better-sqlite3"), 1);
|
|
9193
9784
|
var import_commander10 = require("commander");
|
|
9194
|
-
var
|
|
9785
|
+
var import_node_fs17 = require("fs");
|
|
9786
|
+
init_util();
|
|
9195
9787
|
var LOCAL_BASE = process.env.PRISMER_DAEMON_URL ?? "http://127.0.0.1:3210";
|
|
9196
9788
|
function buildMemoryCommand() {
|
|
9197
9789
|
const cmd = new import_commander10.Command("memory").description("Inspect local daemon memory/cache state");
|
|
@@ -9283,7 +9875,7 @@ async function tryDaemon(methods, paths) {
|
|
|
9283
9875
|
try {
|
|
9284
9876
|
const res = await fetch(`${LOCAL_BASE}${path7}`, { method, signal: AbortSignal.timeout(1500) });
|
|
9285
9877
|
if (res.status === 404) continue;
|
|
9286
|
-
const body = await
|
|
9878
|
+
const body = await readJson4(res);
|
|
9287
9879
|
if (!res.ok) {
|
|
9288
9880
|
return {
|
|
9289
9881
|
ok: false,
|
|
@@ -9302,7 +9894,7 @@ async function tryDaemon(methods, paths) {
|
|
|
9302
9894
|
}
|
|
9303
9895
|
return void 0;
|
|
9304
9896
|
}
|
|
9305
|
-
async function
|
|
9897
|
+
async function readJson4(res) {
|
|
9306
9898
|
const text = await res.text();
|
|
9307
9899
|
if (!text) return null;
|
|
9308
9900
|
try {
|
|
@@ -9342,7 +9934,7 @@ function readCacheSnapshot(limit) {
|
|
|
9342
9934
|
const paths = resolvePaths();
|
|
9343
9935
|
const empty = {
|
|
9344
9936
|
dbPath: paths.localDb,
|
|
9345
|
-
dbExists: (0,
|
|
9937
|
+
dbExists: (0, import_node_fs17.existsSync)(paths.localDb),
|
|
9346
9938
|
tables: {
|
|
9347
9939
|
cached_assets: { exists: false, count: 0, sizeBytes: 0 },
|
|
9348
9940
|
workspace_files_mirror: { exists: false, count: 0 }
|
|
@@ -9396,9 +9988,9 @@ function readCacheSnapshot(limit) {
|
|
|
9396
9988
|
db?.close();
|
|
9397
9989
|
}
|
|
9398
9990
|
}
|
|
9399
|
-
function tableExists(db,
|
|
9400
|
-
const row = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(
|
|
9401
|
-
return row?.name ===
|
|
9991
|
+
function tableExists(db, table2) {
|
|
9992
|
+
const row = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(table2);
|
|
9993
|
+
return row?.name === table2;
|
|
9402
9994
|
}
|
|
9403
9995
|
function assetRow(row) {
|
|
9404
9996
|
const hash = String(row.content_hash ?? "");
|
|
@@ -9462,6 +10054,8 @@ function messageFromBody(body) {
|
|
|
9462
10054
|
|
|
9463
10055
|
// src/cli/commands/pair.ts
|
|
9464
10056
|
var import_commander11 = require("commander");
|
|
10057
|
+
init_util();
|
|
10058
|
+
init_ui();
|
|
9465
10059
|
function buildPairCommand() {
|
|
9466
10060
|
return new import_commander11.Command("pair").description("Legacy QR approval path; use `prismer setup` to bind this runtime").option(
|
|
9467
10061
|
"--cloud <url>",
|
|
@@ -9504,10 +10098,11 @@ function buildPairCommand() {
|
|
|
9504
10098
|
|
|
9505
10099
|
// src/cli/commands/profile.ts
|
|
9506
10100
|
var import_commander12 = require("commander");
|
|
9507
|
-
var
|
|
9508
|
-
var
|
|
9509
|
-
var
|
|
10101
|
+
var import_node_fs18 = require("fs");
|
|
10102
|
+
var import_node_os8 = require("os");
|
|
10103
|
+
var import_node_path13 = require("path");
|
|
9510
10104
|
var import_node_child_process8 = require("child_process");
|
|
10105
|
+
init_util();
|
|
9511
10106
|
function buildProfileCommand() {
|
|
9512
10107
|
const cmd = new import_commander12.Command("profile").description("Manage AgentProfile (per-agent adapter config)");
|
|
9513
10108
|
cmd.command("templates").description("List built-in role templates (PM / Engineer / CEO \u2026)").option("--json", "Output JSON (default)").action(() => {
|
|
@@ -9518,7 +10113,7 @@ function buildProfileCommand() {
|
|
|
9518
10113
|
const data = await cloud.get(`/api/im/agent_profiles?agentId=${encodeURIComponent(opts.agent)}`);
|
|
9519
10114
|
printJson(data);
|
|
9520
10115
|
}, { code: "profile_list_failed" }));
|
|
9521
|
-
cmd.command("create").description("Create an AgentProfile").requiredOption("--agent <imUserId>", "Agent IMUser.id this profile belongs to").requiredOption("--name <name>", "Profile display name (unique within workspace+agent)").option("--adapter <name>", "Adapter name (defaults to template.applicableAdapters[0] or hermes)").option("--config <jsonOrPath>", "Inline JSON or @path/to/file containing adapter config").option("--from-template <name>", "Use a built-in role template (run `prismer profile templates` to list)").option("--workspace-id <id>", "Workspace id (cloud derives Personal default if omitted)").option("--json", "Output JSON (default)").action(runAction(async (opts) => {
|
|
10116
|
+
cmd.command("create").description("Create an AgentProfile").requiredOption("--agent <imUserId>", "Agent IMUser.id this profile belongs to").requiredOption("--name <name>", "Profile display name (unique within workspace+agent)").option("--adapter <name>", "Adapter name (defaults to template.applicableAdapters[0] or hermes)").option("--config <jsonOrPath>", "Inline JSON or @path/to/file containing adapter config").option("--from-template <name>", "Use a built-in role template (run `prismer profile templates` to list)").option("--model <name>", "Model id (overrides template default; fetched from cloud if omitted)").option("--workspace-id <id>", "Workspace id (cloud derives Personal default if omitted)").option("--json", "Output JSON (default)").action(runAction(async (opts) => {
|
|
9522
10117
|
let configObj = {};
|
|
9523
10118
|
let adapterName = opts.adapter ?? "hermes";
|
|
9524
10119
|
if (opts.fromTemplate) {
|
|
@@ -9531,6 +10126,9 @@ function buildProfileCommand() {
|
|
|
9531
10126
|
const inline = readJsonArg(opts.config);
|
|
9532
10127
|
configObj = { ...configObj, ...inline };
|
|
9533
10128
|
}
|
|
10129
|
+
if (opts.model) {
|
|
10130
|
+
configObj.model = opts.model;
|
|
10131
|
+
}
|
|
9534
10132
|
const cloud = mkCloud4();
|
|
9535
10133
|
const wsId = opts.workspaceId ?? await resolveDefaultWorkspaceId(cloud);
|
|
9536
10134
|
const res = await cloud.request("POST", "/api/im/agent_profiles", {
|
|
@@ -9550,12 +10148,12 @@ function buildProfileCommand() {
|
|
|
9550
10148
|
const profile = await cloud.get(
|
|
9551
10149
|
`/api/im/agent_profiles/${encodeURIComponent(profileId)}`
|
|
9552
10150
|
);
|
|
9553
|
-
const tmpFile = (0,
|
|
9554
|
-
(0,
|
|
10151
|
+
const tmpFile = (0, import_node_path13.join)((0, import_node_os8.tmpdir)(), `prismer-profile-${profileId}.json`);
|
|
10152
|
+
(0, import_node_fs18.writeFileSync)(tmpFile, JSON.stringify(profile.config, null, 2), "utf8");
|
|
9555
10153
|
const editor = process.env.EDITOR || "vi";
|
|
9556
10154
|
const ed = (0, import_node_child_process8.spawnSync)(editor, [tmpFile], { stdio: "inherit" });
|
|
9557
10155
|
if (ed.status !== 0) exitWithError(`editor exited ${ed.status}`, { code: "editor_failed" });
|
|
9558
|
-
const newConfig = JSON.parse((0,
|
|
10156
|
+
const newConfig = JSON.parse((0, import_node_fs18.readFileSync)(tmpFile, "utf8"));
|
|
9559
10157
|
const res = await cloud.request("PATCH", `/api/im/agent_profiles/${encodeURIComponent(profileId)}`, {
|
|
9560
10158
|
body: { config: newConfig, version: profile.version }
|
|
9561
10159
|
});
|
|
@@ -9577,8 +10175,8 @@ function mkCloud4() {
|
|
|
9577
10175
|
function readJsonArg(arg) {
|
|
9578
10176
|
if (arg.startsWith("@")) {
|
|
9579
10177
|
const path7 = arg.slice(1);
|
|
9580
|
-
if (!(0,
|
|
9581
|
-
return JSON.parse((0,
|
|
10178
|
+
if (!(0, import_node_fs18.existsSync)(path7)) throw new Error(`File not found: ${path7}`);
|
|
10179
|
+
return JSON.parse((0, import_node_fs18.readFileSync)(path7, "utf8"));
|
|
9582
10180
|
}
|
|
9583
10181
|
return JSON.parse(arg);
|
|
9584
10182
|
}
|
|
@@ -9591,6 +10189,8 @@ async function resolveDefaultWorkspaceId(cloud) {
|
|
|
9591
10189
|
|
|
9592
10190
|
// src/cli/commands/sandbox.ts
|
|
9593
10191
|
var import_commander13 = require("commander");
|
|
10192
|
+
init_util();
|
|
10193
|
+
init_ui();
|
|
9594
10194
|
function buildSandboxCommand() {
|
|
9595
10195
|
const cmd = new import_commander13.Command("sandbox").description("Inspect and smoke-test sandbox lifecycle");
|
|
9596
10196
|
cmd.command("list").description("List sandbox containers in a workspace").requiredOption("--workspace-id <id>", "Workspace id").option("--status <status>", "Status filter").option("--limit <n>", "Max rows", parsePositiveInt5, 50).option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
|
|
@@ -9792,11 +10392,13 @@ async function safeParseResponse(res) {
|
|
|
9792
10392
|
|
|
9793
10393
|
// src/cli/commands/setup.ts
|
|
9794
10394
|
var import_commander14 = require("commander");
|
|
9795
|
-
var
|
|
10395
|
+
var import_node_os9 = require("os");
|
|
9796
10396
|
var import_node_child_process9 = require("child_process");
|
|
9797
|
-
var
|
|
9798
|
-
var
|
|
10397
|
+
var import_node_crypto6 = require("crypto");
|
|
10398
|
+
var import_node_fs19 = require("fs");
|
|
9799
10399
|
var import_node_http2 = require("http");
|
|
10400
|
+
init_util();
|
|
10401
|
+
init_ui();
|
|
9800
10402
|
function buildSetupCommand() {
|
|
9801
10403
|
return new import_commander14.Command("setup").description("Set up this local runtime and bind it to Prismer Cloud").argument("[api-key]", "Prismer daemon API key; primarily for manual recovery and automation").option(
|
|
9802
10404
|
"--cloud <url>",
|
|
@@ -9819,6 +10421,7 @@ function buildSetupCommand() {
|
|
|
9819
10421
|
getUI().blank();
|
|
9820
10422
|
}
|
|
9821
10423
|
const shouldStart = opts.start !== false;
|
|
10424
|
+
stopRunningDaemon(paths);
|
|
9822
10425
|
if (opts.pair || opts.asUser) {
|
|
9823
10426
|
if (!opts.json) warn("Legacy pair setup path", "plain `prismer setup --start` is the canonical runtime binding flow");
|
|
9824
10427
|
if (opts.asUser && process.env.LOCAL_ONLY !== "1") {
|
|
@@ -9835,7 +10438,7 @@ function buildSetupCommand() {
|
|
|
9835
10438
|
}
|
|
9836
10439
|
const result = await pair({
|
|
9837
10440
|
cloudBaseUrl,
|
|
9838
|
-
deviceName: opts.deviceName ?? (0,
|
|
10441
|
+
deviceName: opts.deviceName ?? (0, import_node_os9.hostname)(),
|
|
9839
10442
|
force: opts.force,
|
|
9840
10443
|
paths,
|
|
9841
10444
|
asUserEmail: opts.asUser
|
|
@@ -9852,13 +10455,13 @@ function buildSetupCommand() {
|
|
|
9852
10455
|
apiKey = await mintDaemonApiKey({
|
|
9853
10456
|
cloudBaseUrl,
|
|
9854
10457
|
token: authToken,
|
|
9855
|
-
deviceName: opts.deviceName ?? (0,
|
|
10458
|
+
deviceName: opts.deviceName ?? (0, import_node_os9.hostname)()
|
|
9856
10459
|
});
|
|
9857
10460
|
}
|
|
9858
10461
|
if (!apiKey && !authToken && !opts.check && opts.browser !== false) {
|
|
9859
10462
|
apiKey = await runBrowserSetup({
|
|
9860
10463
|
cloudBaseUrl,
|
|
9861
|
-
deviceName: opts.deviceName ?? (0,
|
|
10464
|
+
deviceName: opts.deviceName ?? (0, import_node_os9.hostname)(),
|
|
9862
10465
|
json: Boolean(opts.json)
|
|
9863
10466
|
});
|
|
9864
10467
|
}
|
|
@@ -9897,9 +10500,13 @@ function buildSetupCommand() {
|
|
|
9897
10500
|
if (configExists(paths) && !opts.force) {
|
|
9898
10501
|
const cfg = loadConfig(paths);
|
|
9899
10502
|
if (opts.json) {
|
|
9900
|
-
printJson({ ok: true, alreadyConfigured: true, config: paths.configFile, daemonId: cfg.daemon_id });
|
|
10503
|
+
printJson({ ok: true, alreadyConfigured: true, config: paths.configFile, daemonId: cfg.daemon_id, daemonStartRequested: shouldStart });
|
|
9901
10504
|
} else {
|
|
9902
10505
|
warn("Already configured", paths.configFile);
|
|
10506
|
+
if (shouldStart) {
|
|
10507
|
+
startDaemonDetached(paths.root);
|
|
10508
|
+
ok("Daemon start requested");
|
|
10509
|
+
}
|
|
9903
10510
|
tip("prismer setup --force <api-key>", "overwrite config");
|
|
9904
10511
|
}
|
|
9905
10512
|
return;
|
|
@@ -9938,7 +10545,7 @@ function buildSetupCommand() {
|
|
|
9938
10545
|
}, { code: "setup_failed" }));
|
|
9939
10546
|
}
|
|
9940
10547
|
async function runBrowserSetup(input) {
|
|
9941
|
-
const state = (0,
|
|
10548
|
+
const state = (0, import_node_crypto6.randomBytes)(18).toString("base64url");
|
|
9942
10549
|
const server = (0, import_node_http2.createServer)();
|
|
9943
10550
|
const apiKeyPromise = waitForSetupCallback(server, state);
|
|
9944
10551
|
await new Promise((resolve3, reject) => {
|
|
@@ -9982,14 +10589,15 @@ function waitForSetupCallback(server, expectedState) {
|
|
|
9982
10589
|
}
|
|
9983
10590
|
const state = url.searchParams.get("state");
|
|
9984
10591
|
const key = url.searchParams.get("key");
|
|
10592
|
+
const PAGE = '<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Prismer Setup</title><style>body{background:#0a0a0a;color:#e5e5e5;font-family:system-ui,-apple-system,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;text-align:center}div{max-width:480px;padding:40px}h1{font-size:28px;margin-bottom:16px}p{font-size:15px;color:#888;line-height:1.6}.check{color:#4ade80;font-size:48px;margin-bottom:16px}.cross{color:#ef4444;font-size:48px;margin-bottom:16px}</style></head><body><div>';
|
|
9985
10593
|
if (state !== expectedState || !key || !/^sk-prismer-/.test(key)) {
|
|
9986
10594
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
9987
|
-
res.end("
|
|
10595
|
+
res.end(`${PAGE}<div class="cross">✗</div><h1>Prismer setup failed</h1><p>Invalid setup callback. You can close this tab and retry.</p></div></body></html>`);
|
|
9988
10596
|
reject(new Error("setup: invalid browser callback"));
|
|
9989
10597
|
return;
|
|
9990
10598
|
}
|
|
9991
10599
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
9992
|
-
res.end("
|
|
10600
|
+
res.end(`${PAGE}<div class="check">✓</div><h1>Prismer setup complete</h1><p>You can close this tab and return to your terminal.</p></div></body></html>`);
|
|
9993
10601
|
resolve3(key);
|
|
9994
10602
|
} catch (err) {
|
|
9995
10603
|
reject(err);
|
|
@@ -10034,13 +10642,35 @@ function startDaemonDetached(home) {
|
|
|
10034
10642
|
});
|
|
10035
10643
|
child.unref();
|
|
10036
10644
|
}
|
|
10645
|
+
function stopRunningDaemon(paths) {
|
|
10646
|
+
const pid = readPidFile(paths);
|
|
10647
|
+
if (!pid || !pidAlive(pid)) {
|
|
10648
|
+
if (pid) clearPidFile(paths);
|
|
10649
|
+
return;
|
|
10650
|
+
}
|
|
10651
|
+
try {
|
|
10652
|
+
process.kill(pid, "SIGTERM");
|
|
10653
|
+
} catch {
|
|
10654
|
+
return;
|
|
10655
|
+
}
|
|
10656
|
+
const deadline = Date.now() + 5e3;
|
|
10657
|
+
while (Date.now() < deadline) {
|
|
10658
|
+
if (!pidAlive(pid)) {
|
|
10659
|
+
clearPidFile(paths);
|
|
10660
|
+
return;
|
|
10661
|
+
}
|
|
10662
|
+
const start = Date.now();
|
|
10663
|
+
while (Date.now() - start < 200) {
|
|
10664
|
+
}
|
|
10665
|
+
}
|
|
10666
|
+
}
|
|
10037
10667
|
function shouldArchiveLocalDb(previous, next) {
|
|
10038
10668
|
return previous.api_key !== next.api_key || previous.cloud_api_base !== next.cloud_api_base || previous.daemon_id !== next.daemon_id;
|
|
10039
10669
|
}
|
|
10040
10670
|
function archiveLocalDb(localDbPath) {
|
|
10041
|
-
if (!(0,
|
|
10671
|
+
if (!(0, import_node_fs19.existsSync)(localDbPath)) return;
|
|
10042
10672
|
const archived = `${localDbPath}.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.bak`;
|
|
10043
|
-
(0,
|
|
10673
|
+
(0, import_node_fs19.renameSync)(localDbPath, archived);
|
|
10044
10674
|
}
|
|
10045
10675
|
async function mintDaemonApiKey(input) {
|
|
10046
10676
|
const label = `Daemon: ${input.deviceName} ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
@@ -10061,6 +10691,8 @@ async function mintDaemonApiKey(input) {
|
|
|
10061
10691
|
|
|
10062
10692
|
// src/cli/commands/status.ts
|
|
10063
10693
|
var import_commander15 = require("commander");
|
|
10694
|
+
init_util();
|
|
10695
|
+
init_ui();
|
|
10064
10696
|
function buildStatusCommand() {
|
|
10065
10697
|
return new import_commander15.Command("status").description("Show daemon + config + cloud status").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
10066
10698
|
const paths = resolvePaths();
|
|
@@ -10087,10 +10719,34 @@ function buildStatusCommand() {
|
|
|
10087
10719
|
const cloud = new CloudClient({ baseUrl: cfg.cloud_api_base, apiKey: cfg.api_key });
|
|
10088
10720
|
let cloudOk = false;
|
|
10089
10721
|
let me = null;
|
|
10722
|
+
let devices = null;
|
|
10723
|
+
let agents = null;
|
|
10090
10724
|
try {
|
|
10091
|
-
const
|
|
10092
|
-
cloudOk =
|
|
10093
|
-
me =
|
|
10725
|
+
const meRes = await cloud.request("GET", "/api/im/me", { timeoutMs: 3e3 });
|
|
10726
|
+
cloudOk = meRes.ok;
|
|
10727
|
+
me = meRes.data ?? null;
|
|
10728
|
+
if (cloudOk) {
|
|
10729
|
+
const wsRes = await cloud.request("GET", "/api/im/workspaces", { timeoutMs: 3e3 });
|
|
10730
|
+
if (wsRes.ok) {
|
|
10731
|
+
const wsBody = wsRes.data;
|
|
10732
|
+
const wsList = wsBody?.data;
|
|
10733
|
+
if (Array.isArray(wsList) && wsList.length > 0) {
|
|
10734
|
+
const wsId = wsList[0]?.id;
|
|
10735
|
+
if (wsId) {
|
|
10736
|
+
const devRes = await cloud.request("GET", `/api/workspace/runtime-installations?workspaceId=${encodeURIComponent(wsId)}&includeStopped=false`, { timeoutMs: 3e3 });
|
|
10737
|
+
if (devRes.ok) {
|
|
10738
|
+
const devBody = devRes.data;
|
|
10739
|
+
devices = devBody?.data;
|
|
10740
|
+
}
|
|
10741
|
+
const agRes = await cloud.request("GET", `/api/im/workspaces/${encodeURIComponent(wsId)}/agents`, { timeoutMs: 3e3 });
|
|
10742
|
+
if (agRes.ok) {
|
|
10743
|
+
const agBody = agRes.data;
|
|
10744
|
+
agents = agBody?.data;
|
|
10745
|
+
}
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
}
|
|
10749
|
+
}
|
|
10094
10750
|
} catch {
|
|
10095
10751
|
cloudOk = false;
|
|
10096
10752
|
}
|
|
@@ -10102,10 +10758,15 @@ function buildStatusCommand() {
|
|
|
10102
10758
|
daemon: {
|
|
10103
10759
|
running: daemonRunning,
|
|
10104
10760
|
pid: daemonStatus.pid ?? pid ?? null,
|
|
10105
|
-
wsConnected: daemonStatus.wsConnected ?? null
|
|
10761
|
+
wsConnected: daemonStatus.wsConnected ?? null,
|
|
10762
|
+
info: daemonStatus.info ?? {}
|
|
10106
10763
|
},
|
|
10107
|
-
cloud: { base: cfg.cloud_api_base, reachable: cloudOk, me },
|
|
10108
|
-
local
|
|
10764
|
+
cloud: { base: cfg.cloud_api_base, reachable: cloudOk, me, devices, agents },
|
|
10765
|
+
local,
|
|
10766
|
+
binding: {
|
|
10767
|
+
daemonId: cfg.daemon_id,
|
|
10768
|
+
apiKey: cfg.api_key
|
|
10769
|
+
}
|
|
10109
10770
|
};
|
|
10110
10771
|
if (opts.json) {
|
|
10111
10772
|
printJson(report);
|
|
@@ -10119,12 +10780,19 @@ async function readDaemonStatus() {
|
|
|
10119
10780
|
const res = await fetch("http://127.0.0.1:3210/healthz", {
|
|
10120
10781
|
signal: AbortSignal.timeout(1e3)
|
|
10121
10782
|
});
|
|
10122
|
-
if (
|
|
10123
|
-
|
|
10124
|
-
|
|
10783
|
+
if (res.ok) {
|
|
10784
|
+
const data = await res.json();
|
|
10785
|
+
return { running: true, pid: data.pid, wsConnected: data.wsConnected, info: data };
|
|
10786
|
+
}
|
|
10125
10787
|
} catch {
|
|
10126
|
-
return { running: false };
|
|
10127
10788
|
}
|
|
10789
|
+
const paths = resolvePaths();
|
|
10790
|
+
const pid = readPidFile(paths);
|
|
10791
|
+
if (pid) {
|
|
10792
|
+
const { pidAlive: pidAlive2 } = await Promise.resolve().then(() => (init_util(), util_exports));
|
|
10793
|
+
if (pidAlive2(pid)) return { running: true, pid };
|
|
10794
|
+
}
|
|
10795
|
+
return { running: false };
|
|
10128
10796
|
}
|
|
10129
10797
|
function readLocalCounts(localDbPath) {
|
|
10130
10798
|
try {
|
|
@@ -10150,21 +10818,59 @@ function printPretty(report) {
|
|
|
10150
10818
|
ui.blank();
|
|
10151
10819
|
ok("Config", report.paths.config);
|
|
10152
10820
|
if (report.daemon.running) {
|
|
10153
|
-
|
|
10821
|
+
const ws = report.daemon.wsConnected ? "connected" : "pending";
|
|
10822
|
+
ok("Daemon", `pid=${report.daemon.pid} ws=${ws}`);
|
|
10823
|
+
if (report.daemon.info) {
|
|
10824
|
+
const info2 = report.daemon.info;
|
|
10825
|
+
if (info2.version) ui.line(` Version: ${info2.version}`);
|
|
10826
|
+
if (info2.uptime) ui.line(` Uptime: ${Math.round(info2.uptime / 60)}m`);
|
|
10827
|
+
if (info2.memoryMb) ui.line(` Memory: ${info2.memoryMb} MB`);
|
|
10828
|
+
}
|
|
10154
10829
|
} else {
|
|
10155
10830
|
warn("Daemon", "not running");
|
|
10156
10831
|
tip("prismer daemon start");
|
|
10157
10832
|
}
|
|
10158
|
-
if (report.cloud.reachable)
|
|
10159
|
-
|
|
10833
|
+
if (report.cloud.reachable) {
|
|
10834
|
+
ok("Cloud", report.cloud.base);
|
|
10835
|
+
const me = report.cloud.me;
|
|
10836
|
+
if (me?.user) {
|
|
10837
|
+
const roleTag = me.user.role ? ` role=${me.user.role}` : "";
|
|
10838
|
+
ui.line(` Account: ${me.user.displayName ?? me.user.username ?? "?"}${roleTag}`);
|
|
10839
|
+
}
|
|
10840
|
+
if (me?.credits) {
|
|
10841
|
+
ui.line(` Credits: ${typeof me.credits.balance === "number" ? me.credits.balance.toLocaleString() : "?"}`);
|
|
10842
|
+
}
|
|
10843
|
+
} else {
|
|
10160
10844
|
fail2("Cloud", `${report.cloud.base} unreachable or unauthorized`);
|
|
10161
10845
|
tip("prismer setup --force");
|
|
10162
10846
|
}
|
|
10847
|
+
if (report.binding) {
|
|
10848
|
+
ui.blank();
|
|
10849
|
+
ui.line(` Daemon ID: ${report.binding.daemonId}`);
|
|
10850
|
+
const masked = report.binding.apiKey.slice(0, 14) + "\u2022\u2022\u2022\u2022" + report.binding.apiKey.slice(-4);
|
|
10851
|
+
ui.line(` API Key: ${masked}`);
|
|
10852
|
+
}
|
|
10853
|
+
if (Array.isArray(report.cloud.devices) && report.cloud.devices.length > 0) {
|
|
10854
|
+
const devs = report.cloud.devices;
|
|
10855
|
+
ui.blank();
|
|
10856
|
+
ui.line(` Workspace Devices (${devs.length}):`);
|
|
10857
|
+
for (const d of devs) {
|
|
10858
|
+
const statusIcon = d.daemonStatus === "connected" ? "\u25CF" : "\u25CB";
|
|
10859
|
+
const kind = d.runtimeKind === "docker" ? "Local" : "K8s";
|
|
10860
|
+
const declared = d.hostedAgentSummary?.declared ?? 0;
|
|
10861
|
+
ui.line(` ${statusIcon} ${d.podName?.replace(/^daemon:/, "")?.slice(0, 28) ?? "?"} kind=${kind} agents=${declared}`);
|
|
10862
|
+
}
|
|
10863
|
+
} else {
|
|
10864
|
+
ui.line(` Workspace Devices: none`);
|
|
10865
|
+
}
|
|
10866
|
+
if (Array.isArray(report.cloud.agents) && report.cloud.agents.length > 0) {
|
|
10867
|
+
ui.line(` Hosted agents: ${report.cloud.agents.length}`);
|
|
10868
|
+
}
|
|
10163
10869
|
if (report.local) {
|
|
10164
10870
|
ui.blank();
|
|
10165
|
-
ui.line(`
|
|
10166
|
-
ui.line(` Profiles:
|
|
10167
|
-
ui.line(`
|
|
10871
|
+
ui.line(` Local agents: ${report.local.agents}`);
|
|
10872
|
+
ui.line(` Profiles: ${report.local.profiles}`);
|
|
10873
|
+
ui.line(` Running tasks: ${report.local.runningTasks}`);
|
|
10168
10874
|
} else {
|
|
10169
10875
|
warn("Local DB", "unavailable");
|
|
10170
10876
|
}
|
|
@@ -10173,6 +10879,7 @@ function printPretty(report) {
|
|
|
10173
10879
|
// src/cli/commands/task.ts
|
|
10174
10880
|
var import_commander16 = require("commander");
|
|
10175
10881
|
var import_promises3 = require("timers/promises");
|
|
10882
|
+
init_util();
|
|
10176
10883
|
function describeStatus(status) {
|
|
10177
10884
|
return status === 0 ? "network error" : `HTTP ${status}`;
|
|
10178
10885
|
}
|
|
@@ -10292,6 +10999,8 @@ function taskFrom2(raw) {
|
|
|
10292
10999
|
|
|
10293
11000
|
// src/cli/commands/workspace.ts
|
|
10294
11001
|
var import_commander17 = require("commander");
|
|
11002
|
+
init_util();
|
|
11003
|
+
init_ui();
|
|
10295
11004
|
function buildWorkspaceCommand() {
|
|
10296
11005
|
const cmd = new import_commander17.Command("workspace").description("Manage workspaces, runtime snapshots, and workspace files");
|
|
10297
11006
|
cmd.command("list").description("List workspaces").option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
|
|
@@ -10526,7 +11235,8 @@ async function readResponseError(res) {
|
|
|
10526
11235
|
}
|
|
10527
11236
|
|
|
10528
11237
|
// src/cli/index.ts
|
|
10529
|
-
|
|
11238
|
+
init_ui();
|
|
11239
|
+
var VERSION = "1.9.7";
|
|
10530
11240
|
function buildProgram() {
|
|
10531
11241
|
const program = new import_commander18.Command("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(VERSION);
|
|
10532
11242
|
program.addCommand(buildBannerCommand());
|