@linzumi/cli 0.0.24-beta → 0.0.26-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 +12 -9
- package/dist/index.js +236 -164
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,10 +132,11 @@ npx -y @linzumi/cli@latest channel post <support_channel_id> "Hello @sean, start
|
|
|
132
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
133
|
commander_id="hello-linzumi-commander-${thread_id%%-*}"
|
|
134
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
|
|
135
|
-
(cd /tmp/hello_linzumi &&
|
|
136
|
-
|
|
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" \
|
|
137
138
|
--runner-id "$commander_id" \
|
|
138
|
-
--allowed-cwd
|
|
139
|
+
--allowed-cwd "$project_dir" \
|
|
139
140
|
--forward-port 8787 \
|
|
140
141
|
--sandbox danger-full-access \
|
|
141
142
|
--approval-policy never
|
|
@@ -143,8 +144,10 @@ npx -y @linzumi/cli@latest commander /tmp/hello_linzumi \
|
|
|
143
144
|
|
|
144
145
|
The agent-owned Commander reads `~/.linzumi/agent-token.json`, uses the
|
|
145
146
|
workspace/channel scope from the approval flow, trusts only the selected
|
|
146
|
-
folder,
|
|
147
|
-
|
|
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
|
|
148
151
|
Commander id per launch thread; Linzumi stores trusted-folder config per
|
|
149
152
|
Commander id, so reusing an old fixed id can pick up stale allowed-cwd config.
|
|
150
153
|
The bootstrap agent waits for `Runner connected:` before starting Codex in the Linzumi
|
|
@@ -170,12 +173,12 @@ Codex and open the browser editor for the same thread and folder:
|
|
|
170
173
|
```bash
|
|
171
174
|
npx -y @linzumi/cli@latest codex start <thread_id> \
|
|
172
175
|
--runner "$commander_id" \
|
|
173
|
-
--cwd
|
|
174
|
-
--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."
|
|
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."
|
|
175
178
|
|
|
176
179
|
npx -y @linzumi/cli@latest editor open <thread_id> \
|
|
177
180
|
--runner "$commander_id" \
|
|
178
|
-
--cwd
|
|
181
|
+
--cwd "$project_dir"
|
|
179
182
|
```
|
|
180
183
|
|
|
181
184
|
The launch target for this path is zero-to-Hello-Linzumi-editor+preview in
|
|
@@ -276,7 +279,7 @@ intentionally. Every action is auditable from the thread.
|
|
|
276
279
|
## Pinning a version
|
|
277
280
|
|
|
278
281
|
```bash
|
|
279
|
-
npm install -g @linzumi/cli@0.0.
|
|
282
|
+
npm install -g @linzumi/cli@0.0.26-beta
|
|
280
283
|
linzumi --version
|
|
281
284
|
```
|
|
282
285
|
|
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') : `${trimTrailingNewlines(table)}
|
|
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.26-beta
|
|
8401
8473
|
`);
|
|
8402
8474
|
return;
|
|
8403
8475
|
case "auth":
|
|
@@ -8532,7 +8604,7 @@ function runPathsCommand(args) {
|
|
|
8532
8604
|
if (pathValue === undefined || pathValue.trim() === "") {
|
|
8533
8605
|
throw new Error("missing path for linzumi paths add");
|
|
8534
8606
|
}
|
|
8535
|
-
const trustedPath =
|
|
8607
|
+
const trustedPath = realpathSync5(resolve7(expandUserPath(pathValue)));
|
|
8536
8608
|
addAllowedCwd(pathValue);
|
|
8537
8609
|
process.stdout.write(`Trusted ${trustedPath}
|
|
8538
8610
|
`);
|
|
@@ -8748,7 +8820,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
8748
8820
|
};
|
|
8749
8821
|
}
|
|
8750
8822
|
function readAgentTokenTextFile(path) {
|
|
8751
|
-
return
|
|
8823
|
+
return existsSync9(path) ? readFileSync8(path, "utf8") : undefined;
|
|
8752
8824
|
}
|
|
8753
8825
|
function rejectAgentRunnerTargetingFlags(values) {
|
|
8754
8826
|
const unsupportedFlags = ["workspace", "channel", "token", "auth-file", "oauth-callback-host"].filter((flag) => values.has(flag));
|
|
@@ -8818,7 +8890,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
8818
8890
|
process.exit(0);
|
|
8819
8891
|
}
|
|
8820
8892
|
if (values.get("version") === true) {
|
|
8821
|
-
process.stdout.write(`linzumi 0.0.
|
|
8893
|
+
process.stdout.write(`linzumi 0.0.26-beta
|
|
8822
8894
|
`);
|
|
8823
8895
|
process.exit(0);
|
|
8824
8896
|
}
|
|
@@ -8937,12 +9009,12 @@ function rejectStartTargetingFlags(values) {
|
|
|
8937
9009
|
}
|
|
8938
9010
|
function resolveUserPath(pathValue) {
|
|
8939
9011
|
if (pathValue === "~") {
|
|
8940
|
-
return
|
|
9012
|
+
return homedir7();
|
|
8941
9013
|
}
|
|
8942
9014
|
if (pathValue.startsWith("~/")) {
|
|
8943
|
-
return
|
|
9015
|
+
return resolve7(homedir7(), pathValue.slice(2));
|
|
8944
9016
|
}
|
|
8945
|
-
return
|
|
9017
|
+
return resolve7(pathValue);
|
|
8946
9018
|
}
|
|
8947
9019
|
function parseChannelSession(values, token, target) {
|
|
8948
9020
|
if (target === undefined) {
|
package/package.json
CHANGED