@prismer/sdk 2.0.5 → 2.0.7
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/dist/cli.js +2313 -257
- package/dist/index.d.mts +723 -2
- package/dist/index.d.ts +723 -2
- package/dist/index.js +379 -1
- package/dist/index.mjs +367 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -36,9 +36,9 @@ __export(cli_exports, {
|
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(cli_exports);
|
|
38
38
|
var import_commander = require("commander");
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
var
|
|
39
|
+
var fs7 = __toESM(require("fs"));
|
|
40
|
+
var path5 = __toESM(require("path"));
|
|
41
|
+
var os3 = __toESM(require("os"));
|
|
42
42
|
var TOML2 = __toESM(require("@iarna/toml"));
|
|
43
43
|
|
|
44
44
|
// src/realtime.ts
|
|
@@ -163,7 +163,7 @@ var RealtimeWSClient = class extends TypedEmitter {
|
|
|
163
163
|
if (this._state === "connected" || this._state === "connecting") return;
|
|
164
164
|
this._state = "connecting";
|
|
165
165
|
this.intentionalClose = false;
|
|
166
|
-
return new Promise((
|
|
166
|
+
return new Promise((resolve5, reject) => {
|
|
167
167
|
try {
|
|
168
168
|
this.ws = new this.WS(this.wsUrl);
|
|
169
169
|
} catch (err) {
|
|
@@ -185,7 +185,7 @@ var RealtimeWSClient = class extends TypedEmitter {
|
|
|
185
185
|
this.emit("connected", void 0);
|
|
186
186
|
this.ws.removeEventListener("message", onFirstMessage);
|
|
187
187
|
this.ws.addEventListener("message", this.handleMessage);
|
|
188
|
-
|
|
188
|
+
resolve5();
|
|
189
189
|
}
|
|
190
190
|
} catch (_) {
|
|
191
191
|
}
|
|
@@ -257,12 +257,12 @@ var RealtimeWSClient = class extends TypedEmitter {
|
|
|
257
257
|
}
|
|
258
258
|
ping() {
|
|
259
259
|
const requestId = `ping-${++this.pingCounter}`;
|
|
260
|
-
return new Promise((
|
|
260
|
+
return new Promise((resolve5, reject) => {
|
|
261
261
|
const timer = setTimeout(() => {
|
|
262
262
|
this.pendingPings.delete(requestId);
|
|
263
263
|
reject(new Error("Ping timeout"));
|
|
264
264
|
}, 1e4);
|
|
265
|
-
this.pendingPings.set(requestId, { resolve:
|
|
265
|
+
this.pendingPings.set(requestId, { resolve: resolve5, timer });
|
|
266
266
|
this.sendRaw({ type: "ping", payload: { requestId } });
|
|
267
267
|
});
|
|
268
268
|
}
|
|
@@ -515,9 +515,9 @@ var WRITE_PATTERNS = [
|
|
|
515
515
|
{ method: "POST", pattern: /\/api\/im\/community\/posts\/[^/]+\/comments$/, opType: "community_comment" },
|
|
516
516
|
{ method: "POST", pattern: /\/api\/im\/community\/vote$/, opType: "community_vote" }
|
|
517
517
|
];
|
|
518
|
-
function matchWriteOp(method,
|
|
518
|
+
function matchWriteOp(method, path6) {
|
|
519
519
|
for (const { method: m, pattern, opType } of WRITE_PATTERNS) {
|
|
520
|
-
if (method === m && pattern.test(
|
|
520
|
+
if (method === m && pattern.test(path6)) return opType;
|
|
521
521
|
}
|
|
522
522
|
return null;
|
|
523
523
|
}
|
|
@@ -585,18 +585,18 @@ var OfflineManager = class extends OfflineEmitter {
|
|
|
585
585
|
/**
|
|
586
586
|
* Dispatch an IM request. Write ops go through outbox; reads check local cache.
|
|
587
587
|
*/
|
|
588
|
-
async dispatch(method,
|
|
589
|
-
const opType = matchWriteOp(method,
|
|
588
|
+
async dispatch(method, path6, body, query) {
|
|
589
|
+
const opType = matchWriteOp(method, path6);
|
|
590
590
|
if (opType) {
|
|
591
|
-
return this.dispatchWrite(opType, method,
|
|
591
|
+
return this.dispatchWrite(opType, method, path6, body, query);
|
|
592
592
|
}
|
|
593
593
|
if (method === "GET") {
|
|
594
|
-
const cached = await this.readFromCache(
|
|
594
|
+
const cached = await this.readFromCache(path6, query);
|
|
595
595
|
if (cached !== null) return cached;
|
|
596
596
|
}
|
|
597
597
|
try {
|
|
598
|
-
const result = await this.networkRequest(method,
|
|
599
|
-
if (method === "GET") this.cacheReadResult(
|
|
598
|
+
const result = await this.networkRequest(method, path6, body, query);
|
|
599
|
+
if (method === "GET") this.cacheReadResult(path6, query, result);
|
|
600
600
|
return result;
|
|
601
601
|
} catch {
|
|
602
602
|
if (!this._isOnline) {
|
|
@@ -606,7 +606,7 @@ var OfflineManager = class extends OfflineEmitter {
|
|
|
606
606
|
}
|
|
607
607
|
}
|
|
608
608
|
// ── Outbox: write operations ──────────────────────────────
|
|
609
|
-
async dispatchWrite(opType, method,
|
|
609
|
+
async dispatchWrite(opType, method, path6, body, query) {
|
|
610
610
|
const clientId = generateId();
|
|
611
611
|
const idempotencyKey = `sdk-${clientId}`;
|
|
612
612
|
let enrichedBody = body;
|
|
@@ -620,7 +620,7 @@ var OfflineManager = class extends OfflineEmitter {
|
|
|
620
620
|
let localMessage;
|
|
621
621
|
if (opType === "message.send" && body && typeof body === "object") {
|
|
622
622
|
const b = body;
|
|
623
|
-
const convIdMatch =
|
|
623
|
+
const convIdMatch = path6.match(/\/(?:messages|direct|groups)\/([^/]+)/);
|
|
624
624
|
const conversationId = convIdMatch?.[1] ?? "";
|
|
625
625
|
localMessage = {
|
|
626
626
|
id: `local-${clientId}`,
|
|
@@ -641,7 +641,7 @@ var OfflineManager = class extends OfflineEmitter {
|
|
|
641
641
|
id: clientId,
|
|
642
642
|
type: opType,
|
|
643
643
|
method,
|
|
644
|
-
path:
|
|
644
|
+
path: path6,
|
|
645
645
|
body: enrichedBody,
|
|
646
646
|
query,
|
|
647
647
|
status: "pending",
|
|
@@ -961,28 +961,28 @@ var OfflineManager = class extends OfflineEmitter {
|
|
|
961
961
|
return 0;
|
|
962
962
|
}
|
|
963
963
|
// ── Read cache ────────────────────────────────────────────
|
|
964
|
-
async readFromCache(
|
|
965
|
-
if (/\/api\/im\/conversations$/.test(
|
|
964
|
+
async readFromCache(path6, query) {
|
|
965
|
+
if (/\/api\/im\/conversations$/.test(path6)) {
|
|
966
966
|
const convos = await this.storage.getConversations({ limit: 50 });
|
|
967
967
|
if (convos.length > 0) return { ok: true, data: convos };
|
|
968
968
|
}
|
|
969
|
-
const msgMatch =
|
|
969
|
+
const msgMatch = path6.match(/\/api\/im\/messages\/([^/]+)$/);
|
|
970
970
|
if (msgMatch) {
|
|
971
971
|
const convId = msgMatch[1];
|
|
972
972
|
const limit = query?.limit ? parseInt(query.limit) : 50;
|
|
973
973
|
const messages = await this.storage.getMessages(convId, { limit, before: query?.before });
|
|
974
974
|
if (messages.length > 0) return { ok: true, data: messages };
|
|
975
975
|
}
|
|
976
|
-
if (/\/api\/im\/contacts$/.test(
|
|
976
|
+
if (/\/api\/im\/contacts$/.test(path6)) {
|
|
977
977
|
const contacts = await this.storage.getContacts();
|
|
978
978
|
if (contacts.length > 0) return { ok: true, data: contacts };
|
|
979
979
|
}
|
|
980
980
|
return null;
|
|
981
981
|
}
|
|
982
|
-
async cacheReadResult(
|
|
982
|
+
async cacheReadResult(path6, _query, result) {
|
|
983
983
|
if (!result?.ok || !result?.data) return;
|
|
984
984
|
try {
|
|
985
|
-
if (/\/api\/im\/conversations$/.test(
|
|
985
|
+
if (/\/api\/im\/conversations$/.test(path6) && Array.isArray(result.data)) {
|
|
986
986
|
const convos = result.data.map((c) => ({
|
|
987
987
|
id: c.id,
|
|
988
988
|
type: c.type ?? "direct",
|
|
@@ -996,7 +996,7 @@ var OfflineManager = class extends OfflineEmitter {
|
|
|
996
996
|
}));
|
|
997
997
|
await this.storage.putConversations(convos);
|
|
998
998
|
}
|
|
999
|
-
const msgMatch =
|
|
999
|
+
const msgMatch = path6.match(/\/api\/im\/messages\/([^/]+)$/);
|
|
1000
1000
|
if (msgMatch && Array.isArray(result.data)) {
|
|
1001
1001
|
const messages = result.data.map((m) => ({
|
|
1002
1002
|
id: m.id,
|
|
@@ -1011,7 +1011,7 @@ var OfflineManager = class extends OfflineEmitter {
|
|
|
1011
1011
|
}));
|
|
1012
1012
|
await this.storage.putMessages(messages);
|
|
1013
1013
|
}
|
|
1014
|
-
if (/\/api\/im\/contacts$/.test(
|
|
1014
|
+
if (/\/api\/im\/contacts$/.test(path6) && Array.isArray(result.data)) {
|
|
1015
1015
|
await this.storage.putContacts(result.data);
|
|
1016
1016
|
}
|
|
1017
1017
|
} catch {
|
|
@@ -2157,6 +2157,37 @@ var WorkspaceClient = class {
|
|
|
2157
2157
|
var TasksClient = class {
|
|
2158
2158
|
constructor(_r) {
|
|
2159
2159
|
this._r = _r;
|
|
2160
|
+
// ── release201/10 rev 2 — SPEC.md ─────────────────────────────────────
|
|
2161
|
+
this.spec = {
|
|
2162
|
+
/** Read SPEC.md latest revision. */
|
|
2163
|
+
get: (taskId) => this._r("GET", `/api/im/tasks/${taskId}/spec`),
|
|
2164
|
+
/** Owner sets/updates SPEC.md (writes a new revision). */
|
|
2165
|
+
set: (taskId, input) => this._r("PUT", `/api/im/tasks/${taskId}/spec`, input)
|
|
2166
|
+
};
|
|
2167
|
+
// ── release201/10 rev 2 — TODO.md ─────────────────────────────────────
|
|
2168
|
+
this.todo = {
|
|
2169
|
+
list: (taskId) => this._r("GET", `/api/im/tasks/${taskId}/todo`),
|
|
2170
|
+
add: (taskId, input) => this._r("POST", `/api/im/tasks/${taskId}/todo/items`, input),
|
|
2171
|
+
toggle: (taskId, index, done) => this._r("PATCH", `/api/im/tasks/${taskId}/todo/items/${index}`, { done }),
|
|
2172
|
+
setText: (taskId, index, text) => this._r("PATCH", `/api/im/tasks/${taskId}/todo/items/${index}`, { text }),
|
|
2173
|
+
remove: (taskId, index) => this._r("DELETE", `/api/im/tasks/${taskId}/todo/items/${index}`)
|
|
2174
|
+
};
|
|
2175
|
+
this.criteria = {
|
|
2176
|
+
/** List current criteria via the acceptance view. */
|
|
2177
|
+
list: (taskId) => this._r("GET", `/api/im/tasks/${taskId}/acceptance`),
|
|
2178
|
+
/** Add a criterion (rev 2 — verifyMode + expectation + verifierAgentId). */
|
|
2179
|
+
add: (taskId, input) => this._r("POST", `/api/im/tasks/${taskId}/criteria`, input),
|
|
2180
|
+
/** Update a criterion in place. */
|
|
2181
|
+
update: (taskId, cid, patch) => this._r("PATCH", `/api/im/tasks/${taskId}/criteria/${cid}`, patch),
|
|
2182
|
+
/** Remove a criterion. */
|
|
2183
|
+
remove: (taskId, cid) => this._r("DELETE", `/api/im/tasks/${taskId}/criteria/${cid}`),
|
|
2184
|
+
/**
|
|
2185
|
+
* Unified verify entry (rev 2). Manual reviewer, agent-self-check
|
|
2186
|
+
* report, AND verifier-agent report all use this — cloud routes by
|
|
2187
|
+
* actor identity.
|
|
2188
|
+
*/
|
|
2189
|
+
verify: (taskId, cid, input) => this._r("POST", `/api/im/tasks/${taskId}/criteria/${cid}/verify`, input)
|
|
2190
|
+
};
|
|
2160
2191
|
}
|
|
2161
2192
|
/** Create a new task */
|
|
2162
2193
|
async create(options) {
|
|
@@ -2176,6 +2207,7 @@ var TasksClient = class {
|
|
|
2176
2207
|
if (options?.limit != null) query.limit = String(options.limit);
|
|
2177
2208
|
if (options?.cursor) query.cursor = options.cursor;
|
|
2178
2209
|
if (options?.workspaceId) query.workspaceId = options.workspaceId;
|
|
2210
|
+
if (options?.projectId) query.projectId = options.projectId;
|
|
2179
2211
|
if (options?.conversationId) query.conversationId = options.conversationId;
|
|
2180
2212
|
return this._r("GET", "/api/im/tasks", void 0, query);
|
|
2181
2213
|
}
|
|
@@ -2211,6 +2243,10 @@ var TasksClient = class {
|
|
|
2211
2243
|
async update(taskId, options) {
|
|
2212
2244
|
return this._r("PATCH", `/api/im/tasks/${taskId}`, options);
|
|
2213
2245
|
}
|
|
2246
|
+
/** Move a task into a project, or pass null to return it to workspace-level. */
|
|
2247
|
+
async moveProject(taskId, targetProjectId) {
|
|
2248
|
+
return this._r("POST", `/api/im/tasks/${taskId}/move-project`, { targetProjectId });
|
|
2249
|
+
}
|
|
2214
2250
|
/** Claim a pending task */
|
|
2215
2251
|
async claim(taskId) {
|
|
2216
2252
|
return this._r("POST", `/api/im/tasks/${taskId}/claim`);
|
|
@@ -2265,6 +2301,39 @@ var TasksClient = class {
|
|
|
2265
2301
|
async forceTransition(taskId, options) {
|
|
2266
2302
|
return this._r("POST", `/api/im/tasks/${taskId}/force-transition`, options);
|
|
2267
2303
|
}
|
|
2304
|
+
// ── release201/10 — acceptance criteria ──────────────────────────────
|
|
2305
|
+
/** Get rolled-up acceptance + criteria list for a task. */
|
|
2306
|
+
async getAcceptance(taskId) {
|
|
2307
|
+
return this._r("GET", `/api/im/tasks/${taskId}/acceptance`);
|
|
2308
|
+
}
|
|
2309
|
+
/** Copy a template's criteria onto the task. */
|
|
2310
|
+
async applyTemplate(taskId, templateId) {
|
|
2311
|
+
return this._r("POST", `/api/im/tasks/${taskId}/apply-template`, { templateId });
|
|
2312
|
+
}
|
|
2313
|
+
};
|
|
2314
|
+
var CriteriaTemplatesClient = class {
|
|
2315
|
+
constructor(_r) {
|
|
2316
|
+
this._r = _r;
|
|
2317
|
+
}
|
|
2318
|
+
list(query) {
|
|
2319
|
+
const q = {};
|
|
2320
|
+
if (query?.capability) q.capability = query.capability;
|
|
2321
|
+
if (query?.workspaceId === null) q.workspaceId = "__global";
|
|
2322
|
+
else if (query?.workspaceId) q.workspaceId = query.workspaceId;
|
|
2323
|
+
return this._r("GET", "/api/im/criteria-templates", void 0, q);
|
|
2324
|
+
}
|
|
2325
|
+
get(id) {
|
|
2326
|
+
return this._r("GET", `/api/im/criteria-templates/${id}`);
|
|
2327
|
+
}
|
|
2328
|
+
create(input) {
|
|
2329
|
+
return this._r("POST", "/api/im/criteria-templates", input);
|
|
2330
|
+
}
|
|
2331
|
+
update(id, patch) {
|
|
2332
|
+
return this._r("PATCH", `/api/im/criteria-templates/${id}`, patch);
|
|
2333
|
+
}
|
|
2334
|
+
delete(id) {
|
|
2335
|
+
return this._r("DELETE", `/api/im/criteria-templates/${id}`);
|
|
2336
|
+
}
|
|
2268
2337
|
};
|
|
2269
2338
|
var MemoryClient = class {
|
|
2270
2339
|
constructor(_r) {
|
|
@@ -2337,6 +2406,32 @@ var KnowledgeLinkClient = class {
|
|
|
2337
2406
|
return this._r("GET", "/api/im/knowledge/links", void 0, { entityType, entityId });
|
|
2338
2407
|
}
|
|
2339
2408
|
};
|
|
2409
|
+
var MetricsClient = class {
|
|
2410
|
+
constructor(_r) {
|
|
2411
|
+
this._r = _r;
|
|
2412
|
+
}
|
|
2413
|
+
async emit(input) {
|
|
2414
|
+
return this._r("POST", "/api/im/metrics/emit", input);
|
|
2415
|
+
}
|
|
2416
|
+
async batch(events) {
|
|
2417
|
+
return this._r("POST", "/api/im/metrics/batch", { events });
|
|
2418
|
+
}
|
|
2419
|
+
async aggregate(opts) {
|
|
2420
|
+
const query = {
|
|
2421
|
+
namespace: opts.namespace,
|
|
2422
|
+
name: opts.name,
|
|
2423
|
+
agg: opts.agg
|
|
2424
|
+
};
|
|
2425
|
+
if (opts.range) query.range = opts.range;
|
|
2426
|
+
if (opts.from) query.from = opts.from;
|
|
2427
|
+
if (opts.to) query.to = opts.to;
|
|
2428
|
+
if (opts.groupBy && opts.groupBy.length) query.groupBy = opts.groupBy.join(",");
|
|
2429
|
+
if (opts.bucket) query.bucket = opts.bucket;
|
|
2430
|
+
const filterPairs = Object.entries(opts.filter).map(([k, v]) => `${k}:${v}`);
|
|
2431
|
+
if (filterPairs.length) query.filter = filterPairs.join(",");
|
|
2432
|
+
return this._r("GET", "/api/im/metrics/aggregate", void 0, query);
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2340
2435
|
var IdentityClient = class {
|
|
2341
2436
|
constructor(_r) {
|
|
2342
2437
|
this._r = _r;
|
|
@@ -2501,9 +2596,51 @@ var EvolutionSkillsClient = class {
|
|
|
2501
2596
|
return this._r("POST", `/api/im/skills/${encodeURIComponent(skillId)}/star`);
|
|
2502
2597
|
}
|
|
2503
2598
|
};
|
|
2599
|
+
var AgentSkillsClient = class {
|
|
2600
|
+
constructor(_r) {
|
|
2601
|
+
this._r = _r;
|
|
2602
|
+
}
|
|
2603
|
+
/** List skills installed on the agent. */
|
|
2604
|
+
async list(agentId, options) {
|
|
2605
|
+
const query = {};
|
|
2606
|
+
if (options?.workspaceId) query.workspaceId = options.workspaceId;
|
|
2607
|
+
if (options?.includeInactive) query.includeInactive = "true";
|
|
2608
|
+
return this._r("GET", `/api/im/agents/${encodeURIComponent(agentId)}/skills`, void 0, query);
|
|
2609
|
+
}
|
|
2610
|
+
/** Install a published skill onto the agent. */
|
|
2611
|
+
async install(agentId, input) {
|
|
2612
|
+
return this._r("POST", `/api/im/agents/${encodeURIComponent(agentId)}/skills`, input);
|
|
2613
|
+
}
|
|
2614
|
+
/** Uninstall (or disable, for built-ins) a skill from the agent. */
|
|
2615
|
+
async uninstall(agentId, skillIdOrSlug, options) {
|
|
2616
|
+
const query = {};
|
|
2617
|
+
if (options?.workspaceId) query.workspaceId = options.workspaceId;
|
|
2618
|
+
return this._r(
|
|
2619
|
+
"DELETE",
|
|
2620
|
+
`/api/im/agents/${encodeURIComponent(agentId)}/skills/${encodeURIComponent(skillIdOrSlug)}`,
|
|
2621
|
+
void 0,
|
|
2622
|
+
query
|
|
2623
|
+
);
|
|
2624
|
+
}
|
|
2625
|
+
/**
|
|
2626
|
+
* PATCH /api/im/agents/:agentId/skills/:skillId — Update per-skill config.
|
|
2627
|
+
*
|
|
2628
|
+
* Validates against the skill's `executableJson.configSchema` server-side
|
|
2629
|
+
* (release201/13 §3.4 / 07 §2.6). Returns the updated agentSkill row;
|
|
2630
|
+
* `installedRevision` will be null until the next daemon sync poll.
|
|
2631
|
+
*/
|
|
2632
|
+
async updateConfig(agentId, skillId, config, options) {
|
|
2633
|
+
return this._r(
|
|
2634
|
+
"PATCH",
|
|
2635
|
+
`/api/im/agents/${encodeURIComponent(agentId)}/skills/${encodeURIComponent(skillId)}`,
|
|
2636
|
+
{ config, workspaceId: options?.workspaceId }
|
|
2637
|
+
);
|
|
2638
|
+
}
|
|
2639
|
+
};
|
|
2504
2640
|
var AgentsClient = class {
|
|
2505
2641
|
constructor(_r) {
|
|
2506
2642
|
this._r = _r;
|
|
2643
|
+
this.skills = new AgentSkillsClient(_r);
|
|
2507
2644
|
}
|
|
2508
2645
|
async spec(agentId, workspaceId) {
|
|
2509
2646
|
const query = workspaceId ? { workspaceId } : void 0;
|
|
@@ -2540,6 +2677,63 @@ var AgentsClient = class {
|
|
|
2540
2677
|
async deletePack(packIdOrSlug) {
|
|
2541
2678
|
return this._r("DELETE", `/api/im/agent-packs/${encodeURIComponent(packIdOrSlug)}`);
|
|
2542
2679
|
}
|
|
2680
|
+
// release201/09 §9.7.2 Phase 3 — agent quiesce + transfer endpoints.
|
|
2681
|
+
//
|
|
2682
|
+
// pause/resume: workspace owner / orchestrator / admin only. Used by
|
|
2683
|
+
// `prismer agent export` to halt cloud dispatch on the source device
|
|
2684
|
+
// before tarballing the agent dir, and by `prismer agent import` to
|
|
2685
|
+
// resume dispatch on the target device after rebind.
|
|
2686
|
+
async pause(agentId) {
|
|
2687
|
+
return this._r("POST", `/api/im/agents/${encodeURIComponent(agentId)}/pause`, {});
|
|
2688
|
+
}
|
|
2689
|
+
async resume(agentId) {
|
|
2690
|
+
return this._r("POST", `/api/im/agents/${encodeURIComponent(agentId)}/resume`, {});
|
|
2691
|
+
}
|
|
2692
|
+
async transfer(input) {
|
|
2693
|
+
return this._r("POST", "/api/im/agent-bindings/transfer", input);
|
|
2694
|
+
}
|
|
2695
|
+
};
|
|
2696
|
+
var StudioEvolutionClient = class {
|
|
2697
|
+
constructor(_r) {
|
|
2698
|
+
this._r = _r;
|
|
2699
|
+
}
|
|
2700
|
+
/** List capsules emitted by the target agent (workspace-owner-scoped). */
|
|
2701
|
+
async capsules(agentId, options) {
|
|
2702
|
+
const query = { agentId };
|
|
2703
|
+
if (options?.page != null) query.page = String(options.page);
|
|
2704
|
+
if (options?.limit != null) query.limit = String(options.limit);
|
|
2705
|
+
if (options?.scope) query.scope = options.scope;
|
|
2706
|
+
return this._r("GET", "/api/im/studio/evolution/capsules", void 0, query);
|
|
2707
|
+
}
|
|
2708
|
+
/** List genes owned by the target agent (workspace-owner-scoped). */
|
|
2709
|
+
async genes(agentId, options) {
|
|
2710
|
+
const query = { agentId };
|
|
2711
|
+
if (options?.signals) query.signals = options.signals;
|
|
2712
|
+
return this._r("GET", "/api/im/studio/evolution/genes", void 0, query);
|
|
2713
|
+
}
|
|
2714
|
+
};
|
|
2715
|
+
var StudioClient = class {
|
|
2716
|
+
constructor(_r) {
|
|
2717
|
+
this._r = _r;
|
|
2718
|
+
this.evolution = new StudioEvolutionClient(_r);
|
|
2719
|
+
}
|
|
2720
|
+
/** Studio overview — counts + recent activity for a workspace. */
|
|
2721
|
+
async getOverview(workspaceId) {
|
|
2722
|
+
const query = workspaceId ? { workspaceId } : void 0;
|
|
2723
|
+
return this._r("GET", "/api/im/studio/overview", void 0, query);
|
|
2724
|
+
}
|
|
2725
|
+
/** Studio Profile domain — agent identity / personality / credits. */
|
|
2726
|
+
async getProfile(agentId) {
|
|
2727
|
+
const query = agentId ? { agentId } : void 0;
|
|
2728
|
+
return this._r("GET", "/api/im/studio/profile", void 0, query);
|
|
2729
|
+
}
|
|
2730
|
+
/** Studio Installed domain — workspace agents + active agent's skills. */
|
|
2731
|
+
async getInstalled(options) {
|
|
2732
|
+
const query = {};
|
|
2733
|
+
if (options?.workspaceId) query.workspaceId = options.workspaceId;
|
|
2734
|
+
if (options?.agentId) query.agentId = options.agentId;
|
|
2735
|
+
return this._r("GET", "/api/im/studio/installed", void 0, query);
|
|
2736
|
+
}
|
|
2543
2737
|
};
|
|
2544
2738
|
var EvolutionClient = class {
|
|
2545
2739
|
constructor(_r) {
|
|
@@ -2849,30 +3043,30 @@ var EvolutionClient = class {
|
|
|
2849
3043
|
}
|
|
2850
3044
|
const localPaths = [];
|
|
2851
3045
|
try {
|
|
2852
|
-
const
|
|
2853
|
-
const
|
|
2854
|
-
const
|
|
2855
|
-
const home =
|
|
2856
|
-
const pluginBase = process.env.PRISMER_PLUGIN_DIR ||
|
|
3046
|
+
const fs8 = await import("fs");
|
|
3047
|
+
const path6 = await import("path");
|
|
3048
|
+
const os4 = await import("os");
|
|
3049
|
+
const home = os4.homedir();
|
|
3050
|
+
const pluginBase = process.env.PRISMER_PLUGIN_DIR || path6.join(home, ".claude", "plugins", "prismer");
|
|
2857
3051
|
const platformPaths = options?.project ? {
|
|
2858
|
-
"claude-code":
|
|
2859
|
-
"openclaw":
|
|
2860
|
-
"opencode":
|
|
2861
|
-
"plugin":
|
|
3052
|
+
"claude-code": path6.join(options.projectRoot || ".", ".claude", "skills", slug),
|
|
3053
|
+
"openclaw": path6.join(options.projectRoot || ".", "skills", slug),
|
|
3054
|
+
"opencode": path6.join(options.projectRoot || ".", ".opencode", "skills", slug),
|
|
3055
|
+
"plugin": path6.join(options.projectRoot || ".", ".claude", "plugins", "prismer", "skills", slug)
|
|
2862
3056
|
} : {
|
|
2863
|
-
"claude-code":
|
|
2864
|
-
"openclaw":
|
|
2865
|
-
"opencode":
|
|
2866
|
-
"plugin":
|
|
3057
|
+
"claude-code": path6.join(home, ".claude", "skills", slug),
|
|
3058
|
+
"openclaw": path6.join(home, ".openclaw", "skills", slug),
|
|
3059
|
+
"opencode": path6.join(home, ".config", "opencode", "skills", slug),
|
|
3060
|
+
"plugin": path6.join(pluginBase, "skills", slug)
|
|
2867
3061
|
};
|
|
2868
3062
|
const targets = options?.platforms ?? Object.keys(platformPaths);
|
|
2869
3063
|
for (const platform of targets) {
|
|
2870
3064
|
const dir = platformPaths[platform];
|
|
2871
3065
|
if (!dir) continue;
|
|
2872
3066
|
try {
|
|
2873
|
-
|
|
2874
|
-
const filePath =
|
|
2875
|
-
|
|
3067
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
3068
|
+
const filePath = path6.join(dir, "SKILL.md");
|
|
3069
|
+
fs8.writeFileSync(filePath, content, "utf-8");
|
|
2876
3070
|
localPaths.push(filePath);
|
|
2877
3071
|
} catch {
|
|
2878
3072
|
}
|
|
@@ -2895,21 +3089,21 @@ var EvolutionClient = class {
|
|
|
2895
3089
|
const slug = safeSlug(slugOrId);
|
|
2896
3090
|
if (!slug) return withRemoved(result.data?.uninstalled ?? false, removedPaths);
|
|
2897
3091
|
try {
|
|
2898
|
-
const
|
|
2899
|
-
const
|
|
2900
|
-
const
|
|
2901
|
-
const home =
|
|
2902
|
-
const pluginBase = process.env.PRISMER_PLUGIN_DIR ||
|
|
3092
|
+
const fs8 = await import("fs");
|
|
3093
|
+
const path6 = await import("path");
|
|
3094
|
+
const os4 = await import("os");
|
|
3095
|
+
const home = os4.homedir();
|
|
3096
|
+
const pluginBase = process.env.PRISMER_PLUGIN_DIR || path6.join(home, ".claude", "plugins", "prismer");
|
|
2903
3097
|
const dirs = [
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
3098
|
+
path6.join(home, ".claude", "skills", slug),
|
|
3099
|
+
path6.join(home, ".openclaw", "skills", slug),
|
|
3100
|
+
path6.join(home, ".config", "opencode", "skills", slug),
|
|
3101
|
+
path6.join(pluginBase, "skills", slug)
|
|
2908
3102
|
];
|
|
2909
3103
|
for (const dir of dirs) {
|
|
2910
3104
|
try {
|
|
2911
|
-
if (
|
|
2912
|
-
|
|
3105
|
+
if (fs8.existsSync(dir)) {
|
|
3106
|
+
fs8.rmSync(dir, { recursive: true });
|
|
2913
3107
|
removedPaths.push(dir);
|
|
2914
3108
|
}
|
|
2915
3109
|
} catch {
|
|
@@ -2946,25 +3140,25 @@ var EvolutionClient = class {
|
|
|
2946
3140
|
failed++;
|
|
2947
3141
|
continue;
|
|
2948
3142
|
}
|
|
2949
|
-
const
|
|
2950
|
-
const
|
|
2951
|
-
const
|
|
2952
|
-
const home =
|
|
2953
|
-
const pluginBase = process.env.PRISMER_PLUGIN_DIR ||
|
|
3143
|
+
const fs8 = await import("fs");
|
|
3144
|
+
const path6 = await import("path");
|
|
3145
|
+
const os4 = await import("os");
|
|
3146
|
+
const home = os4.homedir();
|
|
3147
|
+
const pluginBase = process.env.PRISMER_PLUGIN_DIR || path6.join(home, ".claude", "plugins", "prismer");
|
|
2954
3148
|
const platformPaths = {
|
|
2955
|
-
"claude-code":
|
|
2956
|
-
"openclaw":
|
|
2957
|
-
"opencode":
|
|
2958
|
-
"plugin":
|
|
3149
|
+
"claude-code": path6.join(home, ".claude", "skills", slug),
|
|
3150
|
+
"openclaw": path6.join(home, ".openclaw", "skills", slug),
|
|
3151
|
+
"opencode": path6.join(home, ".config", "opencode", "skills", slug),
|
|
3152
|
+
"plugin": path6.join(pluginBase, "skills", slug)
|
|
2959
3153
|
};
|
|
2960
3154
|
const targets = options?.platforms ?? Object.keys(platformPaths);
|
|
2961
3155
|
for (const platform of targets) {
|
|
2962
3156
|
const dir = platformPaths[platform];
|
|
2963
3157
|
if (!dir) continue;
|
|
2964
3158
|
try {
|
|
2965
|
-
|
|
2966
|
-
const filePath =
|
|
2967
|
-
|
|
3159
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
3160
|
+
const filePath = path6.join(dir, "SKILL.md");
|
|
3161
|
+
fs8.writeFileSync(filePath, content, "utf-8");
|
|
2968
3162
|
paths.push(filePath);
|
|
2969
3163
|
} catch {
|
|
2970
3164
|
}
|
|
@@ -3042,6 +3236,8 @@ function safeSlug(input) {
|
|
|
3042
3236
|
var WorkspacesClient = class {
|
|
3043
3237
|
constructor(_r) {
|
|
3044
3238
|
this._r = _r;
|
|
3239
|
+
this.members = new WorkspaceMembersClient(_r);
|
|
3240
|
+
this.invites = new WorkspaceInvitesClient(_r);
|
|
3045
3241
|
}
|
|
3046
3242
|
/** List active workspaces owned by the caller. */
|
|
3047
3243
|
async list() {
|
|
@@ -3097,6 +3293,101 @@ var WorkspacesClient = class {
|
|
|
3097
3293
|
async revokeOrchestrator(workspaceId) {
|
|
3098
3294
|
return this._r("DELETE", `/api/im/workspaces/${workspaceId}/orchestrator`);
|
|
3099
3295
|
}
|
|
3296
|
+
// Sub-clients `members` / `invites` are declared at the top of the class and
|
|
3297
|
+
// wired up inside the constructor body — see `AgentsClient` for the same
|
|
3298
|
+
// pattern. Declaring them as field initializers that reference `this._r`
|
|
3299
|
+
// hits TS2729 because parameter-property assignment happens after class
|
|
3300
|
+
// field initializers under `useDefineForClassFields`.
|
|
3301
|
+
};
|
|
3302
|
+
var WorkspaceMembersClient = class {
|
|
3303
|
+
constructor(_r) {
|
|
3304
|
+
this._r = _r;
|
|
3305
|
+
}
|
|
3306
|
+
async list(workspaceId) {
|
|
3307
|
+
return this._r("GET", `/api/im/workspaces/${workspaceId}/members`);
|
|
3308
|
+
}
|
|
3309
|
+
async add(workspaceId, options) {
|
|
3310
|
+
return this._r("POST", `/api/im/workspaces/${workspaceId}/members`, options);
|
|
3311
|
+
}
|
|
3312
|
+
async update(workspaceId, memberId, options) {
|
|
3313
|
+
return this._r("PATCH", `/api/im/workspaces/${workspaceId}/members/${memberId}`, options);
|
|
3314
|
+
}
|
|
3315
|
+
async remove(workspaceId, memberId) {
|
|
3316
|
+
return this._r("DELETE", `/api/im/workspaces/${workspaceId}/members/${memberId}`);
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
var WorkspaceInvitesClient = class {
|
|
3320
|
+
constructor(_r) {
|
|
3321
|
+
this._r = _r;
|
|
3322
|
+
}
|
|
3323
|
+
async create(workspaceId, options) {
|
|
3324
|
+
return this._r("POST", `/api/im/workspaces/${workspaceId}/invites`, options);
|
|
3325
|
+
}
|
|
3326
|
+
async list(workspaceId) {
|
|
3327
|
+
return this._r("GET", `/api/im/workspaces/${workspaceId}/invites`);
|
|
3328
|
+
}
|
|
3329
|
+
async revoke(workspaceId, inviteId) {
|
|
3330
|
+
return this._r("DELETE", `/api/im/workspaces/${workspaceId}/invites/${inviteId}`);
|
|
3331
|
+
}
|
|
3332
|
+
};
|
|
3333
|
+
var InvitesClient = class {
|
|
3334
|
+
constructor(_r) {
|
|
3335
|
+
this._r = _r;
|
|
3336
|
+
}
|
|
3337
|
+
async preview(token) {
|
|
3338
|
+
return this._r("GET", `/api/im/invites/${encodeURIComponent(token)}`);
|
|
3339
|
+
}
|
|
3340
|
+
async accept(token) {
|
|
3341
|
+
return this._r("POST", `/api/im/invites/${encodeURIComponent(token)}/accept`);
|
|
3342
|
+
}
|
|
3343
|
+
async reject(token) {
|
|
3344
|
+
return this._r("POST", `/api/im/invites/${encodeURIComponent(token)}/reject`);
|
|
3345
|
+
}
|
|
3346
|
+
};
|
|
3347
|
+
var ProjectMembersClient = class {
|
|
3348
|
+
constructor(_r) {
|
|
3349
|
+
this._r = _r;
|
|
3350
|
+
}
|
|
3351
|
+
async list(projectId) {
|
|
3352
|
+
return this._r("GET", `/api/im/projects/${projectId}/members`);
|
|
3353
|
+
}
|
|
3354
|
+
async add(projectId, options) {
|
|
3355
|
+
return this._r("POST", `/api/im/projects/${projectId}/members`, options);
|
|
3356
|
+
}
|
|
3357
|
+
async update(projectId, membershipId, options) {
|
|
3358
|
+
return this._r("PATCH", `/api/im/projects/${projectId}/members/${membershipId}`, options);
|
|
3359
|
+
}
|
|
3360
|
+
async remove(projectId, membershipId) {
|
|
3361
|
+
return this._r("DELETE", `/api/im/projects/${projectId}/members/${membershipId}`);
|
|
3362
|
+
}
|
|
3363
|
+
};
|
|
3364
|
+
var ProjectsClient = class {
|
|
3365
|
+
constructor(_r) {
|
|
3366
|
+
this._r = _r;
|
|
3367
|
+
this.members = new ProjectMembersClient(_r);
|
|
3368
|
+
}
|
|
3369
|
+
async list(options) {
|
|
3370
|
+
const query = { workspaceId: options.workspaceId };
|
|
3371
|
+
if (options.status) query.status = options.status;
|
|
3372
|
+
if (options.search) query.search = options.search;
|
|
3373
|
+
if (options.limit !== void 0) query.limit = String(options.limit);
|
|
3374
|
+
if (options.offset !== void 0) query.offset = String(options.offset);
|
|
3375
|
+
return this._r("GET", "/api/im/projects", void 0, query);
|
|
3376
|
+
}
|
|
3377
|
+
async create(options) {
|
|
3378
|
+
return this._r("POST", "/api/im/projects", options);
|
|
3379
|
+
}
|
|
3380
|
+
async get(projectId) {
|
|
3381
|
+
return this._r("GET", `/api/im/projects/${projectId}`);
|
|
3382
|
+
}
|
|
3383
|
+
async update(projectId, options) {
|
|
3384
|
+
return this._r("PATCH", `/api/im/projects/${projectId}`, options);
|
|
3385
|
+
}
|
|
3386
|
+
async delete(projectId, options) {
|
|
3387
|
+
const query = {};
|
|
3388
|
+
if (options?.cascade) query.cascade = options.cascade;
|
|
3389
|
+
return this._r("DELETE", `/api/im/projects/${projectId}`, void 0, query);
|
|
3390
|
+
}
|
|
3100
3391
|
};
|
|
3101
3392
|
var WorkspaceFilesClient = class {
|
|
3102
3393
|
constructor(_r) {
|
|
@@ -3116,8 +3407,8 @@ var WorkspaceFilesClient = class {
|
|
|
3116
3407
|
return this._r("POST", `/api/im/workspaces/${workspaceId}/files`, options);
|
|
3117
3408
|
}
|
|
3118
3409
|
/** Soft-delete the active binding at `path`. */
|
|
3119
|
-
async delete(workspaceId,
|
|
3120
|
-
return this._r("DELETE", `/api/im/workspaces/${workspaceId}/files`, void 0, { path:
|
|
3410
|
+
async delete(workspaceId, path6) {
|
|
3411
|
+
return this._r("DELETE", `/api/im/workspaces/${workspaceId}/files`, void 0, { path: path6 });
|
|
3121
3412
|
}
|
|
3122
3413
|
/** Daemon delta-sync workspace files since an ISO timestamp. */
|
|
3123
3414
|
async sync(workspaceId, since) {
|
|
@@ -3142,8 +3433,8 @@ async function sha256BytesHex(bytes) {
|
|
|
3142
3433
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", ab);
|
|
3143
3434
|
return toHex(new Uint8Array(digest));
|
|
3144
3435
|
}
|
|
3145
|
-
const { createHash } = await import("crypto");
|
|
3146
|
-
return
|
|
3436
|
+
const { createHash: createHash4 } = await import("crypto");
|
|
3437
|
+
return createHash4("sha256").update(bytes).digest("hex");
|
|
3147
3438
|
}
|
|
3148
3439
|
function bytesToBlob(bytes, mimeType) {
|
|
3149
3440
|
const ab = new ArrayBuffer(bytes.byteLength);
|
|
@@ -3182,11 +3473,11 @@ async function normalizeAssetUploadInput(input, options) {
|
|
|
3182
3473
|
let bytes;
|
|
3183
3474
|
let fileName;
|
|
3184
3475
|
if (typeof input === "string") {
|
|
3185
|
-
const
|
|
3186
|
-
const
|
|
3187
|
-
const buf = await
|
|
3476
|
+
const fs8 = await import("fs");
|
|
3477
|
+
const path6 = await import("path");
|
|
3478
|
+
const buf = await fs8.promises.readFile(input);
|
|
3188
3479
|
bytes = new Uint8Array(buf);
|
|
3189
|
-
fileName = options.fileName ||
|
|
3480
|
+
fileName = options.fileName || path6.basename(input);
|
|
3190
3481
|
} else if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
3191
3482
|
const ab = await input.arrayBuffer();
|
|
3192
3483
|
bytes = new Uint8Array(ab);
|
|
@@ -3331,8 +3622,8 @@ var AssetsClient = class {
|
|
|
3331
3622
|
return null;
|
|
3332
3623
|
}
|
|
3333
3624
|
}
|
|
3334
|
-
async _postAssetJson(
|
|
3335
|
-
const response = await this._fetchFn(`${this._baseUrl}/api/im/assets${
|
|
3625
|
+
async _postAssetJson(path6, body) {
|
|
3626
|
+
const response = await this._fetchFn(`${this._baseUrl}/api/im/assets${path6}`, {
|
|
3336
3627
|
method: "POST",
|
|
3337
3628
|
headers: {
|
|
3338
3629
|
...this._getAuthHeaders(),
|
|
@@ -3404,10 +3695,18 @@ var RuntimeInstallationsClient = class {
|
|
|
3404
3695
|
constructor(_r) {
|
|
3405
3696
|
this._r = _r;
|
|
3406
3697
|
}
|
|
3407
|
-
/**
|
|
3698
|
+
/**
|
|
3699
|
+
* List runtime installations in a workspace.
|
|
3700
|
+
*
|
|
3701
|
+
* v2.0.8 M448 (release201/20 §1) — `projectId` narrows scope:
|
|
3702
|
+
* - omitted | `'all'` → no project filter (legacy behaviour)
|
|
3703
|
+
* - `'__unscoped'` → only workspace-level rows (projectId IS NULL)
|
|
3704
|
+
* - any other string → exact projectId match
|
|
3705
|
+
*/
|
|
3408
3706
|
async list(workspaceId, options) {
|
|
3409
3707
|
const query = { workspaceId };
|
|
3410
3708
|
if (options?.limit != null) query.limit = String(options.limit);
|
|
3709
|
+
if (options?.projectId) query.projectId = options.projectId;
|
|
3411
3710
|
return this._r("GET", "/api/workspace/runtime-installations", void 0, query);
|
|
3412
3711
|
}
|
|
3413
3712
|
/**
|
|
@@ -3416,10 +3715,27 @@ var RuntimeInstallationsClient = class {
|
|
|
3416
3715
|
* The daemon receives `PRISMER_API_KEY`, `PRISMER_DAEMON_ID`,
|
|
3417
3716
|
* `PRISMER_BASE_URL`, `PRISMER_WORKSPACE_ID`, and
|
|
3418
3717
|
* `PRISMER_RUNTIME_KIND=workspace-daemon` env vars.
|
|
3718
|
+
*
|
|
3719
|
+
* v2.0.8 M448 — `options.projectId` (optional) opts into project scope.
|
|
3720
|
+
* Server validates it belongs to the same workspace and is `active`,
|
|
3721
|
+
* otherwise rejects 422 `PROJECT_WORKSPACE_MISMATCH` / `PROJECT_NOT_ACTIVE`.
|
|
3419
3722
|
*/
|
|
3420
3723
|
async create(options) {
|
|
3421
3724
|
return this._r("POST", "/api/workspace/runtime-installations", options);
|
|
3422
3725
|
}
|
|
3726
|
+
/**
|
|
3727
|
+
* v2.0.8 M448 (release201/20 §1) — update the project binding on an
|
|
3728
|
+
* existing runtime installation. Pass `projectId: null` to detach.
|
|
3729
|
+
* No-op when body omits `projectId`. Same workspace/active invariant as
|
|
3730
|
+
* create — returns 422 on mismatch.
|
|
3731
|
+
*/
|
|
3732
|
+
async patch(runtimeInstallationId, options) {
|
|
3733
|
+
return this._r(
|
|
3734
|
+
"PATCH",
|
|
3735
|
+
`/api/workspace/runtime-installations/${runtimeInstallationId}`,
|
|
3736
|
+
options
|
|
3737
|
+
);
|
|
3738
|
+
}
|
|
3423
3739
|
/**
|
|
3424
3740
|
* Install an agent onto a runtime daemon. Resolves or creates the agent
|
|
3425
3741
|
* profile, calls the controller's `installAgent` RPC, and stamps
|
|
@@ -3516,11 +3832,11 @@ var FilesClient = class {
|
|
|
3516
3832
|
let bytes;
|
|
3517
3833
|
let fileName;
|
|
3518
3834
|
if (typeof input === "string") {
|
|
3519
|
-
const
|
|
3520
|
-
const
|
|
3521
|
-
const buf = await
|
|
3835
|
+
const fs8 = await import("fs");
|
|
3836
|
+
const path6 = await import("path");
|
|
3837
|
+
const buf = await fs8.promises.readFile(input);
|
|
3522
3838
|
bytes = new Uint8Array(buf);
|
|
3523
|
-
fileName = opts?.fileName ||
|
|
3839
|
+
fileName = opts?.fileName || path6.basename(input);
|
|
3524
3840
|
} else if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
3525
3841
|
const ab = await input.arrayBuffer();
|
|
3526
3842
|
bytes = new Uint8Array(ab);
|
|
@@ -3760,6 +4076,36 @@ var IMRealtimeClient = class {
|
|
|
3760
4076
|
};
|
|
3761
4077
|
}
|
|
3762
4078
|
};
|
|
4079
|
+
var SkillsDraftClient = class {
|
|
4080
|
+
constructor(_r) {
|
|
4081
|
+
this._r = _r;
|
|
4082
|
+
}
|
|
4083
|
+
/** Create a draft skill from a manifest v1 payload. */
|
|
4084
|
+
async create(input) {
|
|
4085
|
+
return this._r("POST", "/api/im/skills/draft", input);
|
|
4086
|
+
}
|
|
4087
|
+
/** Apply incremental file ops to a draft. */
|
|
4088
|
+
async patch(id, input) {
|
|
4089
|
+
return this._r("PATCH", `/api/im/skills/${id}/draft`, input);
|
|
4090
|
+
}
|
|
4091
|
+
/** Request a regenerate session for a draft. */
|
|
4092
|
+
async regenerate(id, input) {
|
|
4093
|
+
return this._r("POST", `/api/im/skills/${id}/draft/regenerate`, input);
|
|
4094
|
+
}
|
|
4095
|
+
/** Fetch a single draft's manifest + revision history. */
|
|
4096
|
+
async show(id) {
|
|
4097
|
+
return this._r("GET", `/api/im/skills/draft/${id}`);
|
|
4098
|
+
}
|
|
4099
|
+
/** List drafts for a workspace (most-recently-updated first). */
|
|
4100
|
+
async list(workspaceId) {
|
|
4101
|
+
return this._r("GET", "/api/im/skills/drafts", void 0, { workspaceId });
|
|
4102
|
+
}
|
|
4103
|
+
};
|
|
4104
|
+
var SkillsClient = class {
|
|
4105
|
+
constructor(_r) {
|
|
4106
|
+
this.draft = new SkillsDraftClient(_r);
|
|
4107
|
+
}
|
|
4108
|
+
};
|
|
3763
4109
|
var IMClient = class {
|
|
3764
4110
|
constructor(request2, wsBase, fetchFn, getAuthHeaders, offlineManager, communityHubConfig) {
|
|
3765
4111
|
this._request = request2;
|
|
@@ -3773,16 +4119,22 @@ var IMClient = class {
|
|
|
3773
4119
|
this.credits = new CreditsClient(request2);
|
|
3774
4120
|
this.workspace = new WorkspaceClient(request2);
|
|
3775
4121
|
this.workspaces = new WorkspacesClient(request2);
|
|
4122
|
+
this.invites = new InvitesClient(request2);
|
|
4123
|
+
this.projects = new ProjectsClient(request2);
|
|
3776
4124
|
this.workspaceFiles = new WorkspaceFilesClient(request2);
|
|
3777
4125
|
this.assets = new AssetsClient(request2, wsBase, fetchFn, getAuthHeaders);
|
|
3778
4126
|
this.runtimeInstallations = new RuntimeInstallationsClient(request2);
|
|
3779
4127
|
this.tasks = new TasksClient(request2);
|
|
4128
|
+
this.criteriaTemplates = new CriteriaTemplatesClient(request2);
|
|
3780
4129
|
this.memory = new MemoryClient(request2);
|
|
3781
4130
|
this.knowledge = new KnowledgeLinkClient(request2);
|
|
4131
|
+
this.metrics = new MetricsClient(request2);
|
|
3782
4132
|
this.identity = new IdentityClient(request2);
|
|
3783
4133
|
this.security = new SecurityClient(request2);
|
|
3784
4134
|
this.agents = new AgentsClient(request2);
|
|
3785
4135
|
this.evolution = new EvolutionClient(request2);
|
|
4136
|
+
this.skills = new SkillsClient(request2);
|
|
4137
|
+
this.studio = new StudioClient(request2);
|
|
3786
4138
|
this.community = new CommunityHub(request2, communityHubConfig ?? void 0);
|
|
3787
4139
|
this.files = new FilesClient(request2, wsBase, fetchFn, getAuthHeaders);
|
|
3788
4140
|
this.realtime = new IMRealtimeClient(wsBase, fetchFn);
|
|
@@ -3812,8 +4164,8 @@ var IMClient = class {
|
|
|
3812
4164
|
* 'POST', '/api/im/approvals', { category, title, context, options },
|
|
3813
4165
|
* );
|
|
3814
4166
|
*/
|
|
3815
|
-
async request(method,
|
|
3816
|
-
return this._request(method,
|
|
4167
|
+
async request(method, path6, body, query) {
|
|
4168
|
+
return this._request(method, path6, body, query);
|
|
3817
4169
|
}
|
|
3818
4170
|
};
|
|
3819
4171
|
var PrismerClient = class {
|
|
@@ -3865,20 +4217,20 @@ var PrismerClient = class {
|
|
|
3865
4217
|
) : (m, p, b, q, opts) => this._request(m, p, b, q, opts);
|
|
3866
4218
|
if (config.identity) {
|
|
3867
4219
|
const baseRequest = imRequest;
|
|
3868
|
-
imRequest = (method,
|
|
3869
|
-
if (method === "POST" &&
|
|
4220
|
+
imRequest = (method, path6, body, query, opts) => {
|
|
4221
|
+
if (method === "POST" && path6.includes("/messages") && body) {
|
|
3870
4222
|
const b = body;
|
|
3871
4223
|
if (!b.signature && !b.skipSigning) {
|
|
3872
4224
|
const ready = this._identityReady || Promise.resolve();
|
|
3873
4225
|
return ready.then(() => {
|
|
3874
4226
|
if (this._identity) {
|
|
3875
|
-
return this._signAndSend(baseRequest, method,
|
|
4227
|
+
return this._signAndSend(baseRequest, method, path6, b, query, opts);
|
|
3876
4228
|
}
|
|
3877
|
-
return baseRequest(method,
|
|
4229
|
+
return baseRequest(method, path6, body, query, opts);
|
|
3878
4230
|
});
|
|
3879
4231
|
}
|
|
3880
4232
|
}
|
|
3881
|
-
return baseRequest(method,
|
|
4233
|
+
return baseRequest(method, path6, body, query, opts);
|
|
3882
4234
|
};
|
|
3883
4235
|
}
|
|
3884
4236
|
this.im = new IMClient(
|
|
@@ -3890,6 +4242,8 @@ var PrismerClient = class {
|
|
|
3890
4242
|
config.community ?? null
|
|
3891
4243
|
);
|
|
3892
4244
|
this.workspaces = this.im.workspaces;
|
|
4245
|
+
this.invites = this.im.invites;
|
|
4246
|
+
this.projects = this.im.projects;
|
|
3893
4247
|
this.workspaceFiles = this.im.workspaceFiles;
|
|
3894
4248
|
this.assets = this.im.assets;
|
|
3895
4249
|
this.evolution = this.im.evolution;
|
|
@@ -3900,9 +4254,9 @@ var PrismerClient = class {
|
|
|
3900
4254
|
return this._identity;
|
|
3901
4255
|
}
|
|
3902
4256
|
/** Auto-sign a message body and send (v1.8.0 S1) */
|
|
3903
|
-
async _signAndSend(baseRequest, method,
|
|
4257
|
+
async _signAndSend(baseRequest, method, path6, body, query, opts) {
|
|
3904
4258
|
if (this._identityReady) await this._identityReady;
|
|
3905
|
-
if (!this._identity) return baseRequest(method,
|
|
4259
|
+
if (!this._identity) return baseRequest(method, path6, body, query, opts);
|
|
3906
4260
|
const content = body.content || "";
|
|
3907
4261
|
const contentHashBytes = new Uint8Array(
|
|
3908
4262
|
await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content))
|
|
@@ -3912,7 +4266,7 @@ var PrismerClient = class {
|
|
|
3912
4266
|
const payload = `1|${this._identity.did}|${body.type || "text"}|${timestamp}|${contentHash}`;
|
|
3913
4267
|
const payloadBytes = new TextEncoder().encode(payload);
|
|
3914
4268
|
const signature = await this._identity.sign(payloadBytes);
|
|
3915
|
-
return baseRequest(method,
|
|
4269
|
+
return baseRequest(method, path6, {
|
|
3916
4270
|
...body,
|
|
3917
4271
|
secVersion: 1,
|
|
3918
4272
|
senderDid: this._identity.did,
|
|
@@ -3977,11 +4331,11 @@ var PrismerClient = class {
|
|
|
3977
4331
|
// --------------------------------------------------------------------------
|
|
3978
4332
|
// Internal request helper
|
|
3979
4333
|
// --------------------------------------------------------------------------
|
|
3980
|
-
async _request(method,
|
|
4334
|
+
async _request(method, path6, body, query, opts, _isRetry) {
|
|
3981
4335
|
const controller = new AbortController();
|
|
3982
4336
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
3983
4337
|
try {
|
|
3984
|
-
let url = `${this.baseUrl}${
|
|
4338
|
+
let url = `${this.baseUrl}${path6}`;
|
|
3985
4339
|
if (query && Object.keys(query).length > 0) {
|
|
3986
4340
|
url += "?" + new URLSearchParams(query).toString();
|
|
3987
4341
|
}
|
|
@@ -4002,12 +4356,12 @@ var PrismerClient = class {
|
|
|
4002
4356
|
}
|
|
4003
4357
|
const response = await this.fetchFn(url, init);
|
|
4004
4358
|
const data = await response.json();
|
|
4005
|
-
if (response.status === 401 && this.apiKey.startsWith("eyJ") && !_isRetry && !
|
|
4359
|
+
if (response.status === 401 && this.apiKey.startsWith("eyJ") && !_isRetry && !path6.includes("/token/refresh")) {
|
|
4006
4360
|
try {
|
|
4007
4361
|
const refreshRes = await this._request("POST", "/api/im/token/refresh", void 0, void 0, void 0, true);
|
|
4008
4362
|
if (refreshRes?.ok && refreshRes?.data?.token) {
|
|
4009
4363
|
this.apiKey = refreshRes.data.token;
|
|
4010
|
-
return this._request(method,
|
|
4364
|
+
return this._request(method, path6, body, query, opts, true);
|
|
4011
4365
|
}
|
|
4012
4366
|
} catch {
|
|
4013
4367
|
}
|
|
@@ -5360,7 +5714,7 @@ function register3(parent, getIMClient2, _getAPIClient) {
|
|
|
5360
5714
|
const maxIterations = 30;
|
|
5361
5715
|
let lastStatus;
|
|
5362
5716
|
for (let i = 0; i < maxIterations; i++) {
|
|
5363
|
-
await new Promise((
|
|
5717
|
+
await new Promise((resolve5) => setTimeout(resolve5, 2e3));
|
|
5364
5718
|
if (!opts.json) process.stdout.write(".");
|
|
5365
5719
|
const statusRes = await client.im.evolution.getReportStatus(traceId);
|
|
5366
5720
|
if (!statusRes.ok) break;
|
|
@@ -5743,6 +6097,9 @@ function register3(parent, getIMClient2, _getAPIClient) {
|
|
|
5743
6097
|
}
|
|
5744
6098
|
|
|
5745
6099
|
// src/commands/task.ts
|
|
6100
|
+
var import_node_fs = require("fs");
|
|
6101
|
+
var import_node_path = require("path");
|
|
6102
|
+
var import_node_crypto = require("crypto");
|
|
5746
6103
|
var TASK_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned", "running", "review", "completed", "failed", "cancelled"]);
|
|
5747
6104
|
var TASK_PRIORITIES = /* @__PURE__ */ new Set(["low", "medium", "high", "urgent"]);
|
|
5748
6105
|
var TASK_KINDS = /* @__PURE__ */ new Set(["work_item", "goal"]);
|
|
@@ -5751,6 +6108,67 @@ function parseTaskStatus(raw) {
|
|
|
5751
6108
|
if (TASK_STATUSES.has(raw)) return raw;
|
|
5752
6109
|
throw new Error(`Invalid task status "${raw}".`);
|
|
5753
6110
|
}
|
|
6111
|
+
function normalizeProjectForCreate(raw) {
|
|
6112
|
+
const value = raw ?? process.env.PRISMER_ACTIVE_PROJECT_ID;
|
|
6113
|
+
if (!value) return void 0;
|
|
6114
|
+
const trimmed = value.trim();
|
|
6115
|
+
if (!trimmed || trimmed === "all") return void 0;
|
|
6116
|
+
if (trimmed === "__unscoped" || trimmed === "_unscoped" || trimmed === "none" || trimmed === "null") return null;
|
|
6117
|
+
return trimmed;
|
|
6118
|
+
}
|
|
6119
|
+
function normalizeProjectForList(raw) {
|
|
6120
|
+
const value = raw ?? process.env.PRISMER_ACTIVE_PROJECT_ID;
|
|
6121
|
+
if (!value) return void 0;
|
|
6122
|
+
const trimmed = value.trim();
|
|
6123
|
+
if (!trimmed) return void 0;
|
|
6124
|
+
if (trimmed === "_unscoped" || trimmed === "none" || trimmed === "null") return "__unscoped";
|
|
6125
|
+
return trimmed;
|
|
6126
|
+
}
|
|
6127
|
+
async function runReviewCheckpoint(taskId, toStatus) {
|
|
6128
|
+
const port = process.env.PRISMER_DAEMON_PORT ?? "3210";
|
|
6129
|
+
const url = `http://127.0.0.1:${port}/v1/checkpoints/pre_status_change`;
|
|
6130
|
+
let response;
|
|
6131
|
+
try {
|
|
6132
|
+
response = await fetch(url, {
|
|
6133
|
+
method: "POST",
|
|
6134
|
+
headers: { "Content-Type": "application/json" },
|
|
6135
|
+
body: JSON.stringify({ taskId, toStatus })
|
|
6136
|
+
});
|
|
6137
|
+
} catch (err) {
|
|
6138
|
+
return {
|
|
6139
|
+
ok: false,
|
|
6140
|
+
indeterminate: true,
|
|
6141
|
+
message: `daemon unreachable at ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
6142
|
+
};
|
|
6143
|
+
}
|
|
6144
|
+
let body;
|
|
6145
|
+
try {
|
|
6146
|
+
body = await response.json();
|
|
6147
|
+
} catch {
|
|
6148
|
+
body = {};
|
|
6149
|
+
}
|
|
6150
|
+
if (response.status === 200 && body.ok === true) {
|
|
6151
|
+
return { ok: true };
|
|
6152
|
+
}
|
|
6153
|
+
if (response.status === 409 && Array.isArray(body.pendingFiles)) {
|
|
6154
|
+
return {
|
|
6155
|
+
ok: false,
|
|
6156
|
+
pendingFiles: body.pendingFiles,
|
|
6157
|
+
message: body.error?.message ?? "pending_attach"
|
|
6158
|
+
};
|
|
6159
|
+
}
|
|
6160
|
+
if (response.status === 503) {
|
|
6161
|
+
return {
|
|
6162
|
+
ok: false,
|
|
6163
|
+
indeterminate: true,
|
|
6164
|
+
message: body.error?.message ?? `daemon returned 503`
|
|
6165
|
+
};
|
|
6166
|
+
}
|
|
6167
|
+
return {
|
|
6168
|
+
ok: false,
|
|
6169
|
+
message: body.error?.message ?? `daemon returned ${response.status}`
|
|
6170
|
+
};
|
|
6171
|
+
}
|
|
5754
6172
|
async function resolveAssigneeId(client, name) {
|
|
5755
6173
|
const needle = name.trim().toLowerCase();
|
|
5756
6174
|
const normalized = needle.replace(/^@/, "");
|
|
@@ -5773,7 +6191,7 @@ async function resolveAssigneeId(client, name) {
|
|
|
5773
6191
|
}
|
|
5774
6192
|
function register4(parent, getIMClient2, _getAPIClient) {
|
|
5775
6193
|
const task = parent.command("task").description("Manage tasks in the task marketplace");
|
|
5776
|
-
task.command("create").description("Create a new task").requiredOption("--title <title>", "task title").option("--description <description>", "task description").option("--capability <capability>", "required agent capability").option("--budget <budget>", "budget in credits", parseFloat).option("--reward <reward>", "alias for --budget (credits offered for completion)", parseFloat).option("--priority <priority>", "task priority: low | medium | high | urgent").option("--assignee-id <imUserId>", "directly assign to a specific agent by IM user id").option("--assignee-name <name>", "assign by @username / display name (resolved via discover)").option("--conversation-id <id>", "pin task to a conversation/session").option("--kind <kind>", "board projection kind: work_item (default) | goal", "work_item").option("--schedule-at <iso>", "one-shot ISO 8601 scheduled time (sets scheduleType=once)").option("--schedule-cron <expr>", "cron expression (sets scheduleType=cron)").option("--json", "output raw JSON response").action(async (opts) => {
|
|
6194
|
+
task.command("create").description("Create a new task").requiredOption("--title <title>", "task title").option("--description <description>", "task description").option("--capability <capability>", "required agent capability").option("--budget <budget>", "budget in credits", parseFloat).option("--reward <reward>", "alias for --budget (credits offered for completion)", parseFloat).option("--priority <priority>", "task priority: low | medium | high | urgent").option("--assignee-id <imUserId>", "directly assign to a specific agent by IM user id").option("--assignee-name <name>", "assign by @username / display name (resolved via discover)").option("--conversation-id <id>", "pin task to a conversation/session").option("--project <id>", "scope task to project id; use __unscoped/none for workspace-level").option("--kind <kind>", "board projection kind: work_item (default) | goal", "work_item").option("--schedule-at <iso>", "one-shot ISO 8601 scheduled time (sets scheduleType=once)").option("--schedule-cron <expr>", "cron expression (sets scheduleType=cron)").option("--json", "output raw JSON response").action(async (opts) => {
|
|
5777
6195
|
const client = getIMClient2();
|
|
5778
6196
|
try {
|
|
5779
6197
|
if (opts.priority && !TASK_PRIORITIES.has(opts.priority)) {
|
|
@@ -5817,6 +6235,8 @@ function register4(parent, getIMClient2, _getAPIClient) {
|
|
|
5817
6235
|
};
|
|
5818
6236
|
if (assigneeId) createOpts.assigneeId = assigneeId;
|
|
5819
6237
|
if (opts.conversationId) createOpts.conversationId = opts.conversationId;
|
|
6238
|
+
const projectId = normalizeProjectForCreate(opts.project);
|
|
6239
|
+
if (projectId !== void 0) createOpts.projectId = projectId;
|
|
5820
6240
|
if (opts.scheduleAt) {
|
|
5821
6241
|
createOpts.scheduleType = "once";
|
|
5822
6242
|
createOpts.scheduleAt = opts.scheduleAt;
|
|
@@ -5858,12 +6278,13 @@ function register4(parent, getIMClient2, _getAPIClient) {
|
|
|
5858
6278
|
process.exit(1);
|
|
5859
6279
|
}
|
|
5860
6280
|
});
|
|
5861
|
-
task.command("list").description("List tasks").option("--status <status>", "filter by status").option("--capability <capability>", "filter by required capability").option("-n, --limit <n>", "maximum number of tasks to return", "20").option("--json", "output raw JSON response").action(async (opts) => {
|
|
6281
|
+
task.command("list").description("List tasks").option("--status <status>", "filter by status").option("--capability <capability>", "filter by required capability").option("--project <id>", "filter by project id; use all or __unscoped").option("-n, --limit <n>", "maximum number of tasks to return", "20").option("--json", "output raw JSON response").action(async (opts) => {
|
|
5862
6282
|
const client = getIMClient2();
|
|
5863
6283
|
try {
|
|
5864
6284
|
const res = await client.im.tasks.list({
|
|
5865
6285
|
status: parseTaskStatus(opts.status),
|
|
5866
6286
|
capability: opts.capability,
|
|
6287
|
+
projectId: normalizeProjectForList(opts.project),
|
|
5867
6288
|
limit: parseInt(opts.limit, 10)
|
|
5868
6289
|
});
|
|
5869
6290
|
if (opts.json) {
|
|
@@ -5884,9 +6305,9 @@ function register4(parent, getIMClient2, _getAPIClient) {
|
|
|
5884
6305
|
const statusW = 12;
|
|
5885
6306
|
const titleW = 40;
|
|
5886
6307
|
const header = "ID".padEnd(idW) + "STATUS".padEnd(statusW) + "TITLE";
|
|
5887
|
-
const
|
|
6308
|
+
const sep3 = "-".repeat(idW + statusW + titleW);
|
|
5888
6309
|
process.stdout.write(header + "\n");
|
|
5889
|
-
process.stdout.write(
|
|
6310
|
+
process.stdout.write(sep3 + "\n");
|
|
5890
6311
|
for (const t of tasks) {
|
|
5891
6312
|
const title = t.title.length > titleW ? t.title.slice(0, titleW - 3) + "..." : t.title;
|
|
5892
6313
|
process.stdout.write(
|
|
@@ -5895,6 +6316,32 @@ function register4(parent, getIMClient2, _getAPIClient) {
|
|
|
5895
6316
|
}
|
|
5896
6317
|
process.stdout.write(`
|
|
5897
6318
|
${tasks.length} task(s) listed.
|
|
6319
|
+
`);
|
|
6320
|
+
} catch (err) {
|
|
6321
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6322
|
+
process.stderr.write(`Error: ${message}
|
|
6323
|
+
`);
|
|
6324
|
+
process.exit(1);
|
|
6325
|
+
}
|
|
6326
|
+
});
|
|
6327
|
+
task.command("move-project <task-id> [project-id]").description("Move a task to a project, or use --unscoped to return it to workspace-level").option("--unscoped", "set projectId to null", false).option("--json", "output raw JSON response").action(async (taskId, projectId, opts) => {
|
|
6328
|
+
const client = getIMClient2();
|
|
6329
|
+
try {
|
|
6330
|
+
if (!opts.unscoped && !projectId) {
|
|
6331
|
+
throw new Error("Provide <project-id> or pass --unscoped.");
|
|
6332
|
+
}
|
|
6333
|
+
const targetProjectId = opts.unscoped ? null : projectId.trim();
|
|
6334
|
+
const res = await client.im.tasks.moveProject(taskId, targetProjectId);
|
|
6335
|
+
if (opts.json) {
|
|
6336
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6337
|
+
return;
|
|
6338
|
+
}
|
|
6339
|
+
if (!res.ok) {
|
|
6340
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6341
|
+
`);
|
|
6342
|
+
process.exit(1);
|
|
6343
|
+
}
|
|
6344
|
+
process.stdout.write(`Task ${taskId} project: ${targetProjectId ?? "(workspace-level)"}
|
|
5898
6345
|
`);
|
|
5899
6346
|
} catch (err) {
|
|
5900
6347
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -5997,9 +6444,37 @@ Logs (${logs.length}):
|
|
|
5997
6444
|
process.exit(1);
|
|
5998
6445
|
}
|
|
5999
6446
|
});
|
|
6000
|
-
task.command("update <task-id>").description("Update a task").option("--title <title>", "new title").option("--description <description>", "new description").option("--status <status>", "new status").option("--progress <progress>", "progress (0.0 to 1.0)", parseFloat).option("--status-message <statusMessage>", "status message").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6447
|
+
task.command("update <task-id>").description("Update a task").option("--title <title>", "new title").option("--description <description>", "new description").option("--status <status>", "new status").option("--progress <progress>", "progress (0.0 to 1.0)", parseFloat).option("--status-message <statusMessage>", "status message").option("--skip-checkpoint", "bypass local daemon checkpoint when transitioning to review (release201/09 \xA79.4a.7)", false).option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6001
6448
|
const client = getIMClient2();
|
|
6002
6449
|
try {
|
|
6450
|
+
if (opts.status === "review" && !opts.skipCheckpoint) {
|
|
6451
|
+
const checkpoint = await runReviewCheckpoint(taskId, opts.status);
|
|
6452
|
+
if (!checkpoint.ok) {
|
|
6453
|
+
if (checkpoint.pendingFiles && checkpoint.pendingFiles.length > 0) {
|
|
6454
|
+
process.stderr.write(
|
|
6455
|
+
`[checkpoint] result/ \u542B ${checkpoint.pendingFiles.length} \u4E2A\u672A attach \u6587\u4EF6:
|
|
6456
|
+
`
|
|
6457
|
+
);
|
|
6458
|
+
for (const f of checkpoint.pendingFiles) {
|
|
6459
|
+
const sizeKb = (f.sizeBytes / 1024).toFixed(1);
|
|
6460
|
+
process.stderr.write(` - ${f.path} (sha256=${f.sha256.slice(0, 12)}\u2026, ${sizeKb}KB)
|
|
6461
|
+
`);
|
|
6462
|
+
}
|
|
6463
|
+
process.stderr.write(
|
|
6464
|
+
"\u8BF7 attach \u5B83\u4EEC\u518D retry status change, \u6216\u52A0 --skip-checkpoint \u663E\u5F0F\u5FFD\u7565\n"
|
|
6465
|
+
);
|
|
6466
|
+
} else if (checkpoint.indeterminate) {
|
|
6467
|
+
process.stderr.write(
|
|
6468
|
+
`[checkpoint] daemon unreachable or cloud lookup failed (${checkpoint.message}); pass --skip-checkpoint to bypass
|
|
6469
|
+
`
|
|
6470
|
+
);
|
|
6471
|
+
} else {
|
|
6472
|
+
process.stderr.write(`[checkpoint] failed: ${checkpoint.message ?? "unknown"}
|
|
6473
|
+
`);
|
|
6474
|
+
}
|
|
6475
|
+
process.exit(12);
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6003
6478
|
const res = await client.im.tasks.update(taskId, {
|
|
6004
6479
|
title: opts.title,
|
|
6005
6480
|
description: opts.description,
|
|
@@ -6159,6 +6634,83 @@ Logs (${logs.length}):
|
|
|
6159
6634
|
} catch (err) {
|
|
6160
6635
|
const message = err instanceof Error ? err.message : String(err);
|
|
6161
6636
|
process.stderr.write(`Error: ${message}
|
|
6637
|
+
`);
|
|
6638
|
+
process.exit(1);
|
|
6639
|
+
}
|
|
6640
|
+
});
|
|
6641
|
+
task.command("attach <path>").description("Attach a file to a task as a user-deliverable (release201/09 \xA79.4a.5)").option("--task <taskId>", "target task id; defaults to PRISMER_TASK_ID env").option("--name <displayName>", "override the display filename; defaults to basename(path)").option("--json", "output raw JSON response").action(async (filePath, opts) => {
|
|
6642
|
+
const client = getIMClient2();
|
|
6643
|
+
try {
|
|
6644
|
+
const targetTaskId = opts.task ?? process.env.PRISMER_TASK_ID;
|
|
6645
|
+
if (!targetTaskId) {
|
|
6646
|
+
process.stderr.write("Error: --task is required (or set PRISMER_TASK_ID env)\n");
|
|
6647
|
+
process.exit(1);
|
|
6648
|
+
}
|
|
6649
|
+
const getRes = await client.im.tasks.get(targetTaskId);
|
|
6650
|
+
if (!getRes.ok || !getRes.data) {
|
|
6651
|
+
process.stderr.write(`Error: task ${targetTaskId} not found: ${getRes.error?.message ?? "unknown"}
|
|
6652
|
+
`);
|
|
6653
|
+
process.exit(1);
|
|
6654
|
+
}
|
|
6655
|
+
const wsId = getRes.data.task?.workspaceId;
|
|
6656
|
+
if (!wsId) {
|
|
6657
|
+
process.stderr.write(`Error: task ${targetTaskId} has no workspaceId; cannot upload
|
|
6658
|
+
`);
|
|
6659
|
+
process.exit(1);
|
|
6660
|
+
}
|
|
6661
|
+
let bytes;
|
|
6662
|
+
try {
|
|
6663
|
+
bytes = await import_node_fs.promises.readFile(filePath);
|
|
6664
|
+
} catch (err) {
|
|
6665
|
+
process.stderr.write(`Error: cannot read ${filePath}: ${err instanceof Error ? err.message : String(err)}
|
|
6666
|
+
`);
|
|
6667
|
+
process.exit(1);
|
|
6668
|
+
}
|
|
6669
|
+
const contentHash = (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex");
|
|
6670
|
+
const displayName = opts.name ?? (0, import_node_path.basename)(filePath);
|
|
6671
|
+
const uploadRes = await client.im.assets.upload(bytes, {
|
|
6672
|
+
workspaceId: wsId,
|
|
6673
|
+
sourceTaskId: targetTaskId,
|
|
6674
|
+
kind: "agent-output",
|
|
6675
|
+
fileName: displayName,
|
|
6676
|
+
metadata: {
|
|
6677
|
+
// boundKind stamped via metadata so service-side cloud code can
|
|
6678
|
+
// mirror onto the column. Service POST handler reads this and
|
|
6679
|
+
// populates IMAsset.boundKind='task-bound'.
|
|
6680
|
+
boundKind: "task-bound",
|
|
6681
|
+
filename: displayName,
|
|
6682
|
+
attachedBy: "cloud-task-attach"
|
|
6683
|
+
}
|
|
6684
|
+
});
|
|
6685
|
+
if (opts.json) {
|
|
6686
|
+
process.stdout.write(JSON.stringify(uploadRes, null, 2) + "\n");
|
|
6687
|
+
return;
|
|
6688
|
+
}
|
|
6689
|
+
if (!uploadRes.ok || !uploadRes.data) {
|
|
6690
|
+
process.stderr.write(`Error: attach failed: ${uploadRes.error?.message ?? "unknown"}
|
|
6691
|
+
`);
|
|
6692
|
+
process.exit(1);
|
|
6693
|
+
}
|
|
6694
|
+
const dedupHit = Boolean(uploadRes.meta?.dedup);
|
|
6695
|
+
const asset = uploadRes.data;
|
|
6696
|
+
process.stdout.write(`Attached ${displayName}
|
|
6697
|
+
|
|
6698
|
+
`);
|
|
6699
|
+
process.stdout.write(`AssetId: ${asset.id}
|
|
6700
|
+
`);
|
|
6701
|
+
process.stdout.write(`Task: ${targetTaskId}
|
|
6702
|
+
`);
|
|
6703
|
+
process.stdout.write(`ContentHash: ${contentHash}
|
|
6704
|
+
`);
|
|
6705
|
+
process.stdout.write(`SizeBytes: ${bytes.length}
|
|
6706
|
+
`);
|
|
6707
|
+
process.stdout.write(`Dedup: ${dedupHit ? "true (existing IMAsset row)" : "false (created)"}
|
|
6708
|
+
`);
|
|
6709
|
+
if (asset.cdnUrl) process.stdout.write(`CDN URL: ${asset.cdnUrl}
|
|
6710
|
+
`);
|
|
6711
|
+
} catch (err) {
|
|
6712
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6713
|
+
process.stderr.write(`Error: ${message}
|
|
6162
6714
|
`);
|
|
6163
6715
|
process.exit(1);
|
|
6164
6716
|
}
|
|
@@ -6193,26 +6745,18 @@ Logs (${logs.length}):
|
|
|
6193
6745
|
process.exit(1);
|
|
6194
6746
|
}
|
|
6195
6747
|
});
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
// src/commands/memory.ts
|
|
6199
|
-
var MEMORY_TYPES = /* @__PURE__ */ new Set(["user", "feedback", "project", "reference"]);
|
|
6200
|
-
function register5(parent, getIMClient2, _getAPIClient) {
|
|
6201
|
-
const mem = parent.command("memory").description("Agent memory file management");
|
|
6202
|
-
mem.command("write").description("Write a memory file").requiredOption("-s, --scope <scope>", "memory scope").requiredOption("-p, --path <path>", "file path within scope").requiredOption("-c, --content <content>", "file content").option("--type <type>", "memory type: user | feedback | project | reference").option("--description <description>", "human-readable description of this memory").option("--json", "output raw JSON response").action(async (opts) => {
|
|
6748
|
+
task.command("spec-set <task-id>").description("Owner writes / updates SPEC.md for a task").option("-f, --file <path>", "read SPEC.md content from a file").option("-m, --markdown <markdown>", "inline SPEC.md content").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6203
6749
|
const client = getIMClient2();
|
|
6204
6750
|
try {
|
|
6205
|
-
|
|
6206
|
-
|
|
6751
|
+
let md = "";
|
|
6752
|
+
if (opts.file) {
|
|
6753
|
+
md = await import_node_fs.promises.readFile(opts.file, "utf-8");
|
|
6754
|
+
} else if (opts.markdown) {
|
|
6755
|
+
md = opts.markdown;
|
|
6756
|
+
} else {
|
|
6757
|
+
throw new Error("one of --file or --markdown is required");
|
|
6207
6758
|
}
|
|
6208
|
-
const
|
|
6209
|
-
scope: opts.scope,
|
|
6210
|
-
path: opts.path,
|
|
6211
|
-
content: opts.content
|
|
6212
|
-
};
|
|
6213
|
-
if (opts.type) body.memoryType = opts.type;
|
|
6214
|
-
if (opts.description) body.description = opts.description;
|
|
6215
|
-
const res = await client.im.memory.createFile(body);
|
|
6759
|
+
const res = await client.im.tasks.spec.set(taskId, { markdown: md });
|
|
6216
6760
|
if (opts.json) {
|
|
6217
6761
|
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6218
6762
|
return;
|
|
@@ -6222,19 +6766,353 @@ function register5(parent, getIMClient2, _getAPIClient) {
|
|
|
6222
6766
|
`);
|
|
6223
6767
|
process.exit(1);
|
|
6224
6768
|
}
|
|
6225
|
-
const
|
|
6226
|
-
|
|
6227
|
-
process.stdout.write(`Memory file created
|
|
6228
|
-
`);
|
|
6229
|
-
process.stdout.write(` ID: ${file.id}
|
|
6230
|
-
`);
|
|
6231
|
-
process.stdout.write(` Scope: ${file.scope}
|
|
6232
|
-
`);
|
|
6233
|
-
process.stdout.write(` Path: ${file.path}
|
|
6769
|
+
const v = res.data;
|
|
6770
|
+
process.stdout.write(`SPEC.md saved (revision ${v.revision})
|
|
6234
6771
|
`);
|
|
6235
6772
|
} catch (err) {
|
|
6236
|
-
|
|
6237
|
-
|
|
6773
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6774
|
+
`);
|
|
6775
|
+
process.exit(1);
|
|
6776
|
+
}
|
|
6777
|
+
});
|
|
6778
|
+
task.command("spec-show <task-id>").description("Print SPEC.md content for a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6779
|
+
const client = getIMClient2();
|
|
6780
|
+
try {
|
|
6781
|
+
const res = await client.im.tasks.spec.get(taskId);
|
|
6782
|
+
if (opts.json) {
|
|
6783
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6784
|
+
return;
|
|
6785
|
+
}
|
|
6786
|
+
if (!res.ok) {
|
|
6787
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6788
|
+
`);
|
|
6789
|
+
process.exit(1);
|
|
6790
|
+
}
|
|
6791
|
+
const v = res.data;
|
|
6792
|
+
process.stdout.write(v.markdown);
|
|
6793
|
+
if (!v.markdown.endsWith("\n")) process.stdout.write("\n");
|
|
6794
|
+
process.stdout.write(`# revision ${v.revision}
|
|
6795
|
+
`);
|
|
6796
|
+
} catch (err) {
|
|
6797
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6798
|
+
`);
|
|
6799
|
+
process.exit(1);
|
|
6800
|
+
}
|
|
6801
|
+
});
|
|
6802
|
+
task.command("todo-add <task-id> <text...>").description("Append an item to the task TODO.md (assignee)").option("--depth <n>", "nesting depth (0..3)", (v) => parseInt(v, 10), 0).option("--json", "output raw JSON response").action(async (taskId, text, opts) => {
|
|
6803
|
+
const client = getIMClient2();
|
|
6804
|
+
try {
|
|
6805
|
+
const res = await client.im.tasks.todo.add(taskId, {
|
|
6806
|
+
text: text.join(" "),
|
|
6807
|
+
depth: opts.depth
|
|
6808
|
+
});
|
|
6809
|
+
if (opts.json) {
|
|
6810
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6811
|
+
return;
|
|
6812
|
+
}
|
|
6813
|
+
if (!res.ok) {
|
|
6814
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6815
|
+
`);
|
|
6816
|
+
process.exit(1);
|
|
6817
|
+
}
|
|
6818
|
+
const v = res.data;
|
|
6819
|
+
process.stdout.write(`TODO item added \u2014 ${v.doneCount}/${v.totalCount} (rev ${v.revision})
|
|
6820
|
+
`);
|
|
6821
|
+
} catch (err) {
|
|
6822
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6823
|
+
`);
|
|
6824
|
+
process.exit(1);
|
|
6825
|
+
}
|
|
6826
|
+
});
|
|
6827
|
+
task.command("todo-done <task-id> <index>").description("Mark a TODO item as done").option("--json", "output raw JSON response").action(async (taskId, indexRaw, opts) => {
|
|
6828
|
+
const client = getIMClient2();
|
|
6829
|
+
try {
|
|
6830
|
+
const idx = parseInt(indexRaw, 10);
|
|
6831
|
+
if (!Number.isFinite(idx) || idx < 0) throw new Error("index must be a non-negative integer");
|
|
6832
|
+
const res = await client.im.tasks.todo.toggle(taskId, idx, true);
|
|
6833
|
+
if (opts.json) {
|
|
6834
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6835
|
+
return;
|
|
6836
|
+
}
|
|
6837
|
+
if (!res.ok) {
|
|
6838
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6839
|
+
`);
|
|
6840
|
+
process.exit(1);
|
|
6841
|
+
}
|
|
6842
|
+
const v = res.data;
|
|
6843
|
+
process.stdout.write(
|
|
6844
|
+
`TODO[${idx}] \u2192 done. progress ${v.doneCount}/${v.totalCount} (${Math.round(v.progressPct * 100)}%)
|
|
6845
|
+
`
|
|
6846
|
+
);
|
|
6847
|
+
} catch (err) {
|
|
6848
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6849
|
+
`);
|
|
6850
|
+
process.exit(1);
|
|
6851
|
+
}
|
|
6852
|
+
});
|
|
6853
|
+
task.command("todo-uncheck <task-id> <index>").description("Un-tick a TODO item").option("--json", "output raw JSON response").action(async (taskId, indexRaw, opts) => {
|
|
6854
|
+
const client = getIMClient2();
|
|
6855
|
+
try {
|
|
6856
|
+
const idx = parseInt(indexRaw, 10);
|
|
6857
|
+
if (!Number.isFinite(idx) || idx < 0) throw new Error("index must be a non-negative integer");
|
|
6858
|
+
const res = await client.im.tasks.todo.toggle(taskId, idx, false);
|
|
6859
|
+
if (opts.json) {
|
|
6860
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6861
|
+
return;
|
|
6862
|
+
}
|
|
6863
|
+
if (!res.ok) {
|
|
6864
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6865
|
+
`);
|
|
6866
|
+
process.exit(1);
|
|
6867
|
+
}
|
|
6868
|
+
const v = res.data;
|
|
6869
|
+
process.stdout.write(`TODO[${idx}] \u2192 pending. progress ${v.doneCount}/${v.totalCount}
|
|
6870
|
+
`);
|
|
6871
|
+
} catch (err) {
|
|
6872
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6873
|
+
`);
|
|
6874
|
+
process.exit(1);
|
|
6875
|
+
}
|
|
6876
|
+
});
|
|
6877
|
+
task.command("todo-show <task-id>").description("Render TODO.md checklist + progress for a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6878
|
+
const client = getIMClient2();
|
|
6879
|
+
try {
|
|
6880
|
+
const res = await client.im.tasks.todo.list(taskId);
|
|
6881
|
+
if (opts.json) {
|
|
6882
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6883
|
+
return;
|
|
6884
|
+
}
|
|
6885
|
+
if (!res.ok) {
|
|
6886
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6887
|
+
`);
|
|
6888
|
+
process.exit(1);
|
|
6889
|
+
}
|
|
6890
|
+
const v = res.data;
|
|
6891
|
+
process.stdout.write(
|
|
6892
|
+
`TODO progress: ${v.doneCount}/${v.totalCount} (${Math.round(v.progressPct * 100)}%) \xB7 rev ${v.revision}
|
|
6893
|
+
`
|
|
6894
|
+
);
|
|
6895
|
+
for (const it of v.items) {
|
|
6896
|
+
const tick = it.status === "done" ? "\u2611" : "\u2610";
|
|
6897
|
+
const indent = " ".repeat(it.depth);
|
|
6898
|
+
process.stdout.write(` ${indent}${tick} [${it.index}] ${it.text}
|
|
6899
|
+
`);
|
|
6900
|
+
}
|
|
6901
|
+
} catch (err) {
|
|
6902
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6903
|
+
`);
|
|
6904
|
+
process.exit(1);
|
|
6905
|
+
}
|
|
6906
|
+
});
|
|
6907
|
+
task.command("acceptance <task-id>").description("Show acceptance criteria + rolled-up status for a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6908
|
+
const client = getIMClient2();
|
|
6909
|
+
try {
|
|
6910
|
+
const res = await client.im.tasks.getAcceptance(taskId);
|
|
6911
|
+
if (opts.json) {
|
|
6912
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6913
|
+
return;
|
|
6914
|
+
}
|
|
6915
|
+
if (!res.ok) {
|
|
6916
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6917
|
+
`);
|
|
6918
|
+
process.exit(1);
|
|
6919
|
+
}
|
|
6920
|
+
const v = res.data;
|
|
6921
|
+
process.stdout.write(`Acceptance for ${taskId}: ${v.overall} (${v.completedCount}/${v.totalCount})
|
|
6922
|
+
`);
|
|
6923
|
+
for (const c of v.criteria) {
|
|
6924
|
+
const mark = c.status === "passed" ? "\u2713" : c.status === "failed" ? "\u2717" : c.status === "n/a" ? "\xB7" : "\u25EF";
|
|
6925
|
+
const req = c.required === false ? " [optional]" : "";
|
|
6926
|
+
const ver = c.verifierAgentId ? ` @${c.verifierAgentId}` : "";
|
|
6927
|
+
process.stdout.write(` ${mark} [${c.verifyMode}${ver}] ${c.expectation}${req} (${c.status}) \u2014 ${c.id}
|
|
6928
|
+
`);
|
|
6929
|
+
}
|
|
6930
|
+
} catch (err) {
|
|
6931
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6932
|
+
`);
|
|
6933
|
+
process.exit(1);
|
|
6934
|
+
}
|
|
6935
|
+
});
|
|
6936
|
+
task.command("add-criterion <task-id>").description("Add an acceptance criterion to a task (rev 2 \u2014 verifyMode + expectation)").requiredOption(
|
|
6937
|
+
"--mode <mode>",
|
|
6938
|
+
"verifyMode: qualitative | quantitative | agent-self-check | manual"
|
|
6939
|
+
).requiredOption("--expectation <markdown>", "markdown describing what done looks like").option("--verifier-agent <agentId>", "verifier agent id (default = creator)").option("--weight <n>", "weight (numeric, default 1)", parseFloat).option("--optional", "mark this criterion optional (default required)").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6940
|
+
const client = getIMClient2();
|
|
6941
|
+
try {
|
|
6942
|
+
const VALID = ["qualitative", "quantitative", "agent-self-check", "manual"];
|
|
6943
|
+
if (!VALID.includes(opts.mode)) {
|
|
6944
|
+
throw new Error(`--mode must be one of ${VALID.join(" | ")}`);
|
|
6945
|
+
}
|
|
6946
|
+
const res = await client.im.tasks.criteria.add(taskId, {
|
|
6947
|
+
verifyMode: opts.mode,
|
|
6948
|
+
expectation: opts.expectation,
|
|
6949
|
+
verifierAgentId: opts.verifierAgent ?? null,
|
|
6950
|
+
weight: opts.weight ?? 1,
|
|
6951
|
+
required: !opts.optional
|
|
6952
|
+
});
|
|
6953
|
+
if (opts.json) {
|
|
6954
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6955
|
+
return;
|
|
6956
|
+
}
|
|
6957
|
+
if (!res.ok) {
|
|
6958
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6959
|
+
`);
|
|
6960
|
+
process.exit(1);
|
|
6961
|
+
}
|
|
6962
|
+
const { criterion } = res.data;
|
|
6963
|
+
process.stdout.write(`Added criterion ${criterion.id} to task ${taskId}
|
|
6964
|
+
`);
|
|
6965
|
+
} catch (err) {
|
|
6966
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6967
|
+
`);
|
|
6968
|
+
process.exit(1);
|
|
6969
|
+
}
|
|
6970
|
+
});
|
|
6971
|
+
task.command("verify <task-id>").description(
|
|
6972
|
+
"Assignee self-check: list all agent-self-check criteria + mark them passed (run before status=review)"
|
|
6973
|
+
).option("--note <note>", "note to attach to each self-check", "agent self-check via `cloud task verify`").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6974
|
+
const client = getIMClient2();
|
|
6975
|
+
try {
|
|
6976
|
+
const view = await client.im.tasks.getAcceptance(taskId);
|
|
6977
|
+
if (!view.ok) {
|
|
6978
|
+
process.stderr.write(`Error: ${view.error?.message || "Unknown error"}
|
|
6979
|
+
`);
|
|
6980
|
+
process.exit(1);
|
|
6981
|
+
}
|
|
6982
|
+
const v = view.data;
|
|
6983
|
+
const targets = v.criteria.filter(
|
|
6984
|
+
(c) => c.verifyMode === "agent-self-check" && c.status === "pending"
|
|
6985
|
+
);
|
|
6986
|
+
if (targets.length === 0) {
|
|
6987
|
+
process.stdout.write("No pending agent-self-check criteria.\n");
|
|
6988
|
+
return;
|
|
6989
|
+
}
|
|
6990
|
+
const results = [];
|
|
6991
|
+
let anyFailed = false;
|
|
6992
|
+
for (const c of targets) {
|
|
6993
|
+
const r = await client.im.tasks.criteria.verify(taskId, c.id, {
|
|
6994
|
+
outcome: "passed",
|
|
6995
|
+
note: opts.note
|
|
6996
|
+
});
|
|
6997
|
+
results.push({ id: c.id, outcome: r.ok ? "passed" : "error" });
|
|
6998
|
+
if (!r.ok) anyFailed = true;
|
|
6999
|
+
}
|
|
7000
|
+
if (opts.json) {
|
|
7001
|
+
process.stdout.write(JSON.stringify({ ok: !anyFailed, results }, null, 2) + "\n");
|
|
7002
|
+
return;
|
|
7003
|
+
}
|
|
7004
|
+
for (const r of results) {
|
|
7005
|
+
process.stdout.write(` ${r.outcome === "passed" ? "\u2713" : "\u2717"} ${r.id}
|
|
7006
|
+
`);
|
|
7007
|
+
}
|
|
7008
|
+
if (anyFailed) process.exit(1);
|
|
7009
|
+
} catch (err) {
|
|
7010
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
7011
|
+
`);
|
|
7012
|
+
process.exit(1);
|
|
7013
|
+
}
|
|
7014
|
+
});
|
|
7015
|
+
task.command("verify-criterion <task-id> <criterion-id>").description(
|
|
7016
|
+
"Report verify outcome for one criterion. Used by reviewers (manual), verifier agents, and assignees (self-check)."
|
|
7017
|
+
).requiredOption("--outcome <outcome>", "passed | failed | n/a | waived").option("--note <note>", "free-form markdown: method + result + repro steps").option("--evidence <ref...>", "evidence refs (repeatable; e.g. asset:<id>, url:..., taskRun:<id>)").option("--waive-reason <reason>", "required when --outcome waived").option("--json", "output raw JSON response").action(async (taskId, criterionId, opts) => {
|
|
7018
|
+
const client = getIMClient2();
|
|
7019
|
+
try {
|
|
7020
|
+
const VALID = ["passed", "failed", "n/a", "waived"];
|
|
7021
|
+
if (!VALID.includes(opts.outcome)) {
|
|
7022
|
+
throw new Error(`--outcome must be one of ${VALID.join(" | ")}`);
|
|
7023
|
+
}
|
|
7024
|
+
if (opts.outcome === "waived" && !opts.waiveReason) {
|
|
7025
|
+
throw new Error("--waive-reason is required when --outcome waived");
|
|
7026
|
+
}
|
|
7027
|
+
const outcome = opts.outcome;
|
|
7028
|
+
const res = await client.im.tasks.criteria.verify(taskId, criterionId, {
|
|
7029
|
+
outcome,
|
|
7030
|
+
note: opts.note,
|
|
7031
|
+
evidenceRefs: opts.evidence,
|
|
7032
|
+
waiveReason: opts.waiveReason
|
|
7033
|
+
});
|
|
7034
|
+
if (opts.json) {
|
|
7035
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7036
|
+
return;
|
|
7037
|
+
}
|
|
7038
|
+
if (!res.ok) {
|
|
7039
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7040
|
+
`);
|
|
7041
|
+
process.exit(1);
|
|
7042
|
+
}
|
|
7043
|
+
process.stdout.write(`Criterion ${criterionId} \u2192 ${opts.outcome}
|
|
7044
|
+
`);
|
|
7045
|
+
} catch (err) {
|
|
7046
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
7047
|
+
`);
|
|
7048
|
+
process.exit(1);
|
|
7049
|
+
}
|
|
7050
|
+
});
|
|
7051
|
+
task.command("apply-template <task-id>").description("Apply an acceptance criteria template to a task").requiredOption("--template <templateId>", "template id to apply").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
7052
|
+
const client = getIMClient2();
|
|
7053
|
+
try {
|
|
7054
|
+
const res = await client.im.tasks.applyTemplate(taskId, opts.template);
|
|
7055
|
+
if (opts.json) {
|
|
7056
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7057
|
+
return;
|
|
7058
|
+
}
|
|
7059
|
+
if (!res.ok) {
|
|
7060
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7061
|
+
`);
|
|
7062
|
+
process.exit(1);
|
|
7063
|
+
}
|
|
7064
|
+
const v = res.data;
|
|
7065
|
+
process.stdout.write(`Applied template ${opts.template} (${v.totalCount} criteria total)
|
|
7066
|
+
`);
|
|
7067
|
+
} catch (err) {
|
|
7068
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7069
|
+
process.stderr.write(`Error: ${message}
|
|
7070
|
+
`);
|
|
7071
|
+
process.exit(1);
|
|
7072
|
+
}
|
|
7073
|
+
});
|
|
7074
|
+
}
|
|
7075
|
+
|
|
7076
|
+
// src/commands/memory.ts
|
|
7077
|
+
var MEMORY_TYPES = /* @__PURE__ */ new Set(["user", "feedback", "project", "reference"]);
|
|
7078
|
+
function register5(parent, getIMClient2, _getAPIClient) {
|
|
7079
|
+
const mem = parent.command("memory").description("Agent memory file management");
|
|
7080
|
+
mem.command("write").description("Write a memory file").requiredOption("-s, --scope <scope>", "memory scope").requiredOption("-p, --path <path>", "file path within scope").requiredOption("-c, --content <content>", "file content").option("--type <type>", "memory type: user | feedback | project | reference").option("--description <description>", "human-readable description of this memory").option("--json", "output raw JSON response").action(async (opts) => {
|
|
7081
|
+
const client = getIMClient2();
|
|
7082
|
+
try {
|
|
7083
|
+
if (opts.type && !MEMORY_TYPES.has(opts.type)) {
|
|
7084
|
+
throw new Error(`Invalid --type "${opts.type}". Use one of: user, feedback, project, reference.`);
|
|
7085
|
+
}
|
|
7086
|
+
const body = {
|
|
7087
|
+
scope: opts.scope,
|
|
7088
|
+
path: opts.path,
|
|
7089
|
+
content: opts.content
|
|
7090
|
+
};
|
|
7091
|
+
if (opts.type) body.memoryType = opts.type;
|
|
7092
|
+
if (opts.description) body.description = opts.description;
|
|
7093
|
+
const res = await client.im.memory.createFile(body);
|
|
7094
|
+
if (opts.json) {
|
|
7095
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7096
|
+
return;
|
|
7097
|
+
}
|
|
7098
|
+
if (!res.ok) {
|
|
7099
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7100
|
+
`);
|
|
7101
|
+
process.exit(1);
|
|
7102
|
+
}
|
|
7103
|
+
const file = res.data;
|
|
7104
|
+
if (!file) throw new Error("Memory file response missing data");
|
|
7105
|
+
process.stdout.write(`Memory file created
|
|
7106
|
+
`);
|
|
7107
|
+
process.stdout.write(` ID: ${file.id}
|
|
7108
|
+
`);
|
|
7109
|
+
process.stdout.write(` Scope: ${file.scope}
|
|
7110
|
+
`);
|
|
7111
|
+
process.stdout.write(` Path: ${file.path}
|
|
7112
|
+
`);
|
|
7113
|
+
} catch (err) {
|
|
7114
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7115
|
+
process.stderr.write(`Error: ${message}
|
|
6238
7116
|
`);
|
|
6239
7117
|
process.exit(1);
|
|
6240
7118
|
}
|
|
@@ -6496,7 +7374,7 @@ function printFileTable(files) {
|
|
|
6496
7374
|
const idLen = Math.max(2, ...files.map((f) => f.id.length));
|
|
6497
7375
|
const scopeLen = Math.max(5, ...files.map((f) => f.scope.length));
|
|
6498
7376
|
const pathLen = Math.max(4, ...files.map((f) => f.path.length));
|
|
6499
|
-
const row = (id, scope,
|
|
7377
|
+
const row = (id, scope, path6) => `${id.padEnd(idLen)} ${scope.padEnd(scopeLen)} ${path6.padEnd(pathLen)}`;
|
|
6500
7378
|
process.stdout.write(row("ID", "SCOPE", "PATH") + "\n");
|
|
6501
7379
|
process.stdout.write(`${"-".repeat(idLen)} ${"-".repeat(scopeLen)} ${"-".repeat(pathLen)}
|
|
6502
7380
|
`);
|
|
@@ -6757,17 +7635,471 @@ ${result.content}
|
|
|
6757
7635
|
});
|
|
6758
7636
|
}
|
|
6759
7637
|
|
|
6760
|
-
// src/commands/
|
|
7638
|
+
// src/commands/skill-draft.ts
|
|
7639
|
+
var fs3 = __toESM(require("fs"));
|
|
6761
7640
|
function register7(parent, getIMClient2, _getAPIClient) {
|
|
6762
|
-
const
|
|
6763
|
-
|
|
6764
|
-
|
|
7641
|
+
const skill = parent.commands.find((c) => c.name() === "skill") ?? parent.command("skill");
|
|
7642
|
+
const draft = skill.command("draft").description("Author / patch / regenerate / show / list skill drafts (release201/07)");
|
|
7643
|
+
draft.command("create").description("Submit a new skill draft from a manifest.json file").option("--slug <slug>", "Draft slug (overrides manifest.slug)").option("--manifest <path>", "Path to manifest.json containing slug/name/description/files[]/...").option("--workspace <id>", "Override manifest.workspaceId").option("--owner-agent <id>", "Override manifest.ownerAgentId").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
7644
|
+
if (!opts.manifest) {
|
|
7645
|
+
process.stderr.write("Error: --manifest <path-to-manifest.json> is required\n");
|
|
7646
|
+
process.exit(1);
|
|
7647
|
+
}
|
|
7648
|
+
let manifest;
|
|
6765
7649
|
try {
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
7650
|
+
manifest = JSON.parse(fs3.readFileSync(opts.manifest, "utf-8"));
|
|
7651
|
+
} catch (err) {
|
|
7652
|
+
process.stderr.write(`Error: failed to read manifest ${opts.manifest}: ${err.message}
|
|
7653
|
+
`);
|
|
7654
|
+
process.exit(1);
|
|
7655
|
+
}
|
|
7656
|
+
const slug = opts.slug ?? manifest.slug;
|
|
7657
|
+
const workspaceId = opts.workspace ?? manifest.workspaceId;
|
|
7658
|
+
const ownerAgentId = opts.ownerAgent ?? manifest.ownerAgentId;
|
|
7659
|
+
if (!slug || !manifest.name || !manifest.description) {
|
|
7660
|
+
process.stderr.write("Error: manifest must include slug, name, description\n");
|
|
7661
|
+
process.exit(1);
|
|
7662
|
+
}
|
|
7663
|
+
if (!workspaceId) {
|
|
7664
|
+
process.stderr.write("Error: --workspace <id> or manifest.workspaceId is required\n");
|
|
7665
|
+
process.exit(1);
|
|
7666
|
+
}
|
|
7667
|
+
if (!Array.isArray(manifest.files) || manifest.files.length < 2) {
|
|
7668
|
+
process.stderr.write("Error: manifest.files[] must include at least SKILL.md + skill.json\n");
|
|
7669
|
+
process.exit(1);
|
|
7670
|
+
}
|
|
7671
|
+
const client = getIMClient2();
|
|
7672
|
+
try {
|
|
7673
|
+
const res = await client.im.skills.draft.create({
|
|
7674
|
+
slug,
|
|
7675
|
+
name: manifest.name,
|
|
7676
|
+
description: manifest.description,
|
|
7677
|
+
workspaceId,
|
|
7678
|
+
ownerAgentId,
|
|
7679
|
+
files: manifest.files,
|
|
7680
|
+
metadata: manifest.metadata
|
|
7681
|
+
});
|
|
7682
|
+
if (opts.json) {
|
|
7683
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7684
|
+
return;
|
|
7685
|
+
}
|
|
7686
|
+
if (!res.ok) {
|
|
7687
|
+
process.stderr.write(`Error: ${res.error ?? "create failed"}
|
|
7688
|
+
`);
|
|
7689
|
+
process.exit(1);
|
|
7690
|
+
}
|
|
7691
|
+
const data = res.data ?? res;
|
|
7692
|
+
process.stdout.write(`Draft submitted.
|
|
7693
|
+
`);
|
|
7694
|
+
process.stdout.write(` id: ${data.id}
|
|
7695
|
+
`);
|
|
7696
|
+
process.stdout.write(` slug: ${data.slug}
|
|
7697
|
+
`);
|
|
7698
|
+
process.stdout.write(` manifest revision: ${data.manifestRevision}
|
|
7699
|
+
`);
|
|
7700
|
+
if (data.reviewTaskId) {
|
|
7701
|
+
process.stdout.write(` review task: ${data.reviewTaskId}
|
|
7702
|
+
`);
|
|
7703
|
+
}
|
|
7704
|
+
const warnings = data.validationWarnings ?? [];
|
|
7705
|
+
if (warnings.length > 0) {
|
|
7706
|
+
process.stdout.write(` warnings:
|
|
7707
|
+
`);
|
|
7708
|
+
for (const w of warnings) {
|
|
7709
|
+
process.stdout.write(` [${w.gate}] ${w.message}
|
|
7710
|
+
`);
|
|
7711
|
+
}
|
|
7712
|
+
}
|
|
7713
|
+
} catch (err) {
|
|
7714
|
+
process.stderr.write(`Error: ${err.message}
|
|
7715
|
+
`);
|
|
7716
|
+
process.exit(1);
|
|
7717
|
+
}
|
|
7718
|
+
});
|
|
7719
|
+
draft.command("patch <draft-id>").description("Patch a draft \u2014 add / update / delete a single file").option("--file <path>", "Manifest file path inside the draft (e.g. SKILL.md or references/foo.md)").option("--op <op>", "Operation: add | update | delete", "update").option("--content <text>", "New utf-8 content for add/update (omit for --content-file)").option("--content-file <path>", "Read content from a local file").option("--reason <text>", "Free-form reason recorded in revision history").option("--json", "Output raw JSON response").action(async (draftId, opts) => {
|
|
7720
|
+
if (!opts.file) {
|
|
7721
|
+
process.stderr.write("Error: --file <path> is required\n");
|
|
7722
|
+
process.exit(1);
|
|
7723
|
+
}
|
|
7724
|
+
if (!["add", "update", "delete"].includes(opts.op)) {
|
|
7725
|
+
process.stderr.write(`Error: --op must be add | update | delete (got ${opts.op})
|
|
7726
|
+
`);
|
|
7727
|
+
process.exit(1);
|
|
7728
|
+
}
|
|
7729
|
+
let content;
|
|
7730
|
+
if (opts.op !== "delete") {
|
|
7731
|
+
if (opts.contentFile) {
|
|
7732
|
+
try {
|
|
7733
|
+
content = fs3.readFileSync(opts.contentFile, "utf-8");
|
|
7734
|
+
} catch (err) {
|
|
7735
|
+
process.stderr.write(`Error: failed to read --content-file: ${err.message}
|
|
7736
|
+
`);
|
|
7737
|
+
process.exit(1);
|
|
7738
|
+
}
|
|
7739
|
+
} else if (opts.content !== void 0) {
|
|
7740
|
+
content = opts.content;
|
|
7741
|
+
} else {
|
|
7742
|
+
process.stderr.write("Error: --content or --content-file is required for add/update\n");
|
|
7743
|
+
process.exit(1);
|
|
7744
|
+
}
|
|
7745
|
+
}
|
|
7746
|
+
const client = getIMClient2();
|
|
7747
|
+
try {
|
|
7748
|
+
const res = await client.im.skills.draft.patch(draftId, {
|
|
7749
|
+
files: [{ path: opts.file, op: opts.op, content }],
|
|
7750
|
+
reason: opts.reason
|
|
7751
|
+
});
|
|
7752
|
+
if (opts.json) {
|
|
7753
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7754
|
+
return;
|
|
7755
|
+
}
|
|
7756
|
+
if (!res.ok) {
|
|
7757
|
+
process.stderr.write(`Error: ${res.error ?? "patch failed"}
|
|
7758
|
+
`);
|
|
7759
|
+
process.exit(1);
|
|
7760
|
+
}
|
|
7761
|
+
const data = res.data ?? res;
|
|
7762
|
+
process.stdout.write(`Patch applied.
|
|
7763
|
+
`);
|
|
7764
|
+
process.stdout.write(` id: ${data.id}
|
|
7765
|
+
`);
|
|
7766
|
+
process.stdout.write(` manifest revision: ${data.manifestRevision}
|
|
7767
|
+
`);
|
|
7768
|
+
} catch (err) {
|
|
7769
|
+
process.stderr.write(`Error: ${err.message}
|
|
7770
|
+
`);
|
|
7771
|
+
process.exit(1);
|
|
7772
|
+
}
|
|
7773
|
+
});
|
|
7774
|
+
draft.command("regenerate <draft-id>").description("Request a regenerate session for a draft (does not delete current manifest)").option("--reason <text>", "Why the draft needs regeneration").option("--json", "Output raw JSON response").action(async (draftId, opts) => {
|
|
7775
|
+
if (!opts.reason) {
|
|
7776
|
+
process.stderr.write("Error: --reason <text> is required\n");
|
|
7777
|
+
process.exit(1);
|
|
7778
|
+
}
|
|
7779
|
+
const client = getIMClient2();
|
|
7780
|
+
try {
|
|
7781
|
+
const res = await client.im.skills.draft.regenerate(draftId, { reason: opts.reason });
|
|
7782
|
+
if (opts.json) {
|
|
7783
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7784
|
+
return;
|
|
7785
|
+
}
|
|
7786
|
+
if (!res.ok) {
|
|
7787
|
+
process.stderr.write(`Error: ${res.error ?? "regenerate failed"}
|
|
7788
|
+
`);
|
|
7789
|
+
process.exit(1);
|
|
7790
|
+
}
|
|
7791
|
+
const data = res.data ?? res;
|
|
7792
|
+
process.stdout.write(`Regenerate requested.
|
|
7793
|
+
`);
|
|
7794
|
+
process.stdout.write(` id: ${data.id}
|
|
7795
|
+
`);
|
|
7796
|
+
process.stdout.write(` session id: ${data.sessionId}
|
|
7797
|
+
`);
|
|
7798
|
+
} catch (err) {
|
|
7799
|
+
process.stderr.write(`Error: ${err.message}
|
|
7800
|
+
`);
|
|
7801
|
+
process.exit(1);
|
|
7802
|
+
}
|
|
7803
|
+
});
|
|
7804
|
+
draft.command("show <draft-id>").description("Show a draft's manifest + revision history").option("--json", "Output raw JSON response").action(async (draftId, opts) => {
|
|
7805
|
+
const client = getIMClient2();
|
|
7806
|
+
try {
|
|
7807
|
+
const res = await client.im.skills.draft.show(draftId);
|
|
7808
|
+
if (opts.json) {
|
|
7809
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7810
|
+
return;
|
|
7811
|
+
}
|
|
7812
|
+
if (!res.ok) {
|
|
7813
|
+
process.stderr.write(`Error: ${res.error ?? "show failed"}
|
|
7814
|
+
`);
|
|
7815
|
+
process.exit(1);
|
|
7816
|
+
}
|
|
7817
|
+
const data = res.data ?? res;
|
|
7818
|
+
process.stdout.write(`Draft ${data.id}
|
|
7819
|
+
`);
|
|
7820
|
+
process.stdout.write(` slug: ${data.slug}
|
|
7821
|
+
`);
|
|
7822
|
+
process.stdout.write(` name: ${data.name}
|
|
7823
|
+
`);
|
|
7824
|
+
process.stdout.write(` status: ${data.status}
|
|
7825
|
+
`);
|
|
7826
|
+
process.stdout.write(` workspace: ${data.workspaceId}
|
|
7827
|
+
`);
|
|
7828
|
+
process.stdout.write(` ownerAgent: ${data.ownerAgentId}
|
|
7829
|
+
`);
|
|
7830
|
+
process.stdout.write(` revision: ${data.manifestRevision}
|
|
7831
|
+
`);
|
|
7832
|
+
process.stdout.write(` license: ${data.license}
|
|
7833
|
+
`);
|
|
7834
|
+
process.stdout.write(` compatibility: ${(data.compatibility ?? []).join(", ")}
|
|
7835
|
+
`);
|
|
7836
|
+
process.stdout.write(` files:
|
|
7837
|
+
`);
|
|
7838
|
+
for (const f of data.files ?? []) {
|
|
7839
|
+
process.stdout.write(` ${f.path} (${f.size}B, sha256:${(f.sha256 ?? "").slice(0, 8)}\u2026)
|
|
7840
|
+
`);
|
|
7841
|
+
}
|
|
7842
|
+
} catch (err) {
|
|
7843
|
+
process.stderr.write(`Error: ${err.message}
|
|
7844
|
+
`);
|
|
7845
|
+
process.exit(1);
|
|
7846
|
+
}
|
|
7847
|
+
});
|
|
7848
|
+
draft.command("list").description("List drafts in a workspace").option("--workspace <id>", "Workspace id (required)").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
7849
|
+
if (!opts.workspace) {
|
|
7850
|
+
process.stderr.write("Error: --workspace <id> is required\n");
|
|
7851
|
+
process.exit(1);
|
|
7852
|
+
}
|
|
7853
|
+
const client = getIMClient2();
|
|
7854
|
+
try {
|
|
7855
|
+
const res = await client.im.skills.draft.list(opts.workspace);
|
|
7856
|
+
if (opts.json) {
|
|
7857
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7858
|
+
return;
|
|
7859
|
+
}
|
|
7860
|
+
if (!res.ok) {
|
|
7861
|
+
process.stderr.write(`Error: ${res.error ?? "list failed"}
|
|
7862
|
+
`);
|
|
7863
|
+
process.exit(1);
|
|
7864
|
+
}
|
|
7865
|
+
const drafts = res.data ?? [];
|
|
7866
|
+
if (drafts.length === 0) {
|
|
7867
|
+
process.stdout.write("No drafts in this workspace.\n");
|
|
7868
|
+
return;
|
|
7869
|
+
}
|
|
7870
|
+
process.stdout.write(`Drafts (${drafts.length}):
|
|
7871
|
+
`);
|
|
7872
|
+
for (const d of drafts) {
|
|
7873
|
+
process.stdout.write(` ${d.id} ${d.slug} ${(d.name ?? "").slice(0, 32)} rev:${(d.contentManifestRevision ?? "").slice(0, 8)}\u2026 updated:${d.updatedAt}
|
|
7874
|
+
`);
|
|
7875
|
+
}
|
|
7876
|
+
} catch (err) {
|
|
7877
|
+
process.stderr.write(`Error: ${err.message}
|
|
7878
|
+
`);
|
|
7879
|
+
process.exit(1);
|
|
7880
|
+
}
|
|
7881
|
+
});
|
|
7882
|
+
}
|
|
7883
|
+
|
|
7884
|
+
// src/commands/code-grep.ts
|
|
7885
|
+
var fs4 = __toESM(require("fs"));
|
|
7886
|
+
var path2 = __toESM(require("path"));
|
|
7887
|
+
var crypto2 = __toESM(require("crypto"));
|
|
7888
|
+
var os = __toESM(require("os"));
|
|
7889
|
+
var DEFAULT_MAX_MATCHES = 50;
|
|
7890
|
+
var SCRATCH_DIR_GLOB = path2.join(os.homedir(), ".prismer", "agents");
|
|
7891
|
+
function isPathWhitelisted(repo) {
|
|
7892
|
+
const abs = path2.resolve(repo);
|
|
7893
|
+
const workspaceRoot = process.env.PRISMER_WORKSPACE_ROOT;
|
|
7894
|
+
if (workspaceRoot) {
|
|
7895
|
+
const wsAbs = path2.resolve(workspaceRoot);
|
|
7896
|
+
if (abs === wsAbs || abs.startsWith(wsAbs + path2.sep)) return true;
|
|
7897
|
+
}
|
|
7898
|
+
if (abs.startsWith(SCRATCH_DIR_GLOB + path2.sep)) {
|
|
7899
|
+
const rel = abs.slice(SCRATCH_DIR_GLOB.length + 1);
|
|
7900
|
+
const parts = rel.split(path2.sep);
|
|
7901
|
+
if (parts.length >= 2 && parts[1] === "scratch") return true;
|
|
7902
|
+
}
|
|
7903
|
+
return false;
|
|
7904
|
+
}
|
|
7905
|
+
function walk(dir, glob, acc) {
|
|
7906
|
+
let entries;
|
|
7907
|
+
try {
|
|
7908
|
+
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
7909
|
+
} catch {
|
|
7910
|
+
return;
|
|
7911
|
+
}
|
|
7912
|
+
for (const e of entries) {
|
|
7913
|
+
const full = path2.join(dir, e.name);
|
|
7914
|
+
if (e.isDirectory()) {
|
|
7915
|
+
if (e.name === "node_modules" || e.name === ".git" || e.name === "dist" || e.name === "build") continue;
|
|
7916
|
+
walk(full, glob, acc);
|
|
7917
|
+
} else if (e.isFile()) {
|
|
7918
|
+
if (!glob || matchGlob(e.name, glob)) acc.push(full);
|
|
7919
|
+
}
|
|
7920
|
+
}
|
|
7921
|
+
}
|
|
7922
|
+
function matchGlob(name, glob) {
|
|
7923
|
+
if (glob.startsWith("*.")) {
|
|
7924
|
+
return name.endsWith(glob.slice(1));
|
|
7925
|
+
}
|
|
7926
|
+
return name === glob;
|
|
7927
|
+
}
|
|
7928
|
+
function register8(parent, _getIMClient, _getAPIClient) {
|
|
7929
|
+
const code = parent.command("code").description("Local code-source helpers for skill-authoring (release201/07)");
|
|
7930
|
+
code.command("grep <pattern>").description("Grep a whitelisted repo path for a pattern; output matched snippets as JSON").option("--repo <abs-path>", "Absolute repo path (must be inside PRISMER_WORKSPACE_ROOT or ~/.prismer/agents/<id>/scratch)").option("--glob <pattern>", "File-name glob (e.g. *.ts)").option("--max-matches <n>", "Maximum number of matches to return", String(DEFAULT_MAX_MATCHES)).option("--json", "Output JSON (default)", true).action(async (pattern, opts) => {
|
|
7931
|
+
if (!opts.repo) {
|
|
7932
|
+
process.stderr.write("Error: --repo <abs-path> is required\n");
|
|
7933
|
+
process.exit(1);
|
|
7934
|
+
}
|
|
7935
|
+
if (!isPathWhitelisted(opts.repo)) {
|
|
7936
|
+
process.stderr.write(
|
|
7937
|
+
`Error: --repo path ${opts.repo} is not whitelisted; only PRISMER_WORKSPACE_ROOT or ~/.prismer/agents/<id>/scratch are allowed
|
|
7938
|
+
`
|
|
7939
|
+
);
|
|
7940
|
+
process.exit(1);
|
|
7941
|
+
}
|
|
7942
|
+
const maxMatches = parseInt(opts.maxMatches, 10) || DEFAULT_MAX_MATCHES;
|
|
7943
|
+
let re;
|
|
7944
|
+
try {
|
|
7945
|
+
re = new RegExp(pattern);
|
|
7946
|
+
} catch (err) {
|
|
7947
|
+
process.stderr.write(`Error: invalid regex pattern: ${err.message}
|
|
7948
|
+
`);
|
|
7949
|
+
process.exit(1);
|
|
7950
|
+
}
|
|
7951
|
+
const files = [];
|
|
7952
|
+
walk(opts.repo, opts.glob, files);
|
|
7953
|
+
const results = [];
|
|
7954
|
+
for (const file of files) {
|
|
7955
|
+
if (results.length >= maxMatches) break;
|
|
7956
|
+
let body;
|
|
7957
|
+
try {
|
|
7958
|
+
body = fs4.readFileSync(file, "utf-8");
|
|
7959
|
+
} catch {
|
|
7960
|
+
continue;
|
|
7961
|
+
}
|
|
7962
|
+
const lines = body.split("\n");
|
|
7963
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7964
|
+
if (re.test(lines[i])) {
|
|
7965
|
+
const startLine = Math.max(0, i - 1);
|
|
7966
|
+
const endLine = Math.min(lines.length, i + 4);
|
|
7967
|
+
const snippet = lines.slice(startLine, endLine).join("\n");
|
|
7968
|
+
const sha256 = crypto2.createHash("sha256").update(snippet).digest("hex");
|
|
7969
|
+
results.push({
|
|
7970
|
+
path: path2.relative(opts.repo, file),
|
|
7971
|
+
line: i + 1,
|
|
7972
|
+
snippet,
|
|
7973
|
+
sha256
|
|
7974
|
+
});
|
|
7975
|
+
if (results.length >= maxMatches) break;
|
|
7976
|
+
}
|
|
7977
|
+
}
|
|
7978
|
+
}
|
|
7979
|
+
process.stdout.write(JSON.stringify(results, null, 2) + "\n");
|
|
7980
|
+
});
|
|
7981
|
+
}
|
|
7982
|
+
|
|
7983
|
+
// src/commands/service-introspect.ts
|
|
7984
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
7985
|
+
async function fetchWithTimeout(url, init = {}) {
|
|
7986
|
+
const ctrl = new AbortController();
|
|
7987
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
7988
|
+
try {
|
|
7989
|
+
return await fetch(url, { ...init, signal: ctrl.signal });
|
|
7990
|
+
} finally {
|
|
7991
|
+
clearTimeout(timer);
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
async function probeOpenApi(url) {
|
|
7995
|
+
const base = url.replace(/\/$/, "");
|
|
7996
|
+
const candidates = [
|
|
7997
|
+
`${base}/openapi.json`,
|
|
7998
|
+
`${base}/swagger.json`,
|
|
7999
|
+
`${base}/api/openapi.json`,
|
|
8000
|
+
`${base}/.well-known/openapi.json`
|
|
8001
|
+
];
|
|
8002
|
+
for (const candidate of candidates) {
|
|
8003
|
+
try {
|
|
8004
|
+
const res = await fetchWithTimeout(candidate, { headers: { Accept: "application/json" } });
|
|
8005
|
+
if (res.ok) {
|
|
8006
|
+
const spec = await res.json();
|
|
8007
|
+
if (spec && (spec.openapi || spec.swagger)) {
|
|
8008
|
+
return { found: true, spec, specUrl: candidate };
|
|
8009
|
+
}
|
|
8010
|
+
}
|
|
8011
|
+
} catch {
|
|
8012
|
+
}
|
|
8013
|
+
}
|
|
8014
|
+
return { found: false };
|
|
8015
|
+
}
|
|
8016
|
+
async function probeMcp(url) {
|
|
8017
|
+
try {
|
|
8018
|
+
const res = await fetchWithTimeout(url, {
|
|
8019
|
+
method: "POST",
|
|
8020
|
+
headers: { "Content-Type": "application/json" },
|
|
8021
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
8022
|
+
});
|
|
8023
|
+
if (!res.ok) return { found: false };
|
|
8024
|
+
const body = await res.json();
|
|
8025
|
+
if (body?.result?.tools) {
|
|
8026
|
+
let resources = [];
|
|
8027
|
+
try {
|
|
8028
|
+
const r = await fetchWithTimeout(url, {
|
|
8029
|
+
method: "POST",
|
|
8030
|
+
headers: { "Content-Type": "application/json" },
|
|
8031
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "resources/list" })
|
|
8032
|
+
});
|
|
8033
|
+
if (r.ok) {
|
|
8034
|
+
const rb = await r.json();
|
|
8035
|
+
if (Array.isArray(rb?.result?.resources)) resources = rb.result.resources;
|
|
8036
|
+
}
|
|
8037
|
+
} catch {
|
|
8038
|
+
}
|
|
8039
|
+
return { found: true, tools: body.result.tools, resources };
|
|
8040
|
+
}
|
|
8041
|
+
} catch {
|
|
8042
|
+
}
|
|
8043
|
+
return { found: false };
|
|
8044
|
+
}
|
|
8045
|
+
function register9(parent, _getIMClient, _getAPIClient) {
|
|
8046
|
+
const svc = parent.command("service").description("Local service-endpoint helpers for skill-authoring (release201/07)");
|
|
8047
|
+
svc.command("introspect <url>").description("Probe an HTTP / MCP / OpenAPI endpoint and emit a tool/endpoint catalog").option("--protocol <kind>", "Force protocol: auto | mcp | openapi", "auto").option("--json", "Output JSON (default)", true).action(async (url, opts) => {
|
|
8048
|
+
const protocol = opts.protocol ?? "auto";
|
|
8049
|
+
if (protocol !== "auto" && protocol !== "mcp" && protocol !== "openapi") {
|
|
8050
|
+
process.stderr.write(`Error: --protocol must be auto | mcp | openapi (got ${protocol})
|
|
8051
|
+
`);
|
|
8052
|
+
process.exit(1);
|
|
8053
|
+
}
|
|
8054
|
+
const result = { kind: "unknown", tools: [], endpoints: [], probedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
8055
|
+
if (protocol === "mcp" || protocol === "auto") {
|
|
8056
|
+
const mcp = await probeMcp(url);
|
|
8057
|
+
if (mcp.found) {
|
|
8058
|
+
result.kind = "mcp";
|
|
8059
|
+
result.tools = mcp.tools ?? [];
|
|
8060
|
+
result.resources = mcp.resources ?? [];
|
|
8061
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
8062
|
+
return;
|
|
8063
|
+
}
|
|
8064
|
+
}
|
|
8065
|
+
if (protocol === "openapi" || protocol === "auto") {
|
|
8066
|
+
const oa = await probeOpenApi(url);
|
|
8067
|
+
if (oa.found) {
|
|
8068
|
+
result.kind = "openapi";
|
|
8069
|
+
result.specUrl = oa.specUrl;
|
|
8070
|
+
const paths = oa.spec?.paths ?? {};
|
|
8071
|
+
for (const p of Object.keys(paths)) {
|
|
8072
|
+
for (const method of Object.keys(paths[p])) {
|
|
8073
|
+
if (["get", "post", "put", "delete", "patch"].includes(method.toLowerCase())) {
|
|
8074
|
+
const op = paths[p][method];
|
|
8075
|
+
result.endpoints.push({
|
|
8076
|
+
path: p,
|
|
8077
|
+
method: method.toUpperCase(),
|
|
8078
|
+
operationId: op?.operationId ?? null,
|
|
8079
|
+
summary: op?.summary ?? null
|
|
8080
|
+
});
|
|
8081
|
+
}
|
|
8082
|
+
}
|
|
8083
|
+
}
|
|
8084
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
8085
|
+
return;
|
|
8086
|
+
}
|
|
8087
|
+
}
|
|
8088
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
8089
|
+
});
|
|
8090
|
+
}
|
|
8091
|
+
|
|
8092
|
+
// src/commands/files.ts
|
|
8093
|
+
function register10(parent, getIMClient2, _getAPIClient) {
|
|
8094
|
+
const file = parent.command("file").description("File upload, transfer, quota, and type management");
|
|
8095
|
+
file.command("upload <path>").description("Upload a file and get its upload ID and CDN URL").option("--mime <type>", "Override MIME type (e.g. image/png)").option("--json", "Output raw JSON response").action(async (filePath, opts) => {
|
|
8096
|
+
const client = getIMClient2();
|
|
8097
|
+
try {
|
|
8098
|
+
const uploadOpts = {};
|
|
8099
|
+
if (opts.mime) uploadOpts.mimeType = opts.mime;
|
|
8100
|
+
const res = await client.im.files.upload(filePath, Object.keys(uploadOpts).length ? uploadOpts : void 0);
|
|
8101
|
+
if (opts.json) {
|
|
8102
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6771
8103
|
return;
|
|
6772
8104
|
}
|
|
6773
8105
|
process.stdout.write(`Uploaded: ${res.fileName}
|
|
@@ -6872,8 +8204,43 @@ function register7(parent, getIMClient2, _getAPIClient) {
|
|
|
6872
8204
|
}
|
|
6873
8205
|
|
|
6874
8206
|
// src/commands/workspace.ts
|
|
6875
|
-
function
|
|
6876
|
-
|
|
8207
|
+
function fail(message) {
|
|
8208
|
+
process.stderr.write(`Error: ${message}
|
|
8209
|
+
`);
|
|
8210
|
+
process.exit(1);
|
|
8211
|
+
}
|
|
8212
|
+
function emitJson(data, json, fallback) {
|
|
8213
|
+
if (json) {
|
|
8214
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
8215
|
+
return;
|
|
8216
|
+
}
|
|
8217
|
+
fallback();
|
|
8218
|
+
}
|
|
8219
|
+
function parseAdminMemberRole(raw) {
|
|
8220
|
+
if (raw === "admin" || raw === "member") return raw;
|
|
8221
|
+
if (raw === void 0) return "member";
|
|
8222
|
+
if (raw === "owner") {
|
|
8223
|
+
fail("--role=owner is not allowed via CLI; use ownership transfer (v2.1+ RFC, release201/16 \xA75.2)");
|
|
8224
|
+
}
|
|
8225
|
+
fail("--role must be admin|member");
|
|
8226
|
+
}
|
|
8227
|
+
function printMemberList(items) {
|
|
8228
|
+
if (items.length === 0) {
|
|
8229
|
+
process.stdout.write("No members in this workspace.\n");
|
|
8230
|
+
return;
|
|
8231
|
+
}
|
|
8232
|
+
process.stdout.write(
|
|
8233
|
+
"ID".padEnd(28) + "ROLE".padEnd(10) + "IM_USER_ID".padEnd(38) + "JOINED\n"
|
|
8234
|
+
);
|
|
8235
|
+
for (const m of items) {
|
|
8236
|
+
process.stdout.write(
|
|
8237
|
+
`${m.id.padEnd(28)}${m.role.padEnd(10)}${m.memberImUserId.padEnd(38)}${m.joinedAt}
|
|
8238
|
+
`
|
|
8239
|
+
);
|
|
8240
|
+
}
|
|
8241
|
+
}
|
|
8242
|
+
function register11(parent, getIMClient2, _getAPIClient) {
|
|
8243
|
+
const workspace = parent.command("workspace").description("Workspace management \u2014 init, groups, agent assignment, and members (release201/16)");
|
|
6877
8244
|
workspace.command("init <name>").description("Initialize a workspace with a user and agent").requiredOption("--user-id <id>", "User ID").requiredOption("--user-name <name>", "User display name").requiredOption("--agent-id <id>", "Agent ID").requiredOption("--agent-name <name>", "Agent display name").option("--agent-type <type>", "Agent type", "assistant").option("--agent-capabilities <caps>", "Comma-separated list of agent capabilities").option("--json", "Output raw JSON response").action(async (name, opts) => {
|
|
6878
8245
|
const client = getIMClient2();
|
|
6879
8246
|
try {
|
|
@@ -6928,68 +8295,305 @@ function register8(parent, getIMClient2, _getAPIClient) {
|
|
|
6928
8295
|
}
|
|
6929
8296
|
process.stdout.write(`Group workspace initialized (workspaceId: ${res.data?.workspaceId})
|
|
6930
8297
|
`);
|
|
6931
|
-
} catch (err) {
|
|
6932
|
-
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
8298
|
+
} catch (err) {
|
|
8299
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
8300
|
+
`);
|
|
8301
|
+
process.exit(1);
|
|
8302
|
+
}
|
|
8303
|
+
});
|
|
8304
|
+
workspace.command("add-agent <workspace-id> <agent-id>").description("Add an agent to a workspace").option("--json", "Output raw JSON response").action(async (workspaceId, agentId, opts) => {
|
|
8305
|
+
const client = getIMClient2();
|
|
8306
|
+
try {
|
|
8307
|
+
const res = await client.im.workspace.addAgent(workspaceId, agentId);
|
|
8308
|
+
if (!res.ok) {
|
|
8309
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
8310
|
+
`);
|
|
8311
|
+
process.exit(1);
|
|
8312
|
+
}
|
|
8313
|
+
if (opts.json) {
|
|
8314
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
8315
|
+
return;
|
|
8316
|
+
}
|
|
8317
|
+
process.stdout.write(`Agent ${agentId} added to workspace ${workspaceId}.
|
|
8318
|
+
`);
|
|
8319
|
+
} catch (err) {
|
|
8320
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
8321
|
+
`);
|
|
8322
|
+
process.exit(1);
|
|
8323
|
+
}
|
|
8324
|
+
});
|
|
8325
|
+
workspace.command("agents <workspace-id>").description("List agents in a workspace").option("--json", "Output raw JSON response").action(async (workspaceId, opts) => {
|
|
8326
|
+
const client = getIMClient2();
|
|
8327
|
+
try {
|
|
8328
|
+
const res = await client.im.workspace.listAgents(workspaceId);
|
|
8329
|
+
if (!res.ok) {
|
|
8330
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
8331
|
+
`);
|
|
8332
|
+
process.exit(1);
|
|
8333
|
+
}
|
|
8334
|
+
const agents = res.data || [];
|
|
8335
|
+
if (opts.json) {
|
|
8336
|
+
process.stdout.write(JSON.stringify(agents, null, 2) + "\n");
|
|
8337
|
+
return;
|
|
8338
|
+
}
|
|
8339
|
+
if (agents.length === 0) {
|
|
8340
|
+
process.stdout.write("No agents in this workspace.\n");
|
|
8341
|
+
return;
|
|
8342
|
+
}
|
|
8343
|
+
process.stdout.write("Agent ID".padEnd(36) + "Type".padEnd(14) + "Name\n");
|
|
8344
|
+
for (const a of agents) {
|
|
8345
|
+
process.stdout.write(
|
|
8346
|
+
`${(a.agentId || a.id || "").padEnd(36)}${(a.agentType || "").padEnd(14)}${a.name || a.displayName || ""}
|
|
8347
|
+
`
|
|
8348
|
+
);
|
|
8349
|
+
}
|
|
8350
|
+
} catch (err) {
|
|
8351
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
8352
|
+
`);
|
|
8353
|
+
process.exit(1);
|
|
8354
|
+
}
|
|
8355
|
+
});
|
|
8356
|
+
const member = workspace.command("member").description("Workspace membership management (release201/16)");
|
|
8357
|
+
member.command("list <workspaceId>").description("List members of a workspace (any member can read)").option("--json", "Output JSON").action(async (workspaceId, opts) => {
|
|
8358
|
+
const client = getIMClient2();
|
|
8359
|
+
const res = await client.workspaces.members.list(workspaceId);
|
|
8360
|
+
if (!res.ok || !res.data) fail(res.error?.message || "list members failed");
|
|
8361
|
+
emitJson(res.data, opts.json === true, () => printMemberList(res.data));
|
|
8362
|
+
});
|
|
8363
|
+
member.command("add <workspaceId>").description("Add an existing user to a workspace as admin or member (owner only)").requiredOption("--user <imUserId>", "IM user id of the new member").option("--role <role>", "admin | member (default member)").option("--json", "Output JSON").action(async (workspaceId, opts) => {
|
|
8364
|
+
const role = parseAdminMemberRole(opts.role);
|
|
8365
|
+
const client = getIMClient2();
|
|
8366
|
+
const res = await client.workspaces.members.add(workspaceId, {
|
|
8367
|
+
memberImUserId: opts.user.trim(),
|
|
8368
|
+
role
|
|
8369
|
+
});
|
|
8370
|
+
if (!res.ok || !res.data) fail(res.error?.message || "add member failed");
|
|
8371
|
+
emitJson(res.data, opts.json === true, () => {
|
|
8372
|
+
const m = res.data;
|
|
8373
|
+
process.stdout.write(`Member added: ${m.memberImUserId} \u2192 ${m.role} (${m.id})
|
|
8374
|
+
`);
|
|
8375
|
+
});
|
|
8376
|
+
});
|
|
8377
|
+
member.command("update <workspaceId> <memberId>").description("Change a member role (owner only; owner row is immutable)").requiredOption("--role <role>", "admin | member").option("--json", "Output JSON").action(async (workspaceId, memberId, opts) => {
|
|
8378
|
+
const role = parseAdminMemberRole(opts.role);
|
|
8379
|
+
const client = getIMClient2();
|
|
8380
|
+
const res = await client.workspaces.members.update(workspaceId, memberId, { role });
|
|
8381
|
+
if (!res.ok || !res.data) fail(res.error?.message || "update member failed");
|
|
8382
|
+
emitJson(res.data, opts.json === true, () => {
|
|
8383
|
+
const m = res.data;
|
|
8384
|
+
process.stdout.write(`Member ${m.id} role \u2192 ${m.role}
|
|
8385
|
+
`);
|
|
8386
|
+
});
|
|
8387
|
+
});
|
|
8388
|
+
member.command("remove <workspaceId> <memberId>").description("Remove a member (owner only); cascades project memberships in the same workspace").option("--json", "Output JSON").action(async (workspaceId, memberId, opts) => {
|
|
8389
|
+
const client = getIMClient2();
|
|
8390
|
+
const res = await client.workspaces.members.remove(workspaceId, memberId);
|
|
8391
|
+
if (!res.ok || !res.data) fail(res.error?.message || "remove member failed");
|
|
8392
|
+
emitJson(res.data, opts.json === true, () => {
|
|
8393
|
+
const r = res.data;
|
|
8394
|
+
process.stdout.write(
|
|
8395
|
+
`Member removed: ${r.removed.memberImUserId} (${r.removed.id}); project memberships cascaded: ${r.projectMembershipsRemoved}
|
|
8396
|
+
`
|
|
8397
|
+
);
|
|
8398
|
+
});
|
|
8399
|
+
});
|
|
8400
|
+
}
|
|
8401
|
+
|
|
8402
|
+
// src/commands/project.ts
|
|
8403
|
+
function fail2(message) {
|
|
8404
|
+
process.stderr.write(`Error: ${message}
|
|
8405
|
+
`);
|
|
8406
|
+
process.exit(1);
|
|
8407
|
+
}
|
|
8408
|
+
function emit(data, json, lines) {
|
|
8409
|
+
if (json) {
|
|
8410
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
8411
|
+
return;
|
|
8412
|
+
}
|
|
8413
|
+
lines();
|
|
8414
|
+
}
|
|
8415
|
+
function parsePrincipal(raw) {
|
|
8416
|
+
const idx = raw.indexOf(":");
|
|
8417
|
+
if (idx <= 0) fail2("--principal must be `user:<id>` or `agent:<id>`");
|
|
8418
|
+
const kind = raw.slice(0, idx);
|
|
8419
|
+
const id = raw.slice(idx + 1).trim();
|
|
8420
|
+
if (kind !== "user" && kind !== "agent") fail2("principal kind must be user|agent");
|
|
8421
|
+
if (!id) fail2("principal id must be non-empty");
|
|
8422
|
+
return { kind, id };
|
|
8423
|
+
}
|
|
8424
|
+
function parseRole(raw) {
|
|
8425
|
+
if (raw === void 0) return void 0;
|
|
8426
|
+
if (raw === "owner" || raw === "contributor" || raw === "observer") return raw;
|
|
8427
|
+
fail2("--role must be owner|contributor|observer");
|
|
8428
|
+
}
|
|
8429
|
+
function printProjectList(items) {
|
|
8430
|
+
if (items.length === 0) {
|
|
8431
|
+
process.stdout.write("No projects found.\n");
|
|
8432
|
+
return;
|
|
8433
|
+
}
|
|
8434
|
+
process.stdout.write(
|
|
8435
|
+
"ID".padEnd(28) + "SLUG".padEnd(20) + "STATUS".padEnd(10) + "MEMBERS".padEnd(10) + "NAME\n"
|
|
8436
|
+
);
|
|
8437
|
+
for (const p of items) {
|
|
8438
|
+
process.stdout.write(
|
|
8439
|
+
`${p.id.padEnd(28)}${p.slug.padEnd(20)}${p.status.padEnd(10)}${String(p.memberCount).padEnd(10)}${p.name}
|
|
8440
|
+
`
|
|
8441
|
+
);
|
|
8442
|
+
}
|
|
8443
|
+
}
|
|
8444
|
+
function printProject(p, heading = "Project") {
|
|
8445
|
+
process.stdout.write(`${heading}: ${p.name}
|
|
8446
|
+
`);
|
|
8447
|
+
process.stdout.write(` id ${p.id}
|
|
8448
|
+
`);
|
|
8449
|
+
process.stdout.write(` slug ${p.slug}
|
|
8450
|
+
`);
|
|
8451
|
+
process.stdout.write(` workspaceId ${p.workspaceId}
|
|
8452
|
+
`);
|
|
8453
|
+
process.stdout.write(` status ${p.status}
|
|
8454
|
+
`);
|
|
8455
|
+
process.stdout.write(` owner ${p.ownerUserId}
|
|
8456
|
+
`);
|
|
8457
|
+
if ("memberCount" in p) process.stdout.write(` members ${p.memberCount}
|
|
6933
8458
|
`);
|
|
6934
|
-
|
|
8459
|
+
if (p.description) process.stdout.write(` description ${p.description}
|
|
8460
|
+
`);
|
|
8461
|
+
if (p.archivedAt) process.stdout.write(` archivedAt ${p.archivedAt}
|
|
8462
|
+
`);
|
|
8463
|
+
process.stdout.write(` createdAt ${p.createdAt}
|
|
8464
|
+
`);
|
|
8465
|
+
process.stdout.write(` updatedAt ${p.updatedAt}
|
|
8466
|
+
`);
|
|
8467
|
+
}
|
|
8468
|
+
function printMemberList2(items) {
|
|
8469
|
+
if (items.length === 0) {
|
|
8470
|
+
process.stdout.write("No members in this project.\n");
|
|
8471
|
+
return;
|
|
8472
|
+
}
|
|
8473
|
+
process.stdout.write(
|
|
8474
|
+
"ID".padEnd(28) + "KIND".padEnd(8) + "PRINCIPAL".padEnd(38) + "ROLE".padEnd(14) + "JOINED\n"
|
|
8475
|
+
);
|
|
8476
|
+
for (const m of items) {
|
|
8477
|
+
process.stdout.write(
|
|
8478
|
+
`${m.id.padEnd(28)}${m.principalKind.padEnd(8)}${m.principalId.padEnd(38)}${m.role.padEnd(14)}${m.joinedAt}
|
|
8479
|
+
`
|
|
8480
|
+
);
|
|
8481
|
+
}
|
|
8482
|
+
}
|
|
8483
|
+
function register12(parent, getIMClient2, _getAPIClient) {
|
|
8484
|
+
const project = parent.command("project").description("Project scope (release201/09) \u2014 CRUD + membership management");
|
|
8485
|
+
project.command("list").description("List projects in a workspace").requiredOption("--workspace <id>", "Workspace id").option("--status <status>", "Filter by status: active|archived").option("--search <q>", "Search name/slug substring").option("--limit <n>", "Page size (1-200)", (v) => Number(v)).option("--offset <n>", "Page offset", (v) => Number(v)).option("--json", "Output JSON").action(async (opts) => {
|
|
8486
|
+
if (opts.status && opts.status !== "active" && opts.status !== "archived") {
|
|
8487
|
+
fail2("--status must be active|archived");
|
|
6935
8488
|
}
|
|
8489
|
+
const client = getIMClient2();
|
|
8490
|
+
const res = await client.projects.list({
|
|
8491
|
+
workspaceId: opts.workspace,
|
|
8492
|
+
status: opts.status,
|
|
8493
|
+
search: opts.search,
|
|
8494
|
+
limit: opts.limit,
|
|
8495
|
+
offset: opts.offset
|
|
8496
|
+
});
|
|
8497
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "list failed");
|
|
8498
|
+
const data = res.data;
|
|
8499
|
+
emit(data, opts.json === true, () => {
|
|
8500
|
+
printProjectList(data.items);
|
|
8501
|
+
process.stdout.write(`
|
|
8502
|
+
Total: ${data.total}
|
|
8503
|
+
`);
|
|
8504
|
+
});
|
|
6936
8505
|
});
|
|
6937
|
-
|
|
8506
|
+
project.command("create").description("Create a project in a workspace").requiredOption("--workspace <id>", "Workspace id").requiredOption("--slug <slug>", "Project slug (1-64 chars, [a-z0-9-])").requiredOption("--name <name>", "Project display name").option("--description <text>", "Project description").option("--json", "Output JSON").action(async (opts) => {
|
|
6938
8507
|
const client = getIMClient2();
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
8508
|
+
const res = await client.projects.create({
|
|
8509
|
+
workspaceId: opts.workspace,
|
|
8510
|
+
slug: opts.slug,
|
|
8511
|
+
name: opts.name,
|
|
8512
|
+
description: opts.description ?? null
|
|
8513
|
+
});
|
|
8514
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "create failed");
|
|
8515
|
+
emit(res.data, opts.json === true, () => printProject(res.data, "Project created"));
|
|
8516
|
+
});
|
|
8517
|
+
project.command("show <projectId>").description("Show a project (with member count)").option("--json", "Output JSON").action(async (projectId, opts) => {
|
|
8518
|
+
const client = getIMClient2();
|
|
8519
|
+
const res = await client.projects.get(projectId);
|
|
8520
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "show failed");
|
|
8521
|
+
emit(res.data, opts.json === true, () => printProject(res.data));
|
|
8522
|
+
});
|
|
8523
|
+
project.command("update <projectId>").description("Update project name / description / status").option("--name <name>", "New display name").option("--description <text>", "New description (empty string to clear)").option("--archive", "Set status=archived").option("--unarchive", "Set status=active").option("--json", "Output JSON").action(async (projectId, opts) => {
|
|
8524
|
+
if (opts.archive && opts.unarchive) fail2("--archive and --unarchive are mutually exclusive");
|
|
8525
|
+
const patch = {};
|
|
8526
|
+
if (opts.name !== void 0) patch.name = opts.name;
|
|
8527
|
+
if (opts.description !== void 0) patch.description = opts.description === "" ? null : opts.description;
|
|
8528
|
+
if (opts.archive) patch.status = "archived";
|
|
8529
|
+
if (opts.unarchive) patch.status = "active";
|
|
8530
|
+
if (Object.keys(patch).length === 0) fail2("Nothing to update \u2014 pass --name / --description / --archive / --unarchive");
|
|
8531
|
+
const client = getIMClient2();
|
|
8532
|
+
const res = await client.projects.update(projectId, patch);
|
|
8533
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "update failed");
|
|
8534
|
+
emit(res.data, opts.json === true, () => printProject(res.data, "Project updated"));
|
|
8535
|
+
});
|
|
8536
|
+
project.command("delete <projectId>").description("Soft-delete a project (default cascade=archive). cascade=hard is reserved for v2.0.8+.").option("--cascade <mode>", "archive | null | hard", "archive").option("--json", "Output JSON").action(async (projectId, opts) => {
|
|
8537
|
+
const cascade = opts.cascade ?? "archive";
|
|
8538
|
+
if (cascade !== "archive" && cascade !== "null" && cascade !== "hard") {
|
|
8539
|
+
fail2("--cascade must be archive|null|hard");
|
|
8540
|
+
}
|
|
8541
|
+
const client = getIMClient2();
|
|
8542
|
+
const res = await client.projects.delete(projectId, { cascade });
|
|
8543
|
+
if (!res.ok) fail2(res.error?.message || "delete failed");
|
|
8544
|
+
emit(res.data, opts.json === true, () => {
|
|
8545
|
+
if (res.data) {
|
|
8546
|
+
printProject(res.data, "Project archived");
|
|
8547
|
+
} else {
|
|
8548
|
+
process.stdout.write(`Project ${projectId} archived.
|
|
6943
8549
|
`);
|
|
6944
|
-
process.exit(1);
|
|
6945
|
-
}
|
|
6946
|
-
if (opts.json) {
|
|
6947
|
-
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
6948
|
-
return;
|
|
6949
8550
|
}
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
8551
|
+
});
|
|
8552
|
+
});
|
|
8553
|
+
const members = project.command("members").description("Manage project memberships");
|
|
8554
|
+
members.command("list <projectId>").description("List members of a project").option("--json", "Output JSON").action(async (projectId, opts) => {
|
|
8555
|
+
const client = getIMClient2();
|
|
8556
|
+
const res = await client.projects.members.list(projectId);
|
|
8557
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "list members failed");
|
|
8558
|
+
emit(res.data, opts.json === true, () => printMemberList2(res.data));
|
|
8559
|
+
});
|
|
8560
|
+
members.command("add <projectId>").description("Add a user or agent to the project").requiredOption("--principal <p>", "Principal in `user:<id>` or `agent:<id>` form").option("--role <role>", "owner | contributor | observer (default contributor)").option("--json", "Output JSON").action(async (projectId, opts) => {
|
|
8561
|
+
const { kind, id } = parsePrincipal(opts.principal);
|
|
8562
|
+
const role = parseRole(opts.role);
|
|
8563
|
+
const client = getIMClient2();
|
|
8564
|
+
const res = await client.projects.members.add(projectId, { principalKind: kind, principalId: id, role });
|
|
8565
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "add member failed");
|
|
8566
|
+
emit(res.data, opts.json === true, () => {
|
|
8567
|
+
const m = res.data;
|
|
8568
|
+
process.stdout.write(`Member added: ${m.principalKind}:${m.principalId} \u2192 ${m.role} (${m.id})
|
|
6954
8569
|
`);
|
|
6955
|
-
|
|
6956
|
-
}
|
|
8570
|
+
});
|
|
6957
8571
|
});
|
|
6958
|
-
|
|
8572
|
+
members.command("update <projectId> <membershipId>").description("Change a member role").requiredOption("--role <role>", "owner | contributor | observer").option("--json", "Output JSON").action(async (projectId, membershipId, opts) => {
|
|
8573
|
+
const role = parseRole(opts.role);
|
|
8574
|
+
if (!role) fail2("--role is required");
|
|
6959
8575
|
const client = getIMClient2();
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
8576
|
+
const res = await client.projects.members.update(projectId, membershipId, { role });
|
|
8577
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "update member failed");
|
|
8578
|
+
emit(res.data, opts.json === true, () => {
|
|
8579
|
+
const m = res.data;
|
|
8580
|
+
process.stdout.write(`Member ${m.id} role \u2192 ${m.role}
|
|
6964
8581
|
`);
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
process.stdout.write("No agents in this workspace.\n");
|
|
6974
|
-
return;
|
|
6975
|
-
}
|
|
6976
|
-
process.stdout.write("Agent ID".padEnd(36) + "Type".padEnd(14) + "Name\n");
|
|
6977
|
-
for (const a of agents) {
|
|
6978
|
-
process.stdout.write(
|
|
6979
|
-
`${(a.agentId || a.id || "").padEnd(36)}${(a.agentType || "").padEnd(14)}${a.name || a.displayName || ""}
|
|
6980
|
-
`
|
|
6981
|
-
);
|
|
6982
|
-
}
|
|
6983
|
-
} catch (err) {
|
|
6984
|
-
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
8582
|
+
});
|
|
8583
|
+
});
|
|
8584
|
+
members.command("remove <projectId> <membershipId>").description("Remove a member from the project").option("--json", "Output JSON").action(async (projectId, membershipId, opts) => {
|
|
8585
|
+
const client = getIMClient2();
|
|
8586
|
+
const res = await client.projects.members.remove(projectId, membershipId);
|
|
8587
|
+
if (!res.ok) fail2(res.error?.message || "remove member failed");
|
|
8588
|
+
emit({ ok: true }, opts.json === true, () => {
|
|
8589
|
+
process.stdout.write(`Membership ${membershipId} removed.
|
|
6985
8590
|
`);
|
|
6986
|
-
|
|
6987
|
-
}
|
|
8591
|
+
});
|
|
6988
8592
|
});
|
|
6989
8593
|
}
|
|
6990
8594
|
|
|
6991
8595
|
// src/commands/security.ts
|
|
6992
|
-
function
|
|
8596
|
+
function register13(parent, getIMClient2, _getAPIClient) {
|
|
6993
8597
|
const security = parent.command("security").description("Per-conversation encryption and key management");
|
|
6994
8598
|
security.command("get <conversation-id>").description("Get security settings for a conversation").option("--json", "Output raw JSON response").action(async (convId, opts) => {
|
|
6995
8599
|
const client = getIMClient2();
|
|
@@ -7269,7 +8873,7 @@ function register9(parent, getIMClient2, _getAPIClient) {
|
|
|
7269
8873
|
}
|
|
7270
8874
|
|
|
7271
8875
|
// src/commands/community.ts
|
|
7272
|
-
var
|
|
8876
|
+
var import_node_fs2 = require("fs");
|
|
7273
8877
|
function printJson(res, opts) {
|
|
7274
8878
|
if (opts.json) {
|
|
7275
8879
|
console.log(JSON.stringify(res, null, 2));
|
|
@@ -7296,7 +8900,7 @@ _Next cursor:_ \`${d.nextCursor}\`
|
|
|
7296
8900
|
`;
|
|
7297
8901
|
return t;
|
|
7298
8902
|
}
|
|
7299
|
-
function
|
|
8903
|
+
function register14(parent, getIMClient2, _getAPIClient) {
|
|
7300
8904
|
const comm = parent.command("community").description("Evolution community forum \u2014 feed, ask, search, notify");
|
|
7301
8905
|
comm.command("feed").description("Browse posts (uses hub cache when fresh)").option("-b, --board <id>", "Board: showcase, genelab, helpdesk, ideas, changelog").option("-n, --limit <n>", "Max posts", "15").option("--json", "JSON output").action(async (opts) => {
|
|
7302
8906
|
const c = getIMClient2();
|
|
@@ -7315,7 +8919,7 @@ function register10(parent, getIMClient2, _getAPIClient) {
|
|
|
7315
8919
|
process.stdout.write(formatPostsMarkdown(res.data));
|
|
7316
8920
|
});
|
|
7317
8921
|
comm.command("ask").description("Post a helpdesk question").argument("<title>", "Title").argument("[body]", "Body (Markdown); omit if using --file").option("-f, --file <path>", "Read body from file").option("--tags <csv>", "Comma-separated tags").option("--json", "JSON output").action(async (title, body, opts) => {
|
|
7318
|
-
const content = opts.file ? (0,
|
|
8922
|
+
const content = opts.file ? (0, import_node_fs2.readFileSync)(opts.file, "utf8") : body || "(no body)";
|
|
7319
8923
|
const tags = opts.tags?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7320
8924
|
const c = getIMClient2();
|
|
7321
8925
|
const res = await c.im.community.ask(title, content, tags);
|
|
@@ -7397,8 +9001,8 @@ function register10(parent, getIMClient2, _getAPIClient) {
|
|
|
7397
9001
|
}
|
|
7398
9002
|
|
|
7399
9003
|
// src/commands/asset.ts
|
|
7400
|
-
var
|
|
7401
|
-
var
|
|
9004
|
+
var fs5 = __toESM(require("fs"));
|
|
9005
|
+
var path3 = __toESM(require("path"));
|
|
7402
9006
|
function resolveWorkspaceId(flag) {
|
|
7403
9007
|
if (flag) return flag;
|
|
7404
9008
|
if (typeof process !== "undefined" && process.env?.PRISMER_WORKSPACE_ID) {
|
|
@@ -7432,7 +9036,7 @@ function formatBytes(n) {
|
|
|
7432
9036
|
if (n == null) return "-";
|
|
7433
9037
|
return String(n);
|
|
7434
9038
|
}
|
|
7435
|
-
function
|
|
9039
|
+
function register15(parent, getIMClient2, _getAPIClient) {
|
|
7436
9040
|
const asset = parent.command("asset").description("Inspect and manage workspace assets (content-addressed)");
|
|
7437
9041
|
asset.command("list").description("List assets in a workspace").option("--workspace-id <id>", "workspace id (defaults to PRISMER_WORKSPACE_ID env)").option("--filename <glob>", 'filter by filename glob (client-side, e.g. "design-doc*")').option("--mime <type>", 'filter by MIME type prefix (e.g. "application/pdf")').option("--kind <kind>", "filter by row-level kind (e.g. file, image, sandbox-output)").option("--updated-after <iso>", "filter to assets created/updated after this ISO timestamp").option("--task-id <id>", "filter by source task id").option("-n, --limit <n>", "maximum results to return", "50").option("--json", "output raw JSON response").action(async (opts) => {
|
|
7438
9042
|
const wsId = requireWorkspaceId(opts.workspaceId);
|
|
@@ -7592,7 +9196,7 @@ ${rows.length} asset(s) listed.
|
|
|
7592
9196
|
});
|
|
7593
9197
|
asset.command("upload <path>").description("Upload a local file as a workspace asset").option("--workspace-id <id>", "workspace id (defaults to PRISMER_WORKSPACE_ID env)").option("--conversation-id <id>", "pin upload to a conversation (stored in metadata)").option("--kind <kind>", "asset kind label", "user-upload").option("--task-id <id>", "source task id").option("--mime <type>", "override detected MIME type").option("--filename <name>", "override filename").option("--json", "output raw JSON response").action(async (filePath, opts) => {
|
|
7594
9198
|
const wsId = requireWorkspaceId(opts.workspaceId);
|
|
7595
|
-
if (!
|
|
9199
|
+
if (!fs5.existsSync(filePath)) {
|
|
7596
9200
|
process.stderr.write(`Error: file not found: ${filePath}
|
|
7597
9201
|
`);
|
|
7598
9202
|
process.exit(1);
|
|
@@ -7622,7 +9226,7 @@ ${rows.length} asset(s) listed.
|
|
|
7622
9226
|
process.exit(1);
|
|
7623
9227
|
}
|
|
7624
9228
|
const a = res.data;
|
|
7625
|
-
process.stdout.write(`Uploaded ${
|
|
9229
|
+
process.stdout.write(`Uploaded ${path3.basename(filePath)}
|
|
7626
9230
|
`);
|
|
7627
9231
|
process.stdout.write(` ID: ${a.id}
|
|
7628
9232
|
`);
|
|
@@ -7649,7 +9253,7 @@ ${rows.length} asset(s) listed.
|
|
|
7649
9253
|
opts.length
|
|
7650
9254
|
);
|
|
7651
9255
|
if (opts.out) {
|
|
7652
|
-
|
|
9256
|
+
fs5.writeFileSync(opts.out, bytes);
|
|
7653
9257
|
const range = describeRange(opts.offset, opts.length, bytes.byteLength);
|
|
7654
9258
|
process.stderr.write(
|
|
7655
9259
|
`Downloaded ${assetId} -> ${opts.out} (${bytes.byteLength} bytes${range ? `, ${range}` : ""}${truncated ? ", truncated" : ""}${totalSize != null ? `, total=${totalSize}` : ""})
|
|
@@ -7797,7 +9401,7 @@ function resolveWorkspaceId2(flag) {
|
|
|
7797
9401
|
}
|
|
7798
9402
|
return void 0;
|
|
7799
9403
|
}
|
|
7800
|
-
function
|
|
9404
|
+
function register16(parent, getIMClient2, _getAPIClient) {
|
|
7801
9405
|
const approval = parent.command("approval").description("Submit and manage human approval requests");
|
|
7802
9406
|
approval.command("request-human").description("Submit a human approval request and stop the current turn").requiredOption("--action <text>", "one-sentence summary of the gated action").requiredOption("--context <text>", "multi-sentence framing for the human").requiredOption("--risk <text>", "what breaks if approved wrongly; what is reversible").option("--options <opt...>", "explicit choice values (defaults to approve/reject if omitted)").option("--task-id <id>", "task to resume when the human decides").option("--conversation-id <id>", "conversation context for the request").option("--workspace-id <id>", "workspace id (defaults to PRISMER_WORKSPACE_ID env)").option("--category <category>", "approval category label", "human-approval").option("--expires-in <seconds>", "expiration window in seconds (default 24h)", parseIntOpt2).option("--json", "output raw JSON response").action(async (opts) => {
|
|
7803
9407
|
if (!opts.conversationId && !opts.taskId) {
|
|
@@ -7875,7 +9479,69 @@ function parseIntOpt2(value) {
|
|
|
7875
9479
|
}
|
|
7876
9480
|
|
|
7877
9481
|
// src/commands/agent.ts
|
|
7878
|
-
|
|
9482
|
+
var import_node_crypto2 = require("crypto");
|
|
9483
|
+
var import_node_fs3 = require("fs");
|
|
9484
|
+
var import_node_os = require("os");
|
|
9485
|
+
var import_node_path2 = require("path");
|
|
9486
|
+
var import_node_child_process = require("child_process");
|
|
9487
|
+
function resolvePrismerRoot() {
|
|
9488
|
+
return process.env.PRISMER_HOME ? (0, import_node_path2.resolve)(process.env.PRISMER_HOME) : (0, import_node_path2.join)((0, import_node_os.homedir)(), ".prismer");
|
|
9489
|
+
}
|
|
9490
|
+
function readDaemonConfig() {
|
|
9491
|
+
const path6 = (0, import_node_path2.join)(resolvePrismerRoot(), "config.toml");
|
|
9492
|
+
if (!(0, import_node_fs3.existsSync)(path6)) {
|
|
9493
|
+
throw new Error(`${path6} not found. Run \`prismer setup\` first.`);
|
|
9494
|
+
}
|
|
9495
|
+
const raw = (0, import_node_fs3.readFileSync)(path6, "utf8");
|
|
9496
|
+
const idMatch = raw.match(/^daemon_id\s*=\s*"([^"]+)"/m);
|
|
9497
|
+
const keyMatch = raw.match(/^api_key\s*=\s*"([^"]+)"/m);
|
|
9498
|
+
if (!idMatch || !keyMatch) {
|
|
9499
|
+
throw new Error(`config.toml missing daemon_id or api_key (${path6})`);
|
|
9500
|
+
}
|
|
9501
|
+
return { daemonId: idMatch[1], apiKey: keyMatch[1] };
|
|
9502
|
+
}
|
|
9503
|
+
function resolveAgentDir(daemonId, agentId) {
|
|
9504
|
+
return (0, import_node_path2.join)(resolvePrismerRoot(), "devices", daemonId, "agents", agentId);
|
|
9505
|
+
}
|
|
9506
|
+
function collectFiles(root, excludeBasenames) {
|
|
9507
|
+
const out = [];
|
|
9508
|
+
function walk2(dir) {
|
|
9509
|
+
let entries;
|
|
9510
|
+
try {
|
|
9511
|
+
entries = (0, import_node_fs3.readdirSync)(dir);
|
|
9512
|
+
} catch {
|
|
9513
|
+
return;
|
|
9514
|
+
}
|
|
9515
|
+
for (const name of entries) {
|
|
9516
|
+
if (excludeBasenames.has(name)) continue;
|
|
9517
|
+
const full = (0, import_node_path2.join)(dir, name);
|
|
9518
|
+
let st;
|
|
9519
|
+
try {
|
|
9520
|
+
st = (0, import_node_fs3.statSync)(full);
|
|
9521
|
+
} catch {
|
|
9522
|
+
continue;
|
|
9523
|
+
}
|
|
9524
|
+
if (st.isDirectory()) walk2(full);
|
|
9525
|
+
else if (st.isFile()) {
|
|
9526
|
+
const buf = (0, import_node_fs3.readFileSync)(full);
|
|
9527
|
+
const sha = (0, import_node_crypto2.createHash)("sha256").update(buf).digest("hex");
|
|
9528
|
+
out.push({
|
|
9529
|
+
rel: (0, import_node_path2.relative)(root, full).split(import_node_path2.sep).join("/"),
|
|
9530
|
+
sha,
|
|
9531
|
+
size: buf.byteLength
|
|
9532
|
+
});
|
|
9533
|
+
}
|
|
9534
|
+
}
|
|
9535
|
+
}
|
|
9536
|
+
walk2(root);
|
|
9537
|
+
out.sort((a, b) => a.rel.localeCompare(b.rel));
|
|
9538
|
+
return out;
|
|
9539
|
+
}
|
|
9540
|
+
function computeMerkle(files) {
|
|
9541
|
+
const lines = files.map((f) => `${f.rel}:${f.sha}`).join("\n");
|
|
9542
|
+
return (0, import_node_crypto2.createHash)("sha256").update(lines).digest("hex");
|
|
9543
|
+
}
|
|
9544
|
+
function register17(parent, getIMClient2, _getAPIClient) {
|
|
7879
9545
|
const agent = parent.command("agent").description("Manage agent specs, snapshots, publish, and fork");
|
|
7880
9546
|
agent.command("spec <agent-id>").description("Read an AgentSpec 4-tuple").option("--workspace-id <id>", "Workspace scope").option("--json", "Output raw JSON response").action(async (agentId, opts) => {
|
|
7881
9547
|
const res = await getIMClient2().im.agents.spec(agentId, opts.workspaceId);
|
|
@@ -7954,6 +9620,244 @@ function register13(parent, getIMClient2, _getAPIClient) {
|
|
|
7954
9620
|
}
|
|
7955
9621
|
});
|
|
7956
9622
|
});
|
|
9623
|
+
agent.command("export <agent-id>").description("release201/09 \xA79.7 \u2014 quiesce + tar an agent directory for transfer").requiredOption("--output <file>", "Output tar.gz path").option("--force", "Skip cloud pause (use when source device is offline)").option("--json", "Output raw JSON result").action(async (agentId, opts) => {
|
|
9624
|
+
const { daemonId, apiKey } = readDaemonConfig();
|
|
9625
|
+
const agentDir = resolveAgentDir(daemonId, agentId);
|
|
9626
|
+
if (!(0, import_node_fs3.existsSync)(agentDir)) {
|
|
9627
|
+
process.stderr.write(`Error: agent dir not found at ${agentDir}
|
|
9628
|
+
`);
|
|
9629
|
+
process.exit(1);
|
|
9630
|
+
}
|
|
9631
|
+
if (!opts.force) {
|
|
9632
|
+
const res = await getIMClient2().im.agents.pause(agentId);
|
|
9633
|
+
if (!res.ok) {
|
|
9634
|
+
process.stderr.write(
|
|
9635
|
+
`Error: pause failed (${res.error?.message ?? "unknown"}); pass --force to export anyway
|
|
9636
|
+
`
|
|
9637
|
+
);
|
|
9638
|
+
process.exit(1);
|
|
9639
|
+
}
|
|
9640
|
+
}
|
|
9641
|
+
const excludeBasenames = /* @__PURE__ */ new Set(["transfer-manifest.json"]);
|
|
9642
|
+
const files = collectFiles(agentDir, excludeBasenames);
|
|
9643
|
+
const sha256 = computeMerkle(files);
|
|
9644
|
+
const signature = (0, import_node_crypto2.createHmac)("sha256", apiKey).update(sha256).digest("hex");
|
|
9645
|
+
const manifest = {
|
|
9646
|
+
version: 1,
|
|
9647
|
+
agentId,
|
|
9648
|
+
fromDaemonId: daemonId,
|
|
9649
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9650
|
+
includes: ["profile.json", "skills/**", "memory/**", "outbox/*"],
|
|
9651
|
+
excludes: ["cache/ (daemon-level blob pool)", "local.db (daemon-level)"],
|
|
9652
|
+
sha256,
|
|
9653
|
+
signature
|
|
9654
|
+
};
|
|
9655
|
+
(0, import_node_fs3.writeFileSync)((0, import_node_path2.join)(agentDir, "transfer-manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
9656
|
+
const parentOfAgents = (0, import_node_path2.join)(resolvePrismerRoot(), "devices", daemonId);
|
|
9657
|
+
const outputAbs = (0, import_node_path2.resolve)(opts.output);
|
|
9658
|
+
try {
|
|
9659
|
+
(0, import_node_child_process.execFileSync)("tar", ["-czf", outputAbs, "-C", parentOfAgents, (0, import_node_path2.join)("agents", agentId)], { stdio: "inherit" });
|
|
9660
|
+
} catch (err) {
|
|
9661
|
+
process.stderr.write(`Error: tar failed: ${err.message}
|
|
9662
|
+
`);
|
|
9663
|
+
process.exit(1);
|
|
9664
|
+
}
|
|
9665
|
+
const result = {
|
|
9666
|
+
agentId,
|
|
9667
|
+
fromDaemonId: daemonId,
|
|
9668
|
+
output: outputAbs,
|
|
9669
|
+
manifestSha256: sha256,
|
|
9670
|
+
filesIncluded: files.length,
|
|
9671
|
+
totalBytes: files.reduce((s, f) => s + f.size, 0),
|
|
9672
|
+
paused: !opts.force
|
|
9673
|
+
};
|
|
9674
|
+
if (opts.json) {
|
|
9675
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
9676
|
+
} else {
|
|
9677
|
+
console.log(`Exported agent ${agentId}`);
|
|
9678
|
+
console.log(` output: ${result.output}`);
|
|
9679
|
+
console.log(` files: ${result.filesIncluded}`);
|
|
9680
|
+
console.log(` total bytes: ${result.totalBytes}`);
|
|
9681
|
+
console.log(` manifestSha: ${result.manifestSha256.slice(0, 16)}\u2026`);
|
|
9682
|
+
console.log(` cloud paused: ${result.paused ? "yes" : "no (--force)"}`);
|
|
9683
|
+
console.log("");
|
|
9684
|
+
console.log("Next: copy the tar.gz to the target device and run:");
|
|
9685
|
+
console.log(` prismer agent import ${result.output}`);
|
|
9686
|
+
}
|
|
9687
|
+
});
|
|
9688
|
+
agent.command("import <tar-file>").description("release201/09 \xA79.7 \u2014 extract + rebind an agent transferred from another device").option("--json", "Output raw JSON result").action(async (tarFile, opts) => {
|
|
9689
|
+
const tarAbs = (0, import_node_path2.resolve)(tarFile);
|
|
9690
|
+
if (!(0, import_node_fs3.existsSync)(tarAbs)) {
|
|
9691
|
+
process.stderr.write(`Error: tar file not found: ${tarAbs}
|
|
9692
|
+
`);
|
|
9693
|
+
process.exit(1);
|
|
9694
|
+
}
|
|
9695
|
+
const { daemonId: toDaemonId, apiKey } = readDaemonConfig();
|
|
9696
|
+
const tmpRoot = (0, import_node_path2.join)(resolvePrismerRoot(), ".transfer-staging", `${Date.now()}`);
|
|
9697
|
+
(0, import_node_fs3.mkdirSync)(tmpRoot, { recursive: true });
|
|
9698
|
+
try {
|
|
9699
|
+
(0, import_node_child_process.execFileSync)("tar", ["-xzf", tarAbs, "-C", tmpRoot], { stdio: "inherit" });
|
|
9700
|
+
} catch (err) {
|
|
9701
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9702
|
+
process.stderr.write(`Error: tar extract failed: ${err.message}
|
|
9703
|
+
`);
|
|
9704
|
+
process.exit(1);
|
|
9705
|
+
}
|
|
9706
|
+
const agentsRoot = (0, import_node_path2.join)(tmpRoot, "agents");
|
|
9707
|
+
if (!(0, import_node_fs3.existsSync)(agentsRoot)) {
|
|
9708
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9709
|
+
process.stderr.write("Error: tar layout invalid \u2014 no agents/ root inside archive\n");
|
|
9710
|
+
process.exit(1);
|
|
9711
|
+
}
|
|
9712
|
+
const entries = (0, import_node_fs3.readdirSync)(agentsRoot);
|
|
9713
|
+
if (entries.length !== 1) {
|
|
9714
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9715
|
+
process.stderr.write(`Error: expected exactly 1 agent in archive, got ${entries.length}
|
|
9716
|
+
`);
|
|
9717
|
+
process.exit(1);
|
|
9718
|
+
}
|
|
9719
|
+
const agentId = entries[0];
|
|
9720
|
+
const stagedAgentDir = (0, import_node_path2.join)(agentsRoot, agentId);
|
|
9721
|
+
const manifestPath = (0, import_node_path2.join)(stagedAgentDir, "transfer-manifest.json");
|
|
9722
|
+
if (!(0, import_node_fs3.existsSync)(manifestPath)) {
|
|
9723
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9724
|
+
process.stderr.write("Error: transfer-manifest.json missing from archive\n");
|
|
9725
|
+
process.exit(1);
|
|
9726
|
+
}
|
|
9727
|
+
let manifest;
|
|
9728
|
+
try {
|
|
9729
|
+
manifest = JSON.parse((0, import_node_fs3.readFileSync)(manifestPath, "utf8"));
|
|
9730
|
+
} catch (err) {
|
|
9731
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9732
|
+
process.stderr.write(`Error: manifest parse failed: ${err.message}
|
|
9733
|
+
`);
|
|
9734
|
+
process.exit(1);
|
|
9735
|
+
}
|
|
9736
|
+
const expectedSig = (0, import_node_crypto2.createHmac)("sha256", apiKey).update(manifest.sha256).digest("hex");
|
|
9737
|
+
if (expectedSig !== manifest.signature) {
|
|
9738
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9739
|
+
process.stderr.write(
|
|
9740
|
+
"Error: manifest signature verification failed \u2014 refusing to import (different workspace, or tampered archive)\n"
|
|
9741
|
+
);
|
|
9742
|
+
process.exit(1);
|
|
9743
|
+
}
|
|
9744
|
+
if (manifest.agentId !== agentId) {
|
|
9745
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9746
|
+
process.stderr.write(
|
|
9747
|
+
`Error: manifest agentId (${manifest.agentId}) != extracted dir name (${agentId})
|
|
9748
|
+
`
|
|
9749
|
+
);
|
|
9750
|
+
process.exit(1);
|
|
9751
|
+
}
|
|
9752
|
+
if (manifest.fromDaemonId === toDaemonId) {
|
|
9753
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9754
|
+
process.stderr.write(
|
|
9755
|
+
`Error: fromDaemonId == toDaemonId (${toDaemonId}); nothing to transfer.
|
|
9756
|
+
`
|
|
9757
|
+
);
|
|
9758
|
+
process.exit(1);
|
|
9759
|
+
}
|
|
9760
|
+
const targetAgentDir = resolveAgentDir(toDaemonId, agentId);
|
|
9761
|
+
if ((0, import_node_fs3.existsSync)(targetAgentDir)) {
|
|
9762
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9763
|
+
process.stderr.write(
|
|
9764
|
+
`Error: target dir already exists at ${targetAgentDir}. Run \`prismer agent remove ${agentId}\` (TODO) or move it aside first.
|
|
9765
|
+
`
|
|
9766
|
+
);
|
|
9767
|
+
process.exit(1);
|
|
9768
|
+
}
|
|
9769
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path2.join)(resolvePrismerRoot(), "devices", toDaemonId, "agents"), { recursive: true });
|
|
9770
|
+
try {
|
|
9771
|
+
(0, import_node_child_process.execFileSync)("mv", [stagedAgentDir, targetAgentDir]);
|
|
9772
|
+
} catch {
|
|
9773
|
+
try {
|
|
9774
|
+
(0, import_node_child_process.execFileSync)("cp", ["-R", stagedAgentDir, targetAgentDir]);
|
|
9775
|
+
(0, import_node_fs3.rmSync)(stagedAgentDir, { recursive: true, force: true });
|
|
9776
|
+
} catch (err) {
|
|
9777
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9778
|
+
process.stderr.write(`Error: failed to install agent dir: ${err.message}
|
|
9779
|
+
`);
|
|
9780
|
+
process.exit(1);
|
|
9781
|
+
}
|
|
9782
|
+
}
|
|
9783
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9784
|
+
const rebind = await getIMClient2().im.agents.transfer({
|
|
9785
|
+
agentId,
|
|
9786
|
+
fromDaemonId: manifest.fromDaemonId,
|
|
9787
|
+
toDaemonId,
|
|
9788
|
+
manifestSha256: manifest.sha256
|
|
9789
|
+
});
|
|
9790
|
+
if (!rebind.ok || !rebind.data) {
|
|
9791
|
+
process.stderr.write(
|
|
9792
|
+
`Warning: cloud rebind failed (${rebind.error?.message ?? "unknown"}). Agent files are on disk at ${targetAgentDir} but cloud still routes to ${manifest.fromDaemonId}.
|
|
9793
|
+
Manual fix: POST /api/im/agent-bindings/transfer or rebind via the Devices UI.
|
|
9794
|
+
`
|
|
9795
|
+
);
|
|
9796
|
+
process.exit(2);
|
|
9797
|
+
}
|
|
9798
|
+
await getIMClient2().im.agents.resume(agentId);
|
|
9799
|
+
const result = {
|
|
9800
|
+
agentId,
|
|
9801
|
+
fromDaemonId: manifest.fromDaemonId,
|
|
9802
|
+
toDaemonId,
|
|
9803
|
+
targetDir: targetAgentDir,
|
|
9804
|
+
manifestSha256: manifest.sha256,
|
|
9805
|
+
boundDaemonId: rebind.data.boundDaemonId,
|
|
9806
|
+
boundBy: rebind.data.boundBy
|
|
9807
|
+
};
|
|
9808
|
+
if (opts.json) {
|
|
9809
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
9810
|
+
} else {
|
|
9811
|
+
console.log(`Imported agent ${agentId}`);
|
|
9812
|
+
console.log(` from device: ${result.fromDaemonId}`);
|
|
9813
|
+
console.log(` to device: ${result.toDaemonId}`);
|
|
9814
|
+
console.log(` target dir: ${result.targetDir}`);
|
|
9815
|
+
console.log(` cloud bound: ${result.boundDaemonId} (${result.boundBy})`);
|
|
9816
|
+
}
|
|
9817
|
+
});
|
|
9818
|
+
agent.command("cleanup-orphan").description("release201/09 \xA79.7.3 \u2014 list devices/*/agents/* dirs whose cloud binding points elsewhere").option("--delete", "Actually remove orphan dirs (default: dry-run)").option("--json", "Output raw JSON list").action(async (opts) => {
|
|
9819
|
+
const { daemonId } = readDaemonConfig();
|
|
9820
|
+
const devicesRoot = (0, import_node_path2.join)(resolvePrismerRoot(), "devices");
|
|
9821
|
+
if (!(0, import_node_fs3.existsSync)(devicesRoot)) {
|
|
9822
|
+
if (opts.json) process.stdout.write("[]\n");
|
|
9823
|
+
else console.log("No devices/ dir yet.");
|
|
9824
|
+
return;
|
|
9825
|
+
}
|
|
9826
|
+
const orphans = [];
|
|
9827
|
+
for (const did of (0, import_node_fs3.readdirSync)(devicesRoot)) {
|
|
9828
|
+
if (did === daemonId) continue;
|
|
9829
|
+
const agentsRoot = (0, import_node_path2.join)(devicesRoot, did, "agents");
|
|
9830
|
+
if (!(0, import_node_fs3.existsSync)(agentsRoot)) continue;
|
|
9831
|
+
for (const aid of (0, import_node_fs3.readdirSync)(agentsRoot)) {
|
|
9832
|
+
const dir = (0, import_node_path2.join)(agentsRoot, aid);
|
|
9833
|
+
try {
|
|
9834
|
+
const st = (0, import_node_fs3.statSync)(dir);
|
|
9835
|
+
if (!st.isDirectory()) continue;
|
|
9836
|
+
const files = collectFiles(dir, /* @__PURE__ */ new Set());
|
|
9837
|
+
const sizeBytes = files.reduce((s, f) => s + f.size, 0);
|
|
9838
|
+
orphans.push({ daemonId: did, agentId: aid, dir, sizeBytes });
|
|
9839
|
+
} catch {
|
|
9840
|
+
}
|
|
9841
|
+
}
|
|
9842
|
+
}
|
|
9843
|
+
if (opts.delete) {
|
|
9844
|
+
for (const o of orphans) {
|
|
9845
|
+
(0, import_node_fs3.rmSync)(o.dir, { recursive: true, force: true });
|
|
9846
|
+
}
|
|
9847
|
+
}
|
|
9848
|
+
if (opts.json) {
|
|
9849
|
+
process.stdout.write(JSON.stringify({ orphans, deleted: Boolean(opts.delete) }, null, 2) + "\n");
|
|
9850
|
+
} else {
|
|
9851
|
+
if (orphans.length === 0) {
|
|
9852
|
+
console.log("No orphan agent dirs.");
|
|
9853
|
+
} else {
|
|
9854
|
+
console.log(`Found ${orphans.length} orphan agent dir(s)${opts.delete ? " (DELETED)" : " (dry-run; pass --delete to remove)"}:`);
|
|
9855
|
+
for (const o of orphans) {
|
|
9856
|
+
console.log(` ${o.daemonId}/${o.agentId} ${o.sizeBytes} bytes ${o.dir}`);
|
|
9857
|
+
}
|
|
9858
|
+
}
|
|
9859
|
+
}
|
|
9860
|
+
});
|
|
7957
9861
|
agent.command("fork <pack-id-or-slug>").description("Fork an Agent Pack into a workspace").requiredOption("--workspace-id <id>", "Target workspace id").option("--display-name <name>", "New agent display name").option("--json", "Output raw JSON response").action(async (packId, opts) => {
|
|
7958
9862
|
const res = await getIMClient2().im.agents.forkPack(packId, {
|
|
7959
9863
|
targetWorkspaceId: opts.workspaceId,
|
|
@@ -7979,32 +9883,168 @@ function printOrExit(res, json, print) {
|
|
|
7979
9883
|
print(res.data);
|
|
7980
9884
|
}
|
|
7981
9885
|
|
|
9886
|
+
// src/commands/metric.ts
|
|
9887
|
+
var AGG_FUNCS = ["sum", "count", "avg", "min", "max", "p50", "p95", "p99"];
|
|
9888
|
+
var BUCKETS = ["5m", "1h", "1d"];
|
|
9889
|
+
function splitFqName(fqName) {
|
|
9890
|
+
const i = fqName.lastIndexOf(".");
|
|
9891
|
+
if (i <= 0 || i === fqName.length - 1) {
|
|
9892
|
+
throw new Error(`metric name must be in form "namespace.name" (got "${fqName}")`);
|
|
9893
|
+
}
|
|
9894
|
+
return { namespace: fqName.slice(0, i), name: fqName.slice(i + 1) };
|
|
9895
|
+
}
|
|
9896
|
+
function parseDimFlags(dimArr) {
|
|
9897
|
+
const dims = {};
|
|
9898
|
+
for (const raw of dimArr ?? []) {
|
|
9899
|
+
const i = raw.indexOf("=");
|
|
9900
|
+
if (i <= 0) throw new Error(`--dim "${raw}" must be in form key=value`);
|
|
9901
|
+
const key = raw.slice(0, i);
|
|
9902
|
+
const value = raw.slice(i + 1);
|
|
9903
|
+
if (/^-?\d+(?:\.\d+)?$/.test(value)) dims[key] = Number(value);
|
|
9904
|
+
else if (value === "true" || value === "false") dims[key] = value === "true";
|
|
9905
|
+
else dims[key] = value;
|
|
9906
|
+
}
|
|
9907
|
+
return dims;
|
|
9908
|
+
}
|
|
9909
|
+
async function runEmit(fqName, opts, getIMClient2) {
|
|
9910
|
+
const client = getIMClient2();
|
|
9911
|
+
const { namespace, name } = splitFqName(fqName);
|
|
9912
|
+
const dims = parseDimFlags(opts.dim);
|
|
9913
|
+
if (!dims.workspaceId) {
|
|
9914
|
+
throw new Error("--dim workspaceId=<id> is required (server rejects emits without it)");
|
|
9915
|
+
}
|
|
9916
|
+
let value;
|
|
9917
|
+
if (opts.value !== void 0) {
|
|
9918
|
+
value = /^-?\d+(?:\.\d+)?$/.test(opts.value) ? Number(opts.value) : opts.value;
|
|
9919
|
+
}
|
|
9920
|
+
const input = {
|
|
9921
|
+
namespace,
|
|
9922
|
+
name,
|
|
9923
|
+
ts: opts.ts,
|
|
9924
|
+
value,
|
|
9925
|
+
dims
|
|
9926
|
+
};
|
|
9927
|
+
const res = await client.im.metrics.emit(input);
|
|
9928
|
+
if (opts.json) {
|
|
9929
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
9930
|
+
return;
|
|
9931
|
+
}
|
|
9932
|
+
if (!res.ok) {
|
|
9933
|
+
process.stderr.write(`Error: ${res.error?.message ?? "unknown error"}
|
|
9934
|
+
`);
|
|
9935
|
+
process.exit(1);
|
|
9936
|
+
}
|
|
9937
|
+
process.stdout.write(`emitted ${namespace}.${name}
|
|
9938
|
+
`);
|
|
9939
|
+
}
|
|
9940
|
+
async function runAgg(fqName, opts, getIMClient2) {
|
|
9941
|
+
const client = getIMClient2();
|
|
9942
|
+
const { namespace, name } = splitFqName(fqName);
|
|
9943
|
+
if (!AGG_FUNCS.includes(opts.agg)) {
|
|
9944
|
+
throw new Error(`--agg must be one of ${AGG_FUNCS.join("|")}`);
|
|
9945
|
+
}
|
|
9946
|
+
if (opts.bucket && !BUCKETS.includes(opts.bucket)) {
|
|
9947
|
+
throw new Error(`--bucket must be one of ${BUCKETS.join("|")}`);
|
|
9948
|
+
}
|
|
9949
|
+
const filter = {};
|
|
9950
|
+
for (const raw of (opts.filter ?? "").split(",").filter(Boolean)) {
|
|
9951
|
+
const i = raw.indexOf(":");
|
|
9952
|
+
if (i <= 0) throw new Error(`--filter "${raw}" must be in form key:value`);
|
|
9953
|
+
filter[raw.slice(0, i)] = raw.slice(i + 1);
|
|
9954
|
+
}
|
|
9955
|
+
if (!filter.workspaceId) {
|
|
9956
|
+
throw new Error("--filter must include workspaceId:<id> (cross-workspace queries are admin-only)");
|
|
9957
|
+
}
|
|
9958
|
+
const groupBy = opts.groupBy ? opts.groupBy.split(",").filter(Boolean) : void 0;
|
|
9959
|
+
const res = await client.im.metrics.aggregate({
|
|
9960
|
+
namespace,
|
|
9961
|
+
name,
|
|
9962
|
+
agg: opts.agg,
|
|
9963
|
+
range: opts.range,
|
|
9964
|
+
from: opts.from,
|
|
9965
|
+
to: opts.to,
|
|
9966
|
+
groupBy,
|
|
9967
|
+
filter,
|
|
9968
|
+
bucket: opts.bucket
|
|
9969
|
+
});
|
|
9970
|
+
if (opts.json) {
|
|
9971
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
9972
|
+
return;
|
|
9973
|
+
}
|
|
9974
|
+
if (!res.ok) {
|
|
9975
|
+
process.stderr.write(`Error: ${res.error?.message ?? "unknown error"}
|
|
9976
|
+
`);
|
|
9977
|
+
process.exit(1);
|
|
9978
|
+
}
|
|
9979
|
+
const data = res.data;
|
|
9980
|
+
if (!data) {
|
|
9981
|
+
process.stdout.write("(no data)\n");
|
|
9982
|
+
return;
|
|
9983
|
+
}
|
|
9984
|
+
process.stdout.write(
|
|
9985
|
+
`${data.namespace}.${data.name} ${data.agg} [${data.range.from} \u2192 ${data.range.to}]
|
|
9986
|
+
`
|
|
9987
|
+
);
|
|
9988
|
+
for (const bucket of data.buckets) {
|
|
9989
|
+
const tsLabel = bucket.ts ? `${bucket.ts}` : "(all)";
|
|
9990
|
+
for (const g of bucket.groups) {
|
|
9991
|
+
const keyLabel = Object.entries(g.groupKey).map(([k, v]) => `${k}=${v ?? "\u2205"}`).join(" ");
|
|
9992
|
+
process.stdout.write(` ${tsLabel} ${keyLabel || "(no group)"} \u2192 ${g.value ?? "\u2205"}
|
|
9993
|
+
`);
|
|
9994
|
+
}
|
|
9995
|
+
}
|
|
9996
|
+
}
|
|
9997
|
+
function register18(parent, getIMClient2, _getAPIClient) {
|
|
9998
|
+
const metric = parent.command("metric").description("Emit metric events and query aggregations (release201/11)");
|
|
9999
|
+
metric.command("emit <namespace.name>").description("Emit a single metric event").option("--value <value>", "metric value (number or string)").option("--dim <k=v>", "dimension (repeatable; workspaceId is required)", (val, prev = []) => {
|
|
10000
|
+
prev.push(val);
|
|
10001
|
+
return prev;
|
|
10002
|
+
}).option("--ts <iso>", "business timestamp in ISO 8601 (defaults to now)").option("--json", "output raw JSON response").action(async (fqName, opts) => {
|
|
10003
|
+
try {
|
|
10004
|
+
await runEmit(fqName, opts, getIMClient2);
|
|
10005
|
+
} catch (err) {
|
|
10006
|
+
process.stderr.write(`Error: ${err.message}
|
|
10007
|
+
`);
|
|
10008
|
+
process.exit(1);
|
|
10009
|
+
}
|
|
10010
|
+
});
|
|
10011
|
+
metric.command("agg <namespace.name>").description("Aggregate metric events (release201/11 \xA75)").requiredOption("--agg <fn>", `one of ${AGG_FUNCS.join("|")}`).option("--range <Nh|Nd>", "lookback window (e.g. 24h, 7d)").option("--from <iso>", "window start (ISO, paired with --to)").option("--to <iso>", "window end (ISO, paired with --from)").option("--groupBy <k1,k2>", "csv of dim keys to group by").option("--filter <k1:v1,k2:v2>", "csv k:v filters (workspaceId is required)").option("--bucket <5m|1h|1d>", "timeseries bucket size").option("--json", "output raw JSON response").action(async (fqName, opts) => {
|
|
10012
|
+
try {
|
|
10013
|
+
await runAgg(fqName, opts, getIMClient2);
|
|
10014
|
+
} catch (err) {
|
|
10015
|
+
process.stderr.write(`Error: ${err.message}
|
|
10016
|
+
`);
|
|
10017
|
+
process.exit(1);
|
|
10018
|
+
}
|
|
10019
|
+
});
|
|
10020
|
+
}
|
|
10021
|
+
|
|
7982
10022
|
// src/daemon.ts
|
|
7983
|
-
var
|
|
7984
|
-
var
|
|
10023
|
+
var fs6 = __toESM(require("fs"));
|
|
10024
|
+
var path4 = __toESM(require("path"));
|
|
7985
10025
|
var import_path = require("path");
|
|
7986
|
-
var
|
|
10026
|
+
var os2 = __toESM(require("os"));
|
|
7987
10027
|
var import_os = require("os");
|
|
7988
10028
|
var http = __toESM(require("http"));
|
|
7989
10029
|
var import_http = require("http");
|
|
7990
10030
|
var import_child_process = require("child_process");
|
|
7991
10031
|
var TOML = __toESM(require("@iarna/toml"));
|
|
7992
|
-
var CONFIG_DIR =
|
|
7993
|
-
var CONFIG_PATH =
|
|
7994
|
-
var PID_PATH =
|
|
7995
|
-
var PORT_PATH =
|
|
7996
|
-
var CACHE_DIR =
|
|
7997
|
-
var EVOLUTION_CACHE_PATH =
|
|
7998
|
-
var OUTBOX_PATH =
|
|
10032
|
+
var CONFIG_DIR = path4.join(os2.homedir(), ".prismer");
|
|
10033
|
+
var CONFIG_PATH = path4.join(CONFIG_DIR, "config.toml");
|
|
10034
|
+
var PID_PATH = path4.join(CONFIG_DIR, "daemon.pid");
|
|
10035
|
+
var PORT_PATH = path4.join(CONFIG_DIR, "daemon.port");
|
|
10036
|
+
var CACHE_DIR = path4.join(CONFIG_DIR, "cache");
|
|
10037
|
+
var EVOLUTION_CACHE_PATH = path4.join(CACHE_DIR, "evolution.json");
|
|
10038
|
+
var OUTBOX_PATH = path4.join(CACHE_DIR, "outbox.json");
|
|
7999
10039
|
var SYNC_INTERVAL_MS = 6e4;
|
|
8000
10040
|
var FLUSH_INTERVAL_MS = 3e4;
|
|
8001
10041
|
var API_TIMEOUT_MS = 1e4;
|
|
8002
10042
|
var EVENTS_FILE = (0, import_path.join)(CACHE_DIR, "events.json");
|
|
8003
10043
|
var MAX_EVENTS = 1e3;
|
|
8004
10044
|
function loadConfig() {
|
|
8005
|
-
if (!
|
|
10045
|
+
if (!fs6.existsSync(CONFIG_PATH)) return null;
|
|
8006
10046
|
try {
|
|
8007
|
-
const raw =
|
|
10047
|
+
const raw = fs6.readFileSync(CONFIG_PATH, "utf-8");
|
|
8008
10048
|
const parsed = TOML.parse(raw);
|
|
8009
10049
|
const apiKey = parsed?.default?.api_key || "";
|
|
8010
10050
|
const baseUrl = parsed?.default?.base_url || "https://prismer.cloud";
|
|
@@ -8015,13 +10055,13 @@ function loadConfig() {
|
|
|
8015
10055
|
}
|
|
8016
10056
|
}
|
|
8017
10057
|
function ensureCacheDir() {
|
|
8018
|
-
if (!
|
|
8019
|
-
|
|
10058
|
+
if (!fs6.existsSync(CACHE_DIR)) {
|
|
10059
|
+
fs6.mkdirSync(CACHE_DIR, { recursive: true });
|
|
8020
10060
|
}
|
|
8021
10061
|
}
|
|
8022
10062
|
function loadEvents() {
|
|
8023
10063
|
try {
|
|
8024
|
-
return JSON.parse(
|
|
10064
|
+
return JSON.parse(fs6.readFileSync(EVENTS_FILE, "utf-8"));
|
|
8025
10065
|
} catch {
|
|
8026
10066
|
return [];
|
|
8027
10067
|
}
|
|
@@ -8030,7 +10070,7 @@ function appendEvent(event) {
|
|
|
8030
10070
|
const events = loadEvents();
|
|
8031
10071
|
events.push(event);
|
|
8032
10072
|
if (events.length > MAX_EVENTS) events.splice(0, events.length - MAX_EVENTS);
|
|
8033
|
-
|
|
10073
|
+
fs6.writeFileSync(EVENTS_FILE, JSON.stringify(events), { encoding: "utf-8", mode: 384 });
|
|
8034
10074
|
}
|
|
8035
10075
|
function emitSyncEvent(genesCount) {
|
|
8036
10076
|
if (genesCount > 0) {
|
|
@@ -8045,9 +10085,9 @@ function emitSyncEvent(genesCount) {
|
|
|
8045
10085
|
}
|
|
8046
10086
|
}
|
|
8047
10087
|
function readPid() {
|
|
8048
|
-
if (!
|
|
10088
|
+
if (!fs6.existsSync(PID_PATH)) return null;
|
|
8049
10089
|
try {
|
|
8050
|
-
const raw =
|
|
10090
|
+
const raw = fs6.readFileSync(PID_PATH, "utf-8").trim();
|
|
8051
10091
|
const pid = parseInt(raw, 10);
|
|
8052
10092
|
return isNaN(pid) ? null : pid;
|
|
8053
10093
|
} catch {
|
|
@@ -8055,9 +10095,9 @@ function readPid() {
|
|
|
8055
10095
|
}
|
|
8056
10096
|
}
|
|
8057
10097
|
function readPort() {
|
|
8058
|
-
if (!
|
|
10098
|
+
if (!fs6.existsSync(PORT_PATH)) return null;
|
|
8059
10099
|
try {
|
|
8060
|
-
const raw =
|
|
10100
|
+
const raw = fs6.readFileSync(PORT_PATH, "utf-8").trim();
|
|
8061
10101
|
const port = parseInt(raw, 10);
|
|
8062
10102
|
return isNaN(port) ? null : port;
|
|
8063
10103
|
} catch {
|
|
@@ -8074,28 +10114,28 @@ function isProcessRunning(pid) {
|
|
|
8074
10114
|
}
|
|
8075
10115
|
function writePid(pid) {
|
|
8076
10116
|
ensureCacheDir();
|
|
8077
|
-
if (!
|
|
8078
|
-
|
|
10117
|
+
if (!fs6.existsSync(CONFIG_DIR)) {
|
|
10118
|
+
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
8079
10119
|
}
|
|
8080
|
-
|
|
10120
|
+
fs6.writeFileSync(PID_PATH, String(pid), { encoding: "utf-8", mode: 384 });
|
|
8081
10121
|
}
|
|
8082
10122
|
function writePort(port) {
|
|
8083
|
-
if (!
|
|
8084
|
-
|
|
10123
|
+
if (!fs6.existsSync(CONFIG_DIR)) {
|
|
10124
|
+
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
8085
10125
|
}
|
|
8086
|
-
|
|
10126
|
+
fs6.writeFileSync(PORT_PATH, String(port), { encoding: "utf-8", mode: 384 });
|
|
8087
10127
|
}
|
|
8088
10128
|
function cleanupPidFiles() {
|
|
8089
10129
|
try {
|
|
8090
|
-
if (
|
|
10130
|
+
if (fs6.existsSync(PID_PATH)) fs6.unlinkSync(PID_PATH);
|
|
8091
10131
|
} catch {
|
|
8092
10132
|
}
|
|
8093
10133
|
try {
|
|
8094
|
-
if (
|
|
10134
|
+
if (fs6.existsSync(PORT_PATH)) fs6.unlinkSync(PORT_PATH);
|
|
8095
10135
|
} catch {
|
|
8096
10136
|
}
|
|
8097
10137
|
}
|
|
8098
|
-
async function
|
|
10138
|
+
async function fetchWithTimeout2(url, options, timeoutMs = API_TIMEOUT_MS) {
|
|
8099
10139
|
const controller = new AbortController();
|
|
8100
10140
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
8101
10141
|
try {
|
|
@@ -8114,9 +10154,9 @@ async function runDaemonProcess() {
|
|
|
8114
10154
|
let lastSync = 0;
|
|
8115
10155
|
let syncCount = 0;
|
|
8116
10156
|
let evolutionCursor = 0;
|
|
8117
|
-
if (
|
|
10157
|
+
if (fs6.existsSync(EVOLUTION_CACHE_PATH)) {
|
|
8118
10158
|
try {
|
|
8119
|
-
const cached = JSON.parse(
|
|
10159
|
+
const cached = JSON.parse(fs6.readFileSync(EVOLUTION_CACHE_PATH, "utf-8"));
|
|
8120
10160
|
if (typeof cached?.cursor === "number") evolutionCursor = cached.cursor;
|
|
8121
10161
|
} catch {
|
|
8122
10162
|
}
|
|
@@ -8124,9 +10164,9 @@ async function runDaemonProcess() {
|
|
|
8124
10164
|
const server = (0, import_http.createServer)((req, res) => {
|
|
8125
10165
|
if (req.method === "GET" && req.url === "/health") {
|
|
8126
10166
|
let outboxSize = 0;
|
|
8127
|
-
if (
|
|
10167
|
+
if (fs6.existsSync(OUTBOX_PATH)) {
|
|
8128
10168
|
try {
|
|
8129
|
-
const entries = JSON.parse(
|
|
10169
|
+
const entries = JSON.parse(fs6.readFileSync(OUTBOX_PATH, "utf-8"));
|
|
8130
10170
|
if (Array.isArray(entries)) outboxSize = entries.length;
|
|
8131
10171
|
} catch {
|
|
8132
10172
|
}
|
|
@@ -8167,7 +10207,7 @@ async function runDaemonProcess() {
|
|
|
8167
10207
|
process.on("SIGTERM", shutdown);
|
|
8168
10208
|
const doEvolutionSync = async () => {
|
|
8169
10209
|
try {
|
|
8170
|
-
const res = await
|
|
10210
|
+
const res = await fetchWithTimeout2(
|
|
8171
10211
|
`${cfg.baseUrl}/api/im/evolution/sync`,
|
|
8172
10212
|
{
|
|
8173
10213
|
method: "POST",
|
|
@@ -8189,7 +10229,7 @@ async function runDaemonProcess() {
|
|
|
8189
10229
|
}
|
|
8190
10230
|
ensureCacheDir();
|
|
8191
10231
|
const pulled = data?.data || data;
|
|
8192
|
-
|
|
10232
|
+
fs6.writeFileSync(
|
|
8193
10233
|
EVOLUTION_CACHE_PATH,
|
|
8194
10234
|
JSON.stringify({ cursor: evolutionCursor, lastSync, data: pulled }, null, 2),
|
|
8195
10235
|
{ encoding: "utf-8", mode: 384 }
|
|
@@ -8200,16 +10240,16 @@ async function runDaemonProcess() {
|
|
|
8200
10240
|
}
|
|
8201
10241
|
};
|
|
8202
10242
|
const doOutboxFlush = async () => {
|
|
8203
|
-
if (!
|
|
10243
|
+
if (!fs6.existsSync(OUTBOX_PATH)) return;
|
|
8204
10244
|
let entries = [];
|
|
8205
10245
|
try {
|
|
8206
|
-
entries = JSON.parse(
|
|
10246
|
+
entries = JSON.parse(fs6.readFileSync(OUTBOX_PATH, "utf-8"));
|
|
8207
10247
|
if (!Array.isArray(entries) || entries.length === 0) return;
|
|
8208
10248
|
} catch {
|
|
8209
10249
|
return;
|
|
8210
10250
|
}
|
|
8211
10251
|
try {
|
|
8212
|
-
const res = await
|
|
10252
|
+
const res = await fetchWithTimeout2(
|
|
8213
10253
|
`${cfg.baseUrl}/api/im/evolution/sync`,
|
|
8214
10254
|
{
|
|
8215
10255
|
method: "POST",
|
|
@@ -8224,7 +10264,7 @@ async function runDaemonProcess() {
|
|
|
8224
10264
|
}
|
|
8225
10265
|
);
|
|
8226
10266
|
if (res.ok) {
|
|
8227
|
-
|
|
10267
|
+
fs6.writeFileSync(OUTBOX_PATH, "[]", { encoding: "utf-8", mode: 384 });
|
|
8228
10268
|
}
|
|
8229
10269
|
} catch {
|
|
8230
10270
|
}
|
|
@@ -8342,7 +10382,7 @@ function resolveNpxPath() {
|
|
|
8342
10382
|
} catch {
|
|
8343
10383
|
for (const p of ["/usr/local/bin/npx", "/opt/homebrew/bin/npx", `${(0, import_os.homedir)()}/.nvm/current/bin/npx`]) {
|
|
8344
10384
|
try {
|
|
8345
|
-
|
|
10385
|
+
fs6.accessSync(p);
|
|
8346
10386
|
return p;
|
|
8347
10387
|
} catch {
|
|
8348
10388
|
}
|
|
@@ -8384,8 +10424,8 @@ function installLaunchd() {
|
|
|
8384
10424
|
<string>${(0, import_path.join)((0, import_os.homedir)(), ".prismer", "daemon.stderr.log")}</string>
|
|
8385
10425
|
</dict>
|
|
8386
10426
|
</plist>`;
|
|
8387
|
-
|
|
8388
|
-
|
|
10427
|
+
fs6.mkdirSync((0, import_path.dirname)(plistPath), { recursive: true });
|
|
10428
|
+
fs6.writeFileSync(plistPath, plist, { mode: 384 });
|
|
8389
10429
|
try {
|
|
8390
10430
|
(0, import_child_process.execSync)(`launchctl load ${plistPath}`, { stdio: "pipe" });
|
|
8391
10431
|
console.log("[prismer] Daemon service installed and started (launchd)");
|
|
@@ -8401,7 +10441,7 @@ function uninstallLaunchd() {
|
|
|
8401
10441
|
} catch {
|
|
8402
10442
|
}
|
|
8403
10443
|
try {
|
|
8404
|
-
|
|
10444
|
+
fs6.unlinkSync(plistPath);
|
|
8405
10445
|
} catch {
|
|
8406
10446
|
}
|
|
8407
10447
|
console.log("[prismer] Daemon service uninstalled (launchd)");
|
|
@@ -8426,8 +10466,8 @@ RestartSec=10
|
|
|
8426
10466
|
[Install]
|
|
8427
10467
|
WantedBy=default.target
|
|
8428
10468
|
`;
|
|
8429
|
-
|
|
8430
|
-
|
|
10469
|
+
fs6.mkdirSync(serviceDir, { recursive: true });
|
|
10470
|
+
fs6.writeFileSync(servicePath, unit, { mode: 420 });
|
|
8431
10471
|
try {
|
|
8432
10472
|
(0, import_child_process.execSync)("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
8433
10473
|
(0, import_child_process.execSync)("systemctl --user enable prismer-daemon", { stdio: "pipe" });
|
|
@@ -8450,7 +10490,7 @@ function uninstallSystemd() {
|
|
|
8450
10490
|
}
|
|
8451
10491
|
const servicePath = (0, import_path.join)((0, import_os.homedir)(), ".config", "systemd", "user", "prismer-daemon.service");
|
|
8452
10492
|
try {
|
|
8453
|
-
|
|
10493
|
+
fs6.unlinkSync(servicePath);
|
|
8454
10494
|
} catch {
|
|
8455
10495
|
}
|
|
8456
10496
|
try {
|
|
@@ -8490,21 +10530,21 @@ if (process.env["PRISMER_DAEMON"] === "1") {
|
|
|
8490
10530
|
// src/cli.ts
|
|
8491
10531
|
var cliVersion = "1.7.2";
|
|
8492
10532
|
try {
|
|
8493
|
-
const pkgPath =
|
|
8494
|
-
const pkg = JSON.parse(
|
|
10533
|
+
const pkgPath = path5.join(__dirname, "..", "package.json");
|
|
10534
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf8"));
|
|
8495
10535
|
cliVersion = pkg.version || cliVersion;
|
|
8496
10536
|
} catch {
|
|
8497
10537
|
}
|
|
8498
|
-
var CONFIG_DIR2 = process.env.PRISMER_HOME ?
|
|
8499
|
-
var CONFIG_PATH2 =
|
|
10538
|
+
var CONFIG_DIR2 = process.env.PRISMER_HOME ? path5.resolve(process.env.PRISMER_HOME) : path5.join(os3.homedir(), ".prismer");
|
|
10539
|
+
var CONFIG_PATH2 = path5.join(CONFIG_DIR2, "config.toml");
|
|
8500
10540
|
function ensureConfigDir() {
|
|
8501
|
-
if (!
|
|
8502
|
-
|
|
10541
|
+
if (!fs7.existsSync(CONFIG_DIR2)) {
|
|
10542
|
+
fs7.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
8503
10543
|
}
|
|
8504
10544
|
}
|
|
8505
10545
|
function readConfig() {
|
|
8506
|
-
if (!
|
|
8507
|
-
const raw =
|
|
10546
|
+
if (!fs7.existsSync(CONFIG_PATH2)) return {};
|
|
10547
|
+
const raw = fs7.readFileSync(CONFIG_PATH2, "utf-8");
|
|
8508
10548
|
const parsed = TOML2.parse(raw);
|
|
8509
10549
|
const flatApiKey = parsed.api_key;
|
|
8510
10550
|
const flatBaseUrl = parsed.cloud_api_base ?? parsed.base_url;
|
|
@@ -8522,7 +10562,7 @@ function readConfig() {
|
|
|
8522
10562
|
}
|
|
8523
10563
|
function writeConfig(config) {
|
|
8524
10564
|
ensureConfigDir();
|
|
8525
|
-
|
|
10565
|
+
fs7.writeFileSync(CONFIG_PATH2, TOML2.stringify(config), { encoding: "utf-8", mode: 384 });
|
|
8526
10566
|
}
|
|
8527
10567
|
function setNestedValue(obj, dotPath, value) {
|
|
8528
10568
|
const parts = dotPath.split(".");
|
|
@@ -8685,8 +10725,8 @@ async function runSetup(opts, apiKey) {
|
|
|
8685
10725
|
return;
|
|
8686
10726
|
}
|
|
8687
10727
|
const http2 = require("http");
|
|
8688
|
-
const
|
|
8689
|
-
const state =
|
|
10728
|
+
const crypto3 = require("crypto");
|
|
10729
|
+
const state = crypto3.randomBytes(16).toString("hex");
|
|
8690
10730
|
let resolved = false;
|
|
8691
10731
|
const server = http2.createServer((req, res) => {
|
|
8692
10732
|
const url = new URL(req.url, `http://localhost`);
|
|
@@ -8855,11 +10895,11 @@ program.command("status").description("Show current config and live info").actio
|
|
|
8855
10895
|
});
|
|
8856
10896
|
var configCmd = program.command("config").description("Manage config file");
|
|
8857
10897
|
configCmd.command("show").description("Print config file").action(() => {
|
|
8858
|
-
if (!
|
|
10898
|
+
if (!fs7.existsSync(CONFIG_PATH2)) {
|
|
8859
10899
|
warn('No config file. Run "cloud setup" to create one.');
|
|
8860
10900
|
return;
|
|
8861
10901
|
}
|
|
8862
|
-
console.log(
|
|
10902
|
+
console.log(fs7.readFileSync(CONFIG_PATH2, "utf-8"));
|
|
8863
10903
|
});
|
|
8864
10904
|
configCmd.command("set <key> <value>").description("Set a config value (e.g. default.base_url)").action((key, value) => {
|
|
8865
10905
|
const config = readConfig();
|
|
@@ -8904,6 +10944,11 @@ register10(program, getIMClient, getAPIClient);
|
|
|
8904
10944
|
register11(program, getIMClient, getAPIClient);
|
|
8905
10945
|
register12(program, getIMClient, getAPIClient);
|
|
8906
10946
|
register13(program, getIMClient, getAPIClient);
|
|
10947
|
+
register14(program, getIMClient, getAPIClient);
|
|
10948
|
+
register15(program, getIMClient, getAPIClient);
|
|
10949
|
+
register16(program, getIMClient, getAPIClient);
|
|
10950
|
+
register17(program, getIMClient, getAPIClient);
|
|
10951
|
+
register18(program, getIMClient, getAPIClient);
|
|
8907
10952
|
program.command("send").description("Send a direct message (shortcut for: im send)").argument("<user-id-or-username>", "Target user/agent IM user ID (or username with --by-username)").argument("<message>", "Message content").option("-t, --type <type>", "Message type: text, markdown, code, etc.", "text").option("--reply-to <id>", "Reply to a message ID").option("--conversation-id <id>", "Pin message to a specific conversation/session").option("--asset-id <id>", "Attach a previously uploaded asset (treats type as file)").option("--by-username", "Treat the first argument as a username; resolve to imUserId first").option("--json", "JSON output").action(async (target, message, opts) => {
|
|
8908
10953
|
const client = getIMClient();
|
|
8909
10954
|
let userId = target;
|
|
@@ -8952,6 +10997,17 @@ program.command("send").description("Send a direct message (shortcut for: im sen
|
|
|
8952
10997
|
}
|
|
8953
10998
|
success(`Message sent (conversation: ${res.data?.conversationId})`);
|
|
8954
10999
|
});
|
|
11000
|
+
program.command("emit <namespace.name>").description("Emit a metric event (shortcut for: metric emit)").option("--value <value>", "metric value (number or string)").option("--dim <k=v>", "dimension (repeatable; workspaceId is required)", (val, prev = []) => {
|
|
11001
|
+
prev.push(val);
|
|
11002
|
+
return prev;
|
|
11003
|
+
}).option("--ts <iso>", "business timestamp in ISO 8601 (defaults to now)").option("--json", "output raw JSON response").action(async (fqName, opts) => {
|
|
11004
|
+
try {
|
|
11005
|
+
await runEmit(fqName, opts, getIMClient);
|
|
11006
|
+
} catch (err) {
|
|
11007
|
+
errorLine(`Error: ${err.message}`);
|
|
11008
|
+
process.exit(1);
|
|
11009
|
+
}
|
|
11010
|
+
});
|
|
8955
11011
|
program.command("load").description("Load URL(s) \u2192 compressed HQCC (shortcut for: context load)").argument("<urls...>", "One or more URLs").option("-f, --format <fmt>", "Return format: hqcc, raw, both", "hqcc").option("--json", "JSON output").action(async (urls, opts) => {
|
|
8956
11012
|
const client = getAPIClient();
|
|
8957
11013
|
const input = urls.length === 1 ? urls[0] : urls;
|