@prismer/sdk 2.0.6 → 2.0.8
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 +2595 -297
- package/dist/index.d.mts +724 -2
- package/dist/index.d.ts +724 -2
- package/dist/index.js +397 -1
- package/dist/index.mjs +385 -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((resolve6, 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
|
+
resolve6();
|
|
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((resolve6, 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: resolve6, 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);
|
|
@@ -3552,6 +3868,19 @@ var FilesClient = class {
|
|
|
3552
3868
|
*/
|
|
3553
3869
|
async sendFile(conversationId, input, opts) {
|
|
3554
3870
|
const uploaded = await this.upload(input, opts);
|
|
3871
|
+
const outboxDir = process.env.PRISMER_OUTBOX_DIR;
|
|
3872
|
+
if (outboxDir) {
|
|
3873
|
+
try {
|
|
3874
|
+
const fsMod = await import("fs");
|
|
3875
|
+
const pathMod = await import("path");
|
|
3876
|
+
const srcPath = typeof input === "string" ? input : input.path;
|
|
3877
|
+
if (srcPath && typeof srcPath === "string") {
|
|
3878
|
+
await fsMod.promises.mkdir(outboxDir, { recursive: true });
|
|
3879
|
+
fsMod.cpSync(srcPath, pathMod.join(outboxDir, pathMod.basename(srcPath)));
|
|
3880
|
+
}
|
|
3881
|
+
} catch {
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3555
3884
|
const msgRes = await this._r("POST", `/api/im/messages/${conversationId}`, {
|
|
3556
3885
|
content: opts?.content || uploaded.fileName,
|
|
3557
3886
|
type: "file",
|
|
@@ -3760,6 +4089,36 @@ var IMRealtimeClient = class {
|
|
|
3760
4089
|
};
|
|
3761
4090
|
}
|
|
3762
4091
|
};
|
|
4092
|
+
var SkillsDraftClient = class {
|
|
4093
|
+
constructor(_r) {
|
|
4094
|
+
this._r = _r;
|
|
4095
|
+
}
|
|
4096
|
+
/** Create a draft skill from a manifest v1 payload. */
|
|
4097
|
+
async create(input) {
|
|
4098
|
+
return this._r("POST", "/api/im/skills/draft", input);
|
|
4099
|
+
}
|
|
4100
|
+
/** Apply incremental file ops to a draft. */
|
|
4101
|
+
async patch(id, input) {
|
|
4102
|
+
return this._r("PATCH", `/api/im/skills/${id}/draft`, input);
|
|
4103
|
+
}
|
|
4104
|
+
/** Request a regenerate session for a draft. */
|
|
4105
|
+
async regenerate(id, input) {
|
|
4106
|
+
return this._r("POST", `/api/im/skills/${id}/draft/regenerate`, input);
|
|
4107
|
+
}
|
|
4108
|
+
/** Fetch a single draft's manifest + revision history. */
|
|
4109
|
+
async show(id) {
|
|
4110
|
+
return this._r("GET", `/api/im/skills/draft/${id}`);
|
|
4111
|
+
}
|
|
4112
|
+
/** List drafts for a workspace (most-recently-updated first). */
|
|
4113
|
+
async list(workspaceId) {
|
|
4114
|
+
return this._r("GET", "/api/im/skills/drafts", void 0, { workspaceId });
|
|
4115
|
+
}
|
|
4116
|
+
};
|
|
4117
|
+
var SkillsClient = class {
|
|
4118
|
+
constructor(_r) {
|
|
4119
|
+
this.draft = new SkillsDraftClient(_r);
|
|
4120
|
+
}
|
|
4121
|
+
};
|
|
3763
4122
|
var IMClient = class {
|
|
3764
4123
|
constructor(request2, wsBase, fetchFn, getAuthHeaders, offlineManager, communityHubConfig) {
|
|
3765
4124
|
this._request = request2;
|
|
@@ -3773,16 +4132,22 @@ var IMClient = class {
|
|
|
3773
4132
|
this.credits = new CreditsClient(request2);
|
|
3774
4133
|
this.workspace = new WorkspaceClient(request2);
|
|
3775
4134
|
this.workspaces = new WorkspacesClient(request2);
|
|
4135
|
+
this.invites = new InvitesClient(request2);
|
|
4136
|
+
this.projects = new ProjectsClient(request2);
|
|
3776
4137
|
this.workspaceFiles = new WorkspaceFilesClient(request2);
|
|
3777
4138
|
this.assets = new AssetsClient(request2, wsBase, fetchFn, getAuthHeaders);
|
|
3778
4139
|
this.runtimeInstallations = new RuntimeInstallationsClient(request2);
|
|
3779
4140
|
this.tasks = new TasksClient(request2);
|
|
4141
|
+
this.criteriaTemplates = new CriteriaTemplatesClient(request2);
|
|
3780
4142
|
this.memory = new MemoryClient(request2);
|
|
3781
4143
|
this.knowledge = new KnowledgeLinkClient(request2);
|
|
4144
|
+
this.metrics = new MetricsClient(request2);
|
|
3782
4145
|
this.identity = new IdentityClient(request2);
|
|
3783
4146
|
this.security = new SecurityClient(request2);
|
|
3784
4147
|
this.agents = new AgentsClient(request2);
|
|
3785
4148
|
this.evolution = new EvolutionClient(request2);
|
|
4149
|
+
this.skills = new SkillsClient(request2);
|
|
4150
|
+
this.studio = new StudioClient(request2);
|
|
3786
4151
|
this.community = new CommunityHub(request2, communityHubConfig ?? void 0);
|
|
3787
4152
|
this.files = new FilesClient(request2, wsBase, fetchFn, getAuthHeaders);
|
|
3788
4153
|
this.realtime = new IMRealtimeClient(wsBase, fetchFn);
|
|
@@ -3812,8 +4177,8 @@ var IMClient = class {
|
|
|
3812
4177
|
* 'POST', '/api/im/approvals', { category, title, context, options },
|
|
3813
4178
|
* );
|
|
3814
4179
|
*/
|
|
3815
|
-
async request(method,
|
|
3816
|
-
return this._request(method,
|
|
4180
|
+
async request(method, path6, body, query) {
|
|
4181
|
+
return this._request(method, path6, body, query);
|
|
3817
4182
|
}
|
|
3818
4183
|
};
|
|
3819
4184
|
var PrismerClient = class {
|
|
@@ -3832,6 +4197,7 @@ var PrismerClient = class {
|
|
|
3832
4197
|
this.timeout = config.timeout || 3e4;
|
|
3833
4198
|
this.fetchFn = config.fetch || fetch;
|
|
3834
4199
|
this.imAgent = config.imAgent;
|
|
4200
|
+
this.imWorkspace = config.imWorkspace;
|
|
3835
4201
|
if (config.identity) {
|
|
3836
4202
|
if (config.identity === "auto" && this.apiKey) {
|
|
3837
4203
|
this._identityReady = import_aip_sdk.AIPIdentity.fromApiKey(this.apiKey).then((id) => {
|
|
@@ -3865,20 +4231,20 @@ var PrismerClient = class {
|
|
|
3865
4231
|
) : (m, p, b, q, opts) => this._request(m, p, b, q, opts);
|
|
3866
4232
|
if (config.identity) {
|
|
3867
4233
|
const baseRequest = imRequest;
|
|
3868
|
-
imRequest = (method,
|
|
3869
|
-
if (method === "POST" &&
|
|
4234
|
+
imRequest = (method, path6, body, query, opts) => {
|
|
4235
|
+
if (method === "POST" && path6.includes("/messages") && body) {
|
|
3870
4236
|
const b = body;
|
|
3871
4237
|
if (!b.signature && !b.skipSigning) {
|
|
3872
4238
|
const ready = this._identityReady || Promise.resolve();
|
|
3873
4239
|
return ready.then(() => {
|
|
3874
4240
|
if (this._identity) {
|
|
3875
|
-
return this._signAndSend(baseRequest, method,
|
|
4241
|
+
return this._signAndSend(baseRequest, method, path6, b, query, opts);
|
|
3876
4242
|
}
|
|
3877
|
-
return baseRequest(method,
|
|
4243
|
+
return baseRequest(method, path6, body, query, opts);
|
|
3878
4244
|
});
|
|
3879
4245
|
}
|
|
3880
4246
|
}
|
|
3881
|
-
return baseRequest(method,
|
|
4247
|
+
return baseRequest(method, path6, body, query, opts);
|
|
3882
4248
|
};
|
|
3883
4249
|
}
|
|
3884
4250
|
this.im = new IMClient(
|
|
@@ -3890,6 +4256,8 @@ var PrismerClient = class {
|
|
|
3890
4256
|
config.community ?? null
|
|
3891
4257
|
);
|
|
3892
4258
|
this.workspaces = this.im.workspaces;
|
|
4259
|
+
this.invites = this.im.invites;
|
|
4260
|
+
this.projects = this.im.projects;
|
|
3893
4261
|
this.workspaceFiles = this.im.workspaceFiles;
|
|
3894
4262
|
this.assets = this.im.assets;
|
|
3895
4263
|
this.evolution = this.im.evolution;
|
|
@@ -3900,9 +4268,9 @@ var PrismerClient = class {
|
|
|
3900
4268
|
return this._identity;
|
|
3901
4269
|
}
|
|
3902
4270
|
/** Auto-sign a message body and send (v1.8.0 S1) */
|
|
3903
|
-
async _signAndSend(baseRequest, method,
|
|
4271
|
+
async _signAndSend(baseRequest, method, path6, body, query, opts) {
|
|
3904
4272
|
if (this._identityReady) await this._identityReady;
|
|
3905
|
-
if (!this._identity) return baseRequest(method,
|
|
4273
|
+
if (!this._identity) return baseRequest(method, path6, body, query, opts);
|
|
3906
4274
|
const content = body.content || "";
|
|
3907
4275
|
const contentHashBytes = new Uint8Array(
|
|
3908
4276
|
await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content))
|
|
@@ -3912,7 +4280,7 @@ var PrismerClient = class {
|
|
|
3912
4280
|
const payload = `1|${this._identity.did}|${body.type || "text"}|${timestamp}|${contentHash}`;
|
|
3913
4281
|
const payloadBytes = new TextEncoder().encode(payload);
|
|
3914
4282
|
const signature = await this._identity.sign(payloadBytes);
|
|
3915
|
-
return baseRequest(method,
|
|
4283
|
+
return baseRequest(method, path6, {
|
|
3916
4284
|
...body,
|
|
3917
4285
|
secVersion: 1,
|
|
3918
4286
|
senderDid: this._identity.did,
|
|
@@ -3926,6 +4294,7 @@ var PrismerClient = class {
|
|
|
3926
4294
|
const headers = {};
|
|
3927
4295
|
if (this.apiKey) headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
3928
4296
|
if (this.imAgent) headers["X-IM-Agent"] = this.imAgent;
|
|
4297
|
+
if (this.imWorkspace) headers["X-IM-Workspace"] = this.imWorkspace;
|
|
3929
4298
|
return headers;
|
|
3930
4299
|
}
|
|
3931
4300
|
/**
|
|
@@ -3977,11 +4346,11 @@ var PrismerClient = class {
|
|
|
3977
4346
|
// --------------------------------------------------------------------------
|
|
3978
4347
|
// Internal request helper
|
|
3979
4348
|
// --------------------------------------------------------------------------
|
|
3980
|
-
async _request(method,
|
|
4349
|
+
async _request(method, path6, body, query, opts, _isRetry) {
|
|
3981
4350
|
const controller = new AbortController();
|
|
3982
4351
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
3983
4352
|
try {
|
|
3984
|
-
let url = `${this.baseUrl}${
|
|
4353
|
+
let url = `${this.baseUrl}${path6}`;
|
|
3985
4354
|
if (query && Object.keys(query).length > 0) {
|
|
3986
4355
|
url += "?" + new URLSearchParams(query).toString();
|
|
3987
4356
|
}
|
|
@@ -3992,6 +4361,9 @@ var PrismerClient = class {
|
|
|
3992
4361
|
if (this.imAgent) {
|
|
3993
4362
|
headers["X-IM-Agent"] = this.imAgent;
|
|
3994
4363
|
}
|
|
4364
|
+
if (this.imWorkspace) {
|
|
4365
|
+
headers["X-IM-Workspace"] = this.imWorkspace;
|
|
4366
|
+
}
|
|
3995
4367
|
if (opts?.headers) {
|
|
3996
4368
|
for (const [k, v] of Object.entries(opts.headers)) headers[k] = v;
|
|
3997
4369
|
}
|
|
@@ -4002,12 +4374,12 @@ var PrismerClient = class {
|
|
|
4002
4374
|
}
|
|
4003
4375
|
const response = await this.fetchFn(url, init);
|
|
4004
4376
|
const data = await response.json();
|
|
4005
|
-
if (response.status === 401 && this.apiKey.startsWith("eyJ") && !_isRetry && !
|
|
4377
|
+
if (response.status === 401 && this.apiKey.startsWith("eyJ") && !_isRetry && !path6.includes("/token/refresh")) {
|
|
4006
4378
|
try {
|
|
4007
4379
|
const refreshRes = await this._request("POST", "/api/im/token/refresh", void 0, void 0, void 0, true);
|
|
4008
4380
|
if (refreshRes?.ok && refreshRes?.data?.token) {
|
|
4009
4381
|
this.apiKey = refreshRes.data.token;
|
|
4010
|
-
return this._request(method,
|
|
4382
|
+
return this._request(method, path6, body, query, opts, true);
|
|
4011
4383
|
}
|
|
4012
4384
|
} catch {
|
|
4013
4385
|
}
|
|
@@ -5360,7 +5732,7 @@ function register3(parent, getIMClient2, _getAPIClient) {
|
|
|
5360
5732
|
const maxIterations = 30;
|
|
5361
5733
|
let lastStatus;
|
|
5362
5734
|
for (let i = 0; i < maxIterations; i++) {
|
|
5363
|
-
await new Promise((
|
|
5735
|
+
await new Promise((resolve6) => setTimeout(resolve6, 2e3));
|
|
5364
5736
|
if (!opts.json) process.stdout.write(".");
|
|
5365
5737
|
const statusRes = await client.im.evolution.getReportStatus(traceId);
|
|
5366
5738
|
if (!statusRes.ok) break;
|
|
@@ -5742,15 +6114,162 @@ function register3(parent, getIMClient2, _getAPIClient) {
|
|
|
5742
6114
|
});
|
|
5743
6115
|
}
|
|
5744
6116
|
|
|
6117
|
+
// src/commands/task.ts
|
|
6118
|
+
var import_node_fs = require("fs");
|
|
6119
|
+
var import_node_path2 = require("path");
|
|
6120
|
+
var import_node_crypto = require("crypto");
|
|
6121
|
+
|
|
6122
|
+
// src/commands/deliver-proxy.ts
|
|
6123
|
+
var import_node_path = require("path");
|
|
6124
|
+
function detectDeliverProxy(opts) {
|
|
6125
|
+
const taskId = (opts?.taskId || opts?.runId || "").trim() || process.env.PRISMER_TASK_ID || process.env.PRISMER_RUN_ID || "";
|
|
6126
|
+
if (!taskId) return null;
|
|
6127
|
+
const port = (opts?.daemonPort || "").trim() || (process.env.PRISMER_DAEMON_PORT ?? "3210").trim() || "3210";
|
|
6128
|
+
const ctx = {
|
|
6129
|
+
daemonUrl: `http://127.0.0.1:${port}`,
|
|
6130
|
+
taskId
|
|
6131
|
+
};
|
|
6132
|
+
const agentUsername = process.env.PRISMER_AGENT_USERNAME;
|
|
6133
|
+
if (agentUsername) ctx.agentUsername = agentUsername;
|
|
6134
|
+
const conversationId = (opts?.conversationId || "").trim() || process.env.PRISMER_CONVERSATION_ID;
|
|
6135
|
+
if (conversationId) ctx.conversationId = conversationId;
|
|
6136
|
+
return ctx;
|
|
6137
|
+
}
|
|
6138
|
+
async function proxyDeliver(ctx, filePath, mode, conversationId, messageId) {
|
|
6139
|
+
const abs = (0, import_node_path.resolve)(filePath);
|
|
6140
|
+
const body = {
|
|
6141
|
+
taskId: ctx.taskId,
|
|
6142
|
+
path: abs,
|
|
6143
|
+
mode
|
|
6144
|
+
};
|
|
6145
|
+
const conv = conversationId ?? ctx.conversationId;
|
|
6146
|
+
if (mode === "send") {
|
|
6147
|
+
if (!conv) {
|
|
6148
|
+
return { ok: false, status: 400, error: "conversationId is required for send mode" };
|
|
6149
|
+
}
|
|
6150
|
+
body.conversationId = conv;
|
|
6151
|
+
}
|
|
6152
|
+
if (mode === "message-attach") {
|
|
6153
|
+
if (!conv) {
|
|
6154
|
+
return { ok: false, status: 400, error: "conversationId is required for message-attach mode" };
|
|
6155
|
+
}
|
|
6156
|
+
if (!messageId) {
|
|
6157
|
+
return { ok: false, status: 400, error: "messageId is required for message-attach mode" };
|
|
6158
|
+
}
|
|
6159
|
+
body.conversationId = conv;
|
|
6160
|
+
body.messageId = messageId;
|
|
6161
|
+
}
|
|
6162
|
+
if (ctx.agentUsername) body.agentUsername = ctx.agentUsername;
|
|
6163
|
+
let res;
|
|
6164
|
+
try {
|
|
6165
|
+
res = await fetch(`${ctx.daemonUrl}/local/deliver`, {
|
|
6166
|
+
method: "POST",
|
|
6167
|
+
headers: { "Content-Type": "application/json" },
|
|
6168
|
+
body: JSON.stringify(body)
|
|
6169
|
+
});
|
|
6170
|
+
} catch (err) {
|
|
6171
|
+
return {
|
|
6172
|
+
ok: false,
|
|
6173
|
+
status: 0,
|
|
6174
|
+
error: `daemon unreachable at ${ctx.daemonUrl}/local/deliver: ${err instanceof Error ? err.message : String(err)}`
|
|
6175
|
+
};
|
|
6176
|
+
}
|
|
6177
|
+
let parsed = {};
|
|
6178
|
+
try {
|
|
6179
|
+
parsed = await res.json();
|
|
6180
|
+
} catch {
|
|
6181
|
+
}
|
|
6182
|
+
if (res.ok && parsed.ok) {
|
|
6183
|
+
const out = { ok: true, status: res.status };
|
|
6184
|
+
if (parsed.assetId) out.assetId = parsed.assetId;
|
|
6185
|
+
return out;
|
|
6186
|
+
}
|
|
6187
|
+
return {
|
|
6188
|
+
ok: false,
|
|
6189
|
+
status: res.status,
|
|
6190
|
+
error: parsed.error ?? `daemon returned ${res.status}`
|
|
6191
|
+
};
|
|
6192
|
+
}
|
|
6193
|
+
|
|
5745
6194
|
// src/commands/task.ts
|
|
5746
6195
|
var TASK_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned", "running", "review", "completed", "failed", "cancelled"]);
|
|
5747
6196
|
var TASK_PRIORITIES = /* @__PURE__ */ new Set(["low", "medium", "high", "urgent"]);
|
|
5748
6197
|
var TASK_KINDS = /* @__PURE__ */ new Set(["work_item", "goal"]);
|
|
6198
|
+
function assertNotRunId(id) {
|
|
6199
|
+
if (typeof id === "string" && id.startsWith("run_")) {
|
|
6200
|
+
process.stderr.write(
|
|
6201
|
+
`Error: '${id}' is a run id (run_\u2026); a chat reply doesn't need 'cloud task' ops \u2014 the platform closes the turn from your reply.
|
|
6202
|
+
`
|
|
6203
|
+
);
|
|
6204
|
+
process.exit(1);
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
5749
6207
|
function parseTaskStatus(raw) {
|
|
5750
6208
|
if (!raw) return void 0;
|
|
5751
6209
|
if (TASK_STATUSES.has(raw)) return raw;
|
|
5752
6210
|
throw new Error(`Invalid task status "${raw}".`);
|
|
5753
6211
|
}
|
|
6212
|
+
function normalizeProjectForCreate(raw) {
|
|
6213
|
+
const value = raw ?? process.env.PRISMER_ACTIVE_PROJECT_ID;
|
|
6214
|
+
if (!value) return void 0;
|
|
6215
|
+
const trimmed = value.trim();
|
|
6216
|
+
if (!trimmed || trimmed === "all") return void 0;
|
|
6217
|
+
if (trimmed === "__unscoped" || trimmed === "_unscoped" || trimmed === "none" || trimmed === "null") return null;
|
|
6218
|
+
return trimmed;
|
|
6219
|
+
}
|
|
6220
|
+
function normalizeProjectForList(raw) {
|
|
6221
|
+
const value = raw ?? process.env.PRISMER_ACTIVE_PROJECT_ID;
|
|
6222
|
+
if (!value) return void 0;
|
|
6223
|
+
const trimmed = value.trim();
|
|
6224
|
+
if (!trimmed) return void 0;
|
|
6225
|
+
if (trimmed === "_unscoped" || trimmed === "none" || trimmed === "null") return "__unscoped";
|
|
6226
|
+
return trimmed;
|
|
6227
|
+
}
|
|
6228
|
+
async function runReviewCheckpoint(taskId, toStatus) {
|
|
6229
|
+
const port = process.env.PRISMER_DAEMON_PORT ?? "3210";
|
|
6230
|
+
const url = `http://127.0.0.1:${port}/v1/checkpoints/pre_status_change`;
|
|
6231
|
+
let response;
|
|
6232
|
+
try {
|
|
6233
|
+
response = await fetch(url, {
|
|
6234
|
+
method: "POST",
|
|
6235
|
+
headers: { "Content-Type": "application/json" },
|
|
6236
|
+
body: JSON.stringify({ taskId, toStatus })
|
|
6237
|
+
});
|
|
6238
|
+
} catch (err) {
|
|
6239
|
+
return {
|
|
6240
|
+
ok: false,
|
|
6241
|
+
indeterminate: true,
|
|
6242
|
+
message: `daemon unreachable at ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
6243
|
+
};
|
|
6244
|
+
}
|
|
6245
|
+
let body;
|
|
6246
|
+
try {
|
|
6247
|
+
body = await response.json();
|
|
6248
|
+
} catch {
|
|
6249
|
+
body = {};
|
|
6250
|
+
}
|
|
6251
|
+
if (response.status === 200 && body.ok === true) {
|
|
6252
|
+
return { ok: true };
|
|
6253
|
+
}
|
|
6254
|
+
if (response.status === 409 && Array.isArray(body.pendingFiles)) {
|
|
6255
|
+
return {
|
|
6256
|
+
ok: false,
|
|
6257
|
+
pendingFiles: body.pendingFiles,
|
|
6258
|
+
message: body.error?.message ?? "pending_attach"
|
|
6259
|
+
};
|
|
6260
|
+
}
|
|
6261
|
+
if (response.status === 503) {
|
|
6262
|
+
return {
|
|
6263
|
+
ok: false,
|
|
6264
|
+
indeterminate: true,
|
|
6265
|
+
message: body.error?.message ?? `daemon returned 503`
|
|
6266
|
+
};
|
|
6267
|
+
}
|
|
6268
|
+
return {
|
|
6269
|
+
ok: false,
|
|
6270
|
+
message: body.error?.message ?? `daemon returned ${response.status}`
|
|
6271
|
+
};
|
|
6272
|
+
}
|
|
5754
6273
|
async function resolveAssigneeId(client, name) {
|
|
5755
6274
|
const needle = name.trim().toLowerCase();
|
|
5756
6275
|
const normalized = needle.replace(/^@/, "");
|
|
@@ -5773,7 +6292,7 @@ async function resolveAssigneeId(client, name) {
|
|
|
5773
6292
|
}
|
|
5774
6293
|
function register4(parent, getIMClient2, _getAPIClient) {
|
|
5775
6294
|
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) => {
|
|
6295
|
+
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
6296
|
const client = getIMClient2();
|
|
5778
6297
|
try {
|
|
5779
6298
|
if (opts.priority && !TASK_PRIORITIES.has(opts.priority)) {
|
|
@@ -5817,6 +6336,8 @@ function register4(parent, getIMClient2, _getAPIClient) {
|
|
|
5817
6336
|
};
|
|
5818
6337
|
if (assigneeId) createOpts.assigneeId = assigneeId;
|
|
5819
6338
|
if (opts.conversationId) createOpts.conversationId = opts.conversationId;
|
|
6339
|
+
const projectId = normalizeProjectForCreate(opts.project);
|
|
6340
|
+
if (projectId !== void 0) createOpts.projectId = projectId;
|
|
5820
6341
|
if (opts.scheduleAt) {
|
|
5821
6342
|
createOpts.scheduleType = "once";
|
|
5822
6343
|
createOpts.scheduleAt = opts.scheduleAt;
|
|
@@ -5858,12 +6379,13 @@ function register4(parent, getIMClient2, _getAPIClient) {
|
|
|
5858
6379
|
process.exit(1);
|
|
5859
6380
|
}
|
|
5860
6381
|
});
|
|
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) => {
|
|
6382
|
+
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
6383
|
const client = getIMClient2();
|
|
5863
6384
|
try {
|
|
5864
6385
|
const res = await client.im.tasks.list({
|
|
5865
6386
|
status: parseTaskStatus(opts.status),
|
|
5866
6387
|
capability: opts.capability,
|
|
6388
|
+
projectId: normalizeProjectForList(opts.project),
|
|
5867
6389
|
limit: parseInt(opts.limit, 10)
|
|
5868
6390
|
});
|
|
5869
6391
|
if (opts.json) {
|
|
@@ -5884,9 +6406,9 @@ function register4(parent, getIMClient2, _getAPIClient) {
|
|
|
5884
6406
|
const statusW = 12;
|
|
5885
6407
|
const titleW = 40;
|
|
5886
6408
|
const header = "ID".padEnd(idW) + "STATUS".padEnd(statusW) + "TITLE";
|
|
5887
|
-
const
|
|
6409
|
+
const sep3 = "-".repeat(idW + statusW + titleW);
|
|
5888
6410
|
process.stdout.write(header + "\n");
|
|
5889
|
-
process.stdout.write(
|
|
6411
|
+
process.stdout.write(sep3 + "\n");
|
|
5890
6412
|
for (const t of tasks) {
|
|
5891
6413
|
const title = t.title.length > titleW ? t.title.slice(0, titleW - 3) + "..." : t.title;
|
|
5892
6414
|
process.stdout.write(
|
|
@@ -5895,6 +6417,33 @@ function register4(parent, getIMClient2, _getAPIClient) {
|
|
|
5895
6417
|
}
|
|
5896
6418
|
process.stdout.write(`
|
|
5897
6419
|
${tasks.length} task(s) listed.
|
|
6420
|
+
`);
|
|
6421
|
+
} catch (err) {
|
|
6422
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6423
|
+
process.stderr.write(`Error: ${message}
|
|
6424
|
+
`);
|
|
6425
|
+
process.exit(1);
|
|
6426
|
+
}
|
|
6427
|
+
});
|
|
6428
|
+
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) => {
|
|
6429
|
+
assertNotRunId(taskId);
|
|
6430
|
+
const client = getIMClient2();
|
|
6431
|
+
try {
|
|
6432
|
+
if (!opts.unscoped && !projectId) {
|
|
6433
|
+
throw new Error("Provide <project-id> or pass --unscoped.");
|
|
6434
|
+
}
|
|
6435
|
+
const targetProjectId = opts.unscoped ? null : projectId.trim();
|
|
6436
|
+
const res = await client.im.tasks.moveProject(taskId, targetProjectId);
|
|
6437
|
+
if (opts.json) {
|
|
6438
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6439
|
+
return;
|
|
6440
|
+
}
|
|
6441
|
+
if (!res.ok) {
|
|
6442
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6443
|
+
`);
|
|
6444
|
+
process.exit(1);
|
|
6445
|
+
}
|
|
6446
|
+
process.stdout.write(`Task ${taskId} project: ${targetProjectId ?? "(workspace-level)"}
|
|
5898
6447
|
`);
|
|
5899
6448
|
} catch (err) {
|
|
5900
6449
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -5904,6 +6453,7 @@ ${tasks.length} task(s) listed.
|
|
|
5904
6453
|
}
|
|
5905
6454
|
});
|
|
5906
6455
|
task.command("get <task-id>").description("Get task details and logs").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6456
|
+
assertNotRunId(taskId);
|
|
5907
6457
|
const client = getIMClient2();
|
|
5908
6458
|
try {
|
|
5909
6459
|
const res = await client.im.tasks.get(taskId);
|
|
@@ -5968,6 +6518,7 @@ Logs (${logs.length}):
|
|
|
5968
6518
|
}
|
|
5969
6519
|
});
|
|
5970
6520
|
task.command("claim <task-id>").description("Claim a pending task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6521
|
+
assertNotRunId(taskId);
|
|
5971
6522
|
const client = getIMClient2();
|
|
5972
6523
|
try {
|
|
5973
6524
|
const res = await client.im.tasks.claim(taskId);
|
|
@@ -5997,9 +6548,38 @@ Logs (${logs.length}):
|
|
|
5997
6548
|
process.exit(1);
|
|
5998
6549
|
}
|
|
5999
6550
|
});
|
|
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) => {
|
|
6551
|
+
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) => {
|
|
6552
|
+
assertNotRunId(taskId);
|
|
6001
6553
|
const client = getIMClient2();
|
|
6002
6554
|
try {
|
|
6555
|
+
if (opts.status === "review" && !opts.skipCheckpoint) {
|
|
6556
|
+
const checkpoint = await runReviewCheckpoint(taskId, opts.status);
|
|
6557
|
+
if (!checkpoint.ok) {
|
|
6558
|
+
if (checkpoint.pendingFiles && checkpoint.pendingFiles.length > 0) {
|
|
6559
|
+
process.stderr.write(
|
|
6560
|
+
`[checkpoint] artifacts/ \u542B ${checkpoint.pendingFiles.length} \u4E2A\u672A attach \u6587\u4EF6:
|
|
6561
|
+
`
|
|
6562
|
+
);
|
|
6563
|
+
for (const f of checkpoint.pendingFiles) {
|
|
6564
|
+
const sizeKb = (f.sizeBytes / 1024).toFixed(1);
|
|
6565
|
+
process.stderr.write(` - ${f.path} (sha256=${f.sha256.slice(0, 12)}\u2026, ${sizeKb}KB)
|
|
6566
|
+
`);
|
|
6567
|
+
}
|
|
6568
|
+
process.stderr.write(
|
|
6569
|
+
"\u8BF7 attach \u5B83\u4EEC\u518D retry status change, \u6216\u52A0 --skip-checkpoint \u663E\u5F0F\u5FFD\u7565\n"
|
|
6570
|
+
);
|
|
6571
|
+
} else if (checkpoint.indeterminate) {
|
|
6572
|
+
process.stderr.write(
|
|
6573
|
+
`[checkpoint] daemon unreachable or cloud lookup failed (${checkpoint.message}); pass --skip-checkpoint to bypass
|
|
6574
|
+
`
|
|
6575
|
+
);
|
|
6576
|
+
} else {
|
|
6577
|
+
process.stderr.write(`[checkpoint] failed: ${checkpoint.message ?? "unknown"}
|
|
6578
|
+
`);
|
|
6579
|
+
}
|
|
6580
|
+
process.exit(12);
|
|
6581
|
+
}
|
|
6582
|
+
}
|
|
6003
6583
|
const res = await client.im.tasks.update(taskId, {
|
|
6004
6584
|
title: opts.title,
|
|
6005
6585
|
description: opts.description,
|
|
@@ -6038,6 +6618,7 @@ Logs (${logs.length}):
|
|
|
6038
6618
|
}
|
|
6039
6619
|
});
|
|
6040
6620
|
task.command("complete <task-id>").description("Mark a task as complete").option("--result <result>", "result or output of the task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6621
|
+
assertNotRunId(taskId);
|
|
6041
6622
|
const client = getIMClient2();
|
|
6042
6623
|
try {
|
|
6043
6624
|
const res = await client.im.tasks.complete(taskId, {
|
|
@@ -6072,6 +6653,7 @@ Logs (${logs.length}):
|
|
|
6072
6653
|
}
|
|
6073
6654
|
});
|
|
6074
6655
|
task.command("fail <task-id>").description("Mark a task as failed").requiredOption("--error <error>", "error message describing why the task failed").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6656
|
+
assertNotRunId(taskId);
|
|
6075
6657
|
const client = getIMClient2();
|
|
6076
6658
|
try {
|
|
6077
6659
|
const res = await client.im.tasks.fail(taskId, opts.error);
|
|
@@ -6104,6 +6686,7 @@ Logs (${logs.length}):
|
|
|
6104
6686
|
}
|
|
6105
6687
|
});
|
|
6106
6688
|
task.command("approve <task-id>").description("Approve a completed task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6689
|
+
assertNotRunId(taskId);
|
|
6107
6690
|
const client = getIMClient2();
|
|
6108
6691
|
try {
|
|
6109
6692
|
const res = await client.im.tasks.approve(taskId);
|
|
@@ -6134,6 +6717,7 @@ Logs (${logs.length}):
|
|
|
6134
6717
|
}
|
|
6135
6718
|
});
|
|
6136
6719
|
task.command("reject <task-id>").description("Reject a task").requiredOption("--reason <reason>", "reason for rejection").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6720
|
+
assertNotRunId(taskId);
|
|
6137
6721
|
const client = getIMClient2();
|
|
6138
6722
|
try {
|
|
6139
6723
|
const res = await client.im.tasks.reject(taskId, opts.reason);
|
|
@@ -6163,28 +6747,463 @@ Logs (${logs.length}):
|
|
|
6163
6747
|
process.exit(1);
|
|
6164
6748
|
}
|
|
6165
6749
|
});
|
|
6166
|
-
task.command("
|
|
6167
|
-
const
|
|
6168
|
-
|
|
6169
|
-
|
|
6750
|
+
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("--run-id <id>", "dispatch task id (alias for --task; from <execution_context>; env fallback PRISMER_TASK_ID)").option("--daemon-port <port>", "daemon local-server port (env fallback PRISMER_DAEMON_PORT, default 3210)").option("--json", "output raw JSON response").action(async (filePath, opts) => {
|
|
6751
|
+
const targetTaskId = opts.task ?? opts.runId ?? process.env.PRISMER_TASK_ID;
|
|
6752
|
+
if (!targetTaskId) {
|
|
6753
|
+
process.stderr.write("Error: --task is required (or set PRISMER_TASK_ID env)\n");
|
|
6754
|
+
process.exit(1);
|
|
6755
|
+
}
|
|
6756
|
+
assertNotRunId(targetTaskId);
|
|
6757
|
+
const proxy = detectDeliverProxy({
|
|
6758
|
+
taskId: targetTaskId,
|
|
6759
|
+
daemonPort: opts.daemonPort
|
|
6760
|
+
});
|
|
6761
|
+
if (proxy) {
|
|
6762
|
+
const result = await proxyDeliver(proxy, filePath, "task-attach");
|
|
6763
|
+
if (!result.ok) {
|
|
6764
|
+
process.stderr.write(`Error: ${result.error ?? "task attach failed"}
|
|
6765
|
+
`);
|
|
6766
|
+
process.exit(1);
|
|
6767
|
+
}
|
|
6170
6768
|
if (opts.json) {
|
|
6171
|
-
process.stdout.write(JSON.stringify(
|
|
6769
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
6172
6770
|
return;
|
|
6173
6771
|
}
|
|
6174
|
-
|
|
6175
|
-
|
|
6772
|
+
process.stdout.write(`Attached to task ${targetTaskId} (assetId: ${result.assetId ?? "-"})
|
|
6773
|
+
`);
|
|
6774
|
+
return;
|
|
6775
|
+
}
|
|
6776
|
+
const client = getIMClient2();
|
|
6777
|
+
try {
|
|
6778
|
+
const getRes = await client.im.tasks.get(targetTaskId);
|
|
6779
|
+
if (!getRes.ok || !getRes.data) {
|
|
6780
|
+
process.stderr.write(`Error: task ${targetTaskId} not found: ${getRes.error?.message ?? "unknown"}
|
|
6176
6781
|
`);
|
|
6177
6782
|
process.exit(1);
|
|
6178
6783
|
}
|
|
6179
|
-
const
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
`);
|
|
6183
|
-
process.stdout.write(`ID: ${t.id}
|
|
6184
|
-
`);
|
|
6185
|
-
process.stdout.write(`Title: ${t.title}
|
|
6784
|
+
const wsId = getRes.data.task?.workspaceId;
|
|
6785
|
+
if (!wsId) {
|
|
6786
|
+
process.stderr.write(`Error: task ${targetTaskId} has no workspaceId; cannot upload
|
|
6186
6787
|
`);
|
|
6187
|
-
|
|
6788
|
+
process.exit(1);
|
|
6789
|
+
}
|
|
6790
|
+
let bytes;
|
|
6791
|
+
try {
|
|
6792
|
+
bytes = await import_node_fs.promises.readFile(filePath);
|
|
6793
|
+
} catch (err) {
|
|
6794
|
+
process.stderr.write(`Error: cannot read ${filePath}: ${err instanceof Error ? err.message : String(err)}
|
|
6795
|
+
`);
|
|
6796
|
+
process.exit(1);
|
|
6797
|
+
}
|
|
6798
|
+
const contentHash = (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex");
|
|
6799
|
+
const displayName = opts.name ?? (0, import_node_path2.basename)(filePath);
|
|
6800
|
+
const uploadRes = await client.im.assets.upload(bytes, {
|
|
6801
|
+
workspaceId: wsId,
|
|
6802
|
+
sourceTaskId: targetTaskId,
|
|
6803
|
+
kind: "agent-output",
|
|
6804
|
+
fileName: displayName,
|
|
6805
|
+
metadata: {
|
|
6806
|
+
// boundKind stamped via metadata so service-side cloud code can
|
|
6807
|
+
// mirror onto the column. Service POST handler reads this and
|
|
6808
|
+
// populates IMAsset.boundKind='task-bound'.
|
|
6809
|
+
boundKind: "task-bound",
|
|
6810
|
+
filename: displayName,
|
|
6811
|
+
attachedBy: "cloud-task-attach"
|
|
6812
|
+
}
|
|
6813
|
+
});
|
|
6814
|
+
if (opts.json) {
|
|
6815
|
+
process.stdout.write(JSON.stringify(uploadRes, null, 2) + "\n");
|
|
6816
|
+
return;
|
|
6817
|
+
}
|
|
6818
|
+
if (!uploadRes.ok || !uploadRes.data) {
|
|
6819
|
+
process.stderr.write(`Error: attach failed: ${uploadRes.error?.message ?? "unknown"}
|
|
6820
|
+
`);
|
|
6821
|
+
process.exit(1);
|
|
6822
|
+
}
|
|
6823
|
+
const dedupHit = Boolean(uploadRes.meta?.dedup);
|
|
6824
|
+
const asset = uploadRes.data;
|
|
6825
|
+
process.stdout.write(`Attached ${displayName}
|
|
6826
|
+
|
|
6827
|
+
`);
|
|
6828
|
+
process.stdout.write(`AssetId: ${asset.id}
|
|
6829
|
+
`);
|
|
6830
|
+
process.stdout.write(`Task: ${targetTaskId}
|
|
6831
|
+
`);
|
|
6832
|
+
process.stdout.write(`ContentHash: ${contentHash}
|
|
6833
|
+
`);
|
|
6834
|
+
process.stdout.write(`SizeBytes: ${bytes.length}
|
|
6835
|
+
`);
|
|
6836
|
+
process.stdout.write(`Dedup: ${dedupHit ? "true (existing IMAsset row)" : "false (created)"}
|
|
6837
|
+
`);
|
|
6838
|
+
if (asset.cdnUrl) process.stdout.write(`CDN URL: ${asset.cdnUrl}
|
|
6839
|
+
`);
|
|
6840
|
+
} catch (err) {
|
|
6841
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6842
|
+
process.stderr.write(`Error: ${message}
|
|
6843
|
+
`);
|
|
6844
|
+
process.exit(1);
|
|
6845
|
+
}
|
|
6846
|
+
});
|
|
6847
|
+
task.command("cancel <task-id>").description("Cancel a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6848
|
+
assertNotRunId(taskId);
|
|
6849
|
+
const client = getIMClient2();
|
|
6850
|
+
try {
|
|
6851
|
+
const res = await client.im.tasks.cancel(taskId);
|
|
6852
|
+
if (opts.json) {
|
|
6853
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6854
|
+
return;
|
|
6855
|
+
}
|
|
6856
|
+
if (!res.ok) {
|
|
6857
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6858
|
+
`);
|
|
6859
|
+
process.exit(1);
|
|
6860
|
+
}
|
|
6861
|
+
const t = res.data;
|
|
6862
|
+
process.stdout.write(`Task cancelled
|
|
6863
|
+
|
|
6864
|
+
`);
|
|
6865
|
+
process.stdout.write(`ID: ${t.id}
|
|
6866
|
+
`);
|
|
6867
|
+
process.stdout.write(`Title: ${t.title}
|
|
6868
|
+
`);
|
|
6869
|
+
process.stdout.write(`Status: ${t.status}
|
|
6870
|
+
`);
|
|
6871
|
+
} catch (err) {
|
|
6872
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6873
|
+
process.stderr.write(`Error: ${message}
|
|
6874
|
+
`);
|
|
6875
|
+
process.exit(1);
|
|
6876
|
+
}
|
|
6877
|
+
});
|
|
6878
|
+
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) => {
|
|
6879
|
+
assertNotRunId(taskId);
|
|
6880
|
+
const client = getIMClient2();
|
|
6881
|
+
try {
|
|
6882
|
+
let md = "";
|
|
6883
|
+
if (opts.file) {
|
|
6884
|
+
md = await import_node_fs.promises.readFile(opts.file, "utf-8");
|
|
6885
|
+
} else if (opts.markdown) {
|
|
6886
|
+
md = opts.markdown;
|
|
6887
|
+
} else {
|
|
6888
|
+
throw new Error("one of --file or --markdown is required");
|
|
6889
|
+
}
|
|
6890
|
+
const res = await client.im.tasks.spec.set(taskId, { markdown: md });
|
|
6891
|
+
if (opts.json) {
|
|
6892
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6893
|
+
return;
|
|
6894
|
+
}
|
|
6895
|
+
if (!res.ok) {
|
|
6896
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6897
|
+
`);
|
|
6898
|
+
process.exit(1);
|
|
6899
|
+
}
|
|
6900
|
+
const v = res.data;
|
|
6901
|
+
process.stdout.write(`SPEC.md saved (revision ${v.revision})
|
|
6902
|
+
`);
|
|
6903
|
+
} catch (err) {
|
|
6904
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6905
|
+
`);
|
|
6906
|
+
process.exit(1);
|
|
6907
|
+
}
|
|
6908
|
+
});
|
|
6909
|
+
task.command("spec-show <task-id>").description("Print SPEC.md content for a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
6910
|
+
assertNotRunId(taskId);
|
|
6911
|
+
const client = getIMClient2();
|
|
6912
|
+
try {
|
|
6913
|
+
const res = await client.im.tasks.spec.get(taskId);
|
|
6914
|
+
if (opts.json) {
|
|
6915
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6916
|
+
return;
|
|
6917
|
+
}
|
|
6918
|
+
if (!res.ok) {
|
|
6919
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6920
|
+
`);
|
|
6921
|
+
process.exit(1);
|
|
6922
|
+
}
|
|
6923
|
+
const v = res.data;
|
|
6924
|
+
process.stdout.write(v.markdown);
|
|
6925
|
+
if (!v.markdown.endsWith("\n")) process.stdout.write("\n");
|
|
6926
|
+
process.stdout.write(`# revision ${v.revision}
|
|
6927
|
+
`);
|
|
6928
|
+
} catch (err) {
|
|
6929
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6930
|
+
`);
|
|
6931
|
+
process.exit(1);
|
|
6932
|
+
}
|
|
6933
|
+
});
|
|
6934
|
+
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) => {
|
|
6935
|
+
assertNotRunId(taskId);
|
|
6936
|
+
const client = getIMClient2();
|
|
6937
|
+
try {
|
|
6938
|
+
const res = await client.im.tasks.todo.add(taskId, {
|
|
6939
|
+
text: text.join(" "),
|
|
6940
|
+
depth: opts.depth
|
|
6941
|
+
});
|
|
6942
|
+
if (opts.json) {
|
|
6943
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6944
|
+
return;
|
|
6945
|
+
}
|
|
6946
|
+
if (!res.ok) {
|
|
6947
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6948
|
+
`);
|
|
6949
|
+
process.exit(1);
|
|
6950
|
+
}
|
|
6951
|
+
const v = res.data;
|
|
6952
|
+
process.stdout.write(`TODO item added \u2014 ${v.doneCount}/${v.totalCount} (rev ${v.revision})
|
|
6953
|
+
`);
|
|
6954
|
+
} catch (err) {
|
|
6955
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6956
|
+
`);
|
|
6957
|
+
process.exit(1);
|
|
6958
|
+
}
|
|
6959
|
+
});
|
|
6960
|
+
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) => {
|
|
6961
|
+
assertNotRunId(taskId);
|
|
6962
|
+
const client = getIMClient2();
|
|
6963
|
+
try {
|
|
6964
|
+
const idx = parseInt(indexRaw, 10);
|
|
6965
|
+
if (!Number.isFinite(idx) || idx < 0) throw new Error("index must be a non-negative integer");
|
|
6966
|
+
const res = await client.im.tasks.todo.toggle(taskId, idx, true);
|
|
6967
|
+
if (opts.json) {
|
|
6968
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6969
|
+
return;
|
|
6970
|
+
}
|
|
6971
|
+
if (!res.ok) {
|
|
6972
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
6973
|
+
`);
|
|
6974
|
+
process.exit(1);
|
|
6975
|
+
}
|
|
6976
|
+
const v = res.data;
|
|
6977
|
+
process.stdout.write(
|
|
6978
|
+
`TODO[${idx}] \u2192 done. progress ${v.doneCount}/${v.totalCount} (${Math.round(v.progressPct * 100)}%)
|
|
6979
|
+
`
|
|
6980
|
+
);
|
|
6981
|
+
} catch (err) {
|
|
6982
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6983
|
+
`);
|
|
6984
|
+
process.exit(1);
|
|
6985
|
+
}
|
|
6986
|
+
});
|
|
6987
|
+
task.command("todo-uncheck <task-id> <index>").description("Un-tick a TODO item").option("--json", "output raw JSON response").action(async (taskId, indexRaw, opts) => {
|
|
6988
|
+
assertNotRunId(taskId);
|
|
6989
|
+
const client = getIMClient2();
|
|
6990
|
+
try {
|
|
6991
|
+
const idx = parseInt(indexRaw, 10);
|
|
6992
|
+
if (!Number.isFinite(idx) || idx < 0) throw new Error("index must be a non-negative integer");
|
|
6993
|
+
const res = await client.im.tasks.todo.toggle(taskId, idx, false);
|
|
6994
|
+
if (opts.json) {
|
|
6995
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
6996
|
+
return;
|
|
6997
|
+
}
|
|
6998
|
+
if (!res.ok) {
|
|
6999
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7000
|
+
`);
|
|
7001
|
+
process.exit(1);
|
|
7002
|
+
}
|
|
7003
|
+
const v = res.data;
|
|
7004
|
+
process.stdout.write(`TODO[${idx}] \u2192 pending. progress ${v.doneCount}/${v.totalCount}
|
|
7005
|
+
`);
|
|
7006
|
+
} catch (err) {
|
|
7007
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
7008
|
+
`);
|
|
7009
|
+
process.exit(1);
|
|
7010
|
+
}
|
|
7011
|
+
});
|
|
7012
|
+
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) => {
|
|
7013
|
+
assertNotRunId(taskId);
|
|
7014
|
+
const client = getIMClient2();
|
|
7015
|
+
try {
|
|
7016
|
+
const res = await client.im.tasks.todo.list(taskId);
|
|
7017
|
+
if (opts.json) {
|
|
7018
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7019
|
+
return;
|
|
7020
|
+
}
|
|
7021
|
+
if (!res.ok) {
|
|
7022
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7023
|
+
`);
|
|
7024
|
+
process.exit(1);
|
|
7025
|
+
}
|
|
7026
|
+
const v = res.data;
|
|
7027
|
+
process.stdout.write(
|
|
7028
|
+
`TODO progress: ${v.doneCount}/${v.totalCount} (${Math.round(v.progressPct * 100)}%) \xB7 rev ${v.revision}
|
|
7029
|
+
`
|
|
7030
|
+
);
|
|
7031
|
+
for (const it of v.items) {
|
|
7032
|
+
const tick = it.status === "done" ? "\u2611" : "\u2610";
|
|
7033
|
+
const indent = " ".repeat(it.depth);
|
|
7034
|
+
process.stdout.write(` ${indent}${tick} [${it.index}] ${it.text}
|
|
7035
|
+
`);
|
|
7036
|
+
}
|
|
7037
|
+
} catch (err) {
|
|
7038
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
7039
|
+
`);
|
|
7040
|
+
process.exit(1);
|
|
7041
|
+
}
|
|
7042
|
+
});
|
|
7043
|
+
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) => {
|
|
7044
|
+
assertNotRunId(taskId);
|
|
7045
|
+
const client = getIMClient2();
|
|
7046
|
+
try {
|
|
7047
|
+
const res = await client.im.tasks.getAcceptance(taskId);
|
|
7048
|
+
if (opts.json) {
|
|
7049
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7050
|
+
return;
|
|
7051
|
+
}
|
|
7052
|
+
if (!res.ok) {
|
|
7053
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7054
|
+
`);
|
|
7055
|
+
process.exit(1);
|
|
7056
|
+
}
|
|
7057
|
+
const v = res.data;
|
|
7058
|
+
process.stdout.write(`Acceptance for ${taskId}: ${v.overall} (${v.completedCount}/${v.totalCount})
|
|
7059
|
+
`);
|
|
7060
|
+
for (const c of v.criteria) {
|
|
7061
|
+
const mark = c.status === "passed" ? "\u2713" : c.status === "failed" ? "\u2717" : c.status === "n/a" ? "\xB7" : "\u25EF";
|
|
7062
|
+
const req = c.required === false ? " [optional]" : "";
|
|
7063
|
+
const ver = c.verifierAgentId ? ` @${c.verifierAgentId}` : "";
|
|
7064
|
+
process.stdout.write(` ${mark} [${c.verifyMode}${ver}] ${c.expectation}${req} (${c.status}) \u2014 ${c.id}
|
|
7065
|
+
`);
|
|
7066
|
+
}
|
|
7067
|
+
} catch (err) {
|
|
7068
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
7069
|
+
`);
|
|
7070
|
+
process.exit(1);
|
|
7071
|
+
}
|
|
7072
|
+
});
|
|
7073
|
+
task.command("add-criterion <task-id>").description("Add an acceptance criterion to a task (rev 2 \u2014 verifyMode + expectation)").requiredOption(
|
|
7074
|
+
"--mode <mode>",
|
|
7075
|
+
"verifyMode: qualitative | quantitative | agent-self-check | manual"
|
|
7076
|
+
).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) => {
|
|
7077
|
+
assertNotRunId(taskId);
|
|
7078
|
+
const client = getIMClient2();
|
|
7079
|
+
try {
|
|
7080
|
+
const VALID = ["qualitative", "quantitative", "agent-self-check", "manual"];
|
|
7081
|
+
if (!VALID.includes(opts.mode)) {
|
|
7082
|
+
throw new Error(`--mode must be one of ${VALID.join(" | ")}`);
|
|
7083
|
+
}
|
|
7084
|
+
const res = await client.im.tasks.criteria.add(taskId, {
|
|
7085
|
+
verifyMode: opts.mode,
|
|
7086
|
+
expectation: opts.expectation,
|
|
7087
|
+
verifierAgentId: opts.verifierAgent ?? null,
|
|
7088
|
+
weight: opts.weight ?? 1,
|
|
7089
|
+
required: !opts.optional
|
|
7090
|
+
});
|
|
7091
|
+
if (opts.json) {
|
|
7092
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7093
|
+
return;
|
|
7094
|
+
}
|
|
7095
|
+
if (!res.ok) {
|
|
7096
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7097
|
+
`);
|
|
7098
|
+
process.exit(1);
|
|
7099
|
+
}
|
|
7100
|
+
const { criterion } = res.data;
|
|
7101
|
+
process.stdout.write(`Added criterion ${criterion.id} to task ${taskId}
|
|
7102
|
+
`);
|
|
7103
|
+
} catch (err) {
|
|
7104
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
7105
|
+
`);
|
|
7106
|
+
process.exit(1);
|
|
7107
|
+
}
|
|
7108
|
+
});
|
|
7109
|
+
task.command("verify <task-id>").description(
|
|
7110
|
+
"Assignee self-check: list all agent-self-check criteria + mark them passed (run before status=review)"
|
|
7111
|
+
).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) => {
|
|
7112
|
+
assertNotRunId(taskId);
|
|
7113
|
+
const client = getIMClient2();
|
|
7114
|
+
try {
|
|
7115
|
+
const view = await client.im.tasks.getAcceptance(taskId);
|
|
7116
|
+
if (!view.ok) {
|
|
7117
|
+
process.stderr.write(`Error: ${view.error?.message || "Unknown error"}
|
|
7118
|
+
`);
|
|
7119
|
+
process.exit(1);
|
|
7120
|
+
}
|
|
7121
|
+
const v = view.data;
|
|
7122
|
+
const targets = v.criteria.filter(
|
|
7123
|
+
(c) => c.verifyMode === "agent-self-check" && c.status === "pending"
|
|
7124
|
+
);
|
|
7125
|
+
if (targets.length === 0) {
|
|
7126
|
+
process.stdout.write("No pending agent-self-check criteria.\n");
|
|
7127
|
+
return;
|
|
7128
|
+
}
|
|
7129
|
+
const results = [];
|
|
7130
|
+
let anyFailed = false;
|
|
7131
|
+
for (const c of targets) {
|
|
7132
|
+
const r = await client.im.tasks.criteria.verify(taskId, c.id, {
|
|
7133
|
+
outcome: "passed",
|
|
7134
|
+
note: opts.note
|
|
7135
|
+
});
|
|
7136
|
+
results.push({ id: c.id, outcome: r.ok ? "passed" : "error" });
|
|
7137
|
+
if (!r.ok) anyFailed = true;
|
|
7138
|
+
}
|
|
7139
|
+
if (opts.json) {
|
|
7140
|
+
process.stdout.write(JSON.stringify({ ok: !anyFailed, results }, null, 2) + "\n");
|
|
7141
|
+
return;
|
|
7142
|
+
}
|
|
7143
|
+
for (const r of results) {
|
|
7144
|
+
process.stdout.write(` ${r.outcome === "passed" ? "\u2713" : "\u2717"} ${r.id}
|
|
7145
|
+
`);
|
|
7146
|
+
}
|
|
7147
|
+
if (anyFailed) process.exit(1);
|
|
7148
|
+
} catch (err) {
|
|
7149
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
7150
|
+
`);
|
|
7151
|
+
process.exit(1);
|
|
7152
|
+
}
|
|
7153
|
+
});
|
|
7154
|
+
task.command("verify-criterion <task-id> <criterion-id>").description(
|
|
7155
|
+
"Report verify outcome for one criterion. Used by reviewers (manual), verifier agents, and assignees (self-check)."
|
|
7156
|
+
).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) => {
|
|
7157
|
+
assertNotRunId(taskId);
|
|
7158
|
+
const client = getIMClient2();
|
|
7159
|
+
try {
|
|
7160
|
+
const VALID = ["passed", "failed", "n/a", "waived"];
|
|
7161
|
+
if (!VALID.includes(opts.outcome)) {
|
|
7162
|
+
throw new Error(`--outcome must be one of ${VALID.join(" | ")}`);
|
|
7163
|
+
}
|
|
7164
|
+
if (opts.outcome === "waived" && !opts.waiveReason) {
|
|
7165
|
+
throw new Error("--waive-reason is required when --outcome waived");
|
|
7166
|
+
}
|
|
7167
|
+
const outcome = opts.outcome;
|
|
7168
|
+
const res = await client.im.tasks.criteria.verify(taskId, criterionId, {
|
|
7169
|
+
outcome,
|
|
7170
|
+
note: opts.note,
|
|
7171
|
+
evidenceRefs: opts.evidence,
|
|
7172
|
+
waiveReason: opts.waiveReason
|
|
7173
|
+
});
|
|
7174
|
+
if (opts.json) {
|
|
7175
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7176
|
+
return;
|
|
7177
|
+
}
|
|
7178
|
+
if (!res.ok) {
|
|
7179
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7180
|
+
`);
|
|
7181
|
+
process.exit(1);
|
|
7182
|
+
}
|
|
7183
|
+
process.stdout.write(`Criterion ${criterionId} \u2192 ${opts.outcome}
|
|
7184
|
+
`);
|
|
7185
|
+
} catch (err) {
|
|
7186
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
7187
|
+
`);
|
|
7188
|
+
process.exit(1);
|
|
7189
|
+
}
|
|
7190
|
+
});
|
|
7191
|
+
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) => {
|
|
7192
|
+
assertNotRunId(taskId);
|
|
7193
|
+
const client = getIMClient2();
|
|
7194
|
+
try {
|
|
7195
|
+
const res = await client.im.tasks.applyTemplate(taskId, opts.template);
|
|
7196
|
+
if (opts.json) {
|
|
7197
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7198
|
+
return;
|
|
7199
|
+
}
|
|
7200
|
+
if (!res.ok) {
|
|
7201
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
7202
|
+
`);
|
|
7203
|
+
process.exit(1);
|
|
7204
|
+
}
|
|
7205
|
+
const v = res.data;
|
|
7206
|
+
process.stdout.write(`Applied template ${opts.template} (${v.totalCount} criteria total)
|
|
6188
7207
|
`);
|
|
6189
7208
|
} catch (err) {
|
|
6190
7209
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -6496,7 +7515,7 @@ function printFileTable(files) {
|
|
|
6496
7515
|
const idLen = Math.max(2, ...files.map((f) => f.id.length));
|
|
6497
7516
|
const scopeLen = Math.max(5, ...files.map((f) => f.scope.length));
|
|
6498
7517
|
const pathLen = Math.max(4, ...files.map((f) => f.path.length));
|
|
6499
|
-
const row = (id, scope,
|
|
7518
|
+
const row = (id, scope, path6) => `${id.padEnd(idLen)} ${scope.padEnd(scopeLen)} ${path6.padEnd(pathLen)}`;
|
|
6500
7519
|
process.stdout.write(row("ID", "SCOPE", "PATH") + "\n");
|
|
6501
7520
|
process.stdout.write(`${"-".repeat(idLen)} ${"-".repeat(scopeLen)} ${"-".repeat(pathLen)}
|
|
6502
7521
|
`);
|
|
@@ -6679,88 +7698,544 @@ ${result.content}
|
|
|
6679
7698
|
`);
|
|
6680
7699
|
}
|
|
6681
7700
|
} catch (err) {
|
|
6682
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6683
|
-
process.stderr.write(`Error: ${message}
|
|
7701
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7702
|
+
process.stderr.write(`Error: ${message}
|
|
7703
|
+
`);
|
|
7704
|
+
process.exit(1);
|
|
7705
|
+
}
|
|
7706
|
+
});
|
|
7707
|
+
skill.command("uninstall <slug>").description("Uninstall a skill").option("--no-local", "cloud-only uninstall, do not remove local files").option("--json", "output raw JSON response").action(async (slug, opts) => {
|
|
7708
|
+
const client = getIMClient2();
|
|
7709
|
+
try {
|
|
7710
|
+
let res;
|
|
7711
|
+
if (!opts.local) {
|
|
7712
|
+
res = await client.im.evolution.uninstallSkill(slug);
|
|
7713
|
+
} else {
|
|
7714
|
+
res = await client.im.evolution.uninstallSkillLocal(slug);
|
|
7715
|
+
}
|
|
7716
|
+
if (opts.json) {
|
|
7717
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7718
|
+
return;
|
|
7719
|
+
}
|
|
7720
|
+
const result = res;
|
|
7721
|
+
if (result?.ok === false) {
|
|
7722
|
+
process.stderr.write(`Uninstall failed.
|
|
7723
|
+
`);
|
|
7724
|
+
process.exit(1);
|
|
7725
|
+
}
|
|
7726
|
+
process.stdout.write(`Uninstalled: ${slug}
|
|
7727
|
+
`);
|
|
7728
|
+
const removedPaths = result?.data?.removedPaths ?? [];
|
|
7729
|
+
if (removedPaths.length > 0) {
|
|
7730
|
+
process.stdout.write("Local files removed:\n");
|
|
7731
|
+
for (const p of removedPaths) {
|
|
7732
|
+
process.stdout.write(` ${p}
|
|
7733
|
+
`);
|
|
7734
|
+
}
|
|
7735
|
+
} else if (!opts.local) {
|
|
7736
|
+
process.stdout.write("Cloud-only uninstall complete (no local files removed).\n");
|
|
7737
|
+
}
|
|
7738
|
+
} catch (err) {
|
|
7739
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7740
|
+
process.stderr.write(`Error: ${message}
|
|
7741
|
+
`);
|
|
7742
|
+
process.exit(1);
|
|
7743
|
+
}
|
|
7744
|
+
});
|
|
7745
|
+
skill.command("sync").description("Re-sync all installed skills to local filesystem").option("--platform <platform>", "target platform: claude-code, openclaw, opencode, or all", "all").option("--json", "output raw JSON response").action(async (opts) => {
|
|
7746
|
+
const client = getIMClient2();
|
|
7747
|
+
try {
|
|
7748
|
+
const platforms = parsePlatforms(opts.platform);
|
|
7749
|
+
const res = await client.im.evolution.syncSkillsLocal({ platforms });
|
|
7750
|
+
if (opts.json) {
|
|
7751
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7752
|
+
return;
|
|
7753
|
+
}
|
|
7754
|
+
const result = res;
|
|
7755
|
+
const synced = result?.synced ?? 0;
|
|
7756
|
+
const failed = result?.failed ?? 0;
|
|
7757
|
+
process.stdout.write(`Synced: ${synced} skill(s)`);
|
|
7758
|
+
if (failed > 0) {
|
|
7759
|
+
process.stdout.write(`, failed: ${failed}`);
|
|
7760
|
+
}
|
|
7761
|
+
process.stdout.write("\n");
|
|
7762
|
+
const paths = result?.paths ?? [];
|
|
7763
|
+
if (paths.length > 0) {
|
|
7764
|
+
process.stdout.write("Files written:\n");
|
|
7765
|
+
for (const p of paths) {
|
|
7766
|
+
process.stdout.write(` ${p}
|
|
7767
|
+
`);
|
|
7768
|
+
}
|
|
7769
|
+
}
|
|
7770
|
+
} catch (err) {
|
|
7771
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7772
|
+
process.stderr.write(`Error: ${message}
|
|
7773
|
+
`);
|
|
7774
|
+
process.exit(1);
|
|
7775
|
+
}
|
|
7776
|
+
});
|
|
7777
|
+
}
|
|
7778
|
+
|
|
7779
|
+
// src/commands/skill-draft.ts
|
|
7780
|
+
var fs3 = __toESM(require("fs"));
|
|
7781
|
+
function register7(parent, getIMClient2, _getAPIClient) {
|
|
7782
|
+
const skill = parent.commands.find((c) => c.name() === "skill") ?? parent.command("skill");
|
|
7783
|
+
const draft = skill.command("draft").description("Author / patch / regenerate / show / list skill drafts (release201/07)");
|
|
7784
|
+
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) => {
|
|
7785
|
+
if (!opts.manifest) {
|
|
7786
|
+
process.stderr.write("Error: --manifest <path-to-manifest.json> is required\n");
|
|
7787
|
+
process.exit(1);
|
|
7788
|
+
}
|
|
7789
|
+
let manifest;
|
|
7790
|
+
try {
|
|
7791
|
+
manifest = JSON.parse(fs3.readFileSync(opts.manifest, "utf-8"));
|
|
7792
|
+
} catch (err) {
|
|
7793
|
+
process.stderr.write(`Error: failed to read manifest ${opts.manifest}: ${err.message}
|
|
7794
|
+
`);
|
|
7795
|
+
process.exit(1);
|
|
7796
|
+
}
|
|
7797
|
+
const slug = opts.slug ?? manifest.slug;
|
|
7798
|
+
const workspaceId = opts.workspace ?? manifest.workspaceId;
|
|
7799
|
+
const ownerAgentId = opts.ownerAgent ?? manifest.ownerAgentId;
|
|
7800
|
+
if (!slug || !manifest.name || !manifest.description) {
|
|
7801
|
+
process.stderr.write("Error: manifest must include slug, name, description\n");
|
|
7802
|
+
process.exit(1);
|
|
7803
|
+
}
|
|
7804
|
+
if (!workspaceId) {
|
|
7805
|
+
process.stderr.write("Error: --workspace <id> or manifest.workspaceId is required\n");
|
|
7806
|
+
process.exit(1);
|
|
7807
|
+
}
|
|
7808
|
+
if (!Array.isArray(manifest.files) || manifest.files.length < 2) {
|
|
7809
|
+
process.stderr.write("Error: manifest.files[] must include at least SKILL.md + skill.json\n");
|
|
7810
|
+
process.exit(1);
|
|
7811
|
+
}
|
|
7812
|
+
const client = getIMClient2();
|
|
7813
|
+
try {
|
|
7814
|
+
const res = await client.im.skills.draft.create({
|
|
7815
|
+
slug,
|
|
7816
|
+
name: manifest.name,
|
|
7817
|
+
description: manifest.description,
|
|
7818
|
+
workspaceId,
|
|
7819
|
+
ownerAgentId,
|
|
7820
|
+
files: manifest.files,
|
|
7821
|
+
metadata: manifest.metadata
|
|
7822
|
+
});
|
|
7823
|
+
if (opts.json) {
|
|
7824
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7825
|
+
return;
|
|
7826
|
+
}
|
|
7827
|
+
if (!res.ok) {
|
|
7828
|
+
process.stderr.write(`Error: ${res.error ?? "create failed"}
|
|
7829
|
+
`);
|
|
7830
|
+
process.exit(1);
|
|
7831
|
+
}
|
|
7832
|
+
const data = res.data ?? res;
|
|
7833
|
+
process.stdout.write(`Draft submitted.
|
|
7834
|
+
`);
|
|
7835
|
+
process.stdout.write(` id: ${data.id}
|
|
7836
|
+
`);
|
|
7837
|
+
process.stdout.write(` slug: ${data.slug}
|
|
7838
|
+
`);
|
|
7839
|
+
process.stdout.write(` manifest revision: ${data.manifestRevision}
|
|
7840
|
+
`);
|
|
7841
|
+
if (data.reviewTaskId) {
|
|
7842
|
+
process.stdout.write(` review task: ${data.reviewTaskId}
|
|
7843
|
+
`);
|
|
7844
|
+
}
|
|
7845
|
+
const warnings = data.validationWarnings ?? [];
|
|
7846
|
+
if (warnings.length > 0) {
|
|
7847
|
+
process.stdout.write(` warnings:
|
|
7848
|
+
`);
|
|
7849
|
+
for (const w of warnings) {
|
|
7850
|
+
process.stdout.write(` [${w.gate}] ${w.message}
|
|
7851
|
+
`);
|
|
7852
|
+
}
|
|
7853
|
+
}
|
|
7854
|
+
} catch (err) {
|
|
7855
|
+
process.stderr.write(`Error: ${err.message}
|
|
7856
|
+
`);
|
|
7857
|
+
process.exit(1);
|
|
7858
|
+
}
|
|
7859
|
+
});
|
|
7860
|
+
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) => {
|
|
7861
|
+
if (!opts.file) {
|
|
7862
|
+
process.stderr.write("Error: --file <path> is required\n");
|
|
7863
|
+
process.exit(1);
|
|
7864
|
+
}
|
|
7865
|
+
if (!["add", "update", "delete"].includes(opts.op)) {
|
|
7866
|
+
process.stderr.write(`Error: --op must be add | update | delete (got ${opts.op})
|
|
7867
|
+
`);
|
|
7868
|
+
process.exit(1);
|
|
7869
|
+
}
|
|
7870
|
+
let content;
|
|
7871
|
+
if (opts.op !== "delete") {
|
|
7872
|
+
if (opts.contentFile) {
|
|
7873
|
+
try {
|
|
7874
|
+
content = fs3.readFileSync(opts.contentFile, "utf-8");
|
|
7875
|
+
} catch (err) {
|
|
7876
|
+
process.stderr.write(`Error: failed to read --content-file: ${err.message}
|
|
7877
|
+
`);
|
|
7878
|
+
process.exit(1);
|
|
7879
|
+
}
|
|
7880
|
+
} else if (opts.content !== void 0) {
|
|
7881
|
+
content = opts.content;
|
|
7882
|
+
} else {
|
|
7883
|
+
process.stderr.write("Error: --content or --content-file is required for add/update\n");
|
|
7884
|
+
process.exit(1);
|
|
7885
|
+
}
|
|
7886
|
+
}
|
|
7887
|
+
const client = getIMClient2();
|
|
7888
|
+
try {
|
|
7889
|
+
const res = await client.im.skills.draft.patch(draftId, {
|
|
7890
|
+
files: [{ path: opts.file, op: opts.op, content }],
|
|
7891
|
+
reason: opts.reason
|
|
7892
|
+
});
|
|
7893
|
+
if (opts.json) {
|
|
7894
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7895
|
+
return;
|
|
7896
|
+
}
|
|
7897
|
+
if (!res.ok) {
|
|
7898
|
+
process.stderr.write(`Error: ${res.error ?? "patch failed"}
|
|
7899
|
+
`);
|
|
7900
|
+
process.exit(1);
|
|
7901
|
+
}
|
|
7902
|
+
const data = res.data ?? res;
|
|
7903
|
+
process.stdout.write(`Patch applied.
|
|
7904
|
+
`);
|
|
7905
|
+
process.stdout.write(` id: ${data.id}
|
|
7906
|
+
`);
|
|
7907
|
+
process.stdout.write(` manifest revision: ${data.manifestRevision}
|
|
7908
|
+
`);
|
|
7909
|
+
} catch (err) {
|
|
7910
|
+
process.stderr.write(`Error: ${err.message}
|
|
7911
|
+
`);
|
|
7912
|
+
process.exit(1);
|
|
7913
|
+
}
|
|
7914
|
+
});
|
|
7915
|
+
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) => {
|
|
7916
|
+
if (!opts.reason) {
|
|
7917
|
+
process.stderr.write("Error: --reason <text> is required\n");
|
|
7918
|
+
process.exit(1);
|
|
7919
|
+
}
|
|
7920
|
+
const client = getIMClient2();
|
|
7921
|
+
try {
|
|
7922
|
+
const res = await client.im.skills.draft.regenerate(draftId, { reason: opts.reason });
|
|
7923
|
+
if (opts.json) {
|
|
7924
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7925
|
+
return;
|
|
7926
|
+
}
|
|
7927
|
+
if (!res.ok) {
|
|
7928
|
+
process.stderr.write(`Error: ${res.error ?? "regenerate failed"}
|
|
7929
|
+
`);
|
|
7930
|
+
process.exit(1);
|
|
7931
|
+
}
|
|
7932
|
+
const data = res.data ?? res;
|
|
7933
|
+
process.stdout.write(`Regenerate requested.
|
|
7934
|
+
`);
|
|
7935
|
+
process.stdout.write(` id: ${data.id}
|
|
7936
|
+
`);
|
|
7937
|
+
process.stdout.write(` session id: ${data.sessionId}
|
|
7938
|
+
`);
|
|
7939
|
+
} catch (err) {
|
|
7940
|
+
process.stderr.write(`Error: ${err.message}
|
|
7941
|
+
`);
|
|
7942
|
+
process.exit(1);
|
|
7943
|
+
}
|
|
7944
|
+
});
|
|
7945
|
+
draft.command("show <draft-id>").description("Show a draft's manifest + revision history").option("--json", "Output raw JSON response").action(async (draftId, opts) => {
|
|
7946
|
+
const client = getIMClient2();
|
|
7947
|
+
try {
|
|
7948
|
+
const res = await client.im.skills.draft.show(draftId);
|
|
7949
|
+
if (opts.json) {
|
|
7950
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7951
|
+
return;
|
|
7952
|
+
}
|
|
7953
|
+
if (!res.ok) {
|
|
7954
|
+
process.stderr.write(`Error: ${res.error ?? "show failed"}
|
|
7955
|
+
`);
|
|
7956
|
+
process.exit(1);
|
|
7957
|
+
}
|
|
7958
|
+
const data = res.data ?? res;
|
|
7959
|
+
process.stdout.write(`Draft ${data.id}
|
|
7960
|
+
`);
|
|
7961
|
+
process.stdout.write(` slug: ${data.slug}
|
|
7962
|
+
`);
|
|
7963
|
+
process.stdout.write(` name: ${data.name}
|
|
7964
|
+
`);
|
|
7965
|
+
process.stdout.write(` status: ${data.status}
|
|
7966
|
+
`);
|
|
7967
|
+
process.stdout.write(` workspace: ${data.workspaceId}
|
|
7968
|
+
`);
|
|
7969
|
+
process.stdout.write(` ownerAgent: ${data.ownerAgentId}
|
|
7970
|
+
`);
|
|
7971
|
+
process.stdout.write(` revision: ${data.manifestRevision}
|
|
7972
|
+
`);
|
|
7973
|
+
process.stdout.write(` license: ${data.license}
|
|
7974
|
+
`);
|
|
7975
|
+
process.stdout.write(` compatibility: ${(data.compatibility ?? []).join(", ")}
|
|
7976
|
+
`);
|
|
7977
|
+
process.stdout.write(` files:
|
|
7978
|
+
`);
|
|
7979
|
+
for (const f of data.files ?? []) {
|
|
7980
|
+
process.stdout.write(` ${f.path} (${f.size}B, sha256:${(f.sha256 ?? "").slice(0, 8)}\u2026)
|
|
7981
|
+
`);
|
|
7982
|
+
}
|
|
7983
|
+
} catch (err) {
|
|
7984
|
+
process.stderr.write(`Error: ${err.message}
|
|
7985
|
+
`);
|
|
7986
|
+
process.exit(1);
|
|
7987
|
+
}
|
|
7988
|
+
});
|
|
7989
|
+
draft.command("list").description("List drafts in a workspace").option("--workspace <id>", "Workspace id (required)").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
7990
|
+
if (!opts.workspace) {
|
|
7991
|
+
process.stderr.write("Error: --workspace <id> is required\n");
|
|
7992
|
+
process.exit(1);
|
|
7993
|
+
}
|
|
7994
|
+
const client = getIMClient2();
|
|
7995
|
+
try {
|
|
7996
|
+
const res = await client.im.skills.draft.list(opts.workspace);
|
|
7997
|
+
if (opts.json) {
|
|
7998
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
7999
|
+
return;
|
|
8000
|
+
}
|
|
8001
|
+
if (!res.ok) {
|
|
8002
|
+
process.stderr.write(`Error: ${res.error ?? "list failed"}
|
|
8003
|
+
`);
|
|
8004
|
+
process.exit(1);
|
|
8005
|
+
}
|
|
8006
|
+
const drafts = res.data ?? [];
|
|
8007
|
+
if (drafts.length === 0) {
|
|
8008
|
+
process.stdout.write("No drafts in this workspace.\n");
|
|
8009
|
+
return;
|
|
8010
|
+
}
|
|
8011
|
+
process.stdout.write(`Drafts (${drafts.length}):
|
|
8012
|
+
`);
|
|
8013
|
+
for (const d of drafts) {
|
|
8014
|
+
process.stdout.write(` ${d.id} ${d.slug} ${(d.name ?? "").slice(0, 32)} rev:${(d.contentManifestRevision ?? "").slice(0, 8)}\u2026 updated:${d.updatedAt}
|
|
8015
|
+
`);
|
|
8016
|
+
}
|
|
8017
|
+
} catch (err) {
|
|
8018
|
+
process.stderr.write(`Error: ${err.message}
|
|
8019
|
+
`);
|
|
8020
|
+
process.exit(1);
|
|
8021
|
+
}
|
|
8022
|
+
});
|
|
8023
|
+
}
|
|
8024
|
+
|
|
8025
|
+
// src/commands/code-grep.ts
|
|
8026
|
+
var fs4 = __toESM(require("fs"));
|
|
8027
|
+
var path2 = __toESM(require("path"));
|
|
8028
|
+
var crypto2 = __toESM(require("crypto"));
|
|
8029
|
+
var os = __toESM(require("os"));
|
|
8030
|
+
var DEFAULT_MAX_MATCHES = 50;
|
|
8031
|
+
var SCRATCH_DIR_GLOB = path2.join(os.homedir(), ".prismer", "agents");
|
|
8032
|
+
function isPathWhitelisted(repo) {
|
|
8033
|
+
const abs = path2.resolve(repo);
|
|
8034
|
+
const workspaceRoot = process.env.PRISMER_WORKSPACE_ROOT;
|
|
8035
|
+
if (workspaceRoot) {
|
|
8036
|
+
const wsAbs = path2.resolve(workspaceRoot);
|
|
8037
|
+
if (abs === wsAbs || abs.startsWith(wsAbs + path2.sep)) return true;
|
|
8038
|
+
}
|
|
8039
|
+
if (abs.startsWith(SCRATCH_DIR_GLOB + path2.sep)) {
|
|
8040
|
+
const rel = abs.slice(SCRATCH_DIR_GLOB.length + 1);
|
|
8041
|
+
const parts = rel.split(path2.sep);
|
|
8042
|
+
if (parts.length >= 2 && parts[1] === "scratch") return true;
|
|
8043
|
+
}
|
|
8044
|
+
return false;
|
|
8045
|
+
}
|
|
8046
|
+
function walk(dir, glob, acc) {
|
|
8047
|
+
let entries;
|
|
8048
|
+
try {
|
|
8049
|
+
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
8050
|
+
} catch {
|
|
8051
|
+
return;
|
|
8052
|
+
}
|
|
8053
|
+
for (const e of entries) {
|
|
8054
|
+
const full = path2.join(dir, e.name);
|
|
8055
|
+
if (e.isDirectory()) {
|
|
8056
|
+
if (e.name === "node_modules" || e.name === ".git" || e.name === "dist" || e.name === "build") continue;
|
|
8057
|
+
walk(full, glob, acc);
|
|
8058
|
+
} else if (e.isFile()) {
|
|
8059
|
+
if (!glob || matchGlob(e.name, glob)) acc.push(full);
|
|
8060
|
+
}
|
|
8061
|
+
}
|
|
8062
|
+
}
|
|
8063
|
+
function matchGlob(name, glob) {
|
|
8064
|
+
if (glob.startsWith("*.")) {
|
|
8065
|
+
return name.endsWith(glob.slice(1));
|
|
8066
|
+
}
|
|
8067
|
+
return name === glob;
|
|
8068
|
+
}
|
|
8069
|
+
function register8(parent, _getIMClient, _getAPIClient) {
|
|
8070
|
+
const code = parent.command("code").description("Local code-source helpers for skill-authoring (release201/07)");
|
|
8071
|
+
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) => {
|
|
8072
|
+
if (!opts.repo) {
|
|
8073
|
+
process.stderr.write("Error: --repo <abs-path> is required\n");
|
|
8074
|
+
process.exit(1);
|
|
8075
|
+
}
|
|
8076
|
+
if (!isPathWhitelisted(opts.repo)) {
|
|
8077
|
+
process.stderr.write(
|
|
8078
|
+
`Error: --repo path ${opts.repo} is not whitelisted; only PRISMER_WORKSPACE_ROOT or ~/.prismer/agents/<id>/scratch are allowed
|
|
8079
|
+
`
|
|
8080
|
+
);
|
|
8081
|
+
process.exit(1);
|
|
8082
|
+
}
|
|
8083
|
+
const maxMatches = parseInt(opts.maxMatches, 10) || DEFAULT_MAX_MATCHES;
|
|
8084
|
+
let re;
|
|
8085
|
+
try {
|
|
8086
|
+
re = new RegExp(pattern);
|
|
8087
|
+
} catch (err) {
|
|
8088
|
+
process.stderr.write(`Error: invalid regex pattern: ${err.message}
|
|
6684
8089
|
`);
|
|
6685
8090
|
process.exit(1);
|
|
6686
8091
|
}
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
const
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
8092
|
+
const files = [];
|
|
8093
|
+
walk(opts.repo, opts.glob, files);
|
|
8094
|
+
const results = [];
|
|
8095
|
+
for (const file of files) {
|
|
8096
|
+
if (results.length >= maxMatches) break;
|
|
8097
|
+
let body;
|
|
8098
|
+
try {
|
|
8099
|
+
body = fs4.readFileSync(file, "utf-8");
|
|
8100
|
+
} catch {
|
|
8101
|
+
continue;
|
|
6696
8102
|
}
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
8103
|
+
const lines = body.split("\n");
|
|
8104
|
+
for (let i = 0; i < lines.length; i++) {
|
|
8105
|
+
if (re.test(lines[i])) {
|
|
8106
|
+
const startLine = Math.max(0, i - 1);
|
|
8107
|
+
const endLine = Math.min(lines.length, i + 4);
|
|
8108
|
+
const snippet = lines.slice(startLine, endLine).join("\n");
|
|
8109
|
+
const sha256 = crypto2.createHash("sha256").update(snippet).digest("hex");
|
|
8110
|
+
results.push({
|
|
8111
|
+
path: path2.relative(opts.repo, file),
|
|
8112
|
+
line: i + 1,
|
|
8113
|
+
snippet,
|
|
8114
|
+
sha256
|
|
8115
|
+
});
|
|
8116
|
+
if (results.length >= maxMatches) break;
|
|
8117
|
+
}
|
|
6700
8118
|
}
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
8119
|
+
}
|
|
8120
|
+
process.stdout.write(JSON.stringify(results, null, 2) + "\n");
|
|
8121
|
+
});
|
|
8122
|
+
}
|
|
8123
|
+
|
|
8124
|
+
// src/commands/service-introspect.ts
|
|
8125
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
8126
|
+
async function fetchWithTimeout(url, init = {}) {
|
|
8127
|
+
const ctrl = new AbortController();
|
|
8128
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
8129
|
+
try {
|
|
8130
|
+
return await fetch(url, { ...init, signal: ctrl.signal });
|
|
8131
|
+
} finally {
|
|
8132
|
+
clearTimeout(timer);
|
|
8133
|
+
}
|
|
8134
|
+
}
|
|
8135
|
+
async function probeOpenApi(url) {
|
|
8136
|
+
const base = url.replace(/\/$/, "");
|
|
8137
|
+
const candidates = [
|
|
8138
|
+
`${base}/openapi.json`,
|
|
8139
|
+
`${base}/swagger.json`,
|
|
8140
|
+
`${base}/api/openapi.json`,
|
|
8141
|
+
`${base}/.well-known/openapi.json`
|
|
8142
|
+
];
|
|
8143
|
+
for (const candidate of candidates) {
|
|
8144
|
+
try {
|
|
8145
|
+
const res = await fetchWithTimeout(candidate, { headers: { Accept: "application/json" } });
|
|
8146
|
+
if (res.ok) {
|
|
8147
|
+
const spec = await res.json();
|
|
8148
|
+
if (spec && (spec.openapi || spec.swagger)) {
|
|
8149
|
+
return { found: true, spec, specUrl: candidate };
|
|
8150
|
+
}
|
|
6706
8151
|
}
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
8152
|
+
} catch {
|
|
8153
|
+
}
|
|
8154
|
+
}
|
|
8155
|
+
return { found: false };
|
|
8156
|
+
}
|
|
8157
|
+
async function probeMcp(url) {
|
|
8158
|
+
try {
|
|
8159
|
+
const res = await fetchWithTimeout(url, {
|
|
8160
|
+
method: "POST",
|
|
8161
|
+
headers: { "Content-Type": "application/json" },
|
|
8162
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list" })
|
|
8163
|
+
});
|
|
8164
|
+
if (!res.ok) return { found: false };
|
|
8165
|
+
const body = await res.json();
|
|
8166
|
+
if (body?.result?.tools) {
|
|
8167
|
+
let resources = [];
|
|
8168
|
+
try {
|
|
8169
|
+
const r = await fetchWithTimeout(url, {
|
|
8170
|
+
method: "POST",
|
|
8171
|
+
headers: { "Content-Type": "application/json" },
|
|
8172
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "resources/list" })
|
|
8173
|
+
});
|
|
8174
|
+
if (r.ok) {
|
|
8175
|
+
const rb = await r.json();
|
|
8176
|
+
if (Array.isArray(rb?.result?.resources)) resources = rb.result.resources;
|
|
6715
8177
|
}
|
|
6716
|
-
}
|
|
6717
|
-
process.stdout.write("Cloud-only uninstall complete (no local files removed).\n");
|
|
8178
|
+
} catch {
|
|
6718
8179
|
}
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
8180
|
+
return { found: true, tools: body.result.tools, resources };
|
|
8181
|
+
}
|
|
8182
|
+
} catch {
|
|
8183
|
+
}
|
|
8184
|
+
return { found: false };
|
|
8185
|
+
}
|
|
8186
|
+
function register9(parent, _getIMClient, _getAPIClient) {
|
|
8187
|
+
const svc = parent.command("service").description("Local service-endpoint helpers for skill-authoring (release201/07)");
|
|
8188
|
+
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) => {
|
|
8189
|
+
const protocol = opts.protocol ?? "auto";
|
|
8190
|
+
if (protocol !== "auto" && protocol !== "mcp" && protocol !== "openapi") {
|
|
8191
|
+
process.stderr.write(`Error: --protocol must be auto | mcp | openapi (got ${protocol})
|
|
6722
8192
|
`);
|
|
6723
8193
|
process.exit(1);
|
|
6724
8194
|
}
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
process.stdout.write(JSON.stringify(
|
|
8195
|
+
const result = { kind: "unknown", tools: [], endpoints: [], probedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
8196
|
+
if (protocol === "mcp" || protocol === "auto") {
|
|
8197
|
+
const mcp = await probeMcp(url);
|
|
8198
|
+
if (mcp.found) {
|
|
8199
|
+
result.kind = "mcp";
|
|
8200
|
+
result.tools = mcp.tools ?? [];
|
|
8201
|
+
result.resources = mcp.resources ?? [];
|
|
8202
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
6733
8203
|
return;
|
|
6734
8204
|
}
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
const
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
8205
|
+
}
|
|
8206
|
+
if (protocol === "openapi" || protocol === "auto") {
|
|
8207
|
+
const oa = await probeOpenApi(url);
|
|
8208
|
+
if (oa.found) {
|
|
8209
|
+
result.kind = "openapi";
|
|
8210
|
+
result.specUrl = oa.specUrl;
|
|
8211
|
+
const paths = oa.spec?.paths ?? {};
|
|
8212
|
+
for (const p of Object.keys(paths)) {
|
|
8213
|
+
for (const method of Object.keys(paths[p])) {
|
|
8214
|
+
if (["get", "post", "put", "delete", "patch"].includes(method.toLowerCase())) {
|
|
8215
|
+
const op = paths[p][method];
|
|
8216
|
+
result.endpoints.push({
|
|
8217
|
+
path: p,
|
|
8218
|
+
method: method.toUpperCase(),
|
|
8219
|
+
operationId: op?.operationId ?? null,
|
|
8220
|
+
summary: op?.summary ?? null
|
|
8221
|
+
});
|
|
8222
|
+
}
|
|
8223
|
+
}
|
|
6749
8224
|
}
|
|
8225
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
8226
|
+
return;
|
|
6750
8227
|
}
|
|
6751
|
-
} catch (err) {
|
|
6752
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6753
|
-
process.stderr.write(`Error: ${message}
|
|
6754
|
-
`);
|
|
6755
|
-
process.exit(1);
|
|
6756
8228
|
}
|
|
8229
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
6757
8230
|
});
|
|
6758
8231
|
}
|
|
6759
8232
|
|
|
6760
8233
|
// src/commands/files.ts
|
|
6761
|
-
function
|
|
8234
|
+
function register10(parent, getIMClient2, _getAPIClient) {
|
|
6762
8235
|
const file = parent.command("file").description("File upload, transfer, quota, and type management");
|
|
6763
|
-
file.command("upload <path>").description(
|
|
8236
|
+
file.command("upload <path>").description(
|
|
8237
|
+
"Upload bytes only \u2192 returns an upload ID + CDN URL. NOT a delivery: nothing reaches the user. To deliver an artifact use `cloud deliver <path>`."
|
|
8238
|
+
).option("--mime <type>", "Override MIME type (e.g. image/png)").option("--json", "Output raw JSON response").action(async (filePath, opts) => {
|
|
6764
8239
|
const client = getIMClient2();
|
|
6765
8240
|
try {
|
|
6766
8241
|
const uploadOpts = {};
|
|
@@ -6780,13 +8255,39 @@ function register7(parent, getIMClient2, _getAPIClient) {
|
|
|
6780
8255
|
`);
|
|
6781
8256
|
process.stdout.write(`MIME: ${res.mimeType}
|
|
6782
8257
|
`);
|
|
8258
|
+
process.stdout.write(
|
|
8259
|
+
"Note: this only uploaded bytes \u2014 nothing was delivered to the user. To deliver an artifact, run `cloud deliver <abs-path>`.\n"
|
|
8260
|
+
);
|
|
6783
8261
|
} catch (err) {
|
|
6784
8262
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
6785
8263
|
`);
|
|
6786
8264
|
process.exit(1);
|
|
6787
8265
|
}
|
|
6788
8266
|
});
|
|
6789
|
-
file.command("send <conversation-id> <path>").description(
|
|
8267
|
+
file.command("send <conversation-id> <path>").description(
|
|
8268
|
+
"Send a file as its OWN standalone message (ad-hoc sharing). For a final deliverable use `cloud deliver` instead \u2014 do not run both on the same file (double-delivery)."
|
|
8269
|
+
).option("-c, --content <text>", "Optional text caption to accompany the file").option("--mime <type>", "Override MIME type").option("--run-id <id>", "dispatch run/task id (from <execution_context>; env fallback PRISMER_TASK_ID/RUN_ID)").option("--conversation-id <id>", "conversation id (alternative source; positional <conversation-id> takes precedence)").option("--daemon-port <port>", "daemon local-server port (env fallback PRISMER_DAEMON_PORT, default 3210)").option("--json", "Output raw JSON response").action(async (conversationIdArg, filePath, opts) => {
|
|
8270
|
+
const conversationId = conversationIdArg || opts.conversationId || "";
|
|
8271
|
+
const proxy = detectDeliverProxy({
|
|
8272
|
+
runId: opts.runId,
|
|
8273
|
+
conversationId,
|
|
8274
|
+
daemonPort: opts.daemonPort
|
|
8275
|
+
});
|
|
8276
|
+
if (proxy) {
|
|
8277
|
+
const result = await proxyDeliver(proxy, filePath, "send", conversationId);
|
|
8278
|
+
if (!result.ok) {
|
|
8279
|
+
process.stderr.write(`Error: ${result.error ?? "delivery failed"}
|
|
8280
|
+
`);
|
|
8281
|
+
process.exit(1);
|
|
8282
|
+
}
|
|
8283
|
+
if (opts.json) {
|
|
8284
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
8285
|
+
return;
|
|
8286
|
+
}
|
|
8287
|
+
process.stdout.write(`File sent to conversation ${conversationId} (assetId: ${result.assetId ?? "-"})
|
|
8288
|
+
`);
|
|
8289
|
+
return;
|
|
8290
|
+
}
|
|
6790
8291
|
const client = getIMClient2();
|
|
6791
8292
|
try {
|
|
6792
8293
|
const sendOpts = {};
|
|
@@ -6872,8 +8373,43 @@ function register7(parent, getIMClient2, _getAPIClient) {
|
|
|
6872
8373
|
}
|
|
6873
8374
|
|
|
6874
8375
|
// src/commands/workspace.ts
|
|
6875
|
-
function
|
|
6876
|
-
|
|
8376
|
+
function fail(message) {
|
|
8377
|
+
process.stderr.write(`Error: ${message}
|
|
8378
|
+
`);
|
|
8379
|
+
process.exit(1);
|
|
8380
|
+
}
|
|
8381
|
+
function emitJson(data, json, fallback) {
|
|
8382
|
+
if (json) {
|
|
8383
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
8384
|
+
return;
|
|
8385
|
+
}
|
|
8386
|
+
fallback();
|
|
8387
|
+
}
|
|
8388
|
+
function parseAdminMemberRole(raw) {
|
|
8389
|
+
if (raw === "admin" || raw === "member") return raw;
|
|
8390
|
+
if (raw === void 0) return "member";
|
|
8391
|
+
if (raw === "owner") {
|
|
8392
|
+
fail("--role=owner is not allowed via CLI; use ownership transfer (v2.1+ RFC, release201/16 \xA75.2)");
|
|
8393
|
+
}
|
|
8394
|
+
fail("--role must be admin|member");
|
|
8395
|
+
}
|
|
8396
|
+
function printMemberList(items) {
|
|
8397
|
+
if (items.length === 0) {
|
|
8398
|
+
process.stdout.write("No members in this workspace.\n");
|
|
8399
|
+
return;
|
|
8400
|
+
}
|
|
8401
|
+
process.stdout.write(
|
|
8402
|
+
"ID".padEnd(28) + "ROLE".padEnd(10) + "IM_USER_ID".padEnd(38) + "JOINED\n"
|
|
8403
|
+
);
|
|
8404
|
+
for (const m of items) {
|
|
8405
|
+
process.stdout.write(
|
|
8406
|
+
`${m.id.padEnd(28)}${m.role.padEnd(10)}${m.memberImUserId.padEnd(38)}${m.joinedAt}
|
|
8407
|
+
`
|
|
8408
|
+
);
|
|
8409
|
+
}
|
|
8410
|
+
}
|
|
8411
|
+
function register11(parent, getIMClient2, _getAPIClient) {
|
|
8412
|
+
const workspace = parent.command("workspace").description("Workspace management \u2014 init, groups, agent assignment, and members (release201/16)");
|
|
6877
8413
|
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
8414
|
const client = getIMClient2();
|
|
6879
8415
|
try {
|
|
@@ -6934,62 +8470,299 @@ function register8(parent, getIMClient2, _getAPIClient) {
|
|
|
6934
8470
|
process.exit(1);
|
|
6935
8471
|
}
|
|
6936
8472
|
});
|
|
6937
|
-
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) => {
|
|
8473
|
+
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) => {
|
|
8474
|
+
const client = getIMClient2();
|
|
8475
|
+
try {
|
|
8476
|
+
const res = await client.im.workspace.addAgent(workspaceId, agentId);
|
|
8477
|
+
if (!res.ok) {
|
|
8478
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
8479
|
+
`);
|
|
8480
|
+
process.exit(1);
|
|
8481
|
+
}
|
|
8482
|
+
if (opts.json) {
|
|
8483
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
8484
|
+
return;
|
|
8485
|
+
}
|
|
8486
|
+
process.stdout.write(`Agent ${agentId} added to workspace ${workspaceId}.
|
|
8487
|
+
`);
|
|
8488
|
+
} catch (err) {
|
|
8489
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
8490
|
+
`);
|
|
8491
|
+
process.exit(1);
|
|
8492
|
+
}
|
|
8493
|
+
});
|
|
8494
|
+
workspace.command("agents <workspace-id>").description("List agents in a workspace").option("--json", "Output raw JSON response").action(async (workspaceId, opts) => {
|
|
8495
|
+
const client = getIMClient2();
|
|
8496
|
+
try {
|
|
8497
|
+
const res = await client.im.workspace.listAgents(workspaceId);
|
|
8498
|
+
if (!res.ok) {
|
|
8499
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
8500
|
+
`);
|
|
8501
|
+
process.exit(1);
|
|
8502
|
+
}
|
|
8503
|
+
const agents = res.data || [];
|
|
8504
|
+
if (opts.json) {
|
|
8505
|
+
process.stdout.write(JSON.stringify(agents, null, 2) + "\n");
|
|
8506
|
+
return;
|
|
8507
|
+
}
|
|
8508
|
+
if (agents.length === 0) {
|
|
8509
|
+
process.stdout.write("No agents in this workspace.\n");
|
|
8510
|
+
return;
|
|
8511
|
+
}
|
|
8512
|
+
process.stdout.write("Agent ID".padEnd(36) + "Type".padEnd(14) + "Name\n");
|
|
8513
|
+
for (const a of agents) {
|
|
8514
|
+
process.stdout.write(
|
|
8515
|
+
`${(a.agentId || a.id || "").padEnd(36)}${(a.agentType || "").padEnd(14)}${a.name || a.displayName || ""}
|
|
8516
|
+
`
|
|
8517
|
+
);
|
|
8518
|
+
}
|
|
8519
|
+
} catch (err) {
|
|
8520
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
8521
|
+
`);
|
|
8522
|
+
process.exit(1);
|
|
8523
|
+
}
|
|
8524
|
+
});
|
|
8525
|
+
const member = workspace.command("member").description("Workspace membership management (release201/16)");
|
|
8526
|
+
member.command("list <workspaceId>").description("List members of a workspace (any member can read)").option("--json", "Output JSON").action(async (workspaceId, opts) => {
|
|
8527
|
+
const client = getIMClient2();
|
|
8528
|
+
const res = await client.workspaces.members.list(workspaceId);
|
|
8529
|
+
if (!res.ok || !res.data) fail(res.error?.message || "list members failed");
|
|
8530
|
+
emitJson(res.data, opts.json === true, () => printMemberList(res.data));
|
|
8531
|
+
});
|
|
8532
|
+
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) => {
|
|
8533
|
+
const role = parseAdminMemberRole(opts.role);
|
|
8534
|
+
const client = getIMClient2();
|
|
8535
|
+
const res = await client.workspaces.members.add(workspaceId, {
|
|
8536
|
+
memberImUserId: opts.user.trim(),
|
|
8537
|
+
role
|
|
8538
|
+
});
|
|
8539
|
+
if (!res.ok || !res.data) fail(res.error?.message || "add member failed");
|
|
8540
|
+
emitJson(res.data, opts.json === true, () => {
|
|
8541
|
+
const m = res.data;
|
|
8542
|
+
process.stdout.write(`Member added: ${m.memberImUserId} \u2192 ${m.role} (${m.id})
|
|
8543
|
+
`);
|
|
8544
|
+
});
|
|
8545
|
+
});
|
|
8546
|
+
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) => {
|
|
8547
|
+
const role = parseAdminMemberRole(opts.role);
|
|
8548
|
+
const client = getIMClient2();
|
|
8549
|
+
const res = await client.workspaces.members.update(workspaceId, memberId, { role });
|
|
8550
|
+
if (!res.ok || !res.data) fail(res.error?.message || "update member failed");
|
|
8551
|
+
emitJson(res.data, opts.json === true, () => {
|
|
8552
|
+
const m = res.data;
|
|
8553
|
+
process.stdout.write(`Member ${m.id} role \u2192 ${m.role}
|
|
8554
|
+
`);
|
|
8555
|
+
});
|
|
8556
|
+
});
|
|
8557
|
+
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) => {
|
|
8558
|
+
const client = getIMClient2();
|
|
8559
|
+
const res = await client.workspaces.members.remove(workspaceId, memberId);
|
|
8560
|
+
if (!res.ok || !res.data) fail(res.error?.message || "remove member failed");
|
|
8561
|
+
emitJson(res.data, opts.json === true, () => {
|
|
8562
|
+
const r = res.data;
|
|
8563
|
+
process.stdout.write(
|
|
8564
|
+
`Member removed: ${r.removed.memberImUserId} (${r.removed.id}); project memberships cascaded: ${r.projectMembershipsRemoved}
|
|
8565
|
+
`
|
|
8566
|
+
);
|
|
8567
|
+
});
|
|
8568
|
+
});
|
|
8569
|
+
}
|
|
8570
|
+
|
|
8571
|
+
// src/commands/project.ts
|
|
8572
|
+
function fail2(message) {
|
|
8573
|
+
process.stderr.write(`Error: ${message}
|
|
8574
|
+
`);
|
|
8575
|
+
process.exit(1);
|
|
8576
|
+
}
|
|
8577
|
+
function emit(data, json, lines) {
|
|
8578
|
+
if (json) {
|
|
8579
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
8580
|
+
return;
|
|
8581
|
+
}
|
|
8582
|
+
lines();
|
|
8583
|
+
}
|
|
8584
|
+
function parsePrincipal(raw) {
|
|
8585
|
+
const idx = raw.indexOf(":");
|
|
8586
|
+
if (idx <= 0) fail2("--principal must be `user:<id>` or `agent:<id>`");
|
|
8587
|
+
const kind = raw.slice(0, idx);
|
|
8588
|
+
const id = raw.slice(idx + 1).trim();
|
|
8589
|
+
if (kind !== "user" && kind !== "agent") fail2("principal kind must be user|agent");
|
|
8590
|
+
if (!id) fail2("principal id must be non-empty");
|
|
8591
|
+
return { kind, id };
|
|
8592
|
+
}
|
|
8593
|
+
function parseRole(raw) {
|
|
8594
|
+
if (raw === void 0) return void 0;
|
|
8595
|
+
if (raw === "owner" || raw === "contributor" || raw === "observer") return raw;
|
|
8596
|
+
fail2("--role must be owner|contributor|observer");
|
|
8597
|
+
}
|
|
8598
|
+
function printProjectList(items) {
|
|
8599
|
+
if (items.length === 0) {
|
|
8600
|
+
process.stdout.write("No projects found.\n");
|
|
8601
|
+
return;
|
|
8602
|
+
}
|
|
8603
|
+
process.stdout.write(
|
|
8604
|
+
"ID".padEnd(28) + "SLUG".padEnd(20) + "STATUS".padEnd(10) + "MEMBERS".padEnd(10) + "NAME\n"
|
|
8605
|
+
);
|
|
8606
|
+
for (const p of items) {
|
|
8607
|
+
process.stdout.write(
|
|
8608
|
+
`${p.id.padEnd(28)}${p.slug.padEnd(20)}${p.status.padEnd(10)}${String(p.memberCount).padEnd(10)}${p.name}
|
|
8609
|
+
`
|
|
8610
|
+
);
|
|
8611
|
+
}
|
|
8612
|
+
}
|
|
8613
|
+
function printProject(p, heading = "Project") {
|
|
8614
|
+
process.stdout.write(`${heading}: ${p.name}
|
|
8615
|
+
`);
|
|
8616
|
+
process.stdout.write(` id ${p.id}
|
|
8617
|
+
`);
|
|
8618
|
+
process.stdout.write(` slug ${p.slug}
|
|
8619
|
+
`);
|
|
8620
|
+
process.stdout.write(` workspaceId ${p.workspaceId}
|
|
8621
|
+
`);
|
|
8622
|
+
process.stdout.write(` status ${p.status}
|
|
8623
|
+
`);
|
|
8624
|
+
process.stdout.write(` owner ${p.ownerUserId}
|
|
8625
|
+
`);
|
|
8626
|
+
if ("memberCount" in p) process.stdout.write(` members ${p.memberCount}
|
|
8627
|
+
`);
|
|
8628
|
+
if (p.description) process.stdout.write(` description ${p.description}
|
|
8629
|
+
`);
|
|
8630
|
+
if (p.archivedAt) process.stdout.write(` archivedAt ${p.archivedAt}
|
|
8631
|
+
`);
|
|
8632
|
+
process.stdout.write(` createdAt ${p.createdAt}
|
|
8633
|
+
`);
|
|
8634
|
+
process.stdout.write(` updatedAt ${p.updatedAt}
|
|
8635
|
+
`);
|
|
8636
|
+
}
|
|
8637
|
+
function printMemberList2(items) {
|
|
8638
|
+
if (items.length === 0) {
|
|
8639
|
+
process.stdout.write("No members in this project.\n");
|
|
8640
|
+
return;
|
|
8641
|
+
}
|
|
8642
|
+
process.stdout.write(
|
|
8643
|
+
"ID".padEnd(28) + "KIND".padEnd(8) + "PRINCIPAL".padEnd(38) + "ROLE".padEnd(14) + "JOINED\n"
|
|
8644
|
+
);
|
|
8645
|
+
for (const m of items) {
|
|
8646
|
+
process.stdout.write(
|
|
8647
|
+
`${m.id.padEnd(28)}${m.principalKind.padEnd(8)}${m.principalId.padEnd(38)}${m.role.padEnd(14)}${m.joinedAt}
|
|
8648
|
+
`
|
|
8649
|
+
);
|
|
8650
|
+
}
|
|
8651
|
+
}
|
|
8652
|
+
function register12(parent, getIMClient2, _getAPIClient) {
|
|
8653
|
+
const project = parent.command("project").description("Project scope (release201/09) \u2014 CRUD + membership management");
|
|
8654
|
+
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) => {
|
|
8655
|
+
if (opts.status && opts.status !== "active" && opts.status !== "archived") {
|
|
8656
|
+
fail2("--status must be active|archived");
|
|
8657
|
+
}
|
|
8658
|
+
const client = getIMClient2();
|
|
8659
|
+
const res = await client.projects.list({
|
|
8660
|
+
workspaceId: opts.workspace,
|
|
8661
|
+
status: opts.status,
|
|
8662
|
+
search: opts.search,
|
|
8663
|
+
limit: opts.limit,
|
|
8664
|
+
offset: opts.offset
|
|
8665
|
+
});
|
|
8666
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "list failed");
|
|
8667
|
+
const data = res.data;
|
|
8668
|
+
emit(data, opts.json === true, () => {
|
|
8669
|
+
printProjectList(data.items);
|
|
8670
|
+
process.stdout.write(`
|
|
8671
|
+
Total: ${data.total}
|
|
8672
|
+
`);
|
|
8673
|
+
});
|
|
8674
|
+
});
|
|
8675
|
+
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) => {
|
|
8676
|
+
const client = getIMClient2();
|
|
8677
|
+
const res = await client.projects.create({
|
|
8678
|
+
workspaceId: opts.workspace,
|
|
8679
|
+
slug: opts.slug,
|
|
8680
|
+
name: opts.name,
|
|
8681
|
+
description: opts.description ?? null
|
|
8682
|
+
});
|
|
8683
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "create failed");
|
|
8684
|
+
emit(res.data, opts.json === true, () => printProject(res.data, "Project created"));
|
|
8685
|
+
});
|
|
8686
|
+
project.command("show <projectId>").description("Show a project (with member count)").option("--json", "Output JSON").action(async (projectId, opts) => {
|
|
8687
|
+
const client = getIMClient2();
|
|
8688
|
+
const res = await client.projects.get(projectId);
|
|
8689
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "show failed");
|
|
8690
|
+
emit(res.data, opts.json === true, () => printProject(res.data));
|
|
8691
|
+
});
|
|
8692
|
+
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) => {
|
|
8693
|
+
if (opts.archive && opts.unarchive) fail2("--archive and --unarchive are mutually exclusive");
|
|
8694
|
+
const patch = {};
|
|
8695
|
+
if (opts.name !== void 0) patch.name = opts.name;
|
|
8696
|
+
if (opts.description !== void 0) patch.description = opts.description === "" ? null : opts.description;
|
|
8697
|
+
if (opts.archive) patch.status = "archived";
|
|
8698
|
+
if (opts.unarchive) patch.status = "active";
|
|
8699
|
+
if (Object.keys(patch).length === 0) fail2("Nothing to update \u2014 pass --name / --description / --archive / --unarchive");
|
|
6938
8700
|
const client = getIMClient2();
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
8701
|
+
const res = await client.projects.update(projectId, patch);
|
|
8702
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "update failed");
|
|
8703
|
+
emit(res.data, opts.json === true, () => printProject(res.data, "Project updated"));
|
|
8704
|
+
});
|
|
8705
|
+
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) => {
|
|
8706
|
+
const cascade = opts.cascade ?? "archive";
|
|
8707
|
+
if (cascade !== "archive" && cascade !== "null" && cascade !== "hard") {
|
|
8708
|
+
fail2("--cascade must be archive|null|hard");
|
|
8709
|
+
}
|
|
8710
|
+
const client = getIMClient2();
|
|
8711
|
+
const res = await client.projects.delete(projectId, { cascade });
|
|
8712
|
+
if (!res.ok) fail2(res.error?.message || "delete failed");
|
|
8713
|
+
emit(res.data, opts.json === true, () => {
|
|
8714
|
+
if (res.data) {
|
|
8715
|
+
printProject(res.data, "Project archived");
|
|
8716
|
+
} else {
|
|
8717
|
+
process.stdout.write(`Project ${projectId} archived.
|
|
6943
8718
|
`);
|
|
6944
|
-
process.exit(1);
|
|
6945
|
-
}
|
|
6946
|
-
if (opts.json) {
|
|
6947
|
-
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
6948
|
-
return;
|
|
6949
8719
|
}
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
8720
|
+
});
|
|
8721
|
+
});
|
|
8722
|
+
const members = project.command("members").description("Manage project memberships");
|
|
8723
|
+
members.command("list <projectId>").description("List members of a project").option("--json", "Output JSON").action(async (projectId, opts) => {
|
|
8724
|
+
const client = getIMClient2();
|
|
8725
|
+
const res = await client.projects.members.list(projectId);
|
|
8726
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "list members failed");
|
|
8727
|
+
emit(res.data, opts.json === true, () => printMemberList2(res.data));
|
|
8728
|
+
});
|
|
8729
|
+
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) => {
|
|
8730
|
+
const { kind, id } = parsePrincipal(opts.principal);
|
|
8731
|
+
const role = parseRole(opts.role);
|
|
8732
|
+
const client = getIMClient2();
|
|
8733
|
+
const res = await client.projects.members.add(projectId, { principalKind: kind, principalId: id, role });
|
|
8734
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "add member failed");
|
|
8735
|
+
emit(res.data, opts.json === true, () => {
|
|
8736
|
+
const m = res.data;
|
|
8737
|
+
process.stdout.write(`Member added: ${m.principalKind}:${m.principalId} \u2192 ${m.role} (${m.id})
|
|
6954
8738
|
`);
|
|
6955
|
-
|
|
6956
|
-
}
|
|
8739
|
+
});
|
|
6957
8740
|
});
|
|
6958
|
-
|
|
8741
|
+
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) => {
|
|
8742
|
+
const role = parseRole(opts.role);
|
|
8743
|
+
if (!role) fail2("--role is required");
|
|
6959
8744
|
const client = getIMClient2();
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
8745
|
+
const res = await client.projects.members.update(projectId, membershipId, { role });
|
|
8746
|
+
if (!res.ok || !res.data) fail2(res.error?.message || "update member failed");
|
|
8747
|
+
emit(res.data, opts.json === true, () => {
|
|
8748
|
+
const m = res.data;
|
|
8749
|
+
process.stdout.write(`Member ${m.id} role \u2192 ${m.role}
|
|
6964
8750
|
`);
|
|
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)}
|
|
8751
|
+
});
|
|
8752
|
+
});
|
|
8753
|
+
members.command("remove <projectId> <membershipId>").description("Remove a member from the project").option("--json", "Output JSON").action(async (projectId, membershipId, opts) => {
|
|
8754
|
+
const client = getIMClient2();
|
|
8755
|
+
const res = await client.projects.members.remove(projectId, membershipId);
|
|
8756
|
+
if (!res.ok) fail2(res.error?.message || "remove member failed");
|
|
8757
|
+
emit({ ok: true }, opts.json === true, () => {
|
|
8758
|
+
process.stdout.write(`Membership ${membershipId} removed.
|
|
6985
8759
|
`);
|
|
6986
|
-
|
|
6987
|
-
}
|
|
8760
|
+
});
|
|
6988
8761
|
});
|
|
6989
8762
|
}
|
|
6990
8763
|
|
|
6991
8764
|
// src/commands/security.ts
|
|
6992
|
-
function
|
|
8765
|
+
function register13(parent, getIMClient2, _getAPIClient) {
|
|
6993
8766
|
const security = parent.command("security").description("Per-conversation encryption and key management");
|
|
6994
8767
|
security.command("get <conversation-id>").description("Get security settings for a conversation").option("--json", "Output raw JSON response").action(async (convId, opts) => {
|
|
6995
8768
|
const client = getIMClient2();
|
|
@@ -7269,7 +9042,7 @@ function register9(parent, getIMClient2, _getAPIClient) {
|
|
|
7269
9042
|
}
|
|
7270
9043
|
|
|
7271
9044
|
// src/commands/community.ts
|
|
7272
|
-
var
|
|
9045
|
+
var import_node_fs2 = require("fs");
|
|
7273
9046
|
function printJson(res, opts) {
|
|
7274
9047
|
if (opts.json) {
|
|
7275
9048
|
console.log(JSON.stringify(res, null, 2));
|
|
@@ -7296,7 +9069,7 @@ _Next cursor:_ \`${d.nextCursor}\`
|
|
|
7296
9069
|
`;
|
|
7297
9070
|
return t;
|
|
7298
9071
|
}
|
|
7299
|
-
function
|
|
9072
|
+
function register14(parent, getIMClient2, _getAPIClient) {
|
|
7300
9073
|
const comm = parent.command("community").description("Evolution community forum \u2014 feed, ask, search, notify");
|
|
7301
9074
|
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
9075
|
const c = getIMClient2();
|
|
@@ -7315,7 +9088,7 @@ function register10(parent, getIMClient2, _getAPIClient) {
|
|
|
7315
9088
|
process.stdout.write(formatPostsMarkdown(res.data));
|
|
7316
9089
|
});
|
|
7317
9090
|
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,
|
|
9091
|
+
const content = opts.file ? (0, import_node_fs2.readFileSync)(opts.file, "utf8") : body || "(no body)";
|
|
7319
9092
|
const tags = opts.tags?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7320
9093
|
const c = getIMClient2();
|
|
7321
9094
|
const res = await c.im.community.ask(title, content, tags);
|
|
@@ -7397,8 +9170,8 @@ function register10(parent, getIMClient2, _getAPIClient) {
|
|
|
7397
9170
|
}
|
|
7398
9171
|
|
|
7399
9172
|
// src/commands/asset.ts
|
|
7400
|
-
var
|
|
7401
|
-
var
|
|
9173
|
+
var fs5 = __toESM(require("fs"));
|
|
9174
|
+
var path3 = __toESM(require("path"));
|
|
7402
9175
|
function resolveWorkspaceId(flag) {
|
|
7403
9176
|
if (flag) return flag;
|
|
7404
9177
|
if (typeof process !== "undefined" && process.env?.PRISMER_WORKSPACE_ID) {
|
|
@@ -7432,7 +9205,7 @@ function formatBytes(n) {
|
|
|
7432
9205
|
if (n == null) return "-";
|
|
7433
9206
|
return String(n);
|
|
7434
9207
|
}
|
|
7435
|
-
function
|
|
9208
|
+
function register15(parent, getIMClient2, _getAPIClient) {
|
|
7436
9209
|
const asset = parent.command("asset").description("Inspect and manage workspace assets (content-addressed)");
|
|
7437
9210
|
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
9211
|
const wsId = requireWorkspaceId(opts.workspaceId);
|
|
@@ -7592,7 +9365,7 @@ ${rows.length} asset(s) listed.
|
|
|
7592
9365
|
});
|
|
7593
9366
|
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
9367
|
const wsId = requireWorkspaceId(opts.workspaceId);
|
|
7595
|
-
if (!
|
|
9368
|
+
if (!fs5.existsSync(filePath)) {
|
|
7596
9369
|
process.stderr.write(`Error: file not found: ${filePath}
|
|
7597
9370
|
`);
|
|
7598
9371
|
process.exit(1);
|
|
@@ -7622,7 +9395,7 @@ ${rows.length} asset(s) listed.
|
|
|
7622
9395
|
process.exit(1);
|
|
7623
9396
|
}
|
|
7624
9397
|
const a = res.data;
|
|
7625
|
-
process.stdout.write(`Uploaded ${
|
|
9398
|
+
process.stdout.write(`Uploaded ${path3.basename(filePath)}
|
|
7626
9399
|
`);
|
|
7627
9400
|
process.stdout.write(` ID: ${a.id}
|
|
7628
9401
|
`);
|
|
@@ -7649,7 +9422,7 @@ ${rows.length} asset(s) listed.
|
|
|
7649
9422
|
opts.length
|
|
7650
9423
|
);
|
|
7651
9424
|
if (opts.out) {
|
|
7652
|
-
|
|
9425
|
+
fs5.writeFileSync(opts.out, bytes);
|
|
7653
9426
|
const range = describeRange(opts.offset, opts.length, bytes.byteLength);
|
|
7654
9427
|
process.stderr.write(
|
|
7655
9428
|
`Downloaded ${assetId} -> ${opts.out} (${bytes.byteLength} bytes${range ? `, ${range}` : ""}${truncated ? ", truncated" : ""}${totalSize != null ? `, total=${totalSize}` : ""})
|
|
@@ -7797,7 +9570,7 @@ function resolveWorkspaceId2(flag) {
|
|
|
7797
9570
|
}
|
|
7798
9571
|
return void 0;
|
|
7799
9572
|
}
|
|
7800
|
-
function
|
|
9573
|
+
function register16(parent, getIMClient2, _getAPIClient) {
|
|
7801
9574
|
const approval = parent.command("approval").description("Submit and manage human approval requests");
|
|
7802
9575
|
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
9576
|
if (!opts.conversationId && !opts.taskId) {
|
|
@@ -7875,7 +9648,69 @@ function parseIntOpt2(value) {
|
|
|
7875
9648
|
}
|
|
7876
9649
|
|
|
7877
9650
|
// src/commands/agent.ts
|
|
7878
|
-
|
|
9651
|
+
var import_node_crypto2 = require("crypto");
|
|
9652
|
+
var import_node_fs3 = require("fs");
|
|
9653
|
+
var import_node_os = require("os");
|
|
9654
|
+
var import_node_path3 = require("path");
|
|
9655
|
+
var import_node_child_process = require("child_process");
|
|
9656
|
+
function resolvePrismerRoot() {
|
|
9657
|
+
return process.env.PRISMER_HOME ? (0, import_node_path3.resolve)(process.env.PRISMER_HOME) : (0, import_node_path3.join)((0, import_node_os.homedir)(), ".prismer");
|
|
9658
|
+
}
|
|
9659
|
+
function readDaemonConfig() {
|
|
9660
|
+
const path6 = (0, import_node_path3.join)(resolvePrismerRoot(), "config.toml");
|
|
9661
|
+
if (!(0, import_node_fs3.existsSync)(path6)) {
|
|
9662
|
+
throw new Error(`${path6} not found. Run \`prismer setup\` first.`);
|
|
9663
|
+
}
|
|
9664
|
+
const raw = (0, import_node_fs3.readFileSync)(path6, "utf8");
|
|
9665
|
+
const idMatch = raw.match(/^daemon_id\s*=\s*"([^"]+)"/m);
|
|
9666
|
+
const keyMatch = raw.match(/^api_key\s*=\s*"([^"]+)"/m);
|
|
9667
|
+
if (!idMatch || !keyMatch) {
|
|
9668
|
+
throw new Error(`config.toml missing daemon_id or api_key (${path6})`);
|
|
9669
|
+
}
|
|
9670
|
+
return { daemonId: idMatch[1], apiKey: keyMatch[1] };
|
|
9671
|
+
}
|
|
9672
|
+
function resolveAgentDir(daemonId, agentId) {
|
|
9673
|
+
return (0, import_node_path3.join)(resolvePrismerRoot(), "devices", daemonId, "agents", agentId);
|
|
9674
|
+
}
|
|
9675
|
+
function collectFiles(root, excludeBasenames) {
|
|
9676
|
+
const out = [];
|
|
9677
|
+
function walk2(dir) {
|
|
9678
|
+
let entries;
|
|
9679
|
+
try {
|
|
9680
|
+
entries = (0, import_node_fs3.readdirSync)(dir);
|
|
9681
|
+
} catch {
|
|
9682
|
+
return;
|
|
9683
|
+
}
|
|
9684
|
+
for (const name of entries) {
|
|
9685
|
+
if (excludeBasenames.has(name)) continue;
|
|
9686
|
+
const full = (0, import_node_path3.join)(dir, name);
|
|
9687
|
+
let st;
|
|
9688
|
+
try {
|
|
9689
|
+
st = (0, import_node_fs3.statSync)(full);
|
|
9690
|
+
} catch {
|
|
9691
|
+
continue;
|
|
9692
|
+
}
|
|
9693
|
+
if (st.isDirectory()) walk2(full);
|
|
9694
|
+
else if (st.isFile()) {
|
|
9695
|
+
const buf = (0, import_node_fs3.readFileSync)(full);
|
|
9696
|
+
const sha = (0, import_node_crypto2.createHash)("sha256").update(buf).digest("hex");
|
|
9697
|
+
out.push({
|
|
9698
|
+
rel: (0, import_node_path3.relative)(root, full).split(import_node_path3.sep).join("/"),
|
|
9699
|
+
sha,
|
|
9700
|
+
size: buf.byteLength
|
|
9701
|
+
});
|
|
9702
|
+
}
|
|
9703
|
+
}
|
|
9704
|
+
}
|
|
9705
|
+
walk2(root);
|
|
9706
|
+
out.sort((a, b) => a.rel.localeCompare(b.rel));
|
|
9707
|
+
return out;
|
|
9708
|
+
}
|
|
9709
|
+
function computeMerkle(files) {
|
|
9710
|
+
const lines = files.map((f) => `${f.rel}:${f.sha}`).join("\n");
|
|
9711
|
+
return (0, import_node_crypto2.createHash)("sha256").update(lines).digest("hex");
|
|
9712
|
+
}
|
|
9713
|
+
function register17(parent, getIMClient2, _getAPIClient) {
|
|
7879
9714
|
const agent = parent.command("agent").description("Manage agent specs, snapshots, publish, and fork");
|
|
7880
9715
|
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
9716
|
const res = await getIMClient2().im.agents.spec(agentId, opts.workspaceId);
|
|
@@ -7954,6 +9789,244 @@ function register13(parent, getIMClient2, _getAPIClient) {
|
|
|
7954
9789
|
}
|
|
7955
9790
|
});
|
|
7956
9791
|
});
|
|
9792
|
+
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) => {
|
|
9793
|
+
const { daemonId, apiKey } = readDaemonConfig();
|
|
9794
|
+
const agentDir = resolveAgentDir(daemonId, agentId);
|
|
9795
|
+
if (!(0, import_node_fs3.existsSync)(agentDir)) {
|
|
9796
|
+
process.stderr.write(`Error: agent dir not found at ${agentDir}
|
|
9797
|
+
`);
|
|
9798
|
+
process.exit(1);
|
|
9799
|
+
}
|
|
9800
|
+
if (!opts.force) {
|
|
9801
|
+
const res = await getIMClient2().im.agents.pause(agentId);
|
|
9802
|
+
if (!res.ok) {
|
|
9803
|
+
process.stderr.write(
|
|
9804
|
+
`Error: pause failed (${res.error?.message ?? "unknown"}); pass --force to export anyway
|
|
9805
|
+
`
|
|
9806
|
+
);
|
|
9807
|
+
process.exit(1);
|
|
9808
|
+
}
|
|
9809
|
+
}
|
|
9810
|
+
const excludeBasenames = /* @__PURE__ */ new Set(["transfer-manifest.json"]);
|
|
9811
|
+
const files = collectFiles(agentDir, excludeBasenames);
|
|
9812
|
+
const sha256 = computeMerkle(files);
|
|
9813
|
+
const signature = (0, import_node_crypto2.createHmac)("sha256", apiKey).update(sha256).digest("hex");
|
|
9814
|
+
const manifest = {
|
|
9815
|
+
version: 1,
|
|
9816
|
+
agentId,
|
|
9817
|
+
fromDaemonId: daemonId,
|
|
9818
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9819
|
+
includes: ["profile.json", "skills/**", "memory/**", "outbox/*"],
|
|
9820
|
+
excludes: ["cache/ (daemon-level blob pool)", "local.db (daemon-level)"],
|
|
9821
|
+
sha256,
|
|
9822
|
+
signature
|
|
9823
|
+
};
|
|
9824
|
+
(0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(agentDir, "transfer-manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
9825
|
+
const parentOfAgents = (0, import_node_path3.join)(resolvePrismerRoot(), "devices", daemonId);
|
|
9826
|
+
const outputAbs = (0, import_node_path3.resolve)(opts.output);
|
|
9827
|
+
try {
|
|
9828
|
+
(0, import_node_child_process.execFileSync)("tar", ["-czf", outputAbs, "-C", parentOfAgents, (0, import_node_path3.join)("agents", agentId)], { stdio: "inherit" });
|
|
9829
|
+
} catch (err) {
|
|
9830
|
+
process.stderr.write(`Error: tar failed: ${err.message}
|
|
9831
|
+
`);
|
|
9832
|
+
process.exit(1);
|
|
9833
|
+
}
|
|
9834
|
+
const result = {
|
|
9835
|
+
agentId,
|
|
9836
|
+
fromDaemonId: daemonId,
|
|
9837
|
+
output: outputAbs,
|
|
9838
|
+
manifestSha256: sha256,
|
|
9839
|
+
filesIncluded: files.length,
|
|
9840
|
+
totalBytes: files.reduce((s, f) => s + f.size, 0),
|
|
9841
|
+
paused: !opts.force
|
|
9842
|
+
};
|
|
9843
|
+
if (opts.json) {
|
|
9844
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
9845
|
+
} else {
|
|
9846
|
+
console.log(`Exported agent ${agentId}`);
|
|
9847
|
+
console.log(` output: ${result.output}`);
|
|
9848
|
+
console.log(` files: ${result.filesIncluded}`);
|
|
9849
|
+
console.log(` total bytes: ${result.totalBytes}`);
|
|
9850
|
+
console.log(` manifestSha: ${result.manifestSha256.slice(0, 16)}\u2026`);
|
|
9851
|
+
console.log(` cloud paused: ${result.paused ? "yes" : "no (--force)"}`);
|
|
9852
|
+
console.log("");
|
|
9853
|
+
console.log("Next: copy the tar.gz to the target device and run:");
|
|
9854
|
+
console.log(` prismer agent import ${result.output}`);
|
|
9855
|
+
}
|
|
9856
|
+
});
|
|
9857
|
+
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) => {
|
|
9858
|
+
const tarAbs = (0, import_node_path3.resolve)(tarFile);
|
|
9859
|
+
if (!(0, import_node_fs3.existsSync)(tarAbs)) {
|
|
9860
|
+
process.stderr.write(`Error: tar file not found: ${tarAbs}
|
|
9861
|
+
`);
|
|
9862
|
+
process.exit(1);
|
|
9863
|
+
}
|
|
9864
|
+
const { daemonId: toDaemonId, apiKey } = readDaemonConfig();
|
|
9865
|
+
const tmpRoot = (0, import_node_path3.join)(resolvePrismerRoot(), ".transfer-staging", `${Date.now()}`);
|
|
9866
|
+
(0, import_node_fs3.mkdirSync)(tmpRoot, { recursive: true });
|
|
9867
|
+
try {
|
|
9868
|
+
(0, import_node_child_process.execFileSync)("tar", ["-xzf", tarAbs, "-C", tmpRoot], { stdio: "inherit" });
|
|
9869
|
+
} catch (err) {
|
|
9870
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9871
|
+
process.stderr.write(`Error: tar extract failed: ${err.message}
|
|
9872
|
+
`);
|
|
9873
|
+
process.exit(1);
|
|
9874
|
+
}
|
|
9875
|
+
const agentsRoot = (0, import_node_path3.join)(tmpRoot, "agents");
|
|
9876
|
+
if (!(0, import_node_fs3.existsSync)(agentsRoot)) {
|
|
9877
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9878
|
+
process.stderr.write("Error: tar layout invalid \u2014 no agents/ root inside archive\n");
|
|
9879
|
+
process.exit(1);
|
|
9880
|
+
}
|
|
9881
|
+
const entries = (0, import_node_fs3.readdirSync)(agentsRoot);
|
|
9882
|
+
if (entries.length !== 1) {
|
|
9883
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9884
|
+
process.stderr.write(`Error: expected exactly 1 agent in archive, got ${entries.length}
|
|
9885
|
+
`);
|
|
9886
|
+
process.exit(1);
|
|
9887
|
+
}
|
|
9888
|
+
const agentId = entries[0];
|
|
9889
|
+
const stagedAgentDir = (0, import_node_path3.join)(agentsRoot, agentId);
|
|
9890
|
+
const manifestPath = (0, import_node_path3.join)(stagedAgentDir, "transfer-manifest.json");
|
|
9891
|
+
if (!(0, import_node_fs3.existsSync)(manifestPath)) {
|
|
9892
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9893
|
+
process.stderr.write("Error: transfer-manifest.json missing from archive\n");
|
|
9894
|
+
process.exit(1);
|
|
9895
|
+
}
|
|
9896
|
+
let manifest;
|
|
9897
|
+
try {
|
|
9898
|
+
manifest = JSON.parse((0, import_node_fs3.readFileSync)(manifestPath, "utf8"));
|
|
9899
|
+
} catch (err) {
|
|
9900
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9901
|
+
process.stderr.write(`Error: manifest parse failed: ${err.message}
|
|
9902
|
+
`);
|
|
9903
|
+
process.exit(1);
|
|
9904
|
+
}
|
|
9905
|
+
const expectedSig = (0, import_node_crypto2.createHmac)("sha256", apiKey).update(manifest.sha256).digest("hex");
|
|
9906
|
+
if (expectedSig !== manifest.signature) {
|
|
9907
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9908
|
+
process.stderr.write(
|
|
9909
|
+
"Error: manifest signature verification failed \u2014 refusing to import (different workspace, or tampered archive)\n"
|
|
9910
|
+
);
|
|
9911
|
+
process.exit(1);
|
|
9912
|
+
}
|
|
9913
|
+
if (manifest.agentId !== agentId) {
|
|
9914
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9915
|
+
process.stderr.write(
|
|
9916
|
+
`Error: manifest agentId (${manifest.agentId}) != extracted dir name (${agentId})
|
|
9917
|
+
`
|
|
9918
|
+
);
|
|
9919
|
+
process.exit(1);
|
|
9920
|
+
}
|
|
9921
|
+
if (manifest.fromDaemonId === toDaemonId) {
|
|
9922
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9923
|
+
process.stderr.write(
|
|
9924
|
+
`Error: fromDaemonId == toDaemonId (${toDaemonId}); nothing to transfer.
|
|
9925
|
+
`
|
|
9926
|
+
);
|
|
9927
|
+
process.exit(1);
|
|
9928
|
+
}
|
|
9929
|
+
const targetAgentDir = resolveAgentDir(toDaemonId, agentId);
|
|
9930
|
+
if ((0, import_node_fs3.existsSync)(targetAgentDir)) {
|
|
9931
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9932
|
+
process.stderr.write(
|
|
9933
|
+
`Error: target dir already exists at ${targetAgentDir}. Run \`prismer agent remove ${agentId}\` (TODO) or move it aside first.
|
|
9934
|
+
`
|
|
9935
|
+
);
|
|
9936
|
+
process.exit(1);
|
|
9937
|
+
}
|
|
9938
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path3.join)(resolvePrismerRoot(), "devices", toDaemonId, "agents"), { recursive: true });
|
|
9939
|
+
try {
|
|
9940
|
+
(0, import_node_child_process.execFileSync)("mv", [stagedAgentDir, targetAgentDir]);
|
|
9941
|
+
} catch {
|
|
9942
|
+
try {
|
|
9943
|
+
(0, import_node_child_process.execFileSync)("cp", ["-R", stagedAgentDir, targetAgentDir]);
|
|
9944
|
+
(0, import_node_fs3.rmSync)(stagedAgentDir, { recursive: true, force: true });
|
|
9945
|
+
} catch (err) {
|
|
9946
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9947
|
+
process.stderr.write(`Error: failed to install agent dir: ${err.message}
|
|
9948
|
+
`);
|
|
9949
|
+
process.exit(1);
|
|
9950
|
+
}
|
|
9951
|
+
}
|
|
9952
|
+
(0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
9953
|
+
const rebind = await getIMClient2().im.agents.transfer({
|
|
9954
|
+
agentId,
|
|
9955
|
+
fromDaemonId: manifest.fromDaemonId,
|
|
9956
|
+
toDaemonId,
|
|
9957
|
+
manifestSha256: manifest.sha256
|
|
9958
|
+
});
|
|
9959
|
+
if (!rebind.ok || !rebind.data) {
|
|
9960
|
+
process.stderr.write(
|
|
9961
|
+
`Warning: cloud rebind failed (${rebind.error?.message ?? "unknown"}). Agent files are on disk at ${targetAgentDir} but cloud still routes to ${manifest.fromDaemonId}.
|
|
9962
|
+
Manual fix: POST /api/im/agent-bindings/transfer or rebind via the Devices UI.
|
|
9963
|
+
`
|
|
9964
|
+
);
|
|
9965
|
+
process.exit(2);
|
|
9966
|
+
}
|
|
9967
|
+
await getIMClient2().im.agents.resume(agentId);
|
|
9968
|
+
const result = {
|
|
9969
|
+
agentId,
|
|
9970
|
+
fromDaemonId: manifest.fromDaemonId,
|
|
9971
|
+
toDaemonId,
|
|
9972
|
+
targetDir: targetAgentDir,
|
|
9973
|
+
manifestSha256: manifest.sha256,
|
|
9974
|
+
boundDaemonId: rebind.data.boundDaemonId,
|
|
9975
|
+
boundBy: rebind.data.boundBy
|
|
9976
|
+
};
|
|
9977
|
+
if (opts.json) {
|
|
9978
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
9979
|
+
} else {
|
|
9980
|
+
console.log(`Imported agent ${agentId}`);
|
|
9981
|
+
console.log(` from device: ${result.fromDaemonId}`);
|
|
9982
|
+
console.log(` to device: ${result.toDaemonId}`);
|
|
9983
|
+
console.log(` target dir: ${result.targetDir}`);
|
|
9984
|
+
console.log(` cloud bound: ${result.boundDaemonId} (${result.boundBy})`);
|
|
9985
|
+
}
|
|
9986
|
+
});
|
|
9987
|
+
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) => {
|
|
9988
|
+
const { daemonId } = readDaemonConfig();
|
|
9989
|
+
const devicesRoot = (0, import_node_path3.join)(resolvePrismerRoot(), "devices");
|
|
9990
|
+
if (!(0, import_node_fs3.existsSync)(devicesRoot)) {
|
|
9991
|
+
if (opts.json) process.stdout.write("[]\n");
|
|
9992
|
+
else console.log("No devices/ dir yet.");
|
|
9993
|
+
return;
|
|
9994
|
+
}
|
|
9995
|
+
const orphans = [];
|
|
9996
|
+
for (const did of (0, import_node_fs3.readdirSync)(devicesRoot)) {
|
|
9997
|
+
if (did === daemonId) continue;
|
|
9998
|
+
const agentsRoot = (0, import_node_path3.join)(devicesRoot, did, "agents");
|
|
9999
|
+
if (!(0, import_node_fs3.existsSync)(agentsRoot)) continue;
|
|
10000
|
+
for (const aid of (0, import_node_fs3.readdirSync)(agentsRoot)) {
|
|
10001
|
+
const dir = (0, import_node_path3.join)(agentsRoot, aid);
|
|
10002
|
+
try {
|
|
10003
|
+
const st = (0, import_node_fs3.statSync)(dir);
|
|
10004
|
+
if (!st.isDirectory()) continue;
|
|
10005
|
+
const files = collectFiles(dir, /* @__PURE__ */ new Set());
|
|
10006
|
+
const sizeBytes = files.reduce((s, f) => s + f.size, 0);
|
|
10007
|
+
orphans.push({ daemonId: did, agentId: aid, dir, sizeBytes });
|
|
10008
|
+
} catch {
|
|
10009
|
+
}
|
|
10010
|
+
}
|
|
10011
|
+
}
|
|
10012
|
+
if (opts.delete) {
|
|
10013
|
+
for (const o of orphans) {
|
|
10014
|
+
(0, import_node_fs3.rmSync)(o.dir, { recursive: true, force: true });
|
|
10015
|
+
}
|
|
10016
|
+
}
|
|
10017
|
+
if (opts.json) {
|
|
10018
|
+
process.stdout.write(JSON.stringify({ orphans, deleted: Boolean(opts.delete) }, null, 2) + "\n");
|
|
10019
|
+
} else {
|
|
10020
|
+
if (orphans.length === 0) {
|
|
10021
|
+
console.log("No orphan agent dirs.");
|
|
10022
|
+
} else {
|
|
10023
|
+
console.log(`Found ${orphans.length} orphan agent dir(s)${opts.delete ? " (DELETED)" : " (dry-run; pass --delete to remove)"}:`);
|
|
10024
|
+
for (const o of orphans) {
|
|
10025
|
+
console.log(` ${o.daemonId}/${o.agentId} ${o.sizeBytes} bytes ${o.dir}`);
|
|
10026
|
+
}
|
|
10027
|
+
}
|
|
10028
|
+
}
|
|
10029
|
+
});
|
|
7957
10030
|
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
10031
|
const res = await getIMClient2().im.agents.forkPack(packId, {
|
|
7959
10032
|
targetWorkspaceId: opts.workspaceId,
|
|
@@ -7979,32 +10052,168 @@ function printOrExit(res, json, print) {
|
|
|
7979
10052
|
print(res.data);
|
|
7980
10053
|
}
|
|
7981
10054
|
|
|
10055
|
+
// src/commands/metric.ts
|
|
10056
|
+
var AGG_FUNCS = ["sum", "count", "avg", "min", "max", "p50", "p95", "p99"];
|
|
10057
|
+
var BUCKETS = ["5m", "1h", "1d"];
|
|
10058
|
+
function splitFqName(fqName) {
|
|
10059
|
+
const i = fqName.lastIndexOf(".");
|
|
10060
|
+
if (i <= 0 || i === fqName.length - 1) {
|
|
10061
|
+
throw new Error(`metric name must be in form "namespace.name" (got "${fqName}")`);
|
|
10062
|
+
}
|
|
10063
|
+
return { namespace: fqName.slice(0, i), name: fqName.slice(i + 1) };
|
|
10064
|
+
}
|
|
10065
|
+
function parseDimFlags(dimArr) {
|
|
10066
|
+
const dims = {};
|
|
10067
|
+
for (const raw of dimArr ?? []) {
|
|
10068
|
+
const i = raw.indexOf("=");
|
|
10069
|
+
if (i <= 0) throw new Error(`--dim "${raw}" must be in form key=value`);
|
|
10070
|
+
const key = raw.slice(0, i);
|
|
10071
|
+
const value = raw.slice(i + 1);
|
|
10072
|
+
if (/^-?\d+(?:\.\d+)?$/.test(value)) dims[key] = Number(value);
|
|
10073
|
+
else if (value === "true" || value === "false") dims[key] = value === "true";
|
|
10074
|
+
else dims[key] = value;
|
|
10075
|
+
}
|
|
10076
|
+
return dims;
|
|
10077
|
+
}
|
|
10078
|
+
async function runEmit(fqName, opts, getIMClient2) {
|
|
10079
|
+
const client = getIMClient2();
|
|
10080
|
+
const { namespace, name } = splitFqName(fqName);
|
|
10081
|
+
const dims = parseDimFlags(opts.dim);
|
|
10082
|
+
if (!dims.workspaceId) {
|
|
10083
|
+
throw new Error("--dim workspaceId=<id> is required (server rejects emits without it)");
|
|
10084
|
+
}
|
|
10085
|
+
let value;
|
|
10086
|
+
if (opts.value !== void 0) {
|
|
10087
|
+
value = /^-?\d+(?:\.\d+)?$/.test(opts.value) ? Number(opts.value) : opts.value;
|
|
10088
|
+
}
|
|
10089
|
+
const input = {
|
|
10090
|
+
namespace,
|
|
10091
|
+
name,
|
|
10092
|
+
ts: opts.ts,
|
|
10093
|
+
value,
|
|
10094
|
+
dims
|
|
10095
|
+
};
|
|
10096
|
+
const res = await client.im.metrics.emit(input);
|
|
10097
|
+
if (opts.json) {
|
|
10098
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
10099
|
+
return;
|
|
10100
|
+
}
|
|
10101
|
+
if (!res.ok) {
|
|
10102
|
+
process.stderr.write(`Error: ${res.error?.message ?? "unknown error"}
|
|
10103
|
+
`);
|
|
10104
|
+
process.exit(1);
|
|
10105
|
+
}
|
|
10106
|
+
process.stdout.write(`emitted ${namespace}.${name}
|
|
10107
|
+
`);
|
|
10108
|
+
}
|
|
10109
|
+
async function runAgg(fqName, opts, getIMClient2) {
|
|
10110
|
+
const client = getIMClient2();
|
|
10111
|
+
const { namespace, name } = splitFqName(fqName);
|
|
10112
|
+
if (!AGG_FUNCS.includes(opts.agg)) {
|
|
10113
|
+
throw new Error(`--agg must be one of ${AGG_FUNCS.join("|")}`);
|
|
10114
|
+
}
|
|
10115
|
+
if (opts.bucket && !BUCKETS.includes(opts.bucket)) {
|
|
10116
|
+
throw new Error(`--bucket must be one of ${BUCKETS.join("|")}`);
|
|
10117
|
+
}
|
|
10118
|
+
const filter = {};
|
|
10119
|
+
for (const raw of (opts.filter ?? "").split(",").filter(Boolean)) {
|
|
10120
|
+
const i = raw.indexOf(":");
|
|
10121
|
+
if (i <= 0) throw new Error(`--filter "${raw}" must be in form key:value`);
|
|
10122
|
+
filter[raw.slice(0, i)] = raw.slice(i + 1);
|
|
10123
|
+
}
|
|
10124
|
+
if (!filter.workspaceId) {
|
|
10125
|
+
throw new Error("--filter must include workspaceId:<id> (cross-workspace queries are admin-only)");
|
|
10126
|
+
}
|
|
10127
|
+
const groupBy = opts.groupBy ? opts.groupBy.split(",").filter(Boolean) : void 0;
|
|
10128
|
+
const res = await client.im.metrics.aggregate({
|
|
10129
|
+
namespace,
|
|
10130
|
+
name,
|
|
10131
|
+
agg: opts.agg,
|
|
10132
|
+
range: opts.range,
|
|
10133
|
+
from: opts.from,
|
|
10134
|
+
to: opts.to,
|
|
10135
|
+
groupBy,
|
|
10136
|
+
filter,
|
|
10137
|
+
bucket: opts.bucket
|
|
10138
|
+
});
|
|
10139
|
+
if (opts.json) {
|
|
10140
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
10141
|
+
return;
|
|
10142
|
+
}
|
|
10143
|
+
if (!res.ok) {
|
|
10144
|
+
process.stderr.write(`Error: ${res.error?.message ?? "unknown error"}
|
|
10145
|
+
`);
|
|
10146
|
+
process.exit(1);
|
|
10147
|
+
}
|
|
10148
|
+
const data = res.data;
|
|
10149
|
+
if (!data) {
|
|
10150
|
+
process.stdout.write("(no data)\n");
|
|
10151
|
+
return;
|
|
10152
|
+
}
|
|
10153
|
+
process.stdout.write(
|
|
10154
|
+
`${data.namespace}.${data.name} ${data.agg} [${data.range.from} \u2192 ${data.range.to}]
|
|
10155
|
+
`
|
|
10156
|
+
);
|
|
10157
|
+
for (const bucket of data.buckets) {
|
|
10158
|
+
const tsLabel = bucket.ts ? `${bucket.ts}` : "(all)";
|
|
10159
|
+
for (const g of bucket.groups) {
|
|
10160
|
+
const keyLabel = Object.entries(g.groupKey).map(([k, v]) => `${k}=${v ?? "\u2205"}`).join(" ");
|
|
10161
|
+
process.stdout.write(` ${tsLabel} ${keyLabel || "(no group)"} \u2192 ${g.value ?? "\u2205"}
|
|
10162
|
+
`);
|
|
10163
|
+
}
|
|
10164
|
+
}
|
|
10165
|
+
}
|
|
10166
|
+
function register18(parent, getIMClient2, _getAPIClient) {
|
|
10167
|
+
const metric = parent.command("metric").description("Emit metric events and query aggregations (release201/11)");
|
|
10168
|
+
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 = []) => {
|
|
10169
|
+
prev.push(val);
|
|
10170
|
+
return prev;
|
|
10171
|
+
}).option("--ts <iso>", "business timestamp in ISO 8601 (defaults to now)").option("--json", "output raw JSON response").action(async (fqName, opts) => {
|
|
10172
|
+
try {
|
|
10173
|
+
await runEmit(fqName, opts, getIMClient2);
|
|
10174
|
+
} catch (err) {
|
|
10175
|
+
process.stderr.write(`Error: ${err.message}
|
|
10176
|
+
`);
|
|
10177
|
+
process.exit(1);
|
|
10178
|
+
}
|
|
10179
|
+
});
|
|
10180
|
+
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) => {
|
|
10181
|
+
try {
|
|
10182
|
+
await runAgg(fqName, opts, getIMClient2);
|
|
10183
|
+
} catch (err) {
|
|
10184
|
+
process.stderr.write(`Error: ${err.message}
|
|
10185
|
+
`);
|
|
10186
|
+
process.exit(1);
|
|
10187
|
+
}
|
|
10188
|
+
});
|
|
10189
|
+
}
|
|
10190
|
+
|
|
7982
10191
|
// src/daemon.ts
|
|
7983
|
-
var
|
|
7984
|
-
var
|
|
10192
|
+
var fs6 = __toESM(require("fs"));
|
|
10193
|
+
var path4 = __toESM(require("path"));
|
|
7985
10194
|
var import_path = require("path");
|
|
7986
|
-
var
|
|
10195
|
+
var os2 = __toESM(require("os"));
|
|
7987
10196
|
var import_os = require("os");
|
|
7988
10197
|
var http = __toESM(require("http"));
|
|
7989
10198
|
var import_http = require("http");
|
|
7990
10199
|
var import_child_process = require("child_process");
|
|
7991
10200
|
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 =
|
|
10201
|
+
var CONFIG_DIR = path4.join(os2.homedir(), ".prismer");
|
|
10202
|
+
var CONFIG_PATH = path4.join(CONFIG_DIR, "config.toml");
|
|
10203
|
+
var PID_PATH = path4.join(CONFIG_DIR, "daemon.pid");
|
|
10204
|
+
var PORT_PATH = path4.join(CONFIG_DIR, "daemon.port");
|
|
10205
|
+
var CACHE_DIR = path4.join(CONFIG_DIR, "cache");
|
|
10206
|
+
var EVOLUTION_CACHE_PATH = path4.join(CACHE_DIR, "evolution.json");
|
|
10207
|
+
var OUTBOX_PATH = path4.join(CACHE_DIR, "outbox.json");
|
|
7999
10208
|
var SYNC_INTERVAL_MS = 6e4;
|
|
8000
10209
|
var FLUSH_INTERVAL_MS = 3e4;
|
|
8001
10210
|
var API_TIMEOUT_MS = 1e4;
|
|
8002
10211
|
var EVENTS_FILE = (0, import_path.join)(CACHE_DIR, "events.json");
|
|
8003
10212
|
var MAX_EVENTS = 1e3;
|
|
8004
10213
|
function loadConfig() {
|
|
8005
|
-
if (!
|
|
10214
|
+
if (!fs6.existsSync(CONFIG_PATH)) return null;
|
|
8006
10215
|
try {
|
|
8007
|
-
const raw =
|
|
10216
|
+
const raw = fs6.readFileSync(CONFIG_PATH, "utf-8");
|
|
8008
10217
|
const parsed = TOML.parse(raw);
|
|
8009
10218
|
const apiKey = parsed?.default?.api_key || "";
|
|
8010
10219
|
const baseUrl = parsed?.default?.base_url || "https://prismer.cloud";
|
|
@@ -8015,13 +10224,13 @@ function loadConfig() {
|
|
|
8015
10224
|
}
|
|
8016
10225
|
}
|
|
8017
10226
|
function ensureCacheDir() {
|
|
8018
|
-
if (!
|
|
8019
|
-
|
|
10227
|
+
if (!fs6.existsSync(CACHE_DIR)) {
|
|
10228
|
+
fs6.mkdirSync(CACHE_DIR, { recursive: true });
|
|
8020
10229
|
}
|
|
8021
10230
|
}
|
|
8022
10231
|
function loadEvents() {
|
|
8023
10232
|
try {
|
|
8024
|
-
return JSON.parse(
|
|
10233
|
+
return JSON.parse(fs6.readFileSync(EVENTS_FILE, "utf-8"));
|
|
8025
10234
|
} catch {
|
|
8026
10235
|
return [];
|
|
8027
10236
|
}
|
|
@@ -8030,7 +10239,7 @@ function appendEvent(event) {
|
|
|
8030
10239
|
const events = loadEvents();
|
|
8031
10240
|
events.push(event);
|
|
8032
10241
|
if (events.length > MAX_EVENTS) events.splice(0, events.length - MAX_EVENTS);
|
|
8033
|
-
|
|
10242
|
+
fs6.writeFileSync(EVENTS_FILE, JSON.stringify(events), { encoding: "utf-8", mode: 384 });
|
|
8034
10243
|
}
|
|
8035
10244
|
function emitSyncEvent(genesCount) {
|
|
8036
10245
|
if (genesCount > 0) {
|
|
@@ -8045,9 +10254,9 @@ function emitSyncEvent(genesCount) {
|
|
|
8045
10254
|
}
|
|
8046
10255
|
}
|
|
8047
10256
|
function readPid() {
|
|
8048
|
-
if (!
|
|
10257
|
+
if (!fs6.existsSync(PID_PATH)) return null;
|
|
8049
10258
|
try {
|
|
8050
|
-
const raw =
|
|
10259
|
+
const raw = fs6.readFileSync(PID_PATH, "utf-8").trim();
|
|
8051
10260
|
const pid = parseInt(raw, 10);
|
|
8052
10261
|
return isNaN(pid) ? null : pid;
|
|
8053
10262
|
} catch {
|
|
@@ -8055,9 +10264,9 @@ function readPid() {
|
|
|
8055
10264
|
}
|
|
8056
10265
|
}
|
|
8057
10266
|
function readPort() {
|
|
8058
|
-
if (!
|
|
10267
|
+
if (!fs6.existsSync(PORT_PATH)) return null;
|
|
8059
10268
|
try {
|
|
8060
|
-
const raw =
|
|
10269
|
+
const raw = fs6.readFileSync(PORT_PATH, "utf-8").trim();
|
|
8061
10270
|
const port = parseInt(raw, 10);
|
|
8062
10271
|
return isNaN(port) ? null : port;
|
|
8063
10272
|
} catch {
|
|
@@ -8074,28 +10283,28 @@ function isProcessRunning(pid) {
|
|
|
8074
10283
|
}
|
|
8075
10284
|
function writePid(pid) {
|
|
8076
10285
|
ensureCacheDir();
|
|
8077
|
-
if (!
|
|
8078
|
-
|
|
10286
|
+
if (!fs6.existsSync(CONFIG_DIR)) {
|
|
10287
|
+
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
8079
10288
|
}
|
|
8080
|
-
|
|
10289
|
+
fs6.writeFileSync(PID_PATH, String(pid), { encoding: "utf-8", mode: 384 });
|
|
8081
10290
|
}
|
|
8082
10291
|
function writePort(port) {
|
|
8083
|
-
if (!
|
|
8084
|
-
|
|
10292
|
+
if (!fs6.existsSync(CONFIG_DIR)) {
|
|
10293
|
+
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
8085
10294
|
}
|
|
8086
|
-
|
|
10295
|
+
fs6.writeFileSync(PORT_PATH, String(port), { encoding: "utf-8", mode: 384 });
|
|
8087
10296
|
}
|
|
8088
10297
|
function cleanupPidFiles() {
|
|
8089
10298
|
try {
|
|
8090
|
-
if (
|
|
10299
|
+
if (fs6.existsSync(PID_PATH)) fs6.unlinkSync(PID_PATH);
|
|
8091
10300
|
} catch {
|
|
8092
10301
|
}
|
|
8093
10302
|
try {
|
|
8094
|
-
if (
|
|
10303
|
+
if (fs6.existsSync(PORT_PATH)) fs6.unlinkSync(PORT_PATH);
|
|
8095
10304
|
} catch {
|
|
8096
10305
|
}
|
|
8097
10306
|
}
|
|
8098
|
-
async function
|
|
10307
|
+
async function fetchWithTimeout2(url, options, timeoutMs = API_TIMEOUT_MS) {
|
|
8099
10308
|
const controller = new AbortController();
|
|
8100
10309
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
8101
10310
|
try {
|
|
@@ -8114,9 +10323,9 @@ async function runDaemonProcess() {
|
|
|
8114
10323
|
let lastSync = 0;
|
|
8115
10324
|
let syncCount = 0;
|
|
8116
10325
|
let evolutionCursor = 0;
|
|
8117
|
-
if (
|
|
10326
|
+
if (fs6.existsSync(EVOLUTION_CACHE_PATH)) {
|
|
8118
10327
|
try {
|
|
8119
|
-
const cached = JSON.parse(
|
|
10328
|
+
const cached = JSON.parse(fs6.readFileSync(EVOLUTION_CACHE_PATH, "utf-8"));
|
|
8120
10329
|
if (typeof cached?.cursor === "number") evolutionCursor = cached.cursor;
|
|
8121
10330
|
} catch {
|
|
8122
10331
|
}
|
|
@@ -8124,9 +10333,9 @@ async function runDaemonProcess() {
|
|
|
8124
10333
|
const server = (0, import_http.createServer)((req, res) => {
|
|
8125
10334
|
if (req.method === "GET" && req.url === "/health") {
|
|
8126
10335
|
let outboxSize = 0;
|
|
8127
|
-
if (
|
|
10336
|
+
if (fs6.existsSync(OUTBOX_PATH)) {
|
|
8128
10337
|
try {
|
|
8129
|
-
const entries = JSON.parse(
|
|
10338
|
+
const entries = JSON.parse(fs6.readFileSync(OUTBOX_PATH, "utf-8"));
|
|
8130
10339
|
if (Array.isArray(entries)) outboxSize = entries.length;
|
|
8131
10340
|
} catch {
|
|
8132
10341
|
}
|
|
@@ -8167,7 +10376,7 @@ async function runDaemonProcess() {
|
|
|
8167
10376
|
process.on("SIGTERM", shutdown);
|
|
8168
10377
|
const doEvolutionSync = async () => {
|
|
8169
10378
|
try {
|
|
8170
|
-
const res = await
|
|
10379
|
+
const res = await fetchWithTimeout2(
|
|
8171
10380
|
`${cfg.baseUrl}/api/im/evolution/sync`,
|
|
8172
10381
|
{
|
|
8173
10382
|
method: "POST",
|
|
@@ -8189,7 +10398,7 @@ async function runDaemonProcess() {
|
|
|
8189
10398
|
}
|
|
8190
10399
|
ensureCacheDir();
|
|
8191
10400
|
const pulled = data?.data || data;
|
|
8192
|
-
|
|
10401
|
+
fs6.writeFileSync(
|
|
8193
10402
|
EVOLUTION_CACHE_PATH,
|
|
8194
10403
|
JSON.stringify({ cursor: evolutionCursor, lastSync, data: pulled }, null, 2),
|
|
8195
10404
|
{ encoding: "utf-8", mode: 384 }
|
|
@@ -8200,16 +10409,16 @@ async function runDaemonProcess() {
|
|
|
8200
10409
|
}
|
|
8201
10410
|
};
|
|
8202
10411
|
const doOutboxFlush = async () => {
|
|
8203
|
-
if (!
|
|
10412
|
+
if (!fs6.existsSync(OUTBOX_PATH)) return;
|
|
8204
10413
|
let entries = [];
|
|
8205
10414
|
try {
|
|
8206
|
-
entries = JSON.parse(
|
|
10415
|
+
entries = JSON.parse(fs6.readFileSync(OUTBOX_PATH, "utf-8"));
|
|
8207
10416
|
if (!Array.isArray(entries) || entries.length === 0) return;
|
|
8208
10417
|
} catch {
|
|
8209
10418
|
return;
|
|
8210
10419
|
}
|
|
8211
10420
|
try {
|
|
8212
|
-
const res = await
|
|
10421
|
+
const res = await fetchWithTimeout2(
|
|
8213
10422
|
`${cfg.baseUrl}/api/im/evolution/sync`,
|
|
8214
10423
|
{
|
|
8215
10424
|
method: "POST",
|
|
@@ -8224,7 +10433,7 @@ async function runDaemonProcess() {
|
|
|
8224
10433
|
}
|
|
8225
10434
|
);
|
|
8226
10435
|
if (res.ok) {
|
|
8227
|
-
|
|
10436
|
+
fs6.writeFileSync(OUTBOX_PATH, "[]", { encoding: "utf-8", mode: 384 });
|
|
8228
10437
|
}
|
|
8229
10438
|
} catch {
|
|
8230
10439
|
}
|
|
@@ -8342,7 +10551,7 @@ function resolveNpxPath() {
|
|
|
8342
10551
|
} catch {
|
|
8343
10552
|
for (const p of ["/usr/local/bin/npx", "/opt/homebrew/bin/npx", `${(0, import_os.homedir)()}/.nvm/current/bin/npx`]) {
|
|
8344
10553
|
try {
|
|
8345
|
-
|
|
10554
|
+
fs6.accessSync(p);
|
|
8346
10555
|
return p;
|
|
8347
10556
|
} catch {
|
|
8348
10557
|
}
|
|
@@ -8384,8 +10593,8 @@ function installLaunchd() {
|
|
|
8384
10593
|
<string>${(0, import_path.join)((0, import_os.homedir)(), ".prismer", "daemon.stderr.log")}</string>
|
|
8385
10594
|
</dict>
|
|
8386
10595
|
</plist>`;
|
|
8387
|
-
|
|
8388
|
-
|
|
10596
|
+
fs6.mkdirSync((0, import_path.dirname)(plistPath), { recursive: true });
|
|
10597
|
+
fs6.writeFileSync(plistPath, plist, { mode: 384 });
|
|
8389
10598
|
try {
|
|
8390
10599
|
(0, import_child_process.execSync)(`launchctl load ${plistPath}`, { stdio: "pipe" });
|
|
8391
10600
|
console.log("[prismer] Daemon service installed and started (launchd)");
|
|
@@ -8401,7 +10610,7 @@ function uninstallLaunchd() {
|
|
|
8401
10610
|
} catch {
|
|
8402
10611
|
}
|
|
8403
10612
|
try {
|
|
8404
|
-
|
|
10613
|
+
fs6.unlinkSync(plistPath);
|
|
8405
10614
|
} catch {
|
|
8406
10615
|
}
|
|
8407
10616
|
console.log("[prismer] Daemon service uninstalled (launchd)");
|
|
@@ -8426,8 +10635,8 @@ RestartSec=10
|
|
|
8426
10635
|
[Install]
|
|
8427
10636
|
WantedBy=default.target
|
|
8428
10637
|
`;
|
|
8429
|
-
|
|
8430
|
-
|
|
10638
|
+
fs6.mkdirSync(serviceDir, { recursive: true });
|
|
10639
|
+
fs6.writeFileSync(servicePath, unit, { mode: 420 });
|
|
8431
10640
|
try {
|
|
8432
10641
|
(0, import_child_process.execSync)("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
8433
10642
|
(0, import_child_process.execSync)("systemctl --user enable prismer-daemon", { stdio: "pipe" });
|
|
@@ -8450,7 +10659,7 @@ function uninstallSystemd() {
|
|
|
8450
10659
|
}
|
|
8451
10660
|
const servicePath = (0, import_path.join)((0, import_os.homedir)(), ".config", "systemd", "user", "prismer-daemon.service");
|
|
8452
10661
|
try {
|
|
8453
|
-
|
|
10662
|
+
fs6.unlinkSync(servicePath);
|
|
8454
10663
|
} catch {
|
|
8455
10664
|
}
|
|
8456
10665
|
try {
|
|
@@ -8490,21 +10699,21 @@ if (process.env["PRISMER_DAEMON"] === "1") {
|
|
|
8490
10699
|
// src/cli.ts
|
|
8491
10700
|
var cliVersion = "1.7.2";
|
|
8492
10701
|
try {
|
|
8493
|
-
const pkgPath =
|
|
8494
|
-
const pkg = JSON.parse(
|
|
10702
|
+
const pkgPath = path5.join(__dirname, "..", "package.json");
|
|
10703
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf8"));
|
|
8495
10704
|
cliVersion = pkg.version || cliVersion;
|
|
8496
10705
|
} catch {
|
|
8497
10706
|
}
|
|
8498
|
-
var CONFIG_DIR2 = process.env.PRISMER_HOME ?
|
|
8499
|
-
var CONFIG_PATH2 =
|
|
10707
|
+
var CONFIG_DIR2 = process.env.PRISMER_HOME ? path5.resolve(process.env.PRISMER_HOME) : path5.join(os3.homedir(), ".prismer");
|
|
10708
|
+
var CONFIG_PATH2 = path5.join(CONFIG_DIR2, "config.toml");
|
|
8500
10709
|
function ensureConfigDir() {
|
|
8501
|
-
if (!
|
|
8502
|
-
|
|
10710
|
+
if (!fs7.existsSync(CONFIG_DIR2)) {
|
|
10711
|
+
fs7.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
8503
10712
|
}
|
|
8504
10713
|
}
|
|
8505
10714
|
function readConfig() {
|
|
8506
|
-
if (!
|
|
8507
|
-
const raw =
|
|
10715
|
+
if (!fs7.existsSync(CONFIG_PATH2)) return {};
|
|
10716
|
+
const raw = fs7.readFileSync(CONFIG_PATH2, "utf-8");
|
|
8508
10717
|
const parsed = TOML2.parse(raw);
|
|
8509
10718
|
const flatApiKey = parsed.api_key;
|
|
8510
10719
|
const flatBaseUrl = parsed.cloud_api_base ?? parsed.base_url;
|
|
@@ -8522,7 +10731,7 @@ function readConfig() {
|
|
|
8522
10731
|
}
|
|
8523
10732
|
function writeConfig(config) {
|
|
8524
10733
|
ensureConfigDir();
|
|
8525
|
-
|
|
10734
|
+
fs7.writeFileSync(CONFIG_PATH2, TOML2.stringify(config), { encoding: "utf-8", mode: 384 });
|
|
8526
10735
|
}
|
|
8527
10736
|
function setNestedValue(obj, dotPath, value) {
|
|
8528
10737
|
const parts = dotPath.split(".");
|
|
@@ -8538,16 +10747,32 @@ function getIMClient() {
|
|
|
8538
10747
|
const cfg = readConfig();
|
|
8539
10748
|
const env = cfg?.default?.environment || "production";
|
|
8540
10749
|
const baseUrl = cfg?.default?.base_url || "";
|
|
10750
|
+
const agentUsername = process.env.PRISMER_AGENT_USERNAME;
|
|
10751
|
+
const imAgentOpt = agentUsername ? { imAgent: agentUsername } : {};
|
|
10752
|
+
const workspaceId = process.env.PRISMER_WORKSPACE_ID;
|
|
10753
|
+
const imWorkspaceOpt = workspaceId ? { imWorkspace: workspaceId } : {};
|
|
8541
10754
|
const imToken = cfg?.auth?.im_token;
|
|
8542
10755
|
if (imToken) {
|
|
8543
|
-
return new PrismerClient({
|
|
10756
|
+
return new PrismerClient({
|
|
10757
|
+
apiKey: imToken,
|
|
10758
|
+
environment: env,
|
|
10759
|
+
...baseUrl ? { baseUrl } : {},
|
|
10760
|
+
...imAgentOpt,
|
|
10761
|
+
...imWorkspaceOpt
|
|
10762
|
+
});
|
|
8544
10763
|
}
|
|
8545
10764
|
const apiKey = cfg?.default?.api_key;
|
|
8546
10765
|
if (!apiKey) {
|
|
8547
10766
|
errorLine('No credentials. Run "cloud setup" first (or "cloud setup --agent" / "cloud register <username>" for IM-JWT path).');
|
|
8548
10767
|
process.exit(1);
|
|
8549
10768
|
}
|
|
8550
|
-
return new PrismerClient({
|
|
10769
|
+
return new PrismerClient({
|
|
10770
|
+
apiKey,
|
|
10771
|
+
environment: env,
|
|
10772
|
+
...baseUrl ? { baseUrl } : {},
|
|
10773
|
+
...imAgentOpt,
|
|
10774
|
+
...imWorkspaceOpt
|
|
10775
|
+
});
|
|
8551
10776
|
}
|
|
8552
10777
|
function getAPIClient() {
|
|
8553
10778
|
const cfg = readConfig();
|
|
@@ -8558,7 +10783,11 @@ function getAPIClient() {
|
|
|
8558
10783
|
}
|
|
8559
10784
|
const env = cfg?.default?.environment || "production";
|
|
8560
10785
|
const baseUrl = cfg?.default?.base_url || "";
|
|
8561
|
-
|
|
10786
|
+
const agentUsername = process.env.PRISMER_AGENT_USERNAME;
|
|
10787
|
+
const imAgentOpt = agentUsername ? { imAgent: agentUsername } : {};
|
|
10788
|
+
const workspaceId = process.env.PRISMER_WORKSPACE_ID;
|
|
10789
|
+
const imWorkspaceOpt = workspaceId ? { imWorkspace: workspaceId } : {};
|
|
10790
|
+
return new PrismerClient({ apiKey, environment: env, ...baseUrl ? { baseUrl } : {}, ...imAgentOpt, ...imWorkspaceOpt });
|
|
8562
10791
|
}
|
|
8563
10792
|
var program = new import_commander.Command();
|
|
8564
10793
|
program.name("cloud").description("Prismer Cloud SDK CLI").version(cliVersion);
|
|
@@ -8685,8 +10914,8 @@ async function runSetup(opts, apiKey) {
|
|
|
8685
10914
|
return;
|
|
8686
10915
|
}
|
|
8687
10916
|
const http2 = require("http");
|
|
8688
|
-
const
|
|
8689
|
-
const state =
|
|
10917
|
+
const crypto3 = require("crypto");
|
|
10918
|
+
const state = crypto3.randomBytes(16).toString("hex");
|
|
8690
10919
|
let resolved = false;
|
|
8691
10920
|
const server = http2.createServer((req, res) => {
|
|
8692
10921
|
const url = new URL(req.url, `http://localhost`);
|
|
@@ -8855,11 +11084,11 @@ program.command("status").description("Show current config and live info").actio
|
|
|
8855
11084
|
});
|
|
8856
11085
|
var configCmd = program.command("config").description("Manage config file");
|
|
8857
11086
|
configCmd.command("show").description("Print config file").action(() => {
|
|
8858
|
-
if (!
|
|
11087
|
+
if (!fs7.existsSync(CONFIG_PATH2)) {
|
|
8859
11088
|
warn('No config file. Run "cloud setup" to create one.');
|
|
8860
11089
|
return;
|
|
8861
11090
|
}
|
|
8862
|
-
console.log(
|
|
11091
|
+
console.log(fs7.readFileSync(CONFIG_PATH2, "utf-8"));
|
|
8863
11092
|
});
|
|
8864
11093
|
configCmd.command("set <key> <value>").description("Set a config value (e.g. default.base_url)").action((key, value) => {
|
|
8865
11094
|
const config = readConfig();
|
|
@@ -8904,6 +11133,11 @@ register10(program, getIMClient, getAPIClient);
|
|
|
8904
11133
|
register11(program, getIMClient, getAPIClient);
|
|
8905
11134
|
register12(program, getIMClient, getAPIClient);
|
|
8906
11135
|
register13(program, getIMClient, getAPIClient);
|
|
11136
|
+
register14(program, getIMClient, getAPIClient);
|
|
11137
|
+
register15(program, getIMClient, getAPIClient);
|
|
11138
|
+
register16(program, getIMClient, getAPIClient);
|
|
11139
|
+
register17(program, getIMClient, getAPIClient);
|
|
11140
|
+
register18(program, getIMClient, getAPIClient);
|
|
8907
11141
|
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
11142
|
const client = getIMClient();
|
|
8909
11143
|
let userId = target;
|
|
@@ -8952,6 +11186,70 @@ program.command("send").description("Send a direct message (shortcut for: im sen
|
|
|
8952
11186
|
}
|
|
8953
11187
|
success(`Message sent (conversation: ${res.data?.conversationId})`);
|
|
8954
11188
|
});
|
|
11189
|
+
program.command("deliver <path>").description("Attach a file you wrote to your current reply (in-container explicit delivery)").option("--run-id <id>", "dispatch run/task id (from <execution_context>; env fallback PRISMER_TASK_ID/RUN_ID)").option("--conversation-id <id>", "conversation id (from <execution_context>; env fallback PRISMER_CONVERSATION_ID)").option("--daemon-port <port>", "daemon local-server port (env fallback PRISMER_DAEMON_PORT, default 3210)").option("--json", "JSON output").action(async (filePath, opts) => {
|
|
11190
|
+
const proxy = detectDeliverProxy({
|
|
11191
|
+
runId: opts.runId,
|
|
11192
|
+
conversationId: opts.conversationId,
|
|
11193
|
+
daemonPort: opts.daemonPort
|
|
11194
|
+
});
|
|
11195
|
+
if (!proxy) {
|
|
11196
|
+
errorLine(
|
|
11197
|
+
"cloud deliver only works inside a daemon dispatch (no PRISMER_TASK_ID/PRISMER_RUN_ID, and no --run-id flag). On hermes, pass --run-id <id> (and --conversation-id <id>) copied from <execution_context>. Outside a dispatch, use `cloud file send <conversationId> <path>`."
|
|
11198
|
+
);
|
|
11199
|
+
process.exit(1);
|
|
11200
|
+
}
|
|
11201
|
+
const result = await proxyDeliver(proxy, filePath, "attach");
|
|
11202
|
+
if (!result.ok) {
|
|
11203
|
+
errorLine(`Delivery failed: ${result.error ?? `daemon returned ${result.status}`}`);
|
|
11204
|
+
process.exit(1);
|
|
11205
|
+
}
|
|
11206
|
+
if (opts.json) {
|
|
11207
|
+
console.log(JSON.stringify(result, null, 2));
|
|
11208
|
+
return;
|
|
11209
|
+
}
|
|
11210
|
+
success(`Attached to your reply (assetId: ${result.assetId ?? "-"})`);
|
|
11211
|
+
});
|
|
11212
|
+
program.command("attach <messageId> <path>").description("Attach a file to a message you ALREADY sent (by its messageId)").option("--run-id <id>", "dispatch run/task id (from <execution_context>; env fallback PRISMER_TASK_ID/RUN_ID)").option("--conversation-id <id>", "conversation id of the target message (from <execution_context>; env fallback PRISMER_CONVERSATION_ID)").option("--daemon-port <port>", "daemon local-server port (env fallback PRISMER_DAEMON_PORT, default 3210)").option("--json", "JSON output").action(async (messageId, filePath, opts) => {
|
|
11213
|
+
const proxy = detectDeliverProxy({
|
|
11214
|
+
runId: opts.runId,
|
|
11215
|
+
conversationId: opts.conversationId,
|
|
11216
|
+
daemonPort: opts.daemonPort
|
|
11217
|
+
});
|
|
11218
|
+
if (!proxy) {
|
|
11219
|
+
errorLine(
|
|
11220
|
+
"cloud attach only works inside a daemon dispatch (no PRISMER_TASK_ID/PRISMER_RUN_ID, and no --run-id flag). On hermes, pass --run-id <id> and --conversation-id <id> copied from <execution_context>."
|
|
11221
|
+
);
|
|
11222
|
+
process.exit(1);
|
|
11223
|
+
}
|
|
11224
|
+
const conversationId = (opts.conversationId || proxy.conversationId || "").trim();
|
|
11225
|
+
if (!conversationId) {
|
|
11226
|
+
errorLine(
|
|
11227
|
+
"cloud attach needs the conversation id of the target message. Set PRISMER_CONVERSATION_ID or pass --conversation-id <id> (copy it from <execution_context>)."
|
|
11228
|
+
);
|
|
11229
|
+
process.exit(1);
|
|
11230
|
+
}
|
|
11231
|
+
const result = await proxyDeliver(proxy, filePath, "message-attach", conversationId, messageId);
|
|
11232
|
+
if (!result.ok) {
|
|
11233
|
+
errorLine(`Attach failed: ${result.error ?? `daemon returned ${result.status}`}`);
|
|
11234
|
+
process.exit(1);
|
|
11235
|
+
}
|
|
11236
|
+
if (opts.json) {
|
|
11237
|
+
console.log(JSON.stringify(result, null, 2));
|
|
11238
|
+
return;
|
|
11239
|
+
}
|
|
11240
|
+
success(`Attached to message ${messageId} (assetId: ${result.assetId ?? "-"})`);
|
|
11241
|
+
});
|
|
11242
|
+
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 = []) => {
|
|
11243
|
+
prev.push(val);
|
|
11244
|
+
return prev;
|
|
11245
|
+
}).option("--ts <iso>", "business timestamp in ISO 8601 (defaults to now)").option("--json", "output raw JSON response").action(async (fqName, opts) => {
|
|
11246
|
+
try {
|
|
11247
|
+
await runEmit(fqName, opts, getIMClient);
|
|
11248
|
+
} catch (err) {
|
|
11249
|
+
errorLine(`Error: ${err.message}`);
|
|
11250
|
+
process.exit(1);
|
|
11251
|
+
}
|
|
11252
|
+
});
|
|
8955
11253
|
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
11254
|
const client = getAPIClient();
|
|
8957
11255
|
const input = urls.length === 1 ? urls[0] : urls;
|