@linzumi/cli 0.0.68-beta → 0.0.69-beta

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +498 -91
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -58,7 +58,7 @@ Install the CLI or run it with `npx`:
58
58
  ```bash
59
59
  npm install -g @linzumi/cli@latest
60
60
  npx -y @linzumi/cli@latest signup
61
- npx -y @linzumi/cli@0.0.68-beta --version
61
+ npx -y @linzumi/cli@0.0.69-beta --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -286,6 +286,7 @@ var init_channelSessionSupport = __esm({
286
286
 
287
287
  // src/commanderAttachments.ts
288
288
  import { readFile, realpath, stat, writeFile, mkdir } from "node:fs/promises";
289
+ import { homedir } from "node:os";
289
290
  import {
290
291
  basename,
291
292
  extname,
@@ -402,13 +403,14 @@ async function uploadedFileIdsForCommanderOutput(runtime, body, structured) {
402
403
  return [];
403
404
  }
404
405
  const cwdRealPath = await realpath(runtime.cwd);
406
+ const uploadRoots = await commanderAttachmentUploadRoots(cwdRealPath);
405
407
  const paths = await extractKandanAttachmentPaths(body, cwdRealPath);
406
408
  if (paths.length === 0) {
407
409
  return [];
408
410
  }
409
411
  const files = await Promise.all(
410
412
  paths.map(async (path2) => {
411
- assertCommanderAttachmentUploadAllowed(cwdRealPath, path2);
413
+ assertCommanderAttachmentUploadAllowed(uploadRoots, path2);
412
414
  const info = await stat(path2);
413
415
  if (!info.isFile()) {
414
416
  throw new Error(`Kandan attachment path is not a file: ${path2}`);
@@ -556,13 +558,19 @@ async function extractKandanAttachmentPaths(body, cwdRealPath) {
556
558
  const paths = [];
557
559
  for (const line of lines.slice(headingIndex + 1)) {
558
560
  const trimmed = line.trim();
559
- if (!trimmed.startsWith("- ")) {
560
- if (trimmed !== "") {
561
- break;
562
- }
561
+ const rawPath = kandanAttachmentRawPath(trimmed);
562
+ if (rawPath === "") {
563
563
  continue;
564
564
  }
565
- const rawPath = trimmed.slice(2).trim().replace(/^`|`$/g, "");
565
+ if (rawPath === void 0) {
566
+ break;
567
+ }
568
+ if (commanderAttachmentRawPathHasHiddenSegment(rawPath)) {
569
+ continue;
570
+ }
571
+ if (!isCommanderAttachmentUploadPathCandidate(rawPath)) {
572
+ break;
573
+ }
566
574
  const resolvedPath = await resolveCommanderAttachmentPath(
567
575
  cwdRealPath,
568
576
  rawPath
@@ -575,27 +583,34 @@ async function extractKandanAttachmentPaths(body, cwdRealPath) {
575
583
  }
576
584
  return paths;
577
585
  }
586
+ function kandanAttachmentRawPath(trimmedLine) {
587
+ if (trimmedLine === "") {
588
+ return "";
589
+ }
590
+ const rawPath = trimmedLine.startsWith("- ") ? trimmedLine.slice(2).trim() : trimmedLine;
591
+ if (rawPath === "") {
592
+ return void 0;
593
+ }
594
+ return rawPath.replace(/^`|`$/g, "");
595
+ }
578
596
  async function resolveCommanderAttachmentPath(cwdRealPath, rawPath) {
579
597
  if (rawPath === "") {
580
598
  throw new Error("Kandan attachment path must not be empty");
581
599
  }
582
600
  const candidatePath = isAbsolute(rawPath) ? resolve(rawPath) : resolve(cwdRealPath, rawPath);
583
- const path2 = await realpath(candidatePath);
584
- const relativePath = relative(cwdRealPath, path2);
585
- if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
586
- throw new Error(
587
- `Kandan attachment path must resolve inside the runner cwd: ${rawPath}`
588
- );
589
- }
590
- return path2;
601
+ return await realpath(candidatePath);
591
602
  }
592
- function assertCommanderAttachmentUploadAllowed(cwdRealPath, filePath) {
593
- const relativePath = relative(cwdRealPath, filePath);
594
- if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
603
+ function assertCommanderAttachmentUploadAllowed(uploadRoots, filePath) {
604
+ const matchedRoot = uploadRoots.find(
605
+ (root) => pathIsInsideRoot(root.path, filePath)
606
+ );
607
+ if (matchedRoot === void 0) {
608
+ const rootLabels = uploadRoots.map((root) => root.label).join(", ");
595
609
  throw new Error(
596
- `Kandan attachment path must resolve inside the runner cwd: ${filePath}`
610
+ `Kandan attachment path must resolve inside an allowed upload root (${rootLabels}): ${filePath}`
597
611
  );
598
612
  }
613
+ const relativePath = relative(matchedRoot.path, filePath);
599
614
  const hiddenSegment = relativePath.split(/[\\/]+/).find((segment) => segment.startsWith("."));
600
615
  if (hiddenSegment !== void 0) {
601
616
  throw new Error(
@@ -609,6 +624,25 @@ function assertCommanderAttachmentUploadAllowed(cwdRealPath, filePath) {
609
624
  );
610
625
  }
611
626
  }
627
+ async function commanderAttachmentUploadRoots(cwdRealPath) {
628
+ const roots = [
629
+ { path: cwdRealPath, label: "runner cwd" }
630
+ ];
631
+ const downloadsPath = resolve(homedir(), "Downloads");
632
+ try {
633
+ const downloadsRealPath = await realpath(downloadsPath);
634
+ if (!roots.some((root) => root.path === downloadsRealPath)) {
635
+ roots.push({ path: downloadsRealPath, label: "~/Downloads" });
636
+ }
637
+ } catch (_error) {
638
+ return roots;
639
+ }
640
+ return roots;
641
+ }
642
+ function pathIsInsideRoot(rootPath, filePath) {
643
+ const relativePath = relative(rootPath, filePath);
644
+ return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
645
+ }
612
646
  function contentTypeForFileName(fileName) {
613
647
  const lower = fileName.toLowerCase();
614
648
  if (lower.endsWith(".png")) {
@@ -634,6 +668,69 @@ function contentTypeForFileName(fileName) {
634
668
  }
635
669
  return "application/octet-stream";
636
670
  }
671
+ function bodyWithoutKandanAttachmentFooter(body) {
672
+ const lines = body.split(/\r?\n/);
673
+ const headingIndex = lines.findIndex(
674
+ (line) => line.trim().toLowerCase() === "kandan attachments:"
675
+ );
676
+ if (headingIndex === -1) {
677
+ return body;
678
+ }
679
+ const visibleAfterLines = [];
680
+ let sawVisibleAfterLine = false;
681
+ for (const line of lines.slice(headingIndex + 1)) {
682
+ if (line.trim() === "") {
683
+ switch (sawVisibleAfterLine) {
684
+ case true:
685
+ visibleAfterLines.push(line);
686
+ break;
687
+ case false:
688
+ break;
689
+ }
690
+ continue;
691
+ }
692
+ if (isKandanAttachmentDisplayDirectiveLine(line)) {
693
+ continue;
694
+ }
695
+ visibleAfterLines.push(line);
696
+ sawVisibleAfterLine = true;
697
+ }
698
+ const visibleLines = [...lines.slice(0, headingIndex), ...visibleAfterLines];
699
+ while (visibleLines.length > 0 && visibleLines[visibleLines.length - 1]?.trim() === "") {
700
+ visibleLines.pop();
701
+ }
702
+ return visibleLines.join("\n");
703
+ }
704
+ function displayBodyForCommanderOutput(body, uploadedFileIds) {
705
+ const displayBody = bodyWithoutKandanAttachmentFooter(body);
706
+ switch (displayBody.trim() === "" && uploadedFileIds.length > 0) {
707
+ case true:
708
+ return "Attached file.";
709
+ case false:
710
+ return displayBody;
711
+ }
712
+ }
713
+ function structuredWithAssistantContent(structured, content) {
714
+ return stringValue(structured.kind) === "codex_assistant_message" ? { ...structured, content } : structured;
715
+ }
716
+ function isKandanAttachmentDisplayDirectiveLine(line) {
717
+ const rawPath = kandanAttachmentRawPath(line.trim());
718
+ if (rawPath === "") {
719
+ return true;
720
+ }
721
+ if (rawPath === void 0) {
722
+ return false;
723
+ }
724
+ return !commanderAttachmentRawPathHasHiddenSegment(rawPath) && commanderAttachmentUploadExtensions.has(extname(rawPath).toLowerCase());
725
+ }
726
+ function isCommanderAttachmentUploadPathCandidate(rawPath) {
727
+ return commanderAttachmentUploadExtensions.has(
728
+ extname(rawPath).toLowerCase()
729
+ );
730
+ }
731
+ function commanderAttachmentRawPathHasHiddenSegment(rawPath) {
732
+ return rawPath.split(/[\\/]+/).some((segment) => segment.startsWith("."));
733
+ }
637
734
  async function pushOk(kandan, topic, event, payload) {
638
735
  const reply = await kandan.push(topic, event, payload);
639
736
  if (isJsonObject(reply) && reply.status === "ok" && isJsonObject(reply.response)) {
@@ -754,6 +851,7 @@ var init_codexRuntimeOptions = __esm({
754
851
 
755
852
  // src/codexOutput.ts
756
853
  import { Buffer as Buffer2 } from "node:buffer";
854
+ import { fileURLToPath } from "node:url";
757
855
  function codexOutputMessagesForTurn(response, turnId) {
758
856
  if ("error" in response) {
759
857
  return [];
@@ -803,19 +901,10 @@ function codexOutputMessagesForItem(item, index) {
803
901
  }
804
902
  ];
805
903
  }
806
- case "agentMessage": {
807
- const text2 = assistantVisibleText(item);
808
- return [
809
- {
810
- itemKey,
811
- body: text2,
812
- structured: baseStructured("codex_assistant_message", {
813
- content: text2,
814
- phase: stringValue(item.phase) ?? "final_answer"
815
- })
816
- }
817
- ];
818
- }
904
+ case "agentMessage":
905
+ return codexAssistantMessageForItem(item, itemKey, baseStructured);
906
+ case "message":
907
+ return stringValue(item.role) === "assistant" ? codexAssistantMessageForItem(item, itemKey, baseStructured) : [];
819
908
  case "commandExecution": {
820
909
  const command = stringValue(item.command) ?? "command";
821
910
  const output = nonBlankStringValue(item.aggregatedOutput) ?? "";
@@ -1121,7 +1210,26 @@ function codexUserInputMessageForItem(item, index) {
1121
1210
  const body = textParts.length > 0 ? textParts.join("\n\n") : nonBlankStringValue(item.text) ?? nonBlankStringValue(item.message) ?? "";
1122
1211
  return [{ itemKey, body }];
1123
1212
  }
1213
+ function codexAssistantMessageForItem(item, itemKey, baseStructured) {
1214
+ const text2 = assistantVisibleText(item);
1215
+ const attachmentPaths = assistantImageAttachmentPaths(item);
1216
+ const body = assistantBodyWithAttachmentPaths(text2, attachmentPaths);
1217
+ return [
1218
+ {
1219
+ itemKey,
1220
+ body,
1221
+ structured: baseStructured("codex_assistant_message", {
1222
+ content: text2,
1223
+ phase: stringValue(item.phase) ?? "final_answer"
1224
+ })
1225
+ }
1226
+ ];
1227
+ }
1124
1228
  function assistantVisibleText(item) {
1229
+ const contentText = assistantContentText(item);
1230
+ if (contentText !== "") {
1231
+ return contentText;
1232
+ }
1125
1233
  const direct = stringValue(item.reply) ?? stringValue(item.summary) ?? stringValue(item.content) ?? stringValue(item.text) ?? "";
1126
1234
  const parsed = parseJsonObjectOrUndefined(direct);
1127
1235
  if (parsed === void 0) {
@@ -1129,6 +1237,52 @@ function assistantVisibleText(item) {
1129
1237
  }
1130
1238
  return stringValue(parsed.reply) ?? stringValue(parsed.summary) ?? stringValue(parsed.content) ?? stringValue(parsed.message) ?? stringValue(parsed.output_text) ?? direct;
1131
1239
  }
1240
+ function assistantContentText(item) {
1241
+ return (arrayValue(item.content) ?? []).filter(isJsonObject).flatMap((part) => {
1242
+ const type = stringValue(part.type);
1243
+ if (type !== "text" && type !== "output_text" && type !== "input_text") {
1244
+ return [];
1245
+ }
1246
+ const text2 = nonBlankStringValue(part.text) ?? nonBlankStringValue(part.content) ?? nonBlankStringValue(part.output_text);
1247
+ return text2 === void 0 ? [] : [text2];
1248
+ }).join("\n\n");
1249
+ }
1250
+ function assistantBodyWithAttachmentPaths(text2, attachmentPaths) {
1251
+ const uniqueAttachmentPaths = [...new Set(attachmentPaths)];
1252
+ if (uniqueAttachmentPaths.length === 0) {
1253
+ return text2;
1254
+ }
1255
+ const attachmentFooter = [
1256
+ "Kandan attachments:",
1257
+ ...uniqueAttachmentPaths.map((path2) => `- ${path2}`)
1258
+ ].join("\n");
1259
+ const normalizedText = text2.trim();
1260
+ return normalizedText === "" ? attachmentFooter : [text2, "", attachmentFooter].join("\n");
1261
+ }
1262
+ function assistantImageAttachmentPaths(item) {
1263
+ return (arrayValue(item.content) ?? []).filter(isJsonObject).flatMap((part) => {
1264
+ const type = stringValue(part.type)?.toLowerCase();
1265
+ if (type !== "image" && type !== "output_image" && type !== "input_image") {
1266
+ return [];
1267
+ }
1268
+ const rawPath = stringValue(part.path) ?? stringValue(part.file_path) ?? stringValue(part.filePath) ?? stringValue(part.image_url) ?? stringValue(part.imageUrl) ?? stringValue(part.url);
1269
+ const localPath = rawPath === void 0 ? void 0 : localAttachmentPath(rawPath);
1270
+ return localPath === void 0 ? [] : [localPath];
1271
+ });
1272
+ }
1273
+ function localAttachmentPath(rawPath) {
1274
+ if (/^https?:\/\//i.test(rawPath) || /^data:/i.test(rawPath)) {
1275
+ return void 0;
1276
+ }
1277
+ if (/^file:\/\//i.test(rawPath)) {
1278
+ try {
1279
+ return fileURLToPath(rawPath);
1280
+ } catch (_error) {
1281
+ return void 0;
1282
+ }
1283
+ }
1284
+ return rawPath;
1285
+ }
1132
1286
  function assistantDeltaText(params) {
1133
1287
  return textDeltaFromParams(params);
1134
1288
  }
@@ -3333,6 +3487,32 @@ async function attachChannelSession(args) {
3333
3487
  case "item/completed":
3334
3488
  enqueueWebSearchProgress(args, state, params, payloadContext);
3335
3489
  enqueueCompletedFileChange(args, state, params, payloadContext);
3490
+ const completedAssistantItemForward = forwardCompletedAssistantItem(
3491
+ args,
3492
+ state,
3493
+ params,
3494
+ payloadContext
3495
+ );
3496
+ if (turnId !== void 0) {
3497
+ rememberPendingCompletedAssistantItemForward(
3498
+ state,
3499
+ turnId,
3500
+ completedAssistantItemForward
3501
+ );
3502
+ }
3503
+ void completedAssistantItemForward.catch((error) => {
3504
+ args.log("codex.completed_assistant_item_forward_failed", {
3505
+ message: error instanceof Error ? error.message : String(error)
3506
+ });
3507
+ }).finally(() => {
3508
+ if (turnId !== void 0) {
3509
+ forgetPendingCompletedAssistantItemForward(
3510
+ state,
3511
+ turnId,
3512
+ completedAssistantItemForward
3513
+ );
3514
+ }
3515
+ });
3336
3516
  if (turnId !== void 0) {
3337
3517
  const promise = mirrorLocalTuiInputFromNotification(
3338
3518
  args,
@@ -3426,6 +3606,8 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
3426
3606
  forwardedTurnIds: /* @__PURE__ */ new Set(),
3427
3607
  forwardingTurnIds: /* @__PURE__ */ new Set(),
3428
3608
  retryableTurnIds: /* @__PURE__ */ new Set(),
3609
+ completedAssistantItemKeys: /* @__PURE__ */ new Set(),
3610
+ pendingCompletedAssistantItemForwards: /* @__PURE__ */ new Map(),
3429
3611
  claimedKandanMessageKeys: /* @__PURE__ */ new Set(),
3430
3612
  localTuiTurnIds: /* @__PURE__ */ new Set(),
3431
3613
  mirroredTuiInputProjections: createBoundedCache(maxForwardedTurnIds),
@@ -5014,6 +5196,7 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
5014
5196
  };
5015
5197
  }
5016
5198
  await waitForPendingTuiInputMirror(state, turnId);
5199
+ await waitForPendingCompletedAssistantItemForwards(state, turnId);
5017
5200
  await waitForStreamingForwardChains(args, state, payloadContext);
5018
5201
  rememberForwardingTurnId(state, turnId);
5019
5202
  forgetRetryableTurnId(state, turnId);
@@ -5124,9 +5307,17 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
5124
5307
  }
5125
5308
  }
5126
5309
  function completedOutputProjectionsForTurn(state, turnId, messages) {
5127
- const snapshotOutputs = messages.flatMap(
5128
- (message, snapshotIndex) => completedSnapshotOutputProjection(state, turnId, message, snapshotIndex)
5129
- );
5310
+ const snapshotOutputs = messages.flatMap((message, snapshotIndex) => {
5311
+ if (completedAssistantItemForwarded(state, message.itemKey)) {
5312
+ return [];
5313
+ }
5314
+ return completedSnapshotOutputProjection(
5315
+ state,
5316
+ turnId,
5317
+ message,
5318
+ snapshotIndex
5319
+ );
5320
+ });
5130
5321
  const matchedStructuredKeys = new Set(
5131
5322
  snapshotOutputs.flatMap((output) => {
5132
5323
  switch (output.match.kind) {
@@ -5395,6 +5586,97 @@ function logCompletedCodexOutput(args, turnId, message) {
5395
5586
  file_paths: fileChangePaths(message.structured)
5396
5587
  });
5397
5588
  }
5589
+ async function forwardCompletedAssistantItem(args, state, params, payloadContext) {
5590
+ if (state.kandanThreadId === void 0 || state.codexThreadId === void 0) {
5591
+ return;
5592
+ }
5593
+ const turnId = stringValue(params.turnId) ?? stringValue(params.turn_id) ?? stringValue(objectValue(params.turn)?.id);
5594
+ const item = objectValue(params.item) ?? params;
5595
+ const completedItemKey = stringValue(params.itemId) ?? stringValue(params.item_id) ?? stringValue(objectValue(params.item)?.id);
5596
+ if (turnId !== void 0) {
5597
+ await waitForPendingTuiInputMirror(state, turnId);
5598
+ await waitForStreamingForwardChains(args, state, payloadContext);
5599
+ }
5600
+ const parsedMessage = codexOutputMessagesForItem(item, 0).find(
5601
+ (candidate) => stringValue(candidate.structured.kind) === "codex_assistant_message"
5602
+ );
5603
+ const fallbackStream = parsedMessage === void 0 ? resolveStreamingAssistantOutputForCompletedSignal(
5604
+ state,
5605
+ turnId,
5606
+ completedItemKey
5607
+ ) : { status: "none" };
5608
+ if (fallbackStream.status === "ambiguous") {
5609
+ throw new LogicalProjectionError(
5610
+ `Cannot reconcile completed assistant item signal from item notification; ${fallbackStream.candidateCount} active streamed assistant outputs exist`
5611
+ );
5612
+ }
5613
+ const message = parsedMessage ?? (fallbackStream.status === "matched" ? {
5614
+ itemKey: fallbackStream.output.itemKey,
5615
+ body: fallbackStream.output.content,
5616
+ structured: codexAssistantStructuredMessage(
5617
+ fallbackStream.output.itemKey,
5618
+ fallbackStream.output.content,
5619
+ "completed"
5620
+ )
5621
+ } : void 0);
5622
+ const messageTurnId = turnId ?? (fallbackStream.status === "matched" ? fallbackStream.output.turnId : void 0);
5623
+ if (message === void 0 || completedAssistantItemForwarded(state, message.itemKey)) {
5624
+ return;
5625
+ }
5626
+ const sourceMessageSeq = messageTurnId === void 0 ? void 0 : sourceMessageSeqForTurn(state, messageTurnId);
5627
+ if (messageTurnId !== void 0 && sourceMessageSeq === void 0) {
5628
+ args.log("codex.completed_assistant_item_without_source_message", {
5629
+ turn_id: messageTurnId,
5630
+ item_key: message.itemKey
5631
+ });
5632
+ return;
5633
+ }
5634
+ const structured = codexAssistantStructuredMessage(
5635
+ message.itemKey,
5636
+ message.body,
5637
+ "completed"
5638
+ );
5639
+ const streamed = resolveStreamingAssistantOutputForCompletedMessage(
5640
+ state,
5641
+ messageTurnId ?? `item:${message.itemKey}`,
5642
+ message.itemKey,
5643
+ message.body,
5644
+ structured
5645
+ );
5646
+ switch (streamed.status) {
5647
+ case "none":
5648
+ await streamCompletedCodexOutput(args, state, payloadContext, {
5649
+ turnId: turnId ?? `item:${message.itemKey}`,
5650
+ sourceMessageSeq,
5651
+ rootSeq: state.rootSeq,
5652
+ message: {
5653
+ ...message,
5654
+ structured
5655
+ }
5656
+ });
5657
+ break;
5658
+ case "matched":
5659
+ await editStreamedCodexOutput(
5660
+ args,
5661
+ state,
5662
+ streamed.output.seq,
5663
+ message.itemKey,
5664
+ message.body,
5665
+ "completed"
5666
+ );
5667
+ forgetStreamingAssistantOutput(state, streamed.output.itemKey);
5668
+ break;
5669
+ case "ambiguous":
5670
+ throw new LogicalProjectionError(
5671
+ `Cannot reconcile completed assistant item ${message.itemKey} from item notification; ${streamed.candidateCount} active streamed assistant outputs exist`
5672
+ );
5673
+ }
5674
+ rememberCompletedAssistantItemKey(state, message.itemKey);
5675
+ logCompletedCodexOutput(args, messageTurnId ?? `item:${message.itemKey}`, {
5676
+ ...message,
5677
+ structured
5678
+ });
5679
+ }
5398
5680
  function compareCompletedOutputProjection(left, right) {
5399
5681
  if (left.kind === "snapshot" && right.kind === "snapshot" && left.match.kind !== right.match.kind && (left.match.kind === "none" || right.match.kind === "none")) {
5400
5682
  return left.snapshotIndex - right.snapshotIndex;
@@ -6271,12 +6553,20 @@ async function streamCompletedCodexOutput(args, state, payloadContext, params) {
6271
6553
  params.message.body,
6272
6554
  structured
6273
6555
  );
6556
+ const displayBody = displayBodyForCommanderOutput(
6557
+ params.message.body,
6558
+ uploadedFileIds
6559
+ );
6560
+ const displayStructured = structuredWithAssistantContent(
6561
+ structured,
6562
+ displayBody
6563
+ );
6274
6564
  await pushOk2(args.kandan, args.topic, "session:stream_thread_message", {
6275
6565
  workspace: session.workspaceSlug,
6276
6566
  channel: session.channelSlug,
6277
6567
  thread_id: state.kandanThreadId,
6278
6568
  stream_key: streamKey,
6279
- body: params.message.body,
6569
+ body: displayBody,
6280
6570
  payload: {
6281
6571
  ...localRunnerPayload(
6282
6572
  args.options,
@@ -6290,7 +6580,7 @@ async function streamCompletedCodexOutput(args, state, payloadContext, params) {
6290
6580
  ...params.rootSeq === void 0 ? {} : { reply_to_seq: params.rootSeq },
6291
6581
  ...params.sourceMessageSeq === void 0 ? {} : { source_message_seq: params.sourceMessageSeq },
6292
6582
  ...streamMetadata,
6293
- structured
6583
+ structured: displayStructured
6294
6584
  },
6295
6585
  ...uploadedFileIds.length === 0 ? {} : { uploaded_file_ids: uploadedFileIds },
6296
6586
  client_message_id: streamKey
@@ -6313,13 +6603,36 @@ async function editCodexStructuredOutput(args, state, targetSeq, content, struct
6313
6603
  return;
6314
6604
  }
6315
6605
  const session = args.options.channelSession;
6606
+ const uploadedFileIds = stringValue(structured.stream_state) === "completed" ? await uploadedFileIdsForCommanderOutput(
6607
+ {
6608
+ kandan: args.kandan,
6609
+ topic: args.topic,
6610
+ workspace: session.workspaceSlug,
6611
+ channel: session.channelSlug,
6612
+ cwd: args.options.cwd,
6613
+ kandanUrl: args.options.kandanUrl,
6614
+ token: args.options.token,
6615
+ fetch: args.options.fetch
6616
+ },
6617
+ content,
6618
+ structured
6619
+ ) : [];
6620
+ const displayContent = displayBodyForCommanderOutput(
6621
+ content,
6622
+ uploadedFileIds
6623
+ );
6624
+ const displayStructured = structuredWithAssistantContent(
6625
+ structured,
6626
+ displayContent
6627
+ );
6316
6628
  await pushOk2(args.kandan, args.topic, "session:edit_thread_message", {
6317
6629
  workspace: session.workspaceSlug,
6318
6630
  channel: session.channelSlug,
6319
6631
  thread_id: state.kandanThreadId,
6320
6632
  target_seq: targetSeq,
6321
- body: content,
6322
- structured
6633
+ body: displayContent,
6634
+ structured: displayStructured,
6635
+ ...uploadedFileIds.length === 0 ? {} : { uploaded_file_ids: uploadedFileIds }
6323
6636
  });
6324
6637
  }
6325
6638
  async function mirrorLocalTuiInputFromNotification(args, state, turnId, params, payloadContext) {
@@ -6495,6 +6808,30 @@ function resolveStreamingAssistantOutputForCompletedMessage(state, turnId, itemK
6495
6808
  }
6496
6809
  }
6497
6810
  }
6811
+ function resolveStreamingAssistantOutputForCompletedSignal(state, turnId, itemKey) {
6812
+ if (itemKey !== void 0) {
6813
+ const exact = findStreamingAssistantOutput(state, itemKey);
6814
+ if (exact !== void 0) {
6815
+ return { status: "matched", output: exact };
6816
+ }
6817
+ }
6818
+ if (turnId === void 0) {
6819
+ return { status: "none" };
6820
+ }
6821
+ const candidates = boundedCacheValues(state.streamingAssistantOutputs).filter(
6822
+ (output) => output.turnId === turnId
6823
+ );
6824
+ switch (candidates.length) {
6825
+ case 0:
6826
+ return { status: "none" };
6827
+ case 1: {
6828
+ const [output] = candidates;
6829
+ return output === void 0 ? { status: "none" } : { status: "matched", output };
6830
+ }
6831
+ default:
6832
+ return { status: "ambiguous", candidateCount: candidates.length };
6833
+ }
6834
+ }
6498
6835
  function normalizedTranscriptText(value) {
6499
6836
  return value.trim().replace(/\s+/g, " ");
6500
6837
  }
@@ -6511,6 +6848,16 @@ function rememberStreamingAssistantOutput(state, output) {
6511
6848
  function forgetStreamingAssistantOutput(state, itemKey) {
6512
6849
  forgetBoundedCacheValue(state.streamingAssistantOutputs, itemKey);
6513
6850
  }
6851
+ function completedAssistantItemForwarded(state, itemKey) {
6852
+ return state.completedAssistantItemKeys.has(itemKey);
6853
+ }
6854
+ function rememberCompletedAssistantItemKey(state, itemKey) {
6855
+ rememberBoundedStringSet(
6856
+ state.completedAssistantItemKeys,
6857
+ itemKey,
6858
+ maxForwardedTurnIds
6859
+ );
6860
+ }
6514
6861
  function rememberStreamingReasoningOutput(state, output) {
6515
6862
  rememberBoundedCacheValue(
6516
6863
  state.streamingReasoningOutputs,
@@ -6662,6 +7009,32 @@ async function waitForPendingTuiInputMirror(state, turnId) {
6662
7009
  await pending;
6663
7010
  }
6664
7011
  }
7012
+ function rememberPendingCompletedAssistantItemForward(state, turnId, promise) {
7013
+ const existing = state.pendingCompletedAssistantItemForwards.get(turnId);
7014
+ const pending = existing ?? /* @__PURE__ */ new Set();
7015
+ pending.add(promise);
7016
+ state.pendingCompletedAssistantItemForwards.set(turnId, pending);
7017
+ trimBoundedMap(
7018
+ state.pendingCompletedAssistantItemForwards,
7019
+ maxForwardedTurnIds
7020
+ );
7021
+ }
7022
+ function forgetPendingCompletedAssistantItemForward(state, turnId, promise) {
7023
+ const pending = state.pendingCompletedAssistantItemForwards.get(turnId);
7024
+ if (pending === void 0) {
7025
+ return;
7026
+ }
7027
+ pending.delete(promise);
7028
+ if (pending.size === 0) {
7029
+ state.pendingCompletedAssistantItemForwards.delete(turnId);
7030
+ }
7031
+ }
7032
+ async function waitForPendingCompletedAssistantItemForwards(state, turnId) {
7033
+ const pending = state.pendingCompletedAssistantItemForwards.get(turnId);
7034
+ if (pending !== void 0) {
7035
+ await Promise.allSettled([...pending]);
7036
+ }
7037
+ }
6665
7038
  async function waitForStreamingForwardChains(args, state, payloadContext) {
6666
7039
  flushPendingStreamingDeltas(args, state, payloadContext);
6667
7040
  await Promise.all([
@@ -7175,7 +7548,7 @@ var init_channelSession = __esm({
7175
7548
 
7176
7549
  // src/claudeCodeSession.ts
7177
7550
  import { existsSync, readFileSync } from "node:fs";
7178
- import { homedir } from "node:os";
7551
+ import { homedir as homedir2 } from "node:os";
7179
7552
  import { join as join2 } from "node:path";
7180
7553
  function claudeCodeSettingSources() {
7181
7554
  return ["user", "project", "local"];
@@ -7183,7 +7556,7 @@ function claudeCodeSettingSources() {
7183
7556
  async function probeClaudeCodeAvailability(args) {
7184
7557
  if (!hasClaudeCodeAuthHint(process.env, {
7185
7558
  cwd: args.cwd,
7186
- homeDir: homedir(),
7559
+ homeDir: homedir2(),
7187
7560
  platform: process.platform,
7188
7561
  fileExists: existsSync,
7189
7562
  readTextFile: readTextFileIfPresent
@@ -7848,7 +8221,7 @@ var init_claudeCodeSession = __esm({
7848
8221
  // src/runnerLogger.ts
7849
8222
  import { appendFileSync, openSync } from "node:fs";
7850
8223
  import { createWriteStream } from "node:fs";
7851
- import { homedir as homedir2 } from "node:os";
8224
+ import { homedir as homedir3 } from "node:os";
7852
8225
  import { dirname, join as join3 } from "node:path";
7853
8226
  import { mkdirSync } from "node:fs";
7854
8227
  function createRunnerLogger(logFile, consoleReporter) {
@@ -7890,10 +8263,10 @@ function writeCliAuditEvent(event, payload, options = {}) {
7890
8263
  }
7891
8264
  function defaultCliAuditLogFile() {
7892
8265
  const override = process.env.LINZUMI_CLI_AUDIT_LOG?.trim();
7893
- return override === void 0 || override === "" ? join3(homedir2(), ".linzumi", "logs", "command-events.jsonl") : override;
8266
+ return override === void 0 || override === "" ? join3(homedir3(), ".linzumi", "logs", "command-events.jsonl") : override;
7894
8267
  }
7895
8268
  function defaultRunnerLogFile() {
7896
- return join3(homedir2(), ".linzumi", "logs", "runner-events.jsonl");
8269
+ return join3(homedir3(), ".linzumi", "logs", "runner-events.jsonl");
7897
8270
  }
7898
8271
  function redactForCliLog(value) {
7899
8272
  return redactObject(value);
@@ -8553,11 +8926,11 @@ import {
8553
8926
  realpathSync,
8554
8927
  writeFileSync
8555
8928
  } from "node:fs";
8556
- import { homedir as homedir3 } from "node:os";
8929
+ import { homedir as homedir4 } from "node:os";
8557
8930
  import { join as join4, resolve as resolve2 } from "node:path";
8558
8931
  function ensureCodexProjectTrusted(projectPath, options = {}) {
8559
8932
  const trustedPath = realpathSync(resolve2(projectPath));
8560
- const configHome = options.configHome ?? process.env.CODEX_HOME ?? join4(homedir3(), ".codex");
8933
+ const configHome = options.configHome ?? process.env.CODEX_HOME ?? join4(homedir4(), ".codex");
8561
8934
  const configPath = join4(configHome, "config.toml");
8562
8935
  const currentConfig = existsSync2(configPath) ? readFileSync2(configPath, "utf8") : "";
8563
8936
  const nextConfig = codexConfigWithTrustedProject(currentConfig, trustedPath);
@@ -8612,7 +8985,7 @@ var init_codexProjectTrust = __esm({
8612
8985
 
8613
8986
  // src/localCapabilities.ts
8614
8987
  import { realpathSync as realpathSync2 } from "node:fs";
8615
- import { homedir as homedir4 } from "node:os";
8988
+ import { homedir as homedir5 } from "node:os";
8616
8989
  import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3 } from "node:path";
8617
8990
  function parseAllowedCwdList(value) {
8618
8991
  if (value === void 0) {
@@ -8652,10 +9025,10 @@ function assertConfiguredAllowedCwds(paths) {
8652
9025
  }
8653
9026
  function expandUserPath(pathValue) {
8654
9027
  if (pathValue === "~") {
8655
- return homedir4();
9028
+ return homedir5();
8656
9029
  }
8657
9030
  if (pathValue.startsWith("~/")) {
8658
- return resolve3(homedir4(), pathValue.slice(2));
9031
+ return resolve3(homedir5(), pathValue.slice(2));
8659
9032
  }
8660
9033
  return pathValue;
8661
9034
  }
@@ -9041,11 +9414,11 @@ import {
9041
9414
  unlinkSync,
9042
9415
  writeFileSync as writeFileSync2
9043
9416
  } from "node:fs";
9044
- import { homedir as homedir5 } from "node:os";
9417
+ import { homedir as homedir6 } from "node:os";
9045
9418
  import { basename as basename3, dirname as dirname2, join as join5, resolve as resolve4 } from "node:path";
9046
9419
  function localConfigPath(env = process.env) {
9047
9420
  const override = env.LINZUMI_CONFIG_FILE;
9048
- return override !== void 0 && override.trim() !== "" ? resolve4(expandUserPath(override)) : resolve4(homedir5(), ".linzumi", "config.json");
9421
+ return override !== void 0 && override.trim() !== "" ? resolve4(expandUserPath(override)) : resolve4(homedir6(), ".linzumi", "config.json");
9049
9422
  }
9050
9423
  function localConfigScopeKey(linzumiUrl) {
9051
9424
  const normalizedUrl = kandanHttpBaseUrl(linzumiUrl);
@@ -10741,7 +11114,7 @@ import {
10741
11114
  rmSync,
10742
11115
  writeFileSync as writeFileSync4
10743
11116
  } from "node:fs";
10744
- import { homedir as homedir6 } from "node:os";
11117
+ import { homedir as homedir7 } from "node:os";
10745
11118
  import { dirname as dirname4, join as join7, resolve as resolve5 } from "node:path";
10746
11119
  import { Readable } from "node:stream";
10747
11120
  import { pipeline } from "node:stream/promises";
@@ -11227,7 +11600,7 @@ function fileSha256Sync(path2) {
11227
11600
  return createHash2("sha256").update(readFileSync5(path2)).digest("hex");
11228
11601
  }
11229
11602
  function defaultEditorRuntimeCacheRoot() {
11230
- return join7(homedir6(), ".linzumi", "editor-runtimes");
11603
+ return join7(homedir7(), ".linzumi", "editor-runtimes");
11231
11604
  }
11232
11605
  function nonEmptyString(value) {
11233
11606
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
@@ -11750,7 +12123,7 @@ var linzumiCliVersion, linzumiCliVersionText;
11750
12123
  var init_version = __esm({
11751
12124
  "src/version.ts"() {
11752
12125
  "use strict";
11753
- linzumiCliVersion = "0.0.68-beta";
12126
+ linzumiCliVersion = "0.0.69-beta";
11754
12127
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
11755
12128
  }
11756
12129
  });
@@ -12179,10 +12552,10 @@ var init_runnerConsoleReporter = __esm({
12179
12552
 
12180
12553
  // src/authCache.ts
12181
12554
  import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
12182
- import { homedir as homedir7 } from "node:os";
12555
+ import { homedir as homedir8 } from "node:os";
12183
12556
  import { dirname as dirname6, join as join9 } from "node:path";
12184
12557
  function defaultAuthFilePath() {
12185
- const base = process.env.KANDAN_HOME ?? join9(homedir7(), ".kandan");
12558
+ const base = process.env.KANDAN_HOME ?? join9(homedir8(), ".kandan");
12186
12559
  return join9(base, "auth.json");
12187
12560
  }
12188
12561
  function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePath()) {
@@ -12885,7 +13258,7 @@ import {
12885
13258
  statSync
12886
13259
  } from "node:fs";
12887
13260
  import { createServer as createServer3 } from "node:http";
12888
- import { homedir as homedir8, hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
13261
+ import { homedir as homedir9, hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
12889
13262
  import { dirname as dirname7, join as join11, resolve as resolve6 } from "node:path";
12890
13263
  async function runLocalCodexRunner(options) {
12891
13264
  const log = makeRunnerLogger(options);
@@ -16556,16 +16929,24 @@ async function streamClaudeCodeStructuredMessage(args, sessionId, message) {
16556
16929
  message.body,
16557
16930
  message.structured
16558
16931
  ) : [];
16932
+ const displayBody = displayBodyForCommanderOutput(
16933
+ message.body,
16934
+ uploadedFileIds
16935
+ );
16936
+ const displayStructured = structuredWithAssistantContent(
16937
+ message.structured,
16938
+ displayBody
16939
+ );
16559
16940
  await args.kandan.push(args.topic, "session:stream_thread_message", {
16560
16941
  workspace: args.workspace,
16561
16942
  channel: args.channel,
16562
16943
  thread_id: args.threadId,
16563
16944
  stream_key: streamKey,
16564
- body: message.body,
16945
+ body: displayBody,
16565
16946
  payload: claudeCodePayload(
16566
16947
  { ...args, sourceSeq: args.sourceSeq() },
16567
16948
  sessionId,
16568
- message.structured
16949
+ displayStructured
16569
16950
  ),
16570
16951
  ...uploadedFileIds.length === 0 ? {} : { uploaded_file_ids: uploadedFileIds },
16571
16952
  client_message_id: streamKey
@@ -16590,15 +16971,23 @@ async function postClaudeCodeStructuredMessage(args, sessionId, message) {
16590
16971
  message.body,
16591
16972
  message.structured
16592
16973
  );
16974
+ const displayBody = displayBodyForCommanderOutput(
16975
+ message.body,
16976
+ uploadedFileIds
16977
+ );
16978
+ const displayStructured = structuredWithAssistantContent(
16979
+ message.structured,
16980
+ displayBody
16981
+ );
16593
16982
  await args.kandan.push(args.topic, "session:post_thread_message", {
16594
16983
  workspace: args.workspace,
16595
16984
  channel: args.channel,
16596
16985
  thread_id: args.threadId,
16597
- body: message.body,
16986
+ body: displayBody,
16598
16987
  payload: claudeCodePayload(
16599
16988
  { ...args, sourceSeq: args.sourceSeq() },
16600
16989
  sessionId,
16601
- message.structured
16990
+ displayStructured
16602
16991
  ),
16603
16992
  ...uploadedFileIds.length === 0 ? {} : { uploaded_file_ids: uploadedFileIds },
16604
16993
  client_message_id: claudeCodeMessageClientId(
@@ -17523,7 +17912,7 @@ function isGitProjectDirectory(cwd) {
17523
17912
  }
17524
17913
  function browseRunnerDirectory(control, options) {
17525
17914
  const requestId = stringValue(control.requestId) ?? null;
17526
- const requestedPath = stringValue(control.path) ?? homedir8();
17915
+ const requestedPath = stringValue(control.path) ?? homedir9();
17527
17916
  try {
17528
17917
  const currentPath = realpathSync5(resolve6(expandUserPath(requestedPath)));
17529
17918
  const stats = statSync(currentPath);
@@ -17554,7 +17943,7 @@ function browseRunnerDirectory(control, options) {
17554
17943
  ok: true,
17555
17944
  currentPath,
17556
17945
  parentPath: parent === currentPath ? null : parent,
17557
- homePath: homedir8(),
17946
+ homePath: homedir9(),
17558
17947
  runnerCwd: resolve6(options.cwd),
17559
17948
  entries,
17560
17949
  isGit: isGitProjectDirectory(currentPath)
@@ -17589,7 +17978,7 @@ function createRunnerProject(control, options, allowedCwds) {
17589
17978
  error: "invalid_project_name"
17590
17979
  };
17591
17980
  }
17592
- const projectsRoot = join11(homedir8(), "linzumi");
17981
+ const projectsRoot = join11(homedir9(), "linzumi");
17593
17982
  const projectPath = join11(projectsRoot, projectDirName);
17594
17983
  let createdProjectPath = false;
17595
17984
  try {
@@ -17806,7 +18195,7 @@ var init_kandanTls = __esm({
17806
18195
  // src/helloLinzumiProject.ts
17807
18196
  import { existsSync as existsSync10, mkdirSync as mkdirSync10, readFileSync as readFileSync11, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
17808
18197
  import { dirname as dirname9, join as join13, resolve as resolve7 } from "node:path";
17809
- import { fileURLToPath } from "node:url";
18198
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
17810
18199
  function createHelloLinzumiProject(input = {}) {
17811
18200
  const options = typeof input === "string" ? { rootPath: input } : input;
17812
18201
  const root = resolveHelloProjectRoot(options);
@@ -17895,7 +18284,7 @@ var init_helloLinzumiProject = __esm({
17895
18284
  defaultHelloLinzumiPort = 8787;
17896
18285
  defaultHelloLinzumiHost = "0.0.0.0";
17897
18286
  markerFile = ".linzumi-demo-project";
17898
- moduleDir = dirname9(fileURLToPath(import.meta.url));
18287
+ moduleDir = dirname9(fileURLToPath2(import.meta.url));
17899
18288
  linzumiLogoSvg = readFileSync11(join13(moduleDir, "assets", "linzumi-logo.svg"), "utf8");
17900
18289
  packageJson = `${JSON.stringify(
17901
18290
  {
@@ -38626,7 +39015,7 @@ import {
38626
39015
  writeFileSync as writeFileSync11
38627
39016
  } from "node:fs";
38628
39017
  import { access } from "node:fs/promises";
38629
- import { homedir as homedir11, tmpdir as tmpdir4 } from "node:os";
39018
+ import { homedir as homedir12, tmpdir as tmpdir4 } from "node:os";
38630
39019
  import { delimiter as delimiter2, dirname as dirname11, join as join15, resolve as resolve9 } from "node:path";
38631
39020
  import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
38632
39021
  import { emitKeypressEvents } from "node:readline";
@@ -40929,7 +41318,7 @@ async function runSignupPreflights(runtime) {
40929
41318
  function defaultPreflightRuntime() {
40930
41319
  return {
40931
41320
  cwd: process.cwd(),
40932
- homeDir: homedir11(),
41321
+ homeDir: homedir12(),
40933
41322
  probeTool,
40934
41323
  readGitEmail,
40935
41324
  discoverCodeRoots,
@@ -41138,7 +41527,7 @@ function codexTaskSuggestionProcess2(args) {
41138
41527
  function signupCodexTaskSuggestionProcessForTest(args) {
41139
41528
  return codexTaskSuggestionProcess2(args);
41140
41529
  }
41141
- async function resolveSignupCodexCommand(env = process.env, homeDir = homedir11(), executableExists = fileIsExecutable) {
41530
+ async function resolveSignupCodexCommand(env = process.env, homeDir = homedir12(), executableExists = fileIsExecutable) {
41142
41531
  const override = firstConfiguredValue([
41143
41532
  env.LINZUMI_SIGNUP_CODEX_BIN,
41144
41533
  env.LINZUMI_CODEX_BIN
@@ -41777,10 +42166,10 @@ function directoryExists(path2) {
41777
42166
  }
41778
42167
  function expandHomePath(path2) {
41779
42168
  if (path2 === "~") {
41780
- return homedir11();
42169
+ return homedir12();
41781
42170
  }
41782
42171
  if (path2.startsWith("~/")) {
41783
- return join15(homedir11(), path2.slice(2));
42172
+ return join15(homedir12(), path2.slice(2));
41784
42173
  }
41785
42174
  return resolve9(path2);
41786
42175
  }
@@ -41868,9 +42257,9 @@ init_runner();
41868
42257
  init_claudeCodeSession();
41869
42258
  init_authCache();
41870
42259
  import { existsSync as existsSync13, readFileSync as readFileSync15, realpathSync as realpathSync6 } from "node:fs";
41871
- import { homedir as homedir12 } from "node:os";
42260
+ import { homedir as homedir13 } from "node:os";
41872
42261
  import { resolve as resolve10 } from "node:path";
41873
- import { fileURLToPath as fileURLToPath3 } from "node:url";
42262
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
41874
42263
 
41875
42264
  // src/authResolution.ts
41876
42265
  init_authCache();
@@ -41931,7 +42320,7 @@ init_json();
41931
42320
  init_defaultUrls();
41932
42321
  import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
41933
42322
  import { dirname as dirname8, join as join12 } from "node:path";
41934
- import { homedir as homedir9 } from "node:os";
42323
+ import { homedir as homedir10 } from "node:os";
41935
42324
  async function runAgentCliCommand(args, deps = {
41936
42325
  fetchImpl: fetch,
41937
42326
  stdout: process.stdout,
@@ -42639,7 +43028,7 @@ function agentTokenFile(flags) {
42639
43028
  return flags.get("agent-token-file") ?? defaultAgentTokenFilePath();
42640
43029
  }
42641
43030
  function defaultAgentTokenFilePath() {
42642
- return join12(homedir9(), ".linzumi", "agent-token.json");
43031
+ return join12(homedir10(), ".linzumi", "agent-token.json");
42643
43032
  }
42644
43033
  function normalizedApiUrl(apiUrl) {
42645
43034
  return apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`;
@@ -42747,13 +43136,13 @@ import {
42747
43136
  watch,
42748
43137
  writeFileSync as writeFileSync9
42749
43138
  } from "node:fs";
42750
- import { homedir as homedir10 } from "node:os";
43139
+ import { homedir as homedir11 } from "node:os";
42751
43140
  import { dirname as dirname10, join as join14, resolve as resolve8 } from "node:path";
42752
43141
  import { execFileSync, spawn as spawn8 } from "node:child_process";
42753
- import { fileURLToPath as fileURLToPath2 } from "node:url";
43142
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
42754
43143
  var connectedMarkers = ["Connected to Linzumi", "Runner connected:"];
42755
43144
  function commanderStatusDir() {
42756
- return join14(homedir10(), ".linzumi", "commanders");
43145
+ return join14(homedir11(), ".linzumi", "commanders");
42757
43146
  }
42758
43147
  function commanderStatusFile(runnerId, statusDir = commanderStatusDir()) {
42759
43148
  return join14(statusDir, `${safeRunnerId(runnerId)}.json`);
@@ -42859,7 +43248,7 @@ async function waitForCommanderDaemon(options) {
42859
43248
  if (commanderLogIsConnected(log)) {
42860
43249
  return { ok: true, record: status.record };
42861
43250
  }
42862
- await waitForFileChangeOrTimeout(
43251
+ const readyAfterWait = await waitForFileChangeOrTimeout(
42863
43252
  status.record.logFile,
42864
43253
  deadline,
42865
43254
  now,
@@ -42868,6 +43257,24 @@ async function waitForCommanderDaemon(options) {
42868
43257
  return updatedLog !== void 0 && commanderLogIsConnected(updatedLog);
42869
43258
  }
42870
43259
  );
43260
+ if (readyAfterWait) {
43261
+ const refreshedStatus = statusImpl(
43262
+ options.runnerId,
43263
+ options.statusDir
43264
+ );
43265
+ switch (refreshedStatus.status) {
43266
+ case "missing":
43267
+ return { ok: false, reason: "missing" };
43268
+ case "stopped":
43269
+ return { ok: false, reason: "stopped" };
43270
+ case "running": {
43271
+ const refreshedLog = readTextFile(refreshedStatus.record.logFile);
43272
+ if (refreshedLog !== void 0 && commanderLogIsConnected(refreshedLog)) {
43273
+ return { ok: true, record: refreshedStatus.record };
43274
+ }
43275
+ }
43276
+ }
43277
+ }
42871
43278
  }
42872
43279
  }
42873
43280
  }
@@ -42885,7 +43292,7 @@ function currentEntrypoint() {
42885
43292
  if (scriptPath !== void 0) {
42886
43293
  return scriptPath;
42887
43294
  }
42888
- return fileURLToPath2(import.meta.url);
43295
+ return fileURLToPath3(import.meta.url);
42889
43296
  }
42890
43297
  function parseRecord(content) {
42891
43298
  const parsed = JSON.parse(content);
@@ -42991,31 +43398,31 @@ function safeRunnerId(runnerId) {
42991
43398
  }
42992
43399
  async function waitForFileChangeOrTimeout(path2, deadline, now, ready2 = () => false) {
42993
43400
  const remaining = Math.max(0, deadline - now());
42994
- await new Promise((resolve11) => {
43401
+ return new Promise((resolve11) => {
42995
43402
  let resolved = false;
42996
43403
  let watcher;
42997
- const finish = () => {
43404
+ const finish = (connected) => {
42998
43405
  if (resolved) {
42999
43406
  return;
43000
43407
  }
43001
43408
  resolved = true;
43002
43409
  watcher?.close();
43003
43410
  clearTimeout(timer);
43004
- resolve11();
43411
+ resolve11(connected);
43005
43412
  };
43006
- const timer = setTimeout(finish, remaining);
43413
+ const timer = setTimeout(() => finish(false), remaining);
43007
43414
  try {
43008
- watcher = watch(path2, finish);
43009
- watcher.on("error", finish);
43415
+ watcher = watch(path2, () => finish(ready2()));
43416
+ watcher.on("error", () => finish(ready2()));
43010
43417
  if (ready2()) {
43011
- finish();
43418
+ finish(true);
43012
43419
  }
43013
43420
  } catch (_error) {
43014
43421
  if (ready2()) {
43015
- finish();
43422
+ finish(true);
43016
43423
  return;
43017
43424
  }
43018
- finish();
43425
+ finish(false);
43019
43426
  }
43020
43427
  });
43021
43428
  }
@@ -55197,7 +55604,7 @@ function isMainModule() {
55197
55604
  if (scriptPath === void 0) {
55198
55605
  return false;
55199
55606
  }
55200
- return fileURLToPath3(import.meta.url) === resolve10(scriptPath);
55607
+ return fileURLToPath4(import.meta.url) === resolve10(scriptPath);
55201
55608
  }
55202
55609
  async function main(args) {
55203
55610
  const parsed = parseCommand(args);
@@ -56088,10 +56495,10 @@ function rejectStartTargetingFlags(values) {
56088
56495
  }
56089
56496
  function resolveUserPath(pathValue) {
56090
56497
  if (pathValue === "~") {
56091
- return homedir12();
56498
+ return homedir13();
56092
56499
  }
56093
56500
  if (pathValue.startsWith("~/")) {
56094
- return resolve10(homedir12(), pathValue.slice(2));
56501
+ return resolve10(homedir13(), pathValue.slice(2));
56095
56502
  }
56096
56503
  return resolve10(pathValue);
56097
56504
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.68-beta",
3
+ "version": "0.0.69-beta",
4
4
  "description": "Linzumi CLI — point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {