@linzumi/cli 0.0.49-beta → 0.0.51-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 +179 -72
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -62,7 +62,7 @@ Install the CLI or run it with `npx`:
62
62
  ```bash
63
63
  npm install -g @linzumi/cli@latest
64
64
  npx -y @linzumi/cli@latest signup
65
- npx -y @linzumi/cli@0.0.49-beta --version
65
+ npx -y @linzumi/cli@0.0.51-beta --version
66
66
  linzumi --version
67
67
  ```
68
68
 
package/dist/index.js CHANGED
@@ -10152,7 +10152,7 @@ function realpathOrResolved(pathValue) {
10152
10152
  }
10153
10153
 
10154
10154
  // src/version.ts
10155
- var linzumiCliVersion = "0.0.49-beta";
10155
+ var linzumiCliVersion = "0.0.51-beta";
10156
10156
  var linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
10157
10157
 
10158
10158
  // src/runnerLock.ts
@@ -11364,6 +11364,7 @@ async function discoverCodexThreads(codex, cwd) {
11364
11364
  preview: stringValue(thread.preview) ?? "",
11365
11365
  cwd: stringValue(thread.cwd) ?? "",
11366
11366
  source: stringValue(thread.source) ?? "",
11367
+ createdAt: integerValue(thread.createdAt) ?? stringValue(thread.createdAt) ?? null,
11367
11368
  updatedAt: integerValue(thread.updatedAt) ?? null,
11368
11369
  status: objectValue(thread.status) ?? null
11369
11370
  })).filter((thread) => thread.id !== "");
@@ -11747,6 +11748,22 @@ ${args.developerPrompt}
11747
11748
  `;
11748
11749
  const linzumiContext = args.linzumiContext === undefined ? "" : `
11749
11750
  ${formatLinzumiConversationContextForPrompt(args.linzumiContext)}
11751
+ `;
11752
+ const reviewGuidance = `
11753
+ <linzumi_pr_review_guidance>
11754
+ For frontend or other user-visible changes, ask whether the user wants
11755
+ screenshot or screen-recording proof, ideally before/after when that helps
11756
+ review the change. Screen recordings should be WebM or MP4. When the user asks
11757
+ you to open or update a PR, attach or link that proof in the PR when feasible,
11758
+ and state the exact blocker when it is not feasible.
11759
+ </linzumi_pr_review_guidance>
11760
+
11761
+ <linzumi_user_visible_writing_guide>
11762
+ When you write user-visible UI, status, error, or PR-review copy, keep it short,
11763
+ direct, and focused on the user's outcome. Avoid implementation-heavy sentences,
11764
+ stacked clauses, and internal mechanics unless the user explicitly needs that
11765
+ detail.
11766
+ </linzumi_user_visible_writing_guide>
11750
11767
  `;
11751
11768
  return `<context>
11752
11769
  You are a Linzumi Codex session launched by the Linzumi Commander.
@@ -11791,6 +11808,7 @@ GOOD preview command: npm run dev -- --host 0.0.0.0 --port 8787
11791
11808
  BAD preview command: npm run dev -- --host 127.0.0.1
11792
11809
  </examples>
11793
11810
  ${linzumiContext}
11811
+ ${reviewGuidance}
11794
11812
  ${customPrompt}
11795
11813
  <task_reminder>
11796
11814
  You are the Commander-launched Linzumi Codex session. Do the implementation
