@linzumi/cli 0.0.23-beta → 0.0.25-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 +38 -20
- package/dist/index.js +249 -175
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,8 +32,20 @@ starts that hot-reload app on your computer, creates the shared support
|
|
|
32
32
|
channel, starts a Linzumi Codex session in a work thread, and opens the
|
|
33
33
|
browser editor pointed at the demo app.
|
|
34
34
|
|
|
35
|
+
Terms:
|
|
36
|
+
|
|
37
|
+
- **Bootstrapper Codex**: the outer Codex started by the pasted command. It
|
|
38
|
+
sets up Linzumi and local processes, but does not edit the demo app.
|
|
39
|
+
- **Linzumi Commander**: the long-running local bridge started with
|
|
40
|
+
`linzumi commander`; it owns the secure tunnel, trusted folder, browser
|
|
41
|
+
editor, and inner Codex launch.
|
|
42
|
+
- **Linzumi Codex session**: the inner agent running inside the Linzumi thread;
|
|
43
|
+
it edits `/tmp/hello_linzumi` and posts progress.
|
|
44
|
+
- **Human**: the workspace owner who opens the one-time login link and watches
|
|
45
|
+
the work in `#general` and `#linzumi-support`.
|
|
46
|
+
|
|
35
47
|
The snippet uses automatic command approval and full local process access
|
|
36
|
-
because the bootstrapper must start a real
|
|
48
|
+
because the bootstrapper must start a real Linzumi Commander and browser editor on this
|
|
37
49
|
computer. It is intentionally not the dangerous sandbox-bypass mode.
|
|
38
50
|
|
|
39
51
|
Today, your AI coding agent has two homes, both bad.
|
|
@@ -104,7 +116,7 @@ stays available for compatibility. The bootstrap agent will confirm your
|
|
|
104
116
|
email and workspace choice up front, ask for the emailed code after signup
|
|
105
117
|
sends it, say hello to `@sean` in the shared support channel,
|
|
106
118
|
generate `/tmp/hello_linzumi`, start its hot-reload Node server, start the
|
|
107
|
-
|
|
119
|
+
Commander for that folder, start a Linzumi Codex session in a work thread, and
|
|
108
120
|
open the browser editor. The Linzumi Codex session then adds confetti to the
|
|
109
121
|
demo page while you watch. Workspace names are plain display names from 2 to
|
|
110
122
|
100 characters; Linzumi generates the URL-safe workspace slug.
|
|
@@ -118,24 +130,30 @@ npx -y @linzumi/cli@latest signup --email alice@example.com --workspace-name "Al
|
|
|
118
130
|
npx -y @linzumi/cli@latest claim --pending <pending_id> --code <XXXX-XXXX>
|
|
119
131
|
npx -y @linzumi/cli@latest channel post <support_channel_id> "Hello @sean, starting this launch run."
|
|
120
132
|
npx -y @linzumi/cli@latest thread new "Hello Linzumi confetti" --message "Preparing the Hello Linzumi demo. Next I will generate /tmp/hello_linzumi, start its hot-reload server bound to 0.0.0.0 on port 8787, and ask Linzumi Codex to add confetti when the page loads."
|
|
133
|
+
commander_id="hello-linzumi-commander-${thread_id%%-*}"
|
|
121
134
|
npx -y @linzumi/cli@latest init-hello-linzumi-demo-app --parent-dir /tmp --name hello_linzumi --host 0.0.0.0 --port 8787 --reset --json
|
|
122
|
-
(cd /tmp/hello_linzumi &&
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
--
|
|
135
|
+
project_dir="$(cd /tmp/hello_linzumi && pwd -P)"
|
|
136
|
+
(cd "$project_dir" && npm run dev > "$project_dir/dev.log" 2>&1 &)
|
|
137
|
+
npx -y @linzumi/cli@latest commander "$project_dir" \
|
|
138
|
+
--runner-id "$commander_id" \
|
|
139
|
+
--allowed-cwd "$project_dir" \
|
|
126
140
|
--forward-port 8787 \
|
|
127
141
|
--sandbox danger-full-access \
|
|
128
142
|
--approval-policy never
|
|
129
143
|
```
|
|
130
144
|
|
|
131
|
-
The agent-owned
|
|
145
|
+
The agent-owned Commander reads `~/.linzumi/agent-token.json`, uses the
|
|
132
146
|
workspace/channel scope from the approval flow, trusts only the selected
|
|
133
|
-
folder,
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
folder, marks that same folder trusted in Codex's normal project config so
|
|
148
|
+
Codex does not stop for an interactive trust prompt, advertises the explicit
|
|
149
|
+
preview port, and listens only to the approving human unless `--listen-user` is
|
|
150
|
+
explicitly passed. Use a unique
|
|
151
|
+
Commander id per launch thread; Linzumi stores trusted-folder config per
|
|
152
|
+
Commander id, so reusing an old fixed id can pick up stale allowed-cwd config.
|
|
153
|
+
The bootstrap agent waits for `Runner connected:` before starting Codex in the Linzumi
|
|
136
154
|
thread.
|
|
137
155
|
|
|
138
|
-
By default, the
|
|
156
|
+
By default, the Commander downloads the Linzumi-approved `code-server`
|
|
139
157
|
runtime for your platform and verifies its checksum before enabling the
|
|
140
158
|
browser editor. Linux editor launches are wrapped with `bubblewrap`
|
|
141
159
|
(`bwrap`) for filesystem confinement.
|
|
@@ -146,21 +164,21 @@ workspace. The claim also provisions `#linzumi-support`, a shared channel
|
|
|
146
164
|
connected to Linzumi's workspace so our team can see setup issues from our
|
|
147
165
|
side; the bootstrap agent posts a hello there with the printed
|
|
148
166
|
`support_channel_id`.
|
|
149
|
-
Keep demo work in task threads; use the support channel when signup,
|
|
167
|
+
Keep demo work in task threads; use the support channel when signup, Commander,
|
|
150
168
|
preview, or browser-editor setup gets stuck.
|
|
151
169
|
|
|
152
|
-
Once the
|
|
170
|
+
Once the Commander is online, the bootstrap agent can ask Linzumi to start
|
|
153
171
|
Codex and open the browser editor for the same thread and folder:
|
|
154
172
|
|
|
155
173
|
```bash
|
|
156
174
|
npx -y @linzumi/cli@latest codex start <thread_id> \
|
|
157
|
-
--runner
|
|
158
|
-
--cwd
|
|
159
|
-
--work-description "Work only in /tmp/hello_linzumi. Add tasteful confetti when the Hello Linzumi page loads, keep the hot-reload app working on port 8787, and post the exact files changed."
|
|
175
|
+
--runner "$commander_id" \
|
|
176
|
+
--cwd "$project_dir" \
|
|
177
|
+
--work-description "Work only in the canonical Hello Linzumi project directory printed by pwd -P for /tmp/hello_linzumi. Add tasteful confetti when the Hello Linzumi page loads, keep the hot-reload app working on port 8787, and post the exact files changed."
|
|
160
178
|
|
|
161
179
|
npx -y @linzumi/cli@latest editor open <thread_id> \
|
|
162
|
-
--runner
|
|
163
|
-
--cwd
|
|
180
|
+
--runner "$commander_id" \
|
|
181
|
+
--cwd "$project_dir"
|
|
164
182
|
```
|
|
165
183
|
|
|
166
184
|
The launch target for this path is zero-to-Hello-Linzumi-editor+preview in
|
|
@@ -261,7 +279,7 @@ intentionally. Every action is auditable from the thread.
|
|
|
261
279
|
## Pinning a version
|
|
262
280
|
|
|
263
281
|
```bash
|
|
264
|
-
npm install -g @linzumi/cli@0.0.
|
|
282
|
+
npm install -g @linzumi/cli@0.0.25-beta
|
|
265
283
|
linzumi --version
|
|
266
284
|
```
|
|
267
285
|
|
|
@@ -284,7 +302,7 @@ linzumi connect \
|
|
|
284
302
|
### All the flags
|
|
285
303
|
|
|
286
304
|
```text
|
|
287
|
-
--agent-token-file <path> Agent token cache for `linzumi
|
|
305
|
+
--agent-token-file <path> Agent token cache for `linzumi commander`
|
|
288
306
|
--oauth-callback-host <ip> Sign-in callback host your browser can reach
|
|
289
307
|
--codex-bin <path> Codex executable, default `codex`
|
|
290
308
|
--model <name> Model requested for Codex sessions and labelled in Linzumi
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
3
|
-
import { existsSync as
|
|
4
|
-
import { homedir as
|
|
5
|
-
import { resolve as
|
|
3
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
4
|
+
import { homedir as homedir7 } from "node:os";
|
|
5
|
+
import { resolve as resolve7 } from "node:path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7
7
|
|
|
8
8
|
// src/runner.ts
|
|
9
9
|
import { spawn as spawn6 } from "node:child_process";
|
|
10
10
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11
11
|
import { hostname as hostname2 } from "node:os";
|
|
12
|
-
import { join as
|
|
12
|
+
import { join as join5 } from "node:path";
|
|
13
13
|
|
|
14
14
|
// src/channelSessionSupport.ts
|
|
15
15
|
import { spawnSync } from "node:child_process";
|
|
@@ -4209,10 +4209,72 @@ function readyzUrlForWebsocket(websocketUrl) {
|
|
|
4209
4209
|
return parsed.toString();
|
|
4210
4210
|
}
|
|
4211
4211
|
|
|
4212
|
-
// src/
|
|
4213
|
-
import {
|
|
4212
|
+
// src/codexProjectTrust.ts
|
|
4213
|
+
import {
|
|
4214
|
+
existsSync,
|
|
4215
|
+
mkdirSync,
|
|
4216
|
+
readFileSync,
|
|
4217
|
+
realpathSync,
|
|
4218
|
+
writeFileSync
|
|
4219
|
+
} from "node:fs";
|
|
4214
4220
|
import { homedir } from "node:os";
|
|
4215
|
-
import {
|
|
4221
|
+
import { join as join2, resolve as resolve2 } from "node:path";
|
|
4222
|
+
function ensureCodexProjectTrusted(projectPath, options = {}) {
|
|
4223
|
+
const trustedPath = realpathSync(resolve2(projectPath));
|
|
4224
|
+
const configHome = options.configHome ?? process.env.CODEX_HOME ?? join2(homedir(), ".codex");
|
|
4225
|
+
const configPath = join2(configHome, "config.toml");
|
|
4226
|
+
const currentConfig = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
|
|
4227
|
+
const nextConfig = codexConfigWithTrustedProject(currentConfig, trustedPath);
|
|
4228
|
+
if (nextConfig !== currentConfig) {
|
|
4229
|
+
mkdirSync(configHome, { recursive: true });
|
|
4230
|
+
writeFileSync(configPath, nextConfig);
|
|
4231
|
+
}
|
|
4232
|
+
return trustedPath;
|
|
4233
|
+
}
|
|
4234
|
+
function codexConfigWithTrustedProject(config, projectPath) {
|
|
4235
|
+
const header = `[projects.${tomlString(projectPath)}]`;
|
|
4236
|
+
const range = findTableRange(config, header);
|
|
4237
|
+
if (range === undefined) {
|
|
4238
|
+
const separator = config.trim() === "" ? "" : `
|
|
4239
|
+
|
|
4240
|
+
`;
|
|
4241
|
+
return `${trimTrailingNewlines(config)}${separator}${header}
|
|
4242
|
+
trust_level = "trusted"
|
|
4243
|
+
`;
|
|
4244
|
+
}
|
|
4245
|
+
const table = config.slice(range.start, range.end);
|
|
4246
|
+
const trustLine = /^(\s*trust_level\s*=\s*).*(\r?)$/m;
|
|
4247
|
+
const nextTable = trustLine.test(table) ? table.replace(trustLine, '$1"trusted"$2') : table.replace(/\r?\n/, `
|
|
4248
|
+
trust_level = "trusted"
|
|
4249
|
+
`);
|
|
4250
|
+
return `${config.slice(0, range.start)}${nextTable}${config.slice(range.end)}`;
|
|
4251
|
+
}
|
|
4252
|
+
function findTableRange(config, header) {
|
|
4253
|
+
const tableHeader = /^\s*\[[^\]]+\]\s*$/gm;
|
|
4254
|
+
let match;
|
|
4255
|
+
while ((match = tableHeader.exec(config)) !== null) {
|
|
4256
|
+
if (match[0].trim() !== header) {
|
|
4257
|
+
continue;
|
|
4258
|
+
}
|
|
4259
|
+
const nextMatch = tableHeader.exec(config);
|
|
4260
|
+
return {
|
|
4261
|
+
start: match.index,
|
|
4262
|
+
end: nextMatch?.index ?? config.length
|
|
4263
|
+
};
|
|
4264
|
+
}
|
|
4265
|
+
return;
|
|
4266
|
+
}
|
|
4267
|
+
function tomlString(value) {
|
|
4268
|
+
return JSON.stringify(value);
|
|
4269
|
+
}
|
|
4270
|
+
function trimTrailingNewlines(value) {
|
|
4271
|
+
return value.replace(/\r?\n+$/, "");
|
|
4272
|
+
}
|
|
4273
|
+
|
|
4274
|
+
// src/localCapabilities.ts
|
|
4275
|
+
import { realpathSync as realpathSync2 } from "node:fs";
|
|
4276
|
+
import { homedir as homedir2 } from "node:os";
|
|
4277
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3 } from "node:path";
|
|
4216
4278
|
function parseAllowedCwdList(value) {
|
|
4217
4279
|
if (value === undefined) {
|
|
4218
4280
|
return [];
|
|
@@ -4238,20 +4300,23 @@ function parseAllowedPortList(value) {
|
|
|
4238
4300
|
return [...ports].sort((left, right) => left - right);
|
|
4239
4301
|
}
|
|
4240
4302
|
function assertConfiguredAllowedCwds(paths) {
|
|
4241
|
-
|
|
4303
|
+
const allowed = paths.flatMap((path) => {
|
|
4242
4304
|
try {
|
|
4243
|
-
|
|
4305
|
+
const absolutePath = resolve3(expandUserPath(path));
|
|
4306
|
+
const realPath = realpathSync2(absolutePath);
|
|
4307
|
+
return realPath === absolutePath ? [realPath] : [realPath, absolutePath];
|
|
4244
4308
|
} catch (_error) {
|
|
4245
4309
|
throw new Error(`invalid --allowed-cwd: ${path} does not exist`);
|
|
4246
4310
|
}
|
|
4247
4311
|
});
|
|
4312
|
+
return Array.from(new Set(allowed));
|
|
4248
4313
|
}
|
|
4249
4314
|
function expandUserPath(pathValue) {
|
|
4250
4315
|
if (pathValue === "~") {
|
|
4251
|
-
return
|
|
4316
|
+
return homedir2();
|
|
4252
4317
|
}
|
|
4253
4318
|
if (pathValue.startsWith("~/")) {
|
|
4254
|
-
return
|
|
4319
|
+
return resolve3(homedir2(), pathValue.slice(2));
|
|
4255
4320
|
}
|
|
4256
4321
|
return pathValue;
|
|
4257
4322
|
}
|
|
@@ -4264,7 +4329,7 @@ function resolveAllowedCwd(requestedCwd, allowedRoots) {
|
|
|
4264
4329
|
}
|
|
4265
4330
|
let cwd;
|
|
4266
4331
|
try {
|
|
4267
|
-
cwd =
|
|
4332
|
+
cwd = realpathSync2(resolve3(requestedCwd));
|
|
4268
4333
|
} catch (_error) {
|
|
4269
4334
|
return { ok: false, reason: "cwd_not_found" };
|
|
4270
4335
|
}
|
|
@@ -4569,14 +4634,14 @@ var blockedForwardHeaderNames = new Set([
|
|
|
4569
4634
|
import { spawn as spawn2 } from "node:child_process";
|
|
4570
4635
|
import {
|
|
4571
4636
|
cpSync,
|
|
4572
|
-
existsSync,
|
|
4573
|
-
mkdirSync,
|
|
4637
|
+
existsSync as existsSync2,
|
|
4638
|
+
mkdirSync as mkdirSync2,
|
|
4574
4639
|
mkdtempSync,
|
|
4575
|
-
realpathSync as
|
|
4576
|
-
writeFileSync
|
|
4640
|
+
realpathSync as realpathSync3,
|
|
4641
|
+
writeFileSync as writeFileSync2
|
|
4577
4642
|
} from "node:fs";
|
|
4578
4643
|
import { tmpdir } from "node:os";
|
|
4579
|
-
import { basename as basename3, delimiter, dirname, join as
|
|
4644
|
+
import { basename as basename3, delimiter, dirname, join as join3 } from "node:path";
|
|
4580
4645
|
function isStartLocalEditorControl(control) {
|
|
4581
4646
|
return control.type === "start_local_editor";
|
|
4582
4647
|
}
|
|
@@ -4739,17 +4804,17 @@ function codeServerArgs(port, cwd, userDataDir, extensionsDir) {
|
|
|
4739
4804
|
}
|
|
4740
4805
|
function prepareCodeServerProfile(collaboration, editorRuntime) {
|
|
4741
4806
|
try {
|
|
4742
|
-
const userDataDir = mkdtempSync(
|
|
4743
|
-
const extensionsDir =
|
|
4744
|
-
const collaborationServerDir =
|
|
4745
|
-
const userSettingsDir =
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4807
|
+
const userDataDir = mkdtempSync(join3(tmpdir(), "kandan-local-editor-"));
|
|
4808
|
+
const extensionsDir = join3(userDataDir, "extensions");
|
|
4809
|
+
const collaborationServerDir = join3(userDataDir, "collaboration-server");
|
|
4810
|
+
const userSettingsDir = join3(userDataDir, "User");
|
|
4811
|
+
mkdirSync2(userSettingsDir, { recursive: true });
|
|
4812
|
+
mkdirSync2(extensionsDir, { recursive: true });
|
|
4813
|
+
mkdirSync2(collaborationServerDir, { recursive: true });
|
|
4749
4814
|
if (editorRuntime !== undefined) {
|
|
4750
|
-
installDirectory(editorRuntime.assets.documentStateExtensionDir,
|
|
4815
|
+
installDirectory(editorRuntime.assets.documentStateExtensionDir, join3(extensionsDir, "kandan.document-state-telemetry"));
|
|
4751
4816
|
}
|
|
4752
|
-
|
|
4817
|
+
writeFileSync2(join3(userSettingsDir, "settings.json"), JSON.stringify(codeServerSettings(collaboration), null, 2));
|
|
4753
4818
|
return { ok: true, userDataDir, extensionsDir, collaborationServerDir };
|
|
4754
4819
|
} catch (_error) {
|
|
4755
4820
|
return { ok: false, reason: "code_server_spawn_failed" };
|
|
@@ -4764,7 +4829,7 @@ function prepareCodeServerLaunch(options) {
|
|
|
4764
4829
|
return filesystemSandboxUnavailable();
|
|
4765
4830
|
}
|
|
4766
4831
|
const sandboxExecBin = options.sandboxExecBin ?? "/usr/bin/sandbox-exec";
|
|
4767
|
-
if (!
|
|
4832
|
+
if (!existsSync2(sandboxExecBin)) {
|
|
4768
4833
|
return filesystemSandboxUnavailable();
|
|
4769
4834
|
}
|
|
4770
4835
|
const codeServerExecutable = resolveCodeServerExecutable(options.codeServerBin, options.envPath ?? process.env.PATH ?? "");
|
|
@@ -4818,10 +4883,10 @@ function prepareLinuxCodeServerLaunch(options) {
|
|
|
4818
4883
|
options.userDataDir,
|
|
4819
4884
|
"--setenv",
|
|
4820
4885
|
"XDG_DATA_HOME",
|
|
4821
|
-
|
|
4886
|
+
join3(options.userDataDir, "data"),
|
|
4822
4887
|
"--setenv",
|
|
4823
4888
|
"XDG_CONFIG_HOME",
|
|
4824
|
-
|
|
4889
|
+
join3(options.userDataDir, "config"),
|
|
4825
4890
|
...readOnlyRoots.flatMap((path) => ["--ro-bind-try", path, path]),
|
|
4826
4891
|
"--bind",
|
|
4827
4892
|
options.cwd,
|
|
@@ -4894,11 +4959,11 @@ function resolveCodeServerExecutable(command, envPath) {
|
|
|
4894
4959
|
if (directory.trim() === "") {
|
|
4895
4960
|
continue;
|
|
4896
4961
|
}
|
|
4897
|
-
const candidate =
|
|
4898
|
-
if (!
|
|
4962
|
+
const candidate = join3(directory, command);
|
|
4963
|
+
if (!existsSync2(candidate)) {
|
|
4899
4964
|
continue;
|
|
4900
4965
|
}
|
|
4901
|
-
const realpath =
|
|
4966
|
+
const realpath = realpathSync3(candidate);
|
|
4902
4967
|
return { ok: true, command: realpath, directory: dirname(realpath) };
|
|
4903
4968
|
}
|
|
4904
4969
|
return { ok: false };
|
|
@@ -4908,7 +4973,7 @@ function hasPathSeparator(path) {
|
|
|
4908
4973
|
}
|
|
4909
4974
|
function safeRealpathDir(path) {
|
|
4910
4975
|
try {
|
|
4911
|
-
const directory = dirname(
|
|
4976
|
+
const directory = dirname(realpathSync3(path));
|
|
4912
4977
|
return directory === "/" ? undefined : directory;
|
|
4913
4978
|
} catch (_error) {
|
|
4914
4979
|
const directory = dirname(path);
|
|
@@ -4917,7 +4982,7 @@ function safeRealpathDir(path) {
|
|
|
4917
4982
|
}
|
|
4918
4983
|
function sandboxPathAliases(path) {
|
|
4919
4984
|
try {
|
|
4920
|
-
return [path,
|
|
4985
|
+
return [path, realpathSync3(path)];
|
|
4921
4986
|
} catch (_error) {
|
|
4922
4987
|
return [path];
|
|
4923
4988
|
}
|
|
@@ -4971,7 +5036,7 @@ async function startCollaborationSidecar(collaboration, profile, editorRuntime)
|
|
|
4971
5036
|
installLocalTarball(editorRuntime.assets.collaborationServerTarball, profile.collaborationServerDir)
|
|
4972
5037
|
]);
|
|
4973
5038
|
const child = spawn2(nodeRuntimeExecutable(), [
|
|
4974
|
-
|
|
5039
|
+
join3(profile.collaborationServerDir, "open-collaboration-server", "bundle", "app.js"),
|
|
4975
5040
|
"--hostname",
|
|
4976
5041
|
"127.0.0.1",
|
|
4977
5042
|
"--port",
|
|
@@ -5017,11 +5082,11 @@ function nodeRuntimeExecutable(env = process.env, execPath = process.execPath) {
|
|
|
5017
5082
|
return basename3(execPath).toLowerCase().includes("bun") ? "node" : execPath;
|
|
5018
5083
|
}
|
|
5019
5084
|
async function installLocalTarball(archivePath, destinationDir) {
|
|
5020
|
-
|
|
5085
|
+
mkdirSync2(destinationDir, { recursive: true });
|
|
5021
5086
|
await runProcess("tar", ["-xzf", archivePath, "-C", destinationDir]);
|
|
5022
5087
|
}
|
|
5023
5088
|
function installDirectory(sourceDir, destinationDir) {
|
|
5024
|
-
|
|
5089
|
+
mkdirSync2(dirname(destinationDir), { recursive: true });
|
|
5025
5090
|
cpSync(sourceDir, destinationDir, { recursive: true });
|
|
5026
5091
|
}
|
|
5027
5092
|
function codeServerEnv(env, userDataDir, collaboration) {
|
|
@@ -5029,9 +5094,9 @@ function codeServerEnv(env, userDataDir, collaboration) {
|
|
|
5029
5094
|
const base = {
|
|
5030
5095
|
...hostEnv,
|
|
5031
5096
|
HOME: userDataDir,
|
|
5032
|
-
XDG_CACHE_HOME:
|
|
5033
|
-
XDG_CONFIG_HOME:
|
|
5034
|
-
XDG_DATA_HOME:
|
|
5097
|
+
XDG_CACHE_HOME: join3(userDataDir, "xdg-cache"),
|
|
5098
|
+
XDG_CONFIG_HOME: join3(userDataDir, "xdg-config"),
|
|
5099
|
+
XDG_DATA_HOME: join3(userDataDir, "xdg-data")
|
|
5035
5100
|
};
|
|
5036
5101
|
if (collaboration === undefined) {
|
|
5037
5102
|
return base;
|
|
@@ -5064,14 +5129,14 @@ function sameCollaboration(running, requested) {
|
|
|
5064
5129
|
return running.editorSessionId === requested.editorSessionId && running.runtimeSessionId === requested.runtimeSessionId && running.roomId === requested.roomId;
|
|
5065
5130
|
}
|
|
5066
5131
|
function runProcess(command, args) {
|
|
5067
|
-
return new Promise((
|
|
5132
|
+
return new Promise((resolve4, reject) => {
|
|
5068
5133
|
const child = spawn2(command, [...args], {
|
|
5069
5134
|
stdio: ["ignore", "ignore", "inherit"]
|
|
5070
5135
|
});
|
|
5071
5136
|
child.once("error", reject);
|
|
5072
5137
|
child.once("exit", (code) => {
|
|
5073
5138
|
if (code === 0) {
|
|
5074
|
-
|
|
5139
|
+
resolve4();
|
|
5075
5140
|
} else {
|
|
5076
5141
|
reject(new Error(`${command} exited with ${code ?? "unknown"}`));
|
|
5077
5142
|
}
|
|
@@ -5079,7 +5144,7 @@ function runProcess(command, args) {
|
|
|
5079
5144
|
});
|
|
5080
5145
|
}
|
|
5081
5146
|
function waitForCodeServerExit(child) {
|
|
5082
|
-
return new Promise((
|
|
5147
|
+
return new Promise((resolve4) => {
|
|
5083
5148
|
let settled = false;
|
|
5084
5149
|
const cleanup = () => {
|
|
5085
5150
|
child.off("exit", onDone);
|
|
@@ -5091,7 +5156,7 @@ function waitForCodeServerExit(child) {
|
|
|
5091
5156
|
}
|
|
5092
5157
|
settled = true;
|
|
5093
5158
|
cleanup();
|
|
5094
|
-
|
|
5159
|
+
resolve4();
|
|
5095
5160
|
};
|
|
5096
5161
|
child.once("exit", onDone);
|
|
5097
5162
|
child.once("close", onDone);
|
|
@@ -5103,7 +5168,7 @@ function waitForCodeServerExit(child) {
|
|
|
5103
5168
|
function waitForCodeServerReady(port, exited, timeoutMs = 1e4) {
|
|
5104
5169
|
const deadline = Date.now() + timeoutMs;
|
|
5105
5170
|
const readyUrl = `http://127.0.0.1:${port}/`;
|
|
5106
|
-
return new Promise((
|
|
5171
|
+
return new Promise((resolve4) => {
|
|
5107
5172
|
let settled = false;
|
|
5108
5173
|
let timer;
|
|
5109
5174
|
const finish = (result) => {
|
|
@@ -5114,7 +5179,7 @@ function waitForCodeServerReady(port, exited, timeoutMs = 1e4) {
|
|
|
5114
5179
|
if (timer !== undefined) {
|
|
5115
5180
|
clearTimeout(timer);
|
|
5116
5181
|
}
|
|
5117
|
-
|
|
5182
|
+
resolve4(result);
|
|
5118
5183
|
};
|
|
5119
5184
|
const scheduleCheck = () => {
|
|
5120
5185
|
timer = setTimeout(checkReady, 50);
|
|
@@ -5143,7 +5208,7 @@ function waitForCodeServerReady(port, exited, timeoutMs = 1e4) {
|
|
|
5143
5208
|
function waitForCollaborationServerReady(port, exited, timeoutMs = 1e4) {
|
|
5144
5209
|
const deadline = Date.now() + timeoutMs;
|
|
5145
5210
|
const readyUrl = `http://127.0.0.1:${port}/api/login/initial`;
|
|
5146
|
-
return new Promise((
|
|
5211
|
+
return new Promise((resolve4) => {
|
|
5147
5212
|
let settled = false;
|
|
5148
5213
|
let timer;
|
|
5149
5214
|
const finish = (result) => {
|
|
@@ -5154,7 +5219,7 @@ function waitForCollaborationServerReady(port, exited, timeoutMs = 1e4) {
|
|
|
5154
5219
|
if (timer !== undefined) {
|
|
5155
5220
|
clearTimeout(timer);
|
|
5156
5221
|
}
|
|
5157
|
-
|
|
5222
|
+
resolve4(result);
|
|
5158
5223
|
};
|
|
5159
5224
|
const scheduleCheck = () => {
|
|
5160
5225
|
timer = setTimeout(checkReady, 50);
|
|
@@ -5181,7 +5246,7 @@ function waitForCollaborationServerReady(port, exited, timeoutMs = 1e4) {
|
|
|
5181
5246
|
});
|
|
5182
5247
|
}
|
|
5183
5248
|
function waitForCodeServerSpawn(child) {
|
|
5184
|
-
return new Promise((
|
|
5249
|
+
return new Promise((resolve4) => {
|
|
5185
5250
|
let settled = false;
|
|
5186
5251
|
const cleanup = () => {
|
|
5187
5252
|
child.off("spawn", onSpawn);
|
|
@@ -5193,7 +5258,7 @@ function waitForCodeServerSpawn(child) {
|
|
|
5193
5258
|
}
|
|
5194
5259
|
settled = true;
|
|
5195
5260
|
cleanup();
|
|
5196
|
-
|
|
5261
|
+
resolve4("spawned");
|
|
5197
5262
|
};
|
|
5198
5263
|
const onError = () => {
|
|
5199
5264
|
if (settled) {
|
|
@@ -5201,7 +5266,7 @@ function waitForCodeServerSpawn(child) {
|
|
|
5201
5266
|
}
|
|
5202
5267
|
settled = true;
|
|
5203
5268
|
cleanup();
|
|
5204
|
-
|
|
5269
|
+
resolve4("failed");
|
|
5205
5270
|
};
|
|
5206
5271
|
child.once("spawn", onSpawn);
|
|
5207
5272
|
child.once("error", onError);
|
|
@@ -5222,16 +5287,16 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
5222
5287
|
import {
|
|
5223
5288
|
createReadStream,
|
|
5224
5289
|
createWriteStream,
|
|
5225
|
-
existsSync as
|
|
5226
|
-
mkdirSync as
|
|
5290
|
+
existsSync as existsSync3,
|
|
5291
|
+
mkdirSync as mkdirSync3,
|
|
5227
5292
|
mkdtempSync as mkdtempSync2,
|
|
5228
|
-
readFileSync,
|
|
5293
|
+
readFileSync as readFileSync2,
|
|
5229
5294
|
renameSync,
|
|
5230
5295
|
rmSync,
|
|
5231
|
-
writeFileSync as
|
|
5296
|
+
writeFileSync as writeFileSync3
|
|
5232
5297
|
} from "node:fs";
|
|
5233
|
-
import { homedir as
|
|
5234
|
-
import { dirname as dirname2, join as
|
|
5298
|
+
import { homedir as homedir3 } from "node:os";
|
|
5299
|
+
import { dirname as dirname2, join as join4, resolve as resolve4 } from "node:path";
|
|
5235
5300
|
import { Readable } from "node:stream";
|
|
5236
5301
|
import { pipeline } from "node:stream/promises";
|
|
5237
5302
|
|
|
@@ -5402,7 +5467,7 @@ function stringBodyField(body, key) {
|
|
|
5402
5467
|
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
5403
5468
|
}
|
|
5404
5469
|
function startCallbackServer(args) {
|
|
5405
|
-
return new Promise((
|
|
5470
|
+
return new Promise((resolve4, reject) => {
|
|
5406
5471
|
let resolveCallback;
|
|
5407
5472
|
let rejectCallback;
|
|
5408
5473
|
const callbackPromise = new Promise((callbackResolve, callbackReject) => {
|
|
@@ -5454,7 +5519,7 @@ function startCallbackServer(args) {
|
|
|
5454
5519
|
reject(new Error("local runner OAuth callback server did not bind to a TCP port"));
|
|
5455
5520
|
return;
|
|
5456
5521
|
}
|
|
5457
|
-
|
|
5522
|
+
resolve4({
|
|
5458
5523
|
redirectUri: `http://${args.host}:${address.port}/callback`,
|
|
5459
5524
|
waitForCallback: () => callbackPromise,
|
|
5460
5525
|
close: () => {
|
|
@@ -5505,12 +5570,12 @@ function escapeHtml(value) {
|
|
|
5505
5570
|
function openBrowser(url) {
|
|
5506
5571
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
5507
5572
|
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
5508
|
-
return new Promise((
|
|
5573
|
+
return new Promise((resolve4) => {
|
|
5509
5574
|
const child = spawn3(command, args, { stdio: "ignore", detached: true });
|
|
5510
|
-
child.on("error", () =>
|
|
5575
|
+
child.on("error", () => resolve4());
|
|
5511
5576
|
child.on("spawn", () => {
|
|
5512
5577
|
child.unref();
|
|
5513
|
-
|
|
5578
|
+
resolve4();
|
|
5514
5579
|
});
|
|
5515
5580
|
});
|
|
5516
5581
|
}
|
|
@@ -5693,14 +5758,14 @@ function normalizeRuntimeAssets(value) {
|
|
|
5693
5758
|
}
|
|
5694
5759
|
function installedRuntime(cacheRoot, manifest) {
|
|
5695
5760
|
const runtimeRoot = runtimeInstallRoot(cacheRoot, manifest);
|
|
5696
|
-
const manifestPath =
|
|
5697
|
-
const codeServerBin =
|
|
5761
|
+
const manifestPath = join4(runtimeRoot, manifest.manifestPath);
|
|
5762
|
+
const codeServerBin = join4(runtimeRoot, manifest.codeServerBinPath);
|
|
5698
5763
|
const assets = verifiedRuntimeAssetPaths(runtimeRoot, manifest);
|
|
5699
|
-
if (!
|
|
5764
|
+
if (!existsSync3(manifestPath) || !existsSync3(codeServerBin) || assets === undefined) {
|
|
5700
5765
|
return { ok: false };
|
|
5701
5766
|
}
|
|
5702
5767
|
try {
|
|
5703
|
-
const installed = JSON.parse(
|
|
5768
|
+
const installed = JSON.parse(readFileSync2(manifestPath, "utf8"));
|
|
5704
5769
|
if (isJsonObject(installed) && installed.version === manifest.version && installed.platform === manifest.platform && (installed.archiveSha256 === undefined || installed.archiveSha256 === manifest.archiveSha256)) {
|
|
5705
5770
|
return {
|
|
5706
5771
|
ok: true,
|
|
@@ -5718,10 +5783,10 @@ function installedRuntime(cacheRoot, manifest) {
|
|
|
5718
5783
|
return { ok: false };
|
|
5719
5784
|
}
|
|
5720
5785
|
async function installRuntime(args) {
|
|
5721
|
-
|
|
5722
|
-
const tempRoot = mkdtempSync2(
|
|
5723
|
-
const archivePath =
|
|
5724
|
-
const extractRoot =
|
|
5786
|
+
mkdirSync3(args.cacheRoot, { recursive: true });
|
|
5787
|
+
const tempRoot = mkdtempSync2(join4(args.cacheRoot, ".install-"));
|
|
5788
|
+
const archivePath = join4(tempRoot, "runtime.tar.gz");
|
|
5789
|
+
const extractRoot = join4(tempRoot, "runtime");
|
|
5725
5790
|
try {
|
|
5726
5791
|
const downloaded = await downloadArchive({
|
|
5727
5792
|
kandanUrl: args.kandanUrl,
|
|
@@ -5733,7 +5798,7 @@ async function installRuntime(args) {
|
|
|
5733
5798
|
if (!downloaded.ok) {
|
|
5734
5799
|
return downloaded;
|
|
5735
5800
|
}
|
|
5736
|
-
|
|
5801
|
+
mkdirSync3(extractRoot, { recursive: true });
|
|
5737
5802
|
if (!await args.extractArchive(archivePath, extractRoot)) {
|
|
5738
5803
|
return { ok: false, reason: "archive_extract_failed" };
|
|
5739
5804
|
}
|
|
@@ -5746,14 +5811,14 @@ async function installRuntime(args) {
|
|
|
5746
5811
|
if (!assetsInstalled) {
|
|
5747
5812
|
return { ok: false, reason: "install_failed" };
|
|
5748
5813
|
}
|
|
5749
|
-
const manifestPath =
|
|
5750
|
-
const codeServerBin =
|
|
5814
|
+
const manifestPath = join4(extractRoot, args.manifest.manifestPath);
|
|
5815
|
+
const codeServerBin = join4(extractRoot, args.manifest.codeServerBinPath);
|
|
5751
5816
|
const assets = verifiedRuntimeAssetPaths(extractRoot, args.manifest);
|
|
5752
|
-
if (!
|
|
5817
|
+
if (!existsSync3(codeServerBin) || assets === undefined) {
|
|
5753
5818
|
return { ok: false, reason: "invalid_archive" };
|
|
5754
5819
|
}
|
|
5755
|
-
|
|
5756
|
-
|
|
5820
|
+
mkdirSync3(dirname2(manifestPath), { recursive: true });
|
|
5821
|
+
writeFileSync3(manifestPath, JSON.stringify({
|
|
5757
5822
|
version: args.manifest.version,
|
|
5758
5823
|
platform: args.manifest.platform,
|
|
5759
5824
|
archiveSha256: args.manifest.archiveSha256,
|
|
@@ -5764,18 +5829,18 @@ async function installRuntime(args) {
|
|
|
5764
5829
|
}, null, 2));
|
|
5765
5830
|
const targetRoot = runtimeInstallRoot(args.cacheRoot, args.manifest);
|
|
5766
5831
|
rmSync(targetRoot, { recursive: true, force: true });
|
|
5767
|
-
|
|
5832
|
+
mkdirSync3(dirname2(targetRoot), { recursive: true });
|
|
5768
5833
|
renameSync(extractRoot, targetRoot);
|
|
5769
5834
|
return {
|
|
5770
5835
|
ok: true,
|
|
5771
5836
|
runtime: {
|
|
5772
5837
|
mode: "server_managed",
|
|
5773
5838
|
root: targetRoot,
|
|
5774
|
-
codeServerBin:
|
|
5839
|
+
codeServerBin: join4(targetRoot, args.manifest.codeServerBinPath),
|
|
5775
5840
|
assets: {
|
|
5776
|
-
collaborationExtensionTarball:
|
|
5777
|
-
collaborationServerTarball:
|
|
5778
|
-
documentStateExtensionDir:
|
|
5841
|
+
collaborationExtensionTarball: join4(targetRoot, "kandan", "editor_extensions", "typefox.open-collaboration-tools.tar.gz"),
|
|
5842
|
+
collaborationServerTarball: join4(targetRoot, "kandan", "editor_servers", "open-collaboration-server.tar.gz"),
|
|
5843
|
+
documentStateExtensionDir: join4(targetRoot, "kandan", "editor_extensions", "kandan.document-state-telemetry")
|
|
5779
5844
|
}
|
|
5780
5845
|
}
|
|
5781
5846
|
};
|
|
@@ -5787,7 +5852,7 @@ async function installRuntime(args) {
|
|
|
5787
5852
|
}
|
|
5788
5853
|
async function materializeRuntimeAssets(args) {
|
|
5789
5854
|
for (const asset of args.manifest.assets) {
|
|
5790
|
-
const targetPath =
|
|
5855
|
+
const targetPath = join4(args.runtimeRoot, asset.path);
|
|
5791
5856
|
try {
|
|
5792
5857
|
const bytes = await runtimeAssetBytes({
|
|
5793
5858
|
kandanUrl: args.kandanUrl,
|
|
@@ -5797,8 +5862,8 @@ async function materializeRuntimeAssets(args) {
|
|
|
5797
5862
|
if (bytes === undefined) {
|
|
5798
5863
|
continue;
|
|
5799
5864
|
}
|
|
5800
|
-
|
|
5801
|
-
|
|
5865
|
+
mkdirSync3(dirname2(targetPath), { recursive: true });
|
|
5866
|
+
writeFileSync3(targetPath, bytes);
|
|
5802
5867
|
} catch (_error) {
|
|
5803
5868
|
return false;
|
|
5804
5869
|
}
|
|
@@ -5857,17 +5922,17 @@ function fileSha256(path) {
|
|
|
5857
5922
|
});
|
|
5858
5923
|
}
|
|
5859
5924
|
function runtimeInstallRoot(cacheRoot, manifest) {
|
|
5860
|
-
return
|
|
5925
|
+
return resolve4(cacheRoot, manifest.platform, manifest.archiveSha256);
|
|
5861
5926
|
}
|
|
5862
5927
|
function verifiedRuntimeAssetPaths(runtimeRoot, manifest) {
|
|
5863
|
-
const collaborationExtensionTarball =
|
|
5864
|
-
const collaborationServerTarball =
|
|
5865
|
-
const documentStateExtensionDir =
|
|
5928
|
+
const collaborationExtensionTarball = join4(runtimeRoot, "kandan", "editor_extensions", "typefox.open-collaboration-tools.tar.gz");
|
|
5929
|
+
const collaborationServerTarball = join4(runtimeRoot, "kandan", "editor_servers", "open-collaboration-server.tar.gz");
|
|
5930
|
+
const documentStateExtensionDir = join4(runtimeRoot, "kandan", "editor_extensions", "kandan.document-state-telemetry");
|
|
5866
5931
|
const codeServerRoot = codeServerRuntimeRoot(manifest.codeServerBinPath);
|
|
5867
5932
|
const requiredPaths = [
|
|
5868
5933
|
manifest.codeServerBinPath,
|
|
5869
|
-
|
|
5870
|
-
|
|
5934
|
+
join4(codeServerRoot, "lib", "vscode", "node_modules", "vsda", "rust", "web", "vsda.js"),
|
|
5935
|
+
join4(codeServerRoot, "lib", "vscode", "node_modules", "vsda", "rust", "web", "vsda_bg.wasm"),
|
|
5871
5936
|
"kandan/editor_extensions/typefox.open-collaboration-tools.tar.gz",
|
|
5872
5937
|
"kandan/editor_servers/open-collaboration-server.tar.gz",
|
|
5873
5938
|
"kandan/editor_extensions/kandan.document-state-telemetry/package.json",
|
|
@@ -5879,15 +5944,15 @@ function verifiedRuntimeAssetPaths(runtimeRoot, manifest) {
|
|
|
5879
5944
|
if (expectedSha256 === undefined && relativePath !== manifest.codeServerBinPath) {
|
|
5880
5945
|
return;
|
|
5881
5946
|
}
|
|
5882
|
-
const absolutePath =
|
|
5883
|
-
if (!
|
|
5947
|
+
const absolutePath = join4(runtimeRoot, relativePath);
|
|
5948
|
+
if (!existsSync3(absolutePath)) {
|
|
5884
5949
|
return;
|
|
5885
5950
|
}
|
|
5886
5951
|
if (expectedSha256 !== undefined && fileSha256Sync(absolutePath) !== expectedSha256) {
|
|
5887
5952
|
return;
|
|
5888
5953
|
}
|
|
5889
5954
|
}
|
|
5890
|
-
if (!
|
|
5955
|
+
if (!existsSync3(collaborationExtensionTarball) || !existsSync3(collaborationServerTarball)) {
|
|
5891
5956
|
return;
|
|
5892
5957
|
}
|
|
5893
5958
|
return {
|
|
@@ -5908,10 +5973,10 @@ function manifestAssetChecksums(assets) {
|
|
|
5908
5973
|
return checksums;
|
|
5909
5974
|
}
|
|
5910
5975
|
function fileSha256Sync(path) {
|
|
5911
|
-
return createHash2("sha256").update(
|
|
5976
|
+
return createHash2("sha256").update(readFileSync2(path)).digest("hex");
|
|
5912
5977
|
}
|
|
5913
5978
|
function defaultEditorRuntimeCacheRoot() {
|
|
5914
|
-
return
|
|
5979
|
+
return join4(homedir3(), ".linzumi", "editor-runtimes");
|
|
5915
5980
|
}
|
|
5916
5981
|
function nonEmptyString(value) {
|
|
5917
5982
|
return typeof value === "string" && value.trim() !== "" ? value.trim() : undefined;
|
|
@@ -5923,7 +5988,7 @@ function sha256String(value) {
|
|
|
5923
5988
|
|
|
5924
5989
|
// src/dependencyStatus.ts
|
|
5925
5990
|
function probeTool(command, cwd) {
|
|
5926
|
-
return new Promise((
|
|
5991
|
+
return new Promise((resolve5) => {
|
|
5927
5992
|
const child = spawn5(command, ["--version"], {
|
|
5928
5993
|
cwd,
|
|
5929
5994
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5937,7 +6002,7 @@ function probeTool(command, cwd) {
|
|
|
5937
6002
|
}
|
|
5938
6003
|
resolved = true;
|
|
5939
6004
|
clearTimeout(timeout);
|
|
5940
|
-
|
|
6005
|
+
resolve5(status);
|
|
5941
6006
|
};
|
|
5942
6007
|
const timeout = setTimeout(() => {
|
|
5943
6008
|
child.kill("SIGKILL");
|
|
@@ -6084,8 +6149,8 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new
|
|
|
6084
6149
|
pending.clear();
|
|
6085
6150
|
};
|
|
6086
6151
|
const resetReady = () => {
|
|
6087
|
-
state.ready = new Promise((
|
|
6088
|
-
state.resolveReady =
|
|
6152
|
+
state.ready = new Promise((resolve5, reject) => {
|
|
6153
|
+
state.resolveReady = resolve5;
|
|
6089
6154
|
state.rejectReady = reject;
|
|
6090
6155
|
});
|
|
6091
6156
|
};
|
|
@@ -6120,8 +6185,8 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new
|
|
|
6120
6185
|
const ref = String(state.nextRef);
|
|
6121
6186
|
state.nextRef += 1;
|
|
6122
6187
|
const frame = [null, ref, topic, event, payload];
|
|
6123
|
-
return new Promise((
|
|
6124
|
-
pending.set(ref, { event, resolve:
|
|
6188
|
+
return new Promise((resolve5, reject) => {
|
|
6189
|
+
pending.set(ref, { event, resolve: resolve5, reject });
|
|
6125
6190
|
websocket.send(JSON.stringify(frame));
|
|
6126
6191
|
});
|
|
6127
6192
|
};
|
|
@@ -6257,8 +6322,8 @@ function isKandanControl(value) {
|
|
|
6257
6322
|
return isJsonObject(value) && typeof value.type === "string";
|
|
6258
6323
|
}
|
|
6259
6324
|
function waitForOpen2(websocket) {
|
|
6260
|
-
return new Promise((
|
|
6261
|
-
websocket.addEventListener("open", () =>
|
|
6325
|
+
return new Promise((resolve5, reject) => {
|
|
6326
|
+
websocket.addEventListener("open", () => resolve5(), { once: true });
|
|
6262
6327
|
websocket.addEventListener("error", () => reject(new Error("websocket open failed")), {
|
|
6263
6328
|
once: true
|
|
6264
6329
|
});
|
|
@@ -6269,9 +6334,9 @@ function waitForOpen2(websocket) {
|
|
|
6269
6334
|
import { openSync } from "node:fs";
|
|
6270
6335
|
import { createWriteStream as createWriteStream2 } from "node:fs";
|
|
6271
6336
|
import { dirname as dirname3 } from "node:path";
|
|
6272
|
-
import { mkdirSync as
|
|
6337
|
+
import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
6273
6338
|
function createRunnerLogger(logFile, consoleReporter) {
|
|
6274
|
-
|
|
6339
|
+
mkdirSync4(dirname3(logFile), { recursive: true });
|
|
6275
6340
|
const fd = openSync(logFile, "a");
|
|
6276
6341
|
const stream = createWriteStream2("", { fd, flags: "a", autoClose: true });
|
|
6277
6342
|
const logger = (event, payload) => {
|
|
@@ -6288,9 +6353,9 @@ function closeStream(stream) {
|
|
|
6288
6353
|
if (stream.closed || stream.destroyed) {
|
|
6289
6354
|
return Promise.resolve();
|
|
6290
6355
|
}
|
|
6291
|
-
return new Promise((
|
|
6356
|
+
return new Promise((resolve5, reject) => {
|
|
6292
6357
|
stream.once("error", reject);
|
|
6293
|
-
stream.end(
|
|
6358
|
+
stream.end(resolve5);
|
|
6294
6359
|
});
|
|
6295
6360
|
}
|
|
6296
6361
|
|
|
@@ -6459,11 +6524,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
6459
6524
|
dispatcher(control);
|
|
6460
6525
|
});
|
|
6461
6526
|
await kandan.join(topic, joinPayload(), { rejoinPayload: joinPayload });
|
|
6462
|
-
const started = options.codexUrl === undefined ? await
|
|
6463
|
-
model: options.channelSession?.model,
|
|
6464
|
-
reasoningEffort: options.channelSession?.reasoningEffort,
|
|
6465
|
-
fast: options.fast
|
|
6466
|
-
}) : undefined;
|
|
6527
|
+
const started = options.codexUrl === undefined ? await startOwnedCodexAppServer(options) : undefined;
|
|
6467
6528
|
if (started !== undefined) {
|
|
6468
6529
|
cleanup.actions.push(() => {
|
|
6469
6530
|
started.process.kill("SIGINT");
|
|
@@ -6790,7 +6851,7 @@ function normalizedWorkDescription(value) {
|
|
|
6790
6851
|
return normalized === undefined || normalized === "" ? undefined : normalized;
|
|
6791
6852
|
}
|
|
6792
6853
|
function makeRunnerLogger(options) {
|
|
6793
|
-
return createRunnerLogger(options.logFile ??
|
|
6854
|
+
return createRunnerLogger(options.logFile ?? join5(options.cwd, ".linzumi-runner.log"), options.launchTui ? undefined : reportRunnerConsoleEvent);
|
|
6794
6855
|
}
|
|
6795
6856
|
function installCleanupHandlers(close) {
|
|
6796
6857
|
const closeAndExit = () => {
|
|
@@ -6867,6 +6928,9 @@ async function applyControl(codex, instanceId, options, allowedCwds, control) {
|
|
|
6867
6928
|
error: cwd.reason
|
|
6868
6929
|
};
|
|
6869
6930
|
}
|
|
6931
|
+
if (options.codexUrl === undefined) {
|
|
6932
|
+
ensureCodexProjectTrusted(cwd.cwd);
|
|
6933
|
+
}
|
|
6870
6934
|
const response = await codex.request("thread/start", {
|
|
6871
6935
|
cwd: cwd.cwd,
|
|
6872
6936
|
serviceName: "kandan-local-runner",
|
|
@@ -6951,6 +7015,14 @@ async function applyControl(codex, instanceId, options, allowedCwds, control) {
|
|
|
6951
7015
|
return { instanceId, controlType: control.type, skipped: true };
|
|
6952
7016
|
}
|
|
6953
7017
|
}
|
|
7018
|
+
async function startOwnedCodexAppServer(options) {
|
|
7019
|
+
ensureCodexProjectTrusted(options.cwd);
|
|
7020
|
+
return await startCodexAppServer(options.codexBin, options.cwd, {
|
|
7021
|
+
model: options.channelSession?.model,
|
|
7022
|
+
reasoningEffort: options.channelSession?.reasoningEffort,
|
|
7023
|
+
fast: options.fast
|
|
7024
|
+
});
|
|
7025
|
+
}
|
|
6954
7026
|
function isUpdateRunnerConfigControl(control) {
|
|
6955
7027
|
return control.type === "update_runner_config";
|
|
6956
7028
|
}
|
|
@@ -6965,18 +7037,18 @@ function allowedCwdSuggestions(cwd, allowedCwds) {
|
|
|
6965
7037
|
}
|
|
6966
7038
|
|
|
6967
7039
|
// src/authCache.ts
|
|
6968
|
-
import { existsSync as
|
|
6969
|
-
import { homedir as
|
|
6970
|
-
import { dirname as dirname4, join as
|
|
7040
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7041
|
+
import { homedir as homedir4 } from "node:os";
|
|
7042
|
+
import { dirname as dirname4, join as join6 } from "node:path";
|
|
6971
7043
|
function defaultAuthFilePath() {
|
|
6972
|
-
const base = process.env.KANDAN_HOME ??
|
|
6973
|
-
return
|
|
7044
|
+
const base = process.env.KANDAN_HOME ?? join6(homedir4(), ".kandan");
|
|
7045
|
+
return join6(base, "auth.json");
|
|
6974
7046
|
}
|
|
6975
7047
|
function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePath()) {
|
|
6976
|
-
if (!
|
|
7048
|
+
if (!existsSync4(authFilePath)) {
|
|
6977
7049
|
return;
|
|
6978
7050
|
}
|
|
6979
|
-
const authFile = parseAuthFile(
|
|
7051
|
+
const authFile = parseAuthFile(readFileSync3(authFilePath, "utf8"));
|
|
6980
7052
|
const kandanBaseUrl = kandanHttpBaseUrl(kandanUrl);
|
|
6981
7053
|
const entry = authFile.local_codex_runner?.[kandanBaseUrl];
|
|
6982
7054
|
if (entry === undefined || entry.access_token.trim() === "") {
|
|
@@ -6994,7 +7066,7 @@ function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePat
|
|
|
6994
7066
|
}
|
|
6995
7067
|
function writeCachedLocalRunnerToken(args) {
|
|
6996
7068
|
const authFilePath = args.authFilePath ?? defaultAuthFilePath();
|
|
6997
|
-
const existing =
|
|
7069
|
+
const existing = existsSync4(authFilePath) ? parseAuthFile(readFileSync3(authFilePath, "utf8")) : { version: 1 };
|
|
6998
7070
|
const kandanBaseUrl = kandanHttpBaseUrl(args.kandanUrl);
|
|
6999
7071
|
const issuedAt = new Date;
|
|
7000
7072
|
const expiresAt = args.expiresInSeconds === undefined ? undefined : new Date(issuedAt.getTime() + args.expiresInSeconds * 1000).toISOString();
|
|
@@ -7010,8 +7082,8 @@ function writeCachedLocalRunnerToken(args) {
|
|
|
7010
7082
|
}
|
|
7011
7083
|
}
|
|
7012
7084
|
};
|
|
7013
|
-
|
|
7014
|
-
|
|
7085
|
+
mkdirSync5(dirname4(authFilePath), { recursive: true });
|
|
7086
|
+
writeFileSync4(authFilePath, `${JSON.stringify(next, null, 2)}
|
|
7015
7087
|
`, "utf8");
|
|
7016
7088
|
return {
|
|
7017
7089
|
accessToken: args.accessToken,
|
|
@@ -7104,18 +7176,18 @@ async function acquireAndCacheToken(args) {
|
|
|
7104
7176
|
}
|
|
7105
7177
|
|
|
7106
7178
|
// src/localConfig.ts
|
|
7107
|
-
import { existsSync as
|
|
7108
|
-
import { homedir as
|
|
7109
|
-
import { dirname as dirname5, resolve as
|
|
7179
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, realpathSync as realpathSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
7180
|
+
import { homedir as homedir5 } from "node:os";
|
|
7181
|
+
import { dirname as dirname5, resolve as resolve5 } from "node:path";
|
|
7110
7182
|
function localConfigPath(env = process.env) {
|
|
7111
7183
|
const override = env.LINZUMI_CONFIG_FILE;
|
|
7112
|
-
return override !== undefined && override.trim() !== "" ?
|
|
7184
|
+
return override !== undefined && override.trim() !== "" ? resolve5(expandUserPath(override)) : resolve5(homedir5(), ".linzumi", "config.json");
|
|
7113
7185
|
}
|
|
7114
7186
|
function readLocalConfig(path = localConfigPath()) {
|
|
7115
|
-
if (!
|
|
7187
|
+
if (!existsSync5(path)) {
|
|
7116
7188
|
return { version: 1, allowedCwds: [] };
|
|
7117
7189
|
}
|
|
7118
|
-
const parsed = JSON.parse(
|
|
7190
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
7119
7191
|
if (!isConfigPayload(parsed)) {
|
|
7120
7192
|
throw new Error(`invalid Linzumi config: ${path}`);
|
|
7121
7193
|
}
|
|
@@ -7127,21 +7199,21 @@ function readLocalConfig(path = localConfigPath()) {
|
|
|
7127
7199
|
function readConfiguredAllowedCwds(path = localConfigPath()) {
|
|
7128
7200
|
return readLocalConfig(path).allowedCwds.map((cwd) => {
|
|
7129
7201
|
try {
|
|
7130
|
-
return
|
|
7202
|
+
return realpathSync4(resolve5(expandUserPath(cwd)));
|
|
7131
7203
|
} catch (_error) {
|
|
7132
7204
|
throw new Error(`invalid Linzumi config allowed path: ${cwd} does not exist`);
|
|
7133
7205
|
}
|
|
7134
7206
|
});
|
|
7135
7207
|
}
|
|
7136
7208
|
function addAllowedCwd(pathValue, path = localConfigPath()) {
|
|
7137
|
-
const normalizedPath =
|
|
7209
|
+
const normalizedPath = realpathSync4(resolve5(expandUserPath(pathValue)));
|
|
7138
7210
|
const config = readLocalConfig(path);
|
|
7139
7211
|
const allowedCwds = uniqueStrings([...config.allowedCwds, normalizedPath]);
|
|
7140
7212
|
writeLocalConfig({ version: 1, allowedCwds }, path);
|
|
7141
7213
|
return allowedCwds;
|
|
7142
7214
|
}
|
|
7143
7215
|
function removeAllowedCwd(pathValue, path = localConfigPath()) {
|
|
7144
|
-
const requestedPath =
|
|
7216
|
+
const requestedPath = resolve5(expandUserPath(pathValue));
|
|
7145
7217
|
const normalizedRequest = realpathOrResolved(requestedPath);
|
|
7146
7218
|
const config = readLocalConfig(path);
|
|
7147
7219
|
const allowedCwds = config.allowedCwds.filter((cwd) => {
|
|
@@ -7152,8 +7224,8 @@ function removeAllowedCwd(pathValue, path = localConfigPath()) {
|
|
|
7152
7224
|
return allowedCwds;
|
|
7153
7225
|
}
|
|
7154
7226
|
function writeLocalConfig(config, path = localConfigPath()) {
|
|
7155
|
-
|
|
7156
|
-
|
|
7227
|
+
mkdirSync6(dirname5(path), { recursive: true });
|
|
7228
|
+
writeFileSync5(path, `${JSON.stringify(config, null, 2)}
|
|
7157
7229
|
`, "utf8");
|
|
7158
7230
|
}
|
|
7159
7231
|
function isConfigPayload(value) {
|
|
@@ -7164,14 +7236,14 @@ function uniqueStrings(values) {
|
|
|
7164
7236
|
}
|
|
7165
7237
|
function realpathOrResolved(pathValue) {
|
|
7166
7238
|
try {
|
|
7167
|
-
return
|
|
7239
|
+
return realpathSync4(resolve5(expandUserPath(pathValue)));
|
|
7168
7240
|
} catch (_error) {
|
|
7169
|
-
return
|
|
7241
|
+
return resolve5(expandUserPath(pathValue));
|
|
7170
7242
|
}
|
|
7171
7243
|
}
|
|
7172
7244
|
|
|
7173
7245
|
// src/kandanTls.ts
|
|
7174
|
-
import { existsSync as
|
|
7246
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
|
|
7175
7247
|
import { Agent } from "undici";
|
|
7176
7248
|
import { WebSocket as WsWebSocket } from "ws";
|
|
7177
7249
|
function kandanTlsTrustFromEnv() {
|
|
@@ -7182,10 +7254,10 @@ function kandanTlsTrustFromCaFile(caFile) {
|
|
|
7182
7254
|
return;
|
|
7183
7255
|
}
|
|
7184
7256
|
const trimmed = caFile.trim();
|
|
7185
|
-
if (!
|
|
7257
|
+
if (!existsSync6(trimmed)) {
|
|
7186
7258
|
throw new Error(`KANDAN_TLS_CA_FILE does not exist: ${trimmed}`);
|
|
7187
7259
|
}
|
|
7188
|
-
const ca =
|
|
7260
|
+
const ca = readFileSync5(trimmed, "utf8");
|
|
7189
7261
|
return {
|
|
7190
7262
|
caFile: trimmed,
|
|
7191
7263
|
ca,
|
|
@@ -7214,9 +7286,9 @@ function trustedWebSocketFactory(trust, WebSocketImpl = WsWebSocket) {
|
|
|
7214
7286
|
}
|
|
7215
7287
|
|
|
7216
7288
|
// src/agentBootstrap.ts
|
|
7217
|
-
import { existsSync as
|
|
7218
|
-
import { dirname as dirname6, join as
|
|
7219
|
-
import { homedir as
|
|
7289
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
|
|
7290
|
+
import { dirname as dirname6, join as join7 } from "node:path";
|
|
7291
|
+
import { homedir as homedir6 } from "node:os";
|
|
7220
7292
|
var defaultApiUrl = "https://serve.linzumi.com";
|
|
7221
7293
|
async function runAgentCliCommand(args, deps = {
|
|
7222
7294
|
fetchImpl: fetch,
|
|
@@ -7699,7 +7771,7 @@ function agentTokenFile(flags) {
|
|
|
7699
7771
|
return flags.get("agent-token-file") ?? defaultAgentTokenFilePath();
|
|
7700
7772
|
}
|
|
7701
7773
|
function defaultAgentTokenFilePath() {
|
|
7702
|
-
return
|
|
7774
|
+
return join7(homedir6(), ".linzumi", "agent-token.json");
|
|
7703
7775
|
}
|
|
7704
7776
|
function normalizedApiUrl(apiUrl) {
|
|
7705
7777
|
return apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`;
|
|
@@ -7708,11 +7780,11 @@ function authorizationHeaders(token) {
|
|
|
7708
7780
|
return { authorization: `Bearer ${token}` };
|
|
7709
7781
|
}
|
|
7710
7782
|
function readOptionalTextFile(path) {
|
|
7711
|
-
return
|
|
7783
|
+
return existsSync7(path) ? readFileSync6(path, "utf8") : undefined;
|
|
7712
7784
|
}
|
|
7713
7785
|
function writeTextFile(path, content) {
|
|
7714
|
-
|
|
7715
|
-
|
|
7786
|
+
mkdirSync7(dirname6(path), { recursive: true });
|
|
7787
|
+
writeFileSync6(path, content);
|
|
7716
7788
|
}
|
|
7717
7789
|
function readStoredAgentTokenFile(path, readTextFile = readOptionalTextFile) {
|
|
7718
7790
|
const content = readTextFile(path);
|
|
@@ -7773,8 +7845,8 @@ Launch target:
|
|
|
7773
7845
|
}
|
|
7774
7846
|
|
|
7775
7847
|
// src/helloLinzumiProject.ts
|
|
7776
|
-
import { existsSync as
|
|
7777
|
-
import { dirname as dirname7, join as
|
|
7848
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
7849
|
+
import { dirname as dirname7, join as join8, resolve as resolve6 } from "node:path";
|
|
7778
7850
|
import { fileURLToPath } from "node:url";
|
|
7779
7851
|
var defaultHelloLinzumiProjectDir = "/tmp/hello_linzumi";
|
|
7780
7852
|
var defaultHelloLinzumiProjectName = "hello_linzumi";
|
|
@@ -7783,7 +7855,7 @@ var defaultHelloLinzumiPort = 8787;
|
|
|
7783
7855
|
var defaultHelloLinzumiHost = "0.0.0.0";
|
|
7784
7856
|
var markerFile = ".linzumi-demo-project";
|
|
7785
7857
|
var moduleDir = dirname7(fileURLToPath(import.meta.url));
|
|
7786
|
-
var linzumiLogoSvg =
|
|
7858
|
+
var linzumiLogoSvg = readFileSync7(join8(moduleDir, "assets", "linzumi-logo.svg"), "utf8");
|
|
7787
7859
|
function createHelloLinzumiProject(input = {}) {
|
|
7788
7860
|
const options = typeof input === "string" ? { rootPath: input } : input;
|
|
7789
7861
|
const root = resolveHelloProjectRoot(options);
|
|
@@ -7791,9 +7863,9 @@ function createHelloLinzumiProject(input = {}) {
|
|
|
7791
7863
|
const host = normalizeHost(options.host);
|
|
7792
7864
|
assertTcpPort(port);
|
|
7793
7865
|
assertWritableDemoRoot(root, options.reset === true);
|
|
7794
|
-
|
|
7866
|
+
mkdirSync8(join8(root, "src"), { recursive: true });
|
|
7795
7867
|
for (const file of demoFiles({ root, port, host })) {
|
|
7796
|
-
|
|
7868
|
+
writeFileSync7(join8(root, file.path), file.content, "utf8");
|
|
7797
7869
|
}
|
|
7798
7870
|
return {
|
|
7799
7871
|
root,
|
|
@@ -7808,10 +7880,10 @@ function resolveHelloProjectRoot(options) {
|
|
|
7808
7880
|
throw new Error("linzumi init-hello-linzumi-demo-app accepts either --dir or --parent-dir/--name, not both");
|
|
7809
7881
|
}
|
|
7810
7882
|
if (options.rootPath !== undefined) {
|
|
7811
|
-
return
|
|
7883
|
+
return resolve6(options.rootPath);
|
|
7812
7884
|
}
|
|
7813
7885
|
const name = normalizeProjectName(options.name ?? defaultHelloLinzumiProjectName);
|
|
7814
|
-
return
|
|
7886
|
+
return resolve6(options.parentDir ?? defaultHelloLinzumiParentDir, name);
|
|
7815
7887
|
}
|
|
7816
7888
|
function normalizeProjectName(value) {
|
|
7817
7889
|
const name = value.trim();
|
|
@@ -7834,11 +7906,11 @@ function assertTcpPort(port) {
|
|
|
7834
7906
|
throw new Error("--port must be a TCP port from 1 to 65535");
|
|
7835
7907
|
}
|
|
7836
7908
|
function assertWritableDemoRoot(root, reset) {
|
|
7837
|
-
if (!
|
|
7909
|
+
if (!existsSync8(root)) {
|
|
7838
7910
|
return;
|
|
7839
7911
|
}
|
|
7840
|
-
const markerPath =
|
|
7841
|
-
const isDemoRoot =
|
|
7912
|
+
const markerPath = join8(root, markerFile);
|
|
7913
|
+
const isDemoRoot = existsSync8(markerPath) && readFileSync7(markerPath, "utf8").trim() === "hello-linzumi";
|
|
7842
7914
|
if (isDemoRoot && reset) {
|
|
7843
7915
|
rmSync2(root, { recursive: true, force: true });
|
|
7844
7916
|
return;
|
|
@@ -8388,7 +8460,7 @@ function isMainModule() {
|
|
|
8388
8460
|
if (scriptPath === undefined) {
|
|
8389
8461
|
return false;
|
|
8390
8462
|
}
|
|
8391
|
-
return fileURLToPath2(import.meta.url) ===
|
|
8463
|
+
return fileURLToPath2(import.meta.url) === resolve7(scriptPath);
|
|
8392
8464
|
}
|
|
8393
8465
|
async function main(args) {
|
|
8394
8466
|
const parsed = parseCommand(args);
|
|
@@ -8397,7 +8469,7 @@ async function main(args) {
|
|
|
8397
8469
|
process.stdout.write(connectGuideText());
|
|
8398
8470
|
return;
|
|
8399
8471
|
case "version":
|
|
8400
|
-
process.stdout.write(`linzumi 0.0.
|
|
8472
|
+
process.stdout.write(`linzumi 0.0.25-beta
|
|
8401
8473
|
`);
|
|
8402
8474
|
return;
|
|
8403
8475
|
case "auth":
|
|
@@ -8452,6 +8524,7 @@ function parseCommand(args) {
|
|
|
8452
8524
|
case "agent":
|
|
8453
8525
|
return rest[0] === "runner" ? { command: "agentRunner", args: rest.slice(1) } : { command: "agent", args: rest };
|
|
8454
8526
|
case "agent-runner":
|
|
8527
|
+
case "commander":
|
|
8455
8528
|
return { command: "agentRunner", args: rest };
|
|
8456
8529
|
case "signup":
|
|
8457
8530
|
case "claim":
|
|
@@ -8531,7 +8604,7 @@ function runPathsCommand(args) {
|
|
|
8531
8604
|
if (pathValue === undefined || pathValue.trim() === "") {
|
|
8532
8605
|
throw new Error("missing path for linzumi paths add");
|
|
8533
8606
|
}
|
|
8534
|
-
const trustedPath =
|
|
8607
|
+
const trustedPath = realpathSync5(resolve7(expandUserPath(pathValue)));
|
|
8535
8608
|
addAllowedCwd(pathValue);
|
|
8536
8609
|
process.stdout.write(`Trusted ${trustedPath}
|
|
8537
8610
|
`);
|
|
@@ -8686,7 +8759,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
8686
8759
|
}
|
|
8687
8760
|
rejectAgentRunnerTargetingFlags(values);
|
|
8688
8761
|
if (cwdArg !== undefined && values.has("cwd")) {
|
|
8689
|
-
throw new Error("linzumi
|
|
8762
|
+
throw new Error("linzumi commander accepts either <folder> or --cwd, not both");
|
|
8690
8763
|
}
|
|
8691
8764
|
const tokenFilePath = stringValue3(values, "agent-token-file") ?? defaultAgentTokenFilePath();
|
|
8692
8765
|
const tokenFile = readStoredAgentTokenFile(tokenFilePath, deps.readTextFile);
|
|
@@ -8747,19 +8820,19 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
8747
8820
|
};
|
|
8748
8821
|
}
|
|
8749
8822
|
function readAgentTokenTextFile(path) {
|
|
8750
|
-
return
|
|
8823
|
+
return existsSync9(path) ? readFileSync8(path, "utf8") : undefined;
|
|
8751
8824
|
}
|
|
8752
8825
|
function rejectAgentRunnerTargetingFlags(values) {
|
|
8753
8826
|
const unsupportedFlags = ["workspace", "channel", "token", "auth-file", "oauth-callback-host"].filter((flag) => values.has(flag));
|
|
8754
8827
|
if (unsupportedFlags.length > 0) {
|
|
8755
|
-
throw new Error(`linzumi
|
|
8828
|
+
throw new Error(`linzumi commander uses the claimed agent token scope; remove ${unsupportedFlags.map((flag) => `--${flag}`).join(", ")}.`);
|
|
8756
8829
|
}
|
|
8757
8830
|
}
|
|
8758
8831
|
function requiredStoredAgentChannel(channelId) {
|
|
8759
8832
|
if (channelId !== undefined) {
|
|
8760
8833
|
return channelId;
|
|
8761
8834
|
}
|
|
8762
|
-
throw new Error("agent token file is missing channelId; rerun linzumi claim before starting
|
|
8835
|
+
throw new Error("agent token file is missing channelId; rerun linzumi claim before starting a Commander");
|
|
8763
8836
|
}
|
|
8764
8837
|
function requiredStoredOwnerUsername(ownerUsername) {
|
|
8765
8838
|
if (ownerUsername !== undefined) {
|
|
@@ -8817,7 +8890,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
8817
8890
|
process.exit(0);
|
|
8818
8891
|
}
|
|
8819
8892
|
if (values.get("version") === true) {
|
|
8820
|
-
process.stdout.write(`linzumi 0.0.
|
|
8893
|
+
process.stdout.write(`linzumi 0.0.25-beta
|
|
8821
8894
|
`);
|
|
8822
8895
|
process.exit(0);
|
|
8823
8896
|
}
|
|
@@ -8936,12 +9009,12 @@ function rejectStartTargetingFlags(values) {
|
|
|
8936
9009
|
}
|
|
8937
9010
|
function resolveUserPath(pathValue) {
|
|
8938
9011
|
if (pathValue === "~") {
|
|
8939
|
-
return
|
|
9012
|
+
return homedir7();
|
|
8940
9013
|
}
|
|
8941
9014
|
if (pathValue.startsWith("~/")) {
|
|
8942
|
-
return
|
|
9015
|
+
return resolve7(homedir7(), pathValue.slice(2));
|
|
8943
9016
|
}
|
|
8944
|
-
return
|
|
9017
|
+
return resolve7(pathValue);
|
|
8945
9018
|
}
|
|
8946
9019
|
function parseChannelSession(values, token, target) {
|
|
8947
9020
|
if (target === undefined) {
|
|
@@ -9039,7 +9112,7 @@ Usage:
|
|
|
9039
9112
|
linzumi inbox <thread_id> --since-last
|
|
9040
9113
|
linzumi done <thread_id> --message <message>
|
|
9041
9114
|
linzumi init-hello-linzumi-demo-app
|
|
9042
|
-
linzumi
|
|
9115
|
+
linzumi commander <folder> [options]
|
|
9043
9116
|
linzumi start <folder> [options]
|
|
9044
9117
|
linzumi paths list|add|remove [path]
|
|
9045
9118
|
linzumi connect --kandan-url <ws-url> --workspace <slug> --channel <slug> [options]
|
|
@@ -9081,7 +9154,7 @@ Examples:
|
|
|
9081
9154
|
linzumi post thr_abc123 "PR is open"
|
|
9082
9155
|
linzumi done thr_abc123 --message "Done: https://github.com/example/repo/pull/1"
|
|
9083
9156
|
linzumi init-hello-linzumi-demo-app
|
|
9084
|
-
linzumi
|
|
9157
|
+
linzumi commander ~/code/my-app --runner-id launch-commander
|
|
9085
9158
|
linzumi start ~/
|
|
9086
9159
|
linzumi start ~/code/my-app
|
|
9087
9160
|
linzumi connect --workspace <your-workspace> --channel <your-channel> --launch-tui
|
|
@@ -9173,13 +9246,14 @@ Examples:
|
|
|
9173
9246
|
`;
|
|
9174
9247
|
}
|
|
9175
9248
|
function agentRunnerHelpText() {
|
|
9176
|
-
return `Linzumi
|
|
9249
|
+
return `Linzumi Commander
|
|
9177
9250
|
|
|
9178
9251
|
Usage:
|
|
9252
|
+
linzumi commander <folder> [options]
|
|
9179
9253
|
linzumi agent runner <folder> [options]
|
|
9180
9254
|
|
|
9181
9255
|
What it does:
|
|
9182
|
-
Starts this computer as the claimed agent's scoped
|
|
9256
|
+
Starts this computer as the claimed agent's scoped Linzumi Commander. The command
|
|
9183
9257
|
reads ~/.linzumi/agent-token.json, uses its workspace/channel scope, trusts
|
|
9184
9258
|
only the selected folder by default, and listens only to the owning human
|
|
9185
9259
|
recorded during claim unless --listen-user is passed.
|
|
@@ -9187,7 +9261,7 @@ What it does:
|
|
|
9187
9261
|
Options:
|
|
9188
9262
|
--agent-token-file <path> Agent token cache, default ~/.linzumi/agent-token.json
|
|
9189
9263
|
--kandan-url <ws-url> Kandan websocket base URL. Defaults deterministically from the stored apiUrl.
|
|
9190
|
-
--runner-id <id> Stable
|
|
9264
|
+
--runner-id <id> Stable Commander id
|
|
9191
9265
|
--codex-bin <path> Codex executable, default codex
|
|
9192
9266
|
--code-server-bin <path> Custom development code-server executable. By default Kandan installs the approved editor runtime.
|
|
9193
9267
|
--listen-user <user> Human whose replies Codex may accept, default owner from claim
|
|
@@ -9197,11 +9271,11 @@ Options:
|
|
|
9197
9271
|
--approval-policy <value> Approval-policy metadata shown in Kandan
|
|
9198
9272
|
--forward-port <ports> Comma-separated local TCP ports Kandan may expose as previews
|
|
9199
9273
|
--allowed-cwd <paths> Override the selected folder with comma-separated trusted roots
|
|
9200
|
-
--fast Mark this
|
|
9274
|
+
--fast Mark this Commander as low-latency/fast in Linzumi
|
|
9201
9275
|
|
|
9202
9276
|
Examples:
|
|
9203
|
-
linzumi
|
|
9204
|
-
linzumi
|
|
9277
|
+
linzumi commander "$PWD" --runner-id hello-world-commander
|
|
9278
|
+
linzumi commander ~/code/my-app --kandan-url ws://127.0.0.1:4162 --runner-id local-qa-commander
|
|
9205
9279
|
`;
|
|
9206
9280
|
}
|
|
9207
9281
|
function connectGuideText() {
|
package/package.json
CHANGED