@linzumi/cli 0.0.33-beta → 0.0.34-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +30 -292
  2. package/dist/index.js +646 -151
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/index.ts
2
2
  import { randomUUID as randomUUID3 } from "node:crypto";
3
3
  import { existsSync as existsSync10, readFileSync as readFileSync9, realpathSync as realpathSync5 } from "node:fs";
4
- import { homedir as homedir8 } from "node:os";
4
+ import { homedir as homedir9 } from "node:os";
5
5
  import { resolve as resolve8 } from "node:path";
6
6
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7
7
 
@@ -9,7 +9,7 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
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 join5 } from "node:path";
12
+ import { join as join6 } from "node:path";
13
13
 
14
14
  // src/channelSessionSupport.ts
15
15
  import { spawnSync } from "node:child_process";
@@ -1872,7 +1872,11 @@ async function resolvePendingPortForwardRequest(args, state, payloadContext, con
1872
1872
  }
1873
1873
  state.approvedForwardPorts.add(request.port);
1874
1874
  state.approvedForwardTargets.set(request.port, approvedTargetFromRequest(request));
1875
- const capabilities = args.options.onForwardPortApproved?.(request.port);
1875
+ const capabilities = args.options.onForwardPortApproved?.(request.port, {
1876
+ kandanThreadId: state.kandanThreadId ?? null,
1877
+ codexThreadId: state.codexThreadId ?? null,
1878
+ channelSlug: args.options.channelSession.channelSlug ?? null
1879
+ });
1876
1880
  await publishForwardPortResolvedEvent(args, request, capabilities);
1877
1881
  await publishMessageStateForPortForwardResult(args, state, request, "processed");
1878
1882
  await publishPortForwardReadyMessage(args, state, payloadContext, request);
