@linzumi/cli 0.0.34-beta → 0.0.36-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 +60 -1
  2. package/dist/index.js +46 -32
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -50,10 +50,61 @@ real dotfiles, and your real branches. No "works in the sandbox VM,
50
50
  breaks in your repo" surprises. The chat side runs in your browser;
51
51
  the work side runs locally. This npm package is the local half.
52
52
 
53
+ ## What you'll have in three minutes
54
+
55
+ - A Linzumi workspace in your browser.
56
+ - A local Commander connected to one trusted project folder.
57
+ - A live Coding agent thread your team can watch and steer.
58
+ - Optional browser editor and preview forwarding for the running app.
59
+
60
+ ## Manual Runner Setup
61
+
62
+ Install the CLI or run it with `npx`:
63
+
53
64
  ```bash
65
+ npm install -g @linzumi/cli@latest
66
+ npx -y @linzumi/cli@0.0.36-beta --version
54
67
  linzumi --version
55
68
  ```
56
69
 
70
+ Start a local runner from a trusted folder:
71
+
72
+ ```bash
73
+ linzumi paths add ~/code/my-app
74
+ linzumi start ~/code/my-app
75
+ ```
76
+
77
+ For explicit runner roots, pass a comma-separated allowlist:
78
+
79
+ ```bash
80
+ linzumi start ~/code/my-app --allowed-cwd <paths>
81
+ ```
82
+
83
+ ## Agent-first launch path
84
+
85
+ The fastest path is still the `codex` prompt above. It uses the npm CLI
86
+ to sign in, trust a demo project, connect the Commander, and start the
87
+ first visible Coding agent thread.
88
+
89
+ ## What just happened
90
+
91
+ Linzumi keeps the browser/chat surface separate from the local work
92
+ surface. The CLI authenticates your local Commander, publishes runner
93
+ capabilities, and lets Kandan ask that Commander to start Codex inside
94
+ trusted folders only.
95
+
96
+ ## Three things to try first
97
+
98
+ - Ask the Coding agent to make a small UI change and watch the thread.
99
+ - Open the browser editor from the thread controls.
100
+ - Share the preview URL with a teammate or your phone.
101
+
102
+ ## Working as a team
103
+
104
+ Use the Linzumi thread as the shared source of truth. Humans can reply,
105
+ steer, interrupt, or inspect output while the actual commands still run
106
+ on the trusted local machine.
107
+
57
108
  ## Trusted folders
58
109
 
59
110
  Linzumi only edits inside folders you've explicitly trusted.
@@ -63,7 +114,15 @@ linzumi paths add ~/code/my-app
63
114
  ```
64
115
 
65
116
  The trust list lives at `~/.linzumi/config.json`. The bootstrap agent
66
- adds `/tmp/hello_linzumi` for you on first run.
117
+ adds `/tmp/hello_linzumi` for you on first run. For `linzumi connect`, the
118
+ selected `--cwd` is also trusted for that runner process.
119
+
120
+ ## When something looks wrong
121
+
122
+ Run `linzumi --help` or reconnect with `linzumi start ~/code/my-app`.
123
+ If the browser says no runner is connected, check that the terminal
124
+ running the Commander is still open and that the folder is listed in
125
+ `~/.linzumi/config.json`.
67
126
 
68
127
  ---
69
128
 
package/dist/index.js CHANGED
@@ -5680,7 +5680,7 @@ function kandanHttpBaseUrl(kandanUrl) {
5680
5680
  case "https:":
5681
5681
  break;
5682
5682
  default:
5683
- throw new Error("--kandan-url must be ws://, wss://, http://, or https://");
5683
+ throw new Error("--linzumi-url must be ws://, wss://, http://, or https://");
5684
5684
  }
5685
5685
  parsed.pathname = "";
5686
5686
  parsed.search = "";
@@ -9354,6 +9354,7 @@ async function waitForFileChangeOrTimeout(path, deadline, now, ready2 = () => fa
9354
9354
  // src/index.ts
9355
9355
  var flagDefinitions = new Map([
9356
9356
  ["version", { kind: "boolean" }],
9357
+ ["linzumi-url", { kind: "value" }],
9357
9358
  ["kandan-url", { kind: "value" }],
9358
9359
  ["token", { kind: "value" }],
9359
9360
  ["runner-id", { kind: "value" }],
@@ -9363,6 +9364,7 @@ var flagDefinitions = new Map([
9363
9364
  ["launch-tui", { kind: "boolean" }],
9364
9365
  ["workspace", { kind: "value" }],
9365
9366
  ["channel", { kind: "value" }],
9367
+ ["linzumi-thread-id", { kind: "value" }],
9366
9368
  ["kandan-thread-id", { kind: "value" }],
9367
9369
  ["listen-user", { kind: "value" }],
9368
9370
  ["model", { kind: "value" }],
@@ -9382,6 +9384,10 @@ var flagDefinitions = new Map([
9382
9384
  ["oauth-callback-host", { kind: "value" }],
9383
9385
  ["help", { kind: "boolean" }]
9384
9386
  ]);
9387
+ var flagAliases = new Map([
9388
+ ["kandan-url", "linzumi-url"],
9389
+ ["kandan-thread-id", "linzumi-thread-id"]
9390
+ ]);
9385
9391
  var helloFlagDefinitions = new Map([
9386
9392
  ["dir", { kind: "value" }],
9387
9393
  ["parent-dir", { kind: "value" }],
@@ -9415,7 +9421,7 @@ async function main(args) {
9415
9421
  process.stdout.write(connectGuideText());
9416
9422
  return;
9417
9423
  case "version":
9418
- process.stdout.write(`linzumi 0.0.34-beta
9424
+ process.stdout.write(`linzumi 0.0.36-beta
9419
9425
  `);