@@ -14879,13 +14897,15 @@ var esm_default4 = createPrompt((config, done) => {
14879
14897
  `).trimEnd();
14880
14898
  return `${lines}${cursorHide}`;
14881
14899
  });
14882
- // src/signupMockFlow.ts
14900
+ // src/signupFlow.ts
14883
14901
  import { spawn as spawn7 } from "node:child_process";
14884
14902
  import {
14903
+ closeSync as closeSync2,
14885
14904
  existsSync as existsSync10,
14886
14905
  constants as fsConstants,
14887
14906
  mkdirSync as mkdirSync10,
14888
14907
  mkdtempSync as mkdtempSync3,
14908
+ openSync as openSync3,
14889
14909
  readFileSync as readFileSync10,
14890
14910
  readdirSync,
14891
14911
  rmSync as rmSync3,
@@ -14916,7 +14936,8 @@ function createSignupServerClient(args) {
14916
14936
  method: "POST",
14917
14937
  body: {
14918
14938
  email: request.email,
14919
- mode: request.mode
14939
+ mode: request.mode,
14940
+ selected_tasks: request.selectedTasks ?? []
14920
14941
  },
14921
14942
  render: renderEmailCodeStart
14922
14943
  }),
@@ -15185,7 +15206,7 @@ function booleanValue(record, key) {
15185
15206
  throw new Error(`signup ${key} was incomplete`);
15186
15207
  }
15187
15208
 
15188
- // src/signupMockFlow.ts
15209
+ // src/signupFlow.ts
15189
15210
  var commanderConnectedMarkers = [
15190
15211
  "Connected to Linzumi",
15191
15212
  "Runner connected:",
@@ -15216,7 +15237,7 @@ secure mission control for all your agents on your computers
15216
15237
  `;
15217
15238
  var logoTagline = "secure mission control for all your agents on your computers";
15218
15239
  var signupCancelMessage = "Y'all come back soon, ya hear?";
15219
- function signupMockHelpText() {
15240
+ function signupHelpText() {
15220
15241
  return [
15221
15242
  "Usage:",
15222
15243
  " linzumi signup",
@@ -15286,7 +15307,7 @@ function comparableServiceUrl(value) {
15286
15307
  return trimmed.replace(/\/+$/u, "");
15287
15308
  }
15288
15309
  }
15289
- async function runSignupMockFlow(deps = {}) {
15310
+ async function runSignupFlow(deps = {}) {
15290
15311
  const input = deps.input ?? defaultStdin;
15291
15312
  const output = deps.output ?? defaultStdout;
15292
15313
  const prompts = deps.prompts ?? inquirerPrompts(input, output);
@@ -15363,18 +15384,25 @@ async function runSignupMockFlow(deps = {}) {
15363
15384
  projectDiscovery: { type: "ready", codeRoot: guessedCodeRoot, projects }
15364
15385
  });
15365
15386
  const selectedProjectPaths = await prompts.projectPicker({
15366
- message: "Which projects would you like to add to Linzumi Mission Control?",
15387
+ message: "Pick the first folders where Linzumi should start agents",
15367
15388
  projects,
15368
15389
  defaultSelectedPaths: defaultSelectedProjectPaths(forceSignup ? [] : readConfiguredProjectPaths(serviceUrl))
15369
15390
  });
15370
- if (selectedProjectPaths.length === 0) {
15391
+ const projectPreparation = prepareSelectedSignupProjects(selectedProjectPaths, projects);
15392
+ if (projectPreparation.type === "failed") {
15393
+ writeProjectPreparationFailure(output, projectPreparation.message);
15394
+ writeDebugLaunchPayload(output, state, debugLaunchPayload);
15395
+ return;
15396
+ }
15397
+ const preparedProjectPaths = projectPreparation.selectedProjectPaths;
15398
+ if (preparedProjectPaths.length === 0) {
15371
15399
  writeNoProjectsFound(output);
15372
15400
  writeDebugLaunchPayload(output, state, debugLaunchPayload);
15373
15401
  return;
15374
15402
  }
15375
15403
  state = updateSignupState(state, {
15376
15404
  currentStep: "tasks",
15377
- selectedProjectPaths
15405
+ selectedProjectPaths: preparedProjectPaths
15378
15406
  });
15379
15407
  writeSignupScreen(output, state);
15380
15408
  writePreflightSummary(output, state);
@@ -15407,7 +15435,8 @@ async function runSignupMockFlow(deps = {}) {
15407
15435
  writeSelectedProjectsSummary(output, state);
15408
15436
  writeSelectedTasksSummary(output, state);
15409
15437
  const projectGitEmails = await projectGitEmailsPromise;
15410
- const globalGitEmail = configuredGitEmailFromPreflights(state.preflightChecks);
15438
+ const configuredGitEmail = configuredGitEmailFromPreflights(state.preflightChecks);
15439
+ const globalGitEmail = configuredGitEmail !== undefined && usefulSuggestedEmail(configuredGitEmail) ? configuredGitEmail : undefined;
15411
15440
  const emailCandidates = globalGitEmail === undefined ? uniqueDefinedEmails(projectGitEmails) : [];
15412
15441
  state = updateSignupState(state, { projectGitEmails, emailCandidates });
15413
15442
  const reusedSignupAuth = state.reusedSignupAuth;
@@ -15421,7 +15450,8 @@ async function runSignupMockFlow(deps = {}) {
15421
15450
  state = updateSignupState(state, { currentStep: "code", email });
15422
15451
  const codeRequest = signupServerClient === undefined ? { type: "sent", request: { pendingId: "mock", email } } : await startEmailCodeForSignup(signupServerClient, {
15423
15452
  email,
15424
- mode: "existing_or_create"
15453
+ mode: "existing_or_create",
15454
+ selectedTasks: selectedTaskTitlesFromState(state)
15425
15455
  });
15426
15456
  if (codeRequest.type === "failed") {
15427
15457
  state = updateSignupState(state, {
@@ -15771,13 +15801,22 @@ function writeProjectPickerIntro(output, state) {
15771
15801
  if (!preflightsFinished(state)) {
15772
15802
  return;
15773
15803
  }
15774
- writeBoundedLine(output, blue("2) Where should Linzumi let your Codex agents work?"));
15804
+ writeBoundedLine(output, blue("2) Where should Linzumi let your Codex agents start?"));
15805
+ writeWrappedPrefixMessage(output, " ", "Pick the first local folders for Mission Control. You can add or remove folders later.", muted);
15775
15806
  }
15776
15807
  function writeNoProjectsFound(output) {
15777
15808
  output.write(`${blue("?")} No projects found yet
15778
15809
 
15779
15810
  `);
15780
15811
  }
15812
+ function writeProjectPreparationFailure(output, message) {
15813
+ output.write(`
15814
+ `);
15815
+ writeBoundedLine(output, blue("We couldn't prepare that project"));
15816
+ writeStatusMessage(output, red("x"), message);
15817
+ writeBoundedLine(output, muted(" Choose another folder or move the conflicting demo directory, then retry:"));
15818
+ writeBoundedLine(output, blue(" npx -y @linzumi/cli@latest signup"));
15819
+ }
15781
15820
  function writeSelectedProjectsSummary(output, state) {
15782
15821
  const selectedProjects = selectedProjectsFromState(state);
15783
15822
  output.write(`
@@ -15822,7 +15861,7 @@ function signupProgressRows(state) {
15822
15861
  checked: selectedTaskTitlesFromState(state).length > 0
15823
15862
  },
15824
15863
  {
15825
- label: "We'll launch you into Mission Control with your work already running",
15864
+ label: "Mission Control will open with your work already running",
15826
15865
  checked: state.currentStep === "launch"
15827
15866
  }
15828
15867
  ];
@@ -15891,7 +15930,7 @@ async function chooseTasks(prompts, output, tasks) {
15891
15930
  output.write(`
15892
15931
  `);
15893
15932
  writeBoundedLine(output, blue("4) What should your Codex agents start working on?"));
15894
- writeBoundedLine(output, muted(" Pick up to 5 starting tasks for Linzumi Mission Control."));
15933
+ writeBoundedLine(output, muted(" Pick up to 5 small starter tasks. These just get agents moving."));
15895
15934
  writeBoundedLine(output, muted(" Select suggested tasks, or choose the last row to type your own task."));
15896
15935
  const customTaskChoice = "__custom_task__";
15897
15936
  const checkedTasks = await prompts.checkbox({
@@ -15961,7 +16000,7 @@ function writeLaunchSummary(output, state) {
15961
16000
  writeBoundedLine(output, blue(" npx -y @linzumi/cli@latest signup"));
15962
16001
  return;
15963
16002
  }
15964
- writeBoundedLine(output, blue("7) We'll launch you into Mission Control with your work already running"));
16003
+ writeBoundedLine(output, blue("7) Mission Control will open with your work already running"));
15965
16004
  const taskCount = selectedTaskTitlesFromState(state).length;
15966
16005
  writeBoundedLine(output, muted(` Starting ${taskCount} ${taskCount === 1 ? "task" : "tasks"} and saying hello to the Linzumi team...`));
15967
16006
  state.starterTaskLaunches.forEach((launch) => {
@@ -16037,12 +16076,20 @@ async function defaultSignupCommanderLauncher(args) {
16037
16076
  };
16038
16077
  }
16039
16078
  const logFile = signupCommanderLaunchLogFile(runnerId);
16079
+ let stdoutFd;
16080
+ let stderrFd;
16040
16081
  try {
16041
16082
  mkdirSync10(dirname9(logFile), { recursive: true });
16083
+ stdoutFd = openSync3(logFile, "a");
16084
+ stderrFd = openSync3(logFile, "a");
16042
16085
  const child = spawn7(processExecPath, signupCommanderConnectSpawnArgs(args, runnerId, logFile, scriptPath), {
16043
16086
  detached: true,
16044
- stdio: "ignore"
16087
+ stdio: ["ignore", stdoutFd, stderrFd]
16045
16088
  });
16089
+ closeSync2(stdoutFd);
16090
+ closeSync2(stderrFd);
16091
+ stdoutFd = undefined;
16092
+ stderrFd = undefined;
16046
16093
  child.unref();
16047
16094
  const connected = await waitForSignupCommanderConnection(logFile, 30000);
16048
16095
  if (!connected) {
@@ -16067,6 +16114,9 @@ async function defaultSignupCommanderLauncher(args) {
16067
16114
  restartCommand,
16068
16115
  error: error instanceof Error ? error.message : String(error)
16069
16116
  };
16117
+ } finally {
16118
+ closeFileDescriptor(stdoutFd);
16119
+ closeFileDescriptor(stderrFd);
16070
16120
  }
16071
16121
  }
16072
16122
  async function waitForSignupCommanderConnection(logFile, timeoutMs) {
@@ -16093,6 +16143,14 @@ function readTextFile(path) {
16093
16143
  return;
16094
16144
  }
16095
16145
  }
16146
+ function closeFileDescriptor(fd) {
16147
+ if (fd === undefined) {
16148
+ return;
16149
+ }
16150
+ try {
16151
+ closeSync2(fd);
16152
+ } catch (_error) {}
16153
+ }
16096
16154
  async function waitForSignupCommanderLogChange(logFile, deadline, ready2) {
16097
16155
  const remaining = Math.max(0, deadline - Date.now());
16098
16156
  await new Promise((resolve9) => {
@@ -16152,8 +16210,6 @@ function signupCommanderConnectSpawnArgs(args, runnerId, logFile, scriptPath) {
16152
16210
  args.serviceUrl,
16153
16211
  "--workspace",
16154
16212
  args.workspaceSlug,
16155
- "--channel",
16156
- args.officeChannelSlug,
16157
16213
  "--runner-id",
16158
16214
  runnerId,
16159
16215
  "--log-file",
@@ -16624,6 +16680,40 @@ function projectPickerRows(projects, selectedPaths, pathInput) {
16624
16680
  const recentRows = projects.filter((project) => !selectedPaths.has(project.path)).slice(0, remainingCount).map((project) => ({ project, selected: false }));
16625
16681
  return [...selectedRows, ...recentRows];
16626
16682
  }
16683
+ function prepareSelectedSignupProjects(selectedProjectPaths, projects) {
16684
+ const normalizedPaths = selectedProjectPaths.map(normalizeProjectPath);
16685
+ const selectedProjectByPath = new Map(normalizedPaths.map((path) => [
16686
+ path,
16687
+ projectFromKnownOrPath(projects, path)
16688
+ ]));
16689
+ for (const [path, project] of selectedProjectByPath) {
16690
+ if (project.source === "hello_linzumi_demo") {
16691
+ try {
16692
+ createHelloLinzumiProject({ rootPath: path });
16693
+ } catch (error) {
16694
+ return {
16695
+ type: "failed",
16696
+ message: demoProjectCreateFailureMessage(path, error)
16697
+ };
16698
+ }
16699
+ }
16700
+ }
16701
+ return {
16702
+ type: "ready",
16703
+ selectedProjectPaths: Array.from(new Set(normalizedPaths))
16704
+ };
16705
+ }
16706
+ function demoProjectCreateFailureMessage(path, error) {
16707
+ const detail = error instanceof Error ? error.message : String(error);
16708
+ return `The Hello Linzumi demo project could not be created at ${path}. ${detail}`;
16709
+ }
16710
+ function helloLinzumiDemoProjectOption() {
16711
+ return {
16712
+ path: normalizeProjectPath(defaultHelloLinzumiProjectDir),
16713
+ language: "Demo",
16714
+ source: "hello_linzumi_demo"
16715
+ };
16716
+ }
16627
16717
  function renderProjectPickerRow(row, index, state) {
16628
16718
  const active = index === state.highlightIndex;
16629
16719
  const marker = active ? blue("›") : " ";
@@ -16631,11 +16721,12 @@ function renderProjectPickerRow(row, index, state) {
16631
16721
  const gitHead = row.project.gitHead;
16632
16722
  const sha = truncateEnd(gitHead?.sha ?? "-----", 5);
16633
16723
  const date = gitHead?.date ?? "----------";
16634
- const message = gitHead?.message ?? "";
16724
+ const message = gitHead?.message ?? (row.project.source === "hello_linzumi_demo" ? "Create a safe demo app" : "");
16635
16725
  return ` ${marker}${selected} ${truncateMiddle(row.project.path, 41).padEnd(41, " ")} ${sha.padEnd(5, " ")} ${date.padEnd(10, " ")} ${truncateEnd(message, 34)}`;
16636
16726
  }
16637
16727
  function projectFromKnownOrPath(projects, path) {
16638
- return projects.find((project) => project.path === path) ?? customProjectOption(path);
16728
+ const normalizedPath = normalizeProjectPath(path);
16729
+ return projects.find((project) => project.path === normalizedPath) ?? (normalizedPath === normalizeProjectPath(defaultHelloLinzumiProjectDir) ? helloLinzumiDemoProjectOption() : undefined) ?? customProjectOption(normalizedPath);
16639
16730
  }
16640
16731
  function selectedProjectsFromState(state) {
16641
16732
  if (state.projectDiscovery.type !== "ready") {
@@ -16866,7 +16957,8 @@ function uniqueEmails(emails) {
16866
16957
  }
16867
16958
  function usefulSuggestedEmail(email) {
16868
16959
  const normalizedEmail = email.trim().toLocaleLowerCase();
16869
- return !(normalizedEmail === "noreply@github.com" || normalizedEmail.endsWith("@users.noreply.github.com") || normalizedEmail.endsWith("@noreply.github.com"));
16960
+ const domain = normalizedEmail.split("@")[1] ?? "";
16961
+ return !(normalizedEmail === "noreply@github.com" || normalizedEmail.endsWith("@users.noreply.github.com") || normalizedEmail.endsWith("@noreply.github.com") || domain === "localhost" || domain.endsWith(".local"));
16870
16962
  }
16871
16963
  function configuredGitEmailFromPreflights(checks) {
16872
16964
  const gitEmailCheck = checks.find((check2) => check2.id === "git_email");
@@ -16945,8 +17037,9 @@ function defaultPreflightRuntime() {
16945
17037
  };
16946
17038
  }
16947
17039
  function defaultProjectDiscoveryRuntime() {
17040
+ const cwd = process.cwd();
16948
17041
  return {
16949
- discoverProjects: async (homeDir) => discoverProjectsFromGuessedRoots(homeDir)
17042
+ discoverProjects: async (homeDir) => discoverProjectsFromSignupRoots(homeDir, cwd)
16950
17043
  };
16951
17044
  }
16952
17045
  function defaultTaskSuggestionRuntime() {
@@ -17093,7 +17186,7 @@ async function resolveSignupCodexCommand(env = process.env, homeDir = homedir8()
17093
17186
  "Set LINZUMI_SIGNUP_CODEX_BIN to a working Codex executable or install Codex in a home-managed location such as ~/.volta/bin/codex."
17094
17187
  ].join(" "));
17095
17188
  }
17096
- throw new Error("Codex task suggestions could not find Codex. Set LINZUMI_SIGNUP_CODEX_BIN to a working Codex executable.");
17189
+ throw new Error("Codex task suggestions could not find Codex. Install Codex first, or set LINZUMI_SIGNUP_CODEX_BIN to a working Codex executable.");
17097
17190
  }
17098
17191
  function firstConfiguredValue(values) {
17099
17192
  return values.find((value) => value !== undefined && value.trim() !== "");
@@ -17439,6 +17532,33 @@ function discoverProjectsUnderRoot(codeRoot) {
17439
17532
  gitHead: cachedGitHeadSummary(project.path)
17440
17533
  })).sort(sortProjectsByRecentGitCommit).slice(0, 20);
17441
17534
  }
17535
+ function discoverProjectsFromSignupRoots(homeDir, cwd) {
17536
+ const currentProjects = discoverProjectsFromCurrentDirectory(cwd);
17537
+ const guessedProjects = discoverProjectsFromGuessedRoots(homeDir);
17538
+ const demoProject = helloLinzumiDemoProjectOption();
17539
+ const [firstCurrentProject, ...remainingCurrentProjects] = currentProjects;
17540
+ const seenPaths = new Set;
17541
+ return [
17542
+ ...firstCurrentProject === undefined ? [] : [firstCurrentProject],
17543
+ demoProject,
17544
+ ...remainingCurrentProjects,
17545
+ ...guessedProjects
17546
+ ].filter((project) => {
17547
+ if (seenPaths.has(project.path)) {
17548
+ return false;
17549
+ }
17550
+ seenPaths.add(project.path);
17551
+ return true;
17552
+ }).slice(0, 24);
17553
+ }
17554
+ function discoverProjectsFromCurrentDirectory(cwd) {
17555
+ const normalizedCwd = normalizeProjectPath(cwd);
17556
+ const cwdProject = directoryExists(normalizedCwd) ? [customProjectOption(normalizedCwd)] : [];
17557
+ const gitRoot = gitTopLevel(normalizedCwd);
17558
+ const gitRootProject = gitRoot !== undefined && gitRoot !== normalizedCwd ? [customProjectOption(gitRoot)] : [];
17559
+ const childProjects = discoverProjectsUnderRoot(normalizedCwd);
17560
+ return [...cwdProject, ...gitRootProject, ...childProjects];
17561
+ }
17442
17562
  function discoverProjectsFromGuessedRoots(homeDir) {
17443
17563
  const guessedRoots = ["src", "code", "projects"].map((name) => join12(homeDir, name));
17444
17564
  const projects = guessedRoots.flatMap((root) => discoverProjectsUnderRoot(root));
@@ -17451,6 +17571,9 @@ function discoverProjectsFromGuessedRoots(homeDir) {
17451
17571
  return true;
17452
17572
  }).sort(sortProjectsByRecentGitCommit).slice(0, 20);
17453
17573
  }
17574
+ function gitTopLevel(cwd) {
17575
+ return spawnSyncGit(["rev-parse", "--show-toplevel"], cwd);
17576
+ }
17454
17577
  function sortProjectsByRecentGitCommit(left, right) {
17455
17578
  const leftTimestamp = left.gitHead?.timestamp ?? 0;
17456
17579
  const rightTimestamp = right.gitHead?.timestamp ?? 0;
@@ -17580,9 +17703,9 @@ function renderPreflightRow(check2, spinnerFrame, columns) {
17580
17703
  // src/commanderDaemon.ts
17581
17704
  import {
17582
17705
  existsSync as existsSync11,
17583
- closeSync as closeSync2,
17706
+ closeSync as closeSync3,
17584
17707
  mkdirSync as mkdirSync11,
17585
- openSync as openSync3,
17708
+ openSync as openSync4,
17586
17709
  readFileSync as readFileSync11,
17587
17710
  watch as watch2,
17588
17711
  writeFileSync as writeFileSync9
@@ -17622,8 +17745,8 @@ function startCommanderDaemon(options) {
17622
17745
  ];
17623
17746
  mkdirSync11(statusDir, { recursive: true });
17624
17747
  mkdirSync11(dirname10(logFile), { recursive: true });
17625
- const out = openSync3(logFile, "a");
17626
- const err = openSync3(logFile, "a");
17748
+ const out = openSync4(logFile, "a");
17749
+ const err = openSync4(logFile, "a");
17627
17750
  writeCliAuditEvent("process.spawn", {
17628
17751
  command: nodeBin,
17629
17752
  args: command,
@@ -17642,8 +17765,8 @@ function startCommanderDaemon(options) {
17642
17765
  pid: child.pid,
17643
17766
  purpose: "commander_daemon.start"
17644
17767
  }, { sessionId: options.runnerId });
17645
- closeSync2(out);
17646
- closeSync2(err);
17768
+ closeSync3(out);
17769
+ closeSync3(err);
17647
17770
  child.unref();
17648
17771
  if (child.pid === undefined) {
17649
17772
  throw new Error("commander daemon did not report a pid");
@@ -17917,12 +18040,12 @@ async function main(args) {
17917
18040
  return;
17918
18041
  case "signupMock":
17919
18042
  if (parsed.args.includes("--help") || parsed.args.includes("-h")) {
17920
- process.stdout.write(signupMockHelpText());
18043
+ process.stdout.write(signupHelpText());
17921
18044
  return;
17922
18045
  }
17923
18046
  {
17924
18047
  const options = parseSignupTuiOptions(parsed.args);
17925
- await runSignupMockFlow({
18048
+ await runSignupFlow({
17926
18049
  forceSignup: options.forceSignup,
17927
18050
  serviceUrl: options.serviceUrl,
17928
18051
  openBrowser: false,
@@ -17933,12 +18056,12 @@ async function main(args) {
17933
18056
  return;
17934
18057
  case "signup":
17935
18058
  if (parsed.args.includes("--help") || parsed.args.includes("-h")) {
17936
- process.stdout.write(signupMockHelpText());
18059
+ process.stdout.write(signupHelpText());
17937
18060
  return;
17938
18061
  }
17939
18062
  {
17940
18063
  const options = parseSignupTuiOptions(parsed.args);
17941
- await runSignupMockFlow({
18064
+ await runSignupFlow({
17942
18065
  forceSignup: options.forceSignup,
17943
18066
  serviceUrl: options.serviceUrl,
17944
18067
  openBrowser: options.openBrowser,
@@ -18516,7 +18639,8 @@ async function parseRunnerArgs(args, deps = {
18516
18639
  `);
18517
18640
  process.exit(0);
18518
18641
  }
18519
- const connectTarget = parseConnectTarget(values);
18642
+ rejectConnectChannelFlags(values);
18643
+ const workspaceSlug = stringValue4(values, "workspace");
18520
18644
  const kandanUrl = stringValue4(values, "linzumi-url") ?? defaultLinzumiWebSocketUrl;
18521
18645
  const cwd = stringValue4(values, "cwd") ?? process.cwd();
18522
18646
  const cwdAllowedCwds = assertConfiguredAllowedCwds([cwd]);
@@ -18528,8 +18652,7 @@ async function parseRunnerArgs(args, deps = {
18528
18652
  const token = await deps.resolveToken({
18529
18653
  kandanUrl,
18530
18654
  explicitToken,
18531
- workspaceSlug: connectTarget?.workspaceSlug,
18532
- channelSlug: connectTarget?.channelSlug,
18655
+ workspaceSlug,
18533
18656
  authFilePath: stringValue4(values, "auth-file"),
18534
18657
  callbackHost: stringValue4(values, "oauth-callback-host"),
18535
18658
  reportRejectedCachedToken: () => {
@@ -18537,7 +18660,6 @@ async function parseRunnerArgs(args, deps = {
18537
18660
  `);
18538
18661
  }
18539
18662
  });
18540
- const channelSession = parseChannelSession(values, token, connectTarget);
18541
18663
  const editorRuntime = await deps.resolveEditorRuntime({
18542
18664
  kandanUrl,
18543
18665
  token,
@@ -18568,9 +18690,8 @@ async function parseRunnerArgs(args, deps = {
18568
18690
  editorRuntime: editorRuntime.runtime,
18569
18691
  socketFactory: trustedWebSocketFactory(kandanTlsTrustFromEnv()),
18570
18692
  dependencyStatus,
18571
- workspaceSlug: connectTarget?.workspaceSlug ?? singleWorkspaceScopeFromAccessToken(token),
18572
- runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
18573
- channelSession
18693
+ workspaceSlug: workspaceSlug ?? singleWorkspaceScopeFromAccessToken(token),
18694
+ runtimeDefaults: runnerRuntimeDefaultsFromValues(values)
18574
18695
  };
18575
18696
  }
18576
18697
  function runnerRuntimeDefaultsFromValues(values) {
@@ -18667,7 +18788,7 @@ function stripDaemonSupervisorFlags(args) {
18667
18788
  function rejectStartTargetingFlags(values) {
18668
18789
  const unsupportedFlags = ["workspace", "channel"].filter((flag) => values.has(flag));
18669
18790
  if (unsupportedFlags.length > 0) {
18670
- throw new Error(`linzumi start chooses its workspace during browser onboarding; remove ${unsupportedFlags.map((flag) => `--${flag}`).join(", ")} or use linzumi connect for an explicit workspace/channel.`);
18791
+ throw new Error(`linzumi start chooses its workspace during browser onboarding; remove ${unsupportedFlags.map((flag) => `--${flag}`).join(", ")} or use linzumi connect --workspace for an explicit workspace.`);
18671
18792
  }
18672
18793
  }
18673
18794
  function resolveUserPath(pathValue) {
@@ -18679,26 +18800,6 @@ function resolveUserPath(pathValue) {
18679
18800
  }
18680
18801
  return resolve10(pathValue);
18681
18802
  }
18682
- function parseChannelSession(values, token, target) {
18683
- if (target === undefined || target.channelSlug === undefined) {
18684
- return;
18685
- }
18686
- const listenUser = stringValue4(values, "listen-user") ?? defaultListenUserFromToken(token);
18687
- return {
18688
- workspaceSlug: target.workspaceSlug,
18689
- channelSlug: target.channelSlug,
18690
- kandanThreadId: stringValue4(values, "linzumi-thread-id"),
18691
- listenUser,
18692
- model: stringValue4(values, "model"),
18693
- reasoningEffort: stringValue4(values, "reasoning-effort"),
18694
- sandbox: stringValue4(values, "sandbox"),
18695
- approvalPolicy: stringValue4(values, "approval-policy"),
18696
- streamFlushMs: positiveIntegerValue2(values, "stream-flush-ms")
18697
- };
18698
- }
18699
- function parseConnectTarget(values) {
18700
- return parseOptionalChannelTarget(values);
18701
- }
18702
18803
  function defaultListenUserFromToken(token) {
18703
18804
  const username = identityFromAccessToken(token).actorUsername;
18704
18805
  if (username !== undefined) {
@@ -18706,6 +18807,17 @@ function defaultListenUserFromToken(token) {
18706
18807
  }
18707
18808
  throw new Error("missing --listen-user and authenticated user is unavailable");
18708
18809
  }
18810
+ function rejectConnectChannelFlags(values) {
18811
+ const unsupportedFlags = [
18812
+ "channel",
18813
+ "linzumi-thread-id",
18814
+ "listen-user"
18815
+ ].filter((flag) => values.has(flag));
18816
+ if (unsupportedFlags.length === 0) {
18817
+ return;
18818
+ }
18819
+ throw new Error(`linzumi connect starts a workspace Commander; remove ${unsupportedFlags.map((flag) => `--${flag}`).join(", ")}.`);
18820
+ }
18709
18821
  function parseOptionalChannelTarget(values) {
18710
18822
  const channel = stringValue4(values, "channel");
18711
18823
  const workspace = stringValue4(values, "workspace");
@@ -18792,7 +18904,7 @@ Usage:
18792
18904
  linzumi commander <folder> [options]
18793
18905
  linzumi start <folder> [options]
18794
18906
  linzumi paths list|add|remove [path]
18795
- linzumi connect --workspace <slug> [--channel <slug>] [options]
18907
+ linzumi connect --workspace <slug> [options]
18796
18908
  linzumi auth --linzumi-url <ws-url> [--workspace <slug> --channel <slug>]
18797
18909
 
18798
18910
  Connection:
@@ -18802,12 +18914,8 @@ Connection:
18802
18914
  --auth-file <path> Auth cache path, default ~/.linzumi/auth.json
18803
18915
  --oauth-callback-host <ip> Callback host reachable by your browser
18804
18916
 
18805
- Workspace and optional channel binding:
18917
+ Workspace:
18806
18918
  --workspace <slug> Workspace slug
18807
- --channel <slug|w/c> Optional channel slug, or workspace/channel
18808
- --linzumi-thread-id <uuid> Resume an existing Linzumi thread instead of announcing a new root
18809
- (deprecated alias: --kandan-thread-id)
18810
- --listen-user <user|all> User whose replies are accepted, default authenticated user
18811
18919
 
18812
18920
  Codex:
18813
18921
  --cwd <path> Working directory for Codex, default current directory
@@ -18839,16 +18947,15 @@ Examples:
18839
18947
  linzumi start ~/code/my-app
18840
18948
  linzumi connect --workspace <your-workspace> --launch-tui
18841
18949
  linzumi connect --workspace <your-workspace> --model gpt-5 --reasoning-effort low --fast --launch-tui
18842
- linzumi connect --workspace <your-workspace> --channel <your-channel>
18843
18950
  linzumi auth --workspace <your-workspace> --channel <your-channel>
18844
18951
  linzumi paths add ~/code/my-app
18845
18952
  linzumi paths list
18846
18953
 
18847
18954
  Bad:
18848
- linzumi connect --token not-a-jwt --workspace <your-workspace> --channel <your-channel>
18849
- Missing --listen-user and authenticated user is unavailable.
18850
- linzumi connect --token "$TOKEN" --listen-users sean
18851
- Invalid flag: use --listen-user.
18955
+ linzumi connect --workspace <your-workspace> --channel <your-channel>
18956
+ Invalid: Commanders connect to workspaces, not channels.
18957
+ linzumi connect --token "$TOKEN" --listen-user sean
18958
+ Invalid: Commanders connect to workspaces, not individual channel listeners.
18852
18959
  linzumi connect --workspace <your-workspace> --allowed-cwd /does/not/exist
18853
18960
  Invalid --allowed-cwd: allowed cwd roots must exist locally.
18854
18961
  linzumi connect --workspace <your-workspace> --forward-port vite
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.49-beta",
3
+ "version": "0.0.51-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": {