openmates 0.27.0-alpha.5 → 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-P4SMXK2Z.js → chunk-CLOVZ7RX.js} +704 -139
- 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) {
|
|
@@ -84546,6 +84672,21 @@ function padCells(value, width) {
|
|
|
84546
84672
|
const trimmed = truncateCells(value, width);
|
|
84547
84673
|
return trimmed + " ".repeat(Math.max(0, width - cells(trimmed)));
|
|
84548
84674
|
}
|
|
84675
|
+
function sliceCells(value, start, width) {
|
|
84676
|
+
start = Math.max(0, Math.floor(start));
|
|
84677
|
+
width = Math.max(0, Math.floor(width));
|
|
84678
|
+
const end = start + width;
|
|
84679
|
+
let position = 0, result = "";
|
|
84680
|
+
for (const { segment } of segments.segment(terminalText(value).replace(/\n/g, " "))) {
|
|
84681
|
+
const size = graphemeWidth(segment), next = position + size;
|
|
84682
|
+
if (next > start && position < end) {
|
|
84683
|
+
result += position >= start && next <= end ? segment : " ".repeat(Math.min(end, next) - Math.max(start, position));
|
|
84684
|
+
}
|
|
84685
|
+
position = next;
|
|
84686
|
+
if (position >= end) break;
|
|
84687
|
+
}
|
|
84688
|
+
return result + " ".repeat(Math.max(0, width - cells(result)));
|
|
84689
|
+
}
|
|
84549
84690
|
function wrapCells(value, width) {
|
|
84550
84691
|
width = Math.max(1, width);
|
|
84551
84692
|
const result = [];
|
|
@@ -84672,13 +84813,13 @@ function storedPath(metadata, displayName) {
|
|
|
84672
84813
|
return null;
|
|
84673
84814
|
}
|
|
84674
84815
|
}
|
|
84675
|
-
async function loadTuiProject(client2, id) {
|
|
84816
|
+
async function loadTuiProject(client2, id, chatOnly = false) {
|
|
84676
84817
|
const context = contextFor(client2);
|
|
84677
84818
|
const detail = await client2.getProject(id, context);
|
|
84678
84819
|
const project = await projectFromRecord(client2, detail.project, context);
|
|
84679
84820
|
const [itemsResponse, sourceRecords] = await Promise.all([
|
|
84680
|
-
client2.listProjectItems(id, context),
|
|
84681
|
-
client2.listProjectSources(id, context)
|
|
84821
|
+
client2.listProjectItems(id, { ...context, chatOnly }),
|
|
84822
|
+
chatOnly ? Promise.resolve([]) : client2.listProjectSources(id, context)
|
|
84682
84823
|
]);
|
|
84683
84824
|
const folderIds = itemsResponse.folders.map((folder) => String(folder.folder_id ?? "")).filter(Boolean);
|
|
84684
84825
|
const folderByHash = new Map(folderIds.map((folderId) => [createHash21("sha256").update(folderId).digest("hex"), folderId]));
|
|
@@ -84912,7 +85053,7 @@ function buildProjectForm() {
|
|
|
84912
85053
|
]
|
|
84913
85054
|
};
|
|
84914
85055
|
}
|
|
84915
|
-
async function submitProjectForm(client2, form) {
|
|
85056
|
+
async function submitProjectForm(client2, form, chatOrganizationOnly = false) {
|
|
84916
85057
|
const name = formValue(form, "name").trim();
|
|
84917
85058
|
if (!name) throw new Error("Project name is required.");
|
|
84918
85059
|
const writeMode = formValue(form, "write_mode");
|
|
@@ -84936,7 +85077,7 @@ async function submitProjectForm(client2, form) {
|
|
|
84936
85077
|
created_at: timestamp,
|
|
84937
85078
|
updated_at: timestamp,
|
|
84938
85079
|
last_opened_at: timestamp,
|
|
84939
|
-
write_mode: writeMode,
|
|
85080
|
+
write_mode: chatOrganizationOnly ? null : writeMode,
|
|
84940
85081
|
default_focus_id: focus.focus_id,
|
|
84941
85082
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: focus }), key),
|
|
84942
85083
|
key_wrappers: wrapping.teamId ? [{
|
|
@@ -84948,7 +85089,7 @@ async function submitProjectForm(client2, form) {
|
|
|
84948
85089
|
created_at: timestamp
|
|
84949
85090
|
}] : []
|
|
84950
85091
|
};
|
|
84951
|
-
const result = await client2.createProject(payload, context);
|
|
85092
|
+
const result = await client2.createProject({ ...payload, ...chatOrganizationOnly ? { chat_organization_only: true } : {} }, context);
|
|
84952
85093
|
return projectFromRecord(client2, { ...payload, ...result.project ?? {} }, context);
|
|
84953
85094
|
}
|
|
84954
85095
|
|
|
@@ -85523,6 +85664,10 @@ var TUI_ACTIONS = [
|
|
|
85523
85664
|
{ label: "Move selected task", command: "/task-status" },
|
|
85524
85665
|
{ label: "Add task activity", command: "/task-activity" },
|
|
85525
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" },
|
|
85526
85671
|
{ label: "New chat in Project", command: "/project-chat" },
|
|
85527
85672
|
{ label: "Edit workflow step", command: "/workflow-edit" },
|
|
85528
85673
|
{ label: "Enable or disable workflow", command: "/workflow-toggle" },
|
|
@@ -85537,6 +85682,259 @@ function paletteActions(query) {
|
|
|
85537
85682
|
return TUI_ACTIONS.filter((item) => `${item.label} ${item.command}`.toLowerCase().includes(needle));
|
|
85538
85683
|
}
|
|
85539
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
|
+
|
|
85540
85938
|
// src/tuiLayout.ts
|
|
85541
85939
|
var WORKSPACES = ["chats", "apps", "projects", "workflows", "tasks"];
|
|
85542
85940
|
var DEFAULT_CHAT_BACKGROUND = "#4867cd";
|
|
@@ -85547,7 +85945,13 @@ function sidebarLines(state) {
|
|
|
85547
85945
|
const selected = (label3, index) => `${state.focus === "sidebar" && state.sidebarIndex === index ? ">" : " "} ${label3}`;
|
|
85548
85946
|
switch (state.workspace) {
|
|
85549
85947
|
case "chats":
|
|
85550
|
-
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
|
+
];
|
|
85551
85955
|
case "projects":
|
|
85552
85956
|
return ["Projects", "", ...state.projects.map((p, i) => selected(p.name, i)), "", "/project-create new"];
|
|
85553
85957
|
case "tasks":
|
|
@@ -85572,7 +85976,7 @@ function workspaceHint(state) {
|
|
|
85572
85976
|
if (state.screen === "app") return "1\u20135 tabs \u2191/\u2193 choose Enter open Esc Apps /search";
|
|
85573
85977
|
if (state.screen === "app-skill") return "1\u20133 tabs Enter use skill Esc app Ctrl+P actions";
|
|
85574
85978
|
if (state.screen === "app-result") return "\u2191/\u2193 scroll Esc saved results Ctrl+P actions";
|
|
85575
|
-
if (state.screen === "apps") return "\
|
|
85979
|
+
if (state.screen === "apps") return state.focus === "composer" ? "Type /command Shift+Tab apps Ctrl+B sidebar" : "\u2190/\u2192 app Enter open \u2191/\u2193 scroll /search /browse Show all Ctrl+B sidebar";
|
|
85576
85980
|
if (state.screen === "projects" || state.screen === "tasks" || state.screen === "workflows") return "\u2191/\u2193 choose Enter open /search filter Tab focus Ctrl+P actions";
|
|
85577
85981
|
if (state.screen === "interests") return "\u2191/\u2193 move Space select Enter continue Esc back";
|
|
85578
85982
|
if (state.screen === "examples") return "\u2191/\u2193 choose Enter open /search filter Esc back";
|
|
@@ -85637,7 +86041,7 @@ function renderWorkspaceFrame(state, rawWidth, rawHeight, body, options = {}) {
|
|
|
85637
86041
|
const markers = content.map((row, index) => /(?:^|[│|])\s*[>›] /.test(lineText(row)) ? index : -1).filter((index) => index >= 0);
|
|
85638
86042
|
const marker = state.screen === "workflow" ? markers.at(-1) ?? -1 : markers[0] ?? -1;
|
|
85639
86043
|
let selectionEnd = marker;
|
|
85640
|
-
if (["workflow", "start", "chats"].includes(state.screen) && marker >= 0) {
|
|
86044
|
+
if (["workflow", "start", "chats", "apps"].includes(state.screen) && marker >= 0) {
|
|
85641
86045
|
const closing = content.findIndex((row, index) => index > marker && lineText(row).includes("\u2570"));
|
|
85642
86046
|
selectionEnd = Math.min(marker + 10, closing < 0 ? marker : closing);
|
|
85643
86047
|
}
|
|
@@ -85690,6 +86094,48 @@ function titleCase(value) {
|
|
|
85690
86094
|
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
85691
86095
|
}
|
|
85692
86096
|
|
|
86097
|
+
// src/tuiCarousel.ts
|
|
86098
|
+
function renderCardCarousel(cards, width, selectedIndex, focused) {
|
|
86099
|
+
width = Math.max(1, Math.floor(width));
|
|
86100
|
+
if (!cards.length) return [];
|
|
86101
|
+
const selected = Math.max(0, Math.min(cards.length - 1, selectedIndex));
|
|
86102
|
+
const cardWidth = Math.min(width, 36), inner = Math.max(1, cardWidth - 4), stride = cardWidth + 2;
|
|
86103
|
+
const center = Math.floor((width - cardWidth) / 2);
|
|
86104
|
+
const reach = Math.ceil(width / stride);
|
|
86105
|
+
const visible = cards.slice(Math.max(0, selected - reach), selected + reach + 1).map((card, offset) => {
|
|
86106
|
+
const index = Math.max(0, selected - reach) + offset, active = index === selected && focused;
|
|
86107
|
+
const title = wrapCells(`${active ? "\u203A " : ""}${card.title}`, inner).slice(0, 2);
|
|
86108
|
+
const description = wrapCells(card.description, inner).slice(0, 2);
|
|
86109
|
+
while (title.length < 2) title.push("");
|
|
86110
|
+
while (description.length < 2) description.push("");
|
|
86111
|
+
const rows2 = cardWidth < 4 ? [truncateCells(card.title, cardWidth)] : [
|
|
86112
|
+
`\u256D${"\u2500".repeat(cardWidth - 2)}\u256E`,
|
|
86113
|
+
...title.map((line) => `\u2502 ${padCells(line, inner)} \u2502`),
|
|
86114
|
+
...description.map((line) => `\u2502 ${padCells(line, inner)} \u2502`),
|
|
86115
|
+
`\u2502 ${padCells(card.footer, inner)} \u2502`,
|
|
86116
|
+
`\u2570${"\u2500".repeat(cardWidth - 2)}\u256F`
|
|
86117
|
+
];
|
|
86118
|
+
return { rows: rows2, left: center + (index - selected) * stride, background: card.background, active };
|
|
86119
|
+
});
|
|
86120
|
+
return Array.from({ length: visible[0].rows.length }, (_, row) => {
|
|
86121
|
+
const spans = [];
|
|
86122
|
+
let column = 0;
|
|
86123
|
+
for (const card of visible) {
|
|
86124
|
+
const start = Math.max(0, card.left), end = Math.min(width, card.left + cardWidth);
|
|
86125
|
+
if (end <= start) continue;
|
|
86126
|
+
if (start > column) spans.push({ text: " ".repeat(start - column) });
|
|
86127
|
+
spans.push({ text: sliceCells(card.rows[row], Math.max(0, -card.left), end - start), background: card.background, bold: card.active });
|
|
86128
|
+
column = end;
|
|
86129
|
+
}
|
|
86130
|
+
if (column < width) spans.push({ text: " ".repeat(width - column) });
|
|
86131
|
+
return { text: spans.map((span) => span.text).join(""), spans };
|
|
86132
|
+
});
|
|
86133
|
+
}
|
|
86134
|
+
function centeredCarouselText(text4, width) {
|
|
86135
|
+
const value = truncateCells(text4, width);
|
|
86136
|
+
return " ".repeat(Math.max(0, Math.floor((width - cells(value)) / 2))) + value;
|
|
86137
|
+
}
|
|
86138
|
+
|
|
85693
86139
|
// ../workspaceInspirationDefaults.ts
|
|
85694
86140
|
function getWorkspaceInspirations(surface) {
|
|
85695
86141
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -85950,10 +86396,7 @@ async function loadHomeData(state, client2, render) {
|
|
|
85950
86396
|
render();
|
|
85951
86397
|
}
|
|
85952
86398
|
}
|
|
85953
|
-
|
|
85954
|
-
const value = truncateCells(text4, width);
|
|
85955
|
-
return " ".repeat(Math.max(0, Math.floor((width - cells(value)) / 2))) + value;
|
|
85956
|
-
}
|
|
86399
|
+
var centered2 = centeredCarouselText;
|
|
85957
86400
|
function homeHeader(state, width, height) {
|
|
85958
86401
|
const inspiration = currentInspiration(state), rows2 = Math.max(4, Math.min(8, Math.floor(height / 5))), result = [];
|
|
85959
86402
|
const gradient = inspiration?.category && CATEGORY_GRADIENTS[inspiration.category] || CATEGORY_GRADIENTS.general_knowledge;
|
|
@@ -85971,46 +86414,20 @@ function homeHeader(state, width, height) {
|
|
|
85971
86414
|
}
|
|
85972
86415
|
function renderHomeChatCards(state, width, height) {
|
|
85973
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), "");
|
|
85974
86419
|
if (!chats.length) {
|
|
85975
86420
|
result.push(centered2(state.homeLoading ? "Loading your recent chats\u2026" : "Start a chat below. Your recent chats will appear here.", width), "");
|
|
85976
86421
|
return result;
|
|
85977
86422
|
}
|
|
85978
86423
|
result.push(centered2(state.signedIn ? "Continue where you left off" : "Explore example chats", width), "");
|
|
85979
86424
|
const selected = Math.max(0, Math.min(chats.length - 1, state.selectedIndex));
|
|
85980
|
-
|
|
85981
|
-
|
|
85982
|
-
|
|
85983
|
-
|
|
85984
|
-
|
|
85985
|
-
|
|
85986
|
-
const summary = wrapCells(chat.summary || "Continue this conversation", inner).slice(0, 2);
|
|
85987
|
-
while (title.length < 2) title.push("");
|
|
85988
|
-
while (summary.length < 2) summary.push("");
|
|
85989
|
-
const rows2 = cardWidth < 4 ? [truncateCells(chat.title || "Untitled chat", cardWidth)] : [
|
|
85990
|
-
`\u256D${"\u2500".repeat(cardWidth - 2)}\u256E`,
|
|
85991
|
-
...title.map((line) => `\u2502 ${padCells(line, inner)} \u2502`),
|
|
85992
|
-
...summary.map((line) => `\u2502 ${padCells(line, inner)} \u2502`),
|
|
85993
|
-
`\u2502 ${padCells(chat.category?.replaceAll("_", " ") || "Chat", inner)} \u2502`,
|
|
85994
|
-
`\u2570${"\u2500".repeat(cardWidth - 2)}\u256F`
|
|
85995
|
-
];
|
|
85996
|
-
return { rows: rows2, background: (chat.category && CATEGORY_GRADIENTS[chat.category] || BLUE).start, active };
|
|
85997
|
-
});
|
|
85998
|
-
for (let row = 0; row < cards[0].rows.length; row++) {
|
|
85999
|
-
const spans = [];
|
|
86000
|
-
let remaining = width;
|
|
86001
|
-
for (const card of cards) {
|
|
86002
|
-
if (!remaining) break;
|
|
86003
|
-
const text4 = truncateCells(card.rows[row], remaining);
|
|
86004
|
-
spans.push({ text: text4, background: card.background, bold: card.active });
|
|
86005
|
-
remaining -= cells(text4);
|
|
86006
|
-
if (remaining) {
|
|
86007
|
-
const gap = " ".repeat(Math.min(2, remaining));
|
|
86008
|
-
spans.push({ text: gap });
|
|
86009
|
-
remaining -= gap.length;
|
|
86010
|
-
}
|
|
86011
|
-
}
|
|
86012
|
-
result.push({ text: spans.map((span) => span.text).join(""), spans });
|
|
86013
|
-
}
|
|
86425
|
+
result.push(...renderCardCarousel(chats.map((chat) => ({
|
|
86426
|
+
title: chat.title || "Untitled chat",
|
|
86427
|
+
description: chat.summary || "Continue this conversation",
|
|
86428
|
+
footer: chat.category?.replaceAll("_", " ") || "Chat",
|
|
86429
|
+
background: (chat.category && CATEGORY_GRADIENTS[chat.category] || BLUE).start
|
|
86430
|
+
})), width, selected, state.focus === "content"));
|
|
86014
86431
|
result.push(
|
|
86015
86432
|
"",
|
|
86016
86433
|
centered2(`${selected > 0 ? "\u2039" : " "} Chat ${selected + 1} of ${chats.length} ${selected < chats.length - 1 ? "\u203A" : " "}`, width),
|
|
@@ -86022,7 +86439,7 @@ function renderHomeChatCards(state, width, height) {
|
|
|
86022
86439
|
}
|
|
86023
86440
|
|
|
86024
86441
|
// src/tuiAppsWorkspace.ts
|
|
86025
|
-
import { randomBytes as randomBytes11, randomUUID as
|
|
86442
|
+
import { randomBytes as randomBytes11, randomUUID as randomUUID19 } from "crypto";
|
|
86026
86443
|
|
|
86027
86444
|
// ../ui/src/components/apps/appsSkillFormUtils.ts
|
|
86028
86445
|
var object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -86228,20 +86645,26 @@ function visibleTuiApps(apps, query = "") {
|
|
|
86228
86645
|
const words = query.toLocaleLowerCase().trim().split(/\s+/).filter(Boolean);
|
|
86229
86646
|
return apps.filter((app) => words.every((word) => `${app.name} ${app.description} ${app.category} ${app.skills.map((skill) => skill.name).join(" ")}`.toLocaleLowerCase().includes(word)));
|
|
86230
86647
|
}
|
|
86648
|
+
function homeTuiApps(apps, query = "", showAll = false) {
|
|
86649
|
+
const visible = visibleTuiApps(apps, query);
|
|
86650
|
+
return showAll || query ? visible : visible.slice(0, HOME_APP_ORDER.length);
|
|
86651
|
+
}
|
|
86231
86652
|
function renderTuiAppsHome(apps, options) {
|
|
86232
86653
|
const width = Math.max(1, options.width);
|
|
86233
86654
|
const visible = visibleTuiApps(apps, options.query);
|
|
86234
|
-
|
|
86235
|
-
|
|
86236
|
-
|
|
86237
|
-
|
|
86238
|
-
|
|
86239
|
-
app.description,
|
|
86240
|
-
`${app.skills.length} skills${app.category ? ` \xB7 ${app.category.replaceAll("_", " ")}` : ""}`,
|
|
86241
|
-
|
|
86242
|
-
),
|
|
86243
|
-
|
|
86244
|
-
|
|
86655
|
+
if (!visible.length) return [centeredCarouselText(options.query ? "No matching apps." : "No apps available.", width)];
|
|
86656
|
+
const selected = Math.max(0, Math.min(visible.length - 1, options.selectedIndex ?? visible.findIndex((app) => app.id === options.selectedId)));
|
|
86657
|
+
return [
|
|
86658
|
+
...renderCardCarousel(visible.map((app) => ({
|
|
86659
|
+
title: app.name,
|
|
86660
|
+
description: app.description,
|
|
86661
|
+
footer: `${app.skills.length} skills${app.category ? ` \xB7 ${app.category.replaceAll("_", " ")}` : ""}`,
|
|
86662
|
+
background: (APP_GRADIENTS[app.id] ?? PRIMARY_GRADIENT).start
|
|
86663
|
+
})), width, selected, options.selectedId !== void 0),
|
|
86664
|
+
"",
|
|
86665
|
+
centeredCarouselText(`${selected > 0 ? "\u2039" : " "} App ${selected + 1} of ${visible.length} ${selected < visible.length - 1 ? "\u203A" : " "}`, width),
|
|
86666
|
+
centeredCarouselText("\u2190/\u2192 choose app \xB7 Enter open \xB7 Tab focus", width)
|
|
86667
|
+
];
|
|
86245
86668
|
}
|
|
86246
86669
|
function cardLines(title, description, footer, width) {
|
|
86247
86670
|
if (width < 4) return [truncateCells(title, width)];
|
|
@@ -86473,7 +86896,7 @@ async function saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey,
|
|
|
86473
86896
|
const value = record5(result);
|
|
86474
86897
|
const type = string2(value.type, string2(value.embed_type));
|
|
86475
86898
|
if (!type && typeof value.embed_id !== "string") return [];
|
|
86476
|
-
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();
|
|
86477
86900
|
return [(async () => ({
|
|
86478
86901
|
embed_id: id,
|
|
86479
86902
|
encrypted_type: await encryptWithAesGcmCombined(type || "app_skill_use", key),
|
|
@@ -86518,7 +86941,7 @@ async function executeConfirmedTuiAppsSkill(client2, run) {
|
|
|
86518
86941
|
const owner = record5(await client2.whoAmI());
|
|
86519
86942
|
const ownerId = string2(owner.id, string2(owner.user_id));
|
|
86520
86943
|
if (!ownerId) throw new Error("Account identity unavailable; skill was not run.");
|
|
86521
|
-
const rootId =
|
|
86944
|
+
const rootId = randomUUID19();
|
|
86522
86945
|
const key = randomBytes11(32);
|
|
86523
86946
|
const encryptedKey = await encryptBytesWithAesGcm(key, client2.getMasterKeyBytes());
|
|
86524
86947
|
await saveEncryptedResult(client2, ownerId, rootId, key, encryptedKey, run, { status: "processing" }, "processing");
|
|
@@ -86764,6 +87187,15 @@ function createInitialTuiState() {
|
|
|
86764
87187
|
taskStatusFilter: "",
|
|
86765
87188
|
taskContext: null,
|
|
86766
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,
|
|
86767
87199
|
activeChatId: null,
|
|
86768
87200
|
activeChat: null,
|
|
86769
87201
|
headerState: "new",
|
|
@@ -86888,8 +87320,13 @@ function renderBody(state, width, height) {
|
|
|
86888
87320
|
case "chats":
|
|
86889
87321
|
return renderHomeChatCards(state, width, height);
|
|
86890
87322
|
case "apps": {
|
|
86891
|
-
const apps =
|
|
86892
|
-
return [
|
|
87323
|
+
const apps = homeTuiApps(state.apps, state.filter, state.homeShowAll);
|
|
87324
|
+
return [
|
|
87325
|
+
...homeHeader(state, width, height),
|
|
87326
|
+
...renderTuiAppsHome(apps, { width, selectedIndex: state.selectedIndex, selectedId: state.focus === "content" ? apps[state.selectedIndex]?.id : void 0 }),
|
|
87327
|
+
"",
|
|
87328
|
+
centeredCarouselText(`${state.homeShowAll ? "/browse Featured apps" : "/browse Show all"} \xB7 /search Search apps`, width)
|
|
87329
|
+
];
|
|
86893
87330
|
}
|
|
86894
87331
|
case "app": {
|
|
86895
87332
|
const app = state.activeApp;
|
|
@@ -87170,7 +87607,7 @@ function wrap(line, width) {
|
|
|
87170
87607
|
}
|
|
87171
87608
|
|
|
87172
87609
|
// src/tuiWorkspaceController.ts
|
|
87173
|
-
import { randomUUID as
|
|
87610
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
87174
87611
|
function chatDraftKey(state) {
|
|
87175
87612
|
return state.screen === "example" ? `example:${state.activeExample?.chat.id}` : state.activeChatId ?? "new";
|
|
87176
87613
|
}
|
|
@@ -87217,6 +87654,7 @@ async function recent(context) {
|
|
|
87217
87654
|
if (request !== state.routeVersion || homeRequest !== state.homeLoadVersion || !state.signedIn) return;
|
|
87218
87655
|
state.recentChats = chats;
|
|
87219
87656
|
render();
|
|
87657
|
+
await refreshTuiChatSidebar(state, client2, render, true);
|
|
87220
87658
|
}
|
|
87221
87659
|
async function openSavedChat(context, id) {
|
|
87222
87660
|
const { state, client: client2, render } = context;
|
|
@@ -87418,6 +87856,64 @@ async function handleWorkspaceCommand(context, command) {
|
|
|
87418
87856
|
state.selectedProjectId = null;
|
|
87419
87857
|
render();
|
|
87420
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
|
+
}
|
|
87421
87917
|
case "/sidebar":
|
|
87422
87918
|
state.sidebarOpen = !state.sidebarOpen;
|
|
87423
87919
|
state.focus = state.sidebarOpen ? "sidebar" : state.workspace === "chats" && !["start", "chats"].includes(state.screen) ? "composer" : "content";
|
|
@@ -87658,7 +88154,7 @@ async function saveForm(context) {
|
|
|
87658
88154
|
} else if (form.kind === "workflow-create") {
|
|
87659
88155
|
const description = formValue(form, "description").trim();
|
|
87660
88156
|
if (!description) throw new Error("Describe what this workflow should do.");
|
|
87661
|
-
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() });
|
|
87662
88158
|
if (request !== state.routeVersion || state.form !== form) return;
|
|
87663
88159
|
const workflow = session.workflow ?? session.workflows?.[0];
|
|
87664
88160
|
if (workflow) {
|
|
@@ -87684,7 +88180,7 @@ async function saveForm(context) {
|
|
|
87684
88180
|
}
|
|
87685
88181
|
}
|
|
87686
88182
|
async function handleWorkspaceKey(context, chunk, key) {
|
|
87687
|
-
const { state, render } = context;
|
|
88183
|
+
const { state, render, client: client2 } = context;
|
|
87688
88184
|
if (state.form) {
|
|
87689
88185
|
const form = state.form, field = form.fields[form.fieldIndex];
|
|
87690
88186
|
if (!field) {
|
|
@@ -87750,8 +88246,8 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87750
88246
|
return true;
|
|
87751
88247
|
}
|
|
87752
88248
|
if (state.workflowEdit) return false;
|
|
87753
|
-
const chatHome = state.screen === "start" || state.screen === "chats";
|
|
87754
|
-
if (["pageup", "pagedown", "home", "end", "scrollup", "scrolldown"].includes(key.name ?? "") || (
|
|
88249
|
+
const chatHome = state.screen === "start" || state.screen === "chats", carouselHome = chatHome || state.screen === "apps";
|
|
88250
|
+
if (["pageup", "pagedown", "home", "end", "scrollup", "scrolldown"].includes(key.name ?? "") || (carouselHome || state.screen === "project" && state.projectTab === "overview") && ["up", "down"].includes(key.name ?? "") && ["composer", "content"].includes(state.focus) && !state.input.startsWith("/")) {
|
|
87755
88251
|
const bottom = state.screen === "chat";
|
|
87756
88252
|
state.followSelection = false;
|
|
87757
88253
|
if (key.name === "home") state.scrollOffset = bottom ? 1e4 : 0;
|
|
@@ -87765,6 +88261,13 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87765
88261
|
return true;
|
|
87766
88262
|
}
|
|
87767
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
|
+
}
|
|
87768
88271
|
if (state.focus === "sidebar") {
|
|
87769
88272
|
state.sidebarOpen = false;
|
|
87770
88273
|
state.focus = chatHome ? "content" : state.workspace === "chats" ? "composer" : "content";
|
|
@@ -87824,8 +88327,9 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87824
88327
|
render();
|
|
87825
88328
|
return true;
|
|
87826
88329
|
}
|
|
87827
|
-
if (
|
|
87828
|
-
|
|
88330
|
+
if (carouselHome && state.focus === "content" && (key.name === "left" || key.name === "right")) {
|
|
88331
|
+
const count = chatHome ? homeChatItems(state).length : homeTuiApps(state.apps, state.filter, state.homeShowAll).length;
|
|
88332
|
+
state.selectedIndex = Math.max(0, Math.min(Math.max(0, count - 1), state.selectedIndex + (key.name === "left" ? -1 : 1)));
|
|
87829
88333
|
state.followSelection = true;
|
|
87830
88334
|
render();
|
|
87831
88335
|
return true;
|
|
@@ -87840,11 +88344,34 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87840
88344
|
if (chunk !== "/") return true;
|
|
87841
88345
|
}
|
|
87842
88346
|
if (state.focus === "sidebar") {
|
|
87843
|
-
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;
|
|
87844
88349
|
if (key.name === "up" || key.name === "down") state.sidebarIndex = Math.max(0, Math.min(count - 1, state.sidebarIndex + (key.name === "up" ? -1 : 1)));
|
|
87845
88350
|
else if (key.name === "return") {
|
|
87846
|
-
if (state.workspace === "chats")
|
|
87847
|
-
|
|
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}`);
|
|
87848
88375
|
else if (state.workspace === "tasks") await openTask(context, state.tasks[state.sidebarIndex]?.taskId ?? "");
|
|
87849
88376
|
else if (state.workspace === "workflows") await context.command(`/workflow ${state.workflows[state.sidebarIndex]?.id}`);
|
|
87850
88377
|
else if (state.workspace === "apps") await context.command(`/app ${state.apps[state.sidebarIndex]?.id}`);
|
|
@@ -87880,7 +88407,7 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87880
88407
|
}
|
|
87881
88408
|
const resultsTab = state.screen === "app" ? state.appTab === "embeds" : state.screen === "app-skill" && state.appSkillTab === "embeds";
|
|
87882
88409
|
const workflowsTab = state.screen === "app" ? state.appTab === "workflows" : state.screen === "app-skill" && state.appSkillTab === "workflows";
|
|
87883
|
-
const count = state.screen === "apps" ?
|
|
88410
|
+
const count = state.screen === "apps" ? homeTuiApps(state.apps, state.filter, state.homeShowAll).length : resultsTab ? state.appResults.items.length : workflowsTab ? state.appWorkflows.items.length : state.activeApp?.skills.length ?? 0;
|
|
87884
88411
|
if (key.name === "up" || key.name === "down") {
|
|
87885
88412
|
state.selectedIndex = Math.max(0, Math.min(count - 1, state.selectedIndex + (key.name === "up" ? -1 : 1)));
|
|
87886
88413
|
render();
|
|
@@ -87888,7 +88415,7 @@ async function handleWorkspaceKey(context, chunk, key) {
|
|
|
87888
88415
|
}
|
|
87889
88416
|
if (key.name === "return") {
|
|
87890
88417
|
if (state.screen === "apps") {
|
|
87891
|
-
const app =
|
|
88418
|
+
const app = homeTuiApps(state.apps, state.filter, state.homeShowAll)[state.selectedIndex];
|
|
87892
88419
|
if (app) await context.command(`/app ${app.id}`);
|
|
87893
88420
|
} else if (resultsTab) {
|
|
87894
88421
|
const result = state.appResults.items[state.selectedIndex];
|
|
@@ -88231,6 +88758,27 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88231
88758
|
let resolveResult = null;
|
|
88232
88759
|
let renderTimer = null;
|
|
88233
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);
|
|
88234
88782
|
const render = () => {
|
|
88235
88783
|
if (closed) return;
|
|
88236
88784
|
if (state.screen !== "chat") client2.clearInteractiveChatViewer();
|
|
@@ -88246,6 +88794,10 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88246
88794
|
};
|
|
88247
88795
|
const finish = (result) => {
|
|
88248
88796
|
closed = true;
|
|
88797
|
+
stopActivity?.();
|
|
88798
|
+
clearTimeout(activityTimer);
|
|
88799
|
+
clearInterval(activityPoll);
|
|
88800
|
+
clearInterval(activityAnimation);
|
|
88249
88801
|
client2.endInteractiveViewerSession();
|
|
88250
88802
|
if (renderTimer) clearTimeout(renderTimer);
|
|
88251
88803
|
renderTimer = null;
|
|
@@ -88262,6 +88814,19 @@ async function runTui(client2, terminal = new TuiTerminal()) {
|
|
|
88262
88814
|
});
|
|
88263
88815
|
render();
|
|
88264
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
|
+
}
|
|
88265
88830
|
return new Promise((resolve18) => {
|
|
88266
88831
|
resolveResult = resolve18;
|
|
88267
88832
|
});
|
|
@@ -88574,12 +89139,12 @@ async function sendTuiMessage(params) {
|
|
|
88574
89139
|
if (sourceExample) state.messages = sourceExample.messages.map((m) => ({ role: m.role === "user" ? "user" : "assistant", content: m.content, title: m.senderName }));
|
|
88575
89140
|
const messages = state.messages;
|
|
88576
89141
|
const existingChatId = sourceExample ? null : state.activeChatId;
|
|
88577
|
-
const chatId = existingChatId ??
|
|
89142
|
+
const chatId = existingChatId ?? randomUUID21();
|
|
88578
89143
|
const draftKey = sourceExample ? `example:${sourceExample.chat.id}` : existingChatId ?? "new";
|
|
88579
89144
|
state.drafts[draftKey] = "";
|
|
88580
89145
|
state.input = "";
|
|
88581
89146
|
state.inputCursor = null;
|
|
88582
|
-
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) }));
|
|
88583
89148
|
state.activeChatId = chatId;
|
|
88584
89149
|
state.headerState = existingChatId ? "ready" : "loading";
|
|
88585
89150
|
state.headerError = null;
|
|
@@ -89004,7 +89569,7 @@ function clamp(value, min, max) {
|
|
|
89004
89569
|
}
|
|
89005
89570
|
|
|
89006
89571
|
// src/codexTaskBridge.ts
|
|
89007
|
-
import { createHash as
|
|
89572
|
+
import { createHash as createHash23 } from "crypto";
|
|
89008
89573
|
import { spawn as spawn3 } from "child_process";
|
|
89009
89574
|
import { existsSync as existsSync15, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
|
|
89010
89575
|
import { isAbsolute as isAbsolute4, join as join18, resolve as resolve10 } from "path";
|
|
@@ -89047,7 +89612,7 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
|
|
|
89047
89612
|
const session = client2.getSession();
|
|
89048
89613
|
const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
|
|
89049
89614
|
const account = JSON.stringify(scope2);
|
|
89050
|
-
const directory = join18(stateRoot, "codex-chat-deletions",
|
|
89615
|
+
const directory = join18(stateRoot, "codex-chat-deletions", createHash23("sha256").update(account).digest("hex"));
|
|
89051
89616
|
if (!existsSync15(directory)) return;
|
|
89052
89617
|
const release2 = await lockfile5.lock(directory, { retries: 0, stale: 24e4 });
|
|
89053
89618
|
try {
|
|
@@ -89081,14 +89646,14 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
|
|
|
89081
89646
|
}
|
|
89082
89647
|
|
|
89083
89648
|
// src/taskCommandDelivery.ts
|
|
89084
|
-
import { createHash as
|
|
89649
|
+
import { createHash as createHash24 } from "crypto";
|
|
89085
89650
|
import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
|
|
89086
89651
|
import { join as join19 } from "path";
|
|
89087
89652
|
import lockfile6 from "proper-lockfile";
|
|
89088
89653
|
async function flushTaskCommands(client2, teamId, root = resolveStateDir(), onNotice) {
|
|
89089
89654
|
const session = client2.getSession();
|
|
89090
89655
|
const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
|
|
89091
|
-
const directory = join19(root, "task-command-delivery",
|
|
89656
|
+
const directory = join19(root, "task-command-delivery", createHash24("sha256").update(JSON.stringify(scope2)).digest("hex"));
|
|
89092
89657
|
if (!existsSync16(directory)) return;
|
|
89093
89658
|
const release2 = await lockfile6.lock(directory, { retries: 0, stale: 24e4 });
|
|
89094
89659
|
try {
|
|
@@ -89198,7 +89763,7 @@ import {
|
|
|
89198
89763
|
writeFileSync as writeFileSync13
|
|
89199
89764
|
} from "fs";
|
|
89200
89765
|
import { spawn as spawn7 } from "child_process";
|
|
89201
|
-
import { createHash as
|
|
89766
|
+
import { createHash as createHash30 } from "crypto";
|
|
89202
89767
|
import { extname as extname4, join as join26, resolve as resolve16, relative as relative11 } from "path";
|
|
89203
89768
|
|
|
89204
89769
|
// src/projectSourceRootPolicy.ts
|
|
@@ -89233,7 +89798,7 @@ function isEqualOrInside(parent, candidate) {
|
|
|
89233
89798
|
}
|
|
89234
89799
|
|
|
89235
89800
|
// src/projectPathPolicy.ts
|
|
89236
|
-
import { createHash as
|
|
89801
|
+
import { createHash as createHash25, randomBytes as randomBytes12 } from "crypto";
|
|
89237
89802
|
import {
|
|
89238
89803
|
chmodSync as chmodSync9,
|
|
89239
89804
|
closeSync as closeSync5,
|
|
@@ -89514,7 +90079,7 @@ function isInside2(root, candidate) {
|
|
|
89514
90079
|
return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
|
|
89515
90080
|
}
|
|
89516
90081
|
function sha2563(value) {
|
|
89517
|
-
return
|
|
90082
|
+
return createHash25("sha256").update(value).digest("hex");
|
|
89518
90083
|
}
|
|
89519
90084
|
function stableJson2(value) {
|
|
89520
90085
|
return JSON.stringify(value, (_key, item) => {
|
|
@@ -89524,7 +90089,7 @@ function stableJson2(value) {
|
|
|
89524
90089
|
}
|
|
89525
90090
|
|
|
89526
90091
|
// src/remoteFileWrites.ts
|
|
89527
|
-
import { createHash as
|
|
90092
|
+
import { createHash as createHash26, randomBytes as randomBytes13 } from "crypto";
|
|
89528
90093
|
import {
|
|
89529
90094
|
chmodSync as chmodSync10,
|
|
89530
90095
|
closeSync as closeSync7,
|
|
@@ -89789,7 +90354,7 @@ var MAX_FILE_BYTES2 = 200 * 1024;
|
|
|
89789
90354
|
var LOCK_ROOT = join22(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
|
|
89790
90355
|
var DEFAULT_JOURNAL_ROOT = join22(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
|
|
89791
90356
|
function sha256ProjectFile(content) {
|
|
89792
|
-
return
|
|
90357
|
+
return createHash26("sha256").update(content).digest("hex");
|
|
89793
90358
|
}
|
|
89794
90359
|
async function executeRemoteFileMutation(options) {
|
|
89795
90360
|
const mutation = validateMutation(options.mutation);
|
|
@@ -90477,13 +91042,13 @@ import { existsSync as existsSync23, realpathSync as realpathSync10, statSync as
|
|
|
90477
91042
|
import { basename as basename7, dirname as dirname13, relative as relative10, resolve as resolve15 } from "path";
|
|
90478
91043
|
|
|
90479
91044
|
// src/remoteCommandRuntime.ts
|
|
90480
|
-
import { createHash as
|
|
91045
|
+
import { createHash as createHash28, randomUUID as randomUUID22 } from "crypto";
|
|
90481
91046
|
import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
|
|
90482
91047
|
import { existsSync as existsSync21, lstatSync as lstatSync9, readdirSync as readdirSync12, realpathSync as realpathSync7, statSync as statSync6 } from "fs";
|
|
90483
91048
|
import { isAbsolute as isAbsolute8, join as join23, relative as relative7, resolve as resolve14, sep } from "path";
|
|
90484
91049
|
|
|
90485
91050
|
// src/remoteCommandAppArmor.ts
|
|
90486
|
-
import { createHash as
|
|
91051
|
+
import { createHash as createHash27 } from "crypto";
|
|
90487
91052
|
import { spawn as spawn5 } from "child_process";
|
|
90488
91053
|
import { lstatSync as lstatSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
|
|
90489
91054
|
import { isAbsolute as isAbsolute7, relative as relative6 } from "path";
|
|
@@ -90700,7 +91265,7 @@ function hasControl2(value) {
|
|
|
90700
91265
|
});
|
|
90701
91266
|
}
|
|
90702
91267
|
function sha2564(value) {
|
|
90703
|
-
return
|
|
91268
|
+
return createHash27("sha256").update(value).digest("hex");
|
|
90704
91269
|
}
|
|
90705
91270
|
|
|
90706
91271
|
// src/remoteCommandRuntime.ts
|
|
@@ -91104,7 +91669,7 @@ var RemoteCommandRuntime = class {
|
|
|
91104
91669
|
};
|
|
91105
91670
|
function createRemoteCommandPreflight(request) {
|
|
91106
91671
|
if (!request || typeof request !== "object") throw new RemoteCommandError("invalid_request", "Command request must be an object");
|
|
91107
|
-
const executionId = request.execution_id ??
|
|
91672
|
+
const executionId = request.execution_id ?? randomUUID22();
|
|
91108
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");
|
|
91109
91674
|
if (typeof request.project_id !== "string" || !request.project_id || request.project_id.length > 256) throw new RemoteCommandError("invalid_request", "Invalid Project ID");
|
|
91110
91675
|
if (typeof request.source_root !== "string" || !request.source_root) throw new RemoteCommandError("invalid_request", "Invalid Project source root");
|
|
@@ -91541,7 +92106,7 @@ function stableJson4(value) {
|
|
|
91541
92106
|
return JSON.stringify(value);
|
|
91542
92107
|
}
|
|
91543
92108
|
function sha2565(value) {
|
|
91544
|
-
return
|
|
92109
|
+
return createHash28("sha256").update(value).digest("hex");
|
|
91545
92110
|
}
|
|
91546
92111
|
function deepFreeze(value) {
|
|
91547
92112
|
if (value && typeof value === "object") {
|
|
@@ -91819,7 +92384,7 @@ function capabilityError(message) {
|
|
|
91819
92384
|
}
|
|
91820
92385
|
|
|
91821
92386
|
// src/remoteCommandResourceGrants.ts
|
|
91822
|
-
import { createHash as
|
|
92387
|
+
import { createHash as createHash29 } from "crypto";
|
|
91823
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";
|
|
91824
92389
|
import { isAbsolute as isAbsolute10, join as join25, relative as relative9 } from "path";
|
|
91825
92390
|
var STORE_FILE = "remote-command-resource-grants.json";
|
|
@@ -91914,7 +92479,7 @@ function resolveRemoteCommandResourceGrants(context) {
|
|
|
91914
92479
|
return { writable_targets, network_profiles, credential_profiles };
|
|
91915
92480
|
}
|
|
91916
92481
|
function definitionDigest(kind, definition) {
|
|
91917
|
-
return
|
|
92482
|
+
return createHash29("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
|
|
91918
92483
|
}
|
|
91919
92484
|
function upsert(grant) {
|
|
91920
92485
|
validateIdentity(grant.project_id, grant.source_id, grant.profile_id);
|
|
@@ -93084,7 +93649,7 @@ function readRemoteAccessTextFile(options) {
|
|
|
93084
93649
|
truncated,
|
|
93085
93650
|
sizeBytes: stats.size,
|
|
93086
93651
|
lineCount: countTextLines(content),
|
|
93087
|
-
expected_base: truncated ? null :
|
|
93652
|
+
expected_base: truncated ? null : createHash30("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
|
|
93088
93653
|
};
|
|
93089
93654
|
} catch (error) {
|
|
93090
93655
|
const code = error.code;
|
|
@@ -93134,7 +93699,7 @@ function readRemoteAccessImageChunk(options) {
|
|
|
93134
93699
|
mime_type: mimeType,
|
|
93135
93700
|
size_bytes: stats.size,
|
|
93136
93701
|
offset: options.offset,
|
|
93137
|
-
content_hash:
|
|
93702
|
+
content_hash: createHash30("sha256").update(bytes2).digest("hex")
|
|
93138
93703
|
};
|
|
93139
93704
|
} catch (error) {
|
|
93140
93705
|
if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
|
|
@@ -93182,8 +93747,8 @@ function readRemoteAccessFileChunk(options) {
|
|
|
93182
93747
|
content_base64: bytes2.toString("base64"),
|
|
93183
93748
|
size_bytes: stats.size,
|
|
93184
93749
|
offset: options.offset,
|
|
93185
|
-
file_identity:
|
|
93186
|
-
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")
|
|
93187
93752
|
};
|
|
93188
93753
|
} catch (error) {
|
|
93189
93754
|
if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
|
|
@@ -94745,7 +95310,7 @@ function writeWorkRecoveryAtomically(path2, document) {
|
|
|
94745
95310
|
|
|
94746
95311
|
// src/revolutBusinessCertificate.ts
|
|
94747
95312
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
94748
|
-
import { createSign, randomUUID as
|
|
95313
|
+
import { createSign, randomUUID as randomUUID23 } from "crypto";
|
|
94749
95314
|
import { chmodSync as chmodSync14, existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync23 } from "fs";
|
|
94750
95315
|
import { homedir as homedir8 } from "os";
|
|
94751
95316
|
import { join as join28, resolve as resolve17 } from "path";
|
|
@@ -94819,7 +95384,7 @@ function generateRevolutBusinessClientAssertion(options) {
|
|
|
94819
95384
|
aud: REVOLUT_BUSINESS_AUDIENCE,
|
|
94820
95385
|
exp: now + 3600,
|
|
94821
95386
|
iat: now,
|
|
94822
|
-
jti:
|
|
95387
|
+
jti: randomUUID23()
|
|
94823
95388
|
});
|
|
94824
95389
|
const unsigned = `${header2}.${payload}`;
|
|
94825
95390
|
const signer = createSign("RSA-SHA256");
|
|
@@ -96291,7 +96856,7 @@ async function resolveAddToProjectStorageChoice(client2, project, flags, objectT
|
|
|
96291
96856
|
async function createEncryptedProjectItem(client2, project, input) {
|
|
96292
96857
|
const timestamp = nowSeconds3();
|
|
96293
96858
|
return client2.createProjectItem(project.projectId, {
|
|
96294
|
-
project_item_id:
|
|
96859
|
+
project_item_id: randomUUID24(),
|
|
96295
96860
|
folder_id: input.folderId ?? null,
|
|
96296
96861
|
item_type: input.itemType,
|
|
96297
96862
|
target_id: input.targetId,
|
|
@@ -96490,7 +97055,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
|
|
|
96490
97055
|
const encryptedSources = proof.length ? await encryptPlanAssumptionSources(plan, masterKey, proof) : void 0;
|
|
96491
97056
|
if (action === "create" || action === "add") {
|
|
96492
97057
|
const assumption = await client2.createPlanAssumption(plan.planId, {
|
|
96493
|
-
assumption_id: typeof flags.id === "string" ? flags.id :
|
|
97058
|
+
assumption_id: typeof flags.id === "string" ? flags.id : randomUUID24(),
|
|
96494
97059
|
encrypted_text: await encryptPlanAssumptionSources(plan, masterKey, requiredStringFlag(flags.text ?? rest.slice(2).join(" "), "--text <assumption>")),
|
|
96495
97060
|
status: typeof flags.status === "string" ? flags.status : void 0,
|
|
96496
97061
|
linked_sub_chat_id: typeof flags["sub-chat"] === "string" ? flags["sub-chat"] : void 0,
|
|
@@ -96532,7 +97097,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
|
|
|
96532
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.");
|
|
96533
97098
|
if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
|
|
96534
97099
|
const snapshot2 = JSON.stringify(planToJson(plan));
|
|
96535
|
-
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() });
|
|
96536
97101
|
const reviewUrl = `${deriveAppUrl(client2.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
|
|
96537
97102
|
if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
|
|
96538
97103
|
else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
|
|
@@ -97426,7 +97991,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97426
97991
|
const writeMode = await resolveProjectWriteMode(flags, `Choose how OpenMates may change files in ${JSON.stringify(name)}:`);
|
|
97427
97992
|
const projectKey = randomBytes14(32);
|
|
97428
97993
|
const wrapping = await client2.projectWrappingKey(context);
|
|
97429
|
-
const projectId =
|
|
97994
|
+
const projectId = randomUUID24();
|
|
97430
97995
|
const timestamp = nowSeconds3();
|
|
97431
97996
|
const defaultFocus = buildProjectDefaultFocus(name);
|
|
97432
97997
|
const slugMetadata = await buildEncryptedObjectSlugMetadata({
|
|
@@ -97452,7 +98017,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97452
98017
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
|
|
97453
98018
|
key_wrappers: wrapping.teamId ? [{
|
|
97454
98019
|
key_type: "team",
|
|
97455
|
-
hashed_team_id:
|
|
98020
|
+
hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
|
|
97456
98021
|
team_key_epoch: 1,
|
|
97457
98022
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
97458
98023
|
wrapper_version: 1,
|
|
@@ -97692,7 +98257,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
97692
98257
|
const proposal = isShortWorkspaceAsk(instruction) ? { name: instruction, description: "", icon: "folder", color: "default" } : extractRecord(await client2.planProjectAsk({ instruction }), "proposed_project");
|
|
97693
98258
|
const projectKey = randomBytes14(32);
|
|
97694
98259
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
97695
|
-
const projectId =
|
|
98260
|
+
const projectId = randomUUID24();
|
|
97696
98261
|
const name = requiredString(proposal, "name", instruction);
|
|
97697
98262
|
const slugMetadata = await buildEncryptedObjectSlugMetadata({
|
|
97698
98263
|
value: typeof flags.slug === "string" ? flags.slug : name,
|
|
@@ -97815,8 +98380,8 @@ async function handleProjectFiles(client2, masterKey, rest, flags, context) {
|
|
|
97815
98380
|
...flags["include-ignored"] === true ? {
|
|
97816
98381
|
approvedIgnoredRead: {
|
|
97817
98382
|
path: String(argumentsValue.path),
|
|
97818
|
-
chatId: `cli-direct-${
|
|
97819
|
-
operationId: `cli-direct-${
|
|
98383
|
+
chatId: `cli-direct-${randomUUID24()}`,
|
|
98384
|
+
operationId: `cli-direct-${randomUUID24()}`
|
|
97820
98385
|
}
|
|
97821
98386
|
} : {}
|
|
97822
98387
|
});
|
|
@@ -98604,7 +99169,7 @@ async function handleRemoteAccess(client2, subcommand, rest, flags) {
|
|
|
98604
99169
|
const projects = await loadProjects(client2, masterKey, hostingFlags, hostingContext);
|
|
98605
99170
|
const bindings = await resolveRemoteAccessBindings(client2, masterKey, projects, candidateRoots, hostingFlags, hostingContext);
|
|
98606
99171
|
if (bindings.length === 0) throw new Error("No Project folders were approved for remote access.");
|
|
98607
|
-
const sourceSessionId =
|
|
99172
|
+
const sourceSessionId = randomUUID24();
|
|
98608
99173
|
const controller = new AbortController();
|
|
98609
99174
|
const stop = () => controller.abort();
|
|
98610
99175
|
process.once("SIGINT", stop);
|
|
@@ -98758,7 +99323,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
|
|
|
98758
99323
|
updated_at: nowSeconds3()
|
|
98759
99324
|
}, context);
|
|
98760
99325
|
}
|
|
98761
|
-
resolved.push({ rootPath, project, sourceId:
|
|
99326
|
+
resolved.push({ rootPath, project, sourceId: randomUUID24() });
|
|
98762
99327
|
}
|
|
98763
99328
|
}
|
|
98764
99329
|
const bindings = [];
|
|
@@ -98802,7 +99367,7 @@ async function resolveRemoteAccessBindings(client2, masterKey, projects, candida
|
|
|
98802
99367
|
}
|
|
98803
99368
|
async function createEncryptedRemoteAccessProject(client2, name, rootPath, writeMode, context) {
|
|
98804
99369
|
const projectKey = randomBytes14(32);
|
|
98805
|
-
const projectId =
|
|
99370
|
+
const projectId = randomUUID24();
|
|
98806
99371
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
98807
99372
|
const wrapping = await client2.projectWrappingKey(context);
|
|
98808
99373
|
const importedInstructions = readRootProjectInstructions(rootPath);
|
|
@@ -98828,7 +99393,7 @@ async function createEncryptedRemoteAccessProject(client2, name, rootPath, write
|
|
|
98828
99393
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
|
|
98829
99394
|
key_wrappers: wrapping.teamId ? [{
|
|
98830
99395
|
key_type: "team",
|
|
98831
|
-
hashed_team_id:
|
|
99396
|
+
hashed_team_id: createHash31("sha256").update(wrapping.teamId).digest("hex"),
|
|
98832
99397
|
team_key_epoch: 1,
|
|
98833
99398
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
98834
99399
|
wrapper_version: 1,
|
|
@@ -98886,7 +99451,7 @@ async function ensureProjectDefaultFocus(client2, project, context, rootPath) {
|
|
|
98886
99451
|
function buildProjectDefaultFocus(projectName, instructions, source = "generated") {
|
|
98887
99452
|
const name = projectName.trim() || "Untitled project";
|
|
98888
99453
|
return {
|
|
98889
|
-
focus_id:
|
|
99454
|
+
focus_id: randomUUID24(),
|
|
98890
99455
|
name: `Work on ${name}`,
|
|
98891
99456
|
instructions: instructions?.trim() || `Help with work in ${name}. Follow the user's instructions and the Project's connected source guidance.`,
|
|
98892
99457
|
source
|
|
@@ -100219,7 +100784,7 @@ function normalizeApiKeyLoadedChat(payload) {
|
|
|
100219
100784
|
const messages = rawMessages.map((message) => {
|
|
100220
100785
|
const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
|
|
100221
100786
|
const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
|
|
100222
|
-
const hashedMessageId = messageId ?
|
|
100787
|
+
const hashedMessageId = messageId ? createHash31("sha256").update(messageId).digest("hex") : null;
|
|
100223
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) : [];
|
|
100224
100789
|
return {
|
|
100225
100790
|
id: messageId,
|
|
@@ -102416,7 +102981,7 @@ async function runAccountImport(client2, parserFormat, file, flags) {
|
|
|
102416
102981
|
if (flags.yes !== true) {
|
|
102417
102982
|
await confirmOrExit(`Import ${selectedCount} chat(s) into new encrypted OpenMates chats? [y/N] `);
|
|
102418
102983
|
}
|
|
102419
|
-
const importId = typeof preview.import_id === "string" ? preview.import_id :
|
|
102984
|
+
const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID24();
|
|
102420
102985
|
const selectedChats = parsed.chats.slice(0, selectedCount);
|
|
102421
102986
|
const selectedFingerprints = selectedChats.map((chat) => chat.source_fingerprint);
|
|
102422
102987
|
const confirmation = await client2.confirmAccountImport(importId, selectedFingerprints);
|
|
@@ -102910,8 +103475,8 @@ async function handleFinance(client2, subcommand, _rest, flags) {
|
|
|
102910
103475
|
refresh_token_envelope: connectedAccount.refreshTokenBundle
|
|
102911
103476
|
}
|
|
102912
103477
|
] : [],
|
|
102913
|
-
chatId: `cli-finance-${
|
|
102914
|
-
messageId: `cli-finance-message-${
|
|
103478
|
+
chatId: `cli-finance-${randomUUID24()}`,
|
|
103479
|
+
messageId: `cli-finance-message-${randomUUID24()}`,
|
|
102915
103480
|
apiKey: resolveApiKey(flags) ?? void 0,
|
|
102916
103481
|
promptInjectionProtection: false
|
|
102917
103482
|
});
|