@linzumi/cli 0.0.44-beta → 0.0.45-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 +1 -1
  2. package/dist/index.js +392 -93
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -63,7 +63,7 @@ Install the CLI or run it with `npx`:
63
63
 
64
64
  ```bash
65
65
  npm install -g @linzumi/cli@latest
66
- npx -y @linzumi/cli@0.0.44-beta --version
66
+ npx -y @linzumi/cli@0.0.45-beta --version
67
67
  linzumi --version
68
68
  ```
69
69
 
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import { randomUUID as randomUUID4 } from "node:crypto";
3
- import { existsSync as existsSync11, readFileSync as readFileSync10, realpathSync as realpathSync6 } from "node:fs";
3
+ import { existsSync as existsSync11, readFileSync as readFileSync11, realpathSync as realpathSync6 } from "node:fs";
4
4
  import { homedir as homedir9 } from "node:os";
5
5
  import { resolve as resolve9 } from "node:path";
6
6
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -6016,6 +6016,7 @@ function pathLooksAbsolute(pathValue) {
6016
6016
 
6017
6017
  // src/localForwarding.ts
6018
6018
  import { gzipSync } from "node:zlib";
6019
+ import NodeWebSocket from "ws";
6019
6020
  var maxForwardBodyBytes = 64 * 1024 * 1024;
6020
6021
  var gzipForwardThresholdBytes = 32 * 1024;
6021
6022
  async function handleForwardHttpRequest(control, allowedPorts) {
@@ -6059,15 +6060,15 @@ function isForwardHttpRequestControl(control) {
6059
6060
  function isForwardWebSocketControl(control) {
6060
6061
  return control.type === "forward_websocket_open" || control.type === "forward_websocket_send" || control.type === "forward_websocket_close";
6061
6062
  }
6062
- function createForwardWebSocketManager(kandan, topic, allowedPorts) {
6063
+ function createForwardWebSocketManager(kandan, topic, allowedPorts, socketFactory = defaultForwardWebSocketFactory) {
6063
6064
  const sockets = new Map;
6064
6065
  const pushEvent = (payload) => kandan.push(topic, "forward:websocket_event", payload).catch(() => {
6065
6066
  return;
6066
6067
  });
6067
6068
  const closeSocket = (socketId) => {
6068
- const socket = sockets.get(socketId);
6069
+ const stream = sockets.get(socketId);
6069
6070
  sockets.delete(socketId);
6070
- socket?.close();
6071
+ stream?.socket.close();
6071
6072
  };
6072
6073
  return {
6073
6074
  handle: (control) => {
@@ -6081,12 +6082,12 @@ function createForwardWebSocketManager(kandan, topic, allowedPorts) {
6081
6082
  });
6082
6083
  return;
6083
6084
  }
6084
- openLocalWebSocket(control, sockets, pushEvent, "ws");
6085
+ openLocalWebSocket(control, sockets, pushEvent, socketFactory, "ws");
6085
6086
  return;
6086
6087
  }
6087
6088
  case "forward_websocket_send": {
6088
- const socket = sockets.get(control.socketId);
6089
- if (socket === undefined || socket.readyState !== WebSocket.OPEN) {
6089
+ const stream = sockets.get(control.socketId);
6090
+ if (stream === undefined || stream.socket.readyState !== WebSocket.OPEN) {
6090
6091
  pushEvent({
6091
6092
  socketId: control.socketId,
6092
6093
  type: "error",
@@ -6097,12 +6098,12 @@ function createForwardWebSocketManager(kandan, topic, allowedPorts) {
6097
6098
  const body = Buffer.from(control.bodyBase64, "base64");
6098
6099
  switch (control.opcode) {
6099
6100
  case "text":
6100
- socket.send(body.toString());
6101
+ stream.socket.send(body.toString());
6101
6102
  return;
6102
6103
  case "binary":
6103
6104
  case "ping":
6104
6105
  case "pong":
6105
- socket.send(body);
6106
+ stream.socket.send(body);
6106
6107
  return;
6107
6108
  }
6108
6109
  }
@@ -6118,18 +6119,27 @@ function createForwardWebSocketManager(kandan, topic, allowedPorts) {
6118
6119
  }
6119
6120
  };
6120
6121
  }
6121
- function openLocalWebSocket(control, sockets, pushEvent, scheme) {
6122
+ function openLocalWebSocket(control, sockets, pushEvent, socketFactory, scheme) {
6122
6123
  let opened = false;
6123
- const url = localForwardUrl(scheme === "ws" ? "http" : "https", control.port, control.path, control.queryString).replace(/^http/, scheme);
6124
+ const url = localForwardWebSocketUrl(scheme, control.port, control.path, control.queryString);
6124
6125
  const protocols = webSocketProtocols(control.headers);
6125
- const websocket = protocols === undefined ? new WebSocket(url) : new WebSocket(url, protocols);
6126
+ const headers = webSocketHeaders(control.headers);
6127
+ const websocket = socketFactory(url, protocols, headers);
6126
6128
  websocket.binaryType = "arraybuffer";
6127
- sockets.set(control.socketId, websocket);
6129
+ const previousStream = sockets.get(control.socketId);
6130
+ previousStream?.socket.close();
6131
+ sockets.set(control.socketId, { socket: websocket });
6128
6132
  websocket.addEventListener("open", () => {
6133
+ if (!currentWebSocket(sockets, control.socketId, websocket)) {
6134
+ return;
6135
+ }
6129
6136
  opened = true;
6130
6137
  pushEvent({ socketId: control.socketId, type: "open" });
6131
6138
  });
6132
6139
  websocket.addEventListener("message", (event) => {
6140
+ if (!currentWebSocket(sockets, control.socketId, websocket)) {
6141
+ return;
6142
+ }
6133
6143
  const body = typeof event.data === "string" ? Buffer.from(event.data) : Buffer.from(event.data);
6134
6144
  pushEvent({
6135
6145
  socketId: control.socketId,
@@ -6139,6 +6149,9 @@ function openLocalWebSocket(control, sockets, pushEvent, scheme) {
6139
6149
  });
6140
6150
  });
6141
6151
  websocket.addEventListener("close", (event) => {
6152
+ if (!currentWebSocket(sockets, control.socketId, websocket)) {
6153
+ return;
6154
+ }
6142
6155
  sockets.delete(control.socketId);
6143
6156
  pushEvent({
6144
6157
  socketId: control.socketId,
@@ -6148,18 +6161,32 @@ function openLocalWebSocket(control, sockets, pushEvent, scheme) {
6148
6161
  });
6149
6162
  });
6150
6163
  websocket.addEventListener("error", () => {
6164
+ if (!currentWebSocket(sockets, control.socketId, websocket)) {
6165
+ return;
6166
+ }
6151
6167
  sockets.delete(control.socketId);
6152
6168
  if (!opened && scheme === "ws") {
6153
- openLocalWebSocket(control, sockets, pushEvent, "wss");
6169
+ openLocalWebSocket(control, sockets, pushEvent, socketFactory, "wss");
6154
6170
  return;
6155
6171
  }
6156
6172
  pushEvent({
6157
6173
  socketId: control.socketId,
6158
6174
  type: "error",
6159
- error: "websocket_error"
6175
+ error: "websocket_error",
6176
+ attemptedScheme: scheme
6160
6177
  });
6161
6178
  });
6162
6179
  }
6180
+ function defaultForwardWebSocketFactory(url, protocols, headers) {
6181
+ if (headers === undefined) {
6182
+ return protocols === undefined ? new WebSocket(url) : new WebSocket(url, protocols);
6183
+ }
6184
+ const options = { headers };
6185
+ return protocols === undefined ? new NodeWebSocket(url, options) : new NodeWebSocket(url, protocols, options);
6186
+ }
6187
+ function currentWebSocket(sockets, socketId, socket) {
6188
+ return sockets.get(socketId)?.socket === socket;
6189
+ }
6163
6190
  function webSocketProtocols(headers) {
6164
6191
  if (!Array.isArray(headers)) {
6165
6192
  return;
@@ -6172,6 +6199,21 @@ function webSocketProtocols(headers) {
6172
6199
  });
6173
6200
  return protocols.length === 0 ? undefined : protocols;
6174
6201
  }
6202
+ function webSocketHeaders(headers) {
6203
+ if (!Array.isArray(headers)) {
6204
+ return;
6205
+ }
6206
+ const forwarded = headers.reduce((acc, header) => {
6207
+ if (isOctWebSocketHeader(header)) {
6208
+ acc[header.name] = header.value;
6209
+ }
6210
+ return acc;
6211
+ }, {});
6212
+ return Object.keys(forwarded).length === 0 ? undefined : forwarded;
6213
+ }
6214
+ function isOctWebSocketHeader(value) {
6215
+ return isHeader(value) && value.name.toLowerCase().startsWith("x-oct-");
6216
+ }
6175
6217
  function localForwardUrl(scheme, port, path, queryString) {
6176
6218
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
6177
6219
  const url = new URL(`${scheme}://127.0.0.1:${port}${normalizedPath}`);
@@ -6180,6 +6222,12 @@ function localForwardUrl(scheme, port, path, queryString) {
6180
6222
  }
6181
6223
  return url.toString();
6182
6224
  }
6225
+ function localForwardWebSocketUrl(scheme, port, path, queryString) {
6226
+ const httpScheme = scheme === "ws" ? "http" : "https";
6227
+ const url = new URL(localForwardUrl(httpScheme, port, path, queryString));
6228
+ url.protocol = `${scheme}:`;
6229
+ return url.toString();
6230
+ }
6183
6231
  async function fetchWithHttpsFallback(port, path, queryString, request) {
6184
6232
  try {
6185
6233
  return await fetch(localForwardUrl("http", port, path, queryString), request);
@@ -6292,8 +6340,10 @@ var blockedForwardHeaderNames = new Set([
6292
6340
  "connection",
6293
6341
  "content-encoding",
6294
6342
  "content-length",
6343
+ "cookie",
6295
6344
  "host",
6296
6345
  "keep-alive",
6346
+ "authorization",
6297
6347
  "proxy-authenticate",
6298
6348
  "proxy-authorization",
6299
6349
  "te",
@@ -6305,10 +6355,12 @@ var blockedForwardHeaderNames = new Set([
6305
6355
  // src/localEditor.ts
6306
6356
  import { spawn as spawn2 } from "node:child_process";
6307
6357
  import {
6358
+ copyFileSync,
6308
6359
  cpSync,
6309
6360
  existsSync as existsSync2,
6310
6361
  mkdirSync as mkdirSync3,
6311
6362
  mkdtempSync,
6363
+ readFileSync as readFileSync2,
6312
6364
  realpathSync as realpathSync3,
6313
6365
  writeFileSync as writeFileSync2
6314
6366
  } from "node:fs";
@@ -6510,6 +6562,7 @@ function prepareCodeServerProfile(collaboration, editorRuntime) {
6510
6562
  mkdirSync3(collaborationServerDir, { recursive: true });
6511
6563
  mkdirSync3(tempDir, { recursive: true });
6512
6564
  if (editorRuntime !== undefined) {
6565
+ ensureCodeServerBrowserExtensionAssets(editorRuntime);
6513
6566
  installDirectory(editorRuntime.assets.documentStateExtensionDir, join4(extensionsDir, "kandan.document-state-telemetry"));
6514
6567
  }
6515
6568
  writeFileSync2(join4(userSettingsDir, "settings.json"), JSON.stringify(codeServerSettings(collaboration), null, 2));
@@ -6518,6 +6571,68 @@ function prepareCodeServerProfile(collaboration, editorRuntime) {
6518
6571
  return { ok: false, reason: "code_server_spawn_failed" };
6519
6572
  }
6520
6573
  }
6574
+ function ensureCodeServerBrowserExtensionAssets(runtime) {
6575
+ const vscodeRoot = codeServerVscodeRoot(runtime);
6576
+ const repairs = [
6577
+ {
6578
+ source: join4(vscodeRoot, "extensions", "git-base", "dist", "extension.js"),
6579
+ target: join4(vscodeRoot, "extensions", "git-base", "dist", "browser", "extension.js"),
6580
+ required: true
6581
+ },
6582
+ {
6583
+ source: join4(vscodeRoot, "extensions", "git-base", "dist", "extension.js.map"),
6584
+ target: join4(vscodeRoot, "extensions", "git-base", "dist", "browser", "extension.js.map"),
6585
+ required: false
6586
+ },
6587
+ {
6588
+ source: join4(vscodeRoot, "extensions", "merge-conflict", "dist", "mergeConflictMain.js"),
6589
+ target: join4(vscodeRoot, "extensions", "merge-conflict", "dist", "browser", "mergeConflictMain.js"),
6590
+ required: true
6591
+ },
6592
+ {
6593
+ source: join4(vscodeRoot, "extensions", "merge-conflict", "dist", "mergeConflictMain.js.map"),
6594
+ target: join4(vscodeRoot, "extensions", "merge-conflict", "dist", "browser", "mergeConflictMain.js.map"),
6595
+ required: false
6596
+ }
6597
+ ];
6598
+ repairs.forEach(({ source, target, required }) => {
6599
+ switch (true) {
6600
+ case existsSync2(target):
6601
+ return;
6602
+ case (!required && !existsSync2(source)):
6603
+ return;
6604
+ default:
6605
+ mkdirSync3(dirname2(target), { recursive: true });
6606
+ copyFileSync(source, target);
6607
+ return;
6608
+ }
6609
+ });
6610
+ }
6611
+ function codeServerVscodeRoot(runtime) {
6612
+ const roots = uniquePaths([
6613
+ join4(runtime.root, "lib", "vscode"),
6614
+ join4(dirname2(dirname2(runtime.codeServerBin)), "lib", "vscode"),
6615
+ ...wrappedCodeServerBin(runtime.codeServerBin).map((codeServerBin) => join4(dirname2(dirname2(codeServerBin)), "lib", "vscode"))
6616
+ ]);
6617
+ const rootWithRequiredAssets = roots.find((root) => codeServerBrowserAssetSources(root).every((source) => existsSync2(source)));
6618
+ return rootWithRequiredAssets ?? roots[0];
6619
+ }
6620
+ function wrappedCodeServerBin(codeServerBin) {
6621
+ try {
6622
+ const script = readFileSync2(codeServerBin, "utf8");
6623
+ const match = script.match(/exec\s+(?:"([^"]+)"|'([^']+)'|([^\s]+))\s+"\$@"/u);
6624
+ const target = match?.[1] ?? match?.[2] ?? match?.[3];
6625
+ return target === undefined || target.trim() === "" ? [] : [target];
6626
+ } catch (_error) {
6627
+ return [];
6628
+ }
6629
+ }
6630
+ function codeServerBrowserAssetSources(vscodeRoot) {
6631
+ return [
6632
+ join4(vscodeRoot, "extensions", "git-base", "dist", "extension.js"),
6633
+ join4(vscodeRoot, "extensions", "merge-conflict", "dist", "mergeConflictMain.js")
6634
+ ];
6635
+ }
6521
6636
  function prepareCodeServerLaunch(options) {
6522
6637
  const platform = options.platform ?? process.platform;
6523
6638
  if (platform === "linux") {
@@ -6650,12 +6765,16 @@ function prepareLocalEditorCollaboration(collaboration, runnerId, serverPort, br
6650
6765
  }
6651
6766
  const targetPath = `/local-codex-runners/${encodeURIComponent(runnerId)}/forwards/${serverPort}/preview-target`;
6652
6767
  const serverUrl = new URL(targetPath, `${browserBaseUrl}/`).toString();
6653
- const bootstrapServerUrl = collaboration.bootstrapToken === undefined || collaboration.bootstrapToken === "" ? serverUrl : new URL(`${targetPath}/_kandan-collaboration/${encodeURIComponent(collaboration.bootstrapToken)}`, `${browserBaseUrl}/`).toString();
6768
+ const collaborationStatePath = `/api/v2/editor/sessions/${collaboration.editorSessionId}/collaboration-state`;
6769
+ const collaborationBootstrapPath = collaboration.bootstrapToken === undefined || collaboration.bootstrapToken === "" ? undefined : `${targetPath}/_kandan-collaboration/${encodeURIComponent(collaboration.bootstrapToken)}`;
6770
+ const collaborationStateUrl = collaborationBootstrapPath === undefined ? collaborationStatePath : `${collaborationBootstrapPath}${collaborationStatePath}`;
6771
+ const bootstrapServerUrl = collaborationBootstrapPath === undefined ? serverUrl : new URL(collaborationBootstrapPath, `${browserBaseUrl}/`).toString();
6654
6772
  return {
6655
6773
  ...collaboration,
6656
6774
  serverPort,
6657
6775
  serverUrl,
6658
- bootstrapServerUrl
6776
+ bootstrapServerUrl,
6777
+ collaborationStateUrl
6659
6778
  };
6660
6779
  }
6661
6780
  function codeServerSettings(collaboration) {
@@ -6668,6 +6787,7 @@ function codeServerSettings(collaboration) {
6668
6787
  "oct.joinAcceptMode": "auto",
6669
6788
  ...collaboration === undefined ? {} : {
6670
6789
  "oct.kandanEditorSessionId": collaboration.editorSessionId,
6790
+ "oct.kandanCollaborationStateUrl": collaboration.collaborationStateUrl,
6671
6791
  "oct.serverUrl": collaboration.bootstrapServerUrl
6672
6792
  },
6673
6793
  "security.workspace.trust.enabled": false,
@@ -6746,6 +6866,7 @@ async function startCollaborationSidecar(collaboration, profile, editorRuntime,
6746
6866
  serverPort: collaboration.serverPort,
6747
6867
  serverUrl: collaboration.serverUrl,
6748
6868
  bootstrapServerUrl: collaboration.bootstrapServerUrl,
6869
+ collaborationStateUrl: collaboration.collaborationStateUrl,
6749
6870
  process: child,
6750
6871
  exited
6751
6872
  }
@@ -6986,7 +7107,7 @@ import {
6986
7107
  existsSync as existsSync3,
6987
7108
  mkdirSync as mkdirSync4,
6988
7109
  mkdtempSync as mkdtempSync2,
6989
- readFileSync as readFileSync2,
7110
+ readFileSync as readFileSync3,
6990
7111
  renameSync,
6991
7112
  rmSync,
6992
7113
  writeFileSync as writeFileSync3
@@ -7474,7 +7595,7 @@ function installedRuntime(cacheRoot, manifest) {
7474
7595
  return { ok: false };
7475
7596
  }
7476
7597
  try {
7477
- const installed = JSON.parse(readFileSync2(manifestPath, "utf8"));
7598
+ const installed = JSON.parse(readFileSync3(manifestPath, "utf8"));
7478
7599
  if (isJsonObject(installed) && installed.version === manifest.version && installed.platform === manifest.platform && (installed.archiveSha256 === undefined || installed.archiveSha256 === manifest.archiveSha256)) {
7479
7600
  return {
7480
7601
  ok: true,
@@ -7705,7 +7826,7 @@ function manifestAssetChecksums(assets) {
7705
7826
  return checksums;
7706
7827
  }
7707
7828
  function fileSha256Sync(path) {
7708
- return createHash2("sha256").update(readFileSync2(path)).digest("hex");
7829
+ return createHash2("sha256").update(readFileSync3(path)).digest("hex");
7709
7830
  }
7710
7831
  function defaultEditorRuntimeCacheRoot() {
7711
7832
  return join5(homedir4(), ".linzumi", "editor-runtimes");
@@ -7884,6 +8005,7 @@ function firstNonBlank(...values) {
7884
8005
  }
7885
8006
 
7886
8007
  // src/phoenix.ts
8008
+ var defaultPushTimeoutMs = 30000;
7887
8009
  function phoenixWebsocketUrl(baseUrl, token) {
7888
8010
  const parsed = new URL(baseUrl);
7889
8011
  switch (parsed.protocol) {
@@ -7899,7 +8021,7 @@ function phoenixWebsocketUrl(baseUrl, token) {
7899
8021
  parsed.searchParams.set("vsn", "2.0.0");
7900
8022
  return parsed.toString();
7901
8023
  }
7902
- async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new WebSocket(url)) {
8024
+ async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new WebSocket(url), options = {}) {
7903
8025
  const pending = new Map;
7904
8026
  const joins = new Map;
7905
8027
  const controlCallbacks = new Set;
@@ -7917,9 +8039,13 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new
7917
8039
  rejectReady: undefined,
7918
8040
  reconnectTimer: undefined
7919
8041
  };
8042
+ const pushTimeoutMs = options.pushTimeoutMs ?? defaultPushTimeoutMs;
7920
8043
  const rejectPending = (message) => {
7921
8044
  const error = new Error(message);
7922
- pending.forEach((pendingPush) => pendingPush.reject(error));
8045
+ pending.forEach((pendingPush) => {
8046
+ clearTimeout(pendingPush.timer);
8047
+ pendingPush.reject(error);
8048
+ });
7923
8049
  pending.clear();
7924
8050
  };
7925
8051
  const resetReady = () => {
@@ -7935,6 +8061,7 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new
7935
8061
  const pendingPush = pending.get(ref);
7936
8062
  if (pendingPush !== undefined) {
7937
8063
  pending.delete(ref);
8064
+ clearTimeout(pendingPush.timer);
7938
8065
  if (name === "phx_error") {
7939
8066
  pendingPush.reject(new Error("phoenix push failed"));
7940
8067
  } else if (isNonOkPushReply(payload) && pendingPush.event !== "phx_join") {
@@ -7960,7 +8087,12 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new
7960
8087
  state.nextRef += 1;
7961
8088
  const frame = [null, ref, topic, event, payload];
7962
8089
  return new Promise((resolve5, reject) => {
7963
- pending.set(ref, { event, resolve: resolve5, reject });
8090
+ const timer = setTimeout(() => {
8091
+ if (pending.delete(ref)) {
8092
+ reject(new Error(`phoenix push timed out after ${pushTimeoutMs}ms: ${event}`));
8093
+ }
8094
+ }, pushTimeoutMs);
8095
+ pending.set(ref, { event, timer, resolve: resolve5, reject });
7964
8096
  websocket.send(JSON.stringify(frame));
7965
8097
  });
7966
8098
  };
@@ -8035,10 +8167,10 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new
8035
8167
  return pushOnOpenSocket(topic, event, payload);
8036
8168
  };
8037
8169
  return {
8038
- join: async (topic, payload, options) => {
8170
+ join: async (topic, payload, options2) => {
8039
8171
  const reply = await push(topic, "phx_join", payload);
8040
8172
  if (isJoinReply(reply)) {
8041
- joins.set(topic, { payload: options?.rejoinPayload ?? (() => payload) });
8173
+ joins.set(topic, { payload: options2?.rejoinPayload ?? (() => payload) });
8042
8174
  return reply.response;
8043
8175
  }
8044
8176
  throw new Error(`phoenix join failed: ${joinErrorMessage(reply)}`);
@@ -8110,7 +8242,7 @@ import {
8110
8242
  existsSync as existsSync5,
8111
8243
  mkdirSync as mkdirSync6,
8112
8244
  openSync as openSync2,
8113
- readFileSync as readFileSync4,
8245
+ readFileSync as readFileSync5,
8114
8246
  unlinkSync as unlinkSync2,
8115
8247
  writeSync
8116
8248
  } from "node:fs";
@@ -8122,27 +8254,38 @@ import {
8122
8254
  existsSync as existsSync4,
8123
8255
  linkSync,
8124
8256
  mkdirSync as mkdirSync5,
8125
- readFileSync as readFileSync3,
8257
+ readFileSync as readFileSync4,
8126
8258
  realpathSync as realpathSync4,
8127
8259
  unlinkSync,
8128
8260
  writeFileSync as writeFileSync4
8129
8261
  } from "node:fs";
8130
8262
  import { homedir as homedir5 } from "node:os";
8131
8263
  import { basename as basename4, dirname as dirname4, join as join6, resolve as resolve5 } from "node:path";
8264
+
8265
+ // src/defaultUrls.ts
8266
+ var defaultLinzumiHttpUrl = "https://serve.linzumi.com";
8267
+ var defaultLinzumiWebSocketUrl = "wss://serve.linzumi.com";
8268
+
8269
+ // src/localConfig.ts
8270
+ var prodConfigScope = "prod";
8132
8271
  function localConfigPath(env = process.env) {
8133
8272
  const override = env.LINZUMI_CONFIG_FILE;
8134
8273
  return override !== undefined && override.trim() !== "" ? resolve5(expandUserPath(override)) : resolve5(homedir5(), ".linzumi", "config.json");
8135
8274
  }
8275
+ function localConfigScopeKey(linzumiUrl) {
8276
+ const normalizedUrl = kandanHttpBaseUrl(linzumiUrl);
8277
+ const normalizedProdUrl = kandanHttpBaseUrl(defaultLinzumiWebSocketUrl);
8278
+ return normalizedUrl === normalizedProdUrl ? prodConfigScope : normalizedUrl;
8279
+ }
8280
+ function localConfigScopeFileStem(linzumiUrl) {
8281
+ const scopeKey = localConfigScopeKey(linzumiUrl);
8282
+ return scopeKey === prodConfigScope ? prodConfigScope : Buffer.from(scopeKey).toString("base64url");
8283
+ }
8136
8284
  function readLocalConfig(path = localConfigPath()) {
8137
- if (!existsSync4(path)) {
8138
- return { version: 1, allowedCwds: [] };
8139
- }
8140
- const parsed = JSON.parse(readFileSync3(path, "utf8"));
8141
- if (!isConfigPayload(parsed)) {
8142
- throw new Error(`invalid Linzumi config: ${path}`);
8143
- }
8144
- const allowedCwds = uniqueStrings(parsed.allowedCwds);
8145
- return parsed.machineId === undefined ? { version: 1, allowedCwds } : { version: 1, machineId: parsed.machineId, allowedCwds };
8285
+ return readLocalConfigSection(path);
8286
+ }
8287
+ function readLocalConfigForLinzumiUrl(linzumiUrl, path = localConfigPath()) {
8288
+ return readLocalConfigSection(path, linzumiUrl);
8146
8289
  }
8147
8290
  function ensureLocalMachineId(path = localConfigPath(), createMachineId = randomUUID2) {
8148
8291
  const config = readLocalConfig(path);
@@ -8158,13 +8301,36 @@ function ensureLocalMachineId(path = localConfigPath(), createMachineId = random
8158
8301
  writeLocalConfig({ ...latestConfig, machineId }, path);
8159
8302
  return machineId;
8160
8303
  }
8161
- function localMachineIdSeedPath(configPath = localConfigPath()) {
8304
+ function ensureLocalMachineIdForLinzumiUrl(linzumiUrl, path = localConfigPath(), createMachineId = randomUUID2) {
8305
+ if (localConfigScopeKey(linzumiUrl) === prodConfigScope) {
8306
+ return ensureLocalMachineId(path, createMachineId);
8307
+ }
8308
+ const config = readLocalConfigForLinzumiUrl(linzumiUrl, path);
8309
+ if (config.machineId !== undefined) {
8310
+ return config.machineId;
8311
+ }
8312
+ const machineId = ensureLocalMachineIdSeed(path, createMachineId, linzumiUrl);
8313
+ const latestConfig = readLocalConfigForLinzumiUrl(linzumiUrl, path);
8314
+ const latestMachineId = latestConfig.machineId;
8315
+ if (latestMachineId !== undefined) {
8316
+ return latestMachineId;
8317
+ }
8318
+ writeLocalConfigSection({ ...latestConfig, machineId }, path, linzumiUrl);
8319
+ return machineId;
8320
+ }
8321
+ function localMachineIdSeedPath(configPath = localConfigPath(), linzumiUrl) {
8322
+ if (linzumiUrl !== undefined && localConfigScopeKey(linzumiUrl) !== prodConfigScope) {
8323
+ return join6(dirname4(configPath), `${basename4(configPath)}.${localConfigScopeFileStem(linzumiUrl)}.machine-id`);
8324
+ }
8162
8325
  return join6(dirname4(configPath), `${basename4(configPath)}.machine-id`);
8163
8326
  }
8164
- function readConfiguredAllowedCwdDetails(path = localConfigPath()) {
8327
+ function readConfiguredAllowedCwdDetailsForLinzumiUrl(linzumiUrl, path = localConfigPath()) {
8328
+ return readConfiguredAllowedCwdDetailsFromConfig(readLocalConfigForLinzumiUrl(linzumiUrl, path));
8329
+ }
8330
+ function readConfiguredAllowedCwdDetailsFromConfig(config) {
8165
8331
  const allowedCwds = [];
8166
8332
  const missingAllowedCwds = [];
8167
- for (const cwd of readLocalConfig(path).allowedCwds) {
8333
+ for (const cwd of config.allowedCwds) {
8168
8334
  const absolutePath = resolve5(expandUserPath(cwd));
8169
8335
  try {
8170
8336
  const realPath = realpathSync4(absolutePath);
@@ -8183,36 +8349,91 @@ function readConfiguredAllowedCwdDetails(path = localConfigPath()) {
8183
8349
  };
8184
8350
  }
8185
8351
  function addAllowedCwd(pathValue, path = localConfigPath()) {
8352
+ return addAllowedCwdToConfig(pathValue, path);
8353
+ }
8354
+ function addAllowedCwdForLinzumiUrl(pathValue, linzumiUrl, path = localConfigPath()) {
8355
+ return addAllowedCwdToConfig(pathValue, path, linzumiUrl);
8356
+ }
8357
+ function addAllowedCwdToConfig(pathValue, path, linzumiUrl) {
8186
8358
  const normalizedPath = realpathSync4(resolve5(expandUserPath(pathValue)));
8187
- const config = readLocalConfig(path);
8359
+ const config = readLocalConfigSection(path, linzumiUrl);
8188
8360
  const allowedCwds = uniqueStrings([...config.allowedCwds, normalizedPath]);
8189
- writeLocalConfig({ ...config, version: 1, allowedCwds }, path);
8361
+ writeLocalConfigSection({ ...config, version: 1, allowedCwds }, path, linzumiUrl);
8190
8362
  return allowedCwds;
8191
8363
  }
8192
8364
  function removeAllowedCwd(pathValue, path = localConfigPath()) {
8365
+ return removeAllowedCwdFromConfig(pathValue, path);
8366
+ }
8367
+ function removeAllowedCwdForLinzumiUrl(pathValue, linzumiUrl, path = localConfigPath()) {
8368
+ return removeAllowedCwdFromConfig(pathValue, path, linzumiUrl);
8369
+ }
8370
+ function removeAllowedCwdFromConfig(pathValue, path, linzumiUrl) {
8193
8371
  const requestedPath = resolve5(expandUserPath(pathValue));
8194
8372
  const normalizedRequest = realpathOrResolved(requestedPath);
8195
- const config = readLocalConfig(path);
8373
+ const config = readLocalConfigSection(path, linzumiUrl);
8196
8374
  const allowedCwds = config.allowedCwds.filter((cwd) => {
8197
8375
  const normalizedExisting = realpathOrResolved(cwd);
8198
8376
  return cwd !== pathValue && normalizedExisting !== normalizedRequest;
8199
8377
  });
8200
- writeLocalConfig({ ...config, version: 1, allowedCwds }, path);
8378
+ writeLocalConfigSection({ ...config, version: 1, allowedCwds }, path, linzumiUrl);
8201
8379
  return allowedCwds;
8202
8380
  }
8203
8381
  function writeLocalConfig(config, path = localConfigPath()) {
8382
+ writeLocalConfigSection(config, path);
8383
+ }
8384
+ function readLocalConfigFile(path) {
8385
+ if (!existsSync4(path)) {
8386
+ return { version: 1, allowedCwds: [] };
8387
+ }
8388
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
8389
+ if (!isConfigPayload(parsed)) {
8390
+ throw new Error(`invalid Linzumi config: ${path}`);
8391
+ }
8392
+ return parsed;
8393
+ }
8394
+ function readLocalConfigSection(path, linzumiUrl) {
8395
+ const parsed = readLocalConfigFile(path);
8396
+ const scopeKey = linzumiUrl === undefined ? prodConfigScope : localConfigScopeKey(linzumiUrl);
8397
+ const section = scopeKey === prodConfigScope ? parsed : parsed[scopeKey] ?? emptySection();
8398
+ if (!isConfigSection(section)) {
8399
+ throw new Error(`invalid Linzumi config section ${scopeKey}: ${path}`);
8400
+ }
8401
+ const allowedCwds = uniqueStrings(section.allowedCwds);
8402
+ return section.machineId === undefined ? { version: 1, allowedCwds } : { version: 1, machineId: section.machineId, allowedCwds };
8403
+ }
8404
+ function writeLocalConfigSection(config, path, linzumiUrl) {
8405
+ const scopeKey = linzumiUrl === undefined ? prodConfigScope : localConfigScopeKey(linzumiUrl);
8406
+ const nextSection = normalizedConfigSection(config);
8407
+ const next = scopeKey === prodConfigScope ? { ...readLocalConfigFile(path), ...nextSection, version: 1 } : {
8408
+ ...readLocalConfigFile(path),
8409
+ version: 1,
8410
+ [scopeKey]: nextSection
8411
+ };
8204
8412
  mkdirSync5(dirname4(path), { recursive: true });
8205
- writeFileSync4(path, `${JSON.stringify(config, null, 2)}
8413
+ writeFileSync4(path, `${JSON.stringify(next, null, 2)}
8206
8414
  `, "utf8");
8207
8415
  }
8208
8416
  function isConfigPayload(value) {
8209
- return typeof value === "object" && value !== null && value.version === 1 && Array.isArray(value.allowedCwds) && machineIdValid(value.machineId) && value.allowedCwds.every((cwd) => typeof cwd === "string" && cwd.trim() !== "");
8417
+ return typeof value === "object" && value !== null && value.version === 1 && isConfigSection(value);
8418
+ }
8419
+ function isConfigSection(value) {
8420
+ return typeof value === "object" && value !== null && Array.isArray(value.allowedCwds) && machineIdValid(value.machineId) && value.allowedCwds.every((cwd) => typeof cwd === "string" && cwd.trim() !== "");
8421
+ }
8422
+ function normalizedConfigSection(config) {
8423
+ if (!isConfigPayload(config)) {
8424
+ throw new Error("invalid Linzumi config");
8425
+ }
8426
+ const allowedCwds = uniqueStrings(config.allowedCwds);
8427
+ return config.machineId === undefined ? { allowedCwds } : { machineId: config.machineId, allowedCwds };
8428
+ }
8429
+ function emptySection() {
8430
+ return { allowedCwds: [] };
8210
8431
  }
8211
8432
  function machineIdValid(value) {
8212
8433
  return value === undefined || typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
8213
8434
  }
8214
- function ensureLocalMachineIdSeed(configPath, createMachineId) {
8215
- const seedPath = localMachineIdSeedPath(configPath);
8435
+ function ensureLocalMachineIdSeed(configPath, createMachineId, linzumiUrl) {
8436
+ const seedPath = localMachineIdSeedPath(configPath, linzumiUrl);
8216
8437
  if (existsSync4(seedPath)) {
8217
8438
  return readMachineIdSeed(seedPath);
8218
8439
  }
@@ -8237,7 +8458,7 @@ function ensureLocalMachineIdSeed(configPath, createMachineId) {
8237
8458
  }
8238
8459
  }
8239
8460
  function readMachineIdSeed(seedPath) {
8240
- const machineId = readFileSync3(seedPath, "utf8").trim();
8461
+ const machineId = readFileSync4(seedPath, "utf8").trim();
8241
8462
  if (!machineIdValid(machineId)) {
8242
8463
  throw new Error(`invalid Linzumi machine id seed: ${seedPath}`);
8243
8464
  }
@@ -8263,15 +8484,16 @@ function realpathOrResolved(pathValue) {
8263
8484
  }
8264
8485
 
8265
8486
  // src/version.ts
8266
- var linzumiCliVersion = "0.0.44-beta";
8487
+ var linzumiCliVersion = "0.0.45-beta";
8267
8488
  var linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
8268
8489
 
8269
8490
  // src/runnerLock.ts
8270
- function runnerLockPath(machineId, configPath = localConfigPath()) {
8271
- return join7(dirname5(configPath), "runners", `${encodeURIComponent(machineId)}.lock`);
8491
+ function runnerLockPath(machineId, configPath = localConfigPath(), linzumiUrl) {
8492
+ const lockName = linzumiUrl === undefined ? encodeURIComponent(machineId) : localConfigScopeFileStem(linzumiUrl);
8493
+ return join7(dirname5(configPath), "runners", `${lockName}.lock`);
8272
8494
  }
8273
8495
  function acquireRunnerLock(options) {
8274
- const path = runnerLockPath(options.machineId, options.configPath);
8496
+ const path = runnerLockPath(options.machineId, options.configPath, options.linzumiUrl);
8275
8497
  const isPidAlive = options.isPidAlive ?? processIsAlive;
8276
8498
  const record = {
8277
8499
  version: 1,
@@ -8280,9 +8502,11 @@ function acquireRunnerLock(options) {
8280
8502
  pid: options.pid ?? process.pid,
8281
8503
  cwd: options.cwd,
8282
8504
  workspace: options.workspace,
8505
+ ...options.linzumiUrl === undefined ? {} : { linzumiUrl: kandanHttpBaseUrl(options.linzumiUrl) },
8283
8506
  startedAt: (options.now ?? (() => new Date))().toISOString(),
8284
8507
  cliVersion: options.cliVersion ?? linzumiCliVersion
8285
8508
  };
8509
+ rejectLiveLegacyProductionRunnerLock(options.machineId, options.configPath, options.linzumiUrl, isPidAlive);
8286
8510
  writeLockOrHandleExisting(path, record, isPidAlive, options.beforeReadExistingLock, options.beforeReplaceStaleLock);
8287
8511
  return {
8288
8512
  path,
@@ -8290,6 +8514,28 @@ function acquireRunnerLock(options) {
8290
8514
  release: () => releaseRunnerLock(path, record)
8291
8515
  };
8292
8516
  }
8517
+ function rejectLiveLegacyProductionRunnerLock(machineId, configPath, linzumiUrl, isPidAlive) {
8518
+ if (linzumiUrl === undefined || localConfigScopeKey(linzumiUrl) !== localConfigScopeKey(defaultLinzumiHttpUrl)) {
8519
+ return;
8520
+ }
8521
+ const legacyPath = runnerLockPath(machineId, configPath);
8522
+ const existing = readRunnerLockIfPresent(legacyPath);
8523
+ if (existing === undefined) {
8524
+ return;
8525
+ }
8526
+ if (isPidAlive(existing.pid)) {
8527
+ throw new Error(activeRunnerLockMessage(legacyPath, existing));
8528
+ }
8529
+ withStaleReplacementLock(legacyPath, isPidAlive, () => {
8530
+ const latest = existsSync5(legacyPath) ? readRunnerLock(legacyPath) : undefined;
8531
+ if (latest !== undefined && isPidAlive(latest.pid)) {
8532
+ throw new Error(activeRunnerLockMessage(legacyPath, latest));
8533
+ }
8534
+ if (latest !== undefined) {
8535
+ unlinkSync2(legacyPath);
8536
+ }
8537
+ });
8538
+ }
8293
8539
  function writeLockOrHandleExisting(path, record, isPidAlive, beforeReadExistingLock, beforeReplaceStaleLock) {
8294
8540
  if (tryCreateLock(path, record)) {
8295
8541
  return;
@@ -8375,7 +8621,7 @@ function withStaleReplacementLock(path, isPidAlive, callback) {
8375
8621
  function readReplacementLockPidIfPresent(path) {
8376
8622
  let value;
8377
8623
  try {
8378
- value = readFileSync4(path, "utf8").trim();
8624
+ value = readFileSync5(path, "utf8").trim();
8379
8625
  } catch (error) {
8380
8626
  if (isNodeErrorCode2(error, "ENOENT")) {
8381
8627
  return;
@@ -8415,14 +8661,17 @@ function readRunnerLockIfPresent(path) {
8415
8661
  }
8416
8662
  }
8417
8663
  function readRunnerLock(path) {
8418
- const parsed = JSON.parse(readFileSync4(path, "utf8"));
8664
+ const parsed = JSON.parse(readFileSync5(path, "utf8"));
8419
8665
  if (!isRunnerLockRecord(parsed)) {
8420
8666
  throw new Error(`invalid Linzumi runner lock: ${path}`);
8421
8667
  }
8422
8668
  return parsed;
8423
8669
  }
8424
8670
  function isRunnerLockRecord(value) {
8425
- return typeof value === "object" && value !== null && value.version === 1 && typeof value.machineId === "string" && value.machineId.trim() !== "" && typeof value.runnerId === "string" && value.runnerId.trim() !== "" && Number.isInteger(value.pid) && value.pid > 0 && typeof value.cwd === "string" && value.cwd.trim() !== "" && workspaceValid(value.workspace) && typeof value.startedAt === "string" && value.startedAt.trim() !== "" && typeof value.cliVersion === "string" && value.cliVersion.trim() !== "";
8671
+ return typeof value === "object" && value !== null && value.version === 1 && typeof value.machineId === "string" && value.machineId.trim() !== "" && typeof value.runnerId === "string" && value.runnerId.trim() !== "" && Number.isInteger(value.pid) && value.pid > 0 && typeof value.cwd === "string" && value.cwd.trim() !== "" && workspaceValid(value.workspace) && linzumiUrlValid(value.linzumiUrl) && typeof value.startedAt === "string" && value.startedAt.trim() !== "" && typeof value.cliVersion === "string" && value.cliVersion.trim() !== "";
8672
+ }
8673
+ function linzumiUrlValid(value) {
8674
+ return value === undefined || typeof value === "string" && value.trim() !== "";
8426
8675
  }
8427
8676
  function workspaceValid(value) {
8428
8677
  return value === null || typeof value === "string" && value.trim() !== "";
@@ -8437,8 +8686,10 @@ function processIsAlive(pid) {
8437
8686
  }
8438
8687
  function activeRunnerLockMessage(path, record) {
8439
8688
  const workspace = record.workspace === null ? "workspace: unknown" : `workspace: ${record.workspace}`;
8689
+ const linzumiUrl = record.linzumiUrl === undefined ? undefined : `linzumi url: ${displayLinzumiUrl(record.linzumiUrl)}`;
8440
8690
  return [
8441
- "another Linzumi runner is already running for this machine",
8691
+ record.linzumiUrl === undefined ? "another Linzumi runner is already running for this machine" : "another Linzumi runner is already running for this Linzumi URL",
8692
+ ...linzumiUrl === undefined ? [] : [linzumiUrl],
8442
8693
  `runner id: ${record.runnerId}`,
8443
8694
  `pid: ${record.pid}`,
8444
8695
  `cwd: ${record.cwd}`,
@@ -8450,6 +8701,12 @@ function activeRunnerLockMessage(path, record) {
8450
8701
  ].join(`
8451
8702
  `);
8452
8703
  }
8704
+ function displayLinzumiUrl(linzumiUrl) {
8705
+ if (linzumiUrl === localConfigScopeKey(defaultLinzumiHttpUrl)) {
8706
+ return defaultLinzumiHttpUrl;
8707
+ }
8708
+ return localConfigScopeKey(linzumiUrl) === localConfigScopeKey(defaultLinzumiHttpUrl) ? defaultLinzumiHttpUrl : linzumiUrl;
8709
+ }
8453
8710
  function isNodeErrorCode2(error, code) {
8454
8711
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
8455
8712
  }
@@ -8611,6 +8868,7 @@ async function runLocalCodexRunner(options) {
8611
8868
  runnerId: options.runnerId,
8612
8869
  cwd: options.cwd,
8613
8870
  workspace: runnerWorkspaceSlug(options) ?? null,
8871
+ linzumiUrl: options.kandanUrl,
8614
8872
  configPath: options.runnerLockConfigPath
8615
8873
  });
8616
8874
  cleanup.actions.push(() => runnerLock.release());
@@ -10005,7 +10263,7 @@ function allowedCwdSuggestions(cwd, allowedCwds) {
10005
10263
  }
10006
10264
 
10007
10265
  // src/authCache.ts
10008
- import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
10266
+ import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
10009
10267
  import { homedir as homedir6 } from "node:os";
10010
10268
  import { dirname as dirname6, join as join9 } from "node:path";
10011
10269
  function defaultAuthFilePath() {
@@ -10016,7 +10274,7 @@ function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePat
10016
10274
  if (!existsSync6(authFilePath)) {
10017
10275
  return;
10018
10276
  }
10019
- const authFile = parseAuthFile(readFileSync5(authFilePath, "utf8"));
10277
+ const authFile = parseAuthFile(readFileSync6(authFilePath, "utf8"));
10020
10278
  const kandanBaseUrl = kandanHttpBaseUrl(kandanUrl);
10021
10279
  const entry = authFile.local_codex_runner?.[kandanBaseUrl];
10022
10280
  if (entry === undefined || entry.access_token.trim() === "") {
@@ -10034,7 +10292,7 @@ function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePat
10034
10292
  }
10035
10293
  function writeCachedLocalRunnerToken(args) {
10036
10294
  const authFilePath = args.authFilePath ?? defaultAuthFilePath();
10037
- const existing = existsSync6(authFilePath) ? parseAuthFile(readFileSync5(authFilePath, "utf8")) : { version: 1 };
10295
+ const existing = existsSync6(authFilePath) ? parseAuthFile(readFileSync6(authFilePath, "utf8")) : { version: 1 };
10038
10296
  const kandanBaseUrl = kandanHttpBaseUrl(args.kandanUrl);
10039
10297
  const issuedAt = new Date;
10040
10298
  const expiresAt = args.expiresInSeconds === undefined ? undefined : new Date(issuedAt.getTime() + args.expiresInSeconds * 1000).toISOString();
@@ -10143,12 +10401,8 @@ async function acquireAndCacheToken(args) {
10143
10401
  return token.accessToken;
10144
10402
  }
10145
10403
 
10146
- // src/defaultUrls.ts
10147
- var defaultLinzumiHttpUrl = "https://serve.linzumi.com";
10148
- var defaultLinzumiWebSocketUrl = "wss://serve.linzumi.com";
10149
-
10150
10404
  // src/kandanTls.ts
10151
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
10405
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
10152
10406
  import { Agent } from "undici";
10153
10407
  import { WebSocket as WsWebSocket } from "ws";
10154
10408
  function kandanTlsTrustFromEnv() {
@@ -10162,7 +10416,7 @@ function kandanTlsTrustFromCaFile(caFile) {
10162
10416
  if (!existsSync7(trimmed)) {
10163
10417
  throw new Error(`KANDAN_TLS_CA_FILE does not exist: ${trimmed}`);
10164
10418
  }
10165
- const ca = readFileSync6(trimmed, "utf8");
10419
+ const ca = readFileSync7(trimmed, "utf8");
10166
10420
  return {
10167
10421
  caFile: trimmed,
10168
10422
  ca,
@@ -10191,7 +10445,7 @@ function trustedWebSocketFactory(trust, WebSocketImpl = WsWebSocket) {
10191
10445
  }
10192
10446
 
10193
10447
  // src/agentBootstrap.ts
10194
- import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
10448
+ import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
10195
10449
  import { dirname as dirname7, join as join10 } from "node:path";
10196
10450
  import { homedir as homedir7 } from "node:os";
10197
10451
  async function runAgentCliCommand(args, deps = {
@@ -10801,7 +11055,7 @@ function authorizationHeaders(token) {
10801
11055
  return { authorization: `Bearer ${token}` };
10802
11056
  }
10803
11057
  function readOptionalTextFile(path) {
10804
- return existsSync8(path) ? readFileSync7(path, "utf8") : undefined;
11058
+ return existsSync8(path) ? readFileSync8(path, "utf8") : undefined;
10805
11059
  }
10806
11060
  function writeTextFile(path, content) {
10807
11061
  mkdirSync8(dirname7(path), { recursive: true });
@@ -10881,7 +11135,7 @@ Launch target:
10881
11135
  }
10882
11136
 
10883
11137
  // src/helloLinzumiProject.ts
10884
- import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
11138
+ import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
10885
11139
  import { dirname as dirname8, join as join11, resolve as resolve7 } from "node:path";
10886
11140
  import { fileURLToPath } from "node:url";
10887
11141
  var defaultHelloLinzumiProjectDir = "/tmp/hello_linzumi";
@@ -10891,7 +11145,7 @@ var defaultHelloLinzumiPort = 8787;
10891
11145
  var defaultHelloLinzumiHost = "0.0.0.0";
10892
11146
  var markerFile = ".linzumi-demo-project";
10893
11147
  var moduleDir = dirname8(fileURLToPath(import.meta.url));
10894
- var linzumiLogoSvg = readFileSync8(join11(moduleDir, "assets", "linzumi-logo.svg"), "utf8");
11148
+ var linzumiLogoSvg = readFileSync9(join11(moduleDir, "assets", "linzumi-logo.svg"), "utf8");
10895
11149
  function createHelloLinzumiProject(input = {}) {
10896
11150
  const options = typeof input === "string" ? { rootPath: input } : input;
10897
11151
  const root = resolveHelloProjectRoot(options);
@@ -10946,7 +11200,7 @@ function assertWritableDemoRoot(root, reset) {
10946
11200
  return;
10947
11201
  }
10948
11202
  const markerPath = join11(root, markerFile);
10949
- const isDemoRoot = existsSync9(markerPath) && readFileSync8(markerPath, "utf8").trim() === "hello-linzumi";
11203
+ const isDemoRoot = existsSync9(markerPath) && readFileSync9(markerPath, "utf8").trim() === "hello-linzumi";
10950
11204
  if (isDemoRoot && reset) {
10951
11205
  rmSync2(root, { recursive: true, force: true });
10952
11206
  return;
@@ -11449,7 +11703,7 @@ import {
11449
11703
  closeSync as closeSync2,
11450
11704
  mkdirSync as mkdirSync10,
11451
11705
  openSync as openSync3,
11452
- readFileSync as readFileSync9,
11706
+ readFileSync as readFileSync10,
11453
11707
  watch,
11454
11708
  writeFileSync as writeFileSync8
11455
11709
  } from "node:fs";
@@ -11534,12 +11788,12 @@ function commanderDaemonStatus(runnerId, statusDir = commanderStatusDir(), proce
11534
11788
  if (!existsSync10(statusFile)) {
11535
11789
  return { status: "missing", runnerId, statusFile };
11536
11790
  }
11537
- const record = parseRecord(readFileSync9(statusFile, "utf8"));
11791
+ const record = parseRecord(readFileSync10(statusFile, "utf8"));
11538
11792
  return processIsRunning(record.pid) && processMatchesRecord(record, processIdentityReader) ? { status: "running", record } : { status: "stopped", record };
11539
11793
  }
11540
11794
  async function waitForCommanderDaemon(options) {
11541
11795
  const now = options.now ?? (() => Date.now());
11542
- const readTextFile = options.readTextFile ?? ((path) => existsSync10(path) ? readFileSync9(path, "utf8") : undefined);
11796
+ const readTextFile = options.readTextFile ?? ((path) => existsSync10(path) ? readFileSync10(path, "utf8") : undefined);
11543
11797
  const statusImpl = options.statusImpl ?? commanderDaemonStatus;
11544
11798
  const deadline = now() + options.timeoutMs;
11545
11799
  while (now() <= deadline) {
@@ -11794,7 +12048,7 @@ async function main(args) {
11794
12048
  }
11795
12049
  case "start": {
11796
12050
  const options = await parseStartRunnerArgs(parsed.args);
11797
- addAllowedCwd(options.cwd);
12051
+ addAllowedCwdForLinzumiUrl(options.cwd, options.kandanUrl);
11798
12052
  await runLocalCodexRunner(withLocalMachineId(options));
11799
12053
  return;
11800
12054
  }
@@ -11897,19 +12151,16 @@ function runHelloCommand(args) {
11897
12151
  `);
11898
12152
  }
11899
12153
  function runPathsCommand(args) {
11900
- const [subcommand, pathValue, ...rest] = args;
11901
- if (subcommand === undefined || subcommand === "help" || subcommand === "--help") {
12154
+ const { subcommand, pathValue, linzumiUrl, help } = parsePathsCommandArgs(args);
12155
+ if (help || subcommand === undefined || subcommand === "help") {
11902
12156
  process.stdout.write(pathsHelpText());
11903
12157
  return;
11904
12158
  }
11905
- if (rest.length > 0) {
11906
- throw new Error("linzumi paths accepts one path argument");
11907
- }
11908
12159
  switch (subcommand) {
11909
12160
  case "list": {
11910
- const config = readLocalConfig();
12161
+ const config = linzumiUrl === undefined ? readLocalConfig() : readLocalConfigForLinzumiUrl(linzumiUrl);
11911
12162
  if (config.allowedCwds.length === 0) {
11912
- process.stdout.write(`No trusted paths configured in ${localConfigPath()}
12163
+ process.stdout.write(`No trusted paths configured in ${localConfigPath()}${pathsScopeSuffix(linzumiUrl)}
11913
12164
  `);
11914
12165
  return;
11915
12166
  }
@@ -11923,7 +12174,11 @@ function runPathsCommand(args) {
11923
12174
  throw new Error("missing path for linzumi paths add");
11924
12175
  }
11925
12176
  const trustedPath = realpathSync6(resolve9(expandUserPath(pathValue)));
11926
- addAllowedCwd(pathValue);
12177
+ if (linzumiUrl === undefined) {
12178
+ addAllowedCwd(pathValue);
12179
+ } else {
12180
+ addAllowedCwdForLinzumiUrl(pathValue, linzumiUrl);
12181
+ }
11927
12182
  process.stdout.write(`Trusted ${trustedPath}
11928
12183
  `);
11929
12184
  return;
@@ -11932,7 +12187,11 @@ function runPathsCommand(args) {
11932
12187
  if (pathValue === undefined || pathValue.trim() === "") {
11933
12188
  throw new Error("missing path for linzumi paths remove");
11934
12189
  }
11935
- removeAllowedCwd(pathValue);
12190
+ if (linzumiUrl === undefined) {
12191
+ removeAllowedCwd(pathValue);
12192
+ } else {
12193
+ removeAllowedCwdForLinzumiUrl(pathValue, linzumiUrl);
12194
+ }
11936
12195
  process.stdout.write(`Removed trusted path ${pathValue}
11937
12196
  `);
11938
12197
  return;
@@ -11941,6 +12200,46 @@ function runPathsCommand(args) {
11941
12200
  throw new Error(`invalid paths command: ${subcommand}`);
11942
12201
  }
11943
12202
  }
12203
+ function parsePathsCommandArgs(args) {
12204
+ const positional = [];
12205
+ let linzumiUrl;
12206
+ let help = false;
12207
+ for (let index = 0;index < args.length; index += 1) {
12208
+ const arg = args[index];
12209
+ switch (arg) {
12210
+ case "--help":
12211
+ help = true;
12212
+ break;
12213
+ case "--linzumi-url":
12214
+ case "--kandan-url": {
12215
+ const value = args[index + 1];
12216
+ if (value === undefined || value.startsWith("--")) {
12217
+ throw new Error(`missing value for ${arg}`);
12218
+ }
12219
+ linzumiUrl = value;
12220
+ index += 1;
12221
+ break;
12222
+ }
12223
+ default:
12224
+ if (arg !== undefined && arg.startsWith("--")) {
12225
+ throw new Error(`invalid flag: ${arg}`);
12226
+ }
12227
+ positional.push(arg ?? "");
12228
+ }
12229
+ }
12230
+ if (positional.length > 2) {
12231
+ throw new Error("linzumi paths accepts one path argument");
12232
+ }
12233
+ return {
12234
+ subcommand: positional[0],
12235
+ pathValue: positional[1],
12236
+ linzumiUrl,
12237
+ help
12238
+ };
12239
+ }
12240
+ function pathsScopeSuffix(linzumiUrl) {
12241
+ return linzumiUrl === undefined ? "" : ` for ${linzumiUrl}`;
12242
+ }
11944
12243
  async function runCommanderDaemonCommand(args) {
11945
12244
  const [subcommand, ...rest] = args;
11946
12245
  switch (subcommand) {
@@ -12172,7 +12471,7 @@ async function parseAgentRunnerArgs(args, deps = {
12172
12471
  const kandanUrl = stringValue3(values, "linzumi-url") ?? agentApiUrlToKandanUrl(tokenFile.apiUrl);
12173
12472
  const requestedCwdValue = cwdArg ?? stringValue3(values, "cwd");
12174
12473
  const requestedCwd = resolveUserPath(requestedCwdValue ?? process.cwd());
12175
- const configuredAllowedCwds2 = requestedCwdValue === undefined && !values.has("allowed-cwd") ? readConfiguredAllowedCwdDetails() : { allowedCwds: [], missingAllowedCwds: [] };
12474
+ const configuredAllowedCwds2 = requestedCwdValue === undefined && !values.has("allowed-cwd") ? readConfiguredAllowedCwdDetailsForLinzumiUrl(kandanUrl) : { allowedCwds: [], missingAllowedCwds: [] };
12176
12475
  const allowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : requestedCwdValue === undefined ? configuredAllowedCwds2.allowedCwds.length > 0 ? [...configuredAllowedCwds2.allowedCwds] : assertConfiguredAllowedCwds([requestedCwd]) : assertConfiguredAllowedCwds([requestedCwd]);
12177
12476
  const cwd = allowedCwds[0] ?? requestedCwd;
12178
12477
  const codexBin = stringValue3(values, "codex-bin") ?? "codex";
@@ -12219,7 +12518,7 @@ async function parseAgentRunnerArgs(args, deps = {
12219
12518
  };
12220
12519
  }
12221
12520
  function readAgentTokenTextFile(path) {
12222
- return existsSync11(path) ? readFileSync10(path, "utf8") : undefined;
12521
+ return existsSync11(path) ? readFileSync11(path, "utf8") : undefined;
12223
12522
  }
12224
12523
  function rejectAgentRunnerTargetingFlags(values) {
12225
12524
  const unsupportedFlags = [
@@ -12303,7 +12602,7 @@ async function parseRunnerArgs(args, deps = {
12303
12602
  const kandanUrl = stringValue3(values, "linzumi-url") ?? defaultLinzumiWebSocketUrl;
12304
12603
  const cwd = stringValue3(values, "cwd") ?? process.cwd();
12305
12604
  const cwdAllowedCwds = assertConfiguredAllowedCwds([cwd]);
12306
- const localConfiguredAllowedCwds = values.has("allowed-cwd") ? { allowedCwds: [], missingAllowedCwds: [] } : readConfiguredAllowedCwdDetails();
12605
+ const localConfiguredAllowedCwds = values.has("allowed-cwd") ? { allowedCwds: [], missingAllowedCwds: [] } : readConfiguredAllowedCwdDetailsForLinzumiUrl(kandanUrl);
12307
12606
  const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : [...localConfiguredAllowedCwds.allowedCwds];
12308
12607
  const codexBin = stringValue3(values, "codex-bin") ?? "codex";
12309
12608
  const customCodeServerBin = stringValue3(values, "code-server-bin");
@@ -12516,7 +12815,7 @@ function parseChannelPath(channel) {
12516
12815
  function withLocalMachineId(options) {
12517
12816
  return {
12518
12817
  ...options,
12519
- machineId: ensureLocalMachineId()
12818
+ machineId: localConfigScopeKey(options.kandanUrl) === localConfigScopeKey(defaultLinzumiWebSocketUrl) ? ensureLocalMachineId() : ensureLocalMachineIdForLinzumiUrl(options.kandanUrl)
12520
12819
  };
12521
12820
  }
12522
12821
  function required(values, key) {
@@ -12698,13 +12997,13 @@ function pathsHelpText() {
12698
12997
  return `Linzumi trusted paths
12699
12998
 
12700
12999
  Usage:
12701
- linzumi paths list
12702
- linzumi paths add <path>
12703
- linzumi paths remove <path>
13000
+ linzumi paths [--linzumi-url <ws-url>] list
13001
+ linzumi paths [--linzumi-url <ws-url>] add <path>
13002
+ linzumi paths [--linzumi-url <ws-url>] remove <path>
12704
13003
 
12705
- Trusted paths are stored in ~/.linzumi/config.json. linzumi connect always
12706
- trusts its selected cwd for that runner process and also uses configured paths
12707
- unless --allowed-cwd is passed with explicit extra roots.
13004
+ Trusted paths are stored in ~/.linzumi/config.json. Production/default paths
13005
+ use the root config fields; explicit --linzumi-url paths use a URL-scoped config
13006
+ section so local, staging, and production runners do not share trust state.
12708
13007
  `;
12709
13008
  }
12710
13009
  function startHelpText() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.44-beta",
3
+ "version": "0.0.45-beta",
4
4
  "description": "Linzumi CLI — point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {