@linzumi/cli 0.0.4-beta → 0.0.6-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 +197 -85
- package/package.json +17 -11
- package/src/authResolution.ts +2 -0
- package/src/boundedCache.ts +57 -0
- package/src/channelSession.ts +907 -453
- package/src/codexRuntimeOptions.ts +80 -0
- package/src/dependencyStatus.ts +198 -0
- package/src/forwardTunnel.ts +834 -0
- package/src/forwardTunnelProtocol.ts +324 -0
- package/src/index.ts +414 -30
- package/src/kandanTls.ts +86 -0
- package/src/localCapabilities.ts +130 -0
- package/src/localCodexMessageState.ts +135 -0
- package/src/localCodexTurnState.ts +108 -0
- package/src/localEditor.ts +963 -0
- package/src/localEditorRuntime.ts +603 -0
- package/src/localForwarding.ts +500 -0
- package/src/oauth.ts +135 -4
- package/src/pendingKandanMessageQueue.ts +109 -0
- package/src/phoenix.ts +25 -1
- package/src/portForwardApproval.ts +181 -0
- package/src/portForwardWatcher.ts +404 -0
- package/src/protocol.ts +97 -3
- package/src/runner.ts +413 -28
- package/src/streamDeltaCoalescing.ts +129 -0
- package/src/streamDeltaQueue.ts +102 -0
package/README.md
CHANGED
|
@@ -1,152 +1,264 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Linzumi CLI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
| / \ .-. / \ |
|
|
10
|
-
\ \ (o o) / /
|
|
11
|
-
'._'._ \_/ _.'_.'
|
|
12
|
-
| | |
|
|
13
|
-
| /|\ |
|
|
14
|
-
|___/ | \___|
|
|
15
|
-
\_|_/
|
|
16
|
-
/ \
|
|
17
|
-
```
|
|
3
|
+
Connect your computer to Kandan so you can run Codex locally from the browser.
|
|
4
|
+
|
|
5
|
+
Linzumi gives Kandan a secure, authenticated bridge to your own machine. You
|
|
6
|
+
keep the code, shells, editors, and preview servers local. Kandan gives you the
|
|
7
|
+
web UI, sharing controls, HTTPS editor access, forwarded previews, and Codex
|
|
8
|
+
session orchestration.
|
|
18
9
|
|
|
19
|
-
|
|
10
|
+
## Copy And Paste
|
|
20
11
|
|
|
21
|
-
|
|
22
|
-
runner. It wraps the same local runner that was previously started with
|
|
23
|
-
`bun run start -- ...`.
|
|
12
|
+
Use a Chromium-based browser such as Chrome, Edge, Arc, or Brave.
|
|
24
13
|
|
|
25
|
-
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @linzumi/cli@beta && linzumi start ~/code
|
|
16
|
+
```
|
|
26
17
|
|
|
27
|
-
|
|
18
|
+
To pin this exact beta:
|
|
28
19
|
|
|
29
20
|
```bash
|
|
30
|
-
npm install -g @linzumi/cli@0.0.
|
|
21
|
+
npm install -g @linzumi/cli@0.0.6-beta && linzumi start ~/code
|
|
31
22
|
```
|
|
32
23
|
|
|
33
|
-
|
|
24
|
+
Use a different folder if your projects live somewhere else:
|
|
34
25
|
|
|
35
26
|
```bash
|
|
36
|
-
npm install -g @linzumi/cli@beta
|
|
27
|
+
npm install -g @linzumi/cli@beta && linzumi start ~/work/my-app
|
|
37
28
|
```
|
|
38
29
|
|
|
39
|
-
|
|
30
|
+
That command opens Kandan, walks you through signup or sign-in, connects this
|
|
31
|
+
computer, and lets Kandan start Codex against the folder you chose.
|
|
32
|
+
|
|
33
|
+
## What You Can Do
|
|
34
|
+
|
|
35
|
+
After `linzumi start` is connected, try this from Kandan:
|
|
36
|
+
|
|
37
|
+
1. Start a Codex session.
|
|
38
|
+
2. Ask Codex to inspect or edit a file in the folder you allowed.
|
|
39
|
+
3. Open the local editor from Kandan.
|
|
40
|
+
4. Start a dev server locally, then open its forwarded preview from Kandan.
|
|
41
|
+
5. Share editor or preview access with another user.
|
|
42
|
+
|
|
43
|
+
The editor and previews open on Kandan HTTPS URLs. They should not expose
|
|
44
|
+
`localhost` URLs to the browser.
|
|
45
|
+
|
|
46
|
+
## What Linzumi Runs Locally
|
|
47
|
+
|
|
48
|
+
The CLI starts a local runner process on your computer. That runner:
|
|
49
|
+
|
|
50
|
+
- launches or connects to Codex locally
|
|
51
|
+
- downloads the Kandan-approved editor runtime when needed
|
|
52
|
+
- starts code-server for the allowed folder
|
|
53
|
+
- exposes only explicitly approved local ports through Kandan
|
|
54
|
+
- keeps Kandan auth cookies and bearer tokens away from local services
|
|
55
|
+
|
|
56
|
+
Kandan remains the control plane. Browser traffic terminates at Kandan first,
|
|
57
|
+
where auth, grants, and allowed-port policy are enforced before traffic is sent
|
|
58
|
+
through the runner tunnel.
|
|
59
|
+
|
|
60
|
+
In the local-service hop, `127.0.0.1` means your computer, not the Kandan
|
|
61
|
+
server.
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
Install these before running the beta:
|
|
40
66
|
|
|
41
67
|
```bash
|
|
68
|
+
node --version
|
|
69
|
+
npm --version
|
|
42
70
|
bun --version
|
|
43
71
|
codex --version
|
|
44
|
-
linzumi --version
|
|
45
72
|
```
|
|
46
73
|
|
|
47
|
-
Expected
|
|
74
|
+
Expected:
|
|
75
|
+
|
|
76
|
+
- Node.js 20 or newer
|
|
77
|
+
- npm
|
|
78
|
+
- Bun 1.2 or newer
|
|
79
|
+
- Codex CLI
|
|
80
|
+
- Chrome, Edge, Arc, Brave, or another Chromium-based browser
|
|
81
|
+
|
|
82
|
+
Safari is semi-supported for now. Use Chromium for the best editor and
|
|
83
|
+
collaboration behavior.
|
|
84
|
+
|
|
85
|
+
## First Run
|
|
48
86
|
|
|
49
87
|
```bash
|
|
50
|
-
linzumi
|
|
88
|
+
linzumi start ~/code
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
What happens:
|
|
92
|
+
|
|
93
|
+
1. The CLI opens Kandan.
|
|
94
|
+
2. You sign up or sign in.
|
|
95
|
+
3. Kandan asks permission to connect this computer.
|
|
96
|
+
4. The CLI stores a scoped local-runner token.
|
|
97
|
+
5. The CLI checks Bun, Codex, and the Kandan editor runtime.
|
|
98
|
+
6. Kandan shows the computer as connected.
|
|
99
|
+
|
|
100
|
+
The first editor launch can download a Kandan-approved runtime archive. Later
|
|
101
|
+
runs reuse the verified runtime from:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
~/.linzumi/editor-runtimes
|
|
51
105
|
```
|
|
52
106
|
|
|
53
|
-
|
|
107
|
+
The production path does not use a random local `code-server` install. Kandan
|
|
108
|
+
publishes a checksummed runtime manifest, and the CLI only advertises editor
|
|
109
|
+
readiness after that runtime is verified locally.
|
|
110
|
+
|
|
111
|
+
## Good Things To Try
|
|
54
112
|
|
|
55
|
-
|
|
113
|
+
Start from a real project:
|
|
56
114
|
|
|
57
115
|
```bash
|
|
58
|
-
|
|
116
|
+
linzumi start ~/code/my-app
|
|
117
|
+
```
|
|
59
118
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
--codex-bin codex \
|
|
65
|
-
--launch-tui
|
|
119
|
+
Then in Kandan:
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
Summarize this repository and tell me how to run it locally.
|
|
66
123
|
```
|
|
67
124
|
|
|
68
|
-
|
|
69
|
-
rejected, it opens the OAuth flow and saves the refreshed auth cache.
|
|
70
|
-
By default it listens for replies from the authenticated Kandan user.
|
|
125
|
+
If your app starts a dev server:
|
|
71
126
|
|
|
72
|
-
|
|
73
|
-
|
|
127
|
+
```bash
|
|
128
|
+
npm run dev
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Open the detected forwarded port from Kandan. If a port is not auto-detected,
|
|
132
|
+
restart with an explicit approved port:
|
|
74
133
|
|
|
75
134
|
```bash
|
|
76
|
-
linzumi
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
135
|
+
linzumi start ~/code/my-app --forward-port 3000
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
For a shared editor test, invite another user, open the local editor, and edit
|
|
139
|
+
the same file. Chromium should show remote cursor labels and selections.
|
|
140
|
+
|
|
141
|
+
## Hosted Kandan
|
|
142
|
+
|
|
143
|
+
For a hosted Kandan deployment, point the CLI at the hosted websocket URL:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
linzumi start ~/code/my-app --kandan-url wss://<your-kandan-host>
|
|
85
147
|
```
|
|
86
148
|
|
|
87
|
-
|
|
149
|
+
For Render-hosted Kandan, public TLS should work without local certificate
|
|
150
|
+
flags. The CLI derives the HTTPS API origin from the websocket URL for OAuth,
|
|
151
|
+
runtime manifest download, and runtime archive download.
|
|
88
152
|
|
|
89
|
-
|
|
153
|
+
For local development with a private CA:
|
|
90
154
|
|
|
91
155
|
```bash
|
|
92
|
-
linzumi
|
|
156
|
+
KANDAN_TLS_CA_FILE=/path/to/ca.crt linzumi start ~/code/my-app \
|
|
157
|
+
--kandan-url wss://linzumi.io:4140
|
|
93
158
|
```
|
|
94
159
|
|
|
95
|
-
|
|
160
|
+
## Tailscale Development
|
|
161
|
+
|
|
162
|
+
If your browser needs to reach a local Kandan server through Tailscale:
|
|
96
163
|
|
|
97
164
|
```bash
|
|
98
|
-
linzumi
|
|
99
|
-
--kandan-url
|
|
100
|
-
--
|
|
101
|
-
--channel seans-playground \
|
|
102
|
-
--codex-bin codex \
|
|
103
|
-
--launch-tui
|
|
165
|
+
linzumi start ~/code/my-app \
|
|
166
|
+
--kandan-url ws://100.71.192.98:4162 \
|
|
167
|
+
--oauth-callback-host 100.71.192.98
|
|
104
168
|
```
|
|
105
169
|
|
|
106
|
-
|
|
170
|
+
Use your own Tailscale IP.
|
|
107
171
|
|
|
108
172
|
## Commands
|
|
109
173
|
|
|
110
|
-
Supported commands right now:
|
|
111
|
-
|
|
112
174
|
```bash
|
|
113
175
|
linzumi
|
|
114
176
|
linzumi --help
|
|
115
177
|
linzumi --version
|
|
178
|
+
linzumi start <folder>
|
|
116
179
|
linzumi connect --help
|
|
117
180
|
linzumi connect [runner options]
|
|
118
181
|
linzumi auth [auth options]
|
|
119
182
|
```
|
|
120
183
|
|
|
121
|
-
|
|
122
|
-
|
|
184
|
+
Most people should use `linzumi start`.
|
|
185
|
+
|
|
186
|
+
`linzumi connect` is the lower-level command for connecting to an explicit
|
|
187
|
+
workspace and channel:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
linzumi connect \
|
|
191
|
+
--kandan-url wss://serve.kandanai.com \
|
|
192
|
+
--workspace default \
|
|
193
|
+
--channel general \
|
|
194
|
+
--cwd ~/code/my-app
|
|
195
|
+
```
|
|
123
196
|
|
|
124
197
|
## Useful Options
|
|
125
198
|
|
|
126
199
|
```bash
|
|
127
|
-
--kandan-url <ws-url> Kandan websocket URL
|
|
128
|
-
--workspace <slug> Workspace slug, for example linzumi
|
|
129
|
-
--channel <slug|w/c> Channel slug, or workspace/channel
|
|
130
|
-
--kandan-thread-id <uuid> Resume an existing Kandan thread
|
|
131
|
-
--listen-user <user|all> User whose replies are accepted, default authenticated user
|
|
132
|
-
--cwd <path> Working directory for Codex
|
|
133
|
-
--codex-bin <path> Codex executable, default codex
|
|
134
|
-
--model <name> Codex model
|
|
135
|
-
--reasoning-effort <value> Codex reasoning effort
|
|
136
|
-
--fast Request the fast service tier
|
|
137
|
-
--launch-tui Launch codex --remote against the app-server
|
|
200
|
+
--kandan-url <ws-url> Kandan websocket URL
|
|
138
201
|
--oauth-callback-host <ip> Callback host reachable by your browser
|
|
202
|
+
--runner-id <id> Stable id for this computer
|
|
203
|
+
--codex-bin <path> Codex executable, default codex
|
|
204
|
+
--model <name> Codex model metadata shown in Kandan
|
|
205
|
+
--reasoning-effort <value> Codex reasoning metadata shown in Kandan
|
|
206
|
+
--fast Mark this runner as low-latency
|
|
207
|
+
--forward-port <ports> Comma-separated local ports Kandan may expose
|
|
208
|
+
--allowed-cwd <paths> Comma-separated roots Kandan may use
|
|
209
|
+
--log-file <path> JSONL runner event log
|
|
139
210
|
```
|
|
140
211
|
|
|
141
|
-
|
|
212
|
+
`--code-server-bin` exists only as a development override. It is not the
|
|
213
|
+
supported production editor path.
|
|
142
214
|
|
|
143
215
|
## Troubleshooting
|
|
144
216
|
|
|
145
|
-
|
|
146
|
-
|
|
217
|
+
Check the CLI version:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
linzumi --version
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Expected:
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
linzumi 0.0.6-beta
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
If `linzumi` is not found, your global npm bin directory is not on `PATH`.
|
|
230
|
+
|
|
231
|
+
If `bun` is not found, install Bun and rerun `linzumi start`.
|
|
232
|
+
|
|
233
|
+
If `codex` is not found, install or configure the Codex CLI, or pass
|
|
234
|
+
`--codex-bin`.
|
|
147
235
|
|
|
148
|
-
If
|
|
149
|
-
|
|
236
|
+
If OAuth opens but does not return to the CLI, pass an
|
|
237
|
+
`--oauth-callback-host` that your browser can reach.
|
|
238
|
+
|
|
239
|
+
If the editor does not become ready, do not install a local code-server package
|
|
240
|
+
as a workaround. The server-managed runtime must download and verify cleanly.
|
|
241
|
+
Rerun the CLI and check the runner log.
|
|
242
|
+
|
|
243
|
+
If collaboration behaves oddly in Safari, retry in Chromium. Safari is currently
|
|
244
|
+
semi-supported.
|
|
245
|
+
|
|
246
|
+
## For Kandan Release Engineers
|
|
247
|
+
|
|
248
|
+
The production contract is:
|
|
249
|
+
|
|
250
|
+
1. Build the server-approved editor runtime archive.
|
|
251
|
+
2. Publish the manifest and archive from Kandan.
|
|
252
|
+
3. Publish the CLI beta.
|
|
253
|
+
4. The CLI downloads only the approved runtime archive.
|
|
254
|
+
5. The CLI verifies the archive SHA before advertising editor readiness.
|
|
255
|
+
|
|
256
|
+
For local package verification:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
bun test
|
|
260
|
+
npm pack --dry-run
|
|
261
|
+
```
|
|
150
262
|
|
|
151
|
-
|
|
152
|
-
|
|
263
|
+
The npm package must include this README, `bin/linzumi.js`, and the `src`
|
|
264
|
+
runtime files.
|
package/package.json
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linzumi/cli",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.6-beta",
|
|
4
|
+
"description": "Connect your computer to Kandan for local Codex sessions, editors, and forwarded previews",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"linzumi": "bin/linzumi.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
+
"README.md",
|
|
10
11
|
"bin",
|
|
11
|
-
"src"
|
|
12
|
-
"README.md"
|
|
12
|
+
"src"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
},
|
|
18
|
-
"engines": {
|
|
19
|
-
"bun": ">=1.1.0",
|
|
20
|
-
"node": ">=22.0.0"
|
|
15
|
+
"test": "bun test",
|
|
16
|
+
"pack:dry-run": "npm pack --dry-run"
|
|
21
17
|
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"linzumi",
|
|
20
|
+
"kandan",
|
|
21
|
+
"codex",
|
|
22
|
+
"local-runner"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
22
25
|
"publishConfig": {
|
|
23
26
|
"access": "public"
|
|
24
27
|
},
|
|
25
|
-
"
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20",
|
|
30
|
+
"bun": ">=1.2.0"
|
|
31
|
+
}
|
|
26
32
|
}
|
package/src/authResolution.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type LocalRunnerTokenResolutionArgs = {
|
|
|
13
13
|
readonly explicitToken?: string | undefined;
|
|
14
14
|
readonly workspaceSlug?: string | undefined;
|
|
15
15
|
readonly channelSlug?: string | undefined;
|
|
16
|
+
readonly onboarding?: "start" | undefined;
|
|
16
17
|
readonly authFilePath?: string | undefined;
|
|
17
18
|
readonly callbackHost?: string | undefined;
|
|
18
19
|
readonly reportRejectedCachedToken?: (() => void) | undefined;
|
|
@@ -61,6 +62,7 @@ async function acquireAndCacheToken(args: LocalRunnerTokenResolutionArgs): Promi
|
|
|
61
62
|
kandanUrl: args.kandanUrl,
|
|
62
63
|
workspaceSlug: args.workspaceSlug,
|
|
63
64
|
channelSlug: args.channelSlug,
|
|
65
|
+
onboarding: args.onboarding,
|
|
64
66
|
callbackHost: args.callbackHost,
|
|
65
67
|
});
|
|
66
68
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
- Date: 2026-04-26
|
|
3
|
+
Spec: kandan/server_v2/plans/2026-04-26-local-codex-driver-worldclass-spec.md
|
|
4
|
+
Relationship: Provides bounded in-memory registries for local Codex runner
|
|
5
|
+
state so long turns keep O(1) keyed lookup while retaining deterministic
|
|
6
|
+
eviction order for transcript reconciliation.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type BoundedCache<TValue> = {
|
|
10
|
+
readonly limit: number;
|
|
11
|
+
readonly values: Map<string, TValue>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function createBoundedCache<TValue>(limit: number): BoundedCache<TValue> {
|
|
15
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
16
|
+
throw new Error(`invalid bounded cache limit: ${limit}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
limit,
|
|
21
|
+
values: new Map(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getBoundedCacheValue<TValue>(
|
|
26
|
+
cache: BoundedCache<TValue>,
|
|
27
|
+
key: string,
|
|
28
|
+
): TValue | undefined {
|
|
29
|
+
return cache.values.get(key);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function rememberBoundedCacheValue<TValue>(
|
|
33
|
+
cache: BoundedCache<TValue>,
|
|
34
|
+
key: string,
|
|
35
|
+
value: TValue,
|
|
36
|
+
): void {
|
|
37
|
+
cache.values.set(key, value);
|
|
38
|
+
|
|
39
|
+
while (cache.values.size > cache.limit) {
|
|
40
|
+
const evicted = cache.values.keys().next().value;
|
|
41
|
+
|
|
42
|
+
if (evicted !== undefined) {
|
|
43
|
+
cache.values.delete(evicted);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function forgetBoundedCacheValue<TValue>(
|
|
49
|
+
cache: BoundedCache<TValue>,
|
|
50
|
+
key: string,
|
|
51
|
+
): void {
|
|
52
|
+
cache.values.delete(key);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function boundedCacheValues<TValue>(cache: BoundedCache<TValue>): TValue[] {
|
|
56
|
+
return [...cache.values.values()];
|
|
57
|
+
}
|