@linzumi/cli 0.0.34-beta → 0.0.35-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.
- package/README.md +58 -0
- package/dist/index.js +39 -28
- 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.35-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.
|
|
@@ -65,6 +116,13 @@ linzumi paths add ~/code/my-app
|
|
|
65
116
|
The trust list lives at `~/.linzumi/config.json`. The bootstrap agent
|
|
66
117
|
adds `/tmp/hello_linzumi` for you on first run.
|
|
67
118
|
|
|
119
|
+
## When something looks wrong
|
|
120
|
+
|
|
121
|
+
Run `linzumi --help` or reconnect with `linzumi start ~/code/my-app`.
|
|
122
|
+
If the browser says no runner is connected, check that the terminal
|
|
123
|
+
running the Commander is still open and that the folder is listed in
|
|
124
|
+
`~/.linzumi/config.json`.
|
|
125
|
+
|
|
68
126
|
---
|
|
69
127
|
|
|
70
128
|
*Slack-shaped on the surface. A multiplayer compiler underneath.*
|
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("--
|
|
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.
|
|
9424
|
+
process.stdout.write(`linzumi 0.0.35-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, "
|
|
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
|
|
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, "
|
|
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
|
|
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, "
|
|
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, "
|
|
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, "
|
|
9851
|
+
kandanThreadId: stringValue3(values, "linzumi-thread-id"),
|
|
9846
9852
|
listenUser,
|
|
9847
9853
|
model: stringValue3(values, "model"),
|
|
9848
9854
|
reasoningEffort: stringValue3(values, "reasoning-effort"),
|
|
@@ -9929,12 +9935,12 @@ 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.
|
|
9938
|
+
process.stdout.write(`linzumi 0.0.35-beta
|
|
9933
9939
|
`);
|
|
9934
9940
|
process.exit(0);
|
|
9935
9941
|
}
|
|
9936
9942
|
const channelTarget = parseChannelSessionTarget(values);
|
|
9937
|
-
const kandanUrl = required(values, "
|
|
9943
|
+
const kandanUrl = required(values, "linzumi-url");
|
|
9938
9944
|
const cwd = stringValue3(values, "cwd") ?? process.cwd();
|
|
9939
9945
|
const codexBin = stringValue3(values, "codex-bin") ?? "codex";
|
|
9940
9946
|
const customCodeServerBin = stringValue3(values, "code-server-bin");
|
|
@@ -9947,7 +9953,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
9947
9953
|
authFilePath: stringValue3(values, "auth-file"),
|
|
9948
9954
|
callbackHost: stringValue3(values, "oauth-callback-host"),
|
|
9949
9955
|
reportRejectedCachedToken: () => {
|
|
9950
|
-
process.stderr.write(`Cached
|
|
9956
|
+
process.stderr.write(`Cached Linzumi local runner auth was rejected; starting OAuth.
|
|
9951
9957
|
`);
|
|
9952
9958
|
}
|
|
9953
9959
|
});
|
|
@@ -9991,18 +9997,19 @@ function strictFlagValues(args, definitions = flagDefinitions) {
|
|
|
9991
9997
|
if (arg === undefined || !arg.startsWith("--")) {
|
|
9992
9998
|
throw new Error(`invalid argument: ${arg ?? ""}`);
|
|
9993
9999
|
}
|
|
9994
|
-
const
|
|
9995
|
-
const definition = definitions.get(
|
|
10000
|
+
const rawKey = arg.slice(2);
|
|
10001
|
+
const definition = definitions.get(rawKey);
|
|
9996
10002
|
if (definition === undefined) {
|
|
9997
|
-
throw new Error(`invalid flag: --${
|
|
10003
|
+
throw new Error(`invalid flag: --${rawKey}`);
|
|
9998
10004
|
}
|
|
10005
|
+
const key = flagAliases.get(rawKey) ?? rawKey;
|
|
9999
10006
|
if (definition.kind === "boolean") {
|
|
10000
10007
|
values.set(key, true);
|
|
10001
10008
|
continue;
|
|
10002
10009
|
}
|
|
10003
10010
|
const next = args[index + 1];
|
|
10004
10011
|
if (next === undefined || next.startsWith("--")) {
|
|
10005
|
-
throw new Error(`missing value for --${
|
|
10012
|
+
throw new Error(`missing value for --${rawKey}`);
|
|
10006
10013
|
}
|
|
10007
10014
|
values.set(key, next);
|
|
10008
10015
|
index += 1;
|
|
@@ -10089,7 +10096,7 @@ function parseChannelSession(values, token, target) {
|
|
|
10089
10096
|
return {
|
|
10090
10097
|
workspaceSlug: target.workspaceSlug,
|
|
10091
10098
|
channelSlug: target.channelSlug,
|
|
10092
|
-
kandanThreadId: stringValue3(values, "
|
|
10099
|
+
kandanThreadId: stringValue3(values, "linzumi-thread-id"),
|
|
10093
10100
|
listenUser,
|
|
10094
10101
|
model: stringValue3(values, "model"),
|
|
10095
10102
|
reasoningEffort: stringValue3(values, "reasoning-effort"),
|
|
@@ -10185,11 +10192,12 @@ Usage:
|
|
|
10185
10192
|
linzumi commander <folder> [options]
|
|
10186
10193
|
linzumi start <folder> [options]
|
|
10187
10194
|
linzumi paths list|add|remove [path]
|
|
10188
|
-
linzumi connect --
|
|
10189
|
-
linzumi auth --
|
|
10195
|
+
linzumi connect --linzumi-url <ws-url> --workspace <slug> --channel <slug> [options]
|
|
10196
|
+
linzumi auth --linzumi-url <ws-url> [--workspace <slug> --channel <slug>]
|
|
10190
10197
|
|
|
10191
10198
|
Required:
|
|
10192
|
-
--
|
|
10199
|
+
--linzumi-url <ws-url> Linzumi backend URL, default ${defaultLinzumiWebSocketUrl}
|
|
10200
|
+
(deprecated alias: --kandan-url)
|
|
10193
10201
|
--token <jwt> Optional override token. Otherwise ~/.linzumi/auth.json is validated or OAuth opens.
|
|
10194
10202
|
--auth-file <path> Auth cache path, default ~/.linzumi/auth.json
|
|
10195
10203
|
--oauth-callback-host <ip> Callback host reachable by your browser
|
|
@@ -10197,7 +10205,8 @@ Required:
|
|
|
10197
10205
|
Channel binding:
|
|
10198
10206
|
--workspace <slug> Workspace slug
|
|
10199
10207
|
--channel <slug|w/c> Channel slug, or workspace/channel
|
|
10200
|
-
--
|
|
10208
|
+
--linzumi-thread-id <uuid> Resume an existing Linzumi thread instead of announcing a new root
|
|
10209
|
+
(deprecated alias: --kandan-thread-id)
|
|
10201
10210
|
--listen-user <user|all> User whose replies are accepted, default authenticated user
|
|
10202
10211
|
|
|
10203
10212
|
Codex:
|
|
@@ -10326,7 +10335,8 @@ What it does:
|
|
|
10326
10335
|
previews, and the browser VS Code editor.
|
|
10327
10336
|
|
|
10328
10337
|
Options:
|
|
10329
|
-
--
|
|
10338
|
+
--linzumi-url <ws-url> Linzumi backend URL, default ${defaultLinzumiWebSocketUrl}
|
|
10339
|
+
(deprecated alias: --kandan-url)
|
|
10330
10340
|
--token <jwt> Optional scoped local-runner token override
|
|
10331
10341
|
--auth-file <path> Auth cache path, default ~/.linzumi/auth.json
|
|
10332
10342
|
--oauth-callback-host <ip> Callback host reachable by your browser
|
|
@@ -10366,23 +10376,24 @@ What it does:
|
|
|
10366
10376
|
|
|
10367
10377
|
Options:
|
|
10368
10378
|
--agent-token-file <path> Agent token cache, default ~/.linzumi/agent-token.json
|
|
10369
|
-
--
|
|
10379
|
+
--linzumi-url <ws-url> Linzumi websocket base URL. Defaults deterministically from the stored apiUrl.
|
|
10380
|
+
(deprecated alias: --kandan-url)
|
|
10370
10381
|
--runner-id <id> Stable Commander id
|
|
10371
10382
|
--codex-bin <path> Codex executable, default codex
|
|
10372
|
-
--code-server-bin <path> Custom development code-server executable. By default
|
|
10383
|
+
--code-server-bin <path> Custom development code-server executable. By default Linzumi installs the approved editor runtime.
|
|
10373
10384
|
--listen-user <user> Human whose replies Codex may accept, default owner from claim
|
|
10374
10385
|
--model <name> Model requested for Codex sessions
|
|
10375
10386
|
--reasoning-effort <value> Reasoning effort requested for Codex sessions
|
|
10376
|
-
--sandbox <value> Sandbox metadata shown in
|
|
10377
|
-
--approval-policy <value> Approval-policy metadata shown in
|
|
10378
|
-
--forward-port <ports> Comma-separated local TCP ports
|
|
10387
|
+
--sandbox <value> Sandbox metadata shown in Linzumi
|
|
10388
|
+
--approval-policy <value> Approval-policy metadata shown in Linzumi
|
|
10389
|
+
--forward-port <ports> Comma-separated local TCP ports Linzumi may expose as previews
|
|
10379
10390
|
--allowed-cwd <paths> Override ~/.linzumi/config.json or selected folder with comma-separated trusted roots
|
|
10380
10391
|
--fast Mark this Commander as low-latency/fast in Linzumi
|
|
10381
10392
|
|
|
10382
10393
|
Examples:
|
|
10383
10394
|
linzumi paths add "$PWD"
|
|
10384
10395
|
linzumi commander daemon --runner-id hello-world-commander
|
|
10385
|
-
linzumi commander ~/code/my-app --
|
|
10396
|
+
linzumi commander ~/code/my-app --linzumi-url ws://127.0.0.1:4162 --runner-id local-qa-commander
|
|
10386
10397
|
`;
|
|
10387
10398
|
}
|
|
10388
10399
|
function connectGuideText() {
|
package/package.json
CHANGED