openmates 0.27.0-alpha.6 → 0.27.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WQOBJUWS.js → chunk-CLOVZ7RX.js} +607 -80
- package/dist/{cli-Bq-RdTvp.d.ts → cli-5j9Lsz2s.d.ts} +19 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -2
- 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,
|
|
@@ -17357,7 +17411,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
17357
17411
|
this.requireSession();
|
|
17358
17412
|
const response = await this.http.post(
|
|
17359
17413
|
"/v1/projects/ask/plan",
|
|
17360
|
-
{ instruction: input.instruction },
|
|
17414
|
+
{ instruction: input.instruction, ...input.chatTitles !== void 0 ? { chat_titles: input.chatTitles } : {} },
|
|
17361
17415
|
this.getCliRequestHeaders()
|
|
17362
17416
|
);
|
|
17363
17417
|
if (!response.ok) throw new Error(`Project ask planning failed with HTTP ${response.status}`);
|
|
@@ -17366,7 +17420,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
17366
17420
|
async listProjectItems(projectId, options = {}) {
|
|
17367
17421
|
this.requireSession();
|
|
17368
17422
|
const response = await this.http.get(
|
|
17369
|
-
this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items`, options),
|
|
17423
|
+
this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items${options.chatOnly ? "?chat_only=true" : ""}`, options),
|
|
17370
17424
|
this.getCliRequestHeaders()
|
|
17371
17425
|
);
|
|
17372
17426
|
if (!response.ok) throw this.projectRequestError("item list", response);
|
|
@@ -17503,10 +17557,10 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
17503
17557
|
}
|
|
17504
17558
|
return { ws: opened.ws, ownerId: opened.ownerId };
|
|
17505
17559
|
}
|
|
17506
|
-
async createProjectItem(projectId, input) {
|
|
17560
|
+
async createProjectItem(projectId, input, options = {}) {
|
|
17507
17561
|
this.requireSession();
|
|
17508
17562
|
const response = await this.http.post(
|
|
17509
|
-
`/v1/projects/${encodeURIComponent(projectId)}/items`,
|
|
17563
|
+
this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items`, options),
|
|
17510
17564
|
input,
|
|
17511
17565
|
this.getCliRequestHeaders()
|
|
17512
17566
|
);
|
|
@@ -17515,6 +17569,20 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
17515
17569
|
}
|
|
17516
17570
|
return response.data.item;
|
|
17517
17571
|
}
|
|
17572
|
+
async moveProjectItemToFolder(projectId, itemId, folderId, options = {}) {
|
|
17573
|
+
this.requireSession();
|
|
17574
|
+
const response = await this.http.patch(
|
|
17575
|
+
this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/items/${encodeURIComponent(itemId)}`, options),
|
|
17576
|
+
{ folder_id: folderId, updated_at: Math.floor(Date.now() / 1e3) },
|
|
17577
|
+
this.getCliRequestHeaders()
|
|
17578
|
+
);
|
|
17579
|
+
if (!response.ok) throw this.projectRequestError("folder move", response);
|
|
17580
|
+
}
|
|
17581
|
+
async createProjectFolder(projectId, input, options = {}) {
|
|
17582
|
+
this.requireSession();
|
|
17583
|
+
const response = await this.http.post(this.appendTeamQuery(`/v1/projects/${encodeURIComponent(projectId)}/folders`, options), input, this.getCliRequestHeaders());
|
|
17584
|
+
if (!response.ok) throw this.projectRequestError("folder create", response);
|
|
17585
|
+
}
|
|
17518
17586
|
async deleteProjectItemByTarget(projectId, itemType, targetId, options = {}) {
|
|
17519
17587
|
this.requireSession();
|
|
17520
17588
|
const params = new URLSearchParams({ item_type: itemType, target_id: targetId });
|
|
@@ -19150,7 +19218,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
19150
19218
|
const session = this.requireSession();
|
|
19151
19219
|
const masterKey = base64ToBytes(session.masterKeyExportedB64);
|
|
19152
19220
|
const cache = await this.ensureSynced();
|
|
19153
|
-
const { createHash:
|
|
19221
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
19154
19222
|
const embed = cache.embeds.find(
|
|
19155
19223
|
(e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
|
|
19156
19224
|
);
|
|
@@ -19158,7 +19226,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
19158
19226
|
throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
|
|
19159
19227
|
}
|
|
19160
19228
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
19161
|
-
const hashedEmbedId =
|
|
19229
|
+
const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
|
|
19162
19230
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
19163
19231
|
cache,
|
|
19164
19232
|
masterKey,
|
|
@@ -19266,8 +19334,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
19266
19334
|
if (!embed) {
|
|
19267
19335
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
19268
19336
|
}
|
|
19269
|
-
const { createHash:
|
|
19270
|
-
const hashedEmbedId =
|
|
19337
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
19338
|
+
const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
|
|
19271
19339
|
const embedKey = await this.resolveEmbedKey(
|
|
19272
19340
|
cache,
|
|
19273
19341
|
masterKey,
|
|
@@ -19383,8 +19451,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
19383
19451
|
if (!embed) {
|
|
19384
19452
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
19385
19453
|
}
|
|
19386
|
-
const { createHash:
|
|
19387
|
-
const hashedEmbedId =
|
|
19454
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
19455
|
+
const hashedEmbedId = createHash32("sha256").update(embedId).digest("hex");
|
|
19388
19456
|
const embedKey = await this.resolveEmbedKey(
|
|
19389
19457
|
cache,
|
|
19390
19458
|
masterKey,
|
|
@@ -19916,9 +19984,9 @@ Required: ${schema.required.join(", ")}`
|
|
|
19916
19984
|
chats.splice(0, chats.length, ...reconciledChats);
|
|
19917
19985
|
if (totalChatCount === 0) totalChatCount = chats.length;
|
|
19918
19986
|
if (reconciliation.deleted_chat_ids?.length) {
|
|
19919
|
-
const { createHash:
|
|
19987
|
+
const { createHash: createHash32 } = await import("crypto");
|
|
19920
19988
|
const deletedHashes = new Set(
|
|
19921
|
-
reconciliation.deleted_chat_ids.map((id) =>
|
|
19989
|
+
reconciliation.deleted_chat_ids.map((id) => createHash32("sha256").update(id).digest("hex"))
|
|
19922
19990
|
);
|
|
19923
19991
|
const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
|
|
19924
19992
|
const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
|
|
@@ -31158,7 +31226,7 @@ async function flushPendingTaskActivities(client2, teamId, stateDir2 = resolveSt
|
|
|
31158
31226
|
import { existsSync as existsSync26, readFileSync as readFileSync24, realpathSync as realpathSync12, readdirSync as readdirSync14, statSync as statSync11, writeFileSync as writeFileSync16 } from "fs";
|
|
31159
31227
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
31160
31228
|
import { basename as basename9, dirname as dirname16, join as join29 } from "path";
|
|
31161
|
-
import { createHash as
|
|
31229
|
+
import { createHash as createHash31, randomBytes as randomBytes14, randomUUID as randomUUID24 } from "crypto";
|
|
31162
31230
|
import { arch as arch2, platform as platform3 } from "os";
|
|
31163
31231
|
import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
|
|
31164
31232
|
|
|
@@ -62135,6 +62203,64 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
62135
62203
|
}
|
|
62136
62204
|
},
|
|
62137
62205
|
chats: {
|
|
62206
|
+
projects: {
|
|
62207
|
+
move: {
|
|
62208
|
+
text: "Move to project"
|
|
62209
|
+
},
|
|
62210
|
+
move_here: {
|
|
62211
|
+
text: "Move here"
|
|
62212
|
+
},
|
|
62213
|
+
add: {
|
|
62214
|
+
text: "Add to project"
|
|
62215
|
+
},
|
|
62216
|
+
create: {
|
|
62217
|
+
text: "Create new project"
|
|
62218
|
+
},
|
|
62219
|
+
add_here: {
|
|
62220
|
+
text: "Add here"
|
|
62221
|
+
},
|
|
62222
|
+
new_folder: {
|
|
62223
|
+
text: "New subfolder"
|
|
62224
|
+
},
|
|
62225
|
+
folder_name: {
|
|
62226
|
+
text: "Folder name"
|
|
62227
|
+
},
|
|
62228
|
+
path: {
|
|
62229
|
+
text: "Project path"
|
|
62230
|
+
},
|
|
62231
|
+
up: {
|
|
62232
|
+
text: "Go up one level"
|
|
62233
|
+
},
|
|
62234
|
+
ancestors: {
|
|
62235
|
+
text: "Show full path"
|
|
62236
|
+
},
|
|
62237
|
+
open: {
|
|
62238
|
+
text: "Open project"
|
|
62239
|
+
},
|
|
62240
|
+
organizing: {
|
|
62241
|
+
text: "Organizing chats\u2026"
|
|
62242
|
+
},
|
|
62243
|
+
error: {
|
|
62244
|
+
text: "Could not organize chats. Please try again."
|
|
62245
|
+
}
|
|
62246
|
+
},
|
|
62247
|
+
activity: {
|
|
62248
|
+
heading: {
|
|
62249
|
+
text: "Active chats"
|
|
62250
|
+
},
|
|
62251
|
+
count: {
|
|
62252
|
+
text: "{count} chats active\u2026"
|
|
62253
|
+
},
|
|
62254
|
+
count_single: {
|
|
62255
|
+
text: "1 chat active\u2026"
|
|
62256
|
+
},
|
|
62257
|
+
subchats: {
|
|
62258
|
+
text: "{count} subchats running"
|
|
62259
|
+
},
|
|
62260
|
+
subchats_single: {
|
|
62261
|
+
text: "1 subchat running"
|
|
62262
|
+
}
|
|
62263
|
+
},
|
|
62138
62264
|
chat: {
|
|
62139
62265
|
welcome: {
|
|
62140
62266
|
back_to_recent: {
|
|
@@ -83994,7 +84120,7 @@ function formatMetricDelta(delta, suffix) {
|
|
|
83994
84120
|
// src/tui.ts
|
|
83995
84121
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
83996
84122
|
import { stdin as nodeStdin, stdout as nodeStdout } from "process";
|
|
83997
|
-
import { randomUUID as
|
|
84123
|
+
import { randomUUID as randomUUID21 } from "crypto";
|
|
83998
84124
|
|
|
83999
84125
|
// src/tuiExampleContinuation.ts
|
|
84000
84126
|
function buildExampleContinuationHistory(conversation) {
|
|
@@ -84687,13 +84813,13 @@ function storedPath(metadata, displayName) {
|
|
|
84687
84813
|
return null;
|
|
84688
84814
|
}
|
|
84689
84815
|
}
|
|
84690
|
-
async function loadTuiProject(client2, id) {
|
|
84816
|
+
async function loadTuiProject(client2, id, chatOnly = false) {
|
|
84691
84817
|
const context = contextFor(client2);
|
|
84692
84818
|
const detail = await client2.getProject(id, context);
|
|
84693
84819
|
const project = await projectFromRecord(client2, detail.project, context);
|
|
84694
84820
|
const [itemsResponse, sourceRecords] = await Promise.all([
|
|
84695
|
-
client2.listProjectItems(id, context),
|
|
84696
|
-
client2.listProjectSources(id, context)
|
|
84821
|
+
client2.listProjectItems(id, { ...context, chatOnly }),
|
|
84822
|
+
chatOnly ? Promise.resolve([]) : client2.listProjectSources(id, context)
|
|
84697
84823
|
]);
|
|
84698
84824
|
const folderIds = itemsResponse.folders.map((folder) => String(folder.folder_id ?? "")).filter(Boolean);
|
|
84699
84825
|
const folderByHash = new Map(folderIds.map((folderId) => [createHash21("sha256").update(folderId).digest("hex"), folderId]));
|
|
@@ -84927,7 +85053,7 @@ function buildProjectForm() {
|
|
|
84927
85053
|
]
|
|
84928
85054
|
};
|
|
84929
85055
|
}
|
|
84930
|
-
async function submitProjectForm(client2, form) {
|
|
85056
|
+
async function submitProjectForm(client2, form, chatOrganizationOnly = false) {
|
|
84931
85057
|
const name = formValue(form, "name").trim();
|
|
84932
85058
|
if (!name) throw new Error("Project name is required.");
|
|
84933
85059
|
const writeMode = formValue(form, "write_mode");
|
|
@@ -84951,7 +85077,7 @@ async function submitProjectForm(client2, form) {
|
|
|
84951
85077
|
created_at: timestamp,
|
|
84952
85078
|
updated_at: timestamp,
|
|
84953
85079
|
last_opened_at: timestamp,
|
|
84954
|
-
write_mode: writeMode,
|
|
85080
|
+
write_mode: chatOrganizationOnly ? null : writeMode,
|
|
84955
85081
|
default_focus_id: focus.focus_id,
|
|
84956
85082
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: focus }), key),
|
|
84957
85083
|
key_wrappers: wrapping.teamId ? [{
|
|
@@ -84963,7 +85089,7 @@ async function submitProjectForm(client2, form) {
|
|
|
84963
85089
|
created_at: timestamp
|
|
84964
85090
|
}] : []
|
|
84965
85091
|
};
|
|
84966
|
-
const result = await client2.createProject(payload, context);
|
|
85092
|
+
const result = await client2.createProject({ ...payload, ...chatOrganizationOnly ? { chat_organization_only: true } : {} }, context);
|
|
84967
85093
|
return projectFromRecord(client2, { ...payload, ...result.project ?? {} }, context);
|
|
84968
85094
|
}
|
|
84969
85095
|
|
|
@@ -85538,6 +85664,10 @@ var TUI_ACTIONS = [
|
|
|
85538
85664
|
{ label: "Move selected task", command: "/task-status" },
|
|
85539
85665
|
{ label: "Add task activity", command: "/task-activity" },
|
|
85540
85666
|
{ label: "Create Project", command: "/project-create" },
|
|
85667
|
+
{ label: "Add chat to project", command: "/chat-add-to-project" },
|
|
85668
|
+
{ label: "Move chat to project", command: "/chat-move-to-project" },
|
|
85669
|
+
{ label: "Create project from chats", command: "/chat-create-project" },
|
|
85670
|
+
{ label: "Show active chats", command: "/active" },
|
|
85541
85671
|
{ label: "New chat in Project", command: "/project-chat" },
|
|
85542
85672
|
{ label: "Edit workflow step", command: "/workflow-edit" },
|
|
85543
85673
|
{ label: "Enable or disable workflow", command: "/workflow-toggle" },
|
|
@@ -85552,6 +85682,259 @@ function paletteActions(query) {
|
|
|
85552
85682
|
return TUI_ACTIONS.filter((item) => `${item.label} ${item.command}`.toLowerCase().includes(needle));
|
|
85553
85683
|
}
|
|
85554
85684
|
|
|
85685
|
+
// src/tuiChatSidebar.ts
|
|
85686
|
+
import { createHash as createHash22, randomUUID as randomUUID18 } from "crypto";
|
|
85687
|
+
|
|
85688
|
+
// ../ui/src/utils/chatActivity.ts
|
|
85689
|
+
function processingAncestorIds(chats, runningIds) {
|
|
85690
|
+
const byId = new Map(chats.map((chat) => [chat.chat_id, chat]));
|
|
85691
|
+
const result = /* @__PURE__ */ new Set();
|
|
85692
|
+
for (const id of runningIds) {
|
|
85693
|
+
const ancestry = [];
|
|
85694
|
+
const visited = /* @__PURE__ */ new Set();
|
|
85695
|
+
let chat = byId.get(id);
|
|
85696
|
+
while (chat && !visited.has(chat.chat_id)) {
|
|
85697
|
+
visited.add(chat.chat_id);
|
|
85698
|
+
ancestry.push(chat);
|
|
85699
|
+
chat = chat.parent_id ? byId.get(chat.parent_id) : void 0;
|
|
85700
|
+
}
|
|
85701
|
+
if (ancestry.some((chat2) => chat2.is_hidden || chat2.is_hidden_candidate)) continue;
|
|
85702
|
+
for (const ancestor of ancestry) result.add(ancestor.chat_id);
|
|
85703
|
+
}
|
|
85704
|
+
return result;
|
|
85705
|
+
}
|
|
85706
|
+
function aggregateRunningChats(chats, runningIds) {
|
|
85707
|
+
const byId = new Map(chats.map((chat) => [chat.chat_id, chat]));
|
|
85708
|
+
const groups = /* @__PURE__ */ new Map();
|
|
85709
|
+
for (const id of runningIds) {
|
|
85710
|
+
let root = byId.get(id);
|
|
85711
|
+
if (!root) continue;
|
|
85712
|
+
const visited = /* @__PURE__ */ new Set([id]);
|
|
85713
|
+
let hidden = root.is_hidden || root.is_hidden_candidate;
|
|
85714
|
+
while (root.parent_id && byId.has(root.parent_id) && !visited.has(root.parent_id)) {
|
|
85715
|
+
visited.add(root.parent_id);
|
|
85716
|
+
root = byId.get(root.parent_id);
|
|
85717
|
+
hidden ||= root.is_hidden || root.is_hidden_candidate;
|
|
85718
|
+
}
|
|
85719
|
+
if (hidden) continue;
|
|
85720
|
+
let group = groups.get(root.chat_id);
|
|
85721
|
+
if (!group) {
|
|
85722
|
+
group = { chat: root, activeSubChatCount: 0 };
|
|
85723
|
+
groups.set(root.chat_id, group);
|
|
85724
|
+
}
|
|
85725
|
+
if (id !== root.chat_id) group.activeSubChatCount += 1;
|
|
85726
|
+
}
|
|
85727
|
+
return [...groups.values()];
|
|
85728
|
+
}
|
|
85729
|
+
|
|
85730
|
+
// ../ui/src/utils/chatProjectNavigation.ts
|
|
85731
|
+
function projectBreadcrumbs(project, folderId) {
|
|
85732
|
+
const ancestors = [];
|
|
85733
|
+
const visited = /* @__PURE__ */ new Set();
|
|
85734
|
+
let folder = project.folders.find((folder2) => folder2.id === folderId);
|
|
85735
|
+
while (folder && !visited.has(folder.id)) {
|
|
85736
|
+
visited.add(folder.id);
|
|
85737
|
+
ancestors.unshift({ id: folder.id, name: folder.name });
|
|
85738
|
+
folder = project.folders.find((candidate) => candidate.hash === folder.parentHash);
|
|
85739
|
+
}
|
|
85740
|
+
return [{ id: null, name: project.name }, ...ancestors];
|
|
85741
|
+
}
|
|
85742
|
+
function projectFolderChatIds(project, folderId, recursive = false) {
|
|
85743
|
+
const folder = project.folders.find((folder2) => folder2.id === folderId);
|
|
85744
|
+
if (folderId !== null && !folder) return /* @__PURE__ */ new Set();
|
|
85745
|
+
const hashes = /* @__PURE__ */ new Set([folder?.hash ?? null]);
|
|
85746
|
+
if (recursive) {
|
|
85747
|
+
let previous = -1;
|
|
85748
|
+
while (previous !== hashes.size) {
|
|
85749
|
+
previous = hashes.size;
|
|
85750
|
+
for (const candidate of project.folders) if (hashes.has(candidate.parentHash)) hashes.add(candidate.hash);
|
|
85751
|
+
}
|
|
85752
|
+
}
|
|
85753
|
+
return new Set(project.chats.filter((chat) => hashes.has(chat.folderHash)).map((chat) => chat.chatId));
|
|
85754
|
+
}
|
|
85755
|
+
function locationHasRunningChats(project, folderId, runningIds) {
|
|
85756
|
+
return [...projectFolderChatIds(project, folderId, true)].some((id) => runningIds.has(id));
|
|
85757
|
+
}
|
|
85758
|
+
|
|
85759
|
+
// src/tuiChatSidebar.ts
|
|
85760
|
+
function rowKey(row) {
|
|
85761
|
+
return [row.kind, row.chatId ?? "", row.projectId ?? "", row.folderId ?? ""].join(":");
|
|
85762
|
+
}
|
|
85763
|
+
function updateTuiChatSidebar(state, update) {
|
|
85764
|
+
const selected = tuiChatSidebarRows(state)[state.sidebarIndex];
|
|
85765
|
+
update();
|
|
85766
|
+
if (state.workspace !== "chats" || state.focus !== "sidebar") return;
|
|
85767
|
+
const rows2 = tuiChatSidebarRows(state);
|
|
85768
|
+
const index = selected ? rows2.findIndex((row) => rowKey(row) === rowKey(selected)) : -1;
|
|
85769
|
+
state.sidebarIndex = index >= 0 ? index : Math.max(0, Math.min(state.sidebarIndex, rows2.length - 1));
|
|
85770
|
+
}
|
|
85771
|
+
function runningTuiChatGroups(state) {
|
|
85772
|
+
const chats = new Map([...state.recentChats, ...state.activityChats].map((chat) => [chat.id, chat]));
|
|
85773
|
+
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) }));
|
|
85774
|
+
}
|
|
85775
|
+
function tuiSidebarProjects(state) {
|
|
85776
|
+
return state.chatSidebarProjects.map((project) => ({
|
|
85777
|
+
id: project.id,
|
|
85778
|
+
name: project.name,
|
|
85779
|
+
folders: project.folders.map((folder) => ({
|
|
85780
|
+
id: folder.id,
|
|
85781
|
+
name: folder.name,
|
|
85782
|
+
hash: createHash22("sha256").update(folder.id).digest("hex"),
|
|
85783
|
+
parentHash: folder.parentHash ?? null
|
|
85784
|
+
})),
|
|
85785
|
+
chats: project.items.filter((item) => item.type === "chat").map((item) => ({
|
|
85786
|
+
id: item.id,
|
|
85787
|
+
chatId: item.targetId,
|
|
85788
|
+
folderHash: item.folderId ? createHash22("sha256").update(item.folderId).digest("hex") : null
|
|
85789
|
+
}))
|
|
85790
|
+
}));
|
|
85791
|
+
}
|
|
85792
|
+
function tuiChatBreadcrumb(state, width = 23) {
|
|
85793
|
+
const location = state.chatSidebarLocation;
|
|
85794
|
+
const project = tuiSidebarProjects(state).find((project2) => project2.id === location?.projectId);
|
|
85795
|
+
if (!project || !location) return null;
|
|
85796
|
+
const crumbs = projectBreadcrumbs(project, location.folderId);
|
|
85797
|
+
if (crumbs.length === 1) return truncateCells(project.name, width);
|
|
85798
|
+
const separator = crumbs.length > 2 ? " \u203A \u2026 \u203A " : " \u203A ";
|
|
85799
|
+
const current = crumbs.at(-1).name, budget = Math.max(2, width - cells(separator));
|
|
85800
|
+
if (cells(project.name) + cells(current) <= budget) return `${project.name}${separator}${current}`;
|
|
85801
|
+
const leafWidth = Math.min(cells(current), Math.max(1, budget - Math.min(cells(project.name), Math.ceil(budget / 2))));
|
|
85802
|
+
return `${truncateCells(project.name, budget - leafWidth)}${separator}${truncateCells(current, leafWidth)}`;
|
|
85803
|
+
}
|
|
85804
|
+
function tuiChatSidebarRows(state) {
|
|
85805
|
+
const groups = runningTuiChatGroups(state);
|
|
85806
|
+
const records = [...state.recentChats, ...state.activityChats].map((chat) => ({ chat_id: chat.id, parent_id: chat.parentId, is_hidden_candidate: chat.isHiddenCandidate }));
|
|
85807
|
+
const running = processingAncestorIds(records, new Set(state.runningChatIds));
|
|
85808
|
+
const rows2 = groups.map((group) => ({
|
|
85809
|
+
kind: "chat",
|
|
85810
|
+
chatId: group.chat.id,
|
|
85811
|
+
running: true,
|
|
85812
|
+
label: `${group.chat.title || "Untitled chat"}${group.activeSubChatCount ? ` (${group.activeSubChatCount} ${group.activeSubChatCount === 1 ? "subchat" : "subchats"})` : ""}`
|
|
85813
|
+
}));
|
|
85814
|
+
rows2.push({ kind: "new", label: "+ New chat" });
|
|
85815
|
+
if (state.chatProjectOperation) rows2.push({ kind: "create", label: "+ Create new project" });
|
|
85816
|
+
const projects = tuiSidebarProjects(state), location = state.chatSidebarLocation;
|
|
85817
|
+
const project = projects.find((project2) => project2.id === location?.projectId);
|
|
85818
|
+
let contents = null;
|
|
85819
|
+
if (project && location) {
|
|
85820
|
+
const crumbs = projectBreadcrumbs(project, location.folderId);
|
|
85821
|
+
rows2.push({ kind: "up", label: "\u2039 Up one level", projectId: project.id, folderId: crumbs.length > 1 ? crumbs[crumbs.length - 2].id : void 0 });
|
|
85822
|
+
if (crumbs.length > 2) rows2.push({ kind: "path", label: "\u2026 Show full path" });
|
|
85823
|
+
if (state.chatSidebarAncestors) for (const crumb of crumbs) rows2.push({ kind: "folder", label: crumb.name, projectId: project.id, folderId: crumb.id });
|
|
85824
|
+
if (state.chatProjectOperation) rows2.push({ kind: "choose", label: state.chatProjectOperation.mode === "move" ? "Move here" : "Add here", projectId: project.id, folderId: location.folderId });
|
|
85825
|
+
const hash2 = project.folders.find((folder) => folder.id === location.folderId)?.hash ?? null;
|
|
85826
|
+
rows2.push(...project.folders.filter((folder) => folder.parentHash === hash2).map((folder) => ({
|
|
85827
|
+
kind: "folder",
|
|
85828
|
+
label: folder.name,
|
|
85829
|
+
projectId: project.id,
|
|
85830
|
+
folderId: folder.id,
|
|
85831
|
+
running: locationHasRunningChats(project, folder.id, running)
|
|
85832
|
+
})));
|
|
85833
|
+
contents = projectFolderChatIds(project, location.folderId);
|
|
85834
|
+
} else rows2.push(...projects.map((project2) => ({
|
|
85835
|
+
kind: "project",
|
|
85836
|
+
label: project2.name,
|
|
85837
|
+
projectId: project2.id,
|
|
85838
|
+
running: locationHasRunningChats(project2, null, running)
|
|
85839
|
+
})));
|
|
85840
|
+
const organized = new Set(projects.flatMap((project2) => project2.chats.map((chat) => chat.chatId)));
|
|
85841
|
+
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 }]));
|
|
85842
|
+
if (contents && location) for (const item of state.chatSidebarProjects.find((project2) => project2.id === location.projectId)?.items ?? []) {
|
|
85843
|
+
const verified = candidates.get(item.targetId);
|
|
85844
|
+
if (item.type === "chat" && contents.has(item.targetId) && verified && !verified.title)
|
|
85845
|
+
candidates.set(item.targetId, { ...verified, title: item.name });
|
|
85846
|
+
}
|
|
85847
|
+
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" })));
|
|
85848
|
+
return rows2;
|
|
85849
|
+
}
|
|
85850
|
+
async function refreshTuiChatSidebar(state, client2, render, includeProjects = false) {
|
|
85851
|
+
if (!state.signedIn) return;
|
|
85852
|
+
const team = typeof client2.getActiveTeamId === "function" ? client2.getActiveTeamId() : null;
|
|
85853
|
+
const master = typeof client2.getMasterKeyBytes === "function" ? Buffer.from(client2.getMasterKeyBytes()) : null;
|
|
85854
|
+
const current = () => state.signedIn && (typeof client2.getActiveTeamId !== "function" || client2.getActiveTeamId() === team) && (!master || master.equals(Buffer.from(client2.getMasterKeyBytes())));
|
|
85855
|
+
if (typeof client2.getChatActivity === "function") {
|
|
85856
|
+
try {
|
|
85857
|
+
const activity = await client2.getChatActivity();
|
|
85858
|
+
if (current()) {
|
|
85859
|
+
updateTuiChatSidebar(state, () => {
|
|
85860
|
+
state.runningChatIds = activity.ids;
|
|
85861
|
+
state.activityChats = activity.chats;
|
|
85862
|
+
});
|
|
85863
|
+
render();
|
|
85864
|
+
}
|
|
85865
|
+
} catch {
|
|
85866
|
+
if (current()) {
|
|
85867
|
+
state.status = "Running chat status unavailable.";
|
|
85868
|
+
render();
|
|
85869
|
+
}
|
|
85870
|
+
}
|
|
85871
|
+
}
|
|
85872
|
+
if (includeProjects && typeof client2.listProjects === "function") {
|
|
85873
|
+
const summaries = await loadTuiProjects(client2), projects = [];
|
|
85874
|
+
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))));
|
|
85875
|
+
const linkedIds = [...new Set(projects.flatMap((project) => project.items.filter((item) => item.type === "chat").map((item) => item.targetId)))];
|
|
85876
|
+
const linkedChats = typeof client2.getSidebarChats === "function" ? await client2.getSidebarChats(linkedIds) : [];
|
|
85877
|
+
if (current()) {
|
|
85878
|
+
updateTuiChatSidebar(state, () => {
|
|
85879
|
+
state.chatSidebarProjects = projects;
|
|
85880
|
+
state.sidebarLinkedChats = linkedChats;
|
|
85881
|
+
});
|
|
85882
|
+
render();
|
|
85883
|
+
}
|
|
85884
|
+
}
|
|
85885
|
+
}
|
|
85886
|
+
async function placeTuiChats(state, client2, chatIds, location, mode) {
|
|
85887
|
+
const context = { teamId: client2.getActiveTeamId() }, master = Buffer.from(client2.getMasterKeyBytes());
|
|
85888
|
+
const fence = () => {
|
|
85889
|
+
if (!state.signedIn || context.teamId !== client2.getActiveTeamId() || !master.equals(Buffer.from(client2.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
|
|
85890
|
+
};
|
|
85891
|
+
const destination = await loadTuiProject(client2, location.projectId, true);
|
|
85892
|
+
if (location.folderId && !destination.folders.some((folder) => folder.id === location.folderId)) throw new Error("Folder unavailable");
|
|
85893
|
+
for (const chatId of chatIds) {
|
|
85894
|
+
fence();
|
|
85895
|
+
const existing = destination.items.find((item) => item.type === "chat" && item.targetId === chatId);
|
|
85896
|
+
if (existing) {
|
|
85897
|
+
if ((existing.folderId ?? null) !== location.folderId) await client2.moveProjectItemToFolder(destination.id, existing.id, location.folderId, context);
|
|
85898
|
+
} else {
|
|
85899
|
+
const title = [...state.recentChats, ...state.activityChats].find((chat) => chat.id === chatId)?.title || "Untitled chat";
|
|
85900
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
85901
|
+
const payload = {
|
|
85902
|
+
project_item_id: randomUUID18(),
|
|
85903
|
+
folder_id: location.folderId,
|
|
85904
|
+
item_type: "chat",
|
|
85905
|
+
target_id: chatId,
|
|
85906
|
+
target_id_encrypted: await encryptWithAesGcmCombined(chatId, destination.projectKey),
|
|
85907
|
+
encrypted_display_name: await encryptWithAesGcmCombined(title, destination.projectKey),
|
|
85908
|
+
created_at: timestamp,
|
|
85909
|
+
updated_at: timestamp,
|
|
85910
|
+
position: timestamp
|
|
85911
|
+
};
|
|
85912
|
+
fence();
|
|
85913
|
+
await client2.createProjectItem(destination.id, payload, context);
|
|
85914
|
+
}
|
|
85915
|
+
}
|
|
85916
|
+
if (mode === "move") for (const source of state.chatSidebarProjects.filter((project) => project.id !== destination.id)) {
|
|
85917
|
+
for (const chatId of chatIds.filter((id) => source.items.some((item) => item.type === "chat" && item.targetId === id))) {
|
|
85918
|
+
fence();
|
|
85919
|
+
await client2.deleteProjectItemByTarget(source.id, "chat", chatId, context);
|
|
85920
|
+
}
|
|
85921
|
+
}
|
|
85922
|
+
}
|
|
85923
|
+
async function createTuiChatProject(state, client2, chatIds) {
|
|
85924
|
+
if (!state.signedIn || !chatIds.length) throw new Error("Choose a saved chat first.");
|
|
85925
|
+
const context = client2.getActiveTeamId(), master = Buffer.from(client2.getMasterKeyBytes());
|
|
85926
|
+
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");
|
|
85927
|
+
const plan = await client2.planProjectAsk({ instruction: "Name a new project from chat titles.", chatTitles: titles });
|
|
85928
|
+
const proposed = plan.proposed_project, name = proposed?.name?.trim();
|
|
85929
|
+
if (!name || name.length > 200) throw new Error("Could not generate a project title.");
|
|
85930
|
+
if (context !== client2.getActiveTeamId() || !master.equals(Buffer.from(client2.getMasterKeyBytes()))) throw new Error("Chat workspace changed");
|
|
85931
|
+
const form = buildProjectForm();
|
|
85932
|
+
form.fields.find((field) => field.name === "name").value = name;
|
|
85933
|
+
const project = await submitProjectForm(client2, form, true);
|
|
85934
|
+
await placeTuiChats(state, client2, chatIds, { projectId: project.id, folderId: null }, "move");
|
|
85935
|
+
return project.id;
|
|
85936
|
+
}
|
|
85937
|
+
|
|
85555
85938
|
// src/tuiLayout.ts
|
|
85556
85939
|
var WORKSPACES = ["chats", "apps", "projects", "workflows", "tasks"];
|
|
85557
85940
|
var DEFAULT_CHAT_BACKGROUND = "#4867cd";
|
|
@@ -85562,7 +85945,13 @@ function sidebarLines(state) {
|
|
|
85562
85945
|
const selected = (label3, index) => `${state.focus === "sidebar" && state.sidebarIndex === index ? ">" : " "} ${label3}`;
|
|
85563
85946
|
switch (state.workspace) {
|
|
85564
85947
|
case "chats":
|
|
85565
|
-
return [
|
|
85948
|
+
return [
|
|
85949
|
+
tuiChatBreadcrumb(state) ?? "Chats",
|
|
85950
|
+
"",
|
|
85951
|
+
...tuiChatSidebarRows(state).map((row, index) => selected(`${row.running ? ["\u25F4", "\u25F7", "\u25F6", "\u25F5"][state.activityFrame % 4] + " " : ""}${row.label}`, index)),
|
|
85952
|
+
"",
|
|
85953
|
+
"/active /chat-add-to-project"
|
|
85954
|
+
];
|
|
85566
85955
|
case "projects":
|
|
85567
85956
|
return ["Projects", "", ...state.projects.map((p, i) => selected(p.name, i)), "", "/project-create new"];
|
|
85568
85957
|
case "tasks":
|
|
@@ -86025,6 +86414,8 @@ function homeHeader(state, width, height) {
|
|
|
86025
86414
|
}
|
|
86026
86415
|
function renderHomeChatCards(state, width, height) {
|
|
86027
86416
|
const chats = homeChatItems(state), result = homeHeader(state, width, height);
|
|
86417
|
+
const activeCount = runningTuiChatGroups(state).length;
|
|
86418
|
+
if (activeCount) result.push(centered2(`${activeCount} ${activeCount === 1 ? "chat" : "chats"} active\u2026 /active`, width), "");
|
|
86028
86419
|
if (!chats.length) {
|
|
86029
86420
|
result.push(centered2(state.homeLoading ? "Loading your recent chats\u2026" : "Start a chat below. Your recent chats will appear here.", width), "");
|
|
86030
86421
|
return result;
|
|
@@ -86048,7 +86439,7 @@ function renderHomeChatCards(state, width, height) {
|
|
|
86048
86439
|
}
|
|
86049
86440
|
|
|
86050
86441
|
// src/tuiAppsWorkspace.ts
|
|
86051
|
-
import { randomBytes as randomBytes11, randomUUID as
|
|
86442
|
+
import { randomBytes as randomBytes11, randomUUID as randomUUID19 } from "crypto";
|
|
86052
86443
|
|
|
86053
86444
|
// ../ui/src/components/apps/appsSkillFormUtils.ts
|
|
86054
86445
|
var object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -86505,7 +86896,7 @@ async function saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey,
|
|
|
86505
86896
|
const value = record5(result);
|
|
86506
86897
|
const type = string2(value.type, string2(value.embed_type));
|
|
86507
86898
|
if (!type && typeof value.embed_id !== "string") return [];
|
|
86508
|
-
const id = typeof value.embed_id === "string" && UUID.test(value.embed_id) ? value.embed_id :
|
|
86899
|
+
const id = typeof value.embed_id === "string" && UUID.test(value.embed_id) ? value.embed_id : randomUUID19();
|
|
86509
86900
|
return [(async () => ({
|
|
86510
86901
|
embed_id: id,
|
|
86511
86902
|
encrypted_type: await encryptWithAesGcmCombined(type || "app_skill_use", key),
|
|
@@ -86550,7 +86941,7 @@ async function executeConfirmedTuiAppsSkill(client2, run) {
|
|
|
86550
86941
|
const owner = record5(await client2.whoAmI());
|
|
86551
86942
|
const ownerId = string2(owner.id, string2(owner.user_id));
|
|
86552
86943
|
if (!ownerId) throw new Error("Account identity unavailable; skill was not run.");
|
|
86553
|
-
const rootId =
|
|
86944
|
+
const rootId = randomUUID19();
|
|
86554
86945
|
const key = randomBytes11(32);
|
|
86555
86946
|
const encryptedKey = await encryptBytesWithAesGcm(key, client2.getMasterKeyBytes());
|
|
86556
86947
|
await saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey, run, { status: "processing" }, "processing");
|
|
@@ -86796,6 +87187,15 @@ function createInitialTuiState() {
|
|
|
86796
87187
|
taskStatusFilter: "",
|
|
86797
87188
|
taskContext: null,
|
|
86798
87189
|
recentChats: [],
|
|
87190
|
+
runningChatIds: [],
|
|
87191
|
+
activityChats: [],
|
|
87192
|
+
sidebarLinkedChats: [],
|
|
87193
|
+
activityFrame: 0,
|
|
87194
|
+
chatSidebarProjects: [],
|
|
87195
|
+
chatSidebarLocation: null,
|
|
87196
|
+
chatSidebarAncestors: false,
|
|
87197
|
+
chatProjectOperation: null,
|
|
87198
|
+
chatProjectBusy: false,
|
|
86799
87199
|
activeChatId: null,
|
|
86800
87200
|
activeChat: null,
|
|
86801
87201
|
headerState: "new",
|
|
@@ -87207,7 +87607,7 @@ function wrap(line, width) {
|
|
|
87207
87607
|
}
|
|
87208
87608
|
|
|
87209
87609
|
// src/tuiWorkspaceController.ts
|
|
87210
|
-
import { randomUUID as
|
|
87610
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
87211
87611
|
function chatDraftKey(state) {
|
|
87212
87612
|
return state.screen === "example" ? `example:${state.activeExample?.chat.id}` : state.activeChatId ?? "new";
|
|
87213
87613
|
}
|
|
@@ -87254,6 +87654,7 @@ async function recent(context) {
|
|
|
87254
87654
|
if (request !== state.routeVersion || homeRequest !== state.homeLoadVersion || !state.signedIn) return;
|
|
87255
87655
|
state.recentChats = chats;
|
|
87256
87656
|
render();
|
|
87657
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
87257
87658
|
}
|
|
87258
87659
|
async function openSavedChat(context, id) {
|
|
87259
87660
|
const { state, client: client2, render } = context;
|
|
@@ -87455,6 +87856,64 @@ async function handleWorkspaceCommand(context, command) {
|
|
|
87455
87856
|
state.selectedProjectId = null;
|
|
87456
87857
|
render();
|
|
87457
87858
|
return true;
|
|
87859
|
+
case "/active":
|
|
87860
|
+
state.sidebarOpen = true;
|
|
87861
|
+
state.focus = "sidebar";
|
|
87862
|
+
state.sidebarIndex = 0;
|
|
87863
|
+
render();
|
|
87864
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
87865
|
+
state.sidebarIndex = 0;
|
|
87866
|
+
render();
|
|
87867
|
+
return true;
|
|
87868
|
+
case "/chat-add-to-project":
|
|
87869
|
+
case "/chat-move-to-project":
|
|
87870
|
+
case "/chat-create-project": {
|
|
87871
|
+
if (state.chatProjectBusy) return true;
|
|
87872
|
+
if (!state.signedIn) throw new Error("Sign in to organize chats.");
|
|
87873
|
+
const selected = state.focus === "sidebar" ? tuiChatSidebarRows(state)[state.sidebarIndex]?.chatId : ["start", "chats"].includes(state.screen) ? homeChatItems(state)[state.selectedIndex]?.id : state.activeChatId;
|
|
87874
|
+
const ids = arg ? arg.split(/\s+/).filter(Boolean) : selected ? [selected] : [];
|
|
87875
|
+
if (!ids.length) throw new Error("Choose a saved chat first.");
|
|
87876
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
87877
|
+
if (name === "/chat-create-project") {
|
|
87878
|
+
state.chatProjectBusy = true;
|
|
87879
|
+
state.status = "Organizing chats\u2026";
|
|
87880
|
+
render();
|
|
87881
|
+
try {
|
|
87882
|
+
const id = await createTuiChatProject(state, client2, ids);
|
|
87883
|
+
state.chatProjectOperation = null;
|
|
87884
|
+
await openProject(context, id);
|
|
87885
|
+
} finally {
|
|
87886
|
+
state.chatProjectBusy = false;
|
|
87887
|
+
}
|
|
87888
|
+
} else {
|
|
87889
|
+
state.chatProjectOperation = { chatIds: ids, mode: name === "/chat-move-to-project" ? "move" : "add" };
|
|
87890
|
+
state.chatSidebarLocation = null;
|
|
87891
|
+
state.sidebarOpen = true;
|
|
87892
|
+
state.focus = "sidebar";
|
|
87893
|
+
state.sidebarIndex = 0;
|
|
87894
|
+
state.status = "Choose a project or subfolder.";
|
|
87895
|
+
}
|
|
87896
|
+
render();
|
|
87897
|
+
return true;
|
|
87898
|
+
}
|
|
87899
|
+
case "/chat-subfolder": {
|
|
87900
|
+
const location = state.chatSidebarLocation;
|
|
87901
|
+
if (!location || !arg.trim()) throw new Error("Open a project folder and enter /chat-subfolder followed by a name.");
|
|
87902
|
+
const project = state.chatSidebarProjects.find((project2) => project2.id === location.projectId);
|
|
87903
|
+
if (!project) throw new Error("Project unavailable.");
|
|
87904
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
87905
|
+
await client2.createProjectFolder(project.id, {
|
|
87906
|
+
folder_id: randomUUID20(),
|
|
87907
|
+
parent_folder_id: location.folderId,
|
|
87908
|
+
encrypted_name: await encryptWithAesGcmCombined(arg.trim().slice(0, 200), project.projectKey),
|
|
87909
|
+
encrypted_sort_key: await encryptWithAesGcmCombined(arg.trim().toLowerCase().slice(0, 200), project.projectKey),
|
|
87910
|
+
created_at: timestamp,
|
|
87911
|
+
updated_at: timestamp,
|
|
87912
|
+
position: timestamp
|
|
87913
|
+
});
|
|
87914
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
87915
|
+
return true;
|
|
87916
|
+
}
|
|
87458
87917
|
case "/sidebar":
|
|
87459
87918
|
state.sidebarOpen = !state.sidebarOpen;
|
|
87460
87919
|
state.focus = state.sidebarOpen ? "sidebar" : state.workspace === "chats" && !["start", "chats"].includes(state.screen) ? "composer" : "content";
|
|
@@ -87695,7 +88154,7 @@ async function saveForm(context) {
|
|
|
87695
88154
|
} else if (form.kind === "workflow-create") {
|
|
87696
88155
|
const description = formValue(form, "description").trim();
|
|
87697
88156
|
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:
|
|
88157
|
+
const request = state.routeVersion, session = await client2.startWorkflowInput({ text: description, inputType: "text", optimisticSave: true, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, idempotencyKey: randomUUID20() });
|
|
87699
88158
|
if (request !== state.routeVersion || state.form !== form) return;
|
|
87700
88159
|
const workflow = session.workflow ?? session.workflows?.[0];
|
|
87701
88160
|
if (workflow) {
|
|
@@ -87721,7 +88180,7 @@ async function saveForm(context) {
|
|
|
87721
88180
|
}
|
|
87722
88181
|
}
|
|
87723
88182
|
async function handleWorkspaceKey(context, chunk, key) {
|
|
87724
|
-
const { state, render } = context;
|
|
88183
|
+
const { state, render, client: client2 } = context;
|
|
87725
88184
|
if (state.form) {
|
|
87726
88185
|
const form = state.form, field = form.fields[form.fieldIndex];
|
|
87727
88186
|
if (!field) {
|
|
@@ -87802,6 +88261,13 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87802
88261
|
return true;
|
|
87803
88262
|
}
|
|
87804
88263
|
if (key.name === "escape") {
|
|
88264
|
+
if (state.chatProjectBusy) return true;
|
|
88265
|
+
if (state.chatProjectOperation) {
|
|
88266
|
+
state.chatProjectOperation = null;
|
|
88267
|
+
state.status = null;
|
|
88268
|
+
render();
|
|
88269
|
+
return true;
|
|
88270
|
+
}
|
|
87805
88271
|
if (state.focus === "sidebar") {
|
|
87806
88272
|
state.sidebarOpen = false;
|
|
87807
88273
|
state.focus = chatHome ? "content" : state.workspace === "chats" ? "composer" : "content";
|
|
@@ -87878,11 +88344,34 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87878
88344
|
if (chunk !== "/") return true;
|
|
87879
88345
|
}
|
|
87880
88346
|
if (state.focus === "sidebar") {
|
|
87881
|
-
const
|
|
88347
|
+
const chatRows = tuiChatSidebarRows(state);
|
|
88348
|
+
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
88349
|
if (key.name === "up" || key.name === "down") state.sidebarIndex = Math.max(0, Math.min(count - 1, state.sidebarIndex + (key.name === "up" ? -1 : 1)));
|
|
87883
88350
|
else if (key.name === "return") {
|
|
87884
|
-
if (state.workspace === "chats")
|
|
87885
|
-
|
|
88351
|
+
if (state.workspace === "chats") {
|
|
88352
|
+
if (state.chatProjectBusy) return true;
|
|
88353
|
+
const row = chatRows[state.sidebarIndex];
|
|
88354
|
+
if (row?.kind === "new") await context.command("/new");
|
|
88355
|
+
else if (row?.kind === "chat") await context.command(`/chat ${row.chatId}`);
|
|
88356
|
+
else if (row?.kind === "path") state.chatSidebarAncestors = !state.chatSidebarAncestors;
|
|
88357
|
+
else if (row?.kind === "create" && state.chatProjectOperation) await context.command(`/chat-create-project ${state.chatProjectOperation.chatIds.join(" ")}`);
|
|
88358
|
+
else if (row?.kind === "choose" && state.chatProjectOperation) {
|
|
88359
|
+
const operation = state.chatProjectOperation;
|
|
88360
|
+
state.chatProjectBusy = true;
|
|
88361
|
+
try {
|
|
88362
|
+
await placeTuiChats(state, client2, operation.chatIds, { projectId: row.projectId, folderId: row.folderId ?? null }, operation.mode);
|
|
88363
|
+
state.chatProjectOperation = null;
|
|
88364
|
+
state.status = null;
|
|
88365
|
+
} finally {
|
|
88366
|
+
state.chatProjectBusy = false;
|
|
88367
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
88368
|
+
}
|
|
88369
|
+
} else if (row && ["project", "folder", "up"].includes(row.kind)) {
|
|
88370
|
+
state.chatSidebarLocation = row.kind === "up" && row.folderId === void 0 ? null : { projectId: row.projectId, folderId: row.folderId ?? null };
|
|
88371
|
+
state.chatSidebarAncestors = false;
|
|
88372
|
+
state.sidebarIndex = 0;
|
|
88373
|
+
}
|
|
88374
|
+
} else if (state.workspace === "projects") await context.command(`/project ${state.projects[state.sidebarIndex]?.id}`);
|
|
87886
88375
|
else if (state.workspace === "tasks") await openTask(context, state.tasks[state.sidebarIndex]?.taskId ?? "");
|
|
87887
88376
|
else if (state.workspace === "workflows") await context.command(`/workflow ${state.workflows[state.sidebarIndex]?.id}`);
|
|
87888
88377
|
else if (state.workspace === "apps") await context.command(`/app ${state.apps[state.sidebarIndex]?.id}`);
|
|
@@ -88269,6 +88758,27 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88269
88758
|
let resolveResult = null;
|
|
88270
88759
|
let renderTimer = null;
|
|
88271
88760
|
let closed = false;
|
|
88761
|
+
let stopActivity;
|
|
88762
|
+
let activityTimer;
|
|
88763
|
+
let refreshingActivity = false;
|
|
88764
|
+
const refreshActivity = async () => {
|
|
88765
|
+
if (closed || refreshingActivity || !state.signedIn) return;
|
|
88766
|
+
refreshingActivity = true;
|
|
88767
|
+
try {
|
|
88768
|
+
await refreshTuiChatSidebar(state, client2, render);
|
|
88769
|
+
} finally {
|
|
88770
|
+
refreshingActivity = false;
|
|
88771
|
+
}
|
|
88772
|
+
};
|
|
88773
|
+
const activityPoll = setInterval(() => {
|
|
88774
|
+
void refreshActivity();
|
|
88775
|
+
}, 3e4);
|
|
88776
|
+
const activityAnimation = setInterval(() => {
|
|
88777
|
+
if (!closed && state.runningChatIds.length) {
|
|
88778
|
+
state.activityFrame = (state.activityFrame + 1) % 4;
|
|
88779
|
+
render();
|
|
88780
|
+
}
|
|
88781
|
+
}, 250);
|
|
88272
88782
|
const render = () => {
|
|
88273
88783
|
if (closed) return;
|
|
88274
88784
|
if (state.screen !== "chat") client2.clearInteractiveChatViewer();
|
|
@@ -88284,6 +88794,10 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88284
88794
|
};
|
|
88285
88795
|
const finish = (result) => {
|
|
88286
88796
|
closed = true;
|
|
88797
|
+
stopActivity?.();
|
|
88798
|
+
clearTimeout(activityTimer);
|
|
88799
|
+
clearInterval(activityPoll);
|
|
88800
|
+
clearInterval(activityAnimation);
|
|
88287
88801
|
client2.endInteractiveViewerSession();
|
|
88288
88802
|
if (renderTimer) clearTimeout(renderTimer);
|
|
88289
88803
|
renderTimer = null;
|
|
@@ -88300,6 +88814,19 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88300
88814
|
});
|
|
88301
88815
|
render();
|
|
88302
88816
|
void loadHomeData(state, client2, render);
|
|
88817
|
+
void refreshActivity();
|
|
88818
|
+
if (state.signedIn && typeof client2.observeChatActivity === "function") {
|
|
88819
|
+
void client2.observeChatActivity(() => {
|
|
88820
|
+
clearTimeout(activityTimer);
|
|
88821
|
+
activityTimer = setTimeout(() => {
|
|
88822
|
+
void refreshActivity();
|
|
88823
|
+
}, 300);
|
|
88824
|
+
}).then((stop) => {
|
|
88825
|
+
if (closed) stop();
|
|
88826
|
+
else stopActivity = stop;
|
|
88827
|
+
}).catch(() => {
|
|
88828
|
+
});
|
|
88829
|
+
}
|
|
88303
88830
|
return new Promise((resolve18) => {
|
|
88304
88831
|
resolveResult = resolve18;
|
|
88305
88832
|
});
|
|
@@ -88612,12 +89139,12 @@ async function sendTuiMessage(params) {
|
|
|
88612
89139
|
if (sourceExample) state.messages = sourceExample.messages.map((m) => ({ role: m.role === "user" ? "user" : "assistant", content: m.content, title: m.senderName }));
|
|
88613
89140
|
const messages = state.messages;
|
|
88614
89141
|
const existingChatId = sourceExample ? null : state.activeChatId;
|
|
88615
|
-
const chatId = existingChatId ??
|
|
89142
|
+
const chatId = existingChatId ?? randomUUID21();
|
|
88616
89143
|
const draftKey = sourceExample ? `example:${sourceExample.chat.id}` : existingChatId ?? "new";
|
|
88617
89144
|
state.drafts[draftKey] = "";
|
|
88618
89145
|
state.input = "";
|
|
88619
89146
|
state.inputCursor = null;
|
|
88620
|
-
const anonymousHistory = history ?? messages.filter((m) => m.role !== "system").map((m) => ({ message_id:
|
|
89147
|
+
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
89148
|
state.activeChatId = chatId;
|
|
88622
89149
|
state.headerState = existingChatId ? "ready" : "loading";
|
|
88623
89150
|
state.headerError = null;
|
|
@@ -89042,7 +89569,7 @@ function clamp(value, min, max) {
|
|
|
89042
89569
|
}
|
|
89043
89570
|
|
|
89044
89571
|
// src/codexTaskBridge.ts
|
|
89045
|
-
import { createHash as
|
|
89572
|
+
import { createHash as createHash23 } from "crypto";
|
|
89046
89573
|
import { spawn as spawn3 } from "child_process";
|
|
89047
89574
|
import { existsSync as existsSync15, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
|
|
89048
89575
|
import { isAbsolute as isAbsolute4, join as join18, resolve as resolve10 } from "path";
|
|
@@ -89085,7 +89612,7 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
|
|
|
89085
89612
|
const session = client2.getSession();
|
|
89086
89613
|
const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
|
|
89087
89614
|
const account = JSON.stringify(scope2);
|
|
89088
|
-
const directory = join18(stateRoot, "codex-chat-deletions",
|
|
89615
|
+
const directory = join18(stateRoot, "codex-chat-deletions", createHash23("sha256").update(account).digest("hex"));
|
|
89089
89616
|
if (!existsSync15(directory)) return;
|
|
89090
89617
|
const release2 = await lockfile5.lock(directory, { retries: 0, stale: 24e4 });
|
|
89091
89618
|
try {
|
|
@@ -89119,14 +89646,14 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
|
|
|
89119
89646
|
}
|
|
89120
89647
|
|
|
89121
89648
|
// src/taskCommandDelivery.ts
|
|
89122
|
-
import { createHash as
|
|
89649
|
+
import { createHash as createHash24 } from "crypto";
|
|
89123
89650
|
import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
|
|
89124
89651
|
import { join as join19 } from "path";
|
|
89125
89652
|
import lockfile6 from "proper-lockfile";
|
|
89126
89653
|
async function flushTaskCommands(client2, teamId, root = resolveStateDir(), onNotice) {
|
|
89127
89654
|
const session = client2.getSession();
|
|
89128
89655
|
const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
|
|
89129
|
-
const directory = join19(root, "task-command-delivery",
|
|
89656
|
+
const directory = join19(root, "task-command-delivery", createHash24("sha256").update(JSON.stringify(scope2)).digest("hex"));
|
|
89130
89657
|
if (!existsSync16(directory)) return;
|
|
89131
89658
|
const release2 = await lockfile6.lock(directory, { retries: 0, stale: 24e4 });
|
|
89132
89659
|
try {
|
|
@@ -89236,7 +89763,7 @@ import {
|
|
|
89236
89763
|
writeFileSync as writeFileSync13
|
|
89237
89764
|
} from "fs";
|
|
89238
89765
|
import { spawn as spawn7 } from "child_process";
|
|
89239
|
-
import { createHash as
|
|
89766
|
+
import { createHash as createHash30 } from "crypto";
|
|
89240
89767
|
import { extname as extname4, join as join26, resolve as resolve16, relative as relative11 } from "path";
|
|
89241
89768
|
|
|
89242
89769
|
// src/projectSourceRootPolicy.ts
|
|
@@ -89271,7 +89798,7 @@ function isEqualOrInside(parent, candidate) {
|
|
|
89271
89798
|
}
|
|
89272
89799
|
|
|
89273
89800
|
// src/projectPathPolicy.ts
|
|
89274
|
-
import { createHash as
|
|
89801
|
+
import { createHash as createHash25, randomBytes as randomBytes12 } from "crypto";
|
|
89275
89802
|
import {
|
|
89276
89803
|
chmodSync as chmodSync9,
|
|
89277
89804
|
closeSync as closeSync5,
|
|
@@ -89552,7 +90079,7 @@ function isInside2(root, candidate) {
|
|
|
89552
90079
|
return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
|
|
89553
90080
|
}
|
|
89554
90081
|
function sha2563(value) {
|
|
89555
|
-
return
|
|
90082
|
+
return createHash25("sha256").update(value).digest("hex");
|
|
89556
90083
|
}
|
|
89557
90084
|
function stableJson2(value) {
|
|
89558
90085
|
return JSON.stringify(value, (_key, item) => {
|
|
@@ -89562,7 +90089,7 @@ function stableJson2(value) {
|
|
|
89562
90089
|
}
|
|
89563
90090
|
|
|
89564
90091
|
// src/remoteFileWrites.ts
|
|
89565
|
-
import { createHash as
|
|
90092
|
+
import { createHash as createHash26, randomBytes as randomBytes13 } from "crypto";
|
|
89566
90093
|
import {
|
|
89567
90094
|
chmodSync as chmodSync10,
|
|
89568
90095
|
closeSync as closeSync7,
|
|
@@ -89827,7 +90354,7 @@ var MAX_FILE_BYTES2 = 200 * 1024;
|
|
|
89827
90354
|
var LOCK_ROOT = join22(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
|
|
89828
90355
|
var DEFAULT_JOURNAL_ROOT = join22(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
|
|
89829
90356
|
function sha256ProjectFile(content) {
|
|
89830
|
-
return
|
|
90357
|
+
return createHash26("sha256").update(content).digest("hex");
|
|
89831
90358
|
}
|
|
89832
90359
|
async function executeRemoteFileMutation(options) {
|
|
89833
90360
|
const mutation = validateMutation(options.mutation);
|
|
@@ -90515,13 +91042,13 @@ import { existsSync as existsSync23, realpathSync as realpathSync10, statSync as
|
|
|
90515
91042
|
import { basename as basename7, dirname as dirname13, relative as relative10, resolve as resolve15 } from "path";
|
|
90516
91043
|
|
|
90517
91044
|
// src/remoteCommandRuntime.ts
|
|
90518
|
-
import { createHash as
|
|
91045
|
+
import { createHash as createHash28, randomUUID as randomUUID22 } from "crypto";
|
|
90519
91046
|
import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
|
|
90520
91047
|
import { existsSync as existsSync21, lstatSync as lstatSync9, readdirSync as readdirSync12, realpathSync as realpathSync7, statSync as statSync6 } from "fs";
|
|
90521
91048
|
import { isAbsolute as isAbsolute8, join as join23, relative as relative7, resolve as resolve14, sep } from "path";
|
|
90522
91049
|
|
|
90523
91050
|
// src/remoteCommandAppArmor.ts
|
|
90524
|
-
import { createHash as
|
|
91051
|
+
import { createHash as createHash27 } from "crypto";
|
|
90525
91052
|
import { spawn as spawn5 } from "child_process";
|
|
90526
91053
|
import { lstatSync as lstatSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
|
|
90527
91054
|
import { isAbsolute as isAbsolute7, relative as relative6 } from "path";
|
|
@@ -90738,7 +91265,7 @@ function hasControl2(value) {
|
|
|
90738
91265
|
});
|
|
90739
91266
|
}
|
|
90740
91267
|
function sha2564(value) {
|
|
90741
|
-
return
|
|
91268
|
+
return createHash27("sha256").update(value).digest("hex");
|
|
90742
91269
|
}
|
|
90743
91270
|
|
|
90744
91271
|
// src/remoteCommandRuntime.ts
|
|
@@ -91142,7 +91669,7 @@ var RemoteCommandRuntime = class {
|
|
|
91142
91669
|
};
|
|
91143
91670
|
function createRemoteCommandPreflight(request) {
|
|
91144
91671
|
if (!request || typeof request !== "object") throw new RemoteCommandError("invalid_request", "Command request must be an object");
|
|
91145
|
-
const executionId = request.execution_id ??
|
|
91672
|
+
const executionId = request.execution_id ?? randomUUID22();
|
|
91146
91673
|
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
91674
|
if (typeof request.project_id !== "string" || !request.project_id || request.project_id.length > 256) throw new RemoteCommandError("invalid_request", "Invalid Project ID");
|
|
91148
91675
|
if (typeof request.source_root !== "string" || !request.source_root) throw new RemoteCommandError("invalid_request", "Invalid Project source root");
|
|
@@ -91579,7 +92106,7 @@ function stableJson4(value) {
|
|
|
91579
92106
|
return JSON.stringify(value);
|
|
91580
92107
|
}
|
|
91581
92108
|
function sha2565(value) {
|
|
91582
|
-
return
|
|
92109
|
+
return createHash28("sha256").update(value).digest("hex");
|
|
91583
92110
|
}
|
|
91584
92111
|
function deepFreeze(value) {
|
|
91585
92112
|
if (value && typeof value === "object") {
|
|
@@ -91857,7 +92384,7 @@ function capabilityError(message) {
|
|
|
91857
92384
|
}
|
|
91858
92385
|
|
|
91859
92386
|
// src/remoteCommandResourceGrants.ts
|
|
91860
|
-
import { createHash as
|
|
92387
|
+
import { createHash as createHash29 } from "crypto";
|
|
91861
92388
|
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
92389
|
import { isAbsolute as isAbsolute10, join as join25, relative as relative9 } from "path";
|
|
91863
92390
|
var STORE_FILE = "remote-command-resource-grants.json";
|
|
@@ -91952,7 +92479,7 @@ function resolveRemoteCommandResourceGrants(context) {
|
|
|
91952
92479
|
return { writable_targets, network_profiles, credential_profiles };
|
|
91953
92480
|
}
|
|
91954
92481
|
function definitionDigest(kind, definition) {
|
|
91955
|
-
return
|
|
92482
|
+
return createHash29("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
|
|
91956
92483
|
}
|
|
91957
92484
|
function upsert(grant) {
|
|
91958
92485
|
validateIdentity(grant.project_id, grant.source_id, grant.profile_id);
|
|
@@ -93122,7 +93649,7 @@ function readRemoteAccessTextFile(options) {
|
|
|
93122
93649
|
truncated,
|
|
93123
93650
|
sizeBytes: stats.size,
|
|
93124
93651
|
lineCount: countTextLines(content),
|
|
93125
|
-
expected_base: truncated ? null :
|
|
93652
|
+
expected_base: truncated ? null : createHash30("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
|
|
93126
93653
|
};
|
|
93127
93654
|
} catch (error) {
|
|
93128
93655
|
const code = error.code;
|
|
@@ -93172,7 +93699,7 @@ function readRemoteAccessImageChunk(options) {
|
|
|
93172
93699
|
mime_type: mimeType,
|
|
93173
93700
|
size_bytes: stats.size,
|
|
93174
93701
|
offset: options.offset,
|
|
93175
|
-
content_hash:
|
|
93702
|
+
content_hash: createHash30("sha256").update(bytes2).digest("hex")
|
|
93176
93703
|
};
|
|
93177
93704
|
} catch (error) {
|
|
93178
93705
|
if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
|
|
@@ -93220,8 +93747,8 @@ function readRemoteAccessFileChunk(options) {
|
|
|
93220
93747
|
content_base64: bytes2.toString("base64"),
|
|
93221
93748
|
size_bytes: stats.size,
|
|
93222
93749
|
offset: options.offset,
|
|
93223
|
-
file_identity:
|
|
93224
|
-
chunk_hash:
|
|
93750
|
+
file_identity: createHash30("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
|
|
93751
|
+
chunk_hash: createHash30("sha256").update(bytes2).digest("hex")
|
|
93225
93752
|
};
|
|
93226
93753
|
} catch (error) {
|
|
93227
93754
|
if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
|
|
@@ -94783,7 +95310,7 @@ function writeWorkRecoveryAtomically(path2, document) {
|
|
|
94783
95310
|
|
|
94784
95311
|
// src/revolutBusinessCertificate.ts
|
|
94785
95312
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
94786
|
-
import { createSign, randomUUID as
|
|
95313
|
+
import { createSign, randomUUID as randomUUID23 } from "crypto";
|
|
94787
95314
|
import { chmodSync as chmodSync14, existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync23 } from "fs";
|
|
94788
95315
|
import { homedir as homedir8 } from "os";
|
|
94789
95316
|
import { join as join28, resolve as resolve17 } from "path";
|
|
@@ -94857,7 +95384,7 @@ function generateRevolutBusinessClientAssertion(options) {
|
|
|
94857
95384
|
aud: REVOLUT_BUSINESS_AUDIENCE,
|
|
94858
95385
|
exp: now + 3600,
|
|
94859
95386
|
iat: now,
|
|
94860
|
-
jti:
|
|
95387
|
+
jti: randomUUID23()
|
|
94861
95388
|
});
|
|
94862
95389
|
const unsigned = `${header2}.${payload}`;
|
|
94863
95390
|
const signer = createSign("RSA-SHA256");
|
|
@@ -96329,7 +96856,7 @@ async function resolveAddToProjectStorageChoice(client2, project, flags, objectT
|
|
|
96329
96856
|
async function createEncryptedProjectItem(client2, project, input) {
|
|
96330
96857
|
const timestamp = nowSeconds3();
|
|
96331
96858
|
return client2.createProjectItem(project.projectId, {
|
|
96332
|
-
project_item_id:
|
|
96859
|
+
project_item_id: randomUUID24(),
|
|
96333
96860
|
folder_id: input.folderId ?? null,
|
|
96334
96861
|
item_type: input.itemType,
|
|
96335
96862
|
target_id: input.targetId,
|
|
@@ -96528,7 +97055,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
|
|
|
96528
97055
|
const encryptedSources = proof.length ? await encryptPlanAssumptionSources(plan, masterKey, proof) : void 0;
|
|
96529
97056
|
if (action === "create" || action === "add") {
|
|
96530
97057
|
const assumption = await client2.createPlanAssumption(plan.planId, {
|
|
96531
|
-
assumption_id: typeof flags.id === "string" ? flags.id :
|
|
97058
|
+
assumption_id: typeof flags.id === "string" ? flags.id : randomUUID24(),
|
|
96532
97059
|
encrypted_text: await encryptPlanAssumptionSources(plan, masterKey, requiredStringFlag(flags.text ?? rest.slice(2).join(" "), "--text <assumption>")),
|
|
96533
97060
|
status: typeof flags.status === "string" ? flags.status : void 0,
|
|
96534
97061
|
linked_sub_chat_id: typeof flags["sub-chat"] === "string" ? flags["sub-chat"] : void 0,
|
|
@@ -96570,7 +97097,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
|
|
|
96570
97097
|
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
97098
|
if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
|
|
96572
97099
|
const snapshot2 = JSON.stringify(planToJson(plan));
|
|
96573
|
-
const revision = await client2.submitPlanRevision(plan.planId, { fingerprint:
|
|
97100
|
+
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
97101
|
const reviewUrl = `${deriveAppUrl(client2.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
|
|
96575
97102
|
if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
|
|
96576
97103
|
else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
|
|
@@ -97464,7 +97991,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97464
97991
|
const writeMode = await resolveProjectWriteMode(flags, `Choose how OpenMates may change files in ${JSON.stringify(name)}:`);
|
|
97465
97992
|
const projectKey = randomBytes14(32);
|
|
97466
97993
|
const wrapping = await client2.projectWrappingKey(context);
|
|
97467
|
-
const projectId =
|
|
97994
|
+
const projectId = randomUUID24();
|
|
97468
97995
|
const timestamp = nowSeconds3();
|
|
97469
97996
|
const defaultFocus = buildProjectDefaultFocus(name);
|
|
97470
97997
|
const slugMetadata = await buildEncryptedObjectSlugMetadata({
|
|
@@ -97490,7 +98017,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97490
98017
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
|
|
97491
98018
|
key_wrappers: wrapping.teamId ? [{
|
|
97492
98019
|
key_type: "team",
|
|
97493
|
-
hashed_team_id:
|
|
98020
|
+
hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
|
|
97494
98021
|
team_key_epoch: 1,
|
|
97495
98022
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
97496
98023
|
wrapper_version: 1,
|
|
@@ -97730,7 +98257,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97730
98257
|
const proposal = isShortWorkspaceAsk(instruction) ? { name: instruction, description: "", icon: "folder", color: "default" } : extractRecord(await client2.planProjectAsk({ instruction }), "proposed_project");
|
|
97731
98258
|
const projectKey = randomBytes14(32);
|
|
97732
98259
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
97733
|
-
const projectId =
|
|
98260
|
+
const projectId = randomUUID24();
|
|
97734
98261
|
const name = requiredString(proposal, "name", instruction);
|
|
97735
98262
|
const slugMetadata = await buildEncryptedObjectSlugMetadata({
|
|
97736
98263
|
value: typeof flags.slug === "string" ? flags.slug : name,
|
|
@@ -97853,8 +98380,8 @@ async function handleProjectFiles(client2, masterKey, rest, flags, context) {
|
|
|
97853
98380
|
...flags["include-ignored"] === true ? {
|
|
97854
98381
|
approvedIgnoredRead: {
|
|
97855
98382
|
path: String(argumentsValue.path),
|
|
97856
|
-
chatId: `cli-direct-${
|
|
97857
|
-
operationId: `cli-direct-${
|
|
98383
|
+
chatId: `cli-direct-${randomUUID24()}`,
|
|
98384
|
+
operationId: `cli-direct-${randomUUID24()}`
|
|
97858
98385
|
}
|
|
97859
98386
|
} : {}
|
|
97860
98387
|
});
|
|
@@ -98642,7 +99169,7 @@ async function handleRemoteAccess(client2, subcommand, rest, flags) {
|
|
|
98642
99169
|
const projects = await loadProjects(client2, masterKey, hostingFlags, hostingContext);
|
|
98643
99170
|
const bindings = await resolveRemoteAccessBindings(client2, masterKey, projects, candidateRoots, hostingFlags, hostingContext);
|
|
98644
99171
|
if (bindings.length === 0) throw new Error("No Project folders were approved for remote access.");
|
|
98645
|
-
const sourceSessionId =
|
|
99172
|
+
const sourceSessionId = randomUUID24();
|
|
98646
99173
|
const controller = new AbortController();
|
|
98647
99174
|
const stop = () => controller.abort();
|
|
98648
99175
|
process.once("SIGINT", stop);
|
|
@@ -98796,7 +99323,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
|
|
|
98796
99323
|
updated_at: nowSeconds3()
|
|
98797
99324
|
}, context);
|
|
98798
99325
|
}
|
|
98799
|
-
resolved.push({ rootPath, project, sourceId:
|
|
99326
|
+
resolved.push({ rootPath, project, sourceId: randomUUID24() });
|
|
98800
99327
|
}
|
|
98801
99328
|
}
|
|
98802
99329
|
const bindings = [];
|
|
@@ -98840,7 +99367,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
|
|
|
98840
99367
|
}
|
|
98841
99368
|
async function createEncryptedRemoteAccessProject(client2, name, rootPath, writeMode, context) {
|
|
98842
99369
|
const projectKey = randomBytes14(32);
|
|
98843
|
-
const projectId =
|
|
99370
|
+
const projectId = randomUUID24();
|
|
98844
99371
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
98845
99372
|
const wrapping = await client2.projectWrappingKey(context);
|
|
98846
99373
|
const importedInstructions = readRootProjectInstructions(rootPath);
|
|
@@ -98866,7 +99393,7 @@ async function createEncryptedRemoteAccessProject(client2, name, rootPath, write
|
|
|
98866
99393
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
|
|
98867
99394
|
key_wrappers: wrapping.teamId ? [{
|
|
98868
99395
|
key_type: "team",
|
|
98869
|
-
hashed_team_id:
|
|
99396
|
+
hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
|
|
98870
99397
|
team_key_epoch: 1,
|
|
98871
99398
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
98872
99399
|
wrapper_version: 1,
|
|
@@ -98924,7 +99451,7 @@ async function ensureProjectDefaultFocus(client2, project, context, rootPath) {
|
|
|
98924
99451
|
function buildProjectDefaultFocus(projectName, instructions, source = "generated") {
|
|
98925
99452
|
const name = projectName.trim() || "Untitled project";
|
|
98926
99453
|
return {
|
|
98927
|
-
focus_id:
|
|
99454
|
+
focus_id: randomUUID24(),
|
|
98928
99455
|
name: `Work on ${name}`,
|
|
98929
99456
|
instructions: instructions?.trim() || `Help with work in ${name}. Follow the user's instructions and the Project's connected source guidance.`,
|
|
98930
99457
|
source
|
|
@@ -100257,7 +100784,7 @@ function normalizeApiKeyLoadedChat(payload) {
|
|
|
100257
100784
|
const messages = rawMessages.map((message) => {
|
|
100258
100785
|
const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
|
|
100259
100786
|
const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
|
|
100260
|
-
const hashedMessageId = messageId ?
|
|
100787
|
+
const hashedMessageId = messageId ? createHash31("sha256").update(messageId).digest("hex") : null;
|
|
100261
100788
|
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
100789
|
return {
|
|
100263
100790
|
id: messageId,
|
|
@@ -102454,7 +102981,7 @@ async function runAccountImport(client2, parserFormat, file, flags) {
|
|
|
102454
102981
|
if (flags.yes !== true) {
|
|
102455
102982
|
await confirmOrExit(`Import ${selectedCount} chat(s) into new encrypted OpenMates chats? [y/N] `);
|
|
102456
102983
|
}
|
|
102457
|
-
const importId = typeof preview.import_id === "string" ? preview.import_id :
|
|
102984
|
+
const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID24();
|
|
102458
102985
|
const selectedChats = parsed.chats.slice(0, selectedCount);
|
|
102459
102986
|
const selectedFingerprints = selectedChats.map((chat) => chat.source_fingerprint);
|
|
102460
102987
|
const confirmation = await client2.confirmAccountImport(importId, selectedFingerprints);
|
|
@@ -102948,8 +103475,8 @@ async function handleFinance(client2, subcommand, _rest, flags) {
|
|
|
102948
103475
|
refresh_token_envelope: connectedAccount.refreshTokenBundle
|
|
102949
103476
|
}
|
|
102950
103477
|
] : [],
|
|
102951
|
-
chatId: `cli-finance-${
|
|
102952
|
-
messageId: `cli-finance-message-${
|
|
103478
|
+
chatId: `cli-finance-${randomUUID24()}`,
|
|
103479
|
+
messageId: `cli-finance-message-${randomUUID24()}`,
|
|
102953
103480
|
apiKey: resolveApiKey(flags) ?? void 0,
|
|
102954
103481
|
promptInjectionProtection: false
|
|
102955
103482
|
});
|