openmates 0.27.0-alpha.6 → 0.27.0-alpha.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/{chunk-WQOBJUWS.js → chunk-IUOCCXVD.js} +859 -85
- package/dist/{cli-Bq-RdTvp.d.ts → cli-DPB5Qnz5.d.ts} +52 -10
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +11 -7
- package/dist/index.js +1 -1
- package/package.json +5 -4
|
@@ -12586,7 +12586,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
12586
12586
|
if (!userId) {
|
|
12587
12587
|
throw new Error("Could not resolve current user id for local connected-account connector.");
|
|
12588
12588
|
}
|
|
12589
|
-
const { createHash:
|
|
12589
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
12590
12590
|
const accountId = randomUUID10();
|
|
12591
12591
|
const masterKey = this.getMasterKeyBytes();
|
|
12592
12592
|
const encryptLocalConnectorValue = async (value) => {
|
|
@@ -12595,9 +12595,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
12595
12595
|
};
|
|
12596
12596
|
const payload = {
|
|
12597
12597
|
id: accountId,
|
|
12598
|
-
hashed_user_id:
|
|
12598
|
+
hashed_user_id: createHash32("sha256").update(userId).digest("hex"),
|
|
12599
12599
|
encrypted_provider_type: await encryptLocalConnectorValue(input.provider_id),
|
|
12600
|
-
provider_type_hash:
|
|
12600
|
+
provider_type_hash: createHash32("sha256").update(input.provider_id).digest("hex"),
|
|
12601
12601
|
encrypted_account_label: await encryptLocalConnectorValue(input.label),
|
|
12602
12602
|
encrypted_capabilities: await encryptLocalConnectorValue(input.capabilities),
|
|
12603
12603
|
encrypted_app_permissions: await encryptLocalConnectorValue({
|
|
@@ -13388,7 +13388,61 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
13388
13388
|
createdAt: typeof d.created_at === "number" ? d.created_at : null,
|
|
13389
13389
|
updatedAt: typeof d.last_edited_overall_timestamp === "number" ? d.last_edited_overall_timestamp : null,
|
|
13390
13390
|
category,
|
|
13391
|
-
mateName: category ? MATE_NAMES[category] ?? null : null
|
|
13391
|
+
mateName: category ? MATE_NAMES[category] ?? null : null,
|
|
13392
|
+
parentId: typeof d.parent_id === "string" ? d.parent_id : null,
|
|
13393
|
+
isSubChat: d.is_sub_chat === true,
|
|
13394
|
+
isHiddenCandidate: !chatKeyBytes
|
|
13395
|
+
};
|
|
13396
|
+
}
|
|
13397
|
+
/** Global activity snapshot; decrypt only running chats and their ancestors. */
|
|
13398
|
+
async getChatActivity(options = {}) {
|
|
13399
|
+
this.requireSession();
|
|
13400
|
+
const teamId = this.resolveTeamContext(options), master = Buffer.from(this.getMasterKeyBytes());
|
|
13401
|
+
const response = await this.http.get(
|
|
13402
|
+
this.appendTeamQuery("/v1/chats/activity", options),
|
|
13403
|
+
this.getCliRequestHeaders()
|
|
13404
|
+
);
|
|
13405
|
+
if (!response.ok) throw new Error(`Chat activity unavailable: HTTP ${response.status}`);
|
|
13406
|
+
if (!this.hasSession() || teamId !== this.resolveTeamContext(options) || !master.equals(Buffer.from(this.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
|
|
13407
|
+
const ids = response.data.active_tasks.map((task) => task.chat_id);
|
|
13408
|
+
if (!ids.length) return { ids, chats: [] };
|
|
13409
|
+
const chats = await this.getSidebarChats(response.data.chats.map((chat) => chat.chat_id), options);
|
|
13410
|
+
if (teamId !== this.resolveTeamContext(options)) throw new Error("Chat workspace changed");
|
|
13411
|
+
return { ids, chats };
|
|
13412
|
+
}
|
|
13413
|
+
/** Minimal encrypted metadata; unreadable keys stay excluded from normal surfaces. */
|
|
13414
|
+
async getSidebarChats(ids, options = {}) {
|
|
13415
|
+
this.requireSession();
|
|
13416
|
+
const teamId = this.resolveTeamContext(options), master = Buffer.from(this.getMasterKeyBytes());
|
|
13417
|
+
const key = await this.getChatWrappingKey(teamId, master), result = [];
|
|
13418
|
+
const current = () => this.hasSession() && teamId === this.resolveTeamContext(options) && master.equals(Buffer.from(this.getMasterKeyBytes()));
|
|
13419
|
+
for (let start = 0; start < ids.length; start += 100) {
|
|
13420
|
+
if (!current()) throw new Error("Chat workspace changed");
|
|
13421
|
+
const response = await this.http.post(
|
|
13422
|
+
this.appendTeamQuery("/v1/chats/metadata/batch", options),
|
|
13423
|
+
{ chat_ids: ids.slice(start, start + 100) },
|
|
13424
|
+
this.getCliRequestHeaders()
|
|
13425
|
+
);
|
|
13426
|
+
if (!response.ok) throw new Error(`Sidebar metadata unavailable: HTTP ${response.status}`);
|
|
13427
|
+
for (const details of response.data.chats) {
|
|
13428
|
+
const timestamp = Number(details.updated_at);
|
|
13429
|
+
result.push(await this.decryptChatListItem({ details: {
|
|
13430
|
+
...details,
|
|
13431
|
+
last_edited_overall_timestamp: Number.isFinite(timestamp) ? timestamp : Date.parse(String(details.updated_at)) / 1e3
|
|
13432
|
+
}, messages: [] }, key, null, teamId));
|
|
13433
|
+
}
|
|
13434
|
+
}
|
|
13435
|
+
if (!current()) throw new Error("Chat workspace changed");
|
|
13436
|
+
return result;
|
|
13437
|
+
}
|
|
13438
|
+
/** Observe lifecycle metadata without claiming or collecting AI responses. */
|
|
13439
|
+
async observeChatActivity(onChange) {
|
|
13440
|
+
const { ws } = await this.openWsClient({ taskUpdateJobs: false });
|
|
13441
|
+
const types = ["ai_task_initiated", "ai_typing_started", "ai_typing_ended", "ai_background_response_completed", "ai_task_cancel_requested", "ai_task_error", "pending_ai_response"];
|
|
13442
|
+
const cleanup = types.map((type) => ws.onMessageType(type, onChange));
|
|
13443
|
+
return () => {
|
|
13444
|
+
cleanup.forEach((remove) => remove());
|
|
13445
|
+
ws.close();
|
|
13392
13446
|
};
|
|
13393
13447
|
}
|
|
13394
13448
|
async listChats(limit = 10, page = 1, options = {}) {
|
|
@@ -14391,8 +14445,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
14391
14445
|
);
|
|
14392
14446
|
}
|
|
14393
14447
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
14394
|
-
const { createHash:
|
|
14395
|
-
const hashedEmbedId =
|
|
14448
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
14449
|
+
const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
|
|
14396
14450
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
14397
14451
|
cache,
|
|
14398
14452
|
masterKey,
|
|
@@ -14494,7 +14548,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
14494
14548
|
async resolveEmbedKey(cache, masterKey, embed, embedId, hashedEmbedId, visited = /* @__PURE__ */ new Set()) {
|
|
14495
14549
|
if (visited.has(embedId)) return null;
|
|
14496
14550
|
visited.add(embedId);
|
|
14497
|
-
const { createHash:
|
|
14551
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
14498
14552
|
const masterKeyEntry = cache.embedKeys.find(
|
|
14499
14553
|
(ek) => ek.hashed_embed_id === hashedEmbedId && String(ek.key_type) === "master"
|
|
14500
14554
|
);
|
|
@@ -14511,7 +14565,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
14511
14565
|
if (chatKeyEntry && typeof chatKeyEntry.encrypted_embed_key === "string") {
|
|
14512
14566
|
const hashedChatId = String(chatKeyEntry.hashed_chat_id ?? "");
|
|
14513
14567
|
const owningChat = cache.chats.find((c) => {
|
|
14514
|
-
const chatHash =
|
|
14568
|
+
const chatHash = createHash32("sha256").update(String(c.details.id ?? "")).digest("hex");
|
|
14515
14569
|
return chatHash === hashedChatId;
|
|
14516
14570
|
});
|
|
14517
14571
|
if (owningChat) {
|
|
@@ -14540,7 +14594,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
14540
14594
|
const parentFullId = String(
|
|
14541
14595
|
parentEmbed2.embed_id ?? parentEmbed2.id ?? ""
|
|
14542
14596
|
);
|
|
14543
|
-
const parentHashedId =
|
|
14597
|
+
const parentHashedId = createHash32("sha256").update(parentFullId).digest("hex");
|
|
14544
14598
|
const parentKey = await this.resolveEmbedKey(
|
|
14545
14599
|
cache,
|
|
14546
14600
|
masterKey,
|
|
@@ -16737,6 +16791,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
16737
16791
|
);
|
|
16738
16792
|
}
|
|
16739
16793
|
async resolveWorkflowId(query, options = {}) {
|
|
16794
|
+
if (CANONICAL_UUID_PATTERN.test(query)) return query;
|
|
16740
16795
|
const workflows = await this.listWorkflows(options);
|
|
16741
16796
|
const exactId = workflows.find((workflow) => workflow.id === query);
|
|
16742
16797
|
if (exactId) return exactId.id;
|
|
@@ -16835,6 +16890,39 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
16835
16890
|
}
|
|
16836
16891
|
return this.decryptWorkflowSlug(response.data.workflow, { personal: true });
|
|
16837
16892
|
}
|
|
16893
|
+
async saveWorkflowAsReusable(workflowId, options) {
|
|
16894
|
+
this.requireSession();
|
|
16895
|
+
if (!options.idempotencyKey.trim()) throw new Error("Workflow save-as-reusable requires a stable idempotencyKey");
|
|
16896
|
+
const response = await this.http.post(
|
|
16897
|
+
`/v1/workflows/${encodeURIComponent(workflowId)}/save-as-reusable`,
|
|
16898
|
+
{},
|
|
16899
|
+
{ ...this.getCliRequestHeaders(), "Idempotency-Key": options.idempotencyKey }
|
|
16900
|
+
);
|
|
16901
|
+
if (!response.ok || !response.data.workflow) throw new Error(`Workflow save-as-reusable failed with HTTP ${response.status}`);
|
|
16902
|
+
return this.decryptWorkflowSlug(response.data.workflow, { personal: true });
|
|
16903
|
+
}
|
|
16904
|
+
async workflowRunRouting(params) {
|
|
16905
|
+
const routing = {};
|
|
16906
|
+
if (params.sourceChatId !== void 0) routing.source_chat_id = await this.resolveRequiredChatId(params.sourceChatId, { personal: true });
|
|
16907
|
+
if (params.messageDestinationOverrides !== void 0) {
|
|
16908
|
+
routing.message_destination_overrides = Object.fromEntries(await Promise.all(
|
|
16909
|
+
Object.entries(params.messageDestinationOverrides).map(async ([nodeId, chatId]) => [nodeId, await this.resolveRequiredChatId(chatId, { personal: true })])
|
|
16910
|
+
));
|
|
16911
|
+
}
|
|
16912
|
+
if (params.returnOutputs !== void 0) routing.return_outputs = params.returnOutputs;
|
|
16913
|
+
return routing;
|
|
16914
|
+
}
|
|
16915
|
+
async runWorkflowOnce(params) {
|
|
16916
|
+
this.requireSession();
|
|
16917
|
+
if (!params.idempotencyKey.trim()) throw new Error("Workflow run-once requires a stable idempotencyKey");
|
|
16918
|
+
const response = await this.http.post(
|
|
16919
|
+
"/v1/workflows/run-once",
|
|
16920
|
+
{ title: params.title, graph: params.graph, input: params.input ?? {}, ...await this.workflowRunRouting(params) },
|
|
16921
|
+
{ ...this.getCliRequestHeaders(), "Idempotency-Key": params.idempotencyKey }
|
|
16922
|
+
);
|
|
16923
|
+
if (!response.ok || !response.data.workflow || !response.data.run) throw new Error(`Workflow run-once failed with HTTP ${response.status}`);
|
|
16924
|
+
return { workflow: await this.decryptWorkflowSlug(response.data.workflow, { personal: true }), run: response.data.run };
|
|
16925
|
+
}
|
|
16838
16926
|
async enableWorkflow(workflowId) {
|
|
16839
16927
|
return this.setWorkflowEnabled(workflowId, true);
|
|
16840
16928
|
}
|
|
@@ -16848,7 +16936,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
16848
16936
|
}
|
|
16849
16937
|
const response = await this.http.post(
|
|
16850
16938
|
`/v1/workflows/${encodeURIComponent(workflowId)}/run`,
|
|
16851
|
-
{ mode: params.mode ?? "manual", input: params.input ?? {} },
|
|
16939
|
+
{ mode: params.mode ?? "manual", input: params.input ?? {}, ...await this.workflowRunRouting(params) },
|
|
16852
16940
|
{ ...this.getCliRequestHeaders(), "Idempotency-Key": params.idempotencyKey }
|
|
16853
16941
|
);
|
|
16854
16942
|
if (!response.ok || !response.data.run) {
|
|
@@ -17357,7 +17445,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
17357
17445
|
this.requireSession();
|
|
17358
17446
|
const response = await this.http.post(
|
|
17359
17447
|
"/v1/projects/ask/plan",
|
|
17360
|
-
{ instruction: input.instruction },
|
|
17448
|
+
{ instruction: input.instruction, ...input.chatTitles !== void 0 ? { chat_titles: input.chatTitles } : {} },
|
|
17361
17449
|
this.getCliRequestHeaders()
|
|
17362
17450
|
);
|
|
17363
17451
|
if (!response.ok) throw new Error(`Project ask planning failed with HTTP ${response.status}`);
|
|
@@ -17366,7 +17454,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
17366
17454
|
async listProjectItems(projectId, options = {}) {
|
|
17367
17455
|
this.requireSession();
|
|
17368
17456
|
const response = await this.http.get(
|
|
17369
|
-
this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items`, options),
|
|
17457
|
+
this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items${options.chatOnly ? "?chat_only=true" : ""}`, options),
|
|
17370
17458
|
this.getCliRequestHeaders()
|
|
17371
17459
|
);
|
|
17372
17460
|
if (!response.ok) throw this.projectRequestError("item list", response);
|
|
@@ -17503,10 +17591,10 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
17503
17591
|
}
|
|
17504
17592
|
return { ws: opened.ws, ownerId: opened.ownerId };
|
|
17505
17593
|
}
|
|
17506
|
-
async createProjectItem(projectId, input) {
|
|
17594
|
+
async createProjectItem(projectId, input, options = {}) {
|
|
17507
17595
|
this.requireSession();
|
|
17508
17596
|
const response = await this.http.post(
|
|
17509
|
-
`/v1/projects/${encodeURIComponent(projectId)}/items`,
|
|
17597
|
+
this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items`, options),
|
|
17510
17598
|
input,
|
|
17511
17599
|
this.getCliRequestHeaders()
|
|
17512
17600
|
);
|
|
@@ -17515,6 +17603,20 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
17515
17603
|
}
|
|
17516
17604
|
return response.data.item;
|
|
17517
17605
|
}
|
|
17606
|
+
async moveProjectItemToFolder(projectId, itemId, folderId, options = {}) {
|
|
17607
|
+
this.requireSession();
|
|
17608
|
+
const response = await this.http.patch(
|
|
17609
|
+
this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items/${encodeURIComponent(itemId)}`, options),
|
|
17610
|
+
{ folder_id: folderId, updated_at: Math.floor(Date.now() / 1e3) },
|
|
17611
|
+
this.getCliRequestHeaders()
|
|
17612
|
+
);
|
|
17613
|
+
if (!response.ok) throw this.projectRequestError("folder move", response);
|
|
17614
|
+
}
|
|
17615
|
+
async createProjectFolder(projectId, input, options = {}) {
|
|
17616
|
+
this.requireSession();
|
|
17617
|
+
const response = await this.http.post(this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/folders`, options), input, this.getCliRequestHeaders());
|
|
17618
|
+
if (!response.ok) throw this.projectRequestError("folder create", response);
|
|
17619
|
+
}
|
|
17518
17620
|
async deleteProjectItemByTarget(projectId, itemType, targetId, options = {}) {
|
|
17519
17621
|
this.requireSession();
|
|
17520
17622
|
const params = new URLSearchParams({ item_type: itemType, target_id: targetId });
|
|
@@ -19150,7 +19252,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
19150
19252
|
const session = this.requireSession();
|
|
19151
19253
|
const masterKey = base64ToBytes(session.masterKeyExportedB64);
|
|
19152
19254
|
const cache = await this.ensureSynced();
|
|
19153
|
-
const { createHash:
|
|
19255
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
19154
19256
|
const embed = cache.embeds.find(
|
|
19155
19257
|
(e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
|
|
19156
19258
|
);
|
|
@@ -19158,7 +19260,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
19158
19260
|
throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
|
|
19159
19261
|
}
|
|
19160
19262
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
19161
|
-
const hashedEmbedId =
|
|
19263
|
+
const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
|
|
19162
19264
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
19163
19265
|
cache,
|
|
19164
19266
|
masterKey,
|
|
@@ -19266,8 +19368,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
19266
19368
|
if (!embed) {
|
|
19267
19369
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
19268
19370
|
}
|
|
19269
|
-
const { createHash:
|
|
19270
|
-
const hashedEmbedId =
|
|
19371
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
19372
|
+
const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
|
|
19271
19373
|
const embedKey = await this.resolveEmbedKey(
|
|
19272
19374
|
cache,
|
|
19273
19375
|
masterKey,
|
|
@@ -19383,8 +19485,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
19383
19485
|
if (!embed) {
|
|
19384
19486
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
19385
19487
|
}
|
|
19386
|
-
const { createHash:
|
|
19387
|
-
const hashedEmbedId =
|
|
19488
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
19489
|
+
const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
|
|
19388
19490
|
const embedKey = await this.resolveEmbedKey(
|
|
19389
19491
|
cache,
|
|
19390
19492
|
masterKey,
|
|
@@ -19916,9 +20018,9 @@ Required: ${schema.required.join(", ")}`
|
|
|
19916
20018
|
chats.splice(0, chats.length, ...reconciledChats);
|
|
19917
20019
|
if (totalChatCount === 0) totalChatCount = chats.length;
|
|
19918
20020
|
if (reconciliation.deleted_chat_ids?.length) {
|
|
19919
|
-
const { createHash:
|
|
20021
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
19920
20022
|
const deletedHashes = new Set(
|
|
19921
|
-
reconciliation.deleted_chat_ids.map((id) =>
|
|
20023
|
+
reconciliation.deleted_chat_ids.map((id) => createHash32("sha256").update(id).digest("hex"))
|
|
19922
20024
|
);
|
|
19923
20025
|
const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
|
|
19924
20026
|
const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
|
|
@@ -30556,12 +30658,47 @@ var OpenMatesWorkflows = class {
|
|
|
30556
30658
|
if (!response.workflow) throw new OpenMatesApiError(500, { detail: "Workflow response missing workflow" });
|
|
30557
30659
|
return this.decryptWorkflowSlug(response.workflow);
|
|
30558
30660
|
}
|
|
30661
|
+
async saveAsReusable(workflowId, options) {
|
|
30662
|
+
if (!options.idempotencyKey.trim()) throw new OpenMatesConfigError("Workflow save-as-reusable requires a stable idempotencyKey");
|
|
30663
|
+
const resolvedWorkflowId = await this.resolveId(workflowId);
|
|
30664
|
+
const response = await this.client.request(
|
|
30665
|
+
`/v1/workflows/${encodeURIComponent(resolvedWorkflowId)}/save-as-reusable`,
|
|
30666
|
+
{},
|
|
30667
|
+
void 0,
|
|
30668
|
+
{ "Idempotency-Key": options.idempotencyKey }
|
|
30669
|
+
);
|
|
30670
|
+
if (!response.workflow) throw new OpenMatesApiError(500, { detail: "Workflow response missing workflow" });
|
|
30671
|
+
return this.decryptWorkflowSlug(response.workflow);
|
|
30672
|
+
}
|
|
30673
|
+
async runRouting(params) {
|
|
30674
|
+
const routing = {};
|
|
30675
|
+
if (params.sourceChatId !== void 0) routing.source_chat_id = await resolveSdkChatId(this.client, params.sourceChatId);
|
|
30676
|
+
if (params.messageDestinationOverrides !== void 0) {
|
|
30677
|
+
routing.message_destination_overrides = Object.fromEntries(await Promise.all(
|
|
30678
|
+
Object.entries(params.messageDestinationOverrides).map(async ([nodeId, chatId]) => [nodeId, await resolveSdkChatId(this.client, chatId)])
|
|
30679
|
+
));
|
|
30680
|
+
}
|
|
30681
|
+
if (params.returnOutputs !== void 0) routing.return_outputs = params.returnOutputs;
|
|
30682
|
+
return routing;
|
|
30683
|
+
}
|
|
30684
|
+
async runOnce(params) {
|
|
30685
|
+
if (!params.idempotencyKey.trim()) throw new OpenMatesConfigError("Workflow run-once requires a stable idempotencyKey");
|
|
30686
|
+
const response = await this.client.request(
|
|
30687
|
+
"/v1/workflows/run-once",
|
|
30688
|
+
{ title: params.title, graph: params.graph, input: params.input ?? {}, ...await this.runRouting(params) },
|
|
30689
|
+
void 0,
|
|
30690
|
+
{ "Idempotency-Key": params.idempotencyKey }
|
|
30691
|
+
);
|
|
30692
|
+
if (!response.workflow || !response.run) throw new OpenMatesApiError(500, { detail: "Workflow run-once response missing workflow or run" });
|
|
30693
|
+
return { workflow: await this.decryptWorkflowSlug(response.workflow), run: response.run };
|
|
30694
|
+
}
|
|
30559
30695
|
async run(workflowId, params) {
|
|
30560
30696
|
if (!params.idempotencyKey.trim()) throw new OpenMatesConfigError("Workflow run requires a stable idempotencyKey");
|
|
30561
30697
|
const resolvedWorkflowId = await this.resolveId(workflowId);
|
|
30562
30698
|
const response = await this.client.request(`/v1/workflows/${encodeURIComponent(resolvedWorkflowId)}/run`, {
|
|
30563
30699
|
mode: params.mode ?? "manual",
|
|
30564
|
-
input: params.input ?? {}
|
|
30700
|
+
input: params.input ?? {},
|
|
30701
|
+
...await this.runRouting(params)
|
|
30565
30702
|
}, void 0, { "Idempotency-Key": params.idempotencyKey });
|
|
30566
30703
|
if (!response.run) throw new OpenMatesApiError(500, { detail: "Workflow response missing run" });
|
|
30567
30704
|
return response.run;
|
|
@@ -31158,7 +31295,7 @@ async function flushPendingTaskActivities(client2, teamId, stateDir2 = resolveSt
|
|
|
31158
31295
|
import { existsSync as existsSync26, readFileSync as readFileSync24, realpathSync as realpathSync12, readdirSync as readdirSync14, statSync as statSync11, writeFileSync as writeFileSync16 } from "fs";
|
|
31159
31296
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
31160
31297
|
import { basename as basename9, dirname as dirname16, join as join29 } from "path";
|
|
31161
|
-
import { createHash as
|
|
31298
|
+
import { createHash as createHash31, randomBytes as randomBytes14, randomUUID as randomUUID24 } from "crypto";
|
|
31162
31299
|
import { arch as arch2, platform as platform3 } from "os";
|
|
31163
31300
|
import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
|
|
31164
31301
|
|
|
@@ -31270,7 +31407,7 @@ function assertPortable(value) {
|
|
|
31270
31407
|
}
|
|
31271
31408
|
function remapGraph(graph, ids) {
|
|
31272
31409
|
const escaped = Array.from(ids.keys()).sort((a, b) => b.length - a.length).map((id) => id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
31273
|
-
const references = escaped.length ? new RegExp(`(\\$nodes\\.|\\{\\{\\s*steps\\.)(${escaped.join("|")})(?=[.}\\s]|$)`, "g") : null;
|
|
31410
|
+
const references = escaped.length ? new RegExp(`(\\$nodes\\.|\\{\\{\\s*steps\\.|\\$items\\.|\\{\\{\\s*items\\.)(${escaped.join("|")})(?=[.}\\s]|$)`, "g") : null;
|
|
31274
31411
|
function visit(value) {
|
|
31275
31412
|
if (typeof value === "string") return references ? value.replace(references, (_match, prefix, id) => prefix + ids.get(id)) : value;
|
|
31276
31413
|
if (Array.isArray(value)) return value.map((item) => visit(item));
|
|
@@ -60704,6 +60841,15 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
60704
60841
|
manual_intro: {
|
|
60705
60842
|
text: "Use this skill directly by entering your request below."
|
|
60706
60843
|
},
|
|
60844
|
+
requirements: {
|
|
60845
|
+
text: "Requirements"
|
|
60846
|
+
},
|
|
60847
|
+
optional: {
|
|
60848
|
+
text: "(optional)"
|
|
60849
|
+
},
|
|
60850
|
+
requirements_placeholder: {
|
|
60851
|
+
text: "Describe what the results should match."
|
|
60852
|
+
},
|
|
60707
60853
|
via: {
|
|
60708
60854
|
text: "via"
|
|
60709
60855
|
},
|
|
@@ -60905,6 +61051,21 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
60905
61051
|
request_error: {
|
|
60906
61052
|
text: "The request could not finish. You can try again."
|
|
60907
61053
|
},
|
|
61054
|
+
saving: {
|
|
61055
|
+
text: "Saving results\u2026"
|
|
61056
|
+
},
|
|
61057
|
+
saved: {
|
|
61058
|
+
text: "Results saved"
|
|
61059
|
+
},
|
|
61060
|
+
save_failed: {
|
|
61061
|
+
text: "Results are available. Saving failed."
|
|
61062
|
+
},
|
|
61063
|
+
retry_save: {
|
|
61064
|
+
text: "Retry saving"
|
|
61065
|
+
},
|
|
61066
|
+
saving_results: {
|
|
61067
|
+
text: "Results awaiting save"
|
|
61068
|
+
},
|
|
60908
61069
|
viewer_read_only: {
|
|
60909
61070
|
text: "Your team role allows viewing results. Ask a team member to run this skill."
|
|
60910
61071
|
},
|
|
@@ -62135,6 +62296,64 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
62135
62296
|
}
|
|
62136
62297
|
},
|
|
62137
62298
|
chats: {
|
|
62299
|
+
projects: {
|
|
62300
|
+
move: {
|
|
62301
|
+
text: "Move to project"
|
|
62302
|
+
},
|
|
62303
|
+
move_here: {
|
|
62304
|
+
text: "Move here"
|
|
62305
|
+
},
|
|
62306
|
+
add: {
|
|
62307
|
+
text: "Add to project"
|
|
62308
|
+
},
|
|
62309
|
+
create: {
|
|
62310
|
+
text: "Create new project"
|
|
62311
|
+
},
|
|
62312
|
+
add_here: {
|
|
62313
|
+
text: "Add here"
|
|
62314
|
+
},
|
|
62315
|
+
new_folder: {
|
|
62316
|
+
text: "New subfolder"
|
|
62317
|
+
},
|
|
62318
|
+
folder_name: {
|
|
62319
|
+
text: "Folder name"
|
|
62320
|
+
},
|
|
62321
|
+
path: {
|
|
62322
|
+
text: "Project path"
|
|
62323
|
+
},
|
|
62324
|
+
up: {
|
|
62325
|
+
text: "Go up one level"
|
|
62326
|
+
},
|
|
62327
|
+
ancestors: {
|
|
62328
|
+
text: "Show full path"
|
|
62329
|
+
},
|
|
62330
|
+
open: {
|
|
62331
|
+
text: "Open project"
|
|
62332
|
+
},
|
|
62333
|
+
organizing: {
|
|
62334
|
+
text: "Organizing chats\u2026"
|
|
62335
|
+
},
|
|
62336
|
+
error: {
|
|
62337
|
+
text: "Could not organize chats. Please try again."
|
|
62338
|
+
}
|
|
62339
|
+
},
|
|
62340
|
+
activity: {
|
|
62341
|
+
heading: {
|
|
62342
|
+
text: "Active chats"
|
|
62343
|
+
},
|
|
62344
|
+
count: {
|
|
62345
|
+
text: "{count} chats active\u2026"
|
|
62346
|
+
},
|
|
62347
|
+
count_single: {
|
|
62348
|
+
text: "1 chat active\u2026"
|
|
62349
|
+
},
|
|
62350
|
+
subchats: {
|
|
62351
|
+
text: "{count} subchats running"
|
|
62352
|
+
},
|
|
62353
|
+
subchats_single: {
|
|
62354
|
+
text: "1 subchat running"
|
|
62355
|
+
}
|
|
62356
|
+
},
|
|
62138
62357
|
chat: {
|
|
62139
62358
|
welcome: {
|
|
62140
62359
|
back_to_recent: {
|
|
@@ -81647,6 +81866,108 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
81647
81866
|
if_unsure: {
|
|
81648
81867
|
text: "If unsure"
|
|
81649
81868
|
},
|
|
81869
|
+
check_result: {
|
|
81870
|
+
text: "Result"
|
|
81871
|
+
},
|
|
81872
|
+
check_result_boolean: {
|
|
81873
|
+
text: "True or False"
|
|
81874
|
+
},
|
|
81875
|
+
check_result_options: {
|
|
81876
|
+
text: "Multiple choice"
|
|
81877
|
+
},
|
|
81878
|
+
check_selection_mode: {
|
|
81879
|
+
text: "Select"
|
|
81880
|
+
},
|
|
81881
|
+
check_select_one: {
|
|
81882
|
+
text: "Exactly one"
|
|
81883
|
+
},
|
|
81884
|
+
check_select_multiple: {
|
|
81885
|
+
text: "All that apply"
|
|
81886
|
+
},
|
|
81887
|
+
check_option_name: {
|
|
81888
|
+
text: "Option name"
|
|
81889
|
+
},
|
|
81890
|
+
check_option_description: {
|
|
81891
|
+
text: "Select when (optional)"
|
|
81892
|
+
},
|
|
81893
|
+
add_option: {
|
|
81894
|
+
text: "Add option"
|
|
81895
|
+
},
|
|
81896
|
+
remove_option: {
|
|
81897
|
+
text: "Remove option"
|
|
81898
|
+
},
|
|
81899
|
+
if_no_match: {
|
|
81900
|
+
text: "If none match"
|
|
81901
|
+
},
|
|
81902
|
+
selected: {
|
|
81903
|
+
text: "Selected"
|
|
81904
|
+
},
|
|
81905
|
+
no_match: {
|
|
81906
|
+
text: "No match"
|
|
81907
|
+
},
|
|
81908
|
+
ai_check_options_guidance: {
|
|
81909
|
+
text: "Choose from the named options using earlier results. A variable alone is enough; an instruction is optional. Unsure is used when evidence is missing."
|
|
81910
|
+
},
|
|
81911
|
+
branch_mapping_required: {
|
|
81912
|
+
text: "This branch contains steps. Move or remove those steps before changing its result or deleting it."
|
|
81913
|
+
},
|
|
81914
|
+
for_each: {
|
|
81915
|
+
text: "For each"
|
|
81916
|
+
},
|
|
81917
|
+
for_each_items: {
|
|
81918
|
+
text: "List to process"
|
|
81919
|
+
},
|
|
81920
|
+
for_each_max_items: {
|
|
81921
|
+
text: "Maximum items"
|
|
81922
|
+
},
|
|
81923
|
+
for_each_advanced_limits: {
|
|
81924
|
+
text: "Advanced limits"
|
|
81925
|
+
},
|
|
81926
|
+
for_each_duration: {
|
|
81927
|
+
text: "Total time limit (seconds)"
|
|
81928
|
+
},
|
|
81929
|
+
for_each_credits: {
|
|
81930
|
+
text: "Credit limit"
|
|
81931
|
+
},
|
|
81932
|
+
for_each_timeout: {
|
|
81933
|
+
text: "Time limit per item (seconds)"
|
|
81934
|
+
},
|
|
81935
|
+
for_each_limits: {
|
|
81936
|
+
text: "Processes items one at a time. Defaults: 100 items, 300 seconds, 100 credits and 60 seconds per item."
|
|
81937
|
+
},
|
|
81938
|
+
for_each_array_required: {
|
|
81939
|
+
text: "Choose an earlier list and a maximum of 1 to 100 items."
|
|
81940
|
+
},
|
|
81941
|
+
for_each_body: {
|
|
81942
|
+
text: "For each item"
|
|
81943
|
+
},
|
|
81944
|
+
this_item: {
|
|
81945
|
+
text: "This item"
|
|
81946
|
+
},
|
|
81947
|
+
start_input: {
|
|
81948
|
+
text: "Start input"
|
|
81949
|
+
},
|
|
81950
|
+
item_number: {
|
|
81951
|
+
text: "Item"
|
|
81952
|
+
},
|
|
81953
|
+
chat_actions: {
|
|
81954
|
+
text: "Workflow actions"
|
|
81955
|
+
},
|
|
81956
|
+
chat_open_workflow: {
|
|
81957
|
+
text: "Open workflow"
|
|
81958
|
+
},
|
|
81959
|
+
chat_save_reusable: {
|
|
81960
|
+
text: "Save as reusable"
|
|
81961
|
+
},
|
|
81962
|
+
chat_open_saved_copy: {
|
|
81963
|
+
text: "Open saved copy"
|
|
81964
|
+
},
|
|
81965
|
+
chat_snapshot_note: {
|
|
81966
|
+
text: "Showing the workflow saved with this chat. Open the Workflows workspace to edit it."
|
|
81967
|
+
},
|
|
81968
|
+
chat_copy_failed: {
|
|
81969
|
+
text: "Could not save this workflow."
|
|
81970
|
+
},
|
|
81650
81971
|
if: {
|
|
81651
81972
|
text: "If"
|
|
81652
81973
|
},
|
|
@@ -83994,7 +84315,7 @@ function formatMetricDelta(delta, suffix) {
|
|
|
83994
84315
|
// src/tui.ts
|
|
83995
84316
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
83996
84317
|
import { stdin as nodeStdin, stdout as nodeStdout } from "process";
|
|
83997
|
-
import { randomUUID as
|
|
84318
|
+
import { randomUUID as randomUUID21 } from "crypto";
|
|
83998
84319
|
|
|
83999
84320
|
// src/tuiExampleContinuation.ts
|
|
84000
84321
|
function buildExampleContinuationHistory(conversation) {
|
|
@@ -84687,13 +85008,13 @@ function storedPath(metadata, displayName) {
|
|
|
84687
85008
|
return null;
|
|
84688
85009
|
}
|
|
84689
85010
|
}
|
|
84690
|
-
async function loadTuiProject(client2, id) {
|
|
85011
|
+
async function loadTuiProject(client2, id, chatOnly = false) {
|
|
84691
85012
|
const context = contextFor(client2);
|
|
84692
85013
|
const detail = await client2.getProject(id, context);
|
|
84693
85014
|
const project = await projectFromRecord(client2, detail.project, context);
|
|
84694
85015
|
const [itemsResponse, sourceRecords] = await Promise.all([
|
|
84695
|
-
client2.listProjectItems(id, context),
|
|
84696
|
-
client2.listProjectSources(id, context)
|
|
85016
|
+
client2.listProjectItems(id, { ...context, chatOnly }),
|
|
85017
|
+
chatOnly ? Promise.resolve([]) : client2.listProjectSources(id, context)
|
|
84697
85018
|
]);
|
|
84698
85019
|
const folderIds = itemsResponse.folders.map((folder) => String(folder.folder_id ?? "")).filter(Boolean);
|
|
84699
85020
|
const folderByHash = new Map(folderIds.map((folderId) => [createHash21("sha256").update(folderId).digest("hex"), folderId]));
|
|
@@ -84927,7 +85248,7 @@ function buildProjectForm() {
|
|
|
84927
85248
|
]
|
|
84928
85249
|
};
|
|
84929
85250
|
}
|
|
84930
|
-
async function submitProjectForm(client2, form) {
|
|
85251
|
+
async function submitProjectForm(client2, form, chatOrganizationOnly = false) {
|
|
84931
85252
|
const name = formValue(form, "name").trim();
|
|
84932
85253
|
if (!name) throw new Error("Project name is required.");
|
|
84933
85254
|
const writeMode = formValue(form, "write_mode");
|
|
@@ -84951,7 +85272,7 @@ async function submitProjectForm(client2, form) {
|
|
|
84951
85272
|
created_at: timestamp,
|
|
84952
85273
|
updated_at: timestamp,
|
|
84953
85274
|
last_opened_at: timestamp,
|
|
84954
|
-
write_mode: writeMode,
|
|
85275
|
+
write_mode: chatOrganizationOnly ? null : writeMode,
|
|
84955
85276
|
default_focus_id: focus.focus_id,
|
|
84956
85277
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: focus }), key),
|
|
84957
85278
|
key_wrappers: wrapping.teamId ? [{
|
|
@@ -84963,7 +85284,7 @@ async function submitProjectForm(client2, form) {
|
|
|
84963
85284
|
created_at: timestamp
|
|
84964
85285
|
}] : []
|
|
84965
85286
|
};
|
|
84966
|
-
const result = await client2.createProject(payload, context);
|
|
85287
|
+
const result = await client2.createProject({ ...payload, ...chatOrganizationOnly ? { chat_organization_only: true } : {} }, context);
|
|
84967
85288
|
return projectFromRecord(client2, { ...payload, ...result.project ?? {} }, context);
|
|
84968
85289
|
}
|
|
84969
85290
|
|
|
@@ -85538,6 +85859,10 @@ var TUI_ACTIONS = [
|
|
|
85538
85859
|
{ label: "Move selected task", command: "/task-status" },
|
|
85539
85860
|
{ label: "Add task activity", command: "/task-activity" },
|
|
85540
85861
|
{ label: "Create Project", command: "/project-create" },
|
|
85862
|
+
{ label: "Add chat to project", command: "/chat-add-to-project" },
|
|
85863
|
+
{ label: "Move chat to project", command: "/chat-move-to-project" },
|
|
85864
|
+
{ label: "Create project from chats", command: "/chat-create-project" },
|
|
85865
|
+
{ label: "Show active chats", command: "/active" },
|
|
85541
85866
|
{ label: "New chat in Project", command: "/project-chat" },
|
|
85542
85867
|
{ label: "Edit workflow step", command: "/workflow-edit" },
|
|
85543
85868
|
{ label: "Enable or disable workflow", command: "/workflow-toggle" },
|
|
@@ -85552,6 +85877,259 @@ function paletteActions(query) {
|
|
|
85552
85877
|
return TUI_ACTIONS.filter((item) => `${item.label} ${item.command}`.toLowerCase().includes(needle));
|
|
85553
85878
|
}
|
|
85554
85879
|
|
|
85880
|
+
// src/tuiChatSidebar.ts
|
|
85881
|
+
import { createHash as createHash22, randomUUID as randomUUID18 } from "crypto";
|
|
85882
|
+
|
|
85883
|
+
// ../ui/src/utils/chatActivity.ts
|
|
85884
|
+
function processingAncestorIds(chats, runningIds) {
|
|
85885
|
+
const byId = new Map(chats.map((chat) => [chat.chat_id, chat]));
|
|
85886
|
+
const result = /* @__PURE__ */ new Set();
|
|
85887
|
+
for (const id of runningIds) {
|
|
85888
|
+
const ancestry = [];
|
|
85889
|
+
const visited = /* @__PURE__ */ new Set();
|
|
85890
|
+
let chat = byId.get(id);
|
|
85891
|
+
while (chat && !visited.has(chat.chat_id)) {
|
|
85892
|
+
visited.add(chat.chat_id);
|
|
85893
|
+
ancestry.push(chat);
|
|
85894
|
+
chat = chat.parent_id ? byId.get(chat.parent_id) : void 0;
|
|
85895
|
+
}
|
|
85896
|
+
if (ancestry.some((chat2) => chat2.is_hidden || chat2.is_hidden_candidate)) continue;
|
|
85897
|
+
for (const ancestor of ancestry) result.add(ancestor.chat_id);
|
|
85898
|
+
}
|
|
85899
|
+
return result;
|
|
85900
|
+
}
|
|
85901
|
+
function aggregateRunningChats(chats, runningIds) {
|
|
85902
|
+
const byId = new Map(chats.map((chat) => [chat.chat_id, chat]));
|
|
85903
|
+
const groups = /* @__PURE__ */ new Map();
|
|
85904
|
+
for (const id of runningIds) {
|
|
85905
|
+
let root = byId.get(id);
|
|
85906
|
+
if (!root) continue;
|
|
85907
|
+
const visited = /* @__PURE__ */ new Set([id]);
|
|
85908
|
+
let hidden = root.is_hidden || root.is_hidden_candidate;
|
|
85909
|
+
while (root.parent_id && byId.has(root.parent_id) && !visited.has(root.parent_id)) {
|
|
85910
|
+
visited.add(root.parent_id);
|
|
85911
|
+
root = byId.get(root.parent_id);
|
|
85912
|
+
hidden ||= root.is_hidden || root.is_hidden_candidate;
|
|
85913
|
+
}
|
|
85914
|
+
if (hidden) continue;
|
|
85915
|
+
let group = groups.get(root.chat_id);
|
|
85916
|
+
if (!group) {
|
|
85917
|
+
group = { chat: root, activeSubChatCount: 0 };
|
|
85918
|
+
groups.set(root.chat_id, group);
|
|
85919
|
+
}
|
|
85920
|
+
if (id !== root.chat_id) group.activeSubChatCount += 1;
|
|
85921
|
+
}
|
|
85922
|
+
return [...groups.values()];
|
|
85923
|
+
}
|
|
85924
|
+
|
|
85925
|
+
// ../ui/src/utils/chatProjectNavigation.ts
|
|
85926
|
+
function projectBreadcrumbs(project, folderId) {
|
|
85927
|
+
const ancestors = [];
|
|
85928
|
+
const visited = /* @__PURE__ */ new Set();
|
|
85929
|
+
let folder = project.folders.find((folder2) => folder2.id === folderId);
|
|
85930
|
+
while (folder && !visited.has(folder.id)) {
|
|
85931
|
+
visited.add(folder.id);
|
|
85932
|
+
ancestors.unshift({ id: folder.id, name: folder.name });
|
|
85933
|
+
folder = project.folders.find((candidate) => candidate.hash === folder.parentHash);
|
|
85934
|
+
}
|
|
85935
|
+
return [{ id: null, name: project.name }, ...ancestors];
|
|
85936
|
+
}
|
|
85937
|
+
function projectFolderChatIds(project, folderId, recursive = false) {
|
|
85938
|
+
const folder = project.folders.find((folder2) => folder2.id === folderId);
|
|
85939
|
+
if (folderId !== null && !folder) return /* @__PURE__ */ new Set();
|
|
85940
|
+
const hashes = /* @__PURE__ */ new Set([folder?.hash ?? null]);
|
|
85941
|
+
if (recursive) {
|
|
85942
|
+
let previous = -1;
|
|
85943
|
+
while (previous !== hashes.size) {
|
|
85944
|
+
previous = hashes.size;
|
|
85945
|
+
for (const candidate of project.folders) if (hashes.has(candidate.parentHash)) hashes.add(candidate.hash);
|
|
85946
|
+
}
|
|
85947
|
+
}
|
|
85948
|
+
return new Set(project.chats.filter((chat) => hashes.has(chat.folderHash)).map((chat) => chat.chatId));
|
|
85949
|
+
}
|
|
85950
|
+
function locationHasRunningChats(project, folderId, runningIds) {
|
|
85951
|
+
return [...projectFolderChatIds(project, folderId, true)].some((id) => runningIds.has(id));
|
|
85952
|
+
}
|
|
85953
|
+
|
|
85954
|
+
// src/tuiChatSidebar.ts
|
|
85955
|
+
function rowKey(row) {
|
|
85956
|
+
return [row.kind, row.chatId ?? "", row.projectId ?? "", row.folderId ?? ""].join(":");
|
|
85957
|
+
}
|
|
85958
|
+
function updateTuiChatSidebar(state, update) {
|
|
85959
|
+
const selected = tuiChatSidebarRows(state)[state.sidebarIndex];
|
|
85960
|
+
update();
|
|
85961
|
+
if (state.workspace !== "chats" || state.focus !== "sidebar") return;
|
|
85962
|
+
const rows2 = tuiChatSidebarRows(state);
|
|
85963
|
+
const index = selected ? rows2.findIndex((row) => rowKey(row) === rowKey(selected)) : -1;
|
|
85964
|
+
state.sidebarIndex = index >= 0 ? index : Math.max(0, Math.min(state.sidebarIndex, rows2.length - 1));
|
|
85965
|
+
}
|
|
85966
|
+
function runningTuiChatGroups(state) {
|
|
85967
|
+
const chats = new Map([...state.recentChats, ...state.activityChats].map((chat) => [chat.id, chat]));
|
|
85968
|
+
return aggregateRunningChats([...chats.values()].map((chat) => ({ chat_id: chat.id, parent_id: chat.parentId, is_hidden_candidate: chat.isHiddenCandidate })), new Set(state.runningChatIds)).map((group) => ({ ...group, chat: chats.get(group.chat.chat_id) }));
|
|
85969
|
+
}
|
|
85970
|
+
function tuiSidebarProjects(state) {
|
|
85971
|
+
return state.chatSidebarProjects.map((project) => ({
|
|
85972
|
+
id: project.id,
|
|
85973
|
+
name: project.name,
|
|
85974
|
+
folders: project.folders.map((folder) => ({
|
|
85975
|
+
id: folder.id,
|
|
85976
|
+
name: folder.name,
|
|
85977
|
+
hash: createHash22("sha256").update(folder.id).digest("hex"),
|
|
85978
|
+
parentHash: folder.parentHash ?? null
|
|
85979
|
+
})),
|
|
85980
|
+
chats: project.items.filter((item) => item.type === "chat").map((item) => ({
|
|
85981
|
+
id: item.id,
|
|
85982
|
+
chatId: item.targetId,
|
|
85983
|
+
folderHash: item.folderId ? createHash22("sha256").update(item.folderId).digest("hex") : null
|
|
85984
|
+
}))
|
|
85985
|
+
}));
|
|
85986
|
+
}
|
|
85987
|
+
function tuiChatBreadcrumb(state, width = 23) {
|
|
85988
|
+
const location = state.chatSidebarLocation;
|
|
85989
|
+
const project = tuiSidebarProjects(state).find((project2) => project2.id === location?.projectId);
|
|
85990
|
+
if (!project || !location) return null;
|
|
85991
|
+
const crumbs = projectBreadcrumbs(project, location.folderId);
|
|
85992
|
+
if (crumbs.length === 1) return truncateCells(project.name, width);
|
|
85993
|
+
const separator = crumbs.length > 2 ? " \u203A \u2026 \u203A " : " \u203A ";
|
|
85994
|
+
const current = crumbs.at(-1).name, budget = Math.max(2, width - cells(separator));
|
|
85995
|
+
if (cells(project.name) + cells(current) <= budget) return `${project.name}${separator}${current}`;
|
|
85996
|
+
const leafWidth = Math.min(cells(current), Math.max(1, budget - Math.min(cells(project.name), Math.ceil(budget / 2))));
|
|
85997
|
+
return `${truncateCells(project.name, budget - leafWidth)}${separator}${truncateCells(current, leafWidth)}`;
|
|
85998
|
+
}
|
|
85999
|
+
function tuiChatSidebarRows(state) {
|
|
86000
|
+
const groups = runningTuiChatGroups(state);
|
|
86001
|
+
const records = [...state.recentChats, ...state.activityChats].map((chat) => ({ chat_id: chat.id, parent_id: chat.parentId, is_hidden_candidate: chat.isHiddenCandidate }));
|
|
86002
|
+
const running = processingAncestorIds(records, new Set(state.runningChatIds));
|
|
86003
|
+
const rows2 = groups.map((group) => ({
|
|
86004
|
+
kind: "chat",
|
|
86005
|
+
chatId: group.chat.id,
|
|
86006
|
+
running: true,
|
|
86007
|
+
label: `${group.chat.title || "Untitled chat"}${group.activeSubChatCount ? ` (${group.activeSubChatCount} ${group.activeSubChatCount === 1 ? "subchat" : "subchats"})` : ""}`
|
|
86008
|
+
}));
|
|
86009
|
+
rows2.push({ kind: "new", label: "+ New chat" });
|
|
86010
|
+
if (state.chatProjectOperation) rows2.push({ kind: "create", label: "+ Create new project" });
|
|
86011
|
+
const projects = tuiSidebarProjects(state), location = state.chatSidebarLocation;
|
|
86012
|
+
const project = projects.find((project2) => project2.id === location?.projectId);
|
|
86013
|
+
let contents = null;
|
|
86014
|
+
if (project && location) {
|
|
86015
|
+
const crumbs = projectBreadcrumbs(project, location.folderId);
|
|
86016
|
+
rows2.push({ kind: "up", label: "\u2039 Up one level", projectId: project.id, folderId: crumbs.length > 1 ? crumbs[crumbs.length - 2].id : void 0 });
|
|
86017
|
+
if (crumbs.length > 2) rows2.push({ kind: "path", label: "\u2026 Show full path" });
|
|
86018
|
+
if (state.chatSidebarAncestors) for (const crumb of crumbs) rows2.push({ kind: "folder", label: crumb.name, projectId: project.id, folderId: crumb.id });
|
|
86019
|
+
if (state.chatProjectOperation) rows2.push({ kind: "choose", label: state.chatProjectOperation.mode === "move" ? "Move here" : "Add here", projectId: project.id, folderId: location.folderId });
|
|
86020
|
+
const hash2 = project.folders.find((folder) => folder.id === location.folderId)?.hash ?? null;
|
|
86021
|
+
rows2.push(...project.folders.filter((folder) => folder.parentHash === hash2).map((folder) => ({
|
|
86022
|
+
kind: "folder",
|
|
86023
|
+
label: folder.name,
|
|
86024
|
+
projectId: project.id,
|
|
86025
|
+
folderId: folder.id,
|
|
86026
|
+
running: locationHasRunningChats(project, folder.id, running)
|
|
86027
|
+
})));
|
|
86028
|
+
contents = projectFolderChatIds(project, location.folderId);
|
|
86029
|
+
} else rows2.push(...projects.map((project2) => ({
|
|
86030
|
+
kind: "project",
|
|
86031
|
+
label: project2.name,
|
|
86032
|
+
projectId: project2.id,
|
|
86033
|
+
running: locationHasRunningChats(project2, null, running)
|
|
86034
|
+
})));
|
|
86035
|
+
const organized = new Set(projects.flatMap((project2) => project2.chats.map((chat) => chat.chatId)));
|
|
86036
|
+
const candidates = new Map([...state.recentChats, ...state.sidebarLinkedChats].filter((chat) => !chat.isHiddenCandidate).map((chat) => [chat.id, { id: chat.id, title: chat.title, parentId: chat.parentId, isSubChat: chat.isSubChat }]));
|
|
86037
|
+
if (contents && location) for (const item of state.chatSidebarProjects.find((project2) => project2.id === location.projectId)?.items ?? []) {
|
|
86038
|
+
const verified = candidates.get(item.targetId);
|
|
86039
|
+
if (item.type === "chat" && contents.has(item.targetId) && verified && !verified.title)
|
|
86040
|
+
candidates.set(item.targetId, { ...verified, title: item.name });
|
|
86041
|
+
}
|
|
86042
|
+
if (!state.chatProjectOperation) rows2.push(...[...candidates.values()].filter((chat) => !running.has(chat.id) && (contents ? contents.has(chat.id) : !chat.parentId && !chat.isSubChat && !organized.has(chat.id))).map((chat) => ({ kind: "chat", chatId: chat.id, label: chat.title || "Untitled chat" })));
|
|
86043
|
+
return rows2;
|
|
86044
|
+
}
|
|
86045
|
+
async function refreshTuiChatSidebar(state, client2, render, includeProjects = false) {
|
|
86046
|
+
if (!state.signedIn) return;
|
|
86047
|
+
const team = typeof client2.getActiveTeamId === "function" ? client2.getActiveTeamId() : null;
|
|
86048
|
+
const master = typeof client2.getMasterKeyBytes === "function" ? Buffer.from(client2.getMasterKeyBytes()) : null;
|
|
86049
|
+
const current = () => state.signedIn && (typeof client2.getActiveTeamId !== "function" || client2.getActiveTeamId() === team) && (!master || master.equals(Buffer.from(client2.getMasterKeyBytes())));
|
|
86050
|
+
if (typeof client2.getChatActivity === "function") {
|
|
86051
|
+
try {
|
|
86052
|
+
const activity = await client2.getChatActivity();
|
|
86053
|
+
if (current()) {
|
|
86054
|
+
updateTuiChatSidebar(state, () => {
|
|
86055
|
+
state.runningChatIds = activity.ids;
|
|
86056
|
+
state.activityChats = activity.chats;
|
|
86057
|
+
});
|
|
86058
|
+
render();
|
|
86059
|
+
}
|
|
86060
|
+
} catch {
|
|
86061
|
+
if (current()) {
|
|
86062
|
+
state.status = "Running chat status unavailable.";
|
|
86063
|
+
render();
|
|
86064
|
+
}
|
|
86065
|
+
}
|
|
86066
|
+
}
|
|
86067
|
+
if (includeProjects && typeof client2.listProjects === "function") {
|
|
86068
|
+
const summaries = await loadTuiProjects(client2), projects = [];
|
|
86069
|
+
for (let start = 0; start < summaries.length; start += 4) projects.push(...await Promise.all(summaries.slice(start, start + 4).map((project) => loadTuiProject(client2, project.id, true))));
|
|
86070
|
+
const linkedIds = [...new Set(projects.flatMap((project) => project.items.filter((item) => item.type === "chat").map((item) => item.targetId)))];
|
|
86071
|
+
const linkedChats = typeof client2.getSidebarChats === "function" ? await client2.getSidebarChats(linkedIds) : [];
|
|
86072
|
+
if (current()) {
|
|
86073
|
+
updateTuiChatSidebar(state, () => {
|
|
86074
|
+
state.chatSidebarProjects = projects;
|
|
86075
|
+
state.sidebarLinkedChats = linkedChats;
|
|
86076
|
+
});
|
|
86077
|
+
render();
|
|
86078
|
+
}
|
|
86079
|
+
}
|
|
86080
|
+
}
|
|
86081
|
+
async function placeTuiChats(state, client2, chatIds, location, mode) {
|
|
86082
|
+
const context = { teamId: client2.getActiveTeamId() }, master = Buffer.from(client2.getMasterKeyBytes());
|
|
86083
|
+
const fence = () => {
|
|
86084
|
+
if (!state.signedIn || context.teamId !== client2.getActiveTeamId() || !master.equals(Buffer.from(client2.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
|
|
86085
|
+
};
|
|
86086
|
+
const destination = await loadTuiProject(client2, location.projectId, true);
|
|
86087
|
+
if (location.folderId && !destination.folders.some((folder) => folder.id === location.folderId)) throw new Error("Folder unavailable");
|
|
86088
|
+
for (const chatId of chatIds) {
|
|
86089
|
+
fence();
|
|
86090
|
+
const existing = destination.items.find((item) => item.type === "chat" && item.targetId === chatId);
|
|
86091
|
+
if (existing) {
|
|
86092
|
+
if ((existing.folderId ?? null) !== location.folderId) await client2.moveProjectItemToFolder(destination.id, existing.id, location.folderId, context);
|
|
86093
|
+
} else {
|
|
86094
|
+
const title = [...state.recentChats, ...state.activityChats].find((chat) => chat.id === chatId)?.title || "Untitled chat";
|
|
86095
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
86096
|
+
const payload = {
|
|
86097
|
+
project_item_id: randomUUID18(),
|
|
86098
|
+
folder_id: location.folderId,
|
|
86099
|
+
item_type: "chat",
|
|
86100
|
+
target_id: chatId,
|
|
86101
|
+
target_id_encrypted: await encryptWithAesGcmCombined(chatId, destination.projectKey),
|
|
86102
|
+
encrypted_display_name: await encryptWithAesGcmCombined(title, destination.projectKey),
|
|
86103
|
+
created_at: timestamp,
|
|
86104
|
+
updated_at: timestamp,
|
|
86105
|
+
position: timestamp
|
|
86106
|
+
};
|
|
86107
|
+
fence();
|
|
86108
|
+
await client2.createProjectItem(destination.id, payload, context);
|
|
86109
|
+
}
|
|
86110
|
+
}
|
|
86111
|
+
if (mode === "move") for (const source of state.chatSidebarProjects.filter((project) => project.id !== destination.id)) {
|
|
86112
|
+
for (const chatId of chatIds.filter((id) => source.items.some((item) => item.type === "chat" && item.targetId === id))) {
|
|
86113
|
+
fence();
|
|
86114
|
+
await client2.deleteProjectItemByTarget(source.id, "chat", chatId, context);
|
|
86115
|
+
}
|
|
86116
|
+
}
|
|
86117
|
+
}
|
|
86118
|
+
async function createTuiChatProject(state, client2, chatIds) {
|
|
86119
|
+
if (!state.signedIn || !chatIds.length) throw new Error("Choose a saved chat first.");
|
|
86120
|
+
const context = client2.getActiveTeamId(), master = Buffer.from(client2.getMasterKeyBytes());
|
|
86121
|
+
const titles = chatIds.slice(0, 8).map((id) => [...state.recentChats, ...state.activityChats, ...state.sidebarLinkedChats].find((chat) => chat.id === id)?.title?.slice(0, 200) || "Untitled chat");
|
|
86122
|
+
const plan = await client2.planProjectAsk({ instruction: "Name a new project from chat titles.", chatTitles: titles });
|
|
86123
|
+
const proposed = plan.proposed_project, name = proposed?.name?.trim();
|
|
86124
|
+
if (!name || name.length > 200) throw new Error("Could not generate a project title.");
|
|
86125
|
+
if (context !== client2.getActiveTeamId() || !master.equals(Buffer.from(client2.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
|
|
86126
|
+
const form = buildProjectForm();
|
|
86127
|
+
form.fields.find((field) => field.name === "name").value = name;
|
|
86128
|
+
const project = await submitProjectForm(client2, form, true);
|
|
86129
|
+
await placeTuiChats(state, client2, chatIds, { projectId: project.id, folderId: null }, "move");
|
|
86130
|
+
return project.id;
|
|
86131
|
+
}
|
|
86132
|
+
|
|
85555
86133
|
// src/tuiLayout.ts
|
|
85556
86134
|
var WORKSPACES = ["chats", "apps", "projects", "workflows", "tasks"];
|
|
85557
86135
|
var DEFAULT_CHAT_BACKGROUND = "#4867cd";
|
|
@@ -85562,7 +86140,13 @@ function sidebarLines(state) {
|
|
|
85562
86140
|
const selected = (label3, index) => `${state.focus === "sidebar" && state.sidebarIndex === index ? ">" : " "} ${label3}`;
|
|
85563
86141
|
switch (state.workspace) {
|
|
85564
86142
|
case "chats":
|
|
85565
|
-
return [
|
|
86143
|
+
return [
|
|
86144
|
+
tuiChatBreadcrumb(state) ?? "Chats",
|
|
86145
|
+
"",
|
|
86146
|
+
...tuiChatSidebarRows(state).map((row, index) => selected(`${row.running ? ["\u25F4", "\u25F7", "\u25F6", "\u25F5"][state.activityFrame % 4] + " " : ""}${row.label}`, index)),
|
|
86147
|
+
"",
|
|
86148
|
+
"/active /chat-add-to-project"
|
|
86149
|
+
];
|
|
85566
86150
|
case "projects":
|
|
85567
86151
|
return ["Projects", "", ...state.projects.map((p, i) => selected(p.name, i)), "", "/project-create new"];
|
|
85568
86152
|
case "tasks":
|
|
@@ -86025,6 +86609,8 @@ function homeHeader(state, width, height) {
|
|
|
86025
86609
|
}
|
|
86026
86610
|
function renderHomeChatCards(state, width, height) {
|
|
86027
86611
|
const chats = homeChatItems(state), result = homeHeader(state, width, height);
|
|
86612
|
+
const activeCount = runningTuiChatGroups(state).length;
|
|
86613
|
+
if (activeCount) result.push(centered2(`${activeCount} ${activeCount === 1 ? "chat" : "chats"} active\u2026 /active`, width), "");
|
|
86028
86614
|
if (!chats.length) {
|
|
86029
86615
|
result.push(centered2(state.homeLoading ? "Loading your recent chats\u2026" : "Start a chat below. Your recent chats will appear here.", width), "");
|
|
86030
86616
|
return result;
|
|
@@ -86048,7 +86634,7 @@ function renderHomeChatCards(state, width, height) {
|
|
|
86048
86634
|
}
|
|
86049
86635
|
|
|
86050
86636
|
// src/tuiAppsWorkspace.ts
|
|
86051
|
-
import { randomBytes as randomBytes11, randomUUID as
|
|
86637
|
+
import { randomBytes as randomBytes11, randomUUID as randomUUID19 } from "crypto";
|
|
86052
86638
|
|
|
86053
86639
|
// ../ui/src/components/apps/appsSkillFormUtils.ts
|
|
86054
86640
|
var object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -86505,7 +87091,7 @@ async function saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey,
|
|
|
86505
87091
|
const value = record5(result);
|
|
86506
87092
|
const type = string2(value.type, string2(value.embed_type));
|
|
86507
87093
|
if (!type && typeof value.embed_id !== "string") return [];
|
|
86508
|
-
const id = typeof value.embed_id === "string" && UUID.test(value.embed_id) ? value.embed_id :
|
|
87094
|
+
const id = typeof value.embed_id === "string" && UUID.test(value.embed_id) ? value.embed_id : randomUUID19();
|
|
86509
87095
|
return [(async () => ({
|
|
86510
87096
|
embed_id: id,
|
|
86511
87097
|
encrypted_type: await encryptWithAesGcmCombined(type || "app_skill_use", key),
|
|
@@ -86550,7 +87136,7 @@ async function executeConfirmedTuiAppsSkill(client2, run) {
|
|
|
86550
87136
|
const owner = record5(await client2.whoAmI());
|
|
86551
87137
|
const ownerId = string2(owner.id, string2(owner.user_id));
|
|
86552
87138
|
if (!ownerId) throw new Error("Account identity unavailable; skill was not run.");
|
|
86553
|
-
const rootId =
|
|
87139
|
+
const rootId = randomUUID19();
|
|
86554
87140
|
const key = randomBytes11(32);
|
|
86555
87141
|
const encryptedKey = await encryptBytesWithAesGcm(key, client2.getMasterKeyBytes());
|
|
86556
87142
|
await saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey, run, { status: "processing" }, "processing");
|
|
@@ -86796,6 +87382,15 @@ function createInitialTuiState() {
|
|
|
86796
87382
|
taskStatusFilter: "",
|
|
86797
87383
|
taskContext: null,
|
|
86798
87384
|
recentChats: [],
|
|
87385
|
+
runningChatIds: [],
|
|
87386
|
+
activityChats: [],
|
|
87387
|
+
sidebarLinkedChats: [],
|
|
87388
|
+
activityFrame: 0,
|
|
87389
|
+
chatSidebarProjects: [],
|
|
87390
|
+
chatSidebarLocation: null,
|
|
87391
|
+
chatSidebarAncestors: false,
|
|
87392
|
+
chatProjectOperation: null,
|
|
87393
|
+
chatProjectBusy: false,
|
|
86799
87394
|
activeChatId: null,
|
|
86800
87395
|
activeChat: null,
|
|
86801
87396
|
headerState: "new",
|
|
@@ -87207,7 +87802,7 @@ function wrap(line, width) {
|
|
|
87207
87802
|
}
|
|
87208
87803
|
|
|
87209
87804
|
// src/tuiWorkspaceController.ts
|
|
87210
|
-
import { randomUUID as
|
|
87805
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
87211
87806
|
function chatDraftKey(state) {
|
|
87212
87807
|
return state.screen === "example" ? `example:${state.activeExample?.chat.id}` : state.activeChatId ?? "new";
|
|
87213
87808
|
}
|
|
@@ -87254,6 +87849,7 @@ async function recent(context) {
|
|
|
87254
87849
|
if (request !== state.routeVersion || homeRequest !== state.homeLoadVersion || !state.signedIn) return;
|
|
87255
87850
|
state.recentChats = chats;
|
|
87256
87851
|
render();
|
|
87852
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
87257
87853
|
}
|
|
87258
87854
|
async function openSavedChat(context, id) {
|
|
87259
87855
|
const { state, client: client2, render } = context;
|
|
@@ -87455,6 +88051,64 @@ async function handleWorkspaceCommand(context, command) {
|
|
|
87455
88051
|
state.selectedProjectId = null;
|
|
87456
88052
|
render();
|
|
87457
88053
|
return true;
|
|
88054
|
+
case "/active":
|
|
88055
|
+
state.sidebarOpen = true;
|
|
88056
|
+
state.focus = "sidebar";
|
|
88057
|
+
state.sidebarIndex = 0;
|
|
88058
|
+
render();
|
|
88059
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
88060
|
+
state.sidebarIndex = 0;
|
|
88061
|
+
render();
|
|
88062
|
+
return true;
|
|
88063
|
+
case "/chat-add-to-project":
|
|
88064
|
+
case "/chat-move-to-project":
|
|
88065
|
+
case "/chat-create-project": {
|
|
88066
|
+
if (state.chatProjectBusy) return true;
|
|
88067
|
+
if (!state.signedIn) throw new Error("Sign in to organize chats.");
|
|
88068
|
+
const selected = state.focus === "sidebar" ? tuiChatSidebarRows(state)[state.sidebarIndex]?.chatId : ["start", "chats"].includes(state.screen) ? homeChatItems(state)[state.selectedIndex]?.id : state.activeChatId;
|
|
88069
|
+
const ids = arg ? arg.split(/\s+/).filter(Boolean) : selected ? [selected] : [];
|
|
88070
|
+
if (!ids.length) throw new Error("Choose a saved chat first.");
|
|
88071
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
88072
|
+
if (name === "/chat-create-project") {
|
|
88073
|
+
state.chatProjectBusy = true;
|
|
88074
|
+
state.status = "Organizing chats\u2026";
|
|
88075
|
+
render();
|
|
88076
|
+
try {
|
|
88077
|
+
const id = await createTuiChatProject(state, client2, ids);
|
|
88078
|
+
state.chatProjectOperation = null;
|
|
88079
|
+
await openProject(context, id);
|
|
88080
|
+
} finally {
|
|
88081
|
+
state.chatProjectBusy = false;
|
|
88082
|
+
}
|
|
88083
|
+
} else {
|
|
88084
|
+
state.chatProjectOperation = { chatIds: ids, mode: name === "/chat-move-to-project" ? "move" : "add" };
|
|
88085
|
+
state.chatSidebarLocation = null;
|
|
88086
|
+
state.sidebarOpen = true;
|
|
88087
|
+
state.focus = "sidebar";
|
|
88088
|
+
state.sidebarIndex = 0;
|
|
88089
|
+
state.status = "Choose a project or subfolder.";
|
|
88090
|
+
}
|
|
88091
|
+
render();
|
|
88092
|
+
return true;
|
|
88093
|
+
}
|
|
88094
|
+
case "/chat-subfolder": {
|
|
88095
|
+
const location = state.chatSidebarLocation;
|
|
88096
|
+
if (!location || !arg.trim()) throw new Error("Open a project folder and enter /chat-subfolder followed by a name.");
|
|
88097
|
+
const project = state.chatSidebarProjects.find((project2) => project2.id === location.projectId);
|
|
88098
|
+
if (!project) throw new Error("Project unavailable.");
|
|
88099
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
88100
|
+
await client2.createProjectFolder(project.id, {
|
|
88101
|
+
folder_id: randomUUID20(),
|
|
88102
|
+
parent_folder_id: location.folderId,
|
|
88103
|
+
encrypted_name: await encryptWithAesGcmCombined(arg.trim().slice(0, 200), project.projectKey),
|
|
88104
|
+
encrypted_sort_key: await encryptWithAesGcmCombined(arg.trim().toLowerCase().slice(0, 200), project.projectKey),
|
|
88105
|
+
created_at: timestamp,
|
|
88106
|
+
updated_at: timestamp,
|
|
88107
|
+
position: timestamp
|
|
88108
|
+
});
|
|
88109
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
88110
|
+
return true;
|
|
88111
|
+
}
|
|
87458
88112
|
case "/sidebar":
|
|
87459
88113
|
state.sidebarOpen = !state.sidebarOpen;
|
|
87460
88114
|
state.focus = state.sidebarOpen ? "sidebar" : state.workspace === "chats" && !["start", "chats"].includes(state.screen) ? "composer" : "content";
|
|
@@ -87695,7 +88349,7 @@ async function saveForm(context) {
|
|
|
87695
88349
|
} else if (form.kind === "workflow-create") {
|
|
87696
88350
|
const description = formValue(form, "description").trim();
|
|
87697
88351
|
if (!description) throw new Error("Describe what this workflow should do.");
|
|
87698
|
-
const request = state.routeVersion, session = await client2.startWorkflowInput({ text: description, inputType: "text", optimisticSave: true, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, idempotencyKey:
|
|
88352
|
+
const request = state.routeVersion, session = await client2.startWorkflowInput({ text: description, inputType: "text", optimisticSave: true, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, idempotencyKey: randomUUID20() });
|
|
87699
88353
|
if (request !== state.routeVersion || state.form !== form) return;
|
|
87700
88354
|
const workflow = session.workflow ?? session.workflows?.[0];
|
|
87701
88355
|
if (workflow) {
|
|
@@ -87721,7 +88375,7 @@ async function saveForm(context) {
|
|
|
87721
88375
|
}
|
|
87722
88376
|
}
|
|
87723
88377
|
async function handleWorkspaceKey(context, chunk, key) {
|
|
87724
|
-
const { state, render } = context;
|
|
88378
|
+
const { state, render, client: client2 } = context;
|
|
87725
88379
|
if (state.form) {
|
|
87726
88380
|
const form = state.form, field = form.fields[form.fieldIndex];
|
|
87727
88381
|
if (!field) {
|
|
@@ -87802,6 +88456,13 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87802
88456
|
return true;
|
|
87803
88457
|
}
|
|
87804
88458
|
if (key.name === "escape") {
|
|
88459
|
+
if (state.chatProjectBusy) return true;
|
|
88460
|
+
if (state.chatProjectOperation) {
|
|
88461
|
+
state.chatProjectOperation = null;
|
|
88462
|
+
state.status = null;
|
|
88463
|
+
render();
|
|
88464
|
+
return true;
|
|
88465
|
+
}
|
|
87805
88466
|
if (state.focus === "sidebar") {
|
|
87806
88467
|
state.sidebarOpen = false;
|
|
87807
88468
|
state.focus = chatHome ? "content" : state.workspace === "chats" ? "composer" : "content";
|
|
@@ -87878,11 +88539,34 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87878
88539
|
if (chunk !== "/") return true;
|
|
87879
88540
|
}
|
|
87880
88541
|
if (state.focus === "sidebar") {
|
|
87881
|
-
const
|
|
88542
|
+
const chatRows = tuiChatSidebarRows(state);
|
|
88543
|
+
const count = state.workspace === "chats" ? chatRows.length : state.workspace === "projects" ? state.projects.length : state.workspace === "tasks" ? state.tasks.length : state.workspace === "apps" ? state.apps.length : state.workflows.length;
|
|
87882
88544
|
if (key.name === "up" || key.name === "down") state.sidebarIndex = Math.max(0, Math.min(count - 1, state.sidebarIndex + (key.name === "up" ? -1 : 1)));
|
|
87883
88545
|
else if (key.name === "return") {
|
|
87884
|
-
if (state.workspace === "chats")
|
|
87885
|
-
|
|
88546
|
+
if (state.workspace === "chats") {
|
|
88547
|
+
if (state.chatProjectBusy) return true;
|
|
88548
|
+
const row = chatRows[state.sidebarIndex];
|
|
88549
|
+
if (row?.kind === "new") await context.command("/new");
|
|
88550
|
+
else if (row?.kind === "chat") await context.command(`/chat ${row.chatId}`);
|
|
88551
|
+
else if (row?.kind === "path") state.chatSidebarAncestors = !state.chatSidebarAncestors;
|
|
88552
|
+
else if (row?.kind === "create" && state.chatProjectOperation) await context.command(`/chat-create-project ${state.chatProjectOperation.chatIds.join(" ")}`);
|
|
88553
|
+
else if (row?.kind === "choose" && state.chatProjectOperation) {
|
|
88554
|
+
const operation = state.chatProjectOperation;
|
|
88555
|
+
state.chatProjectBusy = true;
|
|
88556
|
+
try {
|
|
88557
|
+
await placeTuiChats(state, client2, operation.chatIds, { projectId: row.projectId, folderId: row.folderId ?? null }, operation.mode);
|
|
88558
|
+
state.chatProjectOperation = null;
|
|
88559
|
+
state.status = null;
|
|
88560
|
+
} finally {
|
|
88561
|
+
state.chatProjectBusy = false;
|
|
88562
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
88563
|
+
}
|
|
88564
|
+
} else if (row && ["project", "folder", "up"].includes(row.kind)) {
|
|
88565
|
+
state.chatSidebarLocation = row.kind === "up" && row.folderId === void 0 ? null : { projectId: row.projectId, folderId: row.folderId ?? null };
|
|
88566
|
+
state.chatSidebarAncestors = false;
|
|
88567
|
+
state.sidebarIndex = 0;
|
|
88568
|
+
}
|
|
88569
|
+
} else if (state.workspace === "projects") await context.command(`/project ${state.projects[state.sidebarIndex]?.id}`);
|
|
87886
88570
|
else if (state.workspace === "tasks") await openTask(context, state.tasks[state.sidebarIndex]?.taskId ?? "");
|
|
87887
88571
|
else if (state.workspace === "workflows") await context.command(`/workflow ${state.workflows[state.sidebarIndex]?.id}`);
|
|
87888
88572
|
else if (state.workspace === "apps") await context.command(`/app ${state.apps[state.sidebarIndex]?.id}`);
|
|
@@ -88269,6 +88953,27 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88269
88953
|
let resolveResult = null;
|
|
88270
88954
|
let renderTimer = null;
|
|
88271
88955
|
let closed = false;
|
|
88956
|
+
let stopActivity;
|
|
88957
|
+
let activityTimer;
|
|
88958
|
+
let refreshingActivity = false;
|
|
88959
|
+
const refreshActivity = async () => {
|
|
88960
|
+
if (closed || refreshingActivity || !state.signedIn) return;
|
|
88961
|
+
refreshingActivity = true;
|
|
88962
|
+
try {
|
|
88963
|
+
await refreshTuiChatSidebar(state, client2, render);
|
|
88964
|
+
} finally {
|
|
88965
|
+
refreshingActivity = false;
|
|
88966
|
+
}
|
|
88967
|
+
};
|
|
88968
|
+
const activityPoll = setInterval(() => {
|
|
88969
|
+
void refreshActivity();
|
|
88970
|
+
}, 3e4);
|
|
88971
|
+
const activityAnimation = setInterval(() => {
|
|
88972
|
+
if (!closed && state.runningChatIds.length) {
|
|
88973
|
+
state.activityFrame = (state.activityFrame + 1) % 4;
|
|
88974
|
+
render();
|
|
88975
|
+
}
|
|
88976
|
+
}, 250);
|
|
88272
88977
|
const render = () => {
|
|
88273
88978
|
if (closed) return;
|
|
88274
88979
|
if (state.screen !== "chat") client2.clearInteractiveChatViewer();
|
|
@@ -88284,6 +88989,10 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88284
88989
|
};
|
|
88285
88990
|
const finish = (result) => {
|
|
88286
88991
|
closed = true;
|
|
88992
|
+
stopActivity?.();
|
|
88993
|
+
clearTimeout(activityTimer);
|
|
88994
|
+
clearInterval(activityPoll);
|
|
88995
|
+
clearInterval(activityAnimation);
|
|
88287
88996
|
client2.endInteractiveViewerSession();
|
|
88288
88997
|
if (renderTimer) clearTimeout(renderTimer);
|
|
88289
88998
|
renderTimer = null;
|
|
@@ -88300,6 +89009,19 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88300
89009
|
});
|
|
88301
89010
|
render();
|
|
88302
89011
|
void loadHomeData(state, client2, render);
|
|
89012
|
+
void refreshActivity();
|
|
89013
|
+
if (state.signedIn && typeof client2.observeChatActivity === "function") {
|
|
89014
|
+
void client2.observeChatActivity(() => {
|
|
89015
|
+
clearTimeout(activityTimer);
|
|
89016
|
+
activityTimer = setTimeout(() => {
|
|
89017
|
+
void refreshActivity();
|
|
89018
|
+
}, 300);
|
|
89019
|
+
}).then((stop) => {
|
|
89020
|
+
if (closed) stop();
|
|
89021
|
+
else stopActivity = stop;
|
|
89022
|
+
}).catch(() => {
|
|
89023
|
+
});
|
|
89024
|
+
}
|
|
88303
89025
|
return new Promise((resolve18) => {
|
|
88304
89026
|
resolveResult = resolve18;
|
|
88305
89027
|
});
|
|
@@ -88612,12 +89334,12 @@ async function sendTuiMessage(params) {
|
|
|
88612
89334
|
if (sourceExample) state.messages = sourceExample.messages.map((m) => ({ role: m.role === "user" ? "user" : "assistant", content: m.content, title: m.senderName }));
|
|
88613
89335
|
const messages = state.messages;
|
|
88614
89336
|
const existingChatId = sourceExample ? null : state.activeChatId;
|
|
88615
|
-
const chatId = existingChatId ??
|
|
89337
|
+
const chatId = existingChatId ?? randomUUID21();
|
|
88616
89338
|
const draftKey = sourceExample ? `example:${sourceExample.chat.id}` : existingChatId ?? "new";
|
|
88617
89339
|
state.drafts[draftKey] = "";
|
|
88618
89340
|
state.input = "";
|
|
88619
89341
|
state.inputCursor = null;
|
|
88620
|
-
const anonymousHistory = history ?? messages.filter((m) => m.role !== "system").map((m) => ({ message_id:
|
|
89342
|
+
const anonymousHistory = history ?? messages.filter((m) => m.role !== "system").map((m) => ({ message_id: randomUUID21(), role: m.role, content: m.content, sender_name: m.title ?? (m.role === "user" ? "User" : "Assistant"), created_at: Math.floor(Date.now() / 1e3) }));
|
|
88621
89343
|
state.activeChatId = chatId;
|
|
88622
89344
|
state.headerState = existingChatId ? "ready" : "loading";
|
|
88623
89345
|
state.headerError = null;
|
|
@@ -89042,7 +89764,7 @@ function clamp(value, min, max) {
|
|
|
89042
89764
|
}
|
|
89043
89765
|
|
|
89044
89766
|
// src/codexTaskBridge.ts
|
|
89045
|
-
import { createHash as
|
|
89767
|
+
import { createHash as createHash23 } from "crypto";
|
|
89046
89768
|
import { spawn as spawn3 } from "child_process";
|
|
89047
89769
|
import { existsSync as existsSync15, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
|
|
89048
89770
|
import { isAbsolute as isAbsolute4, join as join18, resolve as resolve10 } from "path";
|
|
@@ -89085,7 +89807,7 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
|
|
|
89085
89807
|
const session = client2.getSession();
|
|
89086
89808
|
const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
|
|
89087
89809
|
const account = JSON.stringify(scope2);
|
|
89088
|
-
const directory = join18(stateRoot, "codex-chat-deletions",
|
|
89810
|
+
const directory = join18(stateRoot, "codex-chat-deletions", createHash23("sha256").update(account).digest("hex"));
|
|
89089
89811
|
if (!existsSync15(directory)) return;
|
|
89090
89812
|
const release2 = await lockfile5.lock(directory, { retries: 0, stale: 24e4 });
|
|
89091
89813
|
try {
|
|
@@ -89119,14 +89841,14 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
|
|
|
89119
89841
|
}
|
|
89120
89842
|
|
|
89121
89843
|
// src/taskCommandDelivery.ts
|
|
89122
|
-
import { createHash as
|
|
89844
|
+
import { createHash as createHash24 } from "crypto";
|
|
89123
89845
|
import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
|
|
89124
89846
|
import { join as join19 } from "path";
|
|
89125
89847
|
import lockfile6 from "proper-lockfile";
|
|
89126
89848
|
async function flushTaskCommands(client2, teamId, root = resolveStateDir(), onNotice) {
|
|
89127
89849
|
const session = client2.getSession();
|
|
89128
89850
|
const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
|
|
89129
|
-
const directory = join19(root, "task-command-delivery",
|
|
89851
|
+
const directory = join19(root, "task-command-delivery", createHash24("sha256").update(JSON.stringify(scope2)).digest("hex"));
|
|
89130
89852
|
if (!existsSync16(directory)) return;
|
|
89131
89853
|
const release2 = await lockfile6.lock(directory, { retries: 0, stale: 24e4 });
|
|
89132
89854
|
try {
|
|
@@ -89236,7 +89958,7 @@ import {
|
|
|
89236
89958
|
writeFileSync as writeFileSync13
|
|
89237
89959
|
} from "fs";
|
|
89238
89960
|
import { spawn as spawn7 } from "child_process";
|
|
89239
|
-
import { createHash as
|
|
89961
|
+
import { createHash as createHash30 } from "crypto";
|
|
89240
89962
|
import { extname as extname4, join as join26, resolve as resolve16, relative as relative11 } from "path";
|
|
89241
89963
|
|
|
89242
89964
|
// src/projectSourceRootPolicy.ts
|
|
@@ -89271,7 +89993,7 @@ function isEqualOrInside(parent, candidate) {
|
|
|
89271
89993
|
}
|
|
89272
89994
|
|
|
89273
89995
|
// src/projectPathPolicy.ts
|
|
89274
|
-
import { createHash as
|
|
89996
|
+
import { createHash as createHash25, randomBytes as randomBytes12 } from "crypto";
|
|
89275
89997
|
import {
|
|
89276
89998
|
chmodSync as chmodSync9,
|
|
89277
89999
|
closeSync as closeSync5,
|
|
@@ -89552,7 +90274,7 @@ function isInside2(root, candidate) {
|
|
|
89552
90274
|
return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
|
|
89553
90275
|
}
|
|
89554
90276
|
function sha2563(value) {
|
|
89555
|
-
return
|
|
90277
|
+
return createHash25("sha256").update(value).digest("hex");
|
|
89556
90278
|
}
|
|
89557
90279
|
function stableJson2(value) {
|
|
89558
90280
|
return JSON.stringify(value, (_key, item) => {
|
|
@@ -89562,7 +90284,7 @@ function stableJson2(value) {
|
|
|
89562
90284
|
}
|
|
89563
90285
|
|
|
89564
90286
|
// src/remoteFileWrites.ts
|
|
89565
|
-
import { createHash as
|
|
90287
|
+
import { createHash as createHash26, randomBytes as randomBytes13 } from "crypto";
|
|
89566
90288
|
import {
|
|
89567
90289
|
chmodSync as chmodSync10,
|
|
89568
90290
|
closeSync as closeSync7,
|
|
@@ -89827,7 +90549,7 @@ var MAX_FILE_BYTES2 = 200 * 1024;
|
|
|
89827
90549
|
var LOCK_ROOT = join22(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
|
|
89828
90550
|
var DEFAULT_JOURNAL_ROOT = join22(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
|
|
89829
90551
|
function sha256ProjectFile(content) {
|
|
89830
|
-
return
|
|
90552
|
+
return createHash26("sha256").update(content).digest("hex");
|
|
89831
90553
|
}
|
|
89832
90554
|
async function executeRemoteFileMutation(options) {
|
|
89833
90555
|
const mutation = validateMutation(options.mutation);
|
|
@@ -90515,13 +91237,13 @@ import { existsSync as existsSync23, realpathSync as realpathSync10, statSync as
|
|
|
90515
91237
|
import { basename as basename7, dirname as dirname13, relative as relative10, resolve as resolve15 } from "path";
|
|
90516
91238
|
|
|
90517
91239
|
// src/remoteCommandRuntime.ts
|
|
90518
|
-
import { createHash as
|
|
91240
|
+
import { createHash as createHash28, randomUUID as randomUUID22 } from "crypto";
|
|
90519
91241
|
import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
|
|
90520
91242
|
import { existsSync as existsSync21, lstatSync as lstatSync9, readdirSync as readdirSync12, realpathSync as realpathSync7, statSync as statSync6 } from "fs";
|
|
90521
91243
|
import { isAbsolute as isAbsolute8, join as join23, relative as relative7, resolve as resolve14, sep } from "path";
|
|
90522
91244
|
|
|
90523
91245
|
// src/remoteCommandAppArmor.ts
|
|
90524
|
-
import { createHash as
|
|
91246
|
+
import { createHash as createHash27 } from "crypto";
|
|
90525
91247
|
import { spawn as spawn5 } from "child_process";
|
|
90526
91248
|
import { lstatSync as lstatSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
|
|
90527
91249
|
import { isAbsolute as isAbsolute7, relative as relative6 } from "path";
|
|
@@ -90738,7 +91460,7 @@ function hasControl2(value) {
|
|
|
90738
91460
|
});
|
|
90739
91461
|
}
|
|
90740
91462
|
function sha2564(value) {
|
|
90741
|
-
return
|
|
91463
|
+
return createHash27("sha256").update(value).digest("hex");
|
|
90742
91464
|
}
|
|
90743
91465
|
|
|
90744
91466
|
// src/remoteCommandRuntime.ts
|
|
@@ -91142,7 +91864,7 @@ var RemoteCommandRuntime = class {
|
|
|
91142
91864
|
};
|
|
91143
91865
|
function createRemoteCommandPreflight(request) {
|
|
91144
91866
|
if (!request || typeof request !== "object") throw new RemoteCommandError("invalid_request", "Command request must be an object");
|
|
91145
|
-
const executionId = request.execution_id ??
|
|
91867
|
+
const executionId = request.execution_id ?? randomUUID22();
|
|
91146
91868
|
if (typeof executionId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(executionId)) throw new RemoteCommandError("invalid_request", "Invalid execution ID");
|
|
91147
91869
|
if (typeof request.project_id !== "string" || !request.project_id || request.project_id.length > 256) throw new RemoteCommandError("invalid_request", "Invalid Project ID");
|
|
91148
91870
|
if (typeof request.source_root !== "string" || !request.source_root) throw new RemoteCommandError("invalid_request", "Invalid Project source root");
|
|
@@ -91579,7 +92301,7 @@ function stableJson4(value) {
|
|
|
91579
92301
|
return JSON.stringify(value);
|
|
91580
92302
|
}
|
|
91581
92303
|
function sha2565(value) {
|
|
91582
|
-
return
|
|
92304
|
+
return createHash28("sha256").update(value).digest("hex");
|
|
91583
92305
|
}
|
|
91584
92306
|
function deepFreeze(value) {
|
|
91585
92307
|
if (value && typeof value === "object") {
|
|
@@ -91857,7 +92579,7 @@ function capabilityError(message) {
|
|
|
91857
92579
|
}
|
|
91858
92580
|
|
|
91859
92581
|
// src/remoteCommandResourceGrants.ts
|
|
91860
|
-
import { createHash as
|
|
92582
|
+
import { createHash as createHash29 } from "crypto";
|
|
91861
92583
|
import { chmodSync as chmodSync12, existsSync as existsSync22, lstatSync as lstatSync10, mkdirSync as mkdirSync18, readFileSync as readFileSync20, realpathSync as realpathSync9, renameSync as renameSync9, statSync as statSync8, writeFileSync as writeFileSync12 } from "fs";
|
|
91862
92584
|
import { isAbsolute as isAbsolute10, join as join25, relative as relative9 } from "path";
|
|
91863
92585
|
var STORE_FILE = "remote-command-resource-grants.json";
|
|
@@ -91952,7 +92674,7 @@ function resolveRemoteCommandResourceGrants(context) {
|
|
|
91952
92674
|
return { writable_targets, network_profiles, credential_profiles };
|
|
91953
92675
|
}
|
|
91954
92676
|
function definitionDigest(kind, definition) {
|
|
91955
|
-
return
|
|
92677
|
+
return createHash29("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
|
|
91956
92678
|
}
|
|
91957
92679
|
function upsert(grant) {
|
|
91958
92680
|
validateIdentity(grant.project_id, grant.source_id, grant.profile_id);
|
|
@@ -93122,7 +93844,7 @@ function readRemoteAccessTextFile(options) {
|
|
|
93122
93844
|
truncated,
|
|
93123
93845
|
sizeBytes: stats.size,
|
|
93124
93846
|
lineCount: countTextLines(content),
|
|
93125
|
-
expected_base: truncated ? null :
|
|
93847
|
+
expected_base: truncated ? null : createHash30("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
|
|
93126
93848
|
};
|
|
93127
93849
|
} catch (error) {
|
|
93128
93850
|
const code = error.code;
|
|
@@ -93172,7 +93894,7 @@ function readRemoteAccessImageChunk(options) {
|
|
|
93172
93894
|
mime_type: mimeType,
|
|
93173
93895
|
size_bytes: stats.size,
|
|
93174
93896
|
offset: options.offset,
|
|
93175
|
-
content_hash:
|
|
93897
|
+
content_hash: createHash30("sha256").update(bytes2).digest("hex")
|
|
93176
93898
|
};
|
|
93177
93899
|
} catch (error) {
|
|
93178
93900
|
if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
|
|
@@ -93220,8 +93942,8 @@ function readRemoteAccessFileChunk(options) {
|
|
|
93220
93942
|
content_base64: bytes2.toString("base64"),
|
|
93221
93943
|
size_bytes: stats.size,
|
|
93222
93944
|
offset: options.offset,
|
|
93223
|
-
file_identity:
|
|
93224
|
-
chunk_hash:
|
|
93945
|
+
file_identity: createHash30("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
|
|
93946
|
+
chunk_hash: createHash30("sha256").update(bytes2).digest("hex")
|
|
93225
93947
|
};
|
|
93226
93948
|
} catch (error) {
|
|
93227
93949
|
if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
|
|
@@ -94783,7 +95505,7 @@ function writeWorkRecoveryAtomically(path2, document) {
|
|
|
94783
95505
|
|
|
94784
95506
|
// src/revolutBusinessCertificate.ts
|
|
94785
95507
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
94786
|
-
import { createSign, randomUUID as
|
|
95508
|
+
import { createSign, randomUUID as randomUUID23 } from "crypto";
|
|
94787
95509
|
import { chmodSync as chmodSync14, existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync23 } from "fs";
|
|
94788
95510
|
import { homedir as homedir8 } from "os";
|
|
94789
95511
|
import { join as join28, resolve as resolve17 } from "path";
|
|
@@ -94857,7 +95579,7 @@ function generateRevolutBusinessClientAssertion(options) {
|
|
|
94857
95579
|
aud: REVOLUT_BUSINESS_AUDIENCE,
|
|
94858
95580
|
exp: now + 3600,
|
|
94859
95581
|
iat: now,
|
|
94860
|
-
jti:
|
|
95582
|
+
jti: randomUUID23()
|
|
94861
95583
|
});
|
|
94862
95584
|
const unsigned = `${header2}.${payload}`;
|
|
94863
95585
|
const signer = createSign("RSA-SHA256");
|
|
@@ -96230,6 +96952,7 @@ async function tryResolveWorkflow(client2, target, flags) {
|
|
|
96230
96952
|
return singleExactNameMatch(workflows, target, (workflow) => workflow.title);
|
|
96231
96953
|
}
|
|
96232
96954
|
async function requiredResolvedWorkflowId(client2, target, flags, _action) {
|
|
96955
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(target)) return target;
|
|
96233
96956
|
const workflow = await tryResolveWorkflow(client2, target, flags);
|
|
96234
96957
|
if (!workflow) throw new Error(`Workflow '${target}' not found.`);
|
|
96235
96958
|
return workflow.id;
|
|
@@ -96329,7 +97052,7 @@ async function resolveAddToProjectStorageChoice(client2, project, flags, objectT
|
|
|
96329
97052
|
async function createEncryptedProjectItem(client2, project, input) {
|
|
96330
97053
|
const timestamp = nowSeconds3();
|
|
96331
97054
|
return client2.createProjectItem(project.projectId, {
|
|
96332
|
-
project_item_id:
|
|
97055
|
+
project_item_id: randomUUID24(),
|
|
96333
97056
|
folder_id: input.folderId ?? null,
|
|
96334
97057
|
item_type: input.itemType,
|
|
96335
97058
|
target_id: input.targetId,
|
|
@@ -96528,7 +97251,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
|
|
|
96528
97251
|
const encryptedSources = proof.length ? await encryptPlanAssumptionSources(plan, masterKey, proof) : void 0;
|
|
96529
97252
|
if (action === "create" || action === "add") {
|
|
96530
97253
|
const assumption = await client2.createPlanAssumption(plan.planId, {
|
|
96531
|
-
assumption_id: typeof flags.id === "string" ? flags.id :
|
|
97254
|
+
assumption_id: typeof flags.id === "string" ? flags.id : randomUUID24(),
|
|
96532
97255
|
encrypted_text: await encryptPlanAssumptionSources(plan, masterKey, requiredStringFlag(flags.text ?? rest.slice(2).join(" "), "--text <assumption>")),
|
|
96533
97256
|
status: typeof flags.status === "string" ? flags.status : void 0,
|
|
96534
97257
|
linked_sub_chat_id: typeof flags["sub-chat"] === "string" ? flags["sub-chat"] : void 0,
|
|
@@ -96570,7 +97293,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
|
|
|
96570
97293
|
if (action === "diff") throw new Error("Revision diffs are reviewed in the authenticated web review page; use submit-for-review to print its URL.");
|
|
96571
97294
|
if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
|
|
96572
97295
|
const snapshot2 = JSON.stringify(planToJson(plan));
|
|
96573
|
-
const revision = await client2.submitPlanRevision(plan.planId, { fingerprint:
|
|
97296
|
+
const revision = await client2.submitPlanRevision(plan.planId, { fingerprint: createHash31("sha256").update(snapshot2).digest("hex"), encrypted_snapshot: await encryptPlanAssumptionSources(plan, masterKey, snapshot2), created_at: nowSeconds3() });
|
|
96574
97297
|
const reviewUrl = `${deriveAppUrl(client2.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
|
|
96575
97298
|
if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
|
|
96576
97299
|
else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
|
|
@@ -97464,7 +98187,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97464
98187
|
const writeMode = await resolveProjectWriteMode(flags, `Choose how OpenMates may change files in ${JSON.stringify(name)}:`);
|
|
97465
98188
|
const projectKey = randomBytes14(32);
|
|
97466
98189
|
const wrapping = await client2.projectWrappingKey(context);
|
|
97467
|
-
const projectId =
|
|
98190
|
+
const projectId = randomUUID24();
|
|
97468
98191
|
const timestamp = nowSeconds3();
|
|
97469
98192
|
const defaultFocus = buildProjectDefaultFocus(name);
|
|
97470
98193
|
const slugMetadata = await buildEncryptedObjectSlugMetadata({
|
|
@@ -97490,7 +98213,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97490
98213
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
|
|
97491
98214
|
key_wrappers: wrapping.teamId ? [{
|
|
97492
98215
|
key_type: "team",
|
|
97493
|
-
hashed_team_id:
|
|
98216
|
+
hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
|
|
97494
98217
|
team_key_epoch: 1,
|
|
97495
98218
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
97496
98219
|
wrapper_version: 1,
|
|
@@ -97730,7 +98453,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97730
98453
|
const proposal = isShortWorkspaceAsk(instruction) ? { name: instruction, description: "", icon: "folder", color: "default" } : extractRecord(await client2.planProjectAsk({ instruction }), "proposed_project");
|
|
97731
98454
|
const projectKey = randomBytes14(32);
|
|
97732
98455
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
97733
|
-
const projectId =
|
|
98456
|
+
const projectId = randomUUID24();
|
|
97734
98457
|
const name = requiredString(proposal, "name", instruction);
|
|
97735
98458
|
const slugMetadata = await buildEncryptedObjectSlugMetadata({
|
|
97736
98459
|
value: typeof flags.slug === "string" ? flags.slug : name,
|
|
@@ -97853,8 +98576,8 @@ async function handleProjectFiles(client2, masterKey, rest, flags, context) {
|
|
|
97853
98576
|
...flags["include-ignored"] === true ? {
|
|
97854
98577
|
approvedIgnoredRead: {
|
|
97855
98578
|
path: String(argumentsValue.path),
|
|
97856
|
-
chatId: `cli-direct-${
|
|
97857
|
-
operationId: `cli-direct-${
|
|
98579
|
+
chatId: `cli-direct-${randomUUID24()}`,
|
|
98580
|
+
operationId: `cli-direct-${randomUUID24()}`
|
|
97858
98581
|
}
|
|
97859
98582
|
} : {}
|
|
97860
98583
|
});
|
|
@@ -98642,7 +99365,7 @@ async function handleRemoteAccess(client2, subcommand, rest, flags) {
|
|
|
98642
99365
|
const projects = await loadProjects(client2, masterKey, hostingFlags, hostingContext);
|
|
98643
99366
|
const bindings = await resolveRemoteAccessBindings(client2, masterKey, projects, candidateRoots, hostingFlags, hostingContext);
|
|
98644
99367
|
if (bindings.length === 0) throw new Error("No Project folders were approved for remote access.");
|
|
98645
|
-
const sourceSessionId =
|
|
99368
|
+
const sourceSessionId = randomUUID24();
|
|
98646
99369
|
const controller = new AbortController();
|
|
98647
99370
|
const stop = () => controller.abort();
|
|
98648
99371
|
process.once("SIGINT", stop);
|
|
@@ -98796,7 +99519,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
|
|
|
98796
99519
|
updated_at: nowSeconds3()
|
|
98797
99520
|
}, context);
|
|
98798
99521
|
}
|
|
98799
|
-
resolved.push({ rootPath, project, sourceId:
|
|
99522
|
+
resolved.push({ rootPath, project, sourceId: randomUUID24() });
|
|
98800
99523
|
}
|
|
98801
99524
|
}
|
|
98802
99525
|
const bindings = [];
|
|
@@ -98840,7 +99563,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
|
|
|
98840
99563
|
}
|
|
98841
99564
|
async function createEncryptedRemoteAccessProject(client2, name, rootPath, writeMode, context) {
|
|
98842
99565
|
const projectKey = randomBytes14(32);
|
|
98843
|
-
const projectId =
|
|
99566
|
+
const projectId = randomUUID24();
|
|
98844
99567
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
98845
99568
|
const wrapping = await client2.projectWrappingKey(context);
|
|
98846
99569
|
const importedInstructions = readRootProjectInstructions(rootPath);
|
|
@@ -98866,7 +99589,7 @@ async function createEncryptedRemoteAccessProject(client2, name, rootPath, write
|
|
|
98866
99589
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
|
|
98867
99590
|
key_wrappers: wrapping.teamId ? [{
|
|
98868
99591
|
key_type: "team",
|
|
98869
|
-
hashed_team_id:
|
|
99592
|
+
hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
|
|
98870
99593
|
team_key_epoch: 1,
|
|
98871
99594
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
98872
99595
|
wrapper_version: 1,
|
|
@@ -98924,7 +99647,7 @@ async function ensureProjectDefaultFocus(client2, project, context, rootPath) {
|
|
|
98924
99647
|
function buildProjectDefaultFocus(projectName, instructions, source = "generated") {
|
|
98925
99648
|
const name = projectName.trim() || "Untitled project";
|
|
98926
99649
|
return {
|
|
98927
|
-
focus_id:
|
|
99650
|
+
focus_id: randomUUID24(),
|
|
98928
99651
|
name: `Work on ${name}`,
|
|
98929
99652
|
instructions: instructions?.trim() || `Help with work in ${name}. Follow the user's instructions and the Project's connected source guidance.`,
|
|
98930
99653
|
source
|
|
@@ -100257,7 +100980,7 @@ function normalizeApiKeyLoadedChat(payload) {
|
|
|
100257
100980
|
const messages = rawMessages.map((message) => {
|
|
100258
100981
|
const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
|
|
100259
100982
|
const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
|
|
100260
|
-
const hashedMessageId = messageId ?
|
|
100983
|
+
const hashedMessageId = messageId ? createHash31("sha256").update(messageId).digest("hex") : null;
|
|
100261
100984
|
const matchedEmbedIds = hashedMessageId ? rawEmbeds.filter((embed) => embed.hashed_message_id === hashedMessageId && !embed.parent_embed_id).map((embed) => String(embed.embed_id ?? embed.id ?? "")).filter(Boolean) : [];
|
|
100262
100985
|
return {
|
|
100263
100986
|
id: messageId,
|
|
@@ -100805,6 +101528,52 @@ async function handleWorkflows(client2, subcommand, rest, flags, redactor) {
|
|
|
100805
101528
|
}
|
|
100806
101529
|
return;
|
|
100807
101530
|
}
|
|
101531
|
+
if (subcommand === "save-as-reusable") {
|
|
101532
|
+
const workflowId = rest[0] ? await requiredResolvedWorkflowId(client2, rest[0], flags, "save-as-reusable") : void 0;
|
|
101533
|
+
if (!workflowId) throw new Error("Missing workflow ID. Example: openmates workflows save-as-reusable <id>");
|
|
101534
|
+
const idempotencyKey = typeof flags["idempotency-key"] === "string" ? flags["idempotency-key"] : "";
|
|
101535
|
+
if (!idempotencyKey) throw new Error("Missing --idempotency-key for workflow save-as-reusable.");
|
|
101536
|
+
const workflow = await client2.saveWorkflowAsReusable(workflowId, { idempotencyKey });
|
|
101537
|
+
if (flags.json === true) printJson2(workflow);
|
|
101538
|
+
else printWorkflowDetail(workflow);
|
|
101539
|
+
return;
|
|
101540
|
+
}
|
|
101541
|
+
const runRouting = () => ({
|
|
101542
|
+
...typeof flags["source-chat"] === "string" ? { sourceChatId: flags["source-chat"] } : {},
|
|
101543
|
+
...typeof flags["message-destination-overrides"] === "string" ? { messageDestinationOverrides: parseJsonFlag(flags["message-destination-overrides"], "--message-destination-overrides") } : {},
|
|
101544
|
+
...typeof flags["return-outputs"] === "string" ? { returnOutputs: parseJsonFlag(flags["return-outputs"], "--return-outputs") } : {}
|
|
101545
|
+
});
|
|
101546
|
+
if (subcommand === "run-once") {
|
|
101547
|
+
const titleFlag = typeof flags.title === "string" ? flags.title.trim() : "";
|
|
101548
|
+
const graphFlag = typeof flags.graph === "string" ? flags.graph : "";
|
|
101549
|
+
const fileFlag = typeof flags.file === "string" ? flags.file : "";
|
|
101550
|
+
if (Boolean(graphFlag) === Boolean(fileFlag)) throw new Error("Specify exactly one of --graph JSON or --file definition.workflow.yml for run-once.");
|
|
101551
|
+
let graph;
|
|
101552
|
+
let title = titleFlag;
|
|
101553
|
+
if (fileFlag) {
|
|
101554
|
+
assertWorkflowFilePath(fileFlag);
|
|
101555
|
+
if (statSync11(fileFlag).size > WORKFLOW_FILE_MAX_BYTES) throw new Error("Workflow file exceeds the size limit.");
|
|
101556
|
+
const yaml = parseYamlDocument(readFileSync24(fileFlag, "utf8"), { uniqueKeys: true, strict: true });
|
|
101557
|
+
if (yaml.errors.length > 0) throw new Error(`Invalid workflow YAML: ${yaml.errors[0].message}`);
|
|
101558
|
+
const document = validateWorkflowFile(yaml.toJS({ maxAliasCount: 20 }));
|
|
101559
|
+
graph = document.workflow.graph;
|
|
101560
|
+
title ||= document.workflow.title;
|
|
101561
|
+
} else graph = parseJsonFlag(graphFlag, "--graph");
|
|
101562
|
+
if (!title) throw new Error("Missing --title for workflow run-once.");
|
|
101563
|
+
const idempotencyKey = typeof flags["idempotency-key"] === "string" ? flags["idempotency-key"] : "";
|
|
101564
|
+
if (!idempotencyKey) throw new Error("Missing --idempotency-key for workflow run-once.");
|
|
101565
|
+
const sourceChatId = flags["source-chat"];
|
|
101566
|
+
if (typeof sourceChatId !== "string" || !sourceChatId) throw new Error("Missing --source-chat for workflow run-once.");
|
|
101567
|
+
const input = typeof flags.input === "string" ? parseJsonFlag(flags.input, "--input") : {};
|
|
101568
|
+
const result = await client2.runWorkflowOnce({ title, graph, idempotencyKey, input, ...runRouting(), sourceChatId });
|
|
101569
|
+
if (flags.wait === true) result.run = await waitForWorkflowRun(client2, result.workflow.id, result.run);
|
|
101570
|
+
if (flags.json === true) printJson2(result);
|
|
101571
|
+
else {
|
|
101572
|
+
printWorkflowDetail(result.workflow);
|
|
101573
|
+
printWorkflowRun(result.run);
|
|
101574
|
+
}
|
|
101575
|
+
return;
|
|
101576
|
+
}
|
|
100808
101577
|
if (subcommand === "run") {
|
|
100809
101578
|
const workflowId = rest[0] ? await requiredResolvedWorkflowId(client2, rest[0], flags, "run") : void 0;
|
|
100810
101579
|
if (!workflowId) throw new Error("Missing workflow ID. Example: openmates workflows run <id>");
|
|
@@ -100812,7 +101581,7 @@ async function handleWorkflows(client2, subcommand, rest, flags, redactor) {
|
|
|
100812
101581
|
if (!idempotencyKey) throw new Error("Missing --idempotency-key. Reuse this stable key when retrying the same workflow run.");
|
|
100813
101582
|
const mode = flags.mode === "test" ? "test" : "manual";
|
|
100814
101583
|
const input = typeof flags.input === "string" ? parseJsonFlag(flags.input, "--input") : {};
|
|
100815
|
-
let run = await client2.runWorkflow(workflowId, { idempotencyKey, mode, input });
|
|
101584
|
+
let run = await client2.runWorkflow(workflowId, { idempotencyKey, mode, input, ...runRouting() });
|
|
100816
101585
|
if (flags.wait === true) {
|
|
100817
101586
|
run = await waitForWorkflowRun(client2, workflowId, run);
|
|
100818
101587
|
}
|
|
@@ -102454,7 +103223,7 @@ async function runAccountImport(client2, parserFormat, file, flags) {
|
|
|
102454
103223
|
if (flags.yes !== true) {
|
|
102455
103224
|
await confirmOrExit(`Import ${selectedCount} chat(s) into new encrypted OpenMates chats? [y/N] `);
|
|
102456
103225
|
}
|
|
102457
|
-
const importId = typeof preview.import_id === "string" ? preview.import_id :
|
|
103226
|
+
const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID24();
|
|
102458
103227
|
const selectedChats = parsed.chats.slice(0, selectedCount);
|
|
102459
103228
|
const selectedFingerprints = selectedChats.map((chat) => chat.source_fingerprint);
|
|
102460
103229
|
const confirmation = await client2.confirmAccountImport(importId, selectedFingerprints);
|
|
@@ -102948,8 +103717,8 @@ async function handleFinance(client2, subcommand, _rest, flags) {
|
|
|
102948
103717
|
refresh_token_envelope: connectedAccount.refreshTokenBundle
|
|
102949
103718
|
}
|
|
102950
103719
|
] : [],
|
|
102951
|
-
chatId: `cli-finance-${
|
|
102952
|
-
messageId: `cli-finance-message-${
|
|
103720
|
+
chatId: `cli-finance-${randomUUID24()}`,
|
|
103721
|
+
messageId: `cli-finance-message-${randomUUID24()}`,
|
|
102953
103722
|
apiKey: resolveApiKey(flags) ?? void 0,
|
|
102954
103723
|
promptInjectionProtection: false
|
|
102955
103724
|
});
|
|
@@ -107174,7 +107943,9 @@ function printWorkflowsHelp() {
|
|
|
107174
107943
|
openmates workflows show <workflow-id> [--json]
|
|
107175
107944
|
openmates workflows enable <workflow-id> [--json]
|
|
107176
107945
|
openmates workflows disable <workflow-id> [--json]
|
|
107177
|
-
openmates workflows run <workflow-id> --idempotency-key <stable-key> [--mode manual|test] [--input '<json>'] [--wait] [--json]
|
|
107946
|
+
openmates workflows run <workflow-id> --idempotency-key <stable-key> [--mode manual|test] [--input '<json>'] [--source-chat <id>] [--message-destination-overrides '<json>'] [--return-outputs '<json>'] [--wait] [--json]
|
|
107947
|
+
openmates workflows run-once (--title <title> --graph '<json>' | --file definition.workflow.yml) --source-chat <id> --idempotency-key <stable-key> [--input '<json>'] [--message-destination-overrides '<json>'] [--return-outputs '<json>'] [--wait] [--json]
|
|
107948
|
+
openmates workflows save-as-reusable <workflow-id> --idempotency-key <stable-key> [--json]
|
|
107178
107949
|
openmates workflows runs <workflow-id> [--json]
|
|
107179
107950
|
openmates workflows run-show <workflow-id> <run-id> [--json]
|
|
107180
107951
|
openmates workflows run-cancel <workflow-id> <run-id> [--json]
|
|
@@ -107189,6 +107960,9 @@ Workflows run on the OpenMates server, not in this terminal process. The CLI
|
|
|
107189
107960
|
uses your paired session and shows the same workflow/run records as web, SDKs,
|
|
107190
107961
|
and Apple clients.
|
|
107191
107962
|
Export writes a saved workflow's portable authoring graph to .workflow.yml.
|
|
107963
|
+
Run-once keeps its definition with the source chat. Save-as-reusable creates a new disabled workflow.
|
|
107964
|
+
--message-destination-overrides maps Send step IDs to owned chat IDs.
|
|
107965
|
+
--return-outputs maps result names to {"ref":"$nodes.<step>.output.<field>","type":"string"}.
|
|
107192
107966
|
Run history and account bindings are excluded. Import creates a disabled workflow;
|
|
107193
107967
|
review and complete its bindings before enabling it.
|
|
107194
107968
|
Import creates a Personal Workflow. In an active Team context, pass --personal
|