codex-to-im 1.0.41 → 1.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -5,9 +5,9 @@ import { createRequire } from 'module'; const require = createRequire(import.met
5
5
  import { stdin as input, stdout as output } from "node:process";
6
6
 
7
7
  // src/service-manager.ts
8
- import fs2 from "node:fs";
8
+ import fs3 from "node:fs";
9
9
  import os2 from "node:os";
10
- import path2 from "node:path";
10
+ import path3 from "node:path";
11
11
  import { spawn } from "node:child_process";
12
12
  import { fileURLToPath } from "node:url";
13
13
 
@@ -29,8 +29,14 @@ function parseReasoningEffort(value) {
29
29
  }
30
30
  return void 0;
31
31
  }
32
+ function normalizeChannelId(value) {
33
+ return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
34
+ }
32
35
 
33
36
  // src/config.ts
37
+ function isSupportedChannelProvider(value) {
38
+ return value === "feishu" || value === "weixin";
39
+ }
34
40
  var DEFAULT_CTI_HOME = path.join(os.homedir(), ".codex-to-im");
35
41
  var DEFAULT_WORKSPACE_ROOT = path.join(os.homedir(), "cx2im");
36
42
  var CTI_HOME = process.env.CTI_HOME || DEFAULT_CTI_HOME;
@@ -97,6 +103,8 @@ function readConfigV2File() {
97
103
  try {
98
104
  const parsed = JSON.parse(fs.readFileSync(CONFIG_V2_PATH, "utf-8"));
99
105
  if (parsed && parsed.schemaVersion === 2 && parsed.runtime && Array.isArray(parsed.channels)) {
106
+ parsed.runtime.provider = normalizeRuntimeProvider(parsed.runtime.provider);
107
+ parsed.channels = normalizeChannelInstances(parsed.channels);
100
108
  return parsed;
101
109
  }
102
110
  return null;
@@ -116,9 +124,32 @@ function defaultAliasForProvider(provider) {
116
124
  function buildDefaultChannelId(provider) {
117
125
  return `${provider}-default`;
118
126
  }
127
+ function normalizeRuntimeProvider(_value) {
128
+ return "codex";
129
+ }
130
+ function normalizeChannelInstances(value) {
131
+ if (!Array.isArray(value)) return [];
132
+ return value.flatMap((entry) => {
133
+ if (!entry || typeof entry !== "object") return [];
134
+ const record = entry;
135
+ if (!isSupportedChannelProvider(record.provider)) return [];
136
+ const provider = record.provider;
137
+ const config = record.config && typeof record.config === "object" ? record.config : {};
138
+ const timestamp = nowIso();
139
+ return [{
140
+ id: normalizeChannelId(
141
+ typeof record.id === "string" && record.id.trim() ? record.id : buildDefaultChannelId(provider)
142
+ ),
143
+ alias: typeof record.alias === "string" && record.alias.trim() ? record.alias.trim() : defaultAliasForProvider(provider),
144
+ provider,
145
+ enabled: record.enabled === true,
146
+ createdAt: typeof record.createdAt === "string" ? record.createdAt : timestamp,
147
+ updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : timestamp,
148
+ config
149
+ }];
150
+ });
151
+ }
119
152
  function migrateLegacyEnvToV2(env) {
120
- const rawRuntime = env.get("CTI_RUNTIME") || "codex";
121
- const runtime = ["claude", "codex", "auto"].includes(rawRuntime) ? rawRuntime : "codex";
122
153
  const enabledChannels = splitCsv(env.get("CTI_ENABLED_CHANNELS")) ?? ["feishu"];
123
154
  const timestamp = nowIso();
124
155
  const channels = [];
@@ -165,7 +196,7 @@ function migrateLegacyEnvToV2(env) {
165
196
  return {
166
197
  schemaVersion: 2,
167
198
  runtime: {
168
- provider: runtime,
199
+ provider: "codex",
169
200
  defaultWorkspaceRoot: expandHomePath(env.get("CTI_DEFAULT_WORKSPACE_ROOT")) || void 0,
170
201
  defaultModel: env.get("CTI_DEFAULT_MODEL") || void 0,
171
202
  defaultMode: env.get("CTI_DEFAULT_MODE") || "code",
@@ -176,8 +207,7 @@ function migrateLegacyEnvToV2(env) {
176
207
  codexSandboxMode: parseSandboxMode(env.get("CTI_CODEX_SANDBOX_MODE")) ?? "workspace-write",
177
208
  codexReasoningEffort: parseReasoningEffort(env.get("CTI_CODEX_REASONING_EFFORT")) ?? "medium",
178
209
  uiAllowLan: env.get("CTI_UI_ALLOW_LAN") === "true",
179
- uiAccessToken: env.get("CTI_UI_ACCESS_TOKEN") || void 0,
180
- autoApprove: env.get("CTI_AUTO_APPROVE") === "true"
210
+ uiAccessToken: env.get("CTI_UI_ACCESS_TOKEN") || void 0
181
211
  },
182
212
  channels
183
213
  };
@@ -200,8 +230,7 @@ function expandConfig(v2) {
200
230
  codexSandboxMode: v2.runtime.codexSandboxMode ?? "workspace-write",
201
231
  codexReasoningEffort: v2.runtime.codexReasoningEffort ?? "medium",
202
232
  uiAllowLan: v2.runtime.uiAllowLan === true,
203
- uiAccessToken: v2.runtime.uiAccessToken || void 0,
204
- autoApprove: v2.runtime.autoApprove === true
233
+ uiAccessToken: v2.runtime.uiAccessToken || void 0
205
234
  };
206
235
  }
207
236
  function loadConfig() {
@@ -225,50 +254,86 @@ function loadConfig() {
225
254
  codexSkipGitRepoCheck: true,
226
255
  codexSandboxMode: "workspace-write",
227
256
  codexReasoningEffort: "medium",
228
- uiAllowLan: false,
229
- autoApprove: false
257
+ uiAllowLan: false
230
258
  },
231
259
  channels: []
232
260
  };
233
261
  return expandConfig(empty);
234
262
  }
235
263
 
236
- // src/service-manager.ts
237
- var moduleDir = path2.dirname(fileURLToPath(import.meta.url));
238
- var packageRoot = path2.resolve(moduleDir, "..");
264
+ // src/bridge-instance-lock.ts
265
+ import fs2 from "node:fs";
266
+ import path2 from "node:path";
239
267
  var runtimeDir = path2.join(CTI_HOME, "runtime");
240
- var logsDir = path2.join(CTI_HOME, "logs");
241
- var bridgePidFile = path2.join(runtimeDir, "bridge.pid");
242
- var bridgeStatusFile = path2.join(runtimeDir, "status.json");
243
- var bridgeStartLockFile = path2.join(runtimeDir, "bridge.start.lock");
244
- var uiStatusFile = path2.join(runtimeDir, "ui-server.json");
268
+ var bridgeInstanceLockFile = path2.join(runtimeDir, "bridge.instance.lock");
269
+ function readJsonFile(filePath, fallback) {
270
+ try {
271
+ return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
272
+ } catch {
273
+ return fallback;
274
+ }
275
+ }
276
+ function isProcessAlive(pid) {
277
+ if (!pid) return false;
278
+ try {
279
+ process.kill(pid, 0);
280
+ return true;
281
+ } catch {
282
+ return false;
283
+ }
284
+ }
285
+ function readBridgeInstanceLock(filePath = bridgeInstanceLockFile) {
286
+ const parsed = readJsonFile(filePath, null);
287
+ const pid = Number(parsed?.pid);
288
+ const createdAt = typeof parsed?.createdAt === "string" ? parsed.createdAt : "";
289
+ if (!Number.isFinite(pid) || pid <= 0 || !createdAt) return null;
290
+ return { pid, createdAt };
291
+ }
292
+ function clearStaleBridgeInstanceLock(filePath = bridgeInstanceLockFile, isAlive = isProcessAlive) {
293
+ const existing = readBridgeInstanceLock(filePath);
294
+ if (existing && isAlive(existing.pid)) return;
295
+ try {
296
+ fs2.unlinkSync(filePath);
297
+ } catch {
298
+ }
299
+ }
300
+
301
+ // src/service-manager.ts
302
+ var moduleDir = path3.dirname(fileURLToPath(import.meta.url));
303
+ var packageRoot = path3.resolve(moduleDir, "..");
304
+ var runtimeDir2 = path3.join(CTI_HOME, "runtime");
305
+ var logsDir = path3.join(CTI_HOME, "logs");
306
+ var bridgePidFile = path3.join(runtimeDir2, "bridge.pid");
307
+ var bridgeStatusFile = path3.join(runtimeDir2, "status.json");
308
+ var bridgeStartLockFile = path3.join(runtimeDir2, "bridge.start.lock");
309
+ var uiStatusFile = path3.join(runtimeDir2, "ui-server.json");
245
310
  var uiPort = 4781;
246
311
  var bridgeAutostartTaskName = "CodexToIMBridge";
247
- var bridgeAutostartLauncherFile = path2.join(runtimeDir, "bridge-autostart.ps1");
248
- var npmUninstallLogFile = path2.join(runtimeDir, "npm-uninstall.log");
312
+ var bridgeAutostartLauncherFile = path3.join(runtimeDir2, "bridge-autostart.ps1");
313
+ var npmUninstallLogFile = path3.join(runtimeDir2, "npm-uninstall.log");
249
314
  var WINDOWS_HIDE = process.platform === "win32" ? { windowsHide: true } : {};
250
315
  var BRIDGE_START_LOCK_STALE_MS = 3e4;
251
316
  function ensureDirs() {
252
- fs2.mkdirSync(runtimeDir, { recursive: true });
253
- fs2.mkdirSync(logsDir, { recursive: true });
317
+ fs3.mkdirSync(runtimeDir2, { recursive: true });
318
+ fs3.mkdirSync(logsDir, { recursive: true });
254
319
  }
255
- function readJsonFile(filePath, fallback) {
320
+ function readJsonFile2(filePath, fallback) {
256
321
  try {
257
- return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
322
+ return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
258
323
  } catch {
259
324
  return fallback;
260
325
  }
261
326
  }
262
327
  function readPid(filePath) {
263
328
  try {
264
- const raw = fs2.readFileSync(filePath, "utf-8").trim();
329
+ const raw = fs3.readFileSync(filePath, "utf-8").trim();
265
330
  const pid = Number(raw);
266
331
  return Number.isFinite(pid) ? pid : void 0;
267
332
  } catch {
268
333
  return void 0;
269
334
  }
270
335
  }
271
- function isProcessAlive(pid) {
336
+ function isProcessAlive2(pid) {
272
337
  if (!pid) return false;
273
338
  try {
274
339
  process.kill(pid, 0);
@@ -277,32 +342,37 @@ function isProcessAlive(pid) {
277
342
  return false;
278
343
  }
279
344
  }
280
- function collectTrackedBridgePids(bridgePid, statusPid) {
345
+ function collectTrackedBridgePids(bridgePid, statusPid, instanceLockPid) {
281
346
  const unique = /* @__PURE__ */ new Set();
282
- for (const pid of [bridgePid, statusPid]) {
347
+ for (const pid of [bridgePid, statusPid, instanceLockPid]) {
283
348
  if (Number.isFinite(pid) && pid > 0) {
284
349
  unique.add(pid);
285
350
  }
286
351
  }
287
352
  return [...unique];
288
353
  }
289
- function resolveTrackedBridgePid(bridgePid, statusPid, isAlive = isProcessAlive) {
354
+ function resolveTrackedBridgePid(bridgePid, statusPid, instanceLockPid, isAlive = isProcessAlive2) {
290
355
  if (isAlive(bridgePid)) return bridgePid;
291
356
  if (isAlive(statusPid)) return statusPid;
292
- return bridgePid ?? statusPid;
357
+ if (isAlive(instanceLockPid)) return instanceLockPid;
358
+ return bridgePid ?? statusPid ?? instanceLockPid;
293
359
  }
294
360
  function getTrackedBridgePids(status) {
295
- const resolvedStatus = status ?? readJsonFile(bridgeStatusFile, { running: false });
296
- return collectTrackedBridgePids(readPid(bridgePidFile), resolvedStatus.pid);
361
+ const resolvedStatus = status ?? readJsonFile2(bridgeStatusFile, { running: false });
362
+ return collectTrackedBridgePids(
363
+ readPid(bridgePidFile),
364
+ resolvedStatus.pid,
365
+ readBridgeInstanceLock()?.pid
366
+ );
297
367
  }
298
368
  function clearBridgePidFile() {
299
369
  try {
300
- fs2.unlinkSync(bridgePidFile);
370
+ fs3.unlinkSync(bridgePidFile);
301
371
  } catch {
302
372
  }
303
373
  }
304
374
  function readBridgeStartLock(filePath = bridgeStartLockFile) {
305
- const parsed = readJsonFile(filePath, null);
375
+ const parsed = readJsonFile2(filePath, null);
306
376
  const pid = Number(parsed?.pid);
307
377
  const createdAt = typeof parsed?.createdAt === "string" ? parsed.createdAt : "";
308
378
  if (!Number.isFinite(pid) || pid <= 0 || !createdAt) return null;
@@ -312,7 +382,7 @@ function isBridgeStartLockStale(lock, options = {}) {
312
382
  if (!lock) return true;
313
383
  const nowMs = options.nowMs ?? Date.now();
314
384
  const staleMs = options.staleMs ?? BRIDGE_START_LOCK_STALE_MS;
315
- const isAlive = options.isAlive ?? isProcessAlive;
385
+ const isAlive = options.isAlive ?? isProcessAlive2;
316
386
  const createdAtMs = Date.parse(lock.createdAt);
317
387
  if (!Number.isFinite(createdAtMs)) return true;
318
388
  if (!isAlive(lock.pid)) return true;
@@ -328,7 +398,7 @@ function tryAcquireBridgeStartLock(options = {}) {
328
398
  };
329
399
  for (let attempt = 0; attempt < 2; attempt += 1) {
330
400
  try {
331
- fs2.writeFileSync(filePath, JSON.stringify(payload, null, 2), { encoding: "utf-8", flag: "wx" });
401
+ fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), { encoding: "utf-8", flag: "wx" });
332
402
  return { acquired: true };
333
403
  } catch (error) {
334
404
  const code = error.code;
@@ -342,7 +412,7 @@ function tryAcquireBridgeStartLock(options = {}) {
342
412
  return { acquired: false, holderPid: existing2?.pid };
343
413
  }
344
414
  try {
345
- fs2.unlinkSync(filePath);
415
+ fs3.unlinkSync(filePath);
346
416
  } catch {
347
417
  }
348
418
  }
@@ -354,14 +424,14 @@ function releaseBridgeStartLock(filePath = bridgeStartLockFile, ownerPid = proce
354
424
  const existing = readBridgeStartLock(filePath);
355
425
  if (!existing) {
356
426
  try {
357
- fs2.unlinkSync(filePath);
427
+ fs3.unlinkSync(filePath);
358
428
  } catch {
359
429
  }
360
430
  return;
361
431
  }
362
432
  if (existing.pid !== ownerPid) return;
363
433
  try {
364
- fs2.unlinkSync(filePath);
434
+ fs3.unlinkSync(filePath);
365
435
  } catch {
366
436
  }
367
437
  }
@@ -443,11 +513,11 @@ function ensureBridgeAutostartLauncher() {
443
513
  " }",
444
514
  " } catch { }",
445
515
  "}",
446
- `& $node '${escapePowerShellSingleQuoted(path2.join(packageRoot, "dist", "cli.mjs"))}' start`,
516
+ `& $node '${escapePowerShellSingleQuoted(path3.join(packageRoot, "dist", "cli.mjs"))}' start`,
447
517
  "exit $LASTEXITCODE",
448
518
  ""
449
519
  ].join("\r\n");
450
- fs2.writeFileSync(bridgeAutostartLauncherFile, content, "utf-8");
520
+ fs3.writeFileSync(bridgeAutostartLauncherFile, content, "utf-8");
451
521
  return bridgeAutostartLauncherFile;
452
522
  }
453
523
  function parsePowerShellJson(raw) {
@@ -491,7 +561,7 @@ function buildDeferredGlobalNpmUninstallLaunch(options = {}) {
491
561
  async function launchDeferredGlobalNpmUninstall() {
492
562
  ensureDirs();
493
563
  const launch = buildDeferredGlobalNpmUninstallLaunch();
494
- fs2.writeFileSync(
564
+ fs3.writeFileSync(
495
565
  launch.logPath,
496
566
  [
497
567
  `[${(/* @__PURE__ */ new Date()).toISOString()}] Scheduling global uninstall.`,
@@ -519,14 +589,18 @@ function getUiServerUrl(port = uiPort) {
519
589
  return `http://127.0.0.1:${port}`;
520
590
  }
521
591
  function getCurrentUiServerUrl() {
522
- const status = readJsonFile(uiStatusFile, null);
592
+ const status = readJsonFile2(uiStatusFile, null);
523
593
  if (!status?.port) return void 0;
524
594
  return getUiServerUrl(status.port);
525
595
  }
526
596
  function getBridgeStatus() {
527
- const status = readJsonFile(bridgeStatusFile, { running: false });
528
- const pid = resolveTrackedBridgePid(readPid(bridgePidFile), status.pid);
529
- if (!isProcessAlive(pid)) {
597
+ const status = readJsonFile2(bridgeStatusFile, { running: false });
598
+ const pid = resolveTrackedBridgePid(
599
+ readPid(bridgePidFile),
600
+ status.pid,
601
+ readBridgeInstanceLock()?.pid
602
+ );
603
+ if (!isProcessAlive2(pid)) {
530
604
  return {
531
605
  ...status,
532
606
  pid,
@@ -540,8 +614,8 @@ function getBridgeStatus() {
540
614
  };
541
615
  }
542
616
  function getUiServerStatus() {
543
- const status = readJsonFile(uiStatusFile, { running: false, port: uiPort });
544
- if (!isProcessAlive(status.pid)) {
617
+ const status = readJsonFile2(uiStatusFile, { running: false, port: uiPort });
618
+ if (!isProcessAlive2(status.pid)) {
545
619
  return {
546
620
  ...status,
547
621
  running: false,
@@ -626,7 +700,7 @@ async function waitForUiServer(timeoutMs = 15e3) {
626
700
  async function startBridge() {
627
701
  ensureDirs();
628
702
  const current = getBridgeStatus();
629
- const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid && isProcessAlive(pid));
703
+ const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid && isProcessAlive2(pid));
630
704
  if (current.running && extraAlivePids.length === 0) return current;
631
705
  if (current.running && extraAlivePids.length > 0) {
632
706
  await stopBridge();
@@ -651,17 +725,17 @@ async function startBridge() {
651
725
  startLockHeld = true;
652
726
  try {
653
727
  const currentAfterLock = getBridgeStatus();
654
- const extraAlivePidsAfterLock = getTrackedBridgePids(currentAfterLock).filter((pid) => pid !== currentAfterLock.pid && isProcessAlive(pid));
728
+ const extraAlivePidsAfterLock = getTrackedBridgePids(currentAfterLock).filter((pid) => pid !== currentAfterLock.pid && isProcessAlive2(pid));
655
729
  if (currentAfterLock.running && extraAlivePidsAfterLock.length === 0) return currentAfterLock;
656
730
  if (currentAfterLock.running && extraAlivePidsAfterLock.length > 0) {
657
731
  await stopBridge();
658
732
  }
659
- const daemonEntry = path2.join(packageRoot, "dist", "daemon.mjs");
660
- if (!fs2.existsSync(daemonEntry)) {
733
+ const daemonEntry = path3.join(packageRoot, "dist", "daemon.mjs");
734
+ if (!fs3.existsSync(daemonEntry)) {
661
735
  throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
662
736
  }
663
- const stdoutFd = fs2.openSync(path2.join(logsDir, "bridge-launcher.out.log"), "a");
664
- const stderrFd = fs2.openSync(path2.join(logsDir, "bridge-launcher.err.log"), "a");
737
+ const stdoutFd = fs3.openSync(path3.join(logsDir, "bridge-launcher.out.log"), "a");
738
+ const stderrFd = fs3.openSync(path3.join(logsDir, "bridge-launcher.err.log"), "a");
665
739
  const child = spawn(process.execPath, [daemonEntry], {
666
740
  cwd: packageRoot,
667
741
  detached: true,
@@ -684,10 +758,11 @@ async function startBridge() {
684
758
  }
685
759
  }
686
760
  async function stopBridge() {
687
- const status = readJsonFile(bridgeStatusFile, { running: false });
688
- const pids = getTrackedBridgePids(status).filter((pid) => isProcessAlive(pid));
761
+ const status = readJsonFile2(bridgeStatusFile, { running: false });
762
+ const pids = getTrackedBridgePids(status).filter((pid) => isProcessAlive2(pid));
689
763
  if (pids.length === 0) {
690
764
  clearBridgePidFile();
765
+ clearStaleBridgeInstanceLock();
691
766
  return { ...getBridgeStatus(), running: false };
692
767
  }
693
768
  for (const pid of pids) {
@@ -709,13 +784,15 @@ async function stopBridge() {
709
784
  }
710
785
  const startedAt = Date.now();
711
786
  while (Date.now() - startedAt < 1e4) {
712
- if (pids.every((pid) => !isProcessAlive(pid))) {
787
+ if (pids.every((pid) => !isProcessAlive2(pid))) {
713
788
  clearBridgePidFile();
789
+ clearStaleBridgeInstanceLock();
714
790
  return getBridgeStatus();
715
791
  }
716
792
  await sleep(300);
717
793
  }
718
794
  clearBridgePidFile();
795
+ clearStaleBridgeInstanceLock();
719
796
  return getBridgeStatus();
720
797
  }
721
798
  async function getBridgeAutostartStatus() {
@@ -804,8 +881,8 @@ async function uninstallBridgeAutostart() {
804
881
  ].join("; ");
805
882
  await runPowerShell(script);
806
883
  try {
807
- if (fs2.existsSync(bridgeAutostartLauncherFile)) {
808
- fs2.unlinkSync(bridgeAutostartLauncherFile);
884
+ if (fs3.existsSync(bridgeAutostartLauncherFile)) {
885
+ fs3.unlinkSync(bridgeAutostartLauncherFile);
809
886
  }
810
887
  } catch {
811
888
  }
@@ -836,12 +913,12 @@ async function ensureUiServerRunning() {
836
913
  ensureDirs();
837
914
  const current = getUiServerStatus();
838
915
  if (current.running) return current;
839
- const serverEntry = path2.join(packageRoot, "dist", "ui-server.mjs");
840
- if (!fs2.existsSync(serverEntry)) {
916
+ const serverEntry = path3.join(packageRoot, "dist", "ui-server.mjs");
917
+ if (!fs3.existsSync(serverEntry)) {
841
918
  throw new Error(`UI server bundle not found at ${serverEntry}. Run npm run build first.`);
842
919
  }
843
- const stdoutFd = fs2.openSync(path2.join(logsDir, "ui-server.out.log"), "a");
844
- const stderrFd = fs2.openSync(path2.join(logsDir, "ui-server.err.log"), "a");
920
+ const stdoutFd = fs3.openSync(path3.join(logsDir, "ui-server.out.log"), "a");
921
+ const stderrFd = fs3.openSync(path3.join(logsDir, "ui-server.err.log"), "a");
845
922
  const child = spawn(process.execPath, [serverEntry], {
846
923
  cwd: packageRoot,
847
924
  detached: true,
@@ -861,7 +938,7 @@ async function ensureUiServerRunning() {
861
938
  }
862
939
  async function stopUiServer() {
863
940
  const status = getUiServerStatus();
864
- if (!status.pid || !isProcessAlive(status.pid)) {
941
+ if (!status.pid || !isProcessAlive2(status.pid)) {
865
942
  const next2 = { ...status, running: false };
866
943
  writeUiServerStatus(next2);
867
944
  return next2;
@@ -898,7 +975,7 @@ async function stopUiServer() {
898
975
  }
899
976
  function writeUiServerStatus(status) {
900
977
  ensureDirs();
901
- fs2.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
978
+ fs3.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
902
979
  }
903
980
  function openBrowser(url) {
904
981
  if (process.platform === "win32") {