@@ -3959,6 +3963,179 @@ async function pushOptional(kandan, topic, event, payload, log) {
3959
3963
  // src/codexAppServer.ts
3960
3964
  import { spawn } from "node:child_process";
3961
3965
  import { createServer } from "node:net";
3966
+
3967
+ // src/runnerLogger.ts
3968
+ import { appendFileSync, openSync } from "node:fs";
3969
+ import { createWriteStream } from "node:fs";
3970
+ import { homedir } from "node:os";
3971
+ import { dirname, join as join2 } from "node:path";
3972
+ import { mkdirSync } from "node:fs";
3973
+ var sensitiveMarker = "<SENSITIVE_DATA>";
3974
+ var sensitiveQueryParams = new Set([
3975
+ "access_token",
3976
+ "authorization",
3977
+ "code",
3978
+ "cookie",
3979
+ "kandan_preview_ticket",
3980
+ "refresh_token",
3981
+ "token"
3982
+ ]);
3983
+ var sensitiveArgFlags = new Set([
3984
+ "--access-token",
3985
+ "--api-key",
3986
+ "--authorization",
3987
+ "--cookie",
3988
+ "--oauth-code",
3989
+ "--password",
3990
+ "--refresh-token",
3991
+ "--secret",
3992
+ "--token"
3993
+ ]);
3994
+ function createRunnerLogger(logFile, consoleReporter) {
3995
+ mkdirSync(dirname(logFile), { recursive: true });
3996
+ const fd = openSync(logFile, "a");
3997
+ const stream = createWriteStream("", { fd, flags: "a", autoClose: true });
3998
+ const logger = (event, payload) => {
3999
+ const redacted = redactForCliLog(payload);
4000
+ stream.write(`${JSON.stringify({ ts: new Date().toISOString(), event, ...redacted })}
4001
+ `, "utf8");
4002
+ consoleReporter?.(event, redacted);
4003
+ };
4004
+ Object.defineProperty(logger, "close", {
4005
+ value: () => closeStream(stream)
4006
+ });
4007
+ return logger;
4008
+ }
4009
+ function writeCliAuditEvent(event, payload, options = {}) {
4010
+ const logFile = options.logFile ?? defaultCliAuditLogFile();
4011
+ try {
4012
+ mkdirSync(dirname(logFile), { recursive: true });
4013
+ appendFileSync(logFile, `${JSON.stringify({
4014
+ ts: new Date().toISOString(),
4015
+ event,
4016
+ ...options.sessionId === undefined ? {} : { sessionId: options.sessionId },
4017
+ ...redactForCliLog(payload)
4018
+ })}
4019
+ `, "utf8");
4020
+ } catch (_error) {
4021
+ return;
4022
+ }
4023
+ }
4024
+ function defaultCliAuditLogFile() {
4025
+ const override = process.env.LINZUMI_CLI_AUDIT_LOG?.trim();
4026
+ return override === undefined || override === "" ? join2(homedir(), ".linzumi", "logs", "command-events.jsonl") : override;
4027
+ }
4028
+ function redactForCliLog(value) {
4029
+ return redactValue(value, undefined);
4030
+ }
4031
+ function redactValue(value, key) {
4032
+ if (sensitiveKey(key)) {
4033
+ return sensitiveMarker;
4034
+ }
4035
+ if (typeof value === "string") {
4036
+ return redactString(value);
4037
+ }
4038
+ if (Array.isArray(value)) {
4039
+ return key === "args" ? redactArgs(value) : value.map((item) => redactValue(item, undefined));
4040
+ }
4041
+ if (value !== null && typeof value === "object") {
4042
+ return redactObject(value);
4043
+ }
4044
+ return value;
4045
+ }
4046
+ function redactObject(value) {
4047
+ const headerName = typeof value.name === "string" ? value.name.toLowerCase() : undefined;
4048
+ const shouldRedactHeaderValue = headerName === "authorization" || headerName === "cookie" || headerName === "set-cookie";
4049
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
4050
+ key,
4051
+ shouldRedactHeaderValue && key === "value" ? sensitiveMarker : redactValue(entry, key)
4052
+ ]));
4053
+ }
4054
+ function redactArgs(args) {
4055
+ let redactNext = false;
4056
+ return args.map((arg) => {
4057
+ if (typeof arg !== "string") {
4058
+ redactNext = false;
4059
+ return redactValue(arg, undefined);
4060
+ }
4061
+ if (redactNext) {
4062
+ redactNext = false;
4063
+ return sensitiveMarker;
4064
+ }
4065
+ const [flag, value] = splitArgAssignment(arg);
4066
+ if (sensitiveArgFlags.has(flag)) {
4067
+ if (value === undefined) {
4068
+ redactNext = true;
4069
+ return arg;
4070
+ }
4071
+ return `${flag}=${sensitiveMarker}`;
4072
+ }
4073
+ return redactString(arg);
4074
+ });
4075
+ }
4076
+ function splitArgAssignment(value) {
4077
+ const index = value.indexOf("=");
4078
+ return index === -1 ? [value, undefined] : [value.slice(0, index), value.slice(index + 1)];
4079
+ }
4080
+ function sensitiveKey(key) {
4081
+ if (key === undefined) {
4082
+ return false;
4083
+ }
4084
+ return /^(authorization|cookie|set-cookie|password)$/i.test(key) || /(^|[_-])(access[_-]?token|api[_-]?key|auth[_-]?token|oauth[_-]?code|refresh[_-]?token|secret|token)$/i.test(key);
4085
+ }
4086
+ function redactString(value) {
4087
+ const withRedactedQuery = redactUrlQuery(value);
4088
+ return withRedactedQuery.replace(/\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, sensitiveMarker);
4089
+ }
4090
+ function redactUrlQuery(value) {
4091
+ let parsed;
4092
+ try {
4093
+ parsed = new URL(value);
4094
+ } catch (_error) {
4095
+ return redactRelativeUrlQuery(value);
4096
+ }
4097
+ for (const name of [...parsed.searchParams.keys()]) {
4098
+ if (sensitiveQueryParams.has(name.toLowerCase())) {
4099
+ parsed.searchParams.set(name, sensitiveMarker);
4100
+ }
4101
+ }
4102
+ return parsed.toString();
4103
+ }
4104
+ function redactRelativeUrlQuery(value) {
4105
+ const queryStart = value.indexOf("?");
4106
+ const hashStart = value.indexOf("#");
4107
+ if (queryStart === -1 || hashStart !== -1 && hashStart < queryStart) {
4108
+ return value;
4109
+ }
4110
+ const path = value.slice(0, queryStart);
4111
+ const query = hashStart === -1 ? value.slice(queryStart + 1) : value.slice(queryStart + 1, hashStart);
4112
+ const hash = hashStart === -1 ? "" : value.slice(hashStart);
4113
+ const searchParams = new URLSearchParams(query);
4114
+ let redacted = false;
4115
+ for (const name of [...searchParams.keys()]) {
4116
+ if (sensitiveQueryParams.has(name.toLowerCase())) {
4117
+ searchParams.set(name, sensitiveMarker);
4118
+ redacted = true;
4119
+ }
4120
+ }
4121
+ switch (redacted) {
4122
+ case true:
4123
+ return `${path}?${searchParams.toString()}${hash}`;
4124
+ case false:
4125
+ return value;
4126
+ }
4127
+ }
4128
+ function closeStream(stream) {
4129
+ if (stream.closed || stream.destroyed) {
4130
+ return Promise.resolve();
4131
+ }
4132
+ return new Promise((resolve2, reject) => {
4133
+ stream.once("error", reject);
4134
+ stream.end(resolve2);
4135
+ });
4136
+ }
4137
+
4138
+ // src/codexAppServer.ts
3962
4139
  async function chooseLoopbackPort() {
3963
4140
  return new Promise((resolve2, reject) => {
3964
4141
  const server = createServer();
@@ -3984,12 +4161,35 @@ async function chooseLoopbackPort() {
3984
4161
  async function startCodexAppServer(codexBin, cwd, options = {}) {
3985
4162
  const port = await chooseLoopbackPort();
3986
4163
  const url = `ws://127.0.0.1:${port}`;
3987
- const child = spawn(codexBin, codexAppServerArgs(url, options), {
4164
+ const args = codexAppServerArgs(url, options);
4165
+ writeCliAuditEvent("process.spawn", {
4166
+ command: codexBin,
4167
+ args,
4168
+ cwd,
4169
+ purpose: "codex.app_server"
4170
+ });
4171
+ const child = spawn(codexBin, args, {
3988
4172
  cwd,
3989
4173
  env: process.env,
3990
4174
  stdio: ["ignore", "inherit", "inherit"]
3991
4175
  });
3992
- child.once("exit", (code) => {
4176
+ writeCliAuditEvent("process.spawned", {
4177
+ command: codexBin,
4178
+ args,
4179
+ cwd,
4180
+ pid: child.pid,
4181
+ purpose: "codex.app_server"
4182
+ });
4183
+ child.once("exit", (code, signal) => {
4184
+ writeCliAuditEvent("process.exit", {
4185
+ command: codexBin,
4186
+ args,
4187
+ cwd,
4188
+ pid: child.pid,
4189
+ code,
4190
+ signal,
4191
+ purpose: "codex.app_server"
4192
+ });
3993
4193
  if (code !== 0) {
3994
4194
  process.stderr.write(`codex app-server exited with code ${code ?? "signal"}
3995
4195
  `);
@@ -4218,21 +4418,21 @@ function readyzUrlForWebsocket(websocketUrl) {
4218
4418
  // src/codexProjectTrust.ts
4219
4419
  import {
4220
4420
  existsSync,
4221
- mkdirSync,
4421
+ mkdirSync as mkdirSync2,
4222
4422
  readFileSync,
4223
4423
  realpathSync,
4224
4424
  writeFileSync
4225
4425
  } from "node:fs";
4226
- import { homedir } from "node:os";
4227
- import { join as join2, resolve as resolve2 } from "node:path";
4426
+ import { homedir as homedir2 } from "node:os";
4427
+ import { join as join3, resolve as resolve2 } from "node:path";
4228
4428
  function ensureCodexProjectTrusted(projectPath, options = {}) {
4229
4429
  const trustedPath = realpathSync(resolve2(projectPath));
4230
- const configHome = options.configHome ?? process.env.CODEX_HOME ?? join2(homedir(), ".codex");
4231
- const configPath = join2(configHome, "config.toml");
4430
+ const configHome = options.configHome ?? process.env.CODEX_HOME ?? join3(homedir2(), ".codex");
4431
+ const configPath = join3(configHome, "config.toml");
4232
4432
  const currentConfig = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
4233
4433
  const nextConfig = codexConfigWithTrustedProject(currentConfig, trustedPath);
4234
4434
  if (nextConfig !== currentConfig) {
4235
- mkdirSync(configHome, { recursive: true });
4435
+ mkdirSync2(configHome, { recursive: true });
4236
4436
  writeFileSync(configPath, nextConfig);
4237
4437
  }
4238
4438
  return trustedPath;
@@ -4279,7 +4479,7 @@ function trimTrailingNewlines(value) {
4279
4479
 
4280
4480
  // src/localCapabilities.ts
4281
4481
  import { realpathSync as realpathSync2 } from "node:fs";
4282
- import { homedir as homedir2 } from "node:os";
4482
+ import { homedir as homedir3 } from "node:os";
4283
4483
  import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3 } from "node:path";
4284
4484
  function parseAllowedCwdList(value) {
4285
4485
  if (value === undefined) {
@@ -4319,10 +4519,10 @@ function assertConfiguredAllowedCwds(paths) {
4319
4519
  }
4320
4520
  function expandUserPath(pathValue) {
4321
4521
  if (pathValue === "~") {
4322
- return homedir2();
4522
+ return homedir3();
4323
4523
  }
4324
4524
  if (pathValue.startsWith("~/")) {
4325
- return resolve3(homedir2(), pathValue.slice(2));
4525
+ return resolve3(homedir3(), pathValue.slice(2));
4326
4526
  }
4327
4527
  return pathValue;
4328
4528
  }
@@ -4368,6 +4568,7 @@ async function handleForwardHttpRequest(control, allowedPorts) {
4368
4568
  const request = {
4369
4569
  method: control.method,
4370
4570
  headers: requestHeaders(control.headers),
4571
+ redirect: "manual",
4371
4572
  ...bodyDecision.body === undefined ? {} : { body: bodyDecision.body }
4372
4573
  };
4373
4574
  const response = await fetchWithHttpsFallback(control.port, control.path, control.queryString, request);
@@ -4642,13 +4843,13 @@ import { spawn as spawn2 } from "node:child_process";
4642
4843
  import {
4643
4844
  cpSync,
4644
4845
  existsSync as existsSync2,
4645
- mkdirSync as mkdirSync2,
4846
+ mkdirSync as mkdirSync3,
4646
4847
  mkdtempSync,
4647
4848
  realpathSync as realpathSync3,
4648
4849
  writeFileSync as writeFileSync2
4649
4850
  } from "node:fs";
4650
4851
  import { tmpdir } from "node:os";
4651
- import { basename as basename3, delimiter, dirname, join as join3 } from "node:path";
4852
+ import { basename as basename3, delimiter, dirname as dirname2, join as join4 } from "node:path";
4652
4853
  function isStartLocalEditorControl(control) {
4653
4854
  return control.type === "start_local_editor";
4654
4855
  }
@@ -4728,7 +4929,7 @@ async function startLocalEditor(control, options) {
4728
4929
  reason: launchResult.reason
4729
4930
  };
4730
4931
  }
4731
- const collaborationResult = await startCollaborationSidecar(collaboration, profileResult, options.editorRuntime);
4932
+ const collaborationResult = await startCollaborationSidecar(collaboration, profileResult, options.editorRuntime, options.runnerId);
4732
4933
  if (!collaborationResult.ok) {
4733
4934
  return {
4734
4935
  ok: false,
@@ -4736,11 +4937,35 @@ async function startLocalEditor(control, options) {
4736
4937
  reason: "code_server_spawn_failed"
4737
4938
  };
4738
4939
  }
4940
+ writeCliAuditEvent("process.spawn", {
4941
+ command: launchResult.command,
4942
+ args: launchResult.args,
4943
+ cwd: cwdDecision.cwd,
4944
+ purpose: "local_editor.code_server"
4945
+ }, { sessionId: options.runnerId });
4739
4946
  const child = spawn2(launchResult.command, [...launchResult.args], {
4740
4947
  cwd: cwdDecision.cwd,
4741
- env: codeServerEnv(process.env, profileResult.userDataDir, collaborationResult.collaboration),
4948
+ env: codeServerEnv(process.env, cwdDecision.cwd, profileResult.userDataDir, collaborationResult.collaboration),
4742
4949
  stdio: ["ignore", "inherit", "inherit"]
4743
4950
  });
4951
+ writeCliAuditEvent("process.spawned", {
4952
+ command: launchResult.command,
4953
+ args: launchResult.args,
4954
+ cwd: cwdDecision.cwd,
4955
+ pid: child.pid,
4956
+ purpose: "local_editor.code_server"
4957
+ }, { sessionId: options.runnerId });
4958
+ child.once("exit", (code, signal) => {
4959
+ writeCliAuditEvent("process.exit", {
4960
+ command: launchResult.command,
4961
+ args: launchResult.args,
4962
+ cwd: cwdDecision.cwd,
4963
+ pid: child.pid,
4964
+ code,
4965
+ signal,
4966
+ purpose: "local_editor.code_server"
4967
+ }, { sessionId: options.runnerId });
4968
+ });
4744
4969
  const exited = waitForCodeServerExit(child);
4745
4970
  const spawnResult = await waitForCodeServerSpawn(child);
4746
4971
  if (spawnResult === "failed") {
@@ -4811,17 +5036,17 @@ function codeServerArgs(port, cwd, userDataDir, extensionsDir) {
4811
5036
  }
4812
5037
  function prepareCodeServerProfile(collaboration, editorRuntime) {
4813
5038
  try {
4814
- const userDataDir = mkdtempSync(join3(tmpdir(), "kandan-local-editor-"));
4815
- const extensionsDir = join3(userDataDir, "extensions");
4816
- const collaborationServerDir = join3(userDataDir, "collaboration-server");
4817
- const userSettingsDir = join3(userDataDir, "User");
4818
- mkdirSync2(userSettingsDir, { recursive: true });
4819
- mkdirSync2(extensionsDir, { recursive: true });
4820
- mkdirSync2(collaborationServerDir, { recursive: true });
5039
+ const userDataDir = mkdtempSync(join4(tmpdir(), "kandan-local-editor-"));
5040
+ const extensionsDir = join4(userDataDir, "extensions");
5041
+ const collaborationServerDir = join4(userDataDir, "collaboration-server");
5042
+ const userSettingsDir = join4(userDataDir, "User");
5043
+ mkdirSync3(userSettingsDir, { recursive: true });
5044
+ mkdirSync3(extensionsDir, { recursive: true });
5045
+ mkdirSync3(collaborationServerDir, { recursive: true });
4821
5046
  if (editorRuntime !== undefined) {
4822
- installDirectory(editorRuntime.assets.documentStateExtensionDir, join3(extensionsDir, "kandan.document-state-telemetry"));
5047
+ installDirectory(editorRuntime.assets.documentStateExtensionDir, join4(extensionsDir, "kandan.document-state-telemetry"));
4823
5048
  }
4824
- writeFileSync2(join3(userSettingsDir, "settings.json"), JSON.stringify(codeServerSettings(collaboration), null, 2));
5049
+ writeFileSync2(join4(userSettingsDir, "settings.json"), JSON.stringify(codeServerSettings(collaboration), null, 2));
4825
5050
  return { ok: true, userDataDir, extensionsDir, collaborationServerDir };
4826
5051
  } catch (_error) {
4827
5052
  return { ok: false, reason: "code_server_spawn_failed" };
@@ -4887,13 +5112,16 @@ function prepareLinuxCodeServerLaunch(options) {
4887
5112
  "/tmp",
4888
5113
  "--setenv",
4889
5114
  "HOME",
4890
- options.userDataDir,
5115
+ options.cwd,
5116
+ "--setenv",
5117
+ "PWD",
5118
+ options.cwd,
4891
5119
  "--setenv",
4892
5120
  "XDG_DATA_HOME",
4893
- join3(options.userDataDir, "data"),
5121
+ join4(options.userDataDir, "data"),
4894
5122
  "--setenv",
4895
5123
  "XDG_CONFIG_HOME",
4896
- join3(options.userDataDir, "config"),
5124
+ join4(options.userDataDir, "config"),
4897
5125
  ...readOnlyRoots.flatMap((path) => ["--ro-bind-try", path, path]),
4898
5126
  "--bind",
4899
5127
  options.cwd,
@@ -4966,12 +5194,12 @@ function resolveCodeServerExecutable(command, envPath) {
4966
5194
  if (directory.trim() === "") {
4967
5195
  continue;
4968
5196
  }
4969
- const candidate = join3(directory, command);
5197
+ const candidate = join4(directory, command);
4970
5198
  if (!existsSync2(candidate)) {
4971
5199
  continue;
4972
5200
  }
4973
5201
  const realpath = realpathSync3(candidate);
4974
- return { ok: true, command: realpath, directory: dirname(realpath) };
5202
+ return { ok: true, command: realpath, directory: dirname2(realpath) };
4975
5203
  }
4976
5204
  return { ok: false };
4977
5205
  }
@@ -4980,10 +5208,10 @@ function hasPathSeparator(path) {
4980
5208
  }
4981
5209
  function safeRealpathDir(path) {
4982
5210
  try {
4983
- const directory = dirname(realpathSync3(path));
5211
+ const directory = dirname2(realpathSync3(path));
4984
5212
  return directory === "/" ? undefined : directory;
4985
5213
  } catch (_error) {
4986
- const directory = dirname(path);
5214
+ const directory = dirname2(path);
4987
5215
  return directory === "." || directory === "/" ? undefined : directory;
4988
5216
  }
4989
5217
  }
@@ -5033,7 +5261,7 @@ function codeServerSettings(collaboration) {
5033
5261
  "workbench.welcomePage.walkthroughs.openOnInstall": false
5034
5262
  };
5035
5263
  }
5036
- async function startCollaborationSidecar(collaboration, profile, editorRuntime) {
5264
+ async function startCollaborationSidecar(collaboration, profile, editorRuntime, runnerId) {
5037
5265
  if (collaboration === undefined) {
5038
5266
  return { ok: true, ready: Promise.resolve("ready") };
5039
5267
  }
@@ -5042,13 +5270,22 @@ async function startCollaborationSidecar(collaboration, profile, editorRuntime)
5042
5270
  installLocalTarball(editorRuntime.assets.collaborationExtensionTarball, profile.extensionsDir),
5043
5271
  installLocalTarball(editorRuntime.assets.collaborationServerTarball, profile.collaborationServerDir)
5044
5272
  ]);
5045
- const child = spawn2(nodeRuntimeExecutable(), [
5046
- join3(profile.collaborationServerDir, "open-collaboration-server", "bundle", "app.js"),
5273
+ const command = nodeRuntimeExecutable();
5274
+ const args = [
5275
+ join4(profile.collaborationServerDir, "open-collaboration-server", "bundle", "app.js"),
5047
5276
  "--hostname",
5048
5277
  "127.0.0.1",
5049
5278
  "--port",
5050
5279
  String(collaboration.serverPort)
5051
- ], {
5280
+ ];
5281
+ writeCliAuditEvent("process.spawn", {
5282
+ command,
5283
+ args,
5284
+ purpose: "local_editor.collaboration_sidecar",
5285
+ editorSessionId: collaboration.editorSessionId,
5286
+ runtimeSessionId: collaboration.runtimeSessionId
5287
+ }, { sessionId: runnerId });
5288
+ const child = spawn2(command, args, {
5052
5289
  env: {
5053
5290
  ...process.env,
5054
5291
  OCT_ACTIVATE_SIMPLE_LOGIN: "true",
@@ -5056,6 +5293,26 @@ async function startCollaborationSidecar(collaboration, profile, editorRuntime)
5056
5293
  },
5057
5294
  stdio: ["ignore", "inherit", "inherit"]
5058
5295
  });
5296
+ writeCliAuditEvent("process.spawned", {
5297
+ command,
5298
+ args,
5299
+ pid: child.pid,
5300
+ purpose: "local_editor.collaboration_sidecar",
5301
+ editorSessionId: collaboration.editorSessionId,
5302
+ runtimeSessionId: collaboration.runtimeSessionId
5303
+ }, { sessionId: runnerId });
5304
+ child.once("exit", (code, signal) => {
5305
+ writeCliAuditEvent("process.exit", {
5306
+ command,
5307
+ args,
5308
+ pid: child.pid,
5309
+ code,
5310
+ signal,
5311
+ purpose: "local_editor.collaboration_sidecar",
5312
+ editorSessionId: collaboration.editorSessionId,
5313
+ runtimeSessionId: collaboration.runtimeSessionId
5314
+ }, { sessionId: runnerId });
5315
+ });
5059
5316
  const exited = waitForCodeServerExit(child);
5060
5317
  const spawnResult = await waitForCodeServerSpawn(child);
5061
5318
  if (spawnResult === "failed") {
@@ -5089,21 +5346,22 @@ function nodeRuntimeExecutable(env = process.env, execPath = process.execPath) {
5089
5346
  return basename3(execPath).toLowerCase().includes("bun") ? "node" : execPath;
5090
5347
  }
5091
5348
  async function installLocalTarball(archivePath, destinationDir) {
5092
- mkdirSync2(destinationDir, { recursive: true });
5349
+ mkdirSync3(destinationDir, { recursive: true });
5093
5350
  await runProcess("tar", ["-xzf", archivePath, "-C", destinationDir]);
5094
5351
  }
5095
5352
  function installDirectory(sourceDir, destinationDir) {
5096
- mkdirSync2(dirname(destinationDir), { recursive: true });
5353
+ mkdirSync3(dirname2(destinationDir), { recursive: true });
5097
5354
  cpSync(sourceDir, destinationDir, { recursive: true });
5098
5355
  }
5099
- function codeServerEnv(env, userDataDir, collaboration) {
5356
+ function codeServerEnv(env, cwd, userDataDir, collaboration) {
5100
5357
  const { PORT: _port, ...hostEnv } = env;
5101
5358
  const base = {
5102
5359
  ...hostEnv,
5103
- HOME: userDataDir,
5104
- XDG_CACHE_HOME: join3(userDataDir, "xdg-cache"),
5105
- XDG_CONFIG_HOME: join3(userDataDir, "xdg-config"),
5106
- XDG_DATA_HOME: join3(userDataDir, "xdg-data")
5360
+ HOME: cwd,
5361
+ PWD: cwd,
5362
+ XDG_CACHE_HOME: join4(userDataDir, "xdg-cache"),
5363
+ XDG_CONFIG_HOME: join4(userDataDir, "xdg-config"),
5364
+ XDG_DATA_HOME: join4(userDataDir, "xdg-data")
5107
5365
  };
5108
5366
  if (collaboration === undefined) {
5109
5367
  return base;
@@ -5137,11 +5395,30 @@ function sameCollaboration(running, requested) {
5137
5395
  }
5138
5396
  function runProcess(command, args) {
5139
5397
  return new Promise((resolve4, reject) => {
5398
+ writeCliAuditEvent("process.spawn", {
5399
+ command,
5400
+ args,
5401
+ purpose: "local_editor.install_process"
5402
+ });
5140
5403
  const child = spawn2(command, [...args], {
5141
5404
  stdio: ["ignore", "ignore", "inherit"]
5142
5405
  });
5406
+ writeCliAuditEvent("process.spawned", {
5407
+ command,
5408
+ args,
5409
+ pid: child.pid,
5410
+ purpose: "local_editor.install_process"
5411
+ });
5143
5412
  child.once("error", reject);
5144
- child.once("exit", (code) => {
5413
+ child.once("exit", (code, signal) => {
5414
+ writeCliAuditEvent("process.exit", {
5415
+ command,
5416
+ args,
5417
+ pid: child.pid,
5418
+ code,
5419
+ signal,
5420
+ purpose: "local_editor.install_process"
5421
+ });
5145
5422
  if (code === 0) {
5146
5423
  resolve4();
5147
5424
  } else {
@@ -5293,17 +5570,17 @@ import { spawn as spawn4 } from "node:child_process";
5293
5570
  import { createHash as createHash2 } from "node:crypto";
5294
5571
  import {
5295
5572
  createReadStream,
5296
- createWriteStream,
5573
+ createWriteStream as createWriteStream2,
5297
5574
  existsSync as existsSync3,
5298
- mkdirSync as mkdirSync3,
5575
+ mkdirSync as mkdirSync4,
5299
5576
  mkdtempSync as mkdtempSync2,
5300
5577
  readFileSync as readFileSync2,
5301
5578
  renameSync,
5302
5579
  rmSync,
5303
5580
  writeFileSync as writeFileSync3
5304
5581
  } from "node:fs";
5305
- import { homedir as homedir3 } from "node:os";
5306
- import { dirname as dirname2, join as join4, resolve as resolve4 } from "node:path";
5582
+ import { homedir as homedir4 } from "node:os";
5583
+ import { dirname as dirname3, join as join5, resolve as resolve4 } from "node:path";
5307
5584
  import { Readable } from "node:stream";
5308
5585
  import { pipeline } from "node:stream/promises";
5309
5586
 
@@ -5490,8 +5767,8 @@ function startCallbackServer(args) {
5490
5767
  if (error !== null && error.trim() !== "") {
5491
5768
  rejectCallback?.(new Error(`local runner OAuth failed: ${error}`));
5492
5769
  writeOauthResult(response, {
5493
- title: "Linzumi CLI was not authorized",
5494
- body: "You denied the request. You can close this tab and rerun linzumi start when you are ready.",
5770
+ title: "Not authorized",
5771
+ body: "You denied the request. Close this tab and rerun the bootstrap command when you're ready.",
5495
5772
  status: 403
5496
5773
  });
5497
5774
  return;
@@ -5499,22 +5776,22 @@ function startCallbackServer(args) {
5499
5776
  if (code === null || state === null || code.trim() === "" || state.trim() === "") {
5500
5777
  writeOauthResult(response, {
5501
5778
  title: "Authorization callback was incomplete",
5502
- body: "Kandan did not send the local runner authorization code. Return to your terminal and try again.",
5779
+ body: "We didn't receive the authorization code. Return to your terminal and try again.",
5503
5780
  status: 400
5504
5781
  });
5505
5782
  return;
5506
5783
  }
5507
5784
  resolveCallback?.({ code, state });
5508
5785
  writeOauthResult(response, {
5509
- title: "Linzumi CLI is connected",
5510
- body: "You can close this tab and return to the terminal. Kandan will finish starting the local runner.",
5786
+ title: "Connected",
5787
+ body: "You can close this tab. Your terminal will finish the setup.",
5511
5788
  status: 200
5512
5789
  });
5513
5790
  } catch (error) {
5514
5791
  rejectCallback?.(error);
5515
5792
  writeOauthResult(response, {
5516
5793
  title: "Authorization callback failed",
5517
- body: "Return to your terminal and rerun linzumi start.",
5794
+ body: "Return to your terminal and rerun the bootstrap command.",
5518
5795
  status: 500
5519
5796
  });
5520
5797
  }
@@ -5578,7 +5855,18 @@ function openBrowser(url) {
5578
5855
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
5579
5856
  const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
5580
5857
  return new Promise((resolve4) => {
5858
+ writeCliAuditEvent("process.spawn", {
5859
+ command,
5860
+ args,
5861
+ purpose: "oauth.open_browser"
5862
+ });
5581
5863
  const child = spawn3(command, args, { stdio: "ignore", detached: true });
5864
+ writeCliAuditEvent("process.spawned", {
5865
+ command,
5866
+ args,
5867
+ pid: child.pid,
5868
+ purpose: "oauth.open_browser"
5869
+ });
5582
5870
  child.on("error", () => resolve4());
5583
5871
  child.on("spawn", () => {
5584
5872
  child.unref();
@@ -5765,8 +6053,8 @@ function normalizeRuntimeAssets(value) {
5765
6053
  }
5766
6054
  function installedRuntime(cacheRoot, manifest) {
5767
6055
  const runtimeRoot = runtimeInstallRoot(cacheRoot, manifest);
5768
- const manifestPath = join4(runtimeRoot, manifest.manifestPath);
5769
- const codeServerBin = join4(runtimeRoot, manifest.codeServerBinPath);
6056
+ const manifestPath = join5(runtimeRoot, manifest.manifestPath);
6057
+ const codeServerBin = join5(runtimeRoot, manifest.codeServerBinPath);
5770
6058
  const assets = verifiedRuntimeAssetPaths(runtimeRoot, manifest);
5771
6059
  if (!existsSync3(manifestPath) || !existsSync3(codeServerBin) || assets === undefined) {
5772
6060
  return { ok: false };
@@ -5790,10 +6078,10 @@ function installedRuntime(cacheRoot, manifest) {
5790
6078
  return { ok: false };
5791
6079
  }
5792
6080
  async function installRuntime(args) {
5793
- mkdirSync3(args.cacheRoot, { recursive: true });
5794
- const tempRoot = mkdtempSync2(join4(args.cacheRoot, ".install-"));
5795
- const archivePath = join4(tempRoot, "runtime.tar.gz");
5796
- const extractRoot = join4(tempRoot, "runtime");
6081
+ mkdirSync4(args.cacheRoot, { recursive: true });
6082
+ const tempRoot = mkdtempSync2(join5(args.cacheRoot, ".install-"));
6083
+ const archivePath = join5(tempRoot, "runtime.tar.gz");
6084
+ const extractRoot = join5(tempRoot, "runtime");
5797
6085
  try {
5798
6086
  const downloaded = await downloadArchive({
5799
6087
  kandanUrl: args.kandanUrl,
@@ -5805,7 +6093,7 @@ async function installRuntime(args) {
5805
6093
  if (!downloaded.ok) {
5806
6094
  return downloaded;
5807
6095
  }
5808
- mkdirSync3(extractRoot, { recursive: true });
6096
+ mkdirSync4(extractRoot, { recursive: true });
5809
6097
  if (!await args.extractArchive(archivePath, extractRoot)) {
5810
6098
  return { ok: false, reason: "archive_extract_failed" };
5811
6099
  }
@@ -5818,13 +6106,13 @@ async function installRuntime(args) {
5818
6106
  if (!assetsInstalled) {
5819
6107
  return { ok: false, reason: "install_failed" };
5820
6108
  }
5821
- const manifestPath = join4(extractRoot, args.manifest.manifestPath);
5822
- const codeServerBin = join4(extractRoot, args.manifest.codeServerBinPath);
6109
+ const manifestPath = join5(extractRoot, args.manifest.manifestPath);
6110
+ const codeServerBin = join5(extractRoot, args.manifest.codeServerBinPath);
5823
6111
  const assets = verifiedRuntimeAssetPaths(extractRoot, args.manifest);
5824
6112
  if (!existsSync3(codeServerBin) || assets === undefined) {
5825
6113
  return { ok: false, reason: "invalid_archive" };
5826
6114
  }
5827
- mkdirSync3(dirname2(manifestPath), { recursive: true });
6115
+ mkdirSync4(dirname3(manifestPath), { recursive: true });
5828
6116
  writeFileSync3(manifestPath, JSON.stringify({
5829
6117
  version: args.manifest.version,
5830
6118
  platform: args.manifest.platform,
@@ -5836,18 +6124,18 @@ async function installRuntime(args) {
5836
6124
  }, null, 2));
5837
6125
  const targetRoot = runtimeInstallRoot(args.cacheRoot, args.manifest);
5838
6126
  rmSync(targetRoot, { recursive: true, force: true });
5839
- mkdirSync3(dirname2(targetRoot), { recursive: true });
6127
+ mkdirSync4(dirname3(targetRoot), { recursive: true });
5840
6128
  renameSync(extractRoot, targetRoot);
5841
6129
  return {
5842
6130
  ok: true,
5843
6131
  runtime: {
5844
6132
  mode: "server_managed",
5845
6133
  root: targetRoot,
5846
- codeServerBin: join4(targetRoot, args.manifest.codeServerBinPath),
6134
+ codeServerBin: join5(targetRoot, args.manifest.codeServerBinPath),
5847
6135
  assets: {
5848
- collaborationExtensionTarball: join4(targetRoot, "kandan", "editor_extensions", "typefox.open-collaboration-tools.tar.gz"),
5849
- collaborationServerTarball: join4(targetRoot, "kandan", "editor_servers", "open-collaboration-server.tar.gz"),
5850
- documentStateExtensionDir: join4(targetRoot, "kandan", "editor_extensions", "kandan.document-state-telemetry")
6136
+ collaborationExtensionTarball: join5(targetRoot, "kandan", "editor_extensions", "typefox.open-collaboration-tools.tar.gz"),
6137
+ collaborationServerTarball: join5(targetRoot, "kandan", "editor_servers", "open-collaboration-server.tar.gz"),
6138
+ documentStateExtensionDir: join5(targetRoot, "kandan", "editor_extensions", "kandan.document-state-telemetry")
5851
6139
  }
5852
6140
  }
5853
6141
  };
@@ -5859,7 +6147,7 @@ async function installRuntime(args) {
5859
6147
  }
5860
6148
  async function materializeRuntimeAssets(args) {
5861
6149
  for (const asset of args.manifest.assets) {
5862
- const targetPath = join4(args.runtimeRoot, asset.path);
6150
+ const targetPath = join5(args.runtimeRoot, asset.path);
5863
6151
  try {
5864
6152
  const bytes = await runtimeAssetBytes({
5865
6153
  kandanUrl: args.kandanUrl,
@@ -5869,7 +6157,7 @@ async function materializeRuntimeAssets(args) {
5869
6157
  if (bytes === undefined) {
5870
6158
  continue;
5871
6159
  }
5872
- mkdirSync3(dirname2(targetPath), { recursive: true });
6160
+ mkdirSync4(dirname3(targetPath), { recursive: true });
5873
6161
  writeFileSync3(targetPath, bytes);
5874
6162
  } catch (_error) {
5875
6163
  return false;
@@ -5900,7 +6188,7 @@ async function downloadArchive(args) {
5900
6188
  if (response.status !== 200 || response.body === null) {
5901
6189
  return { ok: false, reason: "download_failed" };
5902
6190
  }
5903
- await pipeline(Readable.fromWeb(response.body), createWriteStream(args.archivePath));
6191
+ await pipeline(Readable.fromWeb(response.body), createWriteStream2(args.archivePath));
5904
6192
  const sha256 = await fileSha256(args.archivePath);
5905
6193
  if (sha256 !== args.manifest.archiveSha256) {
5906
6194
  return { ok: false, reason: "checksum_mismatch" };
@@ -5912,11 +6200,34 @@ function sameOrigin(left, right) {
5912
6200
  }
5913
6201
  function extractTarGz(archivePath, destination) {
5914
6202
  return new Promise((resolveExtract) => {
5915
- const child = spawn4("tar", ["-xzf", archivePath, "-C", destination], {
6203
+ const command = "tar";
6204
+ const args = ["-xzf", archivePath, "-C", destination];
6205
+ writeCliAuditEvent("process.spawn", {
6206
+ command,
6207
+ args,
6208
+ purpose: "editor_runtime.extract"
6209
+ });
6210
+ const child = spawn4(command, args, {
5916
6211
  stdio: ["ignore", "ignore", "ignore"]
5917
6212
  });
6213
+ writeCliAuditEvent("process.spawned", {
6214
+ command,
6215
+ args,
6216
+ pid: child.pid,
6217
+ purpose: "editor_runtime.extract"
6218
+ });
5918
6219
  child.on("error", () => resolveExtract(false));
5919
- child.on("exit", (code) => resolveExtract(code === 0));
6220
+ child.on("exit", (code, signal) => {
6221
+ writeCliAuditEvent("process.exit", {
6222
+ command,
6223
+ args,
6224
+ pid: child.pid,
6225
+ code,
6226
+ signal,
6227
+ purpose: "editor_runtime.extract"
6228
+ });
6229
+ resolveExtract(code === 0);
6230
+ });
5920
6231
  });
5921
6232
  }
5922
6233
  function fileSha256(path) {
@@ -5932,14 +6243,14 @@ function runtimeInstallRoot(cacheRoot, manifest) {
5932
6243
  return resolve4(cacheRoot, manifest.platform, manifest.archiveSha256);
5933
6244
  }
5934
6245
  function verifiedRuntimeAssetPaths(runtimeRoot, manifest) {
5935
- const collaborationExtensionTarball = join4(runtimeRoot, "kandan", "editor_extensions", "typefox.open-collaboration-tools.tar.gz");
5936
- const collaborationServerTarball = join4(runtimeRoot, "kandan", "editor_servers", "open-collaboration-server.tar.gz");
5937
- const documentStateExtensionDir = join4(runtimeRoot, "kandan", "editor_extensions", "kandan.document-state-telemetry");
6246
+ const collaborationExtensionTarball = join5(runtimeRoot, "kandan", "editor_extensions", "typefox.open-collaboration-tools.tar.gz");
6247
+ const collaborationServerTarball = join5(runtimeRoot, "kandan", "editor_servers", "open-collaboration-server.tar.gz");
6248
+ const documentStateExtensionDir = join5(runtimeRoot, "kandan", "editor_extensions", "kandan.document-state-telemetry");
5938
6249
  const codeServerRoot = codeServerRuntimeRoot(manifest.codeServerBinPath);
5939
6250
  const requiredPaths = [
5940
6251
  manifest.codeServerBinPath,
5941
- join4(codeServerRoot, "lib", "vscode", "node_modules", "vsda", "rust", "web", "vsda.js"),
5942
- join4(codeServerRoot, "lib", "vscode", "node_modules", "vsda", "rust", "web", "vsda_bg.wasm"),
6252
+ join5(codeServerRoot, "lib", "vscode", "node_modules", "vsda", "rust", "web", "vsda.js"),
6253
+ join5(codeServerRoot, "lib", "vscode", "node_modules", "vsda", "rust", "web", "vsda_bg.wasm"),
5943
6254
  "kandan/editor_extensions/typefox.open-collaboration-tools.tar.gz",
5944
6255
  "kandan/editor_servers/open-collaboration-server.tar.gz",
5945
6256
  "kandan/editor_extensions/kandan.document-state-telemetry/package.json",
@@ -5951,7 +6262,7 @@ function verifiedRuntimeAssetPaths(runtimeRoot, manifest) {
5951
6262
  if (expectedSha256 === undefined && relativePath !== manifest.codeServerBinPath) {
5952
6263
  return;
5953
6264
  }
5954
- const absolutePath = join4(runtimeRoot, relativePath);
6265
+ const absolutePath = join5(runtimeRoot, relativePath);
5955
6266
  if (!existsSync3(absolutePath)) {
5956
6267
  return;
5957
6268
  }
@@ -5970,7 +6281,7 @@ function verifiedRuntimeAssetPaths(runtimeRoot, manifest) {
5970
6281
  }
5971
6282
  function codeServerRuntimeRoot(codeServerBinPath) {
5972
6283
  const normalized = codeServerBinPath.replaceAll("\\", "/");
5973
- return normalized === "bin/code-server" ? "." : normalized.endsWith("/bin/code-server") ? normalized.slice(0, -"/bin/code-server".length) || "." : dirname2(normalized);
6284
+ return normalized === "bin/code-server" ? "." : normalized.endsWith("/bin/code-server") ? normalized.slice(0, -"/bin/code-server".length) || "." : dirname3(normalized);
5974
6285
  }
5975
6286
  function manifestAssetChecksums(assets) {
5976
6287
  const checksums = new Map;
@@ -5983,7 +6294,7 @@ function fileSha256Sync(path) {
5983
6294
  return createHash2("sha256").update(readFileSync2(path)).digest("hex");
5984
6295
  }
5985
6296
  function defaultEditorRuntimeCacheRoot() {
5986
- return join4(homedir3(), ".linzumi", "editor-runtimes");
6297
+ return join5(homedir4(), ".linzumi", "editor-runtimes");
5987
6298
  }
5988
6299
  function nonEmptyString(value) {
5989
6300
  return typeof value === "string" && value.trim() !== "" ? value.trim() : undefined;
@@ -5996,10 +6307,24 @@ function sha256String(value) {
5996
6307
  // src/dependencyStatus.ts
5997
6308
  function probeTool(command, cwd) {
5998
6309
  return new Promise((resolve5) => {
5999
- const child = spawn5(command, ["--version"], {
6310
+ const args = ["--version"];
6311
+ writeCliAuditEvent("process.spawn", {
6312
+ command,
6313
+ args,
6314
+ cwd,
6315
+ purpose: "dependency_probe"
6316
+ });
6317
+ const child = spawn5(command, args, {
6000
6318
  cwd,
6001
6319
  stdio: ["ignore", "pipe", "pipe"]
6002
6320
  });
6321
+ writeCliAuditEvent("process.spawned", {
6322
+ command,
6323
+ args,
6324
+ cwd,
6325
+ pid: child.pid,
6326
+ purpose: "dependency_probe"
6327
+ });
6003
6328
  let stdout = "";
6004
6329
  let stderr = "";
6005
6330
  let resolved = false;
@@ -6013,6 +6338,15 @@ function probeTool(command, cwd) {
6013
6338
  };
6014
6339
  const timeout = setTimeout(() => {
6015
6340
  child.kill("SIGKILL");
6341
+ writeCliAuditEvent("process.exit", {
6342
+ command,
6343
+ args,
6344
+ cwd,
6345
+ pid: child.pid,
6346
+ code: null,
6347
+ signal: "SIGKILL",
6348
+ purpose: "dependency_probe"
6349
+ });
6016
6350
  finish({ command, available: false });
6017
6351
  }, 1000);
6018
6352
  child.stdout?.on("data", (chunk) => {
@@ -6022,9 +6356,28 @@ function probeTool(command, cwd) {
6022
6356
  stderr += chunk.toString();
6023
6357
  });
6024
6358
  child.on("error", () => {
6359
+ writeCliAuditEvent("process.exit", {
6360
+ command,
6361
+ args,
6362
+ cwd,
6363
+ pid: child.pid,
6364
+ code: null,
6365
+ signal: null,
6366
+ error: "spawn_failed",
6367
+ purpose: "dependency_probe"
6368
+ });
6025
6369
  finish({ command, available: false });
6026
6370
  });
6027
- child.on("exit", (code) => {
6371
+ child.on("exit", (code, signal) => {
6372
+ writeCliAuditEvent("process.exit", {
6373
+ command,
6374
+ args,
6375
+ cwd,
6376
+ pid: child.pid,
6377
+ code,
6378
+ signal,
6379
+ purpose: "dependency_probe"
6380
+ });
6028
6381
  if (code !== 0) {
6029
6382
  finish({ command, available: false });
6030
6383
  return;
@@ -6073,7 +6426,7 @@ function codeServerDependencyStatus(args) {
6073
6426
  }
6074
6427
  function assertStartDependencies(status) {
6075
6428
  if (!status.bun.available) {
6076
- throw new Error("Node.js is not available. Install Node.js 20+, then rerun linzumi start.");
6429
+ throw new Error("Node.js is not available. Install Node.js 20+, then rerun the bootstrap command.");
6077
6430
  }
6078
6431
  if (!status.codex.available) {
6079
6432
  throw new Error(`Codex is not available at ${status.codex.command}. Install Codex or pass --codex-bin <path>.`);
@@ -6337,35 +6690,6 @@ function waitForOpen2(websocket) {
6337
6690
  });
6338
6691
  }
6339
6692
 
6340
- // src/runnerLogger.ts
6341
- import { openSync } from "node:fs";
6342
- import { createWriteStream as createWriteStream2 } from "node:fs";
6343
- import { dirname as dirname3 } from "node:path";
6344
- import { mkdirSync as mkdirSync4 } from "node:fs";
6345
- function createRunnerLogger(logFile, consoleReporter) {
6346
- mkdirSync4(dirname3(logFile), { recursive: true });
6347
- const fd = openSync(logFile, "a");
6348
- const stream = createWriteStream2("", { fd, flags: "a", autoClose: true });
6349
- const logger = (event, payload) => {
6350
- stream.write(`${JSON.stringify({ ts: new Date().toISOString(), event, ...payload })}
6351
- `, "utf8");
6352
- consoleReporter?.(event, payload);
6353
- };
6354
- Object.defineProperty(logger, "close", {
6355
- value: () => closeStream(stream)
6356
- });
6357
- return logger;
6358
- }
6359
- function closeStream(stream) {
6360
- if (stream.closed || stream.destroyed) {
6361
- return Promise.resolve();
6362
- }
6363
- return new Promise((resolve5, reject) => {
6364
- stream.once("error", reject);
6365
- stream.end(resolve5);
6366
- });
6367
- }
6368
-
6369
6693
  // src/runnerConsoleReporter.ts
6370
6694
  function reportRunnerConsoleEvent(event, payload) {
6371
6695
  const line = formatRunnerConsoleEvent(event, payload);
@@ -6487,6 +6811,26 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
6487
6811
  const allowedForwardPorts = options.allowedForwardPorts ?? [];
6488
6812
  const liveForwardPorts = new Set(allowedForwardPorts);
6489
6813
  const managedForwardPorts = new Set;
6814
+ const forwardPortAttributions = new Map;
6815
+ const setForwardPortAttribution = (port, attribution) => {
6816
+ forwardPortAttributions.set(port, {
6817
+ kandanThreadId: attribution.kandanThreadId ?? null,
6818
+ codexThreadId: attribution.codexThreadId ?? null,
6819
+ channelSlug: attribution.channelSlug ?? null
6820
+ });
6821
+ };
6822
+ const clearForwardPortAttribution = (port) => {
6823
+ forwardPortAttributions.delete(port);
6824
+ };
6825
+ const buildForwardPortAttributionPayload = () => Array.from(liveForwardPorts).sort((left, right) => left - right).map((port) => {
6826
+ const attribution = forwardPortAttributions.get(port);
6827
+ return {
6828
+ port,
6829
+ kandanThreadId: attribution?.kandanThreadId ?? null,
6830
+ codexThreadId: attribution?.codexThreadId ?? null,
6831
+ channelSlug: attribution?.channelSlug ?? null
6832
+ };
6833
+ });
6490
6834
  const allowedCwds = { value: [...options.allowedCwds] };
6491
6835
  const localEditorState = {
6492
6836
  value: { status: "disabled" }
@@ -6506,6 +6850,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
6506
6850
  allowedCwdSuggestions: allowedCwdSuggestions(options.cwd, allowedCwds.value),
6507
6851
  portForwarding: liveForwardPorts.size > 0,
6508
6852
  allowedPorts: Array.from(liveForwardPorts).sort((left, right) => left - right),
6853
+ forwardedPortAttributions: buildForwardPortAttributionPayload(),
6509
6854
  toolStatus: options.dependencyStatus === undefined ? null : dependencyStatusPayload(options.dependencyStatus),
6510
6855
  editorRuntime: options.dependencyStatus?.editorRuntime === undefined ? null : dependencyStatusPayload(options.dependencyStatus).editorRuntime,
6511
6856
  ...localEditorCapabilities(options.editorRuntime, allowedCwds.value, localEditorState.value)
@@ -6613,12 +6958,14 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
6613
6958
  enablePortForwardWatch: true,
6614
6959
  initialForwardPorts: allowedForwardPorts,
6615
6960
  suppressedForwardPorts: () => Array.from(managedForwardPorts),
6616
- onForwardPortApproved: (port) => {
6961
+ onForwardPortApproved: (port, attribution) => {
6617
6962
  liveForwardPorts.add(port);
6963
+ setForwardPortAttribution(port, attribution);
6618
6964
  return capabilitiesPayload();
6619
6965
  },
6620
6966
  onForwardPortRevoked: (port) => {
6621
6967
  liveForwardPorts.delete(port);
6968
+ clearForwardPortAttribution(port);
6622
6969
  return capabilitiesPayload();
6623
6970
  },
6624
6971
  channelSession: options.channelSession
@@ -6660,12 +7007,14 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
6660
7007
  enablePortForwardWatch: true,
6661
7008
  initialForwardPorts: allowedForwardPorts,
6662
7009
  suppressedForwardPorts: () => Array.from(managedForwardPorts),
6663
- onForwardPortApproved: (port) => {
7010
+ onForwardPortApproved: (port, attribution) => {
6664
7011
  liveForwardPorts.add(port);
7012
+ setForwardPortAttribution(port, attribution);
6665
7013
  return capabilitiesPayload();
6666
7014
  },
6667
7015
  onForwardPortRevoked: (port) => {
6668
7016
  liveForwardPorts.delete(port);
7017
+ clearForwardPortAttribution(port);
6669
7018
  return capabilitiesPayload();
6670
7019
  },
6671
7020
  channelSession: {
@@ -6761,7 +7110,17 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
6761
7110
  return;
6762
7111
  }
6763
7112
  if (isForwardHttpRequestControl(control)) {
6764
- handleForwardHttpRequest(control, Array.from(liveForwardPorts)).then((response) => kandan.push(topic, "forward:http_response", response)).catch((error) => kandan.push(topic, "forward:http_response", {
7113
+ handleForwardHttpRequest(control, Array.from(liveForwardPorts)).then((response) => {
7114
+ log("kandan.forward_response", {
7115
+ requestId: control.requestId,
7116
+ port: control.port,
7117
+ path: control.path,
7118
+ status: response.status,
7119
+ ok: response.ok,
7120
+ location: forwardedHeaderValue(response.headers, "location")
7121
+ });
7122
+ return kandan.push(topic, "forward:http_response", response);
7123
+ }).catch((error) => kandan.push(topic, "forward:http_response", {
6765
7124
  requestId: control.requestId,
6766
7125
  ok: false,
6767
7126
  error: error instanceof Error ? error.message : String(error)
@@ -6928,7 +7287,7 @@ function normalizedWorkDescription(value) {
6928
7287
  return normalized === undefined || normalized === "" ? undefined : normalized;
6929
7288
  }
6930
7289
  function makeRunnerLogger(options) {
6931
- return createRunnerLogger(options.logFile ?? join5(options.cwd, ".linzumi-runner.log"), options.launchTui ? undefined : reportRunnerConsoleEvent);
7290
+ return createRunnerLogger(options.logFile ?? join6(options.cwd, ".linzumi-runner.log"), options.launchTui ? undefined : reportRunnerConsoleEvent);
6932
7291
  }
6933
7292
  function installCleanupHandlers(close) {
6934
7293
  const closeAndExit = () => {
@@ -6953,11 +7312,44 @@ function installCleanupHandlers(close) {
6953
7312
  };
6954
7313
  }
6955
7314
  function launchCodexTui(codexBin, codexUrl, cwd, codexThreadId, session, fast) {
6956
- return spawn6(codexBin, codexTuiArgs(codexUrl, codexThreadId, session, fast), {
7315
+ const args = codexTuiArgs(codexUrl, codexThreadId, session, fast);
7316
+ writeCliAuditEvent("process.spawn", {
7317
+ command: codexBin,
7318
+ args,
7319
+ cwd,
7320
+ purpose: "codex.tui"
7321
+ });
7322
+ const child = spawn6(codexBin, args, {
6957
7323
  cwd,
6958
7324
  env: process.env,
6959
7325
  stdio: "inherit"
6960
7326
  });
7327
+ writeCliAuditEvent("process.spawned", {
7328
+ command: codexBin,
7329
+ args,
7330
+ cwd,
7331
+ pid: child.pid,
7332
+ purpose: "codex.tui"
7333
+ });
7334
+ child.once("exit", (code, signal) => {
7335
+ writeCliAuditEvent("process.exit", {
7336
+ command: codexBin,
7337
+ args,
7338
+ cwd,
7339
+ pid: child.pid,
7340
+ code,
7341
+ signal,
7342
+ purpose: "codex.tui"
7343
+ });
7344
+ });
7345
+ return child;
7346
+ }
7347
+ function forwardedHeaderValue(headers, name) {
7348
+ if (!Array.isArray(headers)) {
7349
+ return;
7350
+ }
7351
+ const header = headers.find((value) => isJsonObject(value) && typeof value.name === "string" && value.name.toLowerCase() === name.toLowerCase() && typeof value.value === "string");
7352
+ return isJsonObject(header) && typeof header.value === "string" ? header.value : undefined;
6961
7353
  }
6962
7354
  function codexTuiArgs(codexUrl, codexThreadId, session, fast) {
6963
7355
  const overrides = codexTuiConfigArgs(session, fast);
@@ -7206,11 +7598,11 @@ function allowedCwdSuggestions(cwd, allowedCwds) {
7206
7598
 
7207
7599
  // src/authCache.ts
7208
7600
  import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
7209
- import { homedir as homedir4 } from "node:os";
7210
- import { dirname as dirname4, join as join6 } from "node:path";
7601
+ import { homedir as homedir5 } from "node:os";
7602
+ import { dirname as dirname4, join as join7 } from "node:path";
7211
7603
  function defaultAuthFilePath() {
7212
- const base = process.env.KANDAN_HOME ?? join6(homedir4(), ".kandan");
7213
- return join6(base, "auth.json");
7604
+ const base = process.env.KANDAN_HOME ?? join7(homedir5(), ".kandan");
7605
+ return join7(base, "auth.json");
7214
7606
  }
7215
7607
  function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePath()) {
7216
7608
  if (!existsSync4(authFilePath)) {
@@ -7345,11 +7737,11 @@ async function acquireAndCacheToken(args) {
7345
7737
 
7346
7738
  // src/localConfig.ts
7347
7739
  import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, realpathSync as realpathSync4, writeFileSync as writeFileSync5 } from "node:fs";
7348
- import { homedir as homedir5 } from "node:os";
7740
+ import { homedir as homedir6 } from "node:os";
7349
7741
  import { dirname as dirname5, resolve as resolve5 } from "node:path";
7350
7742
  function localConfigPath(env = process.env) {
7351
7743
  const override = env.LINZUMI_CONFIG_FILE;
7352
- return override !== undefined && override.trim() !== "" ? resolve5(expandUserPath(override)) : resolve5(homedir5(), ".linzumi", "config.json");
7744
+ return override !== undefined && override.trim() !== "" ? resolve5(expandUserPath(override)) : resolve5(homedir6(), ".linzumi", "config.json");
7353
7745
  }
7354
7746
  function readLocalConfig(path = localConfigPath()) {
7355
7747
  if (!existsSync5(path)) {
@@ -7410,6 +7802,10 @@ function realpathOrResolved(pathValue) {
7410
7802
  }
7411
7803
  }
7412
7804
 
7805
+ // src/defaultUrls.ts
7806
+ var defaultLinzumiHttpUrl = "https://serve.linzumi.com";
7807
+ var defaultLinzumiWebSocketUrl = "wss://serve.linzumi.com";
7808
+
7413
7809
  // src/kandanTls.ts
7414
7810
  import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
7415
7811
  import { Agent } from "undici";
@@ -7455,9 +7851,8 @@ function trustedWebSocketFactory(trust, WebSocketImpl = WsWebSocket) {
7455
7851
 
7456
7852
  // src/agentBootstrap.ts
7457
7853
  import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
7458
- import { dirname as dirname6, join as join7 } from "node:path";
7459
- import { homedir as homedir6 } from "node:os";
7460
- var defaultApiUrl = "https://serve.linzumi.com";
7854
+ import { dirname as dirname6, join as join8 } from "node:path";
7855
+ import { homedir as homedir7 } from "node:os";
7461
7856
  async function runAgentCliCommand(args, deps = {
7462
7857
  fetchImpl: fetch,
7463
7858
  stdout: process.stdout,
@@ -7475,6 +7870,12 @@ async function runAgentCliCommand(args, deps = {
7475
7870
  case "claim":
7476
7871
  await runClaim(command, deps);
7477
7872
  return;
7873
+ case "loginLinkIssue":
7874
+ await runLoginLinkIssue(command, deps);
7875
+ return;
7876
+ case "loginLinkClaim":
7877
+ await runLoginLinkClaim(command, deps);
7878
+ return;
7478
7879
  case "threadNew":
7479
7880
  await runThreadNew(command, deps);
7480
7881
  return;
@@ -7529,6 +7930,28 @@ function parseAgentCommand(args) {
7529
7930
  tokenFile: agentTokenFile(parsed.flags)
7530
7931
  };
7531
7932
  }
7933
+ case "login-link": {
7934
+ const [subcommand, ...subcommandArgs] = rest;
7935
+ const parsed = parseAgentArgs(subcommandArgs);
7936
+ switch (subcommand) {
7937
+ case "issue":
7938
+ return {
7939
+ kind: "loginLinkIssue",
7940
+ apiUrl: agentApiUrl(parsed.flags),
7941
+ email: requiredFlag(parsed.flags, "email"),
7942
+ workspaceId: requiredFlag(parsed.flags, "workspace")
7943
+ };
7944
+ case "claim":
7945
+ return {
7946
+ kind: "loginLinkClaim",
7947
+ apiUrl: agentApiUrl(parsed.flags),
7948
+ pendingId: requiredFlag(parsed.flags, "pending"),
7949
+ code: requiredFlag(parsed.flags, "code")
7950
+ };
7951
+ default:
7952
+ throw new Error("linzumi login-link supports: issue, claim");
7953
+ }
7954
+ }
7532
7955
  case "thread": {
7533
7956
  const [subcommand, ...subcommandArgs] = rest;
7534
7957
  if (subcommand !== "new") {
@@ -7791,6 +8214,7 @@ async function runClaim(command, deps) {
7791
8214
  `);
7792
8215
  deps.stdout.write(`login_url: ${tokenFile.loginUrl}
7793
8216
  `);
8217
+ writeHumanLoginUrlWarning(deps);
7794
8218
  deps.stdout.write(`channel_url: ${tokenFile.channelUrl}
7795
8219
  `);
7796
8220
  deps.stdout.write(`support_channel_id: ${tokenFile.supportChannelId}
@@ -7798,6 +8222,37 @@ async function runClaim(command, deps) {
7798
8222
  deps.stdout.write(`support_channel_url: ${tokenFile.supportChannelUrl}
7799
8223
  `);
7800
8224
  }
8225
+ async function runLoginLinkIssue(command, deps) {
8226
+ const response = await postJson(command.apiUrl, "/agent/login-link/issue", {
8227
+ email: command.email,
8228
+ workspace_id: command.workspaceId
8229
+ }, undefined, deps.fetchImpl);
8230
+ const pendingId = requiredString(response, "pending_id");
8231
+ const expiresInSeconds = numberValue2(response.expires_in_seconds) ?? 600;
8232
+ const codeFormatHint = stringValue(response.code_format_hint) ?? "XXXX-XXXX";
8233
+ deps.stdout.write(`pending_id: ${pendingId}
8234
+ `);
8235
+ deps.stdout.write(`Code emailed to ${command.email} (format ${codeFormatHint}, expires in ${Math.ceil(expiresInSeconds / 60)}m)
8236
+ `);
8237
+ }
8238
+ async function runLoginLinkClaim(command, deps) {
8239
+ const response = await postJson(command.apiUrl, "/agent/login-link/claim", {
8240
+ pending_id: command.pendingId,
8241
+ code: command.code
8242
+ }, undefined, deps.fetchImpl);
8243
+ deps.stdout.write(`workspace_id: ${requiredString(response, "workspace_id")}
8244
+ `);
8245
+ const workspaceName = stringValue(response.workspace_name);
8246
+ if (workspaceName !== undefined) {
8247
+ deps.stdout.write(`workspace_name: ${workspaceName}
8248
+ `);
8249
+ }
8250
+ deps.stdout.write(`login_url: ${requiredString(response, "login_url")}
8251
+ `);
8252
+ writeHumanLoginUrlWarning(deps);
8253
+ deps.stdout.write(`channel_url: ${requiredString(response, "channel_url")}
8254
+ `);
8255
+ }
7801
8256
  async function runThreadNew(command, deps) {
7802
8257
  const tokenFile = readTokenFile(command.tokenFile, deps.readTextFile);
7803
8258
  const response = await postJson(command.apiUrl, "/agent/threads", {
@@ -7988,13 +8443,13 @@ function numberValue2(value) {
7988
8443
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
7989
8444
  }
7990
8445
  function agentApiUrl(flags) {
7991
- return flags.get("api-url") ?? defaultApiUrl;
8446
+ return flags.get("api-url") ?? defaultLinzumiHttpUrl;
7992
8447
  }
7993
8448
  function agentTokenFile(flags) {
7994
8449
  return flags.get("agent-token-file") ?? defaultAgentTokenFilePath();
7995
8450
  }
7996
8451
  function defaultAgentTokenFilePath() {
7997
- return join7(homedir6(), ".linzumi", "agent-token.json");
8452
+ return join8(homedir7(), ".linzumi", "agent-token.json");
7998
8453
  }
7999
8454
  function normalizedApiUrl(apiUrl) {
8000
8455
  return apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`;
@@ -8045,12 +8500,20 @@ function writeTokenFile(path, tokenFile, writeTextFile2) {
8045
8500
  writeTextFile2(path, `${JSON.stringify(tokenFile, null, 2)}
8046
8501
  `);
8047
8502
  }
8503
+ function writeHumanLoginUrlWarning(deps) {
8504
+ deps.stdout.write(`This is a one-time human login link. Do not open it from automation.
8505
+ `);
8506
+ deps.stdout.write(`Give it directly to the human who will use the workspace.
8507
+ `);
8508
+ }
8048
8509
  function agentHelpText() {
8049
8510
  return `Linzumi agent bootstrap
8050
8511
 
8051
8512
  Usage:
8052
8513
  linzumi signup --email <email> --agent-name <name> [--workspace-name <name>] [--api-url <url>]
8053
8514
  linzumi claim --pending <pending_id> --code <XXXX-XXXX> [--api-url <url>]
8515
+ linzumi login-link issue --email <email> --workspace <workspace_id> [--api-url <url>]
8516
+ linzumi login-link claim --pending <pending_id> --code <XXXX-XXXX> [--api-url <url>]
8054
8517
  linzumi thread new <title> --message <message> [--api-url <url>]
8055
8518
  linzumi channel post <channel_id> <message> [--api-url <url>]
8056
8519
  linzumi post <thread_id> <message> [--kind progress|question]
@@ -8061,10 +8524,14 @@ Usage:
8061
8524
  linzumi editor open <thread_id> --runner <runner_id> --cwd <path>
8062
8525
 
8063
8526
  Options:
8064
- --api-url <url> Agent API origin, default ${defaultApiUrl}
8527
+ --api-url <url> Agent API origin, default ${defaultLinzumiHttpUrl}
8065
8528
  --agent-token-file <path> Token cache path, default ~/.linzumi/agent-token.json
8066
8529
  --workspace-name <name> Human-readable workspace name requested during signup
8067
8530
 
8531
+ Human login:
8532
+ Human browser login recovery; prints one-time links for the human only.
8533
+ Do not open login_url from Codex, CLI automation, or an unconfirmed browser profile.
8534
+
8068
8535
  Launch target:
8069
8536
  zero-to-hello-world-pr+editor in under 3 minutes.
8070
8537
  `;
@@ -8072,7 +8539,7 @@ Launch target:
8072
8539
 
8073
8540
  // src/helloLinzumiProject.ts
8074
8541
  import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
8075
- import { dirname as dirname7, join as join8, resolve as resolve6 } from "node:path";
8542
+ import { dirname as dirname7, join as join9, resolve as resolve6 } from "node:path";
8076
8543
  import { fileURLToPath } from "node:url";
8077
8544
  var defaultHelloLinzumiProjectDir = "/tmp/hello_linzumi";
8078
8545
  var defaultHelloLinzumiProjectName = "hello_linzumi";
@@ -8081,7 +8548,7 @@ var defaultHelloLinzumiPort = 8787;
8081
8548
  var defaultHelloLinzumiHost = "0.0.0.0";
8082
8549
  var markerFile = ".linzumi-demo-project";
8083
8550
  var moduleDir = dirname7(fileURLToPath(import.meta.url));
8084
- var linzumiLogoSvg = readFileSync7(join8(moduleDir, "assets", "linzumi-logo.svg"), "utf8");
8551
+ var linzumiLogoSvg = readFileSync7(join9(moduleDir, "assets", "linzumi-logo.svg"), "utf8");
8085
8552
  function createHelloLinzumiProject(input = {}) {
8086
8553
  const options = typeof input === "string" ? { rootPath: input } : input;
8087
8554
  const root = resolveHelloProjectRoot(options);
@@ -8089,9 +8556,9 @@ function createHelloLinzumiProject(input = {}) {
8089
8556
  const host = normalizeHost(options.host);
8090
8557
  assertTcpPort(port);
8091
8558
  assertWritableDemoRoot(root, options.reset === true);
8092
- mkdirSync8(join8(root, "src"), { recursive: true });
8559
+ mkdirSync8(join9(root, "src"), { recursive: true });
8093
8560
  for (const file of demoFiles({ root, port, host })) {
8094
- writeFileSync7(join8(root, file.path), file.content, "utf8");
8561
+ writeFileSync7(join9(root, file.path), file.content, "utf8");
8095
8562
  }
8096
8563
  return {
8097
8564
  root,
@@ -8135,7 +8602,7 @@ function assertWritableDemoRoot(root, reset) {
8135
8602
  if (!existsSync8(root)) {
8136
8603
  return;
8137
8604
  }
8138
- const markerPath = join8(root, markerFile);
8605
+ const markerPath = join9(root, markerFile);
8139
8606
  const isDemoRoot = existsSync8(markerPath) && readFileSync7(markerPath, "utf8").trim() === "hello-linzumi";
8140
8607
  if (isDemoRoot && reset) {
8141
8608
  rmSync2(root, { recursive: true, force: true });
@@ -8643,19 +9110,19 @@ import {
8643
9110
  watch,
8644
9111
  writeFileSync as writeFileSync8
8645
9112
  } from "node:fs";
8646
- import { homedir as homedir7 } from "node:os";
8647
- import { dirname as dirname8, join as join9, resolve as resolve7 } from "node:path";
9113
+ import { homedir as homedir8 } from "node:os";
9114
+ import { dirname as dirname8, join as join10, resolve as resolve7 } from "node:path";
8648
9115
  import { execFileSync, spawn as spawn7 } from "node:child_process";
8649
9116
  import { fileURLToPath as fileURLToPath2 } from "node:url";
8650
9117
  var connectedMarker = "Runner connected:";
8651
9118
  function commanderStatusDir() {
8652
- return join9(homedir7(), ".linzumi", "commanders");
9119
+ return join10(homedir8(), ".linzumi", "commanders");
8653
9120
  }
8654
9121
  function commanderStatusFile(runnerId, statusDir = commanderStatusDir()) {
8655
- return join9(statusDir, `${safeRunnerId(runnerId)}.json`);
9122
+ return join10(statusDir, `${safeRunnerId(runnerId)}.json`);
8656
9123
  }
8657
9124
  function defaultCommanderLogFile(runnerId, statusDir = commanderStatusDir()) {
8658
- return join9(statusDir, `${safeRunnerId(runnerId)}.log`);
9125
+ return join10(statusDir, `${safeRunnerId(runnerId)}.log`);
8659
9126
  }
8660
9127
  function startCommanderDaemon(options) {
8661
9128
  const statusDir = options.statusDir ?? commanderStatusDir();
@@ -8677,11 +9144,24 @@ function startCommanderDaemon(options) {
8677
9144
  mkdirSync9(dirname8(logFile), { recursive: true });
8678
9145
  const out = openSync2(logFile, "a");
8679
9146
  const err = openSync2(logFile, "a");
9147
+ writeCliAuditEvent("process.spawn", {
9148
+ command: nodeBin,
9149
+ args: command,
9150
+ cwd: options.cwd,
9151
+ purpose: "commander_daemon.start"
9152
+ }, { sessionId: options.runnerId });
8680
9153
  const child = (options.spawnImpl ?? spawn7)(nodeBin, command, {
8681
9154
  detached: true,
8682
9155
  stdio: ["ignore", out, err],
8683
9156
  env: process.env
8684
9157
  });
9158
+ writeCliAuditEvent("process.spawned", {
9159
+ command: nodeBin,
9160
+ args: command,
9161
+ cwd: options.cwd,
9162
+ pid: child.pid,
9163
+ purpose: "commander_daemon.start"
9164
+ }, { sessionId: options.runnerId });
8685
9165
  closeSync(out);
8686
9166
  closeSync(err);
8687
9167
  child.unref();
@@ -8805,10 +9285,22 @@ function processMatchesRecord(record, processIdentityReader) {
8805
9285
  }
8806
9286
  function readProcessIdentity(pid) {
8807
9287
  try {
8808
- const output = execFileSync("ps", ["-p", String(pid), "-o", "lstart=", "-o", "command="], {
9288
+ const command = "ps";
9289
+ const args = ["-p", String(pid), "-o", "lstart=", "-o", "command="];
9290
+ writeCliAuditEvent("process.exec_file", {
9291
+ command,
9292
+ args,
9293
+ purpose: "commander_daemon.process_identity"
9294
+ });
9295
+ const output = execFileSync(command, args, {
8809
9296
  encoding: "utf8",
8810
9297
  stdio: ["ignore", "pipe", "ignore"]
8811
9298
  }).trim();
9299
+ writeCliAuditEvent("process.exec_file_completed", {
9300
+ command,
9301
+ args,
9302
+ purpose: "commander_daemon.process_identity"
9303
+ });
8812
9304
  if (output === "") {
8813
9305
  return;
8814
9306
  }
@@ -8923,7 +9415,7 @@ async function main(args) {
8923
9415
  process.stdout.write(connectGuideText());
8924
9416
  return;
8925
9417
  case "version":
8926
- process.stdout.write(`linzumi 0.0.33-beta
9418
+ process.stdout.write(`linzumi 0.0.34-beta
8927
9419
  `);
8928
9420
  return;
8929
9421
  case "auth":
@@ -8994,6 +9486,7 @@ function parseCommand(args) {
8994
9486
  return { command: "commanderDaemon", args: ["stop", ...rest] };
8995
9487
  case "signup":
8996
9488
  case "claim":
9489
+ case "login-link":
8997
9490
  case "thread":
8998
9491
  case "channel":
8999
9492
  case "post":
@@ -9199,7 +9692,7 @@ async function parseStartRunnerArgs(args, deps = {
9199
9692
  process.exit(0);
9200
9693
  }
9201
9694
  rejectStartTargetingFlags(values);
9202
- const kandanUrl = stringValue3(values, "kandan-url") ?? "wss://serve.linzumi.com";
9695
+ const kandanUrl = stringValue3(values, "kandan-url") ?? defaultLinzumiWebSocketUrl;
9203
9696
  const requestedCwd = resolveUserPath(cwdArg ?? process.cwd());
9204
9697
  const cwd = assertConfiguredAllowedCwds([requestedCwd])[0] ?? requestedCwd;
9205
9698
  const explicitAllowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : [];
@@ -9436,7 +9929,7 @@ async function parseRunnerArgs(args, deps = {
9436
9929
  process.exit(0);
9437
9930
  }
9438
9931
  if (values.get("version") === true) {
9439
- process.stdout.write(`linzumi 0.0.33-beta
9932
+ process.stdout.write(`linzumi 0.0.34-beta
9440
9933
  `);
9441
9934
  process.exit(0);
9442
9935
  }
@@ -9581,10 +10074,10 @@ function rejectStartTargetingFlags(values) {
9581
10074
  }
9582
10075
  function resolveUserPath(pathValue) {
9583
10076
  if (pathValue === "~") {
9584
- return homedir8();
10077
+ return homedir9();
9585
10078
  }
9586
10079
  if (pathValue.startsWith("~/")) {
9587
- return resolve8(homedir8(), pathValue.slice(2));
10080
+ return resolve8(homedir9(), pathValue.slice(2));
9588
10081
  }
9589
10082
  return resolve8(pathValue);
9590
10083
  }
@@ -9679,6 +10172,8 @@ Usage:
9679
10172
  linzumi
9680
10173
  linzumi signup --email <email> --agent-name <name> [--workspace-name <name>]
9681
10174
  linzumi claim --pending <pending_id> --code <XXXX-XXXX>
10175
+ linzumi login-link issue --email <email> --workspace <workspace_id>
10176
+ linzumi login-link claim --pending <pending_id> --code <XXXX-XXXX>
9682
10177
  linzumi thread new <title> --message <message>
9683
10178
  linzumi post <thread_id> <message>
9684
10179
  linzumi inbox <thread_id> --since-last
@@ -9694,7 +10189,7 @@ Usage:
9694
10189
  linzumi auth --kandan-url <ws-url> [--workspace <slug> --channel <slug>]
9695
10190
 
9696
10191
  Required:
9697
- --kandan-url <ws-url> Linzumi backend URL, default wss://serve.linzumi.com
10192
+ --kandan-url <ws-url> Linzumi backend URL, default ${defaultLinzumiWebSocketUrl}
9698
10193
  --token <jwt> Optional override token. Otherwise ~/.linzumi/auth.json is validated or OAuth opens.
9699
10194
  --auth-file <path> Auth cache path, default ~/.linzumi/auth.json
9700
10195
  --oauth-callback-host <ip> Callback host reachable by your browser
@@ -9831,7 +10326,7 @@ What it does:
9831
10326
  previews, and the browser VS Code editor.
9832
10327
 
9833
10328
  Options:
9834
- --kandan-url <ws-url> Linzumi backend URL, default wss://serve.linzumi.com
10329
+ --kandan-url <ws-url> Linzumi backend URL, default ${defaultLinzumiWebSocketUrl}
9835
10330
  --token <jwt> Optional scoped local-runner token override
9836
10331
  --auth-file <path> Auth cache path, default ~/.linzumi/auth.json
9837
10332
  --oauth-callback-host <ip> Callback host reachable by your browser