9420
9426
  return;
9421
9427
  case "auth":
@@ -9661,7 +9667,7 @@ async function runAuthCommand(args) {
9661
9667
  process.stdout.write(helpText());
9662
9668
  return;
9663
9669
  }
9664
- const kandanUrl = required(values, "kandan-url");
9670
+ const kandanUrl = required(values, "linzumi-url");
9665
9671
  const target = parseOptionalChannelTarget(values);
9666
9672
  const token = await acquireLocalRunnerTokenDetails({
9667
9673
  kandanUrl,
@@ -9675,7 +9681,7 @@ async function runAuthCommand(args) {
9675
9681
  expiresInSeconds: token.expiresInSeconds,
9676
9682
  authFilePath: stringValue3(values, "auth-file")
9677
9683
  });
9678
- process.stdout.write(`Saved Kandan local runner auth for ${cached.kandanBaseUrl}
9684
+ process.stdout.write(`Saved Linzumi local runner auth for ${cached.kandanBaseUrl}
9679
9685
  `);
9680
9686
  }
9681
9687
  async function parseStartRunnerArgs(args, deps = {
@@ -9692,7 +9698,7 @@ async function parseStartRunnerArgs(args, deps = {
9692
9698
  process.exit(0);
9693
9699
  }
9694
9700
  rejectStartTargetingFlags(values);
9695
- const kandanUrl = stringValue3(values, "kandan-url") ?? defaultLinzumiWebSocketUrl;
9701
+ const kandanUrl = stringValue3(values, "linzumi-url") ?? defaultLinzumiWebSocketUrl;
9696
9702
  const requestedCwd = resolveUserPath(cwdArg ?? process.cwd());
9697
9703
  const cwd = assertConfiguredAllowedCwds([requestedCwd])[0] ?? requestedCwd;
9698
9704
  const explicitAllowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : [];
@@ -9709,7 +9715,7 @@ async function parseStartRunnerArgs(args, deps = {
9709
9715
  const authFilePath = stringValue3(values, "auth-file");
9710
9716
  const callbackHost = stringValue3(values, "oauth-callback-host");
9711
9717
  const reportRejectedCachedToken = () => {
9712
- process.stderr.write(`Cached Kandan local runner auth was rejected; starting OAuth.
9718
+ process.stderr.write(`Cached Linzumi local runner auth was rejected; starting OAuth.
9713
9719
  `);
9714
9720
  };
9715
9721
  const token = await deps.resolveToken({
@@ -9768,7 +9774,7 @@ async function parseStartRunnerArgs(args, deps = {
9768
9774
  channelSession: {
9769
9775
  workspaceSlug: target.workspaceSlug,
9770
9776
  channelSlug: target.channelSlug,
9771
- kandanThreadId: stringValue3(values, "kandan-thread-id"),
9777
+ kandanThreadId: stringValue3(values, "linzumi-thread-id"),
9772
9778
  listenUser: stringValue3(values, "listen-user") ?? defaultListenUserFromToken(targetToken),
9773
9779
  model: stringValue3(values, "model"),
9774
9780
  reasoningEffort: stringValue3(values, "reasoning-effort"),
@@ -9797,7 +9803,7 @@ async function parseAgentRunnerArgs(args, deps = {
9797
9803
  const tokenFile = readStoredAgentTokenFile(tokenFilePath, deps.readTextFile);
9798
9804
  const channelSlug = requiredStoredAgentChannel(tokenFile.channelId);
9799
9805
  const listenUser = stringValue3(values, "listen-user") ?? requiredStoredOwnerUsername(tokenFile.ownerUsername);
9800
- const kandanUrl = stringValue3(values, "kandan-url") ?? agentApiUrlToKandanUrl(tokenFile.apiUrl);
9806
+ const kandanUrl = stringValue3(values, "linzumi-url") ?? agentApiUrlToKandanUrl(tokenFile.apiUrl);
9801
9807
  const requestedCwdValue = cwdArg ?? stringValue3(values, "cwd");
9802
9808
  const requestedCwd = resolveUserPath(requestedCwdValue ?? process.cwd());
9803
9809
  const allowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : requestedCwdValue === undefined ? readConfiguredAllowedCwds() : assertConfiguredAllowedCwds([requestedCwd]);
@@ -9842,7 +9848,7 @@ async function parseAgentRunnerArgs(args, deps = {
9842
9848
  channelSession: {
9843
9849
  workspaceSlug: tokenFile.workspaceId,
9844
9850
  channelSlug,
9845
- kandanThreadId: stringValue3(values, "kandan-thread-id"),
9851
+ kandanThreadId: stringValue3(values, "linzumi-thread-id"),
9846
9852
  listenUser,
9847
9853
  model: stringValue3(values, "model"),
9848
9854
  reasoningEffort: stringValue3(values, "reasoning-effort"),
@@ -9929,13 +9935,15 @@ async function parseRunnerArgs(args, deps = {
9929
9935
  process.exit(0);
9930
9936
  }
9931
9937
  if (values.get("version") === true) {
9932
- process.stdout.write(`linzumi 0.0.34-beta
9938
+ process.stdout.write(`linzumi 0.0.36-beta
9933
9939
  `);
9934
9940
  process.exit(0);
9935
9941
  }
9936
9942
  const channelTarget = parseChannelSessionTarget(values);
9937
- const kandanUrl = required(values, "kandan-url");
9943
+ const kandanUrl = required(values, "linzumi-url");
9938
9944
  const cwd = stringValue3(values, "cwd") ?? process.cwd();
9945
+ const cwdAllowedCwds = assertConfiguredAllowedCwds([cwd]);
9946
+ const configuredAllowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : readConfiguredAllowedCwds();
9939
9947
  const codexBin = stringValue3(values, "codex-bin") ?? "codex";
9940
9948
  const customCodeServerBin = stringValue3(values, "code-server-bin");
9941
9949
  const explicitToken = stringValue3(values, "token");
@@ -9947,7 +9955,7 @@ async function parseRunnerArgs(args, deps = {
9947
9955
  authFilePath: stringValue3(values, "auth-file"),
9948
9956
  callbackHost: stringValue3(values, "oauth-callback-host"),
9949
9957
  reportRejectedCachedToken: () => {
9950
- process.stderr.write(`Cached Kandan local runner auth was rejected; starting OAuth.
9958
+ process.stderr.write(`Cached Linzumi local runner auth was rejected; starting OAuth.
9951
9959
  `);
9952
9960
  }
9953
9961
  });
@@ -9975,7 +9983,7 @@ async function parseRunnerArgs(args, deps = {
9975
9983
  launchTui: values.get("launch-tui") === true,
9976
9984
  fast: values.get("fast") === true,
9977
9985
  logFile: stringValue3(values, "log-file"),
9978
- allowedCwds: values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : readConfiguredAllowedCwds(),
9986
+ allowedCwds: Array.from(new Set([...cwdAllowedCwds, ...configuredAllowedCwds])),
9979
9987
  allowedForwardPorts: parseAllowedPortList(stringValue3(values, "forward-port")),
9980
9988
  codeServerBin: editorRuntime.codeServerBin,
9981
9989
  editorRuntime: editorRuntime.runtime,
@@ -9991,18 +9999,19 @@ function strictFlagValues(args, definitions = flagDefinitions) {
9991
9999
  if (arg === undefined || !arg.startsWith("--")) {
9992
10000
  throw new Error(`invalid argument: ${arg ?? ""}`);
9993
10001
  }
9994
- const key = arg.slice(2);
9995
- const definition = definitions.get(key);
10002
+ const rawKey = arg.slice(2);
10003
+ const definition = definitions.get(rawKey);
9996
10004
  if (definition === undefined) {
9997
- throw new Error(`invalid flag: --${key}`);
10005
+ throw new Error(`invalid flag: --${rawKey}`);
9998
10006
  }
10007
+ const key = flagAliases.get(rawKey) ?? rawKey;
9999
10008
  if (definition.kind === "boolean") {
10000
10009
  values.set(key, true);
10001
10010
  continue;
10002
10011
  }
10003
10012
  const next = args[index + 1];
10004
10013
  if (next === undefined || next.startsWith("--")) {
10005
- throw new Error(`missing value for --${key}`);
10014
+ throw new Error(`missing value for --${rawKey}`);
10006
10015
  }
10007
10016
  values.set(key, next);
10008
10017
  index += 1;
@@ -10089,7 +10098,7 @@ function parseChannelSession(values, token, target) {
10089
10098
  return {
10090
10099
  workspaceSlug: target.workspaceSlug,
10091
10100
  channelSlug: target.channelSlug,
10092
- kandanThreadId: stringValue3(values, "kandan-thread-id"),
10101
+ kandanThreadId: stringValue3(values, "linzumi-thread-id"),
10093
10102
  listenUser,
10094
10103
  model: stringValue3(values, "model"),
10095
10104
  reasoningEffort: stringValue3(values, "reasoning-effort"),
@@ -10185,11 +10194,12 @@ Usage:
10185
10194
  linzumi commander <folder> [options]
10186
10195
  linzumi start <folder> [options]
10187
10196
  linzumi paths list|add|remove [path]
10188
- linzumi connect --kandan-url <ws-url> --workspace <slug> --channel <slug> [options]
10189
- linzumi auth --kandan-url <ws-url> [--workspace <slug> --channel <slug>]
10197
+ linzumi connect --linzumi-url <ws-url> --workspace <slug> --channel <slug> [options]
10198
+ linzumi auth --linzumi-url <ws-url> [--workspace <slug> --channel <slug>]
10190
10199
 
10191
10200
  Required:
10192
- --kandan-url <ws-url> Linzumi backend URL, default ${defaultLinzumiWebSocketUrl}
10201
+ --linzumi-url <ws-url> Linzumi backend URL, default ${defaultLinzumiWebSocketUrl}
10202
+ (deprecated alias: --kandan-url)
10193
10203
  --token <jwt> Optional override token. Otherwise ~/.linzumi/auth.json is validated or OAuth opens.
10194
10204
  --auth-file <path> Auth cache path, default ~/.linzumi/auth.json
10195
10205
  --oauth-callback-host <ip> Callback host reachable by your browser
@@ -10197,7 +10207,8 @@ Required:
10197
10207
  Channel binding:
10198
10208
  --workspace <slug> Workspace slug
10199
10209
  --channel <slug|w/c> Channel slug, or workspace/channel
10200
- --kandan-thread-id <uuid> Resume an existing Linzumi thread instead of announcing a new root
10210
+ --linzumi-thread-id <uuid> Resume an existing Linzumi thread instead of announcing a new root
10211
+ (deprecated alias: --kandan-thread-id)
10201
10212
  --listen-user <user|all> User whose replies are accepted, default authenticated user
10202
10213
 
10203
10214
  Codex:
@@ -10212,7 +10223,7 @@ Codex:
10212
10223
  --stream-flush-ms <ms> Batch live Codex deltas before Linzumi persistence, default 150
10213
10224
  --fast Mark this runner as low-latency/fast in the availability message
10214
10225
  --log-file <path> JSONL event log path, default <cwd>/.linzumi-runner.log
10215
- --allowed-cwd <paths> Comma-separated roots where Linzumi may start new local Codex sessions
10226
+ --allowed-cwd <paths> Extra comma-separated roots where Linzumi may start local Codex sessions
10216
10227
  --forward-port <ports> Comma-separated local TCP ports Linzumi may expose as authenticated previews
10217
10228
  --code-server-bin <path> Custom development code-server executable. The default editor runtime is downloaded from Linzumi.
10218
10229
 
@@ -10309,8 +10320,9 @@ Usage:
10309
10320
  linzumi paths add <path>
10310
10321
  linzumi paths remove <path>
10311
10322
 
10312
- Trusted paths are stored in ~/.linzumi/config.json. linzumi connect uses them
10313
- unless --allowed-cwd is passed for that runner process.
10323
+ Trusted paths are stored in ~/.linzumi/config.json. linzumi connect always
10324
+ trusts its selected cwd for that runner process and also uses configured paths
10325
+ unless --allowed-cwd is passed with explicit extra roots.
10314
10326
  `;
10315
10327
  }
10316
10328
  function startHelpText() {
@@ -10326,7 +10338,8 @@ What it does:
10326
10338
  previews, and the browser VS Code editor.
10327
10339
 
10328
10340
  Options:
10329
- --kandan-url <ws-url> Linzumi backend URL, default ${defaultLinzumiWebSocketUrl}
10341
+ --linzumi-url <ws-url> Linzumi backend URL, default ${defaultLinzumiWebSocketUrl}
10342
+ (deprecated alias: --kandan-url)
10330
10343
  --token <jwt> Optional scoped local-runner token override
10331
10344
  --auth-file <path> Auth cache path, default ~/.linzumi/auth.json
10332
10345
  --oauth-callback-host <ip> Callback host reachable by your browser
@@ -10366,23 +10379,24 @@ What it does:
10366
10379
 
10367
10380
  Options:
10368
10381
  --agent-token-file <path> Agent token cache, default ~/.linzumi/agent-token.json
10369
- --kandan-url <ws-url> Kandan websocket base URL. Defaults deterministically from the stored apiUrl.
10382
+ --linzumi-url <ws-url> Linzumi websocket base URL. Defaults deterministically from the stored apiUrl.
10383
+ (deprecated alias: --kandan-url)
10370
10384
  --runner-id <id> Stable Commander id
10371
10385
  --codex-bin <path> Codex executable, default codex
10372
- --code-server-bin <path> Custom development code-server executable. By default Kandan installs the approved editor runtime.
10386
+ --code-server-bin <path> Custom development code-server executable. By default Linzumi installs the approved editor runtime.
10373
10387
  --listen-user <user> Human whose replies Codex may accept, default owner from claim
10374
10388
  --model <name> Model requested for Codex sessions
10375
10389
  --reasoning-effort <value> Reasoning effort requested for Codex sessions
10376
- --sandbox <value> Sandbox metadata shown in Kandan
10377
- --approval-policy <value> Approval-policy metadata shown in Kandan
10378
- --forward-port <ports> Comma-separated local TCP ports Kandan may expose as previews
10390
+ --sandbox <value> Sandbox metadata shown in Linzumi
10391
+ --approval-policy <value> Approval-policy metadata shown in Linzumi
10392
+ --forward-port <ports> Comma-separated local TCP ports Linzumi may expose as previews
10379
10393
  --allowed-cwd <paths> Override ~/.linzumi/config.json or selected folder with comma-separated trusted roots
10380
10394
  --fast Mark this Commander as low-latency/fast in Linzumi
10381
10395
 
10382
10396
  Examples:
10383
10397
  linzumi paths add "$PWD"
10384
10398
  linzumi commander daemon --runner-id hello-world-commander
10385
- linzumi commander ~/code/my-app --kandan-url ws://127.0.0.1:4162 --runner-id local-qa-commander
10399
+ linzumi commander ~/code/my-app --linzumi-url ws://127.0.0.1:4162 --runner-id local-qa-commander
10386
10400
  `;
10387
10401
  }
10388
10402
  function connectGuideText() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.34-beta",
3
+ "version": "0.0.36-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": {