@keysync/cli 0.1.131 → 0.1.133

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.
@@ -16924,6 +16924,191 @@ var require_zod = __commonJS({
16924
16924
  }
16925
16925
  });
16926
16926
 
16927
+ // apps/client-ui/electron/install-identity.cjs
16928
+ var require_install_identity = __commonJS({
16929
+ "apps/client-ui/electron/install-identity.cjs"(exports, module) {
16930
+ "use strict";
16931
+ var fs = __require("node:fs");
16932
+ var path = __require("node:path");
16933
+ var { randomBytes: randomBytes4 } = __require("node:crypto");
16934
+ function readInstall(pathname) {
16935
+ try {
16936
+ const parsed = JSON.parse(fs.readFileSync(pathname, "utf8"));
16937
+ return typeof parsed.install_id === "string" && parsed.install_id && typeof parsed.created_at === "string" && parsed.created_at ? { install_id: parsed.install_id, created_at: parsed.created_at } : null;
16938
+ } catch {
16939
+ return null;
16940
+ }
16941
+ }
16942
+ function publishCompleteFileIfAbsent(pathname, install) {
16943
+ fs.mkdirSync(path.dirname(pathname), { recursive: true, mode: 448 });
16944
+ const temporary = `${pathname}.${process.pid}.${randomBytes4(8).toString("hex")}.tmp`;
16945
+ const fd = fs.openSync(temporary, "wx", 384);
16946
+ try {
16947
+ fs.writeFileSync(fd, `${JSON.stringify(install, null, 2)}
16948
+ `);
16949
+ fs.fsyncSync(fd);
16950
+ } finally {
16951
+ fs.closeSync(fd);
16952
+ }
16953
+ try {
16954
+ fs.linkSync(temporary, pathname);
16955
+ let directoryFd = null;
16956
+ try {
16957
+ directoryFd = fs.openSync(path.dirname(pathname), "r");
16958
+ fs.fsyncSync(directoryFd);
16959
+ } catch {
16960
+ } finally {
16961
+ if (directoryFd !== null) try {
16962
+ fs.closeSync(directoryFd);
16963
+ } catch {
16964
+ }
16965
+ }
16966
+ } catch (error) {
16967
+ if (error?.code !== "EEXIST") throw error;
16968
+ } finally {
16969
+ try {
16970
+ fs.rmSync(temporary, { force: true });
16971
+ } catch {
16972
+ }
16973
+ }
16974
+ const winner = readInstall(pathname);
16975
+ if (!winner) throw Object.assign(new Error("INSTALL_STATE_UNREADABLE"), { code: "INSTALL_STATE_UNREADABLE" });
16976
+ return winner;
16977
+ }
16978
+ function ensureInstallIdentitySync2(installStateDir, options = {}) {
16979
+ const pathname = path.join(installStateDir, "install.json");
16980
+ const existing = readInstall(pathname);
16981
+ if (existing) return existing;
16982
+ for (const legacyDir of options.legacyStateDirs || []) {
16983
+ const legacy = readInstall(path.join(legacyDir, "install.json"));
16984
+ if (legacy) return publishCompleteFileIfAbsent(pathname, legacy);
16985
+ }
16986
+ if (options.legacyInstallationPath) {
16987
+ try {
16988
+ const legacy = JSON.parse(fs.readFileSync(options.legacyInstallationPath, "utf8"));
16989
+ if (typeof legacy.installation_id === "string" && legacy.installation_id) {
16990
+ return publishCompleteFileIfAbsent(pathname, { install_id: legacy.installation_id, created_at: legacy.created_at || (/* @__PURE__ */ new Date()).toISOString() });
16991
+ }
16992
+ } catch {
16993
+ }
16994
+ }
16995
+ return publishCompleteFileIfAbsent(pathname, { install_id: randomBytes4(32).toString("base64url"), created_at: (/* @__PURE__ */ new Date()).toISOString() });
16996
+ }
16997
+ module.exports = { ensureInstallIdentitySync: ensureInstallIdentitySync2, readInstall };
16998
+ }
16999
+ });
17000
+
17001
+ // apps/client-ui/electron/diagnostic-sanitize.cjs
17002
+ var require_diagnostic_sanitize = __commonJS({
17003
+ "apps/client-ui/electron/diagnostic-sanitize.cjs"(exports, module) {
17004
+ "use strict";
17005
+ var SECRET_KEY = /(?:password|token|secret|api[_-]?key|authorization|cookie|private[_-]?key|recovery[_-]?key)/i;
17006
+ var USER_PATH = /(?:[A-Za-z]:\\Users\\[^\\\s"'`]+|\/Users\/[^/\s"'`]+|\/home\/[^/\s"'`]+)/gi;
17007
+ var MAX_COLLECTION_ITEMS = 50;
17008
+ var MAX_DEPTH = 8;
17009
+ var MAX_NODES = 256;
17010
+ var MAX_ENTRIES = 256;
17011
+ var MAX_TOTAL_TEXT_BYTES = 24 * 1024;
17012
+ var MAX_KEY_LENGTH = 64;
17013
+ var MAX_PAYLOAD_BYTES = 48 * 1024;
17014
+ function sanitizeDiagnosticText2(value) {
17015
+ return String(value ?? "").replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [REDACTED]").replace(/\bsk-[A-Za-z0-9_-]{4,}\b/gi, "[REDACTED]").replace(/((?:password|token|secret|api[_-]?key|authorization|cookie|private[_-]?key|recovery[_-]?key)\s*[:=]\s*)[^,\s}]+/gi, "$1[REDACTED]").replace(USER_PATH, "[REDACTED_USER_DIR]").slice(0, 4096);
17016
+ }
17017
+ function boundedUtf8(value, state) {
17018
+ const text = sanitizeDiagnosticText2(value);
17019
+ const remaining = Math.max(0, (state.maxTextBytes ?? MAX_TOTAL_TEXT_BYTES) - state.textBytes);
17020
+ if (Buffer.byteLength(text, "utf8") <= remaining) {
17021
+ state.textBytes += Buffer.byteLength(text, "utf8");
17022
+ return text;
17023
+ }
17024
+ state.truncated = true;
17025
+ let low = 0;
17026
+ let high = text.length;
17027
+ while (low < high) {
17028
+ const middle = Math.ceil((low + high) / 2);
17029
+ if (Buffer.byteLength(text.slice(0, middle), "utf8") <= remaining) low = middle;
17030
+ else high = middle - 1;
17031
+ }
17032
+ const bounded = text.slice(0, low);
17033
+ state.textBytes += Buffer.byteLength(bounded, "utf8");
17034
+ return bounded;
17035
+ }
17036
+ function safeDiagnosticKey(value, index) {
17037
+ const key = String(value ?? "");
17038
+ return /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(key) ? key.slice(0, MAX_KEY_LENGTH) : `diagnostic_field_${index}`;
17039
+ }
17040
+ function sanitizeDiagnosticValue(value, key = "", seen = /* @__PURE__ */ new WeakSet(), depth = 0, state = { nodes: 0, entries: 0, textBytes: 0, truncated: false }) {
17041
+ if (SECRET_KEY.test(key)) return "[REDACTED]";
17042
+ if (typeof value === "string") return boundedUtf8(value, state);
17043
+ if (typeof value === "number" || typeof value === "boolean" || value === null) return value;
17044
+ if (!value || typeof value !== "object") return void 0;
17045
+ state.nodes += 1;
17046
+ if (state.nodes > MAX_NODES || depth >= MAX_DEPTH || seen.has(value)) {
17047
+ state.truncated = true;
17048
+ return "[REDACTED_COMPLEX_VALUE]";
17049
+ }
17050
+ seen.add(value);
17051
+ if (Array.isArray(value)) {
17052
+ if (value.length > MAX_COLLECTION_ITEMS) state.truncated = true;
17053
+ const result2 = [];
17054
+ for (const item of value.slice(0, MAX_COLLECTION_ITEMS)) {
17055
+ if (state.entries >= MAX_ENTRIES) {
17056
+ state.truncated = true;
17057
+ break;
17058
+ }
17059
+ state.entries += 1;
17060
+ result2.push(sanitizeDiagnosticValue(item, "", seen, depth + 1, state));
17061
+ }
17062
+ return result2;
17063
+ }
17064
+ const priority = new Map(["client_event_id", "installation_id", "identity_epoch", "identity_proof", "domain", "action", "status", "error_code", "message", "occurred_at", "client_version", "platform", "arch", "details", "failure_stage"].map((name, index) => [name, index]));
17065
+ const entries = Object.entries(value).sort(([left], [right]) => (priority.get(left) ?? 100) - (priority.get(right) ?? 100));
17066
+ if (entries.length > MAX_COLLECTION_ITEMS) state.truncated = true;
17067
+ const result = {};
17068
+ entries.slice(0, MAX_COLLECTION_ITEMS).forEach(([childKey, childValue], index) => {
17069
+ if (state.entries >= MAX_ENTRIES) {
17070
+ state.truncated = true;
17071
+ return;
17072
+ }
17073
+ state.entries += 1;
17074
+ const safeKey = safeDiagnosticKey(childKey, index);
17075
+ if (Object.prototype.hasOwnProperty.call(result, safeKey)) state.truncated = true;
17076
+ else result[safeKey] = sanitizeDiagnosticValue(childValue, childKey, seen, depth + 1, state);
17077
+ });
17078
+ return result;
17079
+ }
17080
+ function sanitizeDiagnosticPayload2(value) {
17081
+ const state = { nodes: 0, entries: 0, textBytes: 0, truncated: false };
17082
+ const sanitized = sanitizeDiagnosticValue(value, "", /* @__PURE__ */ new WeakSet(), 0, state);
17083
+ if (state.truncated && sanitized && typeof sanitized === "object" && !Array.isArray(sanitized)) {
17084
+ if (sanitized.details && typeof sanitized.details === "object" && !Array.isArray(sanitized.details)) sanitized.details.diagnostic_payload_truncated = true;
17085
+ else sanitized.diagnostic_payload_truncated = true;
17086
+ }
17087
+ if (Buffer.byteLength(JSON.stringify(sanitized), "utf8") >= MAX_PAYLOAD_BYTES) return compactDiagnosticPayload2(sanitized);
17088
+ return sanitized;
17089
+ }
17090
+ function compactDiagnosticPayload2(value) {
17091
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
17092
+ const rootKeys = ["client_event_id", "installation_id", "identity_epoch", "identity_proof", "device_id", "domain", "action", "status", "error_code", "message", "occurred_at", "client_version", "platform", "arch", "failure_stage"];
17093
+ const detailKeys = ["failure_stage", "operation_id", "caused_by_operation_id", "control_id", "label_key", "context_id", "view_id", "field_id", "interaction", "outcome", "state", "decision", "validation", "phase", "reason", "os_error_code", "error_name", "prompt_state", "client_event_seq", "session_id", "run_id", "test_run_id", "provider_type", "endpoint_protocol", "device_filter", "desktop_app", "selected_locale", "import_change", "identity_quarantined", "delivery_quarantined", "diagnostic_ipc_fallback", "diagnostic_payload_truncated"];
17094
+ const state = { nodes: 0, entries: 0, textBytes: 0, maxTextBytes: 8 * 1024, truncated: true };
17095
+ const compacted = {};
17096
+ for (const key of rootKeys) {
17097
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
17098
+ compacted[key] = sanitizeDiagnosticValue(value[key], key, /* @__PURE__ */ new WeakSet(), 0, state);
17099
+ }
17100
+ const sourceDetails = value.details && typeof value.details === "object" && !Array.isArray(value.details) ? value.details : {};
17101
+ compacted.details = { payload_compacted: true };
17102
+ for (const key of detailKeys) {
17103
+ if (!Object.prototype.hasOwnProperty.call(sourceDetails, key)) continue;
17104
+ compacted.details[key] = sanitizeDiagnosticValue(sourceDetails[key], key, /* @__PURE__ */ new WeakSet(), 0, state);
17105
+ }
17106
+ return compacted;
17107
+ }
17108
+ module.exports = { sanitizeDiagnosticText: sanitizeDiagnosticText2, sanitizeDiagnosticValue, sanitizeDiagnosticPayload: sanitizeDiagnosticPayload2, compactDiagnosticPayload: compactDiagnosticPayload2 };
17109
+ }
17110
+ });
17111
+
16927
17112
  // apps/client-ui/electron/dsh-plugin-manager-release.cjs
16928
17113
  var require_dsh_plugin_manager_release = __commonJS({
16929
17114
  "apps/client-ui/electron/dsh-plugin-manager-release.cjs"(exports, module) {
@@ -17210,8 +17395,8 @@ var require_managed_dsh = __commonJS({
17210
17395
  }
17211
17396
  function readBoundedJson(filePath, maxBytes, fallback = null) {
17212
17397
  try {
17213
- const stat6 = fs.statSync(filePath);
17214
- if (!stat6.isFile() || stat6.size > maxBytes) return fallback;
17398
+ const stat7 = fs.statSync(filePath);
17399
+ if (!stat7.isFile() || stat7.size > maxBytes) return fallback;
17215
17400
  return JSON.parse(fs.readFileSync(filePath, "utf8"));
17216
17401
  } catch {
17217
17402
  return fallback;
@@ -17263,8 +17448,8 @@ var require_managed_dsh = __commonJS({
17263
17448
  };
17264
17449
  }
17265
17450
  function assertManagedChild(root, target) {
17266
- const relative2 = path.relative(path.resolve(root), path.resolve(target));
17267
- if (!relative2 || relative2.startsWith("..") || path.isAbsolute(relative2)) {
17451
+ const relative = path.relative(path.resolve(root), path.resolve(target));
17452
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
17268
17453
  throw new ManagedDshError("unsafe_path", "Managed DSH operation resolved outside its owned directory.");
17269
17454
  }
17270
17455
  }
@@ -17355,7 +17540,7 @@ var require_managed_dsh = __commonJS({
17355
17540
  return edition.runtime.artifacts[`${platform3}-${arch2}`] ?? null;
17356
17541
  }
17357
17542
  function wait(ms) {
17358
- return new Promise((resolve2) => setTimeout(resolve2, ms));
17543
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
17359
17544
  }
17360
17545
  function createManagedDshManager(options) {
17361
17546
  const bundledEdition = options.edition ?? OFFICIAL_DSH_EDITION;
@@ -17715,15 +17900,15 @@ var require_managed_dsh = __commonJS({
17715
17900
  }
17716
17901
  async function waitForAuthenticatedUrl(timeoutMs) {
17717
17902
  if (runningAuthenticatedUrl) return runningAuthenticatedUrl;
17718
- return new Promise((resolve2) => {
17903
+ return new Promise((resolve3) => {
17719
17904
  const timer = setTimeout(() => {
17720
17905
  if (authenticatedUrlWaiter === finish) authenticatedUrlWaiter = null;
17721
- resolve2(null);
17906
+ resolve3(null);
17722
17907
  }, timeoutMs);
17723
17908
  timer.unref?.();
17724
17909
  const finish = (url) => {
17725
17910
  clearTimeout(timer);
17726
- resolve2(url);
17911
+ resolve3(url);
17727
17912
  };
17728
17913
  authenticatedUrlWaiter = finish;
17729
17914
  });
@@ -18008,7 +18193,7 @@ var require_managed_dsh = __commonJS({
18008
18193
  if (testHooks.runProcess) {
18009
18194
  return testHooks.runProcess(command, args, { ...runOptions, signal: activeOperation?.controller.signal ?? null });
18010
18195
  }
18011
- return new Promise((resolve2, reject) => {
18196
+ return new Promise((resolve3, reject) => {
18012
18197
  let stdout = "";
18013
18198
  let stderr = "";
18014
18199
  let settled = false;
@@ -18045,7 +18230,7 @@ var require_managed_dsh = __commonJS({
18045
18230
  boundedCommandFailureDetail(stderr, code)
18046
18231
  ));
18047
18232
  }
18048
- resolve2({ stdout, stderr, code });
18233
+ resolve3({ stdout, stderr, code });
18049
18234
  });
18050
18235
  });
18051
18236
  }
@@ -18574,11 +18759,11 @@ var require_managed_dsh = __commonJS({
18574
18759
  async function findAvailablePort() {
18575
18760
  if (testHooks.findAvailablePort) return testHooks.findAvailablePort();
18576
18761
  for (let port = 3080; port <= 3180; port += 1) {
18577
- const available = await new Promise((resolve2) => {
18762
+ const available = await new Promise((resolve3) => {
18578
18763
  const server = net.createServer();
18579
18764
  server.unref();
18580
- server.once("error", () => resolve2(false));
18581
- server.listen({ host: "127.0.0.1", port, exclusive: true }, () => server.close(() => resolve2(true)));
18765
+ server.once("error", () => resolve3(false));
18766
+ server.listen({ host: "127.0.0.1", port, exclusive: true }, () => server.close(() => resolve3(true)));
18582
18767
  });
18583
18768
  if (available) return port;
18584
18769
  }
@@ -19006,7 +19191,7 @@ var require_managed_dsh = __commonJS({
19006
19191
  function windowIsUsable() {
19007
19192
  return dshWindow && !(typeof dshWindow.isDestroyed === "function" && dshWindow.isDestroyed());
19008
19193
  }
19009
- async function open() {
19194
+ async function open2() {
19010
19195
  const status = await start();
19011
19196
  if (windowIsUsable()) {
19012
19197
  if (typeof dshWindow.show === "function") dshWindow.show();
@@ -19111,7 +19296,7 @@ var require_managed_dsh = __commonJS({
19111
19296
  cancelInstall,
19112
19297
  start,
19113
19298
  stop,
19114
- open,
19299
+ open: open2,
19115
19300
  openDataDirectory,
19116
19301
  removeAllPlugins,
19117
19302
  uninstall,
@@ -24207,11 +24392,11 @@ function __metadata(metadataKey, metadataValue) {
24207
24392
  }
24208
24393
  function __awaiter(thisArg, _arguments, P, generator) {
24209
24394
  function adopt(value) {
24210
- return value instanceof P ? value : new P(function(resolve2) {
24211
- resolve2(value);
24395
+ return value instanceof P ? value : new P(function(resolve3) {
24396
+ resolve3(value);
24212
24397
  });
24213
24398
  }
24214
- return new (P || (P = Promise))(function(resolve2, reject) {
24399
+ return new (P || (P = Promise))(function(resolve3, reject) {
24215
24400
  function fulfilled(value) {
24216
24401
  try {
24217
24402
  step(generator.next(value));
@@ -24227,7 +24412,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
24227
24412
  }
24228
24413
  }
24229
24414
  function step(result) {
24230
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
24415
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
24231
24416
  }
24232
24417
  step((generator = generator.apply(thisArg, _arguments || [])).next());
24233
24418
  });
@@ -24418,14 +24603,14 @@ function __asyncValues(o) {
24418
24603
  }, i);
24419
24604
  function verb(n) {
24420
24605
  i[n] = o[n] && function(v) {
24421
- return new Promise(function(resolve2, reject) {
24422
- v = o[n](v), settle(resolve2, reject, v.done, v.value);
24606
+ return new Promise(function(resolve3, reject) {
24607
+ v = o[n](v), settle(resolve3, reject, v.done, v.value);
24423
24608
  });
24424
24609
  };
24425
24610
  }
24426
- function settle(resolve2, reject, d, v) {
24611
+ function settle(resolve3, reject, d, v) {
24427
24612
  Promise.resolve(v).then(function(v2) {
24428
- resolve2({ value: v2, done: d });
24613
+ resolve3({ value: v2, done: d });
24429
24614
  }, reject);
24430
24615
  }
24431
24616
  }
@@ -31594,11 +31779,11 @@ function __metadata2(metadataKey, metadataValue) {
31594
31779
  }
31595
31780
  function __awaiter2(thisArg, _arguments, P, generator) {
31596
31781
  function adopt(value) {
31597
- return value instanceof P ? value : new P(function(resolve2) {
31598
- resolve2(value);
31782
+ return value instanceof P ? value : new P(function(resolve3) {
31783
+ resolve3(value);
31599
31784
  });
31600
31785
  }
31601
- return new (P || (P = Promise))(function(resolve2, reject) {
31786
+ return new (P || (P = Promise))(function(resolve3, reject) {
31602
31787
  function fulfilled(value) {
31603
31788
  try {
31604
31789
  step(generator.next(value));
@@ -31614,7 +31799,7 @@ function __awaiter2(thisArg, _arguments, P, generator) {
31614
31799
  }
31615
31800
  }
31616
31801
  function step(result) {
31617
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
31802
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
31618
31803
  }
31619
31804
  step((generator = generator.apply(thisArg, _arguments || [])).next());
31620
31805
  });
@@ -31792,14 +31977,14 @@ function __asyncValues2(o) {
31792
31977
  }, i);
31793
31978
  function verb(n) {
31794
31979
  i[n] = o[n] && function(v) {
31795
- return new Promise(function(resolve2, reject) {
31796
- v = o[n](v), settle(resolve2, reject, v.done, v.value);
31980
+ return new Promise(function(resolve3, reject) {
31981
+ v = o[n](v), settle(resolve3, reject, v.done, v.value);
31797
31982
  });
31798
31983
  };
31799
31984
  }
31800
- function settle(resolve2, reject, d, v) {
31985
+ function settle(resolve3, reject, d, v) {
31801
31986
  Promise.resolve(v).then(function(v2) {
31802
- resolve2({ value: v2, done: d });
31987
+ resolve3({ value: v2, done: d });
31803
31988
  }, reject);
31804
31989
  }
31805
31990
  }
@@ -48184,12 +48369,12 @@ var require_form_data = __commonJS({
48184
48369
  if (value.end != void 0 && value.end != Infinity && value.start != void 0) {
48185
48370
  callback(null, value.end + 1 - (value.start ? value.start : 0));
48186
48371
  } else {
48187
- fs.stat(value.path, function(err, stat6) {
48372
+ fs.stat(value.path, function(err, stat7) {
48188
48373
  if (err) {
48189
48374
  callback(err);
48190
48375
  return;
48191
48376
  }
48192
- var fileSize = stat6.size - (value.start ? value.start : 0);
48377
+ var fileSize = stat7.size - (value.start ? value.start : 0);
48193
48378
  callback(null, fileSize);
48194
48379
  });
48195
48380
  }
@@ -48433,12 +48618,12 @@ var require_promisify = __commonJS({
48433
48618
  Object.defineProperty(exports, "__esModule", { value: true });
48434
48619
  function promisify4(fn) {
48435
48620
  return function(req, opts) {
48436
- return new Promise((resolve2, reject) => {
48621
+ return new Promise((resolve3, reject) => {
48437
48622
  fn.call(this, req, opts, (err, rtn) => {
48438
48623
  if (err) {
48439
48624
  reject(err);
48440
48625
  } else {
48441
- resolve2(rtn);
48626
+ resolve3(rtn);
48442
48627
  }
48443
48628
  });
48444
48629
  });
@@ -48642,7 +48827,7 @@ var require_parse_proxy_response = __commonJS({
48642
48827
  var debug_1 = __importDefault3(require_src());
48643
48828
  var debug = debug_1.default("https-proxy-agent:parse-proxy-response");
48644
48829
  function parseProxyResponse(socket) {
48645
- return new Promise((resolve2, reject) => {
48830
+ return new Promise((resolve3, reject) => {
48646
48831
  let buffersLength = 0;
48647
48832
  const buffers = [];
48648
48833
  function read() {
@@ -48682,7 +48867,7 @@ var require_parse_proxy_response = __commonJS({
48682
48867
  const firstLine = buffered.toString("ascii", 0, buffered.indexOf("\r\n"));
48683
48868
  const statusCode = +firstLine.split(" ")[1];
48684
48869
  debug("got proxy server response: %o", firstLine);
48685
- resolve2({
48870
+ resolve3({
48686
48871
  statusCode,
48687
48872
  buffered
48688
48873
  });
@@ -48703,11 +48888,11 @@ var require_agent = __commonJS({
48703
48888
  "use strict";
48704
48889
  var __awaiter3 = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
48705
48890
  function adopt(value) {
48706
- return value instanceof P ? value : new P(function(resolve2) {
48707
- resolve2(value);
48891
+ return value instanceof P ? value : new P(function(resolve3) {
48892
+ resolve3(value);
48708
48893
  });
48709
48894
  }
48710
- return new (P || (P = Promise))(function(resolve2, reject) {
48895
+ return new (P || (P = Promise))(function(resolve3, reject) {
48711
48896
  function fulfilled(value) {
48712
48897
  try {
48713
48898
  step(generator.next(value));
@@ -48723,7 +48908,7 @@ var require_agent = __commonJS({
48723
48908
  }
48724
48909
  }
48725
48910
  function step(result) {
48726
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
48911
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
48727
48912
  }
48728
48913
  step((generator = generator.apply(thisArg, _arguments || [])).next());
48729
48914
  });
@@ -49314,8 +49499,8 @@ var require_follow_redirects = __commonJS({
49314
49499
  }
49315
49500
  return parsed;
49316
49501
  }
49317
- function resolveUrl(relative2, base) {
49318
- return useNativeURL ? new URL2(relative2, base) : parseUrl(url.resolve(base, relative2));
49502
+ function resolveUrl(relative, base) {
49503
+ return useNativeURL ? new URL2(relative, base) : parseUrl(url.resolve(base, relative));
49319
49504
  }
49320
49505
  function validateUrl(input) {
49321
49506
  if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -50870,10 +51055,10 @@ var require_axios = __commonJS({
50870
51055
  this.__CANCEL__ = true;
50871
51056
  }
50872
51057
  };
50873
- function settle(resolve2, reject, response) {
51058
+ function settle(resolve3, reject, response) {
50874
51059
  const validateStatus = response.config.validateStatus;
50875
51060
  if (!response.status || !validateStatus || validateStatus(response.status)) {
50876
- resolve2(response);
51061
+ resolve3(response);
50877
51062
  } else {
50878
51063
  reject(new AxiosError("Request failed with status code " + response.status, response.status >= 400 && response.status < 500 ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE, response.config, response.request, response));
50879
51064
  }
@@ -51501,12 +51686,12 @@ var require_axios = __commonJS({
51501
51686
  };
51502
51687
  }
51503
51688
  function throttle(fn, freq) {
51504
- let timestamp3 = 0;
51689
+ let timestamp2 = 0;
51505
51690
  let threshold = 1e3 / freq;
51506
51691
  let lastArgs;
51507
51692
  let timer;
51508
51693
  const invoke = (args, now = Date.now()) => {
51509
- timestamp3 = now;
51694
+ timestamp2 = now;
51510
51695
  lastArgs = null;
51511
51696
  if (timer) {
51512
51697
  clearTimeout(timer);
@@ -51516,7 +51701,7 @@ var require_axios = __commonJS({
51516
51701
  };
51517
51702
  const throttled = (...args) => {
51518
51703
  const now = Date.now();
51519
- const passed = now - timestamp3;
51704
+ const passed = now - timestamp2;
51520
51705
  if (passed >= threshold) {
51521
51706
  invoke(args, now);
51522
51707
  } else {
@@ -51855,7 +52040,7 @@ var require_axios = __commonJS({
51855
52040
  }
51856
52041
  var isHttpAdapterSupported = typeof process !== "undefined" && utils$1.kindOf(process) === "process";
51857
52042
  var wrapAsync = (asyncExecutor) => {
51858
- return new Promise((resolve2, reject) => {
52043
+ return new Promise((resolve3, reject) => {
51859
52044
  let onDone;
51860
52045
  let isDone;
51861
52046
  const done = (value, isRejected) => {
@@ -51865,7 +52050,7 @@ var require_axios = __commonJS({
51865
52050
  };
51866
52051
  const _resolve = (value) => {
51867
52052
  done(value);
51868
- resolve2(value);
52053
+ resolve3(value);
51869
52054
  };
51870
52055
  const _reject = (reason) => {
51871
52056
  done(reason, true);
@@ -51926,7 +52111,7 @@ var require_axios = __commonJS({
51926
52111
  }
51927
52112
  };
51928
52113
  var httpAdapter = isHttpAdapterSupported && function httpAdapter2(config) {
51929
- return wrapAsync(async function dispatchHttpRequest(resolve2, reject, onDone) {
52114
+ return wrapAsync(async function dispatchHttpRequest(resolve3, reject, onDone) {
51930
52115
  const own2 = (key) => utils$1.getSafeProp(config, key);
51931
52116
  const transitional = own2("transitional") || transitionalDefaults;
51932
52117
  let data = own2("data");
@@ -52040,7 +52225,7 @@ var require_axios = __commonJS({
52040
52225
  }
52041
52226
  let convertedData;
52042
52227
  if (method !== "GET") {
52043
- return settle(resolve2, reject, {
52228
+ return settle(resolve3, reject, {
52044
52229
  status: 405,
52045
52230
  statusText: "method not allowed",
52046
52231
  headers: {},
@@ -52062,7 +52247,7 @@ var require_axios = __commonJS({
52062
52247
  } else if (responseType === "stream") {
52063
52248
  convertedData = stream.Readable.from(convertedData);
52064
52249
  }
52065
- return settle(resolve2, reject, {
52250
+ return settle(resolve3, reject, {
52066
52251
  data: convertedData,
52067
52252
  status: 200,
52068
52253
  statusText: "OK",
@@ -52336,7 +52521,7 @@ var require_axios = __commonJS({
52336
52521
  });
52337
52522
  }
52338
52523
  response.data = responseStream;
52339
- settle(resolve2, reject, response);
52524
+ settle(resolve3, reject, response);
52340
52525
  } else {
52341
52526
  const responseBuffer = [];
52342
52527
  let totalResponseBytes = 0;
@@ -52374,7 +52559,7 @@ var require_axios = __commonJS({
52374
52559
  } catch (err) {
52375
52560
  return reject(AxiosError.from(err, null, config, response.request, response));
52376
52561
  }
52377
- settle(resolve2, reject, response);
52562
+ settle(resolve3, reject, response);
52378
52563
  });
52379
52564
  }
52380
52565
  abortEmitter.once("abort", (err) => {
@@ -52706,7 +52891,7 @@ var require_axios = __commonJS({
52706
52891
  }
52707
52892
  var isXHRAdapterSupported = typeof XMLHttpRequest !== "undefined";
52708
52893
  var xhrAdapter = isXHRAdapterSupported && function(config) {
52709
- return new Promise(function dispatchXhrRequest(resolve2, reject) {
52894
+ return new Promise(function dispatchXhrRequest(resolve3, reject) {
52710
52895
  const _config = resolveConfig(config);
52711
52896
  let requestData = _config.data;
52712
52897
  const requestHeaders = AxiosHeaders.from(_config.headers).normalize();
@@ -52742,7 +52927,7 @@ var require_axios = __commonJS({
52742
52927
  request
52743
52928
  };
52744
52929
  settle(function _resolve(value) {
52745
- resolve2(value);
52930
+ resolve3(value);
52746
52931
  done();
52747
52932
  }, function _reject(err) {
52748
52933
  reject(err);
@@ -53240,8 +53425,8 @@ var require_axios = __commonJS({
53240
53425
  }
53241
53426
  }
53242
53427
  !isStreamResponse && unsubscribe && unsubscribe();
53243
- return await new Promise((resolve2, reject) => {
53244
- settle(resolve2, reject, {
53428
+ return await new Promise((resolve3, reject) => {
53429
+ settle(resolve3, reject, {
53245
53430
  data: responseData,
53246
53431
  headers: AxiosHeaders.from(response.headers),
53247
53432
  status: response.status,
@@ -53652,8 +53837,8 @@ var require_axios = __commonJS({
53652
53837
  throw new TypeError("executor must be a function.");
53653
53838
  }
53654
53839
  let resolvePromise;
53655
- this.promise = new Promise(function promiseExecutor(resolve2) {
53656
- resolvePromise = resolve2;
53840
+ this.promise = new Promise(function promiseExecutor(resolve3) {
53841
+ resolvePromise = resolve3;
53657
53842
  });
53658
53843
  const token = this;
53659
53844
  this.promise.then((cancel) => {
@@ -53666,9 +53851,9 @@ var require_axios = __commonJS({
53666
53851
  });
53667
53852
  this.promise.then = (onfulfilled) => {
53668
53853
  let _resolve;
53669
- const promise = new Promise((resolve2) => {
53670
- token.subscribe(resolve2);
53671
- _resolve = resolve2;
53854
+ const promise = new Promise((resolve3) => {
53855
+ token.subscribe(resolve3);
53856
+ _resolve = resolve3;
53672
53857
  }).then(onfulfilled);
53673
53858
  promise.cancel = function reject() {
53674
53859
  token.unsubscribe(_resolve);
@@ -53898,8 +54083,8 @@ var require_util2 = __commonJS({
53898
54083
  }
53899
54084
  const duration = backoff.duration();
53900
54085
  log(`Promise rejected attempt #${backoff.attempts}, retrying in ${duration}ms: ${e.message}`);
53901
- await new Promise((resolve2) => {
53902
- setTimeout(resolve2, duration);
54086
+ await new Promise((resolve3) => {
54087
+ setTimeout(resolve3, duration);
53903
54088
  });
53904
54089
  return retryPromise(fn, attempts, backoff);
53905
54090
  }
@@ -54004,7 +54189,7 @@ var require_util2 = __commonJS({
54004
54189
  return resolver;
54005
54190
  }
54006
54191
  async function retrieveTlsAlpnCertificate(host, port, timeout = 3e4) {
54007
- return new Promise((resolve2, reject) => {
54192
+ return new Promise((resolve3, reject) => {
54008
54193
  let result;
54009
54194
  const socket = tls.connect({
54010
54195
  host,
@@ -54027,7 +54212,7 @@ var require_util2 = __commonJS({
54027
54212
  });
54028
54213
  socket.on("end", () => {
54029
54214
  if (result) {
54030
- return resolve2(result.toString());
54215
+ return resolve3(result.toString());
54031
54216
  }
54032
54217
  return reject(new Error("TLS ALPN lookup failed to retrieve certificate"));
54033
54218
  });
@@ -54168,8 +54353,8 @@ var require_axios2 = __commonJS({
54168
54353
  retryAfter = retryDefaultDelay * config.retryAttempt;
54169
54354
  log(`Unable to locate or parse retry-after response header, waiting ${retryAfter} seconds`);
54170
54355
  }
54171
- await new Promise((resolve2) => {
54172
- setTimeout(resolve2, retryAfter * 1e3);
54356
+ await new Promise((resolve3) => {
54357
+ setTimeout(resolve3, retryAfter * 1e3);
54173
54358
  });
54174
54359
  return instance(config);
54175
54360
  }
@@ -72851,9 +73036,9 @@ var require_pkcs7 = __commonJS({
72851
73036
  var jan_1_2050 = /* @__PURE__ */ new Date("2050-01-01T00:00:00Z");
72852
73037
  var date = attr.value;
72853
73038
  if (typeof date === "string") {
72854
- var timestamp3 = Date.parse(date);
72855
- if (!isNaN(timestamp3)) {
72856
- date = new Date(timestamp3);
73039
+ var timestamp2 = Date.parse(date);
73040
+ if (!isNaN(timestamp2)) {
73041
+ date = new Date(timestamp2);
72857
73042
  } else if (date.length === 13) {
72858
73043
  date = asn1.utcTimeToDate(date);
72859
73044
  } else {
@@ -73934,7 +74119,7 @@ var require_remote_dsh = __commonJS({
73934
74119
  return claims;
73935
74120
  }
73936
74121
  function readJsonBody(request, maxBytes = 64 * 1024) {
73937
- return new Promise((resolve2, reject) => {
74122
+ return new Promise((resolve3, reject) => {
73938
74123
  const chunks = [];
73939
74124
  let size = 0;
73940
74125
  request.on("data", (chunk) => {
@@ -73948,7 +74133,7 @@ var require_remote_dsh = __commonJS({
73948
74133
  });
73949
74134
  request.once("end", () => {
73950
74135
  try {
73951
- resolve2(JSON.parse(Buffer.concat(chunks).toString("utf8")));
74136
+ resolve3(JSON.parse(Buffer.concat(chunks).toString("utf8")));
73952
74137
  } catch {
73953
74138
  reject(new Error("invalid_json"));
73954
74139
  }
@@ -74202,7 +74387,7 @@ var require_remote_dsh = __commonJS({
74202
74387
  usedGrants,
74203
74388
  async start() {
74204
74389
  if (address) return address;
74205
- await new Promise((resolve2, reject) => server.once("error", reject).listen({ host: "127.0.0.1", port: 0, exclusive: true }, resolve2));
74390
+ await new Promise((resolve3, reject) => server.once("error", reject).listen({ host: "127.0.0.1", port: 0, exclusive: true }, resolve3));
74206
74391
  address = server.address();
74207
74392
  return address;
74208
74393
  },
@@ -74210,7 +74395,7 @@ var require_remote_dsh = __commonJS({
74210
74395
  sessions.clear();
74211
74396
  usedGrants.clear();
74212
74397
  if (!address) return;
74213
- await new Promise((resolve2) => server.close(resolve2));
74398
+ await new Promise((resolve3) => server.close(resolve3));
74214
74399
  address = null;
74215
74400
  }
74216
74401
  };
@@ -74789,13 +74974,13 @@ ${registrationNonce}`);
74789
74974
  const socket = tunnel;
74790
74975
  tunnel = null;
74791
74976
  if (socket && !socket.destroyed) {
74792
- await new Promise((resolve2) => {
74977
+ await new Promise((resolve3) => {
74793
74978
  let settled = false;
74794
74979
  const finish = () => {
74795
74980
  if (settled) return;
74796
74981
  settled = true;
74797
74982
  clearTimeout(timer);
74798
- resolve2();
74983
+ resolve3();
74799
74984
  };
74800
74985
  const timer = setTimeout(() => {
74801
74986
  socket.destroy();
@@ -74854,11 +75039,11 @@ ${registrationNonce}`);
74854
75039
 
74855
75040
  // apps/cli/src/index.ts
74856
75041
  import { execFile as execFile4, spawn as spawn3 } from "node:child_process";
74857
- import { createHash as createHash7 } from "node:crypto";
75042
+ import { createHash as createHash7, randomBytes as randomBytes3 } from "node:crypto";
74858
75043
  import { closeSync as closeSync2, existsSync as existsSync4, openSync as openSync2, readFileSync as readFileSync2 } from "node:fs";
74859
75044
  import { arch, hostname as hostname2, platform as platform2, tmpdir } from "node:os";
74860
- import { appendFile as appendFile3, chmod as chmod5, copyFile as copyFile3, cp as cp2, link, mkdir as mkdir6, mkdtemp, readdir as readdir3, readFile as readFile9, rm as rm6, stat as stat5, writeFile as writeFile6 } from "node:fs/promises";
74861
- import { basename as basename3, dirname as dirname6, join as join9 } from "node:path";
75045
+ import { appendFile as appendFile3, chmod as chmod5, copyFile as copyFile2, cp as cp2, link, mkdir as mkdir6, mkdtemp, readdir as readdir3, readFile as readFile10, rm as rm6, stat as stat6, writeFile as writeFile6 } from "node:fs/promises";
75046
+ import { basename as basename2, dirname as dirname6, join as join10 } from "node:path";
74862
75047
  import { createInterface } from "node:readline/promises";
74863
75048
  import { fileURLToPath as fileURLToPath2 } from "node:url";
74864
75049
  import { promisify as promisify3 } from "node:util";
@@ -74875,6 +75060,21 @@ var ApiError = class extends Error {
74875
75060
  code;
74876
75061
  details;
74877
75062
  };
75063
+ var apiFailureObserver = null;
75064
+ function notifyApiFailure(event) {
75065
+ try {
75066
+ void Promise.resolve(apiFailureObserver?.(event)).catch(() => void 0);
75067
+ } catch {
75068
+ }
75069
+ }
75070
+ function diagnosticPath(path) {
75071
+ try {
75072
+ const parsed = /^https?:\/\//i.test(path) ? new URL(path) : new URL(path.replace(/^\/+/, ""), "http://keysync.local/");
75073
+ return parsed.pathname;
75074
+ } catch {
75075
+ return "invalid-path";
75076
+ }
75077
+ }
74878
75078
  var KEYSYNC_CLIENT_CAPABILITIES = "deepseek_harness_managed_agent_v1,deepseek_harness_dynamic_release_v1,remote_dsh_tls_tunnel_v1,default_provider_credit_v1";
74879
75079
  var KeySyncApiClient = class {
74880
75080
  constructor(options) {
@@ -74917,15 +75117,34 @@ var KeySyncApiClient = class {
74917
75117
  signal: controller?.signal
74918
75118
  });
74919
75119
  } catch (error) {
74920
- if (controller?.signal.aborted) throw new ApiError(0, "REQUEST_TIMEOUT", `Request timed out after ${timeoutMs}ms`);
74921
- throw new ApiError(0, "NETWORK_ERROR", "Cannot reach the backend server");
75120
+ const apiError = controller?.signal.aborted ? new ApiError(0, "REQUEST_TIMEOUT", `Request timed out after ${timeoutMs}ms`) : new ApiError(0, "NETWORK_ERROR", "Cannot reach the backend server");
75121
+ notifyApiFailure({ method, path: diagnosticPath(path), status: apiError.status, code: apiError.code, message: apiError.message, failure_stage: "request" });
75122
+ throw apiError;
74922
75123
  } finally {
74923
75124
  if (timeout) clearTimeout(timeout);
74924
75125
  }
74925
- const payload = await response.json().catch(() => ({}));
75126
+ const rawResponse = await response.text();
75127
+ let payload = {};
75128
+ if (rawResponse.trim()) {
75129
+ try {
75130
+ payload = JSON.parse(rawResponse);
75131
+ } catch {
75132
+ if (response.ok) {
75133
+ const apiError = new ApiError(response.status, "RESPONSE_PARSE_ERROR", "Backend returned an invalid JSON response");
75134
+ notifyApiFailure({ method, path: diagnosticPath(path), status: apiError.status, code: apiError.code, message: apiError.message, failure_stage: "response_parse" });
75135
+ throw apiError;
75136
+ }
75137
+ }
75138
+ } else if (response.ok && response.status !== 204) {
75139
+ const apiError = new ApiError(response.status, "RESPONSE_PARSE_ERROR", "Backend returned an empty response");
75140
+ notifyApiFailure({ method, path: diagnosticPath(path), status: apiError.status, code: apiError.code, message: apiError.message, failure_stage: "response_parse" });
75141
+ throw apiError;
75142
+ }
74926
75143
  if (!response.ok) {
74927
75144
  const message = payload.error?.message?.trim() || payload.message?.trim() || response.statusText.trim() || `HTTP ${response.status}`;
74928
- throw new ApiError(response.status, payload.error?.code ?? "HTTP_ERROR", message, payload.error?.details);
75145
+ const apiError = new ApiError(response.status, payload.error?.code ?? "HTTP_ERROR", message, payload.error?.details);
75146
+ notifyApiFailure({ method, path: diagnosticPath(path), status: apiError.status, code: apiError.code, message: apiError.message, failure_stage: "response" });
75147
+ throw apiError;
74929
75148
  }
74930
75149
  return payload.data;
74931
75150
  }
@@ -74947,13 +75166,13 @@ var KeySyncApiClient = class {
74947
75166
  };
74948
75167
 
74949
75168
  // packages/client-core/src/index.ts
74950
- import { appendFile, chmod as chmod2, mkdir as mkdir3, readFile as readFile6, rename as rename2, rm as rm3, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
75169
+ import { appendFile, chmod as chmod2, mkdir as mkdir3, readFile as readFile7, rename as rename3, rm as rm3, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
74951
75170
  import { closeSync, existsSync as existsSync3, openSync } from "node:fs";
74952
75171
  import { execFile as execFile2, spawn } from "node:child_process";
74953
75172
  import { createServer } from "node:net";
74954
- import { delimiter as delimiter2, dirname as dirname3, join as join6, posix as posixPath, win32 as win32Path2 } from "node:path";
75173
+ import { delimiter as delimiter2, dirname as dirname3, join as join7, resolve as resolve2, posix as posixPath, win32 as win32Path2 } from "node:path";
74955
75174
  import { homedir as homedir6, platform } from "node:os";
74956
- import { createCipheriv, createDecipheriv, createHash as createHash5, createHmac, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, pbkdf2Sync, randomBytes } from "node:crypto";
75175
+ import { createCipheriv, createDecipheriv, createHash as createHash5, createHmac, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, pbkdf2Sync, randomBytes as randomBytes2 } from "node:crypto";
74957
75176
 
74958
75177
  // packages/app-adapters/src/index.ts
74959
75178
  import { access, chmod, constants, copyFile, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
@@ -84371,7 +84590,7 @@ var apiProtocols = [
84371
84590
  var agentAppNames = ["ClaudeCode", "Claude", "Codex", "OpenClaw", "Hermes", "DeepSeekHarness"];
84372
84591
  var appSourceTypes = ["provider", "runtime_config"];
84373
84592
  var deviceLifecycleStatuses = ["active", "removed"];
84374
- var applyStatuses = ["success", "failed"];
84593
+ var applyStatuses = ["success", "failed", "conflict"];
84375
84594
  var protocolBridgeModes = ["always", "when_needed", "off"];
84376
84595
  var bindingCompatibilityStatuses = ["direct", "bridge_required", "bridge_optional", "unavailable", "conflict"];
84377
84596
  var bindingCompatibilityReasonCodes = [
@@ -84510,7 +84729,7 @@ var rawProviderTemplates = {
84510
84729
  sort_order: 30
84511
84730
  },
84512
84731
  endpoints: [
84513
- { name: "GLM Coding OpenAI Chat", protocol: "openai_chat_completions", base_url: "https://api.z.ai/api/coding/paas/v4", sort_order: 0 },
84732
+ { name: "GLM Coding OpenAI Chat", protocol: "openai_chat_completions", base_url: "https://open.bigmodel.cn/api/coding/paas/v4", sort_order: 0 },
84514
84733
  { name: "GLM Anthropic", protocol: "anthropic_messages", base_url: "https://api.z.ai/api/anthropic", sort_order: 1 }
84515
84734
  ]
84516
84735
  },
@@ -84568,8 +84787,8 @@ var rawProviderTemplates = {
84568
84787
  description: "\u544A\u8BC9\u5BA2\u670D\u8981\u5F00 GPT \u4F1A\u5458\uFF1B\u5BA2\u670D\u534F\u52A9\u8D2D\u4E70\u4F1A\u5458\u8D26\u53F7\uFF0C\u4E70\u5B8C\u540E\u5728\u672C\u673A\u767B\u5F55 Codex\uFF0C\u518D\u56DE\u6765\u5BFC\u5165\u3002",
84569
84788
  steps: [
84570
84789
  "\u5BA2\u670D\u534F\u52A9\u8D2D\u4E70 GPT \u4F1A\u5458\u8D26\u53F7",
84571
- "\u5728\u5F53\u524D\u7535\u8111\u5B8C\u6210 Codex \u5B98\u65B9\u767B\u5F55",
84572
- "\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\u5730 Codex \u5BFC\u5165"
84790
+ "\u5728\u5F53\u524D\u8BBE\u5907\u5B8C\u6210 Codex \u5B98\u65B9\u767B\u5F55",
84791
+ "\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\u673A Codex \u5BFC\u5165"
84573
84792
  ],
84574
84793
  sort_order: 10
84575
84794
  },
@@ -84776,8 +84995,8 @@ var ccSwitchCoreProviderTemplatesV2 = [
84776
84995
  description: "\u544A\u8BC9\u5BA2\u670D\u8981\u5F00 Claude \u4F1A\u5458\uFF1B\u5BA2\u670D\u534F\u52A9\u8D2D\u4E70\u4F1A\u5458\u8D26\u53F7\uFF0C\u4E70\u5B8C\u540E\u5728\u672C\u673A\u767B\u5F55 Claude Code\uFF0C\u518D\u56DE\u6765\u5BFC\u5165\u3002",
84777
84996
  steps: [
84778
84997
  "\u5BA2\u670D\u534F\u52A9\u8D2D\u4E70 Claude \u4F1A\u5458\u8D26\u53F7",
84779
- "\u5728\u5F53\u524D\u7535\u8111\u5B8C\u6210 Claude Code \u5B98\u65B9\u767B\u5F55",
84780
- "\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\u5730 Claude Code \u5BFC\u5165"
84998
+ "\u5728\u5F53\u524D\u8BBE\u5907\u5B8C\u6210 Claude Code \u5B98\u65B9\u767B\u5F55",
84999
+ "\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\u673A Claude Code \u5BFC\u5165"
84781
85000
  ],
84782
85001
  sort_order: 15
84783
85002
  },
@@ -84801,8 +85020,8 @@ var ccSwitchCoreProviderTemplatesV2 = [
84801
85020
  description: "\u544A\u8BC9\u5BA2\u670D\u8981\u5F00 GPT \u4F1A\u5458\uFF1B\u5BA2\u670D\u534F\u52A9\u8D2D\u4E70\u4F1A\u5458\u8D26\u53F7\uFF0C\u4E70\u5B8C\u540E\u5728\u672C\u673A\u767B\u5F55 Codex\uFF0C\u518D\u56DE\u6765\u5BFC\u5165\u3002",
84802
85021
  steps: [
84803
85022
  "\u5BA2\u670D\u534F\u52A9\u8D2D\u4E70 GPT \u4F1A\u5458\u8D26\u53F7",
84804
- "\u5728\u5F53\u524D\u7535\u8111\u5B8C\u6210 Codex \u5B98\u65B9\u767B\u5F55",
84805
- "\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\u5730 Codex \u5BFC\u5165"
85023
+ "\u5728\u5F53\u524D\u8BBE\u5907\u5B8C\u6210 Codex \u5B98\u65B9\u767B\u5F55",
85024
+ "\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\u673A Codex \u5BFC\u5165"
84806
85025
  ],
84807
85026
  sort_order: 10
84808
85027
  },
@@ -84865,6 +85084,78 @@ var ccSwitchCoreProviderTemplatesV2 = [
84865
85084
  cc_switch_family: "cloud_provider"
84866
85085
  },
84867
85086
  sort_order: 250
85087
+ },
85088
+ {
85089
+ type: "ant_ling",
85090
+ display_name: "Ant Ling",
85091
+ description: "Ant Ling OpenAI-compatible API.",
85092
+ template_group: "official",
85093
+ fields: [apiKeyField, defaultModelField("Ling-2.6-flash")],
85094
+ defaults: { default_model: "Ling-2.6-flash", endpoints: [{ protocol: "openai_chat_completions", base_url: "https://api.ant-ling.com/v1" }], cc_switch_family: "official_api" },
85095
+ sort_order: 260
85096
+ },
85097
+ {
85098
+ type: "groq",
85099
+ display_name: "Groq",
85100
+ description: "Groq OpenAI-compatible API.",
85101
+ template_group: "official",
85102
+ fields: [apiKeyField, defaultModelField("llama-3.3-70b-versatile")],
85103
+ defaults: { default_model: "llama-3.3-70b-versatile", endpoints: [{ protocol: "openai_chat_completions", base_url: "https://api.groq.com/openai/v1" }], cc_switch_family: "official_api" },
85104
+ sort_order: 261
85105
+ },
85106
+ {
85107
+ type: "moonshotai",
85108
+ display_name: "Moonshot AI",
85109
+ description: "Moonshot AI international OpenAI-compatible API.",
85110
+ template_group: "official",
85111
+ fields: [apiKeyField, defaultModelField("kimi-k2.6")],
85112
+ defaults: { default_model: "kimi-k2.6", endpoints: [{ protocol: "openai_chat_completions", base_url: "https://api.moonshot.ai/v1" }], cc_switch_family: "official_api" },
85113
+ sort_order: 262
85114
+ },
85115
+ {
85116
+ type: "qwen_token_plan",
85117
+ display_name: "Qwen Token Plan",
85118
+ description: "Qwen Token Plan OpenAI-compatible API.",
85119
+ template_group: "official",
85120
+ fields: [apiKeyField, defaultModelField("qwen3.6-plus")],
85121
+ defaults: { default_model: "qwen3.6-plus", endpoints: [{ protocol: "openai_chat_completions", base_url: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1" }], cc_switch_family: "official_api" },
85122
+ sort_order: 263
85123
+ },
85124
+ {
85125
+ type: "qwen_token_plan_cn",
85126
+ display_name: "Qwen Token Plan CN",
85127
+ description: "Qwen Token Plan China OpenAI-compatible API.",
85128
+ template_group: "official",
85129
+ fields: [apiKeyField, defaultModelField("qwen3.6-plus")],
85130
+ defaults: { default_model: "qwen3.6-plus", endpoints: [{ protocol: "openai_chat_completions", base_url: "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1" }], cc_switch_family: "official_api" },
85131
+ sort_order: 264
85132
+ },
85133
+ {
85134
+ type: "xiaomi_token_plan_ams",
85135
+ display_name: "Xiaomi Token Plan AMS",
85136
+ description: "Xiaomi MiMo Token Plan international API.",
85137
+ template_group: "official",
85138
+ fields: [apiKeyField, defaultModelField("mimo-v2.5-pro")],
85139
+ defaults: { default_model: "mimo-v2.5-pro", endpoints: [{ protocol: "openai_chat_completions", base_url: "https://token-plan-ams.xiaomimimo.com/v1" }], cc_switch_family: "official_api" },
85140
+ sort_order: 265
85141
+ },
85142
+ {
85143
+ type: "xiaomi_token_plan_cn",
85144
+ display_name: "Xiaomi Token Plan CN",
85145
+ description: "Xiaomi MiMo Token Plan China API.",
85146
+ template_group: "official",
85147
+ fields: [apiKeyField, defaultModelField("mimo-v2.5-pro")],
85148
+ defaults: { default_model: "mimo-v2.5-pro", endpoints: [{ protocol: "openai_chat_completions", base_url: "https://token-plan-cn.xiaomimimo.com/v1" }], cc_switch_family: "official_api" },
85149
+ sort_order: 266
85150
+ },
85151
+ {
85152
+ type: "xiaomi_token_plan_sgp",
85153
+ display_name: "Xiaomi Token Plan SGP",
85154
+ description: "Xiaomi MiMo Token Plan Singapore API.",
85155
+ template_group: "official",
85156
+ fields: [apiKeyField, defaultModelField("mimo-v2.5-pro")],
85157
+ defaults: { default_model: "mimo-v2.5-pro", endpoints: [{ protocol: "openai_chat_completions", base_url: "https://token-plan-sgp.xiaomimimo.com/v1" }], cc_switch_family: "official_api" },
85158
+ sort_order: 267
84868
85159
  }
84869
85160
  ];
84870
85161
  var ccSwitchProviderTemplatesV2 = [
@@ -86118,7 +86409,7 @@ async function commandSearchPathEntries() {
86118
86409
  }
86119
86410
  var execFileAsync = promisify(execFile);
86120
86411
  function sleep(ms) {
86121
- return new Promise((resolve2) => setTimeout(resolve2, ms));
86412
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
86122
86413
  }
86123
86414
  function restartableAppName(appName) {
86124
86415
  return appName === "Codex" || appName === "Claude" ? appName : null;
@@ -87736,20 +88027,29 @@ async function applyDeepSeekHarnessConfig(input, endpoint) {
87736
88027
  if (typeof apiKey !== "string" || !apiKey) throw new Error("Missing provider secret: api_key");
87737
88028
  const context = contextFor(input, endpoint);
87738
88029
  const model = providerDefaultModel(context);
87739
- const existingAdapter = scalarRecord(settings["llm-deepseek"]);
87740
- const existingDefault = scalarRecord(settings["agent-default-model"]);
87741
- settings["llm-deepseek"] = {
87742
- ...existingAdapter,
87743
- apiKeyEnv: "DEEPSEEK_API_KEY",
87744
- baseURL: endpoint.base_url,
87745
- models: [{ id: model, name: model }]
87746
- };
87747
- settings["agent-default-model"] = {
87748
- ...existingDefault,
87749
- provider: "deepseek-official",
87750
- model
87751
- };
87752
- credentialStore.refs.DEEPSEEK_API_KEY = apiKey;
88030
+ const native = input.provider.type ? dshNativeBindingFor(input.provider.type) : null;
88031
+ if (native?.adapter === "deepseek") {
88032
+ settings["agent-default-model"] = { provider: native.provider, model, reasoningEffort: "high" };
88033
+ credentialStore.refs[native.credentialRef] = apiKey;
88034
+ } else {
88035
+ const piAi = scalarRecord(settings["llm-pi-ai"]);
88036
+ const profiles = scalarRecord(piAi.providers);
88037
+ const binding = native ?? dshCustomBinding(input.provider.type, input.provider.name);
88038
+ const profile = scalarRecord(profiles[binding.provider]);
88039
+ if (native) {
88040
+ const nativeProfile = { ...profile, apiKeyEnv: binding.credentialRef };
88041
+ delete nativeProfile.api;
88042
+ delete nativeProfile.baseURL;
88043
+ delete nativeProfile.models;
88044
+ delete nativeProfile.displayName;
88045
+ profiles[binding.provider] = nativeProfile;
88046
+ } else {
88047
+ profiles[binding.provider] = { ...profile, displayName: input.provider.name, apiKeyEnv: binding.credentialRef, api: "openai-completions", baseURL: endpoint.base_url, models: [{ id: model, name: model }] };
88048
+ }
88049
+ settings["llm-pi-ai"] = { ...piAi, providers: profiles };
88050
+ settings["agent-default-model"] = { provider: binding.provider, model };
88051
+ credentialStore.refs[binding.credentialRef] = apiKey;
88052
+ }
87753
88053
  const credentials = renderDshCredentials(credentialStore, await dshCredentialsLayout(credentialStore, input.runtimePathOptions));
87754
88054
  const writes = [
87755
88055
  { path: settingsPath, content: yamlDocument(settings) },
@@ -87784,6 +88084,44 @@ async function applyDeepSeekHarnessConfig(input, endpoint) {
87784
88084
  config_changed: configChanged
87785
88085
  };
87786
88086
  }
88087
+ var DSH_NATIVE_BINDINGS = {
88088
+ ant_ling: { provider: "ant-ling", credentialRef: "ANT_LING_API_KEY", adapter: "pi-ai" },
88089
+ cerebras: { provider: "cerebras", credentialRef: "CEREBRAS_API_KEY", adapter: "pi-ai" },
88090
+ deepseek: { provider: "deepseek-official", credentialRef: "DEEPSEEK_API_KEY", adapter: "deepseek" },
88091
+ fireworks_ai: { provider: "fireworks", credentialRef: "FIREWORKS_API_KEY", adapter: "pi-ai" },
88092
+ groq: { provider: "groq", credentialRef: "GROQ_API_KEY", adapter: "pi-ai" },
88093
+ huggingface_inference: { provider: "huggingface", credentialRef: "HUGGINGFACE_API_KEY", adapter: "pi-ai" },
88094
+ kimi: { provider: "moonshotai-cn", credentialRef: "MOONSHOTAI_CN_API_KEY", adapter: "pi-ai" },
88095
+ kimi_k26: { provider: "moonshotai-cn", credentialRef: "MOONSHOTAI_CN_API_KEY", adapter: "pi-ai" },
88096
+ mistral_ai: { provider: "mistral", credentialRef: "MISTRAL_API_KEY", adapter: "pi-ai" },
88097
+ moonshotai: { provider: "moonshotai", credentialRef: "MOONSHOTAI_API_KEY", adapter: "pi-ai" },
88098
+ nvidia: { provider: "nvidia", credentialRef: "NVIDIA_API_KEY", adapter: "pi-ai" },
88099
+ nvidia_nim: { provider: "nvidia", credentialRef: "NVIDIA_API_KEY", adapter: "pi-ai" },
88100
+ openai_api: { provider: "openai", credentialRef: "OPENAI_API_KEY", adapter: "pi-ai" },
88101
+ openrouter: { provider: "openrouter", credentialRef: "OPENROUTER_API_KEY", adapter: "pi-ai" },
88102
+ opencode_go_deepseek_v4_flash: { provider: "opencode-go", credentialRef: "OPENCODE_GO_API_KEY", adapter: "pi-ai" },
88103
+ qwen_token_plan: { provider: "qwen-token-plan", credentialRef: "QWEN_TOKEN_PLAN_API_KEY", adapter: "pi-ai" },
88104
+ qwen_token_plan_cn: { provider: "qwen-token-plan-cn", credentialRef: "QWEN_TOKEN_PLAN_CN_API_KEY", adapter: "pi-ai" },
88105
+ together_ai: { provider: "together", credentialRef: "TOGETHER_API_KEY", adapter: "pi-ai" },
88106
+ vercel_ai_gateway: { provider: "vercel-ai-gateway", credentialRef: "VERCEL_AI_GATEWAY_API_KEY", adapter: "pi-ai" },
88107
+ xai_grok: { provider: "xai", credentialRef: "XAI_API_KEY", adapter: "pi-ai" },
88108
+ xiaomi_mimo: { provider: "xiaomi", credentialRef: "XIAOMI_API_KEY", adapter: "pi-ai" },
88109
+ xiaomi_mimo_token_plan_cn: { provider: "xiaomi-token-plan-cn", credentialRef: "XIAOMI_TOKEN_PLAN_CN_API_KEY", adapter: "pi-ai" },
88110
+ xiaomi_token_plan_ams: { provider: "xiaomi-token-plan-ams", credentialRef: "XIAOMI_TOKEN_PLAN_AMS_API_KEY", adapter: "pi-ai" },
88111
+ xiaomi_token_plan_cn: { provider: "xiaomi-token-plan-cn", credentialRef: "XIAOMI_TOKEN_PLAN_CN_API_KEY", adapter: "pi-ai" },
88112
+ xiaomi_token_plan_sgp: { provider: "xiaomi-token-plan-sgp", credentialRef: "XIAOMI_TOKEN_PLAN_SGP_API_KEY", adapter: "pi-ai" },
88113
+ glm_coding_plan: { provider: "zai-coding-cn", credentialRef: "ZAI_CODING_CN_API_KEY", adapter: "pi-ai" },
88114
+ zhipu_glm: { provider: "zai", credentialRef: "ZAI_API_KEY", adapter: "pi-ai" },
88115
+ zhipu_glm_en: { provider: "zai", credentialRef: "ZAI_API_KEY", adapter: "pi-ai" }
88116
+ };
88117
+ function dshNativeBindingFor(providerType) {
88118
+ return DSH_NATIVE_BINDINGS[providerType.replace(/^ccswitch_/, "")] ?? null;
88119
+ }
88120
+ function dshCustomBinding(providerType, providerName) {
88121
+ const route = `keysync-${(providerType ?? providerName).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "provider"}`;
88122
+ const credentialRef = `KEYSYNC_${route.replace(/[^A-Z0-9]+/gi, "_").toUpperCase()}_API_KEY`;
88123
+ return { provider: route, credentialRef, adapter: "pi-ai" };
88124
+ }
87787
88125
  async function applyAppConfig(input) {
87788
88126
  const endpoint = preferredEndpointFor(input) ?? selectEndpoint(input.appName, input.provider.endpoints, input.supportedProtocols);
87789
88127
  if (!endpoint) {
@@ -87846,22 +88184,16 @@ function resolveProviderForApp(snapshot, appId) {
87846
88184
  }
87847
88185
 
87848
88186
  // packages/client-core/src/codex-session-migration.ts
87849
- import { copyFile as copyFile2, mkdir as mkdir2, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
88187
+ import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
87850
88188
  import { existsSync, readdirSync } from "node:fs";
87851
88189
  import { homedir as homedir2 } from "node:os";
87852
- import { basename as basename2, dirname as dirname2, join as join2, relative, resolve } from "node:path";
88190
+ import { join as join2, resolve } from "node:path";
87853
88191
  import { DatabaseSync } from "node:sqlite";
87854
- function timestamp2(date = /* @__PURE__ */ new Date()) {
87855
- return date.toISOString().replace(/[-:]/g, "").replace(".", "-").replace("Z", "").replace("T", "-");
87856
- }
87857
88192
  function expandHome(path) {
87858
88193
  if (path === "~") return homedir2();
87859
88194
  if (path.startsWith("~/")) return join2(homedir2(), path.slice(2));
87860
88195
  return path;
87861
88196
  }
87862
- function sqlString(value) {
87863
- return `'${value.replace(/'/g, "''")}'`;
87864
- }
87865
88197
  async function readCodexEffectiveSessionProvider(configPath) {
87866
88198
  let text = "";
87867
88199
  try {
@@ -87914,31 +88246,6 @@ function discoverCodexStateDatabases(codexHome) {
87914
88246
  walk(codexHome);
87915
88247
  return candidates;
87916
88248
  }
87917
- function backupPathFor(sourcePath, codexHome, backupRoot) {
87918
- const source = resolve(sourcePath);
87919
- const home = resolve(codexHome);
87920
- const relativePath = source.startsWith(`${home}/`) ? relative(home, source) : basename2(source);
87921
- return join2(backupRoot, relativePath);
87922
- }
87923
- function uniqueBackupPath(path) {
87924
- if (!existsSync(path)) return path;
87925
- for (let index = 1; ; index += 1) {
87926
- const candidate = `${path}.${index}`;
87927
- if (!existsSync(candidate)) return candidate;
87928
- }
87929
- }
87930
- async function createSqliteBackup(db, dbPath, codexHome, backupRoot) {
87931
- const backupPath = uniqueBackupPath(`${backupPathFor(dbPath, codexHome, backupRoot)}.bak`);
87932
- await mkdir2(dirname2(backupPath), { recursive: true });
87933
- db.exec(`VACUUM INTO ${sqlString(backupPath)}`);
87934
- return backupPath;
87935
- }
87936
- async function backupRolloutFile(rolloutPath, codexHome, backupRoot) {
87937
- const backupPath = uniqueBackupPath(backupPathFor(rolloutPath, codexHome, backupRoot));
87938
- await mkdir2(dirname2(backupPath), { recursive: true });
87939
- await copyFile2(rolloutPath, backupPath);
87940
- return backupPath;
87941
- }
87942
88249
  function hasThreadsTable(db) {
87943
88250
  const row = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'threads'").get();
87944
88251
  return row?.name === "threads";
@@ -87950,7 +88257,7 @@ function updateThreadProviders(db, targetProvider) {
87950
88257
  const result = db.prepare("UPDATE threads SET model_provider = ? WHERE model_provider <> ?").run(targetProvider, targetProvider);
87951
88258
  return Number(result.changes ?? 0);
87952
88259
  }
87953
- async function updateRolloutProvider(rolloutPath, targetProvider, codexHome, backupRoot, warnings) {
88260
+ async function updateRolloutProvider(rolloutPath, targetProvider, warnings) {
87954
88261
  if (!existsSync(rolloutPath)) {
87955
88262
  warnings.push({ path: rolloutPath, code: "ROLLOUT_NOT_FOUND", message: "Rollout file was not found." });
87956
88263
  return { status: "skipped" };
@@ -87987,54 +88294,18 @@ async function updateRolloutProvider(rolloutPath, targetProvider, codexHome, bac
87987
88294
  return { status: "skipped" };
87988
88295
  }
87989
88296
  if (payload.model_provider === targetProvider) return { status: "skipped" };
87990
- let backupPath;
87991
- try {
87992
- backupPath = await backupRolloutFile(rolloutPath, codexHome, backupRoot);
87993
- } catch (error) {
87994
- warnings.push({ path: rolloutPath, code: "ROLLOUT_BACKUP_FAILED", message: error instanceof Error ? error.message : "Rollout file could not be backed up." });
87995
- return { status: "skipped" };
87996
- }
87997
88297
  payload.model_provider = targetProvider;
87998
88298
  lines[0] = `${JSON.stringify(firstEvent)}${newline}`;
87999
88299
  try {
88000
88300
  await writeFile2(rolloutPath, lines.join(""), "utf8");
88001
- return { status: "changed", backupPath };
88301
+ return { status: "changed" };
88002
88302
  } catch (error) {
88003
88303
  warnings.push({ path: rolloutPath, code: "ROLLOUT_WRITE_FAILED", message: error instanceof Error ? error.message : "Rollout file could not be written." });
88004
- return { status: "skipped", backupPath };
88005
- }
88006
- }
88007
- function countFilesRecursively(root) {
88008
- if (!existsSync(root)) return 0;
88009
- let count = 0;
88010
- let entries;
88011
- try {
88012
- entries = readdirSync(root, { withFileTypes: true });
88013
- } catch {
88014
- return 0;
88015
- }
88016
- for (const entry of entries) {
88017
- const path = join2(root, entry.name);
88018
- if (entry.isDirectory()) count += countFilesRecursively(path);
88019
- else count += 1;
88020
- }
88021
- return count;
88022
- }
88023
- async function cleanupSuccessfulMigrationBackups(backupBaseDir, warnings) {
88024
- const fileCount = countFilesRecursively(backupBaseDir);
88025
- if (fileCount === 0) return 0;
88026
- try {
88027
- await rm2(backupBaseDir, { recursive: true, force: true });
88028
- return fileCount;
88029
- } catch (error) {
88030
- warnings.push({ path: backupBaseDir, code: "BACKUP_CLEANUP_FAILED", message: error instanceof Error ? error.message : "Migration backups could not be cleaned up." });
88031
- return 0;
88304
+ return { status: "skipped" };
88032
88305
  }
88033
88306
  }
88034
88307
  async function migrateCodexSessionsToProvider(targetProvider, options) {
88035
88308
  const codexHome = resolve(expandHome(options.codexHome));
88036
- const backupBaseDir = options.backupDir ?? join2(codexHome, "backups", "session_convert");
88037
- const backupRoot = join2(backupBaseDir, `migration.${timestamp2(options.now)}`);
88038
88309
  const warnings = [];
88039
88310
  const result = {
88040
88311
  target_provider: targetProvider,
@@ -88043,12 +88314,9 @@ async function migrateCodexSessionsToProvider(targetProvider, options) {
88043
88314
  sqlite_rows_changed: 0,
88044
88315
  rollout_files_changed: 0,
88045
88316
  rollout_files_skipped: 0,
88046
- backups_created: 0,
88047
- backups_cleaned: 0,
88048
88317
  warnings
88049
88318
  };
88050
88319
  const processedRollouts = /* @__PURE__ */ new Set();
88051
- const createdBackups = [];
88052
88320
  for (const dbPath of discoverCodexStateDatabases(codexHome)) {
88053
88321
  result.databases_found += 1;
88054
88322
  let db = null;
@@ -88062,13 +88330,10 @@ async function migrateCodexSessionsToProvider(targetProvider, options) {
88062
88330
  let sqliteUpdated = true;
88063
88331
  if (threads.some((thread) => thread.model_provider !== targetProvider)) {
88064
88332
  try {
88065
- const backupPath = await createSqliteBackup(db, dbPath, codexHome, backupRoot);
88066
- createdBackups.push(backupPath);
88067
- result.backups_created += 1;
88068
88333
  result.sqlite_rows_changed += updateThreadProviders(db, targetProvider);
88069
88334
  } catch (error) {
88070
88335
  sqliteUpdated = false;
88071
- warnings.push({ path: dbPath, code: "SQLITE_BACKUP_OR_UPDATE_FAILED", message: error instanceof Error ? error.message : "Codex state database could not be backed up or updated." });
88336
+ warnings.push({ path: dbPath, code: "SQLITE_UPDATE_FAILED", message: error instanceof Error ? error.message : "Codex state database could not be updated." });
88072
88337
  }
88073
88338
  }
88074
88339
  if (!sqliteUpdated) {
@@ -88083,11 +88348,7 @@ async function migrateCodexSessionsToProvider(targetProvider, options) {
88083
88348
  const rolloutPath = resolve(expandHome(thread.rollout_path));
88084
88349
  if (processedRollouts.has(rolloutPath)) continue;
88085
88350
  processedRollouts.add(rolloutPath);
88086
- const rolloutResult = await updateRolloutProvider(rolloutPath, targetProvider, codexHome, backupRoot, warnings);
88087
- if (rolloutResult.backupPath) {
88088
- createdBackups.push(rolloutResult.backupPath);
88089
- result.backups_created += 1;
88090
- }
88351
+ const rolloutResult = await updateRolloutProvider(rolloutPath, targetProvider, warnings);
88091
88352
  if (rolloutResult.status === "changed") {
88092
88353
  result.rollout_files_changed += 1;
88093
88354
  } else {
@@ -88100,9 +88361,6 @@ async function migrateCodexSessionsToProvider(targetProvider, options) {
88100
88361
  db?.close();
88101
88362
  }
88102
88363
  }
88103
- if (warnings.length === 0 && createdBackups.length > 0) {
88104
- result.backups_cleaned = await cleanupSuccessfulMigrationBackups(backupBaseDir, warnings);
88105
- }
88106
88364
  return result;
88107
88365
  }
88108
88366
 
@@ -88345,6 +88603,33 @@ async function probeLocalModelConfigs(options = {}) {
88345
88603
  return probes;
88346
88604
  }
88347
88605
 
88606
+ // packages/client-core/src/index.ts
88607
+ var import_install_identity = __toESM(require_install_identity(), 1);
88608
+
88609
+ // packages/client-core/src/codex-login-import.ts
88610
+ import { createHash as createHash3 } from "node:crypto";
88611
+ import { readFile as readFile5 } from "node:fs/promises";
88612
+ import { homedir as homedir5 } from "node:os";
88613
+ import { join as join5 } from "node:path";
88614
+
88615
+ // packages/client-core/src/provider-import-name.ts
88616
+ import { hostname } from "node:os";
88617
+ function uniqueProviderImportName(base, takenNames, deviceLabel = hostname()) {
88618
+ const taken = new Set([...takenNames].map((name) => name.trim()).filter(Boolean));
88619
+ if (!taken.has(base)) return base;
88620
+ const label = deviceLabel.trim().replace(/\.local$/i, "");
88621
+ const withDevice = label ? `${base} \xB7 ${label}` : base;
88622
+ if (!taken.has(withDevice)) return withDevice;
88623
+ for (let ordinal = 2; ordinal <= 99; ordinal += 1) {
88624
+ const candidate = `${withDevice} ${ordinal}`;
88625
+ if (!taken.has(candidate)) return candidate;
88626
+ }
88627
+ return `${withDevice} ${taken.size + 1}`;
88628
+ }
88629
+ function providerDisplayNames(providers) {
88630
+ return providers.map((provider) => provider.name ?? "").filter(Boolean);
88631
+ }
88632
+
88348
88633
  // packages/client-core/src/ccswitch-import.ts
88349
88634
  import { createHash as createHash2 } from "node:crypto";
88350
88635
  import { existsSync as existsSync2 } from "node:fs";
@@ -88798,29 +89083,7 @@ async function executeCcswitchProviderImport(client, options) {
88798
89083
  return { plan, created, updated, skipped };
88799
89084
  }
88800
89085
 
88801
- // packages/client-core/src/provider-import-name.ts
88802
- import { hostname } from "node:os";
88803
- function uniqueProviderImportName(base, takenNames, deviceLabel = hostname()) {
88804
- const taken = new Set([...takenNames].map((name) => name.trim()).filter(Boolean));
88805
- if (!taken.has(base)) return base;
88806
- const label = deviceLabel.trim().replace(/\.local$/i, "");
88807
- const withDevice = label ? `${base} \xB7 ${label}` : base;
88808
- if (!taken.has(withDevice)) return withDevice;
88809
- for (let ordinal = 2; ordinal <= 99; ordinal += 1) {
88810
- const candidate = `${withDevice} ${ordinal}`;
88811
- if (!taken.has(candidate)) return candidate;
88812
- }
88813
- return `${withDevice} ${taken.size + 1}`;
88814
- }
88815
- function providerDisplayNames(providers) {
88816
- return providers.map((provider) => provider.name ?? "").filter(Boolean);
88817
- }
88818
-
88819
89086
  // packages/client-core/src/codex-login-import.ts
88820
- import { createHash as createHash3 } from "node:crypto";
88821
- import { readFile as readFile5 } from "node:fs/promises";
88822
- import { homedir as homedir5 } from "node:os";
88823
- import { join as join5 } from "node:path";
88824
89087
  var codexLoginImportSource = "codex";
88825
89088
  var codexLoginTemplateType = "codex_official_login";
88826
89089
  function defaultCodexConfigDir() {
@@ -89126,6 +89389,550 @@ async function executeCodexLoginProviderImport(client, options) {
89126
89389
  // packages/client-core/src/claude-login-import.ts
89127
89390
  import { createHash as createHash4 } from "node:crypto";
89128
89391
 
89392
+ // packages/client-core/src/diagnostic-reporter.ts
89393
+ import { randomBytes } from "node:crypto";
89394
+ import { mkdir as mkdir2, open, readFile as readFile6, rename as rename2, rm as rm2, stat as stat3 } from "node:fs/promises";
89395
+ import { dirname as dirname2, join as join6 } from "node:path";
89396
+ var import_diagnostic_sanitize = __toESM(require_diagnostic_sanitize(), 1);
89397
+ var LOCK_STALE_MS = 3e4;
89398
+ var UPLOAD_LEASE_MS = 45e3;
89399
+ var IDENTITY_EPOCH_PATTERN = /^epoch_[a-f0-9]{32}$/;
89400
+ var IDENTITY_PROOF_PATTERN = /^proof_[a-f0-9]{64}$/;
89401
+ var OWNER_USER_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,191}$/;
89402
+ function newIdentityEpoch() {
89403
+ return `epoch_${randomBytes(16).toString("hex")}`;
89404
+ }
89405
+ function newIdentityProof() {
89406
+ return `proof_${randomBytes(32).toString("hex")}`;
89407
+ }
89408
+ function normalizedOwnerUserId(value) {
89409
+ if (value === null || value === void 0 || value === "") return null;
89410
+ const normalized = String(value).trim();
89411
+ if (!OWNER_USER_ID_PATTERN.test(normalized)) throw Object.assign(new Error("DIAGNOSTIC_IDENTITY_INVALID"), { code: "DIAGNOSTIC_IDENTITY_INVALID" });
89412
+ return normalized;
89413
+ }
89414
+ function ensureIdentityEpochs(outbox) {
89415
+ let legacyEpoch = null;
89416
+ for (const event of outbox.events) {
89417
+ if (!IDENTITY_EPOCH_PATTERN.test(event.identity_epoch || "")) {
89418
+ legacyEpoch ??= newIdentityEpoch();
89419
+ event.identity_epoch = legacyEpoch;
89420
+ event.owner_user_id = null;
89421
+ }
89422
+ event.body.identity_epoch = event.identity_epoch;
89423
+ if (typeof event.device_id === "string" && event.device_id) event.body.device_id = event.device_id;
89424
+ if (!OWNER_USER_ID_PATTERN.test(event.owner_user_id || "")) event.owner_user_id = null;
89425
+ delete event.body.user_id;
89426
+ }
89427
+ if (!outbox.identity || !IDENTITY_EPOCH_PATTERN.test(outbox.identity.current_epoch || "")) {
89428
+ outbox.identity = { current_epoch: newIdentityEpoch(), current_owner_user_id: null, current_proof: newIdentityProof() };
89429
+ } else {
89430
+ outbox.identity.current_owner_user_id = OWNER_USER_ID_PATTERN.test(outbox.identity.current_owner_user_id || "") ? outbox.identity.current_owner_user_id : null;
89431
+ if (!IDENTITY_PROOF_PATTERN.test(outbox.identity.current_proof || "")) {
89432
+ outbox.identity.current_proof = outbox.events.find((event) => event.identity_epoch === outbox.identity?.current_epoch && IDENTITY_PROOF_PATTERN.test(event.identity_proof || ""))?.identity_proof ?? newIdentityProof();
89433
+ }
89434
+ }
89435
+ outbox.owned_epochs ??= [];
89436
+ for (const item of outbox.retired_pending_claims || []) {
89437
+ if (!item.owner_user_id || !IDENTITY_EPOCH_PATTERN.test(item.identity_epoch) || typeof item.installation_id !== "string") continue;
89438
+ if (!outbox.owned_epochs.some((owned) => owned.installation_id === item.installation_id && owned.identity_epoch === item.identity_epoch && owned.owner_user_id === item.owner_user_id)) {
89439
+ outbox.owned_epochs.push({ installation_id: item.installation_id, identity_epoch: item.identity_epoch, identity_proof: IDENTITY_PROOF_PATTERN.test(item.identity_proof || "") ? item.identity_proof : newIdentityProof(), owner_user_id: item.owner_user_id, needs_reclaim: true });
89440
+ }
89441
+ }
89442
+ for (const item of outbox.owned_epochs) {
89443
+ if (!IDENTITY_PROOF_PATTERN.test(item.identity_proof || "")) {
89444
+ item.identity_proof = outbox.events.find((event) => event.identity_epoch === item.identity_epoch && event.owner_user_id === item.owner_user_id && IDENTITY_PROOF_PATTERN.test(event.identity_proof || ""))?.identity_proof ?? newIdentityProof();
89445
+ }
89446
+ }
89447
+ for (const event of outbox.events) {
89448
+ const historical = outbox.owned_epochs.find((item) => item.identity_epoch === event.identity_epoch && item.owner_user_id === event.owner_user_id && item.installation_id === event.body.installation_id);
89449
+ const proof = event.identity_epoch === outbox.identity.current_epoch ? outbox.identity.current_proof : historical?.identity_proof ?? event.identity_proof;
89450
+ if (proof && IDENTITY_PROOF_PATTERN.test(proof)) {
89451
+ event.identity_proof = proof;
89452
+ event.body.identity_proof = proof;
89453
+ } else {
89454
+ delete event.identity_proof;
89455
+ delete event.body.identity_proof;
89456
+ }
89457
+ }
89458
+ return outbox.identity;
89459
+ }
89460
+ function stableErrorCode(value) {
89461
+ const normalized = String(value ?? "").trim().toUpperCase().replace(/[^A-Z0-9_]/g, "_").slice(0, 80);
89462
+ return /^[A-Z][A-Z0-9_]{1,80}$/.test(normalized) ? normalized : "CLIENT_OPERATION_FAILED";
89463
+ }
89464
+ async function readOutbox(filePath) {
89465
+ try {
89466
+ const parsed = JSON.parse(await readFile6(filePath, "utf8"));
89467
+ if (parsed.version !== 1 || !Array.isArray(parsed.events)) throw new Error("DIAGNOSTIC_OUTBOX_CORRUPT");
89468
+ const events = parsed.events.map((event) => {
89469
+ if (!("body" in event)) {
89470
+ const { queued_at, attempts, last_attempt_at, last_error_code, next_attempt_at, rejected_at, rejection_status, rejection_code, ...body } = event;
89471
+ return { version: 1, client_event_id: body.client_event_id, device_id: body.device_id ?? null, body, queued_at, attempts, last_attempt_at, last_error_code, next_attempt_at, rejected_at, rejection_status, rejection_code };
89472
+ }
89473
+ return event;
89474
+ });
89475
+ if (events.some((event) => !event || typeof event.client_event_id !== "string" || !event.body || typeof event.body.installation_id !== "string" || typeof event.body.domain !== "string" || typeof event.body.action !== "string" || typeof event.body.message !== "string" || typeof event.attempts !== "number")) {
89476
+ throw new Error("DIAGNOSTIC_OUTBOX_CORRUPT");
89477
+ }
89478
+ const outbox = { version: 1, events, rejected_evicted: Number(parsed.rejected_evicted ?? 0), dropped_capacity: Number(parsed.dropped_capacity ?? 0), pending_claim: parsed.pending_claim ?? null, retired_pending_claims: parsed.retired_pending_claims ?? [], owned_epochs: parsed.owned_epochs ?? [], identity: parsed.identity ?? null };
89479
+ ensureIdentityEpochs(outbox);
89480
+ return outbox;
89481
+ } catch (error) {
89482
+ if (error?.code === "ENOENT") {
89483
+ const outbox = { version: 1, events: [], rejected_evicted: 0, dropped_capacity: 0, pending_claim: null, retired_pending_claims: [], owned_epochs: [], identity: null };
89484
+ ensureIdentityEpochs(outbox);
89485
+ return outbox;
89486
+ }
89487
+ throw Object.assign(new Error("DIAGNOSTIC_OUTBOX_CORRUPT"), { code: "DIAGNOSTIC_OUTBOX_CORRUPT", cause: error });
89488
+ }
89489
+ }
89490
+ async function withOutboxLock(filePath, operation, staleMs = LOCK_STALE_MS) {
89491
+ const lockPath = `${filePath}.lock`;
89492
+ await mkdir2(dirname2(filePath), { recursive: true, mode: 448 });
89493
+ const deadline = Date.now() + Math.max(3e4, staleMs + 5e3);
89494
+ let attempt = 0;
89495
+ while (Date.now() < deadline) {
89496
+ let handle;
89497
+ try {
89498
+ handle = await open(lockPath, "wx", 384);
89499
+ await handle.writeFile(`${process.pid} ${Date.now()} ${randomBytes(12).toString("hex")}
89500
+ `);
89501
+ try {
89502
+ return await operation();
89503
+ } finally {
89504
+ await handle.close();
89505
+ await rm2(lockPath, { force: true });
89506
+ }
89507
+ } catch (error) {
89508
+ await handle?.close().catch(() => void 0);
89509
+ if (error?.code !== "EEXIST") throw error;
89510
+ const age = Date.now() - (await stat3(lockPath).catch(() => ({ mtimeMs: Date.now() }))).mtimeMs;
89511
+ if (age > staleMs) {
89512
+ const owner = await readFile6(lockPath, "utf8").catch(() => "");
89513
+ const pid = Number(owner.trim().split(/\s+/)[0]);
89514
+ let ownerAlive = Number.isInteger(pid) && pid > 0;
89515
+ if (ownerAlive) {
89516
+ try {
89517
+ process.kill(pid, 0);
89518
+ } catch (ownerError) {
89519
+ ownerAlive = ownerError?.code === "EPERM";
89520
+ }
89521
+ }
89522
+ if (!ownerAlive && owner && await readFile6(lockPath, "utf8").catch(() => "") === owner) {
89523
+ await rm2(lockPath, { force: true });
89524
+ continue;
89525
+ }
89526
+ }
89527
+ attempt += 1;
89528
+ await new Promise((resolve3) => setTimeout(resolve3, Math.min(50, 5 + Math.floor(Math.random() * (attempt + 5)))));
89529
+ }
89530
+ }
89531
+ throw Object.assign(new Error("DIAGNOSTIC_OUTBOX_LOCK_TIMEOUT"), { code: "DIAGNOSTIC_OUTBOX_LOCK_TIMEOUT" });
89532
+ }
89533
+ async function writeOutbox(filePath, outbox) {
89534
+ await mkdir2(dirname2(filePath), { recursive: true, mode: 448 });
89535
+ const temporaryPath = `${filePath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
89536
+ try {
89537
+ const handle = await open(temporaryPath, "w", 384);
89538
+ try {
89539
+ await handle.writeFile(`${JSON.stringify({ version: 1, events: outbox.events, rejected_evicted: Number(outbox.rejected_evicted ?? 0), dropped_capacity: Number(outbox.dropped_capacity ?? 0), pending_claim: outbox.pending_claim ?? null, retired_pending_claims: outbox.retired_pending_claims ?? [], owned_epochs: outbox.owned_epochs ?? [], identity: outbox.identity })}
89540
+ `);
89541
+ await handle.sync();
89542
+ } finally {
89543
+ await handle.close();
89544
+ }
89545
+ await rename2(temporaryPath, filePath);
89546
+ const directory = await open(dirname2(filePath), "r").catch(() => null);
89547
+ if (directory) {
89548
+ try {
89549
+ await directory.sync();
89550
+ } finally {
89551
+ await directory.close();
89552
+ }
89553
+ }
89554
+ } catch (error) {
89555
+ await rm2(temporaryPath, { force: true }).catch(() => void 0);
89556
+ throw Object.assign(new Error("DIAGNOSTIC_OUTBOX_WRITE_FAILED"), { code: "DIAGNOSTIC_OUTBOX_WRITE_FAILED", cause: error });
89557
+ }
89558
+ }
89559
+ function permanent(error) {
89560
+ return error instanceof ApiError && error.status >= 400 && error.status < 500 && ![401, 408, 409, 425, 429].includes(error.status);
89561
+ }
89562
+ function nextAttemptAt(attempts) {
89563
+ const base = Math.min(3e5, 1e3 * 2 ** Math.max(0, attempts - 1));
89564
+ const jitter = 0.8 + Math.random() * 0.4;
89565
+ return new Date(Date.now() + Math.round(base * jitter)).toISOString();
89566
+ }
89567
+ function uploadLeaseAvailable(event, now = Date.now()) {
89568
+ if (!event.upload_lease_owner || !event.upload_lease_expires_at || Date.parse(event.upload_lease_expires_at) <= now) return true;
89569
+ const ownerPid = Number(/^uploader_(\d+)_/.exec(event.upload_lease_owner)?.[1]);
89570
+ if (!Number.isInteger(ownerPid) || ownerPid <= 0) return false;
89571
+ try {
89572
+ process.kill(ownerPid, 0);
89573
+ return false;
89574
+ } catch (error) {
89575
+ return error?.code === "ESRCH";
89576
+ }
89577
+ }
89578
+ var DiagnosticReporter = class {
89579
+ constructor(options) {
89580
+ this.options = options;
89581
+ this.filePath = join6(options.stateDir, "client-diagnostic-outbox.json");
89582
+ this.installStateDir = options.installStateDir ?? options.stateDir;
89583
+ }
89584
+ options;
89585
+ filePath;
89586
+ installStateDir;
89587
+ deliveredEventIds = /* @__PURE__ */ new Set();
89588
+ flushPromise = null;
89589
+ writeChain = Promise.resolve();
89590
+ installationId = null;
89591
+ context = {};
89592
+ identityEpoch = null;
89593
+ identityProof = null;
89594
+ uploadLeaseOwner = `uploader_${process.pid}_${randomBytes(12).toString("hex")}`;
89595
+ notifyHealth(error, fallbackCode) {
89596
+ try {
89597
+ this.options.onHealthError?.({
89598
+ code: error?.code ?? fallbackCode,
89599
+ message: error instanceof Error ? error.message : String(error ?? fallbackCode)
89600
+ });
89601
+ } catch {
89602
+ }
89603
+ }
89604
+ async serializeWrite(operation) {
89605
+ const queued = this.writeChain.then(operation, operation);
89606
+ this.writeChain = queued.catch(() => void 0);
89607
+ return queued;
89608
+ }
89609
+ setContext(context) {
89610
+ const previousUserId = normalizedOwnerUserId(this.context.user_id);
89611
+ const nextUserId = Object.prototype.hasOwnProperty.call(context, "user_id") ? normalizedOwnerUserId(context.user_id) : previousUserId;
89612
+ if (previousUserId !== nextUserId && !(previousUserId === null && nextUserId !== null)) {
89613
+ this.identityEpoch = null;
89614
+ this.identityProof = null;
89615
+ }
89616
+ this.context = { ...this.context, ...context };
89617
+ }
89618
+ resolveReporterIdentity(outbox) {
89619
+ const ownerUserId = normalizedOwnerUserId(this.context.user_id);
89620
+ const sharedIdentity = ensureIdentityEpochs(outbox);
89621
+ const resolved = (epoch) => {
89622
+ let proof = epoch === sharedIdentity.current_epoch ? sharedIdentity.current_proof : outbox.owned_epochs?.find((item) => item.identity_epoch === epoch && item.owner_user_id === ownerUserId)?.identity_proof;
89623
+ if (!proof && outbox.pending_claim?.identity_epoch === epoch) proof = outbox.pending_claim.identity_proof;
89624
+ const localProof = outbox.events.find((event) => event.identity_epoch === epoch && event.owner_user_id === ownerUserId && IDENTITY_PROOF_PATTERN.test(event.identity_proof || ""))?.identity_proof;
89625
+ const identityProof = [proof, localProof, this.identityEpoch === epoch ? this.identityProof : null].find((value) => IDENTITY_PROOF_PATTERN.test(value || "")) ?? newIdentityProof();
89626
+ this.identityProof = identityProof;
89627
+ return { identity_epoch: epoch, identity_proof: identityProof, owner_user_id: ownerUserId };
89628
+ };
89629
+ if (this.identityEpoch && IDENTITY_EPOCH_PATTERN.test(this.identityEpoch)) {
89630
+ const localEvents = outbox.events.filter((event) => event.identity_epoch === this.identityEpoch);
89631
+ const localOwner = localEvents.find((event) => event.owner_user_id !== null)?.owner_user_id ?? null;
89632
+ if (localOwner === ownerUserId) return resolved(this.identityEpoch);
89633
+ if (localOwner === null && ownerUserId !== null) {
89634
+ for (const event of localEvents) event.owner_user_id = ownerUserId;
89635
+ if (sharedIdentity.current_epoch === this.identityEpoch && sharedIdentity.current_owner_user_id === null) sharedIdentity.current_owner_user_id = ownerUserId;
89636
+ return resolved(this.identityEpoch);
89637
+ }
89638
+ }
89639
+ if (ownerUserId && outbox.pending_claim?.owner_user_id === ownerUserId && IDENTITY_EPOCH_PATTERN.test(outbox.pending_claim.identity_epoch)) {
89640
+ this.identityEpoch = outbox.pending_claim.identity_epoch;
89641
+ return resolved(this.identityEpoch);
89642
+ }
89643
+ if (sharedIdentity.current_owner_user_id === ownerUserId) {
89644
+ this.identityEpoch = sharedIdentity.current_epoch;
89645
+ } else {
89646
+ const owned = ownerUserId && this.installationId ? [...outbox.owned_epochs || []].reverse().find((item) => item.installation_id === this.installationId && item.owner_user_id === ownerUserId && IDENTITY_EPOCH_PATTERN.test(item.identity_epoch)) : null;
89647
+ this.identityEpoch = owned?.identity_epoch ?? newIdentityEpoch();
89648
+ this.identityProof = null;
89649
+ }
89650
+ return resolved(this.identityEpoch);
89651
+ }
89652
+ rememberOwnedEpoch(outbox, identity, installationId) {
89653
+ if (!identity.owner_user_id || identity.identity_epoch === outbox.identity?.current_epoch) return;
89654
+ outbox.owned_epochs ??= [];
89655
+ if (!outbox.owned_epochs.some((item) => item.installation_id === installationId && item.identity_epoch === identity.identity_epoch && item.owner_user_id === identity.owner_user_id)) {
89656
+ outbox.owned_epochs.push({ installation_id: installationId, identity_epoch: identity.identity_epoch, identity_proof: identity.identity_proof, owner_user_id: identity.owner_user_id, needs_reclaim: true });
89657
+ }
89658
+ }
89659
+ async claimContext() {
89660
+ const requestedUserId = normalizedOwnerUserId(this.context.user_id);
89661
+ if (!requestedUserId) return null;
89662
+ const claimHolder = {};
89663
+ try {
89664
+ await this.serializeWrite(() => withOutboxLock(this.filePath, async () => {
89665
+ const outbox = await this.readOutboxRecovering();
89666
+ const installationId = await this.installId();
89667
+ if (normalizedOwnerUserId(this.context.user_id) !== requestedUserId) return;
89668
+ const identity = this.resolveReporterIdentity(outbox);
89669
+ if (identity.owner_user_id !== requestedUserId) return;
89670
+ const claim = { installation_id: installationId, device_id: this.context.device_id ?? null, identity_epoch: identity.identity_epoch, identity_proof: identity.identity_proof };
89671
+ this.rememberOwnedEpoch(outbox, identity, claim.installation_id);
89672
+ const acknowledged = outbox.identity?.current_claimed;
89673
+ const ownedAcknowledged = outbox.owned_epochs?.find((item) => item.installation_id === claim.installation_id && item.identity_epoch === claim.identity_epoch && item.owner_user_id === requestedUserId);
89674
+ const alreadyClaimed = !outbox.pending_claim && (acknowledged?.installation_id === claim.installation_id && acknowledged.device_id === claim.device_id && acknowledged.identity_epoch === claim.identity_epoch && acknowledged.identity_proof === claim.identity_proof && acknowledged.owner_user_id === requestedUserId || ownedAcknowledged?.needs_reclaim === false && Boolean(ownedAcknowledged.last_claimed_at) && ownedAcknowledged.claimed_device_id === claim.device_id && ownedAcknowledged.identity_proof === claim.identity_proof);
89675
+ if (alreadyClaimed) return;
89676
+ claimHolder.value = { ...claim, owner_user_id: requestedUserId };
89677
+ const previous = outbox.pending_claim;
89678
+ outbox.pending_claim = previous?.installation_id === claim.installation_id && previous.device_id === claim.device_id && previous.identity_epoch === claim.identity_epoch ? previous : { ...claim, owner_user_id: requestedUserId, attempts: 0 };
89679
+ await writeOutbox(this.filePath, outbox);
89680
+ }, this.options.lockStaleMs));
89681
+ } catch (error) {
89682
+ this.notifyHealth(error, "DIAGNOSTIC_OUTBOX_WRITE_FAILED");
89683
+ throw error;
89684
+ }
89685
+ const resolvedClaim = claimHolder.value;
89686
+ if (!resolvedClaim) return null;
89687
+ if (normalizedOwnerUserId(this.context.user_id) !== requestedUserId) return null;
89688
+ const client = this.options.getClient?.() ?? null;
89689
+ if (!client) return null;
89690
+ try {
89691
+ const result = await client.post("/client-diagnostic-events/claim", {
89692
+ installation_id: resolvedClaim.installation_id,
89693
+ device_id: resolvedClaim.device_id,
89694
+ identity_epoch: resolvedClaim.identity_epoch,
89695
+ identity_proof: resolvedClaim.identity_proof
89696
+ });
89697
+ await withOutboxLock(this.filePath, async () => {
89698
+ const outbox = await this.readOutboxRecovering();
89699
+ const pendingClaim = outbox.pending_claim;
89700
+ if (pendingClaim?.installation_id === resolvedClaim.installation_id && pendingClaim.device_id === resolvedClaim.device_id && pendingClaim.identity_epoch === resolvedClaim.identity_epoch && pendingClaim.identity_proof === resolvedClaim.identity_proof && pendingClaim.owner_user_id === requestedUserId) {
89701
+ outbox.pending_claim = null;
89702
+ }
89703
+ if (outbox.identity?.current_epoch === resolvedClaim.identity_epoch && outbox.identity.current_owner_user_id === requestedUserId && outbox.identity.current_proof === resolvedClaim.identity_proof) {
89704
+ outbox.identity.current_claimed = { ...resolvedClaim, claimed_at: (/* @__PURE__ */ new Date()).toISOString() };
89705
+ }
89706
+ const owned = outbox.owned_epochs?.find((item) => item.installation_id === resolvedClaim.installation_id && item.identity_epoch === resolvedClaim.identity_epoch && item.owner_user_id === requestedUserId && item.identity_proof === resolvedClaim.identity_proof);
89707
+ if (owned) {
89708
+ owned.needs_reclaim = false;
89709
+ owned.last_claimed_at = (/* @__PURE__ */ new Date()).toISOString();
89710
+ owned.claimed_device_id = resolvedClaim.device_id;
89711
+ delete owned.last_error_code;
89712
+ }
89713
+ await writeOutbox(this.filePath, outbox);
89714
+ }, this.options.lockStaleMs);
89715
+ return result;
89716
+ } catch (error) {
89717
+ this.notifyHealth(error, "DIAGNOSTIC_CLAIM_FAILED");
89718
+ await withOutboxLock(this.filePath, async () => {
89719
+ const outbox = await this.readOutboxRecovering();
89720
+ const pendingClaim = outbox.pending_claim;
89721
+ if (pendingClaim?.installation_id === resolvedClaim.installation_id && pendingClaim.device_id === resolvedClaim.device_id && pendingClaim.identity_epoch === resolvedClaim.identity_epoch && pendingClaim.identity_proof === resolvedClaim.identity_proof && pendingClaim.owner_user_id === requestedUserId) {
89722
+ pendingClaim.attempts += 1;
89723
+ pendingClaim.last_attempt_at = (/* @__PURE__ */ new Date()).toISOString();
89724
+ pendingClaim.last_error_code = error instanceof ApiError ? error.code : error?.code ?? "DIAGNOSTIC_CLAIM_FAILED";
89725
+ }
89726
+ await writeOutbox(this.filePath, outbox);
89727
+ }, this.options.lockStaleMs);
89728
+ throw error;
89729
+ }
89730
+ }
89731
+ async installId() {
89732
+ if (this.options.getInstallationId) return this.options.getInstallationId();
89733
+ if (this.installationId) return this.installationId;
89734
+ const state = await ensureInstallState(this.installStateDir);
89735
+ this.installationId = state.install_id;
89736
+ return this.installationId;
89737
+ }
89738
+ async readOutboxRecovering() {
89739
+ try {
89740
+ return await readOutbox(this.filePath);
89741
+ } catch (error) {
89742
+ this.notifyHealth(error, "DIAGNOSTIC_OUTBOX_UNREADABLE");
89743
+ const quarantinePath = `${this.filePath}.corrupt.${Date.now()}`;
89744
+ try {
89745
+ await rename2(this.filePath, quarantinePath);
89746
+ } catch {
89747
+ throw error;
89748
+ }
89749
+ const now = (/* @__PURE__ */ new Date()).toISOString();
89750
+ const eventId = `evt_${randomBytes(16).toString("hex")}`;
89751
+ const outbox = {
89752
+ version: 1,
89753
+ events: [{ version: 1, client_event_id: eventId, device_id: null, body: {
89754
+ client_event_id: eventId,
89755
+ installation_id: await this.installId(),
89756
+ domain: "diagnostics",
89757
+ action: "outbox_recovered",
89758
+ status: "failure",
89759
+ error_code: "DIAGNOSTIC_OUTBOX_CORRUPT",
89760
+ message: "The diagnostic outbox was corrupt and was quarantined.",
89761
+ details: { quarantine_file: quarantinePath.split(/[\\/]/).pop() },
89762
+ occurred_at: now,
89763
+ identity_epoch: ""
89764
+ }, identity_epoch: "", owner_user_id: null, queued_at: now, attempts: 0 }],
89765
+ rejected_evicted: 0,
89766
+ dropped_capacity: 0,
89767
+ pending_claim: null,
89768
+ identity: null
89769
+ };
89770
+ ensureIdentityEpochs(outbox);
89771
+ this.identityEpoch = outbox.identity.current_epoch;
89772
+ const identity = this.resolveReporterIdentity(outbox);
89773
+ const recoveryEvent = outbox.events[0];
89774
+ recoveryEvent.identity_epoch = identity.identity_epoch;
89775
+ recoveryEvent.owner_user_id = identity.owner_user_id;
89776
+ recoveryEvent.body.identity_epoch = identity.identity_epoch;
89777
+ await writeOutbox(this.filePath, outbox);
89778
+ return outbox;
89779
+ }
89780
+ }
89781
+ async report(input) {
89782
+ const installationId = await this.installId();
89783
+ const eventId = input.client_event_id || `evt_${randomBytes(16).toString("hex")}`;
89784
+ if (this.deliveredEventIds.has(eventId)) return this.flush();
89785
+ try {
89786
+ await this.serializeWrite(() => withOutboxLock(this.filePath, async () => {
89787
+ const outbox = await this.readOutboxRecovering();
89788
+ const identity = this.resolveReporterIdentity(outbox);
89789
+ this.rememberOwnedEpoch(outbox, identity, installationId);
89790
+ if (outbox.events.some((event) => event.client_event_id === eventId)) return;
89791
+ const body = (0, import_diagnostic_sanitize.sanitizeDiagnosticPayload)({
89792
+ ...input,
89793
+ device_id: input.device_id ?? this.context.device_id ?? null,
89794
+ status: input.status ?? "failure",
89795
+ error_code: stableErrorCode(input.error_code),
89796
+ client_event_id: eventId,
89797
+ installation_id: installationId,
89798
+ identity_epoch: identity.identity_epoch,
89799
+ identity_proof: identity.identity_proof,
89800
+ occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString(),
89801
+ message: (0, import_diagnostic_sanitize.sanitizeDiagnosticText)(input.message),
89802
+ details: input.details === void 0 ? void 0 : (0, import_diagnostic_sanitize.sanitizeDiagnosticValue)(input.details)
89803
+ });
89804
+ delete body.user_id;
89805
+ outbox.events.push({ version: 1, client_event_id: eventId, device_id: body.device_id ?? null, body, identity_epoch: identity.identity_epoch, identity_proof: identity.identity_proof, owner_user_id: identity.owner_user_id, queued_at: (/* @__PURE__ */ new Date()).toISOString(), attempts: 0 });
89806
+ await writeOutbox(this.filePath, outbox);
89807
+ }, this.options.lockStaleMs));
89808
+ } catch (error) {
89809
+ this.notifyHealth(error, "DIAGNOSTIC_OUTBOX_WRITE_FAILED");
89810
+ throw error;
89811
+ }
89812
+ return this.flush();
89813
+ }
89814
+ async status() {
89815
+ try {
89816
+ return await withOutboxLock(this.filePath, async () => {
89817
+ const outbox = await this.readOutboxRecovering();
89818
+ const events = outbox.events;
89819
+ const latestError = [...events].filter((event) => !event.rejected_at && event.last_error_code).sort((left, right) => String(right.last_attempt_at).localeCompare(String(left.last_attempt_at)))[0];
89820
+ const next = events.filter((event) => !event.rejected_at && event.next_attempt_at).map((event) => event.next_attempt_at).sort()[0] ?? null;
89821
+ return {
89822
+ pending: events.filter((event) => !event.rejected_at).length,
89823
+ rejected: events.filter((event) => event.rejected_at).length,
89824
+ rejected_evicted: Number(outbox.rejected_evicted ?? 0),
89825
+ dropped_capacity: Number(outbox.dropped_capacity ?? 0),
89826
+ claim_pending: Boolean(outbox.pending_claim),
89827
+ claim_attempts: Number(outbox.pending_claim?.attempts ?? 0),
89828
+ health: Number(outbox.dropped_capacity ?? 0) > 0 || Boolean(latestError) || Boolean(outbox.pending_claim?.last_error_code) ? "degraded" : "healthy",
89829
+ oldest_queued_at: events.map((event) => event.queued_at).sort()[0] ?? null,
89830
+ last_error_code: latestError?.last_error_code ?? null,
89831
+ next_attempt_at: next
89832
+ };
89833
+ }, this.options.lockStaleMs);
89834
+ } catch (error) {
89835
+ this.notifyHealth(error, "DIAGNOSTIC_OUTBOX_UNREADABLE");
89836
+ return {
89837
+ pending: null,
89838
+ rejected: null,
89839
+ rejected_evicted: null,
89840
+ dropped_capacity: null,
89841
+ claim_pending: null,
89842
+ claim_attempts: null,
89843
+ health: "degraded",
89844
+ oldest_queued_at: null,
89845
+ last_error_code: error?.code ?? "DIAGNOSTIC_OUTBOX_UNREADABLE",
89846
+ next_attempt_at: null
89847
+ };
89848
+ }
89849
+ }
89850
+ async flush(options = {}) {
89851
+ if (this.flushPromise) return this.flushPromise;
89852
+ this.flushPromise = (async () => {
89853
+ let delivered = 0;
89854
+ while (true) {
89855
+ const authenticatedClient = this.options.getClient?.() ?? null;
89856
+ const explicitAnonymousClient = this.options.getAnonymousClient?.() ?? null;
89857
+ if (!authenticatedClient && !explicitAnonymousClient) break;
89858
+ const selected = await withOutboxLock(this.filePath, async () => {
89859
+ const outbox = await this.readOutboxRecovering();
89860
+ ensureIdentityEpochs(outbox);
89861
+ const authenticatedUserId = normalizedOwnerUserId(this.context.user_id);
89862
+ const now = Date.now();
89863
+ const candidate = outbox.events.find((item) => !item.rejected_at && uploadLeaseAvailable(item, now) && (options.force || !item.next_attempt_at || Date.parse(item.next_attempt_at) <= now));
89864
+ if (!candidate) return null;
89865
+ const proofFallback = candidate.authentication_required === true && IDENTITY_PROOF_PATTERN.test(candidate.identity_proof || "");
89866
+ const authenticated = Boolean(!proofFallback && authenticatedClient && candidate.owner_user_id && candidate.owner_user_id === authenticatedUserId);
89867
+ candidate.upload_lease_owner = this.uploadLeaseOwner;
89868
+ candidate.upload_lease_expires_at = new Date(now + UPLOAD_LEASE_MS).toISOString();
89869
+ await writeOutbox(this.filePath, outbox);
89870
+ return { event: candidate, authenticated, proofFallback };
89871
+ }, this.options.lockStaleMs);
89872
+ if (!selected) break;
89873
+ const { event } = selected;
89874
+ const client = selected.authenticated ? authenticatedClient : explicitAnonymousClient ?? (selected.proofFallback || normalizedOwnerUserId(this.context.user_id) !== null ? new KeySyncApiClient({ baseUrl: this.options.baseUrl }) : authenticatedClient);
89875
+ if (!client) break;
89876
+ try {
89877
+ await client.post("/client-diagnostic-events", event.body);
89878
+ this.deliveredEventIds.add(event.client_event_id);
89879
+ delivered += 1;
89880
+ await withOutboxLock(this.filePath, async () => {
89881
+ const outbox = await this.readOutboxRecovering();
89882
+ outbox.events = outbox.events.filter((item) => item.client_event_id !== event.client_event_id || item.upload_lease_owner !== this.uploadLeaseOwner);
89883
+ await writeOutbox(this.filePath, outbox);
89884
+ }, this.options.lockStaleMs);
89885
+ } catch (error) {
89886
+ const compactable = error instanceof ApiError && error.status === 413 && !(event.body?.details && typeof event.body.details === "object" && event.body.details.payload_compacted === true || event.body.payload_compacted === true);
89887
+ if (compactable) {
89888
+ await withOutboxLock(this.filePath, async () => {
89889
+ const outbox = await this.readOutboxRecovering();
89890
+ const current = outbox.events.find((item) => item.client_event_id === event.client_event_id);
89891
+ if (!current || current.upload_lease_owner !== this.uploadLeaseOwner) return;
89892
+ delete current.upload_lease_owner;
89893
+ delete current.upload_lease_expires_at;
89894
+ current.body = (0, import_diagnostic_sanitize.compactDiagnosticPayload)(current.body);
89895
+ current.body.identity_epoch = current.identity_epoch;
89896
+ if (current.device_id) current.body.device_id = current.device_id;
89897
+ current.attempts += 1;
89898
+ current.last_attempt_at = (/* @__PURE__ */ new Date()).toISOString();
89899
+ current.last_error_code = error.code || "PAYLOAD_TOO_LARGE";
89900
+ delete current.next_attempt_at;
89901
+ await writeOutbox(this.filePath, outbox);
89902
+ }, this.options.lockStaleMs);
89903
+ continue;
89904
+ }
89905
+ this.notifyHealth(error, "DIAGNOSTIC_UPLOAD_FAILED");
89906
+ const rejected = permanent(error);
89907
+ await withOutboxLock(this.filePath, async () => {
89908
+ const outbox = await this.readOutboxRecovering();
89909
+ const current = outbox.events.find((item) => item.client_event_id === event.client_event_id);
89910
+ if (current?.upload_lease_owner === this.uploadLeaseOwner) {
89911
+ delete current.upload_lease_owner;
89912
+ delete current.upload_lease_expires_at;
89913
+ current.attempts += 1;
89914
+ current.last_attempt_at = (/* @__PURE__ */ new Date()).toISOString();
89915
+ current.last_error_code = error instanceof ApiError ? error.code : error?.code ?? "DIAGNOSTIC_UPLOAD_FAILED";
89916
+ if (error instanceof ApiError && error.status === 401) current.authentication_required = true;
89917
+ if (rejected) {
89918
+ current.rejected_at = current.last_attempt_at;
89919
+ current.rejection_status = error instanceof ApiError ? error.status : void 0;
89920
+ current.rejection_code = error instanceof ApiError ? error.code : null;
89921
+ } else current.next_attempt_at = nextAttemptAt(current.attempts);
89922
+ await writeOutbox(this.filePath, outbox);
89923
+ }
89924
+ }, this.options.lockStaleMs);
89925
+ if (!rejected) break;
89926
+ }
89927
+ }
89928
+ return { delivered, pending: (await this.readOutboxRecovering()).events.length };
89929
+ })().finally(() => {
89930
+ this.flushPromise = null;
89931
+ });
89932
+ return this.flushPromise;
89933
+ }
89934
+ };
89935
+
89129
89936
  // packages/client-core/src/index.ts
89130
89937
  var BridgeConfigWriteError = class extends Error {
89131
89938
  code;
@@ -89167,16 +89974,15 @@ function serverKeyFromBaseUrl(baseUrl) {
89167
89974
  }
89168
89975
  }
89169
89976
  function stateDirForBaseUrl(baseUrl) {
89170
- return join6(defaultStateRoot(), "hosts", sanitizeHostForPath(hostFromBaseUrl(baseUrl)));
89977
+ return join7(defaultStateRoot(), "hosts", sanitizeHostForPath(hostFromBaseUrl(baseUrl)));
89171
89978
  }
89172
89979
  function defaultStateDir(baseUrl) {
89173
89980
  if (process.env.KEYSYNC_STATE_DIR) return process.env.KEYSYNC_STATE_DIR;
89174
- return baseUrl ? stateDirForBaseUrl(baseUrl) : join6(defaultStateRoot(), "hosts", "default");
89981
+ return baseUrl ? stateDirForBaseUrl(baseUrl) : join7(defaultStateRoot(), "hosts", "default");
89175
89982
  }
89176
89983
  function defaultInstallStateDir(baseUrl, stateDir = defaultStateDir(baseUrl)) {
89177
89984
  if (process.env.KEYSYNC_INSTALL_STATE_DIR) return process.env.KEYSYNC_INSTALL_STATE_DIR;
89178
- if (process.env.KEYSYNC_STATE_DIR && !process.env.KEYSYNC_STATE_ROOT) return stateDir;
89179
- return defaultStateRoot();
89985
+ return stateDir;
89180
89986
  }
89181
89987
  function uniquePaths2(paths) {
89182
89988
  return [...new Set(paths.filter((item) => item.trim()))];
@@ -89218,7 +90024,7 @@ function legacyStateDirs(baseUrl) {
89218
90024
  }
89219
90025
  async function readSession(stateDir = defaultStateDir()) {
89220
90026
  try {
89221
- return JSON.parse(await readFile6(join6(stateDir, "session.json"), "utf8"));
90027
+ return JSON.parse(await readFile7(join7(stateDir, "session.json"), "utf8"));
89222
90028
  } catch {
89223
90029
  return null;
89224
90030
  }
@@ -89226,13 +90032,13 @@ async function readSession(stateDir = defaultStateDir()) {
89226
90032
  async function writeSession(session, stateDir = defaultStateDir()) {
89227
90033
  await mkdir3(stateDir, { recursive: true, mode: 448 });
89228
90034
  if (platform() !== "win32") await chmod2(stateDir, 448);
89229
- const sessionPath = join6(stateDir, "session.json");
89230
- const temporaryPath = `${sessionPath}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
90035
+ const sessionPath = join7(stateDir, "session.json");
90036
+ const temporaryPath = `${sessionPath}.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`;
89231
90037
  try {
89232
90038
  await writeFile3(temporaryPath, `${JSON.stringify(session, null, 2)}
89233
90039
  `, { mode: 384 });
89234
90040
  if (platform() !== "win32") await chmod2(temporaryPath, 384);
89235
- await rename2(temporaryPath, sessionPath);
90041
+ await rename3(temporaryPath, sessionPath);
89236
90042
  if (platform() !== "win32") await chmod2(sessionPath, 384);
89237
90043
  } catch (error) {
89238
90044
  await rm3(temporaryPath, { force: true }).catch(() => void 0);
@@ -89241,11 +90047,13 @@ async function writeSession(session, stateDir = defaultStateDir()) {
89241
90047
  }
89242
90048
  async function readDeviceStateFromDir(stateDir) {
89243
90049
  try {
89244
- const parsed = JSON.parse(await readFile6(join6(stateDir, "device.json"), "utf8"));
90050
+ const parsed = JSON.parse(await readFile7(join7(stateDir, "device.json"), "utf8"));
89245
90051
  if (typeof parsed.device_id !== "string" || !parsed.device_id.trim()) return null;
89246
90052
  return {
89247
90053
  device_id: parsed.device_id,
89248
- ...typeof parsed.user_id === "string" && parsed.user_id.trim() ? { user_id: parsed.user_id } : {}
90054
+ ...typeof parsed.user_id === "string" && parsed.user_id.trim() ? { user_id: parsed.user_id } : {},
90055
+ ...typeof parsed.claim_ticket === "string" && parsed.claim_ticket.trim() ? { claim_ticket: parsed.claim_ticket } : {},
90056
+ ...typeof parsed.install_id_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(parsed.install_id_hash) ? { install_id_hash: parsed.install_id_hash } : {}
89249
90057
  };
89250
90058
  } catch {
89251
90059
  return null;
@@ -89263,17 +90071,17 @@ async function readDeviceState(stateDir = defaultStateDir(), options = {}) {
89263
90071
  }
89264
90072
  async function writeDeviceState(device, stateDir = defaultStateDir()) {
89265
90073
  await mkdir3(stateDir, { recursive: true });
89266
- await writeFile3(join6(stateDir, "device.json"), JSON.stringify(device, null, 2));
90074
+ await writeFile3(join7(stateDir, "device.json"), JSON.stringify(device, null, 2));
89267
90075
  }
89268
90076
  async function clearDeviceState(stateDir = defaultStateDir()) {
89269
- await rm3(join6(stateDir, "device.json"), { force: true });
90077
+ await rm3(join7(stateDir, "device.json"), { force: true });
89270
90078
  }
89271
90079
  function localDeviceSettingsPath(stateDir = defaultStateDir()) {
89272
- return join6(stateDir, "device-settings.json");
90080
+ return join7(stateDir, "device-settings.json");
89273
90081
  }
89274
90082
  async function readLocalDeviceSettings(stateDir = defaultStateDir()) {
89275
90083
  try {
89276
- const parsed = JSON.parse(await readFile6(localDeviceSettingsPath(stateDir), "utf8"));
90084
+ const parsed = JSON.parse(await readFile7(localDeviceSettingsPath(stateDir), "utf8"));
89277
90085
  return {
89278
90086
  version: 1,
89279
90087
  ...typeof parsed.device_id === "string" && parsed.device_id.trim() ? { device_id: parsed.device_id } : {},
@@ -89290,7 +90098,7 @@ async function readLocalDeviceSettings(stateDir = defaultStateDir()) {
89290
90098
  }
89291
90099
  async function readInstallStateFromDir(installStateDir) {
89292
90100
  try {
89293
- const parsed = JSON.parse(await readFile6(join6(installStateDir, "install.json"), "utf8"));
90101
+ const parsed = JSON.parse(await readFile7(join7(installStateDir, "install.json"), "utf8"));
89294
90102
  if (typeof parsed.install_id !== "string" || !parsed.install_id.trim()) return null;
89295
90103
  if (typeof parsed.created_at !== "string" || !parsed.created_at.trim()) return null;
89296
90104
  return { install_id: parsed.install_id, created_at: parsed.created_at };
@@ -89308,25 +90116,32 @@ async function readInstallState(installStateDir = defaultInstallStateDir(), opti
89308
90116
  }
89309
90117
  return null;
89310
90118
  }
89311
- async function writeInstallState(install, installStateDir = defaultInstallStateDir()) {
89312
- await mkdir3(installStateDir, { recursive: true });
89313
- await writeFile3(join6(installStateDir, "install.json"), JSON.stringify(install, null, 2), { mode: 384 });
89314
- }
89315
90119
  async function ensureInstallState(installStateDir = defaultInstallStateDir(), options = {}) {
89316
- const existing = await readInstallStateFromDir(installStateDir);
89317
- if (existing) return existing;
89318
- const legacy = await readInstallState(installStateDir, options);
89319
- if (legacy) {
89320
- await writeInstallState(legacy, installStateDir);
89321
- return legacy;
89322
- }
89323
- const install = { install_id: randomBytes(32).toString("base64url"), created_at: (/* @__PURE__ */ new Date()).toISOString() };
89324
- await writeInstallState(install, installStateDir);
89325
- return install;
90120
+ return (0, import_install_identity.ensureInstallIdentitySync)(installStateDir, { legacyStateDirs: options.legacyStateDirs });
89326
90121
  }
89327
90122
  function installIdHashFor(installId) {
89328
90123
  return `sha256:${createHash5("sha256").update(`keysync-install-id:${installId}`).digest("hex")}`;
89329
90124
  }
90125
+ async function installCandidatesForServer(baseUrl, stateDir = defaultStateDir(baseUrl)) {
90126
+ const hostDirectory = defaultInstallStateDir(baseUrl, stateDir);
90127
+ const directories = [hostDirectory];
90128
+ if (resolve2(stateDir) === resolve2(stateDirForBaseUrl(baseUrl)) && resolve2(defaultStateRoot()) !== resolve2(hostDirectory)) {
90129
+ directories.push(defaultStateRoot());
90130
+ }
90131
+ const candidates = [];
90132
+ for (const directory of directories) {
90133
+ const install = await readInstallState(directory);
90134
+ if (install) candidates.push({ install, directory });
90135
+ }
90136
+ return candidates;
90137
+ }
90138
+ async function installForServerDevice(baseUrl, stateDir = defaultStateDir(baseUrl), verifiedHash) {
90139
+ if (verifiedHash) {
90140
+ const matching = (await installCandidatesForServer(baseUrl, stateDir)).find(({ install }) => installIdHashFor(install.install_id) === verifiedHash);
90141
+ if (matching) return matching.install;
90142
+ }
90143
+ return ensureInstallState(defaultInstallStateDir(baseUrl, stateDir), { legacyStateDirs: legacyStateDirs(baseUrl) });
90144
+ }
89330
90145
  function clientFromSession(session, options = {}) {
89331
90146
  return new KeySyncApiClient({ ...options, baseUrl: session.baseUrl, accessToken: session.access_token });
89332
90147
  }
@@ -89365,8 +90180,6 @@ async function maybeMigrateCodexSessionsAfterApply(input) {
89365
90180
  sqlite_rows_changed: 0,
89366
90181
  rollout_files_changed: 0,
89367
90182
  rollout_files_skipped: 0,
89368
- backups_created: 0,
89369
- backups_cleaned: 0,
89370
90183
  warnings: [{
89371
90184
  code: "MIGRATION_FAILED",
89372
90185
  message: error instanceof Error ? error.message : "Codex session migration failed."
@@ -89423,10 +90236,10 @@ function canonicalJson(value) {
89423
90236
  return JSON.stringify(canonicalize(value));
89424
90237
  }
89425
90238
  function localApplyDir(stateDir) {
89426
- return join6(stateDir, "local-apply");
90239
+ return join7(stateDir, "local-apply");
89427
90240
  }
89428
90241
  function localApplyEventPath(stateDir) {
89429
- return join6(stateDir, "local-apply-events.jsonl");
90242
+ return join7(stateDir, "local-apply-events.jsonl");
89430
90243
  }
89431
90244
  var REPORT_OUTBOX_MAX_EVENTS = 200;
89432
90245
  var ReportOutboxError = class extends Error {
@@ -89535,7 +90348,7 @@ var reportOutboxDetailKeys = /* @__PURE__ */ new Set([
89535
90348
  "ownership_attempts"
89536
90349
  ]);
89537
90350
  function reportOutboxPath(stateDir) {
89538
- return join6(stateDir, "report-outbox.json");
90351
+ return join7(stateDir, "report-outbox.json");
89539
90352
  }
89540
90353
  function reportOutboxKind(path) {
89541
90354
  if (/^\/devices\/[^/]+\/apps\/[^/]+\/apply-report$/.test(path)) return "apply";
@@ -89578,7 +90391,7 @@ function sanitizeReportOutboxBody(path, body) {
89578
90391
  throw new ReportOutboxError("REPORT_OUTBOX_CORRUPT");
89579
90392
  })
89580
90393
  );
89581
- if (kind === "apply" && (typeof sanitized.snapshot_id !== "string" || !["success", "failed"].includes(String(sanitized.status))) || kind === "actual" && (typeof sanitized.local_app_detected !== "boolean" || typeof sanitized.config_applied !== "boolean") || kind === "error" && (typeof sanitized.error_type !== "string" || typeof sanitized.message !== "string")) {
90394
+ if (kind === "apply" && (typeof sanitized.snapshot_id !== "string" || !["success", "failed", "conflict"].includes(String(sanitized.status))) || kind === "actual" && (typeof sanitized.local_app_detected !== "boolean" || typeof sanitized.config_applied !== "boolean") || kind === "error" && (typeof sanitized.error_type !== "string" || typeof sanitized.message !== "string")) {
89582
90395
  throw new ReportOutboxError("REPORT_OUTBOX_CORRUPT");
89583
90396
  }
89584
90397
  return sanitized;
@@ -89592,7 +90405,7 @@ function compactReportOutbox(outbox) {
89592
90405
  async function readReportOutbox(stateDir) {
89593
90406
  let content;
89594
90407
  try {
89595
- content = await readFile6(reportOutboxPath(stateDir), "utf8");
90408
+ content = await readFile7(reportOutboxPath(stateDir), "utf8");
89596
90409
  } catch (error) {
89597
90410
  if (errnoCode(error) === "ENOENT") return { version: 1, events: [] };
89598
90411
  throw new ReportOutboxError("REPORT_OUTBOX_UNREADABLE", error);
@@ -89686,7 +90499,7 @@ async function flushPendingReports(session, stateDir = defaultStateDir()) {
89686
90499
  );
89687
90500
  }
89688
90501
  async function postReportDurably(client, stateDir, path, body) {
89689
- const clientEventId = typeof body.client_event_id === "string" && body.client_event_id ? body.client_event_id : `evt_${randomBytes(16).toString("hex")}`;
90502
+ const clientEventId = typeof body.client_event_id === "string" && body.client_event_id ? body.client_event_id : `evt_${randomBytes2(16).toString("hex")}`;
89690
90503
  const outbox = await readReportOutbox(stateDir);
89691
90504
  if (!outbox.events.some((event) => event.client_event_id === clientEventId)) {
89692
90505
  if (outbox.events.length >= REPORT_OUTBOX_MAX_EVENTS) {
@@ -89721,17 +90534,17 @@ async function postReportGroupDurably(client, stateDir, reports) {
89721
90534
  if (rejection) throw rejection;
89722
90535
  }
89723
90536
  function localApplyKeyPath(stateDir) {
89724
- return join6(localApplyDir(stateDir), "fingerprint.key");
90537
+ return join7(localApplyDir(stateDir), "fingerprint.key");
89725
90538
  }
89726
90539
  function localApplyRecordPath(stateDir, deviceId, appId, mode) {
89727
- return join6(localApplyDir(stateDir), mode, `${sanitizeHostForPath(`${deviceId}-${appId}`)}.json`);
90540
+ return join7(localApplyDir(stateDir), mode, `${sanitizeHostForPath(`${deviceId}-${appId}`)}.json`);
89728
90541
  }
89729
90542
  async function localApplyFingerprintKey(stateDir) {
89730
90543
  const path = localApplyKeyPath(stateDir);
89731
90544
  try {
89732
- return Buffer.from((await readFile6(path, "utf8")).trim(), "base64");
90545
+ return Buffer.from((await readFile7(path, "utf8")).trim(), "base64");
89733
90546
  } catch {
89734
- const key = randomBytes(32);
90547
+ const key = randomBytes2(32);
89735
90548
  await mkdir3(localApplyDir(stateDir), { recursive: true });
89736
90549
  await writeFile3(path, `${key.toString("base64")}
89737
90550
  `, { mode: 384 });
@@ -89840,20 +90653,40 @@ function buildSourceFingerprintInput(appName, appId, source, provider, selectedE
89840
90653
  }
89841
90654
  async function readLocalApplyRecord(stateDir, deviceId, appId, mode) {
89842
90655
  try {
89843
- return JSON.parse(await readFile6(localApplyRecordPath(stateDir, deviceId, appId, mode), "utf8"));
90656
+ return JSON.parse(await readFile7(localApplyRecordPath(stateDir, deviceId, appId, mode), "utf8"));
89844
90657
  } catch {
89845
90658
  return null;
89846
90659
  }
89847
90660
  }
89848
90661
  async function writeLocalApplyRecord(stateDir, record) {
89849
90662
  const path = localApplyRecordPath(stateDir, record.device_id, record.app_id, record.apply_mode);
89850
- await mkdir3(join6(localApplyDir(stateDir), record.apply_mode), { recursive: true });
90663
+ await mkdir3(join7(localApplyDir(stateDir), record.apply_mode), { recursive: true });
89851
90664
  await writeFile3(path, `${JSON.stringify(record, null, 2)}
89852
90665
  `, { mode: 384 });
89853
90666
  }
89854
90667
  async function clearLocalApplyRecord(stateDir, deviceId, appId, mode) {
89855
90668
  await rm3(localApplyRecordPath(stateDir, deviceId, appId, mode), { force: true });
89856
90669
  }
90670
+ function writtenAccountKeyFor(provider) {
90671
+ return typeof provider.values?._import_source_key === "string" ? provider.values._import_source_key.trim() || null : null;
90672
+ }
90673
+ async function heldWriteReason(input) {
90674
+ const expectedProviderType = input.app.app_name === "Codex" ? "codex_official_login" : input.app.app_name === "ClaudeCode" ? "claude_official_login" : null;
90675
+ if (!expectedProviderType || input.provider.type !== expectedProviderType) return null;
90676
+ const previousProviderId = input.previousRecord?.resolved_provider_id?.trim() || null;
90677
+ if (previousProviderId && previousProviderId !== input.provider.id) return null;
90678
+ if (!input.probe) clearLocalModelConfigProbeCache();
90679
+ const probe = await (input.probe ?? probeLocalModelConfig)(input.app.app_name);
90680
+ if (probe.state === "unreadable" || probe.state === "unknown") return "retry";
90681
+ if (probe.state === "unconfigured") return null;
90682
+ if (probe.state === "local_apikey") return "LOCAL_KIND_MISMATCH";
90683
+ const localAccount = probe.credentials.find((credential) => credential.kind === "oauth")?.import_source_key?.trim() || null;
90684
+ if (!localAccount) return "retry";
90685
+ const storedAccount = writtenAccountKeyFor(input.provider);
90686
+ if (!storedAccount) return null;
90687
+ const writtenAccount = input.previousRecord?.written_account_key?.trim() || storedAccount;
90688
+ return localAccount === writtenAccount ? null : "LOCAL_ACCOUNT_MISMATCH";
90689
+ }
89857
90690
  async function recordApplyAttempt(input) {
89858
90691
  await writeLocalApplyRecord(input.stateDir, {
89859
90692
  version: 2,
@@ -89876,7 +90709,8 @@ async function recordApplyAttempt(input) {
89876
90709
  bridge_generation: input.bridgeGeneration ?? null,
89877
90710
  failure_code: input.failureCode,
89878
90711
  failure_message: input.failureMessage,
89879
- adopted_local: input.adoptedLocal
90712
+ adopted_local: input.adoptedLocal,
90713
+ written_account_key: input.writtenAccountKey
89880
90714
  });
89881
90715
  }
89882
90716
  async function appendLocalApplyEvent(stateDir, event) {
@@ -89887,14 +90721,16 @@ async function appendLocalApplyEvent(stateDir, event) {
89887
90721
  function localApplyEventMessage(status, message) {
89888
90722
  if (status === "success") return "APPLIED";
89889
90723
  const candidate = message?.trim() ?? "";
90724
+ if (status === "conflict") return /^[A-Z0-9_ -]{1,80}$/.test(candidate) ? candidate : "LOCAL_ACCOUNT_MISMATCH";
89890
90725
  return /^[A-Z0-9_ -]{1,80}$/.test(candidate) ? candidate : "APPLY_FAILED";
89891
90726
  }
89892
- async function recordDaemonAutoApplyEvent(input) {
89893
- if (input.trigger !== "daemon_auto") return;
90727
+ async function recordLocalApplyEvent(input) {
90728
+ const trigger = input.trigger ?? "daemon_auto";
90729
+ if (trigger !== "daemon_auto" && input.status !== "conflict") return;
89894
90730
  const event = {
89895
90731
  version: 2,
89896
90732
  id: [
89897
- "daemon_auto",
90733
+ trigger,
89898
90734
  input.session.user_id ?? "unknown_user",
89899
90735
  input.deviceId,
89900
90736
  input.app.id,
@@ -89903,7 +90739,7 @@ async function recordDaemonAutoApplyEvent(input) {
89903
90739
  input.status,
89904
90740
  input.reportedAt
89905
90741
  ].map((part) => sanitizeHostForPath(part)).join(":"),
89906
- trigger: "daemon_auto",
90742
+ trigger,
89907
90743
  server_key: serverKeyFromBaseUrl(input.session.baseUrl),
89908
90744
  user_id: input.session.user_id ?? null,
89909
90745
  device_id: input.deviceId,
@@ -89979,12 +90815,12 @@ async function enrichApplyFailureMessage(stateDir, failureCode, message) {
89979
90815
  };
89980
90816
  }
89981
90817
  function randomBase64(byteLength = 32) {
89982
- return randomBytes(byteLength).toString("base64");
90818
+ return randomBytes2(byteLength).toString("base64");
89983
90819
  }
89984
90820
  function encryptWithAesGcm(rawKey, plaintext) {
89985
90821
  const key = Buffer.from(rawKey);
89986
90822
  if (key.length !== 32) throw new Error("AES-256-GCM key must be 32 bytes");
89987
- const iv = randomBytes(12);
90823
+ const iv = randomBytes2(12);
89988
90824
  const cipher = createCipheriv("aes-256-gcm", key, iv);
89989
90825
  const body = Buffer.concat([cipher.update(typeof plaintext === "string" ? Buffer.from(plaintext, "utf8") : Buffer.from(plaintext)), cipher.final()]);
89990
90826
  return {
@@ -90010,7 +90846,7 @@ function deriveVaultKek(vaultPassword, kdf) {
90010
90846
  return pbkdf2Sync(vaultPassword, Buffer.from(kdf.salt, "base64"), iterations, 32, "sha256");
90011
90847
  }
90012
90848
  async function createVaultBootstrapInput(vaultPassword, name = "Personal") {
90013
- const vaultKey = randomBytes(32);
90849
+ const vaultKey = randomBytes2(32);
90014
90850
  const keyPair = generateKeyPairSync("ec", {
90015
90851
  namedCurve: "prime256v1",
90016
90852
  publicKeyEncoding: { type: "spki", format: "der" },
@@ -90050,7 +90886,7 @@ function wrapSecretDataKeyForUser(recipientPublicKeyBase64, secretDataKey) {
90050
90886
  };
90051
90887
  }
90052
90888
  function createProviderSecretEnvelopeV2WithGrants(vaultId, vaultKey, secrets, preservedUserGrants) {
90053
- const secretDataKey = randomBytes(32);
90889
+ const secretDataKey = randomBytes2(32);
90054
90890
  return {
90055
90891
  encrypted_secrets: Object.fromEntries(Object.entries(secrets).map(([key, value]) => [key, encryptWithAesGcm(secretDataKey, value)])),
90056
90892
  grants: [
@@ -90090,6 +90926,15 @@ async function buildProviderSecretEnvelopeForImport(client, providerId, vaultId,
90090
90926
  const preserved = providerId ? await fetchPreservedProviderUserGrants(client, providerId) : [];
90091
90927
  return createProviderSecretEnvelopeV2WithGrants(vaultId, vaultKey, secrets, preserved);
90092
90928
  }
90929
+ async function setupVaultMaterial(client, vaultPassword) {
90930
+ if (!vaultPassword) throw new Error("Vault Password is required");
90931
+ const { body, vaultKey } = await createVaultBootstrapInput(vaultPassword);
90932
+ const bootstrap = await client.post("/vault/bootstrap", body);
90933
+ return { vaultKeys: /* @__PURE__ */ new Map([[bootstrap.vault_id, vaultKey]]), privateKey: null };
90934
+ }
90935
+ function accessVaultMaterial(client, vaultPassword, mode) {
90936
+ return mode === "setup" ? setupVaultMaterial(client, vaultPassword) : unlockVaultMaterial(client, vaultPassword);
90937
+ }
90093
90938
  async function unlockOrBootstrapVault(client, vaultPassword) {
90094
90939
  if (!vaultPassword) throw new Error("Vault Password is required");
90095
90940
  try {
@@ -90099,17 +90944,14 @@ async function unlockOrBootstrapVault(client, vaultPassword) {
90099
90944
  const message = error instanceof Error ? error.message : "";
90100
90945
  if (!isUninitialized && !message.includes("Vault is not initialized")) throw error;
90101
90946
  }
90102
- const { body, vaultKey } = await createVaultBootstrapInput(vaultPassword);
90103
- let bootstrap;
90104
90947
  try {
90105
- bootstrap = await client.post("/vault/bootstrap", body);
90948
+ return await setupVaultMaterial(client, vaultPassword);
90106
90949
  } catch (error) {
90107
90950
  if (error instanceof ApiError && error.code === "VAULT_ALREADY_INITIALIZED") {
90108
90951
  return unlockVaultMaterial(client, vaultPassword);
90109
90952
  }
90110
90953
  throw error;
90111
90954
  }
90112
- return { vaultKeys: /* @__PURE__ */ new Map([[bootstrap.vault_id, vaultKey]]), privateKey: null };
90113
90955
  }
90114
90956
  async function unlockOrBootstrapVaultKeys(client, vaultPassword) {
90115
90957
  return (await unlockOrBootstrapVault(client, vaultPassword)).vaultKeys;
@@ -90179,28 +91021,28 @@ function snapshotProviderStrategyMap(snapshot) {
90179
91021
  return result;
90180
91022
  }
90181
91023
  function bridgeDir(stateDir) {
90182
- return join6(stateDir, "bridge", "bifrost");
91024
+ return join7(stateDir, "bridge", "bifrost");
90183
91025
  }
90184
91026
  function bridgeBifrostConfigPath(stateDir) {
90185
- return join6(bridgeDir(stateDir), "config.json");
91027
+ return join7(bridgeDir(stateDir), "config.json");
90186
91028
  }
90187
91029
  function bridgeRouteManifestPath(stateDir) {
90188
- return join6(bridgeDir(stateDir), "keysync-routes.json");
91030
+ return join7(bridgeDir(stateDir), "keysync-routes.json");
90189
91031
  }
90190
91032
  function bridgeRuntimePath(stateDir) {
90191
- return join6(bridgeDir(stateDir), "runtime.json");
91033
+ return join7(bridgeDir(stateDir), "runtime.json");
90192
91034
  }
90193
91035
  function bridgeStartupStdoutPath(stateDir) {
90194
- return join6(bridgeDir(stateDir), "bridge-startup.stdout.log");
91036
+ return join7(bridgeDir(stateDir), "bridge-startup.stdout.log");
90195
91037
  }
90196
91038
  function bridgeStartupStderrPath(stateDir) {
90197
- return join6(bridgeDir(stateDir), "bridge-startup.stderr.log");
91039
+ return join7(bridgeDir(stateDir), "bridge-startup.stderr.log");
90198
91040
  }
90199
91041
  function bridgeTokenPath(stateDir) {
90200
- return join6(bridgeDir(stateDir), "token.json");
91042
+ return join7(bridgeDir(stateDir), "token.json");
90201
91043
  }
90202
91044
  function bridgeGenerationPath(stateDir) {
90203
- return join6(bridgeDir(stateDir), "generation.json");
91045
+ return join7(bridgeDir(stateDir), "generation.json");
90204
91046
  }
90205
91047
  var bridgeWriteFailureCounts = /* @__PURE__ */ new Map();
90206
91048
  function errnoCode(error) {
@@ -90225,7 +91067,7 @@ function classifyBridgeConfigWriteError(path, code, metadata) {
90225
91067
  }
90226
91068
  async function bridgeFileMetadata(path) {
90227
91069
  try {
90228
- const item = await stat3(path);
91070
+ const item = await stat4(path);
90229
91071
  const windowsReadonly = runtimePlatform() === "win32" ? await windowsReadonlyAttribute(path) : null;
90230
91072
  return {
90231
91073
  exists: true,
@@ -90302,7 +91144,7 @@ function maybeInjectBridgeWriteFailure(path, stage = "before_write") {
90302
91144
  async function writePrivateBridgeFile(path, content, stateDirForDiagnostics) {
90303
91145
  await mkdir3(dirname3(path), { recursive: true });
90304
91146
  const maxAttempts = bridgeWriteRetryAttempts();
90305
- const tmpPath = `${path}.${process.pid}.${Date.now()}.${randomBytes(4).toString("hex")}.tmp`;
91147
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.${randomBytes2(4).toString("hex")}.tmp`;
90306
91148
  let lastError = null;
90307
91149
  let observedReadonly = false;
90308
91150
  const pathKind = stateDirForDiagnostics ? bridgePathKind(path, stateDirForDiagnostics) : "bridge_private_file";
@@ -90316,7 +91158,7 @@ async function writePrivateBridgeFile(path, content, stateDirForDiagnostics) {
90316
91158
  await writeFile3(tmpPath, content, { mode: 384 });
90317
91159
  await chmod2(tmpPath, 384).catch(() => void 0);
90318
91160
  maybeInjectBridgeWriteFailure(path, "before_rename");
90319
- await rename2(tmpPath, path);
91161
+ await rename3(tmpPath, path);
90320
91162
  await chmod2(path, 384).catch(() => void 0);
90321
91163
  if (stateDirForDiagnostics && (attempt > 1 || before.readonly === true || clearReadonlyResult)) {
90322
91164
  await appendBridgeDiagnostic(stateDirForDiagnostics, "bridge_private_file_write.success", {
@@ -90371,7 +91213,7 @@ function bridgeVirtualKeyId(bridgeProviderKey2) {
90371
91213
  async function readBridgeTokenStore(stateDir) {
90372
91214
  const path = bridgeTokenPath(stateDir);
90373
91215
  try {
90374
- const parsed = JSON.parse(await readFile6(path, "utf8"));
91216
+ const parsed = JSON.parse(await readFile7(path, "utf8"));
90375
91217
  if (parsed.tokens && typeof parsed.tokens === "object" && !Array.isArray(parsed.tokens)) {
90376
91218
  const tokens = {};
90377
91219
  for (const [appId, value] of Object.entries(parsed.tokens)) {
@@ -90412,7 +91254,7 @@ async function bridgeVirtualKey(stateDir, appId) {
90412
91254
  }
90413
91255
  return existing.token;
90414
91256
  }
90415
- const token = `sk-bf-${randomBytes(32).toString("base64url")}`;
91257
+ const token = `sk-bf-${randomBytes2(32).toString("base64url")}`;
90416
91258
  store.tokens[appId] = { token, created_at: (/* @__PURE__ */ new Date()).toISOString() };
90417
91259
  delete store.tokens.legacy;
90418
91260
  await writeBridgeTokenStore(stateDir, store);
@@ -90453,7 +91295,7 @@ async function appendBridgeDiagnostic(stateDir, event, details = {}) {
90453
91295
  try {
90454
91296
  const dir = bridgeDir(stateDir);
90455
91297
  await mkdir3(dir, { recursive: true });
90456
- await appendFile(join6(dir, "bridge-diagnostics.jsonl"), `${JSON.stringify({
91298
+ await appendFile(join7(dir, "bridge-diagnostics.jsonl"), `${JSON.stringify({
90457
91299
  ts: (/* @__PURE__ */ new Date()).toISOString(),
90458
91300
  pid: process.pid,
90459
91301
  platform: runtimePlatform(),
@@ -90575,11 +91417,11 @@ var DEFAULT_SYNC_APPLY_LOCK_OWNER_GRACE_MS = 5e3;
90575
91417
  var DEFAULT_SYNC_APPLY_MANUAL_REAPPLY_WAIT_MS = 5e3;
90576
91418
  var DEFAULT_SYNC_APPLY_LOCK_HEARTBEAT_MS = 3e4;
90577
91419
  function syncApplyLockDir(stateDir) {
90578
- return join6(stateDir, "sync-apply.lock");
91420
+ return join7(stateDir, "sync-apply.lock");
90579
91421
  }
90580
91422
  async function readSyncApplyLockOwner(lockDir) {
90581
91423
  try {
90582
- const parsed = JSON.parse(await readFile6(join6(lockDir, "owner.json"), "utf8"));
91424
+ const parsed = JSON.parse(await readFile7(join7(lockDir, "owner.json"), "utf8"));
90583
91425
  if (!parsed || typeof parsed !== "object") return null;
90584
91426
  const owner = parsed;
90585
91427
  return {
@@ -90607,7 +91449,7 @@ function syncApplyLockHeartbeatMs() {
90607
91449
  return Math.min(DEFAULT_SYNC_APPLY_LOCK_HEARTBEAT_MS, Math.max(1e3, Math.floor(staleMs / 3)));
90608
91450
  }
90609
91451
  async function writeSyncApplyLockOwner(handle, updatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
90610
- await writeFile3(join6(handle.lockDir, "owner.json"), `${JSON.stringify({
91452
+ await writeFile3(join7(handle.lockDir, "owner.json"), `${JSON.stringify({
90611
91453
  version: 1,
90612
91454
  token: handle.token,
90613
91455
  pid: handle.pid,
@@ -90626,7 +91468,7 @@ async function acquireSyncApplyLock(stateDir, deviceId, trigger) {
90626
91468
  const deadline = Date.now() + waitMs;
90627
91469
  await mkdir3(stateDir, { recursive: true });
90628
91470
  for (; ; ) {
90629
- const token = randomBytes(16).toString("hex");
91471
+ const token = randomBytes2(16).toString("hex");
90630
91472
  try {
90631
91473
  await mkdir3(lockDir, { recursive: false });
90632
91474
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -90638,7 +91480,7 @@ async function acquireSyncApplyLock(stateDir, deviceId, trigger) {
90638
91480
  if (code !== "EEXIST") throw error;
90639
91481
  try {
90640
91482
  const owner = await readSyncApplyLockOwner(lockDir);
90641
- const lockStat = await stat3(lockDir);
91483
+ const lockStat = await stat4(lockDir);
90642
91484
  const ownerUpdatedAtMs = owner?.updated_at ? Date.parse(owner.updated_at) : Number.NaN;
90643
91485
  const lockAgeMs = Date.now() - lockStat.mtimeMs;
90644
91486
  const heartbeatAgeMs = Number.isFinite(ownerUpdatedAtMs) ? Date.now() - ownerUpdatedAtMs : lockAgeMs;
@@ -90677,7 +91519,7 @@ async function appendSyncApplyLockDiagnostic(stateDir, event, details = {}) {
90677
91519
  if (process.env.KEYSYNC_SYNC_APPLY_LOCK_DIAGNOSTICS !== "1") return;
90678
91520
  try {
90679
91521
  await mkdir3(stateDir, { recursive: true });
90680
- await appendFile(join6(stateDir, "sync-apply-lock-diagnostics.jsonl"), `${JSON.stringify({
91522
+ await appendFile(join7(stateDir, "sync-apply-lock-diagnostics.jsonl"), `${JSON.stringify({
90681
91523
  ts: (/* @__PURE__ */ new Date()).toISOString(),
90682
91524
  pid: process.pid,
90683
91525
  event,
@@ -90720,7 +91562,7 @@ async function recordBridgeLifecycleEvent(event) {
90720
91562
  }
90721
91563
  async function readBridgeLifecycleLockOwner(lockDir) {
90722
91564
  try {
90723
- const parsed = JSON.parse(await readFile6(join6(lockDir, "owner.json"), "utf8"));
91565
+ const parsed = JSON.parse(await readFile7(join7(lockDir, "owner.json"), "utf8"));
90724
91566
  if (!parsed || typeof parsed !== "object") return null;
90725
91567
  const owner = parsed;
90726
91568
  return {
@@ -90732,7 +91574,7 @@ async function readBridgeLifecycleLockOwner(lockDir) {
90732
91574
  }
90733
91575
  }
90734
91576
  async function withBridgeLifecycleLock(stateDir, fn) {
90735
- const lockDir = join6(bridgeDir(stateDir), ".lifecycle.lock");
91577
+ const lockDir = join7(bridgeDir(stateDir), ".lifecycle.lock");
90736
91578
  await mkdir3(bridgeDir(stateDir), { recursive: true });
90737
91579
  const timeoutMs = Number(process.env.KEYSYNC_BIFROST_LOCK_TIMEOUT_MS ?? 3e4);
90738
91580
  const staleMs = Number(process.env.KEYSYNC_BIFROST_LOCK_STALE_MS ?? DEFAULT_BIFROST_LOCK_STALE_MS);
@@ -90740,7 +91582,7 @@ async function withBridgeLifecycleLock(stateDir, fn) {
90740
91582
  for (; ; ) {
90741
91583
  try {
90742
91584
  await mkdir3(lockDir, { recursive: false });
90743
- await writeFile3(join6(lockDir, "owner.json"), `${JSON.stringify({ pid: process.pid, created_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
91585
+ await writeFile3(join7(lockDir, "owner.json"), `${JSON.stringify({ pid: process.pid, created_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
90744
91586
  `);
90745
91587
  await recordBridgeLifecycleEvent("acquire_lock");
90746
91588
  break;
@@ -90758,7 +91600,7 @@ async function withBridgeLifecycleLock(stateDir, fn) {
90758
91600
  await rm3(lockDir, { recursive: true, force: true });
90759
91601
  continue;
90760
91602
  }
90761
- const lockStat = await stat3(lockDir);
91603
+ const lockStat = await stat4(lockDir);
90762
91604
  if (Date.now() - lockStat.mtimeMs > staleMs) {
90763
91605
  await appendBridgeDiagnostic(stateDir, "bridge_lifecycle_lock.reclaim_stale_mtime", {
90764
91606
  lockDir,
@@ -90859,7 +91701,7 @@ function buildBifrostProviderConfig(plan) {
90859
91701
  }
90860
91702
  async function readBridgeRouteManifest(stateDir) {
90861
91703
  try {
90862
- const parsed = JSON.parse(await readFile6(bridgeRouteManifestPath(stateDir), "utf8"));
91704
+ const parsed = JSON.parse(await readFile7(bridgeRouteManifestPath(stateDir), "utf8"));
90863
91705
  const routes = Array.isArray(parsed.routes) ? parsed.routes.filter((item) => Boolean(item && typeof item === "object" && typeof item.app_id === "string")).map((item) => ({
90864
91706
  ...item,
90865
91707
  app_name: typeof item.app_name === "string" ? item.app_name : "Codex"
@@ -90876,7 +91718,7 @@ async function readBridgeRouteManifest(stateDir) {
90876
91718
  }
90877
91719
  async function readBifrostProviders(stateDir) {
90878
91720
  try {
90879
- const parsed = JSON.parse(await readFile6(bridgeBifrostConfigPath(stateDir), "utf8"));
91721
+ const parsed = JSON.parse(await readFile7(bridgeBifrostConfigPath(stateDir), "utf8"));
90880
91722
  return parsed.providers && typeof parsed.providers === "object" && !Array.isArray(parsed.providers) ? parsed.providers : {};
90881
91723
  } catch {
90882
91724
  return {};
@@ -90921,7 +91763,7 @@ function buildBifrostConfig(stateDir, providers, virtualKeys) {
90921
91763
  enabled: true,
90922
91764
  type: "sqlite",
90923
91765
  config: {
90924
- path: join6(dir, "config.db")
91766
+ path: join7(dir, "config.db")
90925
91767
  }
90926
91768
  },
90927
91769
  logs_store: {
@@ -90929,7 +91771,7 @@ function buildBifrostConfig(stateDir, providers, virtualKeys) {
90929
91771
  type: "sqlite",
90930
91772
  retention_days: 30,
90931
91773
  config: {
90932
- path: join6(dir, "logs.db")
91774
+ path: join7(dir, "logs.db")
90933
91775
  }
90934
91776
  },
90935
91777
  governance: {
@@ -91006,7 +91848,7 @@ function bridgeManifestMatchesActiveAppIds(manifest, activeBridgeAppIds) {
91006
91848
  }
91007
91849
  async function readBridgeRuntimeState(stateDir) {
91008
91850
  try {
91009
- const parsed = JSON.parse(await readFile6(bridgeRuntimePath(stateDir), "utf8"));
91851
+ const parsed = JSON.parse(await readFile7(bridgeRuntimePath(stateDir), "utf8"));
91010
91852
  if (typeof parsed.listen_url !== "string" || !parsed.listen_url.trim()) return null;
91011
91853
  return {
91012
91854
  version: 1,
@@ -91052,14 +91894,14 @@ function bridgeRuntimeBaseState(stateDir, listenUrl, status, current) {
91052
91894
  };
91053
91895
  }
91054
91896
  async function bridgeConfigHash(stateDir) {
91055
- return createHash5("sha256").update(await readFile6(bridgeBifrostConfigPath(stateDir))).digest("hex");
91897
+ return createHash5("sha256").update(await readFile7(bridgeBifrostConfigPath(stateDir))).digest("hex");
91056
91898
  }
91057
91899
  async function readCommittedBridgeGeneration(stateDir) {
91058
91900
  try {
91059
- const parsed = JSON.parse(await readFile6(bridgeGenerationPath(stateDir), "utf8"));
91901
+ const parsed = JSON.parse(await readFile7(bridgeGenerationPath(stateDir), "utf8"));
91060
91902
  if (parsed.version !== 1 || parsed.status !== "committed" || typeof parsed.config_hash !== "string" || typeof parsed.route_count !== "number" || typeof parsed.listen_url !== "string" || typeof parsed.committed_at !== "string") return null;
91061
91903
  if (await bridgeConfigHash(stateDir) !== parsed.config_hash) return null;
91062
- const config = JSON.parse(await readFile6(bridgeBifrostConfigPath(stateDir), "utf8"));
91904
+ const config = JSON.parse(await readFile7(bridgeBifrostConfigPath(stateDir), "utf8"));
91063
91905
  const virtualKeys = Array.isArray(config.governance?.virtual_keys) ? config.governance.virtual_keys : [];
91064
91906
  const providers = config.providers && typeof config.providers === "object" && !Array.isArray(config.providers) ? config.providers : {};
91065
91907
  if (virtualKeys.length !== parsed.route_count) return null;
@@ -91293,7 +92135,7 @@ async function applyBridgeConfigPlan(stateDir, baseUrl, prepared) {
91293
92135
  });
91294
92136
  }
91295
92137
  function sleep2(ms) {
91296
- return new Promise((resolve2) => setTimeout(resolve2, ms));
92138
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
91297
92139
  }
91298
92140
  function truncateText(value, maxLength) {
91299
92141
  if (value.length <= maxLength) return value;
@@ -91304,7 +92146,7 @@ function redactDiagnosticText(value) {
91304
92146
  }
91305
92147
  async function readTextTail(filePath, maxLength = 12e3) {
91306
92148
  try {
91307
- return redactDiagnosticText(truncateText(await readFile6(filePath, "utf8"), maxLength));
92149
+ return redactDiagnosticText(truncateText(await readFile7(filePath, "utf8"), maxLength));
91308
92150
  } catch {
91309
92151
  return null;
91310
92152
  }
@@ -91348,9 +92190,9 @@ function bifrostBinaryPath() {
91348
92190
  const processWithResources = process;
91349
92191
  const resourceBinaryName = runtimePlatform() === "win32" ? "bifrost-http.exe" : "bifrost-http";
91350
92192
  const candidates = [
91351
- processWithResources.resourcesPath ? join6(processWithResources.resourcesPath, "bridge", resourceBinaryName) : null,
91352
- join6(process.cwd(), "resources", "bridge", runtimePlatform(), process.arch, resourceBinaryName),
91353
- join6(process.cwd(), "keysync-client", "resources", "bridge", `${runtimePlatform()}-${process.arch}`, resourceBinaryName)
92193
+ processWithResources.resourcesPath ? join7(processWithResources.resourcesPath, "bridge", resourceBinaryName) : null,
92194
+ join7(process.cwd(), "resources", "bridge", runtimePlatform(), process.arch, resourceBinaryName),
92195
+ join7(process.cwd(), "keysync-client", "resources", "bridge", `${runtimePlatform()}-${process.arch}`, resourceBinaryName)
91354
92196
  ].filter((item) => Boolean(item));
91355
92197
  return candidates.find((candidate) => existsSync3(candidate)) ?? null;
91356
92198
  }
@@ -91581,7 +92423,7 @@ function bridgeHostAndPort(baseUrl) {
91581
92423
  return { host, port };
91582
92424
  }
91583
92425
  function execFileText(command, args, timeout = 2e3) {
91584
- return new Promise((resolve2, reject) => {
92426
+ return new Promise((resolve3, reject) => {
91585
92427
  execFile2(command, args, { timeout }, (error, stdout, stderr) => {
91586
92428
  if (error) {
91587
92429
  const enriched = error;
@@ -91589,7 +92431,7 @@ function execFileText(command, args, timeout = 2e3) {
91589
92431
  enriched.stderr = stderr.toString();
91590
92432
  reject(enriched);
91591
92433
  } else {
91592
- resolve2(stdout.toString());
92434
+ resolve3(stdout.toString());
91593
92435
  }
91594
92436
  });
91595
92437
  });
@@ -91995,7 +92837,7 @@ async function stopBridgeProcessesOnPort(stateDir, baseUrl, timeoutMs, knownMana
91995
92837
  async function tcpPortIsBindable(host, port) {
91996
92838
  const portNumber = Number(port);
91997
92839
  if (!Number.isInteger(portNumber) || portNumber <= 0) return false;
91998
- return new Promise((resolve2) => {
92840
+ return new Promise((resolve3) => {
91999
92841
  const server = createServer();
92000
92842
  let settled = false;
92001
92843
  const finish = (result) => {
@@ -92003,8 +92845,8 @@ async function tcpPortIsBindable(host, port) {
92003
92845
  settled = true;
92004
92846
  clearTimeout(timer);
92005
92847
  server.removeAllListeners();
92006
- if (server.listening) server.close(() => resolve2(result));
92007
- else resolve2(result);
92848
+ if (server.listening) server.close(() => resolve3(result));
92849
+ else resolve3(result);
92008
92850
  };
92009
92851
  const timer = setTimeout(() => finish(false), 1e3);
92010
92852
  server.once("error", () => finish(false));
@@ -92049,7 +92891,7 @@ async function resetBridgeConfigStore(stateDir) {
92049
92891
  });
92050
92892
  const deadline = Date.now() + Number(process.env.KEYSYNC_BIFROST_CONFIG_STORE_UNLOCK_TIMEOUT_MS ?? DEFAULT_BIFROST_CONFIG_STORE_UNLOCK_TIMEOUT_MS);
92051
92893
  for (const file of BRIDGE_SQLITE_CONFIG_FILES) {
92052
- const filePath = join6(dir, file);
92894
+ const filePath = join7(dir, file);
92053
92895
  let attempts = 0;
92054
92896
  for (; ; ) {
92055
92897
  try {
@@ -92556,11 +93398,11 @@ function isManuallyMarkedUninstalled(discovery) {
92556
93398
  return discovery?.detection_mode === "manual" && discovery.detected === false;
92557
93399
  }
92558
93400
  function discoveryStabilityPath(stateDir) {
92559
- return join6(stateDir, "app-discovery-state.json");
93401
+ return join7(stateDir, "app-discovery-state.json");
92560
93402
  }
92561
93403
  async function readDiscoveryStability(stateDir) {
92562
93404
  try {
92563
- const parsed = JSON.parse(await readFile6(discoveryStabilityPath(stateDir), "utf8"));
93405
+ const parsed = JSON.parse(await readFile7(discoveryStabilityPath(stateDir), "utf8"));
92564
93406
  return { version: 1, apps: parsed.apps && typeof parsed.apps === "object" ? parsed.apps : {} };
92565
93407
  } catch {
92566
93408
  return { version: 1, apps: {} };
@@ -92569,10 +93411,10 @@ async function readDiscoveryStability(stateDir) {
92569
93411
  async function writeDiscoveryStability(stateDir, state) {
92570
93412
  await mkdir3(stateDir, { recursive: true });
92571
93413
  const path = discoveryStabilityPath(stateDir);
92572
- const tmpPath = `${path}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
93414
+ const tmpPath = `${path}.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`;
92573
93415
  await writeFile3(tmpPath, `${JSON.stringify(state, null, 2)}
92574
93416
  `, { mode: 384 });
92575
- await rename2(tmpPath, path);
93417
+ await rename3(tmpPath, path);
92576
93418
  await chmod2(path, 384).catch(() => void 0);
92577
93419
  }
92578
93420
  function stabilizedAutomaticDetection(app, current, state) {
@@ -92607,8 +93449,8 @@ var appDiscoveryQueues = /* @__PURE__ */ new Map();
92607
93449
  async function withAppDiscoveryQueue(key, task) {
92608
93450
  const previous = appDiscoveryQueues.get(key) ?? Promise.resolve();
92609
93451
  let release;
92610
- const current = new Promise((resolve2) => {
92611
- release = resolve2;
93452
+ const current = new Promise((resolve3) => {
93453
+ release = resolve3;
92612
93454
  });
92613
93455
  const tail = previous.catch(() => void 0).then(() => current);
92614
93456
  appDiscoveryQueues.set(key, tail);
@@ -92748,7 +93590,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92748
93590
  vaultUnlockError = error;
92749
93591
  }
92750
93592
  }
92751
- const outputDir = join6(stateDir, "applied-configs");
93593
+ const outputDir = join7(stateDir, "applied-configs");
92752
93594
  const mode = applyModeFromEnv();
92753
93595
  const localKey = await localApplyFingerprintKey(stateDir);
92754
93596
  const writeStrategies = snapshotWriteStrategies(snapshot);
@@ -92802,6 +93644,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92802
93644
  return leftRank - rightRank;
92803
93645
  });
92804
93646
  for (const app of orderedApps) {
93647
+ const forceThisApp = Boolean(options.force) || (options.forceAppNames?.includes(app.app_name) ?? false);
92805
93648
  if (!app.source) {
92806
93649
  await clearLocalApplyRecord(stateDir, deviceId, app.id, mode);
92807
93650
  appResults.push({ app_name: app.app_name, status: "skipped", message: "APP_NOT_CONFIGURED" });
@@ -92809,13 +93652,13 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92809
93652
  }
92810
93653
  const provider = resolveProviderForApp(snapshot, app.id);
92811
93654
  const sourceIdentityFingerprint = hmacHex(localKey, sourceIdentityInput(snapshot, app, provider));
92812
- const attemptId = `att_${randomBytes(8).toString("hex")}`;
93655
+ const attemptId = `att_${randomBytes2(8).toString("hex")}`;
92813
93656
  const previousRecord = await readLocalApplyRecord(stateDir, deviceId, app.id, mode);
92814
93657
  const currentInstallationStateFingerprint = installationStateFingerprint(app);
92815
93658
  const installationStateChanged = previousRecord?.installation_state_fingerprint !== void 0 && previousRecord.installation_state_fingerprint !== currentInstallationStateFingerprint;
92816
93659
  const adoptionSource = app.source.source_type === "provider" ? app.source : null;
92817
93660
  const adoptionAppliesToCurrentRevision = adoptionSource?.initial_apply_mode === "preserve_local_once" && Boolean(adoptionSource.binding_revision && app.desired_state_revision) && (adoptionSource.adoption_status === "pending" || adoptionSource.adoption_status === "failed" || adoptionSource.adoption_status === "confirmed" && adoptionSource.adopted_desired_state_revision === (app.desired_state_revision ?? null));
92818
- if (!options.force && adoptionAppliesToCurrentRevision) {
93661
+ if (!forceThisApp && adoptionAppliesToCurrentRevision) {
92819
93662
  if (adoptionSource.adoption_status === "failed") {
92820
93663
  appResults.push({ app_name: app.app_name, status: "skipped", message: "LOCAL_ADOPTION_FAILED_REQUIRES_RETRY" });
92821
93664
  continue;
@@ -92867,56 +93710,59 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92867
93710
  await reportAdoptionFailure("LOCAL_ADOPTION_IDENTITY_MISMATCH");
92868
93711
  continue;
92869
93712
  }
92870
- let adoptionBaselineRecorded = false;
92871
- try {
92872
- const decryptedProvider = await resolveLocalDecryptedProvider(provider, vaultMaterial);
92873
- const routePlan = await buildAppRoutePlan({ app, provider: decryptedProvider, snapshot, stateDir, bridgeBaseUrl: bridgeRuntimeBaseUrl, providerStrategyMap, writeStrategies });
92874
- if (routePlan.route_kind !== "direct") throw new Error("LOCAL_ADOPTION_BRIDGE_UNSUPPORTED");
92875
- const sourceFingerprint = hmacHex(
92876
- localKey,
92877
- buildSourceFingerprintInput(
92878
- app.app_name,
92879
- app.id,
92880
- stableAppSourceForFingerprint(app.source),
92881
- decryptedProvider,
92882
- routePlan.endpoint,
92883
- localKey
92884
- )
92885
- );
92886
- if (!options.localModelConfigProbe) clearLocalModelConfigProbeCache();
92887
- const finalProbe = await adoptionProbe(app.app_name);
92888
- const finalOauth = finalProbe.credentials.find((credential) => credential.kind === "oauth");
92889
- if (finalProbe.state !== "local_oauth" || finalProbe.in_effect !== "oauth" || finalOauth?.import_source_key !== providerSourceKey) {
92890
- await reportAdoptionFailure("LOCAL_ADOPTION_LOCAL_STATE_CHANGED");
92891
- continue;
93713
+ const providerExpiresAt = typeof provider.values?._import_expires_at === "string" ? provider.values._import_expires_at : void 0;
93714
+ if (!staleAgainstStored(localOauth.expires_at ?? void 0, providerExpiresAt)) {
93715
+ let adoptionBaselineRecorded = false;
93716
+ try {
93717
+ const decryptedProvider = await resolveLocalDecryptedProvider(provider, vaultMaterial);
93718
+ const routePlan = await buildAppRoutePlan({ app, provider: decryptedProvider, snapshot, stateDir, bridgeBaseUrl: bridgeRuntimeBaseUrl, providerStrategyMap, writeStrategies });
93719
+ if (routePlan.route_kind !== "direct") throw new Error("LOCAL_ADOPTION_BRIDGE_UNSUPPORTED");
93720
+ const sourceFingerprint = hmacHex(
93721
+ localKey,
93722
+ buildSourceFingerprintInput(
93723
+ app.app_name,
93724
+ app.id,
93725
+ stableAppSourceForFingerprint(app.source),
93726
+ decryptedProvider,
93727
+ routePlan.endpoint,
93728
+ localKey
93729
+ )
93730
+ );
93731
+ if (!options.localModelConfigProbe) clearLocalModelConfigProbeCache();
93732
+ const finalProbe = await adoptionProbe(app.app_name);
93733
+ const finalOauth = finalProbe.credentials.find((credential) => credential.kind === "oauth");
93734
+ if (finalProbe.state !== "local_oauth" || finalProbe.in_effect !== "oauth" || finalOauth?.import_source_key !== providerSourceKey) {
93735
+ await reportAdoptionFailure("LOCAL_ADOPTION_LOCAL_STATE_CHANGED");
93736
+ continue;
93737
+ }
93738
+ await recordApplyAttempt({
93739
+ stateDir,
93740
+ deviceId,
93741
+ app,
93742
+ mode,
93743
+ snapshotId: snapshot.snapshot_id,
93744
+ sourceIdentityFingerprint,
93745
+ attemptFingerprint: sourceFingerprint,
93746
+ sourceFingerprint,
93747
+ status: "success",
93748
+ providerId: provider.id,
93749
+ selectedEndpoint: routePlan.endpoint,
93750
+ adoptedLocal: true
93751
+ });
93752
+ adoptionBaselineRecorded = true;
93753
+ await client.post(`/devices/${deviceId}/apps/${app.id}/adoption-report`, {
93754
+ binding_revision: adoptionSource.binding_revision,
93755
+ desired_state_revision: app.desired_state_revision,
93756
+ status: "confirmed"
93757
+ });
93758
+ appResults.push({ app_name: app.app_name, status: "success", message: "ADOPTED_LOCAL_CONFIG" });
93759
+ } catch (error) {
93760
+ if (adoptionBaselineRecorded) throw error;
93761
+ const errorCode = error instanceof Error && /^LOCAL_ADOPTION_[A-Z0-9_]+$/.test(error.message) ? error.message : "LOCAL_ADOPTION_ROUTE_INVALID";
93762
+ await reportAdoptionFailure(errorCode);
92892
93763
  }
92893
- await recordApplyAttempt({
92894
- stateDir,
92895
- deviceId,
92896
- app,
92897
- mode,
92898
- snapshotId: snapshot.snapshot_id,
92899
- sourceIdentityFingerprint,
92900
- attemptFingerprint: sourceFingerprint,
92901
- sourceFingerprint,
92902
- status: "success",
92903
- providerId: provider.id,
92904
- selectedEndpoint: routePlan.endpoint,
92905
- adoptedLocal: true
92906
- });
92907
- adoptionBaselineRecorded = true;
92908
- await client.post(`/devices/${deviceId}/apps/${app.id}/adoption-report`, {
92909
- binding_revision: adoptionSource.binding_revision,
92910
- desired_state_revision: app.desired_state_revision,
92911
- status: "confirmed"
92912
- });
92913
- appResults.push({ app_name: app.app_name, status: "success", message: "ADOPTED_LOCAL_CONFIG" });
92914
- } catch (error) {
92915
- if (adoptionBaselineRecorded) throw error;
92916
- const errorCode = error instanceof Error && /^LOCAL_ADOPTION_[A-Z0-9_]+$/.test(error.message) ? error.message : "LOCAL_ADOPTION_ROUTE_INVALID";
92917
- await reportAdoptionFailure(errorCode);
93764
+ continue;
92918
93765
  }
92919
- continue;
92920
93766
  }
92921
93767
  if (app.latest_discovery?.detection_mode === "manual" && app.latest_discovery.detected === false) {
92922
93768
  retainedBridgeAppIds.delete(app.id);
@@ -92924,11 +93770,11 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92924
93770
  continue;
92925
93771
  }
92926
93772
  const previousFailureWasRecoverableBridge = activeBridgeAppIds.has(app.id) && (isRecoverableBridgeFailureCode(previousRecord?.failure_code) || previousRecord?.version !== 2 || previousRecord.desired_state_revision !== (app.desired_state_revision ?? null));
92927
- if (!options.force && previousRecord?.last_attempt_status === "failed" && previousRecord.source_identity_fingerprint === sourceIdentityFingerprint && !previousFailureWasRecoverableBridge) {
93773
+ if (!forceThisApp && previousRecord?.last_attempt_status === "failed" && previousRecord.source_identity_fingerprint === sourceIdentityFingerprint && !previousFailureWasRecoverableBridge) {
92928
93774
  appResults.push({ app_name: app.app_name, status: "skipped", message: "SKIPPED_FAILED_FINGERPRINT" });
92929
93775
  continue;
92930
93776
  }
92931
- if (!options.force && previousRecord?.last_attempt_status === "success" && previousRecord.source_identity_fingerprint === sourceIdentityFingerprint && (!activeBridgeAppIds.has(app.id) || existingBridgeRuntimeReady && previousRecord.version === 2 && previousRecord.desired_state_revision === (app.desired_state_revision ?? null) && previousRecord.bridge_generation === existingBridgeGenerationHash)) {
93777
+ if (!forceThisApp && previousRecord?.last_attempt_status === "success" && previousRecord.source_identity_fingerprint === sourceIdentityFingerprint && (!activeBridgeAppIds.has(app.id) || existingBridgeRuntimeReady && previousRecord.version === 2 && previousRecord.desired_state_revision === (app.desired_state_revision ?? null) && previousRecord.bridge_generation === existingBridgeGenerationHash)) {
92932
93778
  if (!activeBridgeAppIds.has(app.id)) retainedBridgeAppIds.delete(app.id);
92933
93779
  appResults.push({ app_name: app.app_name, status: "skipped", message: "SKIPPED_SAME_FINGERPRINT" });
92934
93780
  continue;
@@ -92994,7 +93840,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92994
93840
  }
92995
93841
  }
92996
93842
  ]);
92997
- await recordDaemonAutoApplyEvent({
93843
+ await recordLocalApplyEvent({
92998
93844
  stateDir,
92999
93845
  trigger: options.trigger,
93000
93846
  session: activeSession,
@@ -93073,7 +93919,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93073
93919
  }
93074
93920
  }
93075
93921
  ]);
93076
- await recordDaemonAutoApplyEvent({
93922
+ await recordLocalApplyEvent({
93077
93923
  stateDir,
93078
93924
  trigger: options.trigger,
93079
93925
  session: activeSession,
@@ -93152,7 +93998,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93152
93998
  }
93153
93999
  }
93154
94000
  ]);
93155
- await recordDaemonAutoApplyEvent({
94001
+ await recordLocalApplyEvent({
93156
94002
  stateDir,
93157
94003
  trigger: options.trigger,
93158
94004
  session: activeSession,
@@ -93203,7 +94049,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93203
94049
  route_kind: routePlan.route_kind,
93204
94050
  strategy_id: routePlan.strategy_id
93205
94051
  };
93206
- if (!options.force && previousRecord?.last_attempt_status === "success" && previousRecord.source_fingerprint === sourceFingerprint && (routePlan.route_kind === "bridge" || !installationStateChanged)) {
94052
+ if (!forceThisApp && previousRecord?.last_attempt_status === "success" && previousRecord.source_fingerprint === sourceFingerprint && (routePlan.route_kind === "bridge" || !installationStateChanged)) {
93207
94053
  const sourceIdentityChanged = previousRecord.source_identity_fingerprint !== sourceIdentityFingerprint;
93208
94054
  const desiredStateRevisionChanged = previousRecord.desired_state_revision !== (app.desired_state_revision ?? null);
93209
94055
  if (routePlan.route_kind === "bridge" && sourceIdentityChanged || desiredStateRevisionChanged) {
@@ -93229,7 +94075,8 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93229
94075
  status: "success",
93230
94076
  providerId: provider.id,
93231
94077
  selectedEndpoint: fingerprintEndpoint,
93232
- bridgeGeneration: routePlan.route_kind === "bridge" ? existingBridgeGenerationHash : null
94078
+ bridgeGeneration: routePlan.route_kind === "bridge" ? existingBridgeGenerationHash : null,
94079
+ writtenAccountKey: writtenAccountKeyFor(provider)
93233
94080
  });
93234
94081
  await postReportDurably(client, stateDir, `/devices/${deviceId}/apps/${app.id}/apply-report`, {
93235
94082
  snapshot_id: snapshot.snapshot_id,
@@ -93252,7 +94099,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93252
94099
  selected_model: providerSelectedModel(applyProvider, fingerprintEndpoint),
93253
94100
  reported_at: (/* @__PURE__ */ new Date()).toISOString()
93254
94101
  });
93255
- await recordDaemonAutoApplyEvent({
94102
+ await recordLocalApplyEvent({
93256
94103
  stateDir,
93257
94104
  trigger: options.trigger,
93258
94105
  session: activeSession,
@@ -93274,6 +94121,55 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93274
94121
  continue;
93275
94122
  }
93276
94123
  }
94124
+ const holdReason = forceThisApp ? null : await heldWriteReason({ app, provider, previousRecord, probe: options.localModelConfigProbe });
94125
+ if (holdReason === "retry") {
94126
+ appResults.push({ app_name: app.app_name, status: "skipped", message: "LOCAL_STATE_UNREADABLE" });
94127
+ continue;
94128
+ }
94129
+ if (holdReason) {
94130
+ const reportedAt2 = (/* @__PURE__ */ new Date()).toISOString();
94131
+ const attemptFingerprint = hmacHex(localKey, { source_identity_fingerprint: sourceIdentityFingerprint, conflict_code: holdReason });
94132
+ const alreadyReported = previousRecord?.last_attempt_status === "conflict" && previousRecord.source_identity_fingerprint === sourceIdentityFingerprint && previousRecord.failure_code === holdReason;
94133
+ await recordApplyAttempt({
94134
+ stateDir,
94135
+ deviceId,
94136
+ app,
94137
+ mode,
94138
+ snapshotId: snapshot.snapshot_id,
94139
+ sourceIdentityFingerprint,
94140
+ attemptFingerprint,
94141
+ status: "conflict",
94142
+ providerId: provider.id,
94143
+ failureCode: holdReason,
94144
+ failureMessage: holdReason,
94145
+ writtenAccountKey: previousRecord?.written_account_key ?? null
94146
+ });
94147
+ if (!alreadyReported) {
94148
+ await postReportDurably(client, stateDir, `/devices/${deviceId}/apps/${app.id}/apply-report`, {
94149
+ snapshot_id: snapshot.snapshot_id,
94150
+ desired_state_revision: app.desired_state_revision ?? null,
94151
+ status: "conflict",
94152
+ message: holdReason,
94153
+ error_code: holdReason,
94154
+ ...applyReportContext,
94155
+ reported_at: reportedAt2
94156
+ });
94157
+ await recordLocalApplyEvent({
94158
+ stateDir,
94159
+ trigger: options.trigger,
94160
+ session: activeSession,
94161
+ deviceId,
94162
+ app,
94163
+ status: "conflict",
94164
+ errorCode: holdReason,
94165
+ snapshotId: snapshot.snapshot_id,
94166
+ message: holdReason,
94167
+ reportedAt: reportedAt2
94168
+ });
94169
+ }
94170
+ appResults.push({ app_name: app.app_name, status: "skipped", message: holdReason });
94171
+ continue;
94172
+ }
93277
94173
  if (routePlan.route_kind === "bridge") {
93278
94174
  retainedBridgeAppIds.add(app.id);
93279
94175
  const preparedBridgeConfig = await buildBridgeConfigForPlan(stateDir, routePlan, localKey, retainedBridgeAppIds);
@@ -93309,7 +94205,8 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93309
94205
  status: "success",
93310
94206
  providerId: provider.id,
93311
94207
  selectedEndpoint: fingerprintEndpoint,
93312
- bridgeGeneration: routePlan.route_kind === "bridge" ? existingBridgeGenerationHash : null
94208
+ bridgeGeneration: routePlan.route_kind === "bridge" ? existingBridgeGenerationHash : null,
94209
+ writtenAccountKey: writtenAccountKeyFor(provider)
93313
94210
  });
93314
94211
  await postReportDurably(client, stateDir, `/devices/${deviceId}/apps/${app.id}/apply-report`, {
93315
94212
  snapshot_id: snapshot.snapshot_id,
@@ -93332,7 +94229,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93332
94229
  selected_model: result.selected_model,
93333
94230
  reported_at: (/* @__PURE__ */ new Date()).toISOString()
93334
94231
  });
93335
- await recordDaemonAutoApplyEvent({
94232
+ await recordLocalApplyEvent({
93336
94233
  stateDir,
93337
94234
  trigger: options.trigger,
93338
94235
  session: activeSession,
@@ -93427,7 +94324,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93427
94324
  }
93428
94325
  }
93429
94326
  ]);
93430
- await recordDaemonAutoApplyEvent({
94327
+ await recordLocalApplyEvent({
93431
94328
  stateDir,
93432
94329
  trigger: options.trigger,
93433
94330
  session: activeSession,
@@ -93451,7 +94348,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93451
94348
  const removedAppIds = [...new Set(
93452
94349
  (manifestBeforeFinalReconcile?.routes ?? []).map((route) => route.app_id).filter((appId) => !retainedBridgeAppIds.has(appId))
93453
94350
  )].sort();
93454
- const reconciliationAttemptId = `att_${randomBytes(8).toString("hex")}`;
94351
+ const reconciliationAttemptId = `att_${randomBytes2(8).toString("hex")}`;
93455
94352
  try {
93456
94353
  const reconciliation = await buildBridgeReconciliationPlan(stateDir, retainedBridgeAppIds, bridgeRuntimeBaseUrl);
93457
94354
  await applyBridgeConfigPlan(stateDir, bridgeRuntimeBaseUrl, reconciliation);
@@ -93587,44 +94484,19 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93587
94484
  };
93588
94485
  }
93589
94486
 
93590
- // apps/cli/package.json
93591
- var package_default = {
93592
- name: "@keysync/cli",
93593
- version: "0.1.131",
93594
- license: "MIT",
93595
- private: true,
93596
- type: "module",
93597
- bin: {
93598
- keysync: "./src/index.ts"
93599
- },
93600
- scripts: {
93601
- cli: "tsx src/index.ts",
93602
- build: "tsc -p tsconfig.json",
93603
- typecheck: "tsc -p tsconfig.json --noEmit",
93604
- test: "vitest run --exclude=dist/** --passWithNoTests"
93605
- },
93606
- dependencies: {
93607
- "@keysync/api-client": "workspace:*",
93608
- "@keysync/client-core": "workspace:*"
93609
- },
93610
- devDependencies: {
93611
- "@types/node": "^24.10.1",
93612
- tsx: "^4.20.6",
93613
- typescript: "^5.9.3",
93614
- vitest: "^4.1.11"
93615
- }
93616
- };
94487
+ // keysync-cli-version:keysync-cli-package.json
94488
+ var keysync_cli_package_default = { name: "@keysync/cli", version: "0.1.133" };
93617
94489
 
93618
94490
  // apps/cli/src/version.ts
93619
94491
  var overrideVersion = process.env.KEYSYNC_ALLOW_CLIENT_VERSION_OVERRIDE === "1" ? process.env.KEYSYNC_CLIENT_VERSION_OVERRIDE?.trim() : "";
93620
- var version = overrideVersion || package_default.version;
94492
+ var version = overrideVersion || keysync_cli_package_default.version;
93621
94493
 
93622
94494
  // apps/cli/src/dsh-host.ts
93623
94495
  import { createServer as createServer2, createConnection } from "node:net";
93624
94496
  import { createHash as createHash6, randomUUID } from "node:crypto";
93625
94497
  import { spawn as spawn2 } from "node:child_process";
93626
- import { appendFile as appendFile2, chmod as chmod3, cp, mkdir as mkdir4, readFile as readFile7, readdir as readdir2, rename as rename3, rm as rm4, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
93627
- import { dirname as dirname4, join as join7 } from "node:path";
94498
+ import { appendFile as appendFile2, chmod as chmod3, cp, mkdir as mkdir4, readFile as readFile8, readdir as readdir2, rename as rename4, rm as rm4, stat as stat5, writeFile as writeFile4 } from "node:fs/promises";
94499
+ import { dirname as dirname4, join as join8 } from "node:path";
93628
94500
  import { fileURLToPath } from "node:url";
93629
94501
  var import_managed_dsh = __toESM(require_managed_dsh(), 1);
93630
94502
  var import_remote_dsh = __toESM(require_remote_dsh(), 1);
@@ -93680,7 +94552,7 @@ function safeUserId(userId) {
93680
94552
  return sanitizeHostForPath(userId);
93681
94553
  }
93682
94554
  function dshAccountRoot(session, stateRoot = defaultStateRoot()) {
93683
- return join7(
94555
+ return join8(
93684
94556
  stateRoot,
93685
94557
  "managed-agents",
93686
94558
  "deepseek-harness",
@@ -93701,7 +94573,7 @@ function dshDiscoveryHintsJson(accountRoot, input = process.env.KEYSYNC_DISCOVER
93701
94573
  ...hints,
93702
94574
  DeepSeekHarness: {
93703
94575
  ...existingDsh && typeof existingDsh === "object" && !Array.isArray(existingDsh) ? existingDsh : {},
93704
- marker_path: join7(accountRoot, "state.json")
94576
+ marker_path: join8(accountRoot, "state.json")
93705
94577
  }
93706
94578
  });
93707
94579
  }
@@ -93710,10 +94582,10 @@ function dshHostSocketPath(stateRoot = defaultStateRoot()) {
93710
94582
  const suffix2 = createHash6("sha256").update(stateRoot).digest("hex").slice(0, 20);
93711
94583
  return `\\\\.\\pipe\\keysync-dsh-host-${suffix2}`;
93712
94584
  }
93713
- const localPath = join7(stateRoot, "dsh-host.sock");
94585
+ const localPath = join8(stateRoot, "dsh-host.sock");
93714
94586
  if (Buffer.byteLength(localPath) <= 96) return localPath;
93715
94587
  const suffix = createHash6("sha256").update(stateRoot).digest("hex").slice(0, 20);
93716
- return join7("/tmp", `ksdh-${process.getuid?.() ?? "user"}-${suffix}`, "s");
94588
+ return join8("/tmp", `ksdh-${process.getuid?.() ?? "user"}-${suffix}`, "s");
93717
94589
  }
93718
94590
  function bundledAssetDir() {
93719
94591
  if (process.env.KEYSYNC_DSH_ASSET_DIR?.trim()) return process.env.KEYSYNC_DSH_ASSET_DIR;
@@ -93729,7 +94601,7 @@ async function atomicPrivateWrite(filePath, contents) {
93729
94601
  try {
93730
94602
  await writeFile4(temporary, contents, { mode: 384 });
93731
94603
  if (process.platform !== "win32") await chmod3(temporary, 384);
93732
- await rename3(temporary, filePath);
94604
+ await rename4(temporary, filePath);
93733
94605
  } catch (error) {
93734
94606
  await rm4(temporary, { force: true }).catch(() => void 0);
93735
94607
  throw error;
@@ -93752,7 +94624,7 @@ function validLegacyOwner(value) {
93752
94624
  }
93753
94625
  async function pathExists(filePath) {
93754
94626
  try {
93755
- await stat4(filePath);
94627
+ await stat5(filePath);
93756
94628
  return true;
93757
94629
  } catch (error) {
93758
94630
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return false;
@@ -93760,8 +94632,8 @@ async function pathExists(filePath) {
93760
94632
  }
93761
94633
  }
93762
94634
  async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93763
- const legacyRoot = join7(stateRoot, "managed-agents", "deepseek-harness");
93764
- const ownerPath = join7(legacyRoot, ".legacy-owner.json");
94635
+ const legacyRoot = join8(stateRoot, "managed-agents", "deepseek-harness");
94636
+ const ownerPath = join8(legacyRoot, ".legacy-owner.json");
93765
94637
  const completionName = ".legacy-migration-complete.json";
93766
94638
  const excluded = /* @__PURE__ */ new Set(["accounts", ".legacy-owner.json"]);
93767
94639
  let entries;
@@ -93778,7 +94650,7 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93778
94650
  await ensurePrivateDirectory(legacyRoot);
93779
94651
  let recordedOwner = null;
93780
94652
  try {
93781
- const parsed = JSON.parse(await readFile7(ownerPath, "utf8"));
94653
+ const parsed = JSON.parse(await readFile8(ownerPath, "utf8"));
93782
94654
  if (!validLegacyOwner(parsed)) throw new Error("invalid owner");
93783
94655
  recordedOwner = parsed;
93784
94656
  } catch (error) {
@@ -93788,11 +94660,11 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93788
94660
  }
93789
94661
  if (!recordedOwner && preferredServerKey && !currentServerIsPreferred) return;
93790
94662
  if (recordedOwner && !sameLegacyOwner(recordedOwner, expectedOwner)) {
93791
- const recordedAccountRoot = join7(legacyRoot, "accounts", recordedOwner.server_key, recordedOwner.user_id);
93792
- const recordedMigrationComplete = await pathExists(join7(recordedAccountRoot, completionName));
94663
+ const recordedAccountRoot = join8(legacyRoot, "accounts", recordedOwner.server_key, recordedOwner.user_id);
94664
+ const recordedMigrationComplete = await pathExists(join8(recordedAccountRoot, completionName));
93793
94665
  const preferredServerCorrectingNonPreferredClaim = currentServerIsPreferred && recordedOwner.server_key !== preferredServerKey;
93794
94666
  if (recordedMigrationComplete && !preferredServerCorrectingNonPreferredClaim) return;
93795
- const ownerMetadata = await stat4(ownerPath);
94667
+ const ownerMetadata = await stat5(ownerPath);
93796
94668
  if (!preferredServerCorrectingNonPreferredClaim && Date.now() - ownerMetadata.mtimeMs < LEGACY_DSH_CLAIM_STALE_MS) return;
93797
94669
  await rm4(ownerPath, { force: true });
93798
94670
  recordedOwner = null;
@@ -93809,13 +94681,13 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93809
94681
  }
93810
94682
  if (process.platform !== "win32") await chmod3(ownerPath, 384);
93811
94683
  try {
93812
- await stat4(join7(accountRoot, completionName));
94684
+ await stat5(join8(accountRoot, completionName));
93813
94685
  return;
93814
94686
  } catch (error) {
93815
94687
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
93816
94688
  }
93817
- if (await pathExists(join7(accountRoot, "state.json"))) {
93818
- await atomicPrivateWrite(join7(accountRoot, completionName), `${JSON.stringify({ ...expectedOwner, adopted_existing_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
94689
+ if (await pathExists(join8(accountRoot, "state.json"))) {
94690
+ await atomicPrivateWrite(join8(accountRoot, completionName), `${JSON.stringify({ ...expectedOwner, adopted_existing_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
93819
94691
  `);
93820
94692
  return;
93821
94693
  }
@@ -93825,7 +94697,7 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93825
94697
  await ensurePrivateDirectory(stagingRoot);
93826
94698
  try {
93827
94699
  for (const entry of entries) {
93828
- await cp(join7(legacyRoot, entry.name), join7(stagingRoot, entry.name), {
94700
+ await cp(join8(legacyRoot, entry.name), join8(stagingRoot, entry.name), {
93829
94701
  recursive: true,
93830
94702
  errorOnExist: true,
93831
94703
  force: false,
@@ -93833,23 +94705,23 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93833
94705
  });
93834
94706
  }
93835
94707
  for (const retainedName of ["host-desired.json", "remote-dsh.sealed"]) {
93836
- const retainedPath = join7(accountRoot, retainedName);
94708
+ const retainedPath = join8(accountRoot, retainedName);
93837
94709
  if (await pathExists(retainedPath)) {
93838
- await cp(retainedPath, join7(stagingRoot, retainedName), { force: true, preserveTimestamps: true });
94710
+ await cp(retainedPath, join8(stagingRoot, retainedName), { force: true, preserveTimestamps: true });
93839
94711
  }
93840
94712
  }
93841
- await atomicPrivateWrite(join7(stagingRoot, completionName), `${JSON.stringify({ ...expectedOwner, migrated_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
94713
+ await atomicPrivateWrite(join8(stagingRoot, completionName), `${JSON.stringify({ ...expectedOwner, migrated_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
93842
94714
  `);
93843
- if (await pathExists(accountRoot)) await rename3(accountRoot, displacedRoot);
93844
- await rename3(stagingRoot, accountRoot);
94715
+ if (await pathExists(accountRoot)) await rename4(accountRoot, displacedRoot);
94716
+ await rename4(stagingRoot, accountRoot);
93845
94717
  } catch (error) {
93846
94718
  await rm4(stagingRoot, { recursive: true, force: true }).catch(() => void 0);
93847
94719
  if (!await pathExists(accountRoot) && await pathExists(displacedRoot)) {
93848
- await rename3(displacedRoot, accountRoot).catch(() => void 0);
94720
+ await rename4(displacedRoot, accountRoot).catch(() => void 0);
93849
94721
  }
93850
94722
  if (claim.claim_id) {
93851
94723
  try {
93852
- const currentOwner = JSON.parse(await readFile7(ownerPath, "utf8"));
94724
+ const currentOwner = JSON.parse(await readFile8(ownerPath, "utf8"));
93853
94725
  if (currentOwner.claim_id === claim.claim_id) await rm4(ownerPath, { force: true });
93854
94726
  } catch {
93855
94727
  }
@@ -93868,7 +94740,7 @@ function normalizeDesiredState(value) {
93868
94740
  }
93869
94741
  async function readDesiredState(filePath) {
93870
94742
  try {
93871
- return normalizeDesiredState(JSON.parse(await readFile7(filePath, "utf8")));
94743
+ return normalizeDesiredState(JSON.parse(await readFile8(filePath, "utf8")));
93872
94744
  } catch {
93873
94745
  return normalizeDesiredState(null);
93874
94746
  }
@@ -93890,10 +94762,10 @@ function publicRpcError(error) {
93890
94762
  async function createDshHostController(options) {
93891
94763
  const stateRoot = options.stateRoot ?? defaultStateRoot();
93892
94764
  const accountRoot = dshAccountRoot(options.session, stateRoot);
93893
- const dshHome = join7(accountRoot, "instances", "default", "home");
93894
- const desiredPath = join7(accountRoot, "host-desired.json");
93895
- const secretPath = join7(accountRoot, "remote-dsh.sealed");
93896
- const hostLogPath = join7(accountRoot, "logs", "host.log");
94765
+ const dshHome = join8(accountRoot, "instances", "default", "home");
94766
+ const desiredPath = join8(accountRoot, "host-desired.json");
94767
+ const secretPath = join8(accountRoot, "remote-dsh.sealed");
94768
+ const hostLogPath = join8(accountRoot, "logs", "host.log");
93897
94769
  const assetDir = options.assetDir ?? bundledAssetDir();
93898
94770
  await ensurePrivateDirectory(stateRoot);
93899
94771
  await claimLegacyDshRoot(options.session, stateRoot, accountRoot);
@@ -93916,8 +94788,8 @@ async function createDshHostController(options) {
93916
94788
  spawn: spawn2,
93917
94789
  fetchImpl: globalThis.fetch,
93918
94790
  windowsZipExtractor: process.env.KEYSYNC_DSH_WINDOWS_ZIP_EXTRACTOR === "tar" ? "tar" : void 0,
93919
- packageLockPath: join7(assetDir, "managed-dsh-official-0.1.2-alpha.2-package-lock.json"),
93920
- pnpmLockPath: join7(assetDir, "managed-dsh-pnpm-10.32.1-package-lock.json")
94791
+ packageLockPath: join8(assetDir, "managed-dsh-official-0.1.2-alpha.2-package-lock.json"),
94792
+ pnpmLockPath: join8(assetDir, "managed-dsh-pnpm-10.32.1-package-lock.json")
93921
94793
  });
93922
94794
  let releaseCatalog = releaseCatalogFromStatus(managed.status(), true);
93923
94795
  const apiRequest = options.apiRequest ?? (async (method, pathname, body, requestOptions) => withSessionRefresh(
@@ -93925,6 +94797,31 @@ async function createDshHostController(options) {
93925
94797
  (activeSession) => clientFromSession(activeSession).request(method, pathname, body, { timeoutMs: requestOptions?.timeoutMs }),
93926
94798
  options.stateDir
93927
94799
  ));
94800
+ const diagnosticReporter2 = new DiagnosticReporter({
94801
+ baseUrl: options.session.baseUrl,
94802
+ stateDir: options.stateDir,
94803
+ installStateDir: defaultInstallStateDir(options.session.baseUrl, options.stateDir),
94804
+ getInstallationId: async () => {
94805
+ const device = await readDeviceState(options.stateDir);
94806
+ return (await installForServerDevice(
94807
+ options.session.baseUrl,
94808
+ options.stateDir,
94809
+ device?.user_id === options.session.user_id ? device?.install_id_hash : null
94810
+ )).install_id;
94811
+ },
94812
+ getClient: () => ({ post: (pathname, body) => apiRequest("POST", pathname, body) })
94813
+ });
94814
+ diagnosticReporter2.setContext({ user_id: options.session.user_id ?? null });
94815
+ const reportHostFailure = async (action, error, failureStage) => {
94816
+ const value = publicRpcError(error);
94817
+ await diagnosticReporter2.report({
94818
+ domain: "dsh_host",
94819
+ action,
94820
+ error_code: typeof value.code === "string" ? value.code : "DSH_HOST_BACKGROUND_FAILED",
94821
+ message: typeof value.message === "string" ? value.message : "DeepSeek Harness background operation failed.",
94822
+ details: { failure_stage: failureStage }
94823
+ }).catch((reportError) => appendHostLog({ source: "diagnostics", event: "report_failed", error: publicRpcError(reportError) }));
94824
+ };
93928
94825
  const remoteFactory = options.remoteFactory ?? import_remote_dsh.default.createRemoteDshTunnelManager;
93929
94826
  const remote = remoteFactory({
93930
94827
  secretPath,
@@ -93985,6 +94882,7 @@ async function createDshHostController(options) {
93985
94882
  event: "release_refresh_failed",
93986
94883
  code: error instanceof ApiError ? error.code : "UNKNOWN_ERROR"
93987
94884
  }).catch(() => void 0);
94885
+ await reportHostFailure("release_refresh", error, "release_refresh");
93988
94886
  }
93989
94887
  };
93990
94888
  const refreshReleaseCatalog = async () => {
@@ -94041,7 +94939,7 @@ async function createDshHostController(options) {
94041
94939
  return { opened: false, error: { code: "data_directory_not_installed", message: "Install DeepSeek Harness before opening its data directory." } };
94042
94940
  }
94043
94941
  try {
94044
- await stat4(dshHome);
94942
+ await stat5(dshHome);
94045
94943
  } catch (error) {
94046
94944
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
94047
94945
  return { opened: false, error: { code: "data_directory_missing", message: "The DeepSeek Harness data directory does not exist." } };
@@ -94123,6 +95021,7 @@ async function createDshHostController(options) {
94123
95021
  } catch (error) {
94124
95022
  nextRecoveryAt = now + recoveryDelayMs;
94125
95023
  await appendHostLog({ source: "managed_dsh", event: "recovery_failed", retry_in_ms: recoveryDelayMs, error: publicRpcError(error) });
95024
+ await reportHostFailure("managed_recovery", error, "background_recovery");
94126
95025
  recoveryDelayMs = Math.min(3e4, recoveryDelayMs * 2);
94127
95026
  }
94128
95027
  }
@@ -94137,6 +95036,7 @@ async function createDshHostController(options) {
94137
95036
  } catch (error) {
94138
95037
  nextRemoteRecoveryAt = now + remoteRecoveryDelayMs;
94139
95038
  await appendHostLog({ source: "remote_dsh", event: "recovery_failed", retry_in_ms: remoteRecoveryDelayMs, error: publicRpcError(error) });
95039
+ await reportHostFailure("remote_recovery", error, "background_recovery");
94140
95040
  remoteRecoveryDelayMs = Math.min(3e4, remoteRecoveryDelayMs * 2);
94141
95041
  }
94142
95042
  }
@@ -94150,6 +95050,7 @@ async function createDshHostController(options) {
94150
95050
  await managed.start();
94151
95051
  } catch (error) {
94152
95052
  await appendHostLog({ source: "managed_dsh", event: "restore_failed", error: publicRpcError(error) });
95053
+ await reportHostFailure("managed_restore", error, "startup_restore");
94153
95054
  }
94154
95055
  }
94155
95056
  if (desired.remote === "enabled") {
@@ -94157,6 +95058,7 @@ async function createDshHostController(options) {
94157
95058
  await remote.start(await resolveDeviceId());
94158
95059
  } catch (error) {
94159
95060
  await appendHostLog({ source: "remote_dsh", event: "restore_failed", error: publicRpcError(error) });
95061
+ await reportHostFailure("remote_restore", error, "startup_restore");
94160
95062
  }
94161
95063
  }
94162
95064
  });
@@ -94236,18 +95138,18 @@ async function startDshHostRpcServer(socketPath, dispatch, requestTimeoutMs = RP
94236
95138
  });
94237
95139
  socket.on("error", () => void 0);
94238
95140
  });
94239
- await new Promise((resolve2, reject) => {
95141
+ await new Promise((resolve3, reject) => {
94240
95142
  server.once("error", reject);
94241
95143
  server.listen(socketPath, () => {
94242
95144
  server.off("error", reject);
94243
- resolve2();
95145
+ resolve3();
94244
95146
  });
94245
95147
  });
94246
95148
  if (process.platform !== "win32") await chmod3(socketPath, 384);
94247
95149
  return {
94248
95150
  server,
94249
95151
  async close() {
94250
- const closed = new Promise((resolve2) => server.close(() => resolve2()));
95152
+ const closed = new Promise((resolve3) => server.close(() => resolve3()));
94251
95153
  await closed;
94252
95154
  if (process.platform !== "win32") await rm4(socketPath, { force: true });
94253
95155
  }
@@ -94255,7 +95157,7 @@ async function startDshHostRpcServer(socketPath, dispatch, requestTimeoutMs = RP
94255
95157
  }
94256
95158
  async function callDshHostRpc(socketPath, method, params, timeoutMs = RPC_TIMEOUT_MS) {
94257
95159
  const id = `rpc_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
94258
- return new Promise((resolve2, reject) => {
95160
+ return new Promise((resolve3, reject) => {
94259
95161
  const socket = createConnection(socketPath);
94260
95162
  socket.setEncoding("utf8");
94261
95163
  let input = "";
@@ -94266,7 +95168,7 @@ async function callDshHostRpc(socketPath, method, params, timeoutMs = RPC_TIMEOU
94266
95168
  settled = true;
94267
95169
  clearTimeout(timer);
94268
95170
  if (error) reject(error);
94269
- else resolve2(result);
95171
+ else resolve3(result);
94270
95172
  };
94271
95173
  socket.once("connect", () => socket.write(`${JSON.stringify({ version: HOST_PROTOCOL_VERSION, id, method, params })}
94272
95174
  `));
@@ -94352,13 +95254,13 @@ function isProcessRunning(pid, options = {}) {
94352
95254
 
94353
95255
  // apps/cli/src/systemd-service.ts
94354
95256
  import { execFile as execFile3 } from "node:child_process";
94355
- import { chmod as chmod4, mkdir as mkdir5, readFile as readFile8, rm as rm5, writeFile as writeFile5 } from "node:fs/promises";
95257
+ import { chmod as chmod4, mkdir as mkdir5, readFile as readFile9, rm as rm5, writeFile as writeFile5 } from "node:fs/promises";
94356
95258
  import { homedir as homedir7 } from "node:os";
94357
- import { dirname as dirname5, join as join8 } from "node:path";
95259
+ import { dirname as dirname5, join as join9 } from "node:path";
94358
95260
  import { promisify as promisify2 } from "node:util";
94359
95261
  var execFileAsync2 = promisify2(execFile3);
94360
95262
  function systemdUserUnitPath(home = homedir7()) {
94361
- return join8(home, ".config", "systemd", "user", "keysync.service");
95263
+ return join9(home, ".config", "systemd", "user", "keysync.service");
94362
95264
  }
94363
95265
  function systemdArgument(value) {
94364
95266
  if (!value || /[\r\n\0]/.test(value)) throw new Error("Invalid systemd command path.");
@@ -94411,7 +95313,7 @@ async function uninstallSystemdUserService(home = homedir7()) {
94411
95313
  await execFileAsync2("systemctl", ["--user", "disable", "--now", "keysync.service"]).catch(() => void 0);
94412
95314
  let removed = false;
94413
95315
  try {
94414
- await readFile8(unitPath);
95316
+ await readFile9(unitPath);
94415
95317
  await rm5(unitPath, { force: true });
94416
95318
  removed = true;
94417
95319
  } catch {
@@ -94428,9 +95330,9 @@ async function systemdUserServiceStatus() {
94428
95330
  // apps/cli/src/index.ts
94429
95331
  function loadEnvFile() {
94430
95332
  const clientRoot = fileURLToPath2(new URL("../../..", import.meta.url));
94431
- const parentRoot = join9(clientRoot, "..");
94432
- const envPaths = [join9(clientRoot, ".env")];
94433
- if (process.env.KEYSYNC_UNIFIED_WORKSPACE === "1") envPaths.push(join9(parentRoot, ".env"));
95333
+ const parentRoot = join10(clientRoot, "..");
95334
+ const envPaths = [join10(clientRoot, ".env")];
95335
+ if (process.env.KEYSYNC_UNIFIED_WORKSPACE === "1") envPaths.push(join10(parentRoot, ".env"));
94434
95336
  for (const envPath2 of envPaths) {
94435
95337
  if (!existsSync4(envPath2)) continue;
94436
95338
  for (const rawLine of readFileSync2(envPath2, "utf8").split(/\r?\n/)) {
@@ -94448,6 +95350,25 @@ function loadEnvFile() {
94448
95350
  loadEnvFile();
94449
95351
  var defaultBaseUrl = process.env.KEYSYNC_API_URL || process.env.KEYSYNC_DEFAULT_API_URL || "";
94450
95352
  var defaultUpdateUrl = process.env.KEYSYNC_UPDATE_URL || process.env.KEYSYNC_DEFAULT_UPDATE_URL || "";
95353
+ var diagnosticBaseUrl = defaultBaseUrl || "http://127.0.0.1:1";
95354
+ var diagnosticAccessToken = "";
95355
+ var diagnosticReporter = new DiagnosticReporter({
95356
+ baseUrl: diagnosticBaseUrl,
95357
+ stateDir: defaultStateDir(diagnosticBaseUrl),
95358
+ installStateDir: defaultInstallStateDir(diagnosticBaseUrl),
95359
+ getInstallationId: async () => {
95360
+ const stateDir = defaultStateDir(diagnosticBaseUrl);
95361
+ const session = await readSession(stateDir);
95362
+ const device = await readDeviceState(stateDir);
95363
+ return (await installForServerDevice(
95364
+ session?.baseUrl ?? diagnosticBaseUrl,
95365
+ stateDir,
95366
+ session?.user_id && device?.user_id === session.user_id ? device.install_id_hash : null
95367
+ )).install_id;
95368
+ },
95369
+ getClient: () => new KeySyncApiClient({ baseUrl: diagnosticBaseUrl, accessToken: diagnosticAccessToken || void 0, timeoutMs: 1e4 })
95370
+ });
95371
+ var cliFailureEventIds = /* @__PURE__ */ new WeakMap();
94451
95372
  var heartbeatTimeoutMs = Number(process.env.KEYSYNC_HEARTBEAT_TIMEOUT_MS ?? "5000");
94452
95373
  var execFileAsync3 = promisify3(execFile4);
94453
95374
  function warnIfPasswordCouldBeStronger(password) {
@@ -94512,6 +95433,7 @@ Options:
94512
95433
  --install-dir DIR Override Linux CLI install directory for update testing or custom installs
94513
95434
  --vault-password PASSWORD Unlock Provider secrets for sync/daemon. Can also use KEYSYNC_VAULT_PASSWORD
94514
95435
  --force Re-apply the current server snapshot even when the local apply fingerprint is unchanged
95436
+ --app With --force, aim it at one app (e.g. --app Codex) and leave every other app alone
94515
95437
  --no-start-daemon Login/register without automatically starting the background daemon
94516
95438
  --daemon-interval-seconds N Background daemon interval after login/register. Defaults to 30
94517
95439
  --json Print machine-readable DSH status
@@ -94576,6 +95498,32 @@ async function promptForMissingRegisterInput(input) {
94576
95498
  function printJson(value) {
94577
95499
  console.log(JSON.stringify(value, null, 2));
94578
95500
  }
95501
+ async function reportCliFailure(error, details = {}) {
95502
+ const message = error instanceof Error ? error.message : String(error);
95503
+ const errorObject = error && typeof error === "object" ? error : null;
95504
+ const clientEventId = errorObject ? cliFailureEventIds.get(errorObject) ?? `evt_${randomBytes3(16).toString("hex")}` : `evt_${randomBytes3(16).toString("hex")}`;
95505
+ if (errorObject) cliFailureEventIds.set(errorObject, clientEventId);
95506
+ try {
95507
+ await diagnosticReporter.report({
95508
+ client_event_id: clientEventId,
95509
+ domain: "cli",
95510
+ action: process.argv[2] || "startup",
95511
+ error_code: error instanceof ApiError ? error.code : error?.code || "CLI_COMMAND_FAILED",
95512
+ message,
95513
+ details: {
95514
+ ...details,
95515
+ error_name: error instanceof Error ? error.name : typeof error,
95516
+ http_status: error instanceof ApiError ? error.status : null,
95517
+ argv: redactedProcessCommand(process.argv.slice(2))
95518
+ },
95519
+ client_version: version,
95520
+ platform: platform2(),
95521
+ arch: arch()
95522
+ });
95523
+ } catch (reportError) {
95524
+ console.error(`Unable to persist client diagnostic: ${reportError instanceof Error ? reportError.message : String(reportError)}`);
95525
+ }
95526
+ }
94579
95527
  function summarizeImportPlan(plan) {
94580
95528
  console.log(`CC Switch config: ${plan.config_dir}`);
94581
95529
  console.log(`Database: ${plan.db_path}`);
@@ -94695,11 +95643,11 @@ function selectCliArchive(feed) {
94695
95643
  }
94696
95644
  function safeArchiveFileName(fileUrl) {
94697
95645
  const parsed = new URL(fileUrl, "https://updates.local/");
94698
- const name = decodeURIComponent(basename3(parsed.pathname));
95646
+ const name = decodeURIComponent(basename2(parsed.pathname));
94699
95647
  return name.replace(/[^a-zA-Z0-9._ -]/g, "_") || `keysync-cli-linux-${cliUpdateArch()}.tar.gz`;
94700
95648
  }
94701
95649
  async function sha512Base64(filePath) {
94702
- return createHash7("sha512").update(await readFile9(filePath)).digest("base64");
95650
+ return createHash7("sha512").update(await readFile10(filePath)).digest("base64");
94703
95651
  }
94704
95652
  async function downloadUpdateFile(url, destination) {
94705
95653
  const response = await fetch(url, { cache: "no-store" });
@@ -94708,7 +95656,7 @@ async function downloadUpdateFile(url, destination) {
94708
95656
  await writeFile6(destination, Buffer.from(await response.arrayBuffer()));
94709
95657
  }
94710
95658
  async function runLocalCommand(command, args) {
94711
- await new Promise((resolve2, reject) => {
95659
+ await new Promise((resolve3, reject) => {
94712
95660
  const child = spawn3(command, args, { stdio: ["ignore", "pipe", "pipe"] });
94713
95661
  let stderr = "";
94714
95662
  child.stderr?.on("data", (chunk) => {
@@ -94716,7 +95664,7 @@ async function runLocalCommand(command, args) {
94716
95664
  });
94717
95665
  child.once("error", reject);
94718
95666
  child.once("exit", (code, signal) => {
94719
- if (code === 0) resolve2();
95667
+ if (code === 0) resolve3();
94720
95668
  else reject(new Error(`${command} exited with code=${code ?? "null"} signal=${signal ?? "null"}${stderr ? `: ${stderr.trim()}` : ""}`));
94721
95669
  });
94722
95670
  });
@@ -94728,25 +95676,25 @@ async function findExtractedCliDir(extractDir) {
94728
95676
  const entries = await readdir3(extractDir, { withFileTypes: true });
94729
95677
  for (const entry of entries) {
94730
95678
  if (!entry.isDirectory()) continue;
94731
- const candidate = join9(extractDir, entry.name);
94732
- if (existsSync4(join9(candidate, "keysync-cli.mjs")) && existsSync4(join9(candidate, "keysync")) && existsSync4(join9(candidate, "bridge", "bifrost-http"))) return candidate;
95679
+ const candidate = join10(extractDir, entry.name);
95680
+ if (existsSync4(join10(candidate, "keysync-cli.mjs")) && existsSync4(join10(candidate, "keysync")) && existsSync4(join10(candidate, "bridge", "bifrost-http"))) return candidate;
94733
95681
  }
94734
95682
  throw new Error("CLI archive did not contain keysync-cli.mjs, keysync, and bridge/bifrost-http");
94735
95683
  }
94736
95684
  async function installCliArchive(archivePath, installDir) {
94737
- const extractDir = await mkdtemp(join9(tmpdir(), "keysync-cli-update-extract-"));
95685
+ const extractDir = await mkdtemp(join10(tmpdir(), "keysync-cli-update-extract-"));
94738
95686
  try {
94739
95687
  await runLocalCommand("tar", ["-xzf", archivePath, "-C", extractDir]);
94740
95688
  const sourceDir = await findExtractedCliDir(extractDir);
94741
95689
  await mkdir6(installDir, { recursive: true });
94742
- await copyFile3(join9(sourceDir, "keysync-cli.mjs"), join9(installDir, "keysync-cli.mjs"));
94743
- await copyFile3(join9(sourceDir, "keysync"), join9(installDir, "keysync"));
94744
- await rm6(join9(installDir, "bridge"), { recursive: true, force: true });
94745
- await cp2(join9(sourceDir, "bridge"), join9(installDir, "bridge"), { recursive: true });
94746
- if (existsSync4(join9(sourceDir, "README.txt"))) await copyFile3(join9(sourceDir, "README.txt"), join9(installDir, "README.txt"));
94747
- await chmod5(join9(installDir, "keysync-cli.mjs"), 493);
94748
- await chmod5(join9(installDir, "keysync"), 493);
94749
- await chmod5(join9(installDir, "bridge", "bifrost-http"), 493);
95690
+ await copyFile2(join10(sourceDir, "keysync-cli.mjs"), join10(installDir, "keysync-cli.mjs"));
95691
+ await copyFile2(join10(sourceDir, "keysync"), join10(installDir, "keysync"));
95692
+ await rm6(join10(installDir, "bridge"), { recursive: true, force: true });
95693
+ await cp2(join10(sourceDir, "bridge"), join10(installDir, "bridge"), { recursive: true });
95694
+ if (existsSync4(join10(sourceDir, "README.txt"))) await copyFile2(join10(sourceDir, "README.txt"), join10(installDir, "README.txt"));
95695
+ await chmod5(join10(installDir, "keysync-cli.mjs"), 493);
95696
+ await chmod5(join10(installDir, "keysync"), 493);
95697
+ await chmod5(join10(installDir, "bridge", "bifrost-http"), 493);
94750
95698
  } finally {
94751
95699
  await rm6(extractDir, { recursive: true, force: true });
94752
95700
  }
@@ -94773,14 +95721,14 @@ async function updateLinuxCli() {
94773
95721
  }
94774
95722
  console.log(`KeySync CLI ${feed.version} is available (current ${version}).`);
94775
95723
  if (process.argv.includes("--check")) return;
94776
- const workDir = await mkdtemp(join9(tmpdir(), "keysync-cli-update-"));
94777
- const archivePath = join9(workDir, safeArchiveFileName(archive.url));
95724
+ const workDir = await mkdtemp(join10(tmpdir(), "keysync-cli-update-"));
95725
+ const archivePath = join10(workDir, safeArchiveFileName(archive.url));
94778
95726
  try {
94779
95727
  await downloadUpdateFile(resolveUpdateArtifactUrl(feedBaseUrl, archive.url), archivePath);
94780
95728
  const checksum = await sha512Base64(archivePath);
94781
95729
  if (checksum !== archive.sha512) throw new Error("Downloaded CLI package checksum did not match update feed");
94782
95730
  const installDir = cliInstallDir();
94783
- const before = existsSync4(installDir) ? await stat5(installDir) : null;
95731
+ const before = existsSync4(installDir) ? await stat6(installDir) : null;
94784
95732
  if (before && !before.isDirectory()) throw new Error(`Install target is not a directory: ${installDir}`);
94785
95733
  await installCliArchive(archivePath, installDir);
94786
95734
  console.log(`Installed KeySync CLI ${feed.version} to ${installDir}.`);
@@ -94814,11 +95762,25 @@ async function requireSession(stateDir) {
94814
95762
  if (device?.device_id && !device.user_id) {
94815
95763
  await writeDeviceState({ ...device, user_id: session.user_id }, stateDir);
94816
95764
  }
95765
+ diagnosticAccessToken = session.access_token;
95766
+ diagnosticReporter.setContext({ user_id: session.user_id ?? null, device_id: await verifiedDiagnosticDeviceId(session, stateDir) });
95767
+ await diagnosticReporter.claimContext().catch(() => void 0);
95768
+ await diagnosticReporter.flush({ force: true }).catch(() => void 0);
94817
95769
  const accountRoot = dshAccountRoot(session, defaultStateRoot());
94818
- process.env.KEYSYNC_DSH_HOME = join9(accountRoot, "instances", "default", "home");
95770
+ process.env.KEYSYNC_DSH_HOME = join10(accountRoot, "instances", "default", "home");
94819
95771
  process.env.KEYSYNC_DISCOVERY_HINTS_JSON = dshDiscoveryHintsJson(accountRoot);
94820
95772
  return session;
94821
95773
  }
95774
+ async function verifiedDiagnosticDeviceId(session, stateDir) {
95775
+ const device = await readDeviceState(stateDir);
95776
+ if (!device?.device_id || !device.install_id_hash || device.user_id !== session.user_id) return null;
95777
+ try {
95778
+ const candidates = await installCandidatesForServer(session.baseUrl, stateDir);
95779
+ return candidates.some(({ install }) => installIdHashFor(install.install_id) === device.install_id_hash) ? device.device_id : null;
95780
+ } catch {
95781
+ return null;
95782
+ }
95783
+ }
94822
95784
  async function requireDeviceId(stateDir) {
94823
95785
  const device = await readDeviceState(stateDir);
94824
95786
  if (!device?.device_id) throw new Error("Device is not bound.");
@@ -94830,35 +95792,53 @@ async function installIdHashForSession(session, stateDir) {
94830
95792
  }
94831
95793
  async function ensureDaemonDeviceId(session, stateDir) {
94832
95794
  const registration = await detectDeviceRegistrationInfo();
94833
- const install_id_hash = await installIdHashForSession(session, stateDir);
94834
95795
  const device = await readDeviceState(stateDir, { legacyStateDirs: legacyStateDirs(session.baseUrl) });
94835
95796
  if (device?.device_id) {
94836
95797
  try {
94837
95798
  const current = await withSessionRefresh(session, async (activeSession) => {
94838
95799
  const activeClient = clientFromSession(activeSession);
94839
95800
  const current2 = await activeClient.get(`/devices/${device.device_id}`);
94840
- if (!current2.install_id_hash) {
94841
- return activeClient.post("/devices/register", {
94842
- name: registration.name,
94843
- platform: registration.platform,
94844
- hostname: registration.hostname,
94845
- client_version: version,
94846
- install_id_hash,
94847
- existing_device_id: device.device_id
94848
- });
94849
- }
94850
- if (current2.install_id_hash !== install_id_hash) {
94851
- throw new Error("Local device is linked to a different install identity.");
94852
- }
94853
- return current2;
95801
+ const candidates2 = await installCandidatesForServer(session.baseUrl, stateDir);
95802
+ const match = current2.install_id_hash ? candidates2.find(({ install }) => installIdHashFor(install.install_id) === current2.install_id_hash) : null;
95803
+ if (current2.install_id_hash && !match) {
95804
+ return { id: current2.id, install_id_hash: current2.install_id_hash, claim_ticket: null, unmatched: true };
95805
+ }
95806
+ const install_id_hash2 = match ? installIdHashFor(match.install.install_id) : await installIdHashForSession(session, stateDir);
95807
+ const payload = current2.install_id_hash ? {
95808
+ name: current2.name,
95809
+ hostname: current2.hostname ?? null
95810
+ } : {
95811
+ name: registration.name,
95812
+ hostname: registration.hostname
95813
+ };
95814
+ const result2 = await activeClient.post("/devices/register", {
95815
+ ...payload,
95816
+ platform: registration.platform,
95817
+ client_version: version,
95818
+ install_id_hash: install_id_hash2,
95819
+ ...current2.install_id_hash ? {} : { existing_device_id: device.device_id }
95820
+ });
95821
+ return { ...result2, install_id_hash: install_id_hash2, unmatched: false };
95822
+ }, stateDir);
95823
+ await writeDeviceState({
95824
+ device_id: current.id,
95825
+ user_id: session.user_id,
95826
+ install_id_hash: current.install_id_hash,
95827
+ ...!current.unmatched && current.claim_ticket ? { claim_ticket: current.claim_ticket } : {}
94854
95828
  }, stateDir);
94855
- await writeDeviceState({ device_id: current.id, user_id: session.user_id }, stateDir);
94856
95829
  return current.id;
94857
95830
  } catch (error) {
94858
95831
  if (!(error instanceof ApiError) || error.status !== 404 && error.status !== 410) throw error;
94859
95832
  await clearDeviceState(stateDir);
94860
95833
  }
94861
95834
  }
95835
+ const candidates = await installCandidatesForServer(session.baseUrl, stateDir);
95836
+ const needsLegacyLookup = candidates.some(({ directory }) => directory !== defaultInstallStateDir(session.baseUrl, stateDir));
95837
+ const existing = needsLegacyLookup ? await withSessionRefresh(session, async (activeSession) => {
95838
+ const devices = await clientFromSession(activeSession).get("/devices");
95839
+ return candidates.find(({ install }) => devices.some((item) => item.status !== "removed" && item.install_id_hash === installIdHashFor(install.install_id)));
95840
+ }, stateDir) : null;
95841
+ const install_id_hash = existing ? installIdHashFor(existing.install.install_id) : await installIdHashForSession(session, stateDir);
94862
95842
  const result = await withSessionRefresh(
94863
95843
  session,
94864
95844
  async (activeSession) => clientFromSession(activeSession).post("/devices/register", {
@@ -94870,7 +95850,7 @@ async function ensureDaemonDeviceId(session, stateDir) {
94870
95850
  }),
94871
95851
  stateDir
94872
95852
  );
94873
- await writeDeviceState({ device_id: result.id, user_id: session.user_id }, stateDir);
95853
+ await writeDeviceState({ device_id: result.id, user_id: session.user_id, install_id_hash, ...result.claim_ticket ? { claim_ticket: result.claim_ticket } : {} }, stateDir);
94874
95854
  return result.id;
94875
95855
  }
94876
95856
  function parseDaemonLock(raw) {
@@ -94882,6 +95862,8 @@ function parseDaemonLock(raw) {
94882
95862
  if (typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
94883
95863
  return {
94884
95864
  pid: parsed.pid,
95865
+ ...typeof parsed.run_id === "string" ? { run_id: parsed.run_id } : {},
95866
+ ...typeof parsed.started_at === "string" ? { started_at: parsed.started_at } : {},
94885
95867
  ...typeof parsed.version === "string" ? { version: parsed.version } : {},
94886
95868
  ...typeof parsed.command === "string" ? { command: parsed.command } : {},
94887
95869
  ...typeof parsed.process_identity === "string" ? { process_identity: parsed.process_identity } : {},
@@ -94892,19 +95874,20 @@ function parseDaemonLock(raw) {
94892
95874
  }
94893
95875
  }
94894
95876
  async function acquireDaemonLock(stateDir) {
94895
- const lockPath = join9(stateDir, "daemon.pid");
94896
- const reclaimPath = join9(stateDir, "daemon.pid.reclaim");
95877
+ const lockPath = join10(stateDir, "daemon.pid");
95878
+ const reclaimPath = join10(stateDir, "daemon.pid.reclaim");
94897
95879
  await mkdir6(stateDir, { recursive: true, mode: 448 });
94898
95880
  if (process.platform !== "win32") await chmod5(stateDir, 448);
94899
95881
  const contents = JSON.stringify({
94900
95882
  pid: process.pid,
95883
+ run_id: `run_${randomBytes3(16).toString("hex")}`,
94901
95884
  version,
94902
95885
  started_at: (/* @__PURE__ */ new Date()).toISOString(),
94903
95886
  command: redactedProcessCommand(process.argv),
94904
95887
  process_identity: processIdentity(process.pid),
94905
95888
  vault_unlocked: Boolean(vaultPasswordFromInput())
94906
95889
  }, null, 2);
94907
- const candidatePath = join9(stateDir, `.daemon.pid.${process.pid}.${Date.now()}`);
95890
+ const candidatePath = join10(stateDir, `.daemon.pid.${process.pid}.${Date.now()}`);
94908
95891
  await writeFile6(candidatePath, contents, { mode: 384, flag: "wx" });
94909
95892
  if (process.platform !== "win32") await chmod5(candidatePath, 384);
94910
95893
  try {
@@ -94912,7 +95895,7 @@ async function acquireDaemonLock(stateDir) {
94912
95895
  let reclaimOwner = null;
94913
95896
  let reclaimPresent = false;
94914
95897
  try {
94915
- reclaimOwner = parseDaemonLock(await readFile9(reclaimPath, "utf8"));
95898
+ reclaimOwner = parseDaemonLock(await readFile10(reclaimPath, "utf8"));
94916
95899
  reclaimPresent = true;
94917
95900
  } catch {
94918
95901
  }
@@ -94931,7 +95914,7 @@ async function acquireDaemonLock(stateDir) {
94931
95914
  }
94932
95915
  let existingLock = null;
94933
95916
  try {
94934
- existingLock = parseDaemonLock(await readFile9(lockPath, "utf8"));
95917
+ existingLock = parseDaemonLock(await readFile10(lockPath, "utf8"));
94935
95918
  } catch {
94936
95919
  }
94937
95920
  const identityMatches = !existingLock?.process_identity || processIdentity(existingLock.pid) === existingLock.process_identity;
@@ -94964,32 +95947,42 @@ async function acquireDaemonLock(stateDir) {
94964
95947
  async function releaseDaemonLock(stateDir, expectedPid) {
94965
95948
  if (expectedPid !== void 0) {
94966
95949
  try {
94967
- const current = parseDaemonLock(await readFile9(join9(stateDir, "daemon.pid"), "utf8"));
95950
+ const current = parseDaemonLock(await readFile10(join10(stateDir, "daemon.pid"), "utf8"));
94968
95951
  if (current?.pid !== expectedPid) return;
94969
95952
  } catch {
94970
95953
  return;
94971
95954
  }
94972
95955
  }
94973
- await rm6(join9(stateDir, "daemon.pid"), { force: true });
94974
- if (expectedPid === void 0) await rm6(join9(stateDir, "daemon.pid.reclaim"), { force: true });
95956
+ await rm6(join10(stateDir, "daemon.pid"), { force: true });
95957
+ if (expectedPid === void 0) await rm6(join10(stateDir, "daemon.pid.reclaim"), { force: true });
94975
95958
  }
94976
95959
  function daemonLogPath(stateDir) {
94977
- return join9(stateDir, "daemon.log");
95960
+ return join10(stateDir, "daemon.log");
94978
95961
  }
94979
95962
  async function activeDaemonLock(stateDir) {
94980
95963
  try {
94981
- const lock = parseDaemonLock(await readFile9(join9(stateDir, "daemon.pid"), "utf8"));
95964
+ const lock = parseDaemonLock(await readFile10(join10(stateDir, "daemon.pid"), "utf8"));
94982
95965
  const identityMatches = !lock?.process_identity || processIdentity(lock.pid) === lock.process_identity;
94983
95966
  if (lock && isProcessRunning(lock.pid) && identityMatches) return lock;
94984
95967
  } catch {
94985
95968
  }
94986
95969
  return null;
94987
95970
  }
95971
+ async function interruptedDaemonRun(stateDir) {
95972
+ try {
95973
+ const lock = parseDaemonLock(await readFile10(join10(stateDir, "daemon.pid"), "utf8"));
95974
+ if (!lock) return null;
95975
+ const identityMatches = !lock.process_identity || processIdentity(lock.pid) === lock.process_identity;
95976
+ return isProcessRunning(lock.pid) && identityMatches ? null : lock;
95977
+ } catch {
95978
+ return null;
95979
+ }
95980
+ }
94988
95981
  async function writeDaemonStatus(stateDir, patch) {
94989
- const statusPath = join9(stateDir, "daemon-status.json");
95982
+ const statusPath = join10(stateDir, "daemon-status.json");
94990
95983
  let current = { running: false };
94991
95984
  try {
94992
- current = JSON.parse(await readFile9(statusPath, "utf8"));
95985
+ current = JSON.parse(await readFile10(statusPath, "utf8"));
94993
95986
  } catch {
94994
95987
  }
94995
95988
  await mkdir6(stateDir, { recursive: true, mode: 448 });
@@ -95001,12 +95994,20 @@ function formatError(error) {
95001
95994
  if (error instanceof ApiError) return `${error.code} (${error.status}): ${error.message}`;
95002
95995
  return error instanceof Error ? error.message : String(error);
95003
95996
  }
95997
+ function daemonFailureMessage(error) {
95998
+ const raw = formatError(error).trim();
95999
+ const code = error && typeof error === "object" && "code" in error ? String(error.code ?? "") : "";
96000
+ if (code === "ENOSPC" || /\bENOSPC\b|no space left on device/i.test(raw)) {
96001
+ return "DAEMON_STORAGE_FULL: The daemon could not start because the disk is full. Free disk space and retry.";
96002
+ }
96003
+ return raw || "DAEMON_START_FAILED: The daemon stopped unexpectedly.";
96004
+ }
95004
96005
  function isFatalDaemonError(error) {
95005
96006
  if (!(error instanceof ApiError)) return false;
95006
96007
  return error.status === 401 || error.status === 403 || error.status === 410 || error.code === "DEVICE_REMOVED";
95007
96008
  }
95008
96009
  function sleep3(ms) {
95009
- return new Promise((resolve2) => setTimeout(resolve2, ms));
96010
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
95010
96011
  }
95011
96012
  async function stopDaemon(stateDir) {
95012
96013
  const lock = await activeDaemonLock(stateDir);
@@ -95084,12 +96085,15 @@ async function startDetachedDaemon(stateDir, intervalSeconds, vaultPassword) {
95084
96085
  }
95085
96086
  async function startDaemonAfterAuth(session, stateDir, vaultPassword) {
95086
96087
  const deviceId = await ensureDaemonDeviceId(session, stateDir);
96088
+ diagnosticReporter.setContext({ user_id: session.user_id ?? null, device_id: await verifiedDiagnosticDeviceId(session, stateDir) });
96089
+ await diagnosticReporter.claimContext().catch(() => void 0);
96090
+ await diagnosticReporter.flush({ force: true }).catch(() => void 0);
95087
96091
  console.log(`Device ready: ${deviceId}`);
95088
96092
  const intervalSeconds = Number(arg("--daemon-interval-seconds", "30"));
95089
96093
  const result = await startDetachedDaemon(stateDir, Number.isFinite(intervalSeconds) ? intervalSeconds : 30, vaultPassword);
95090
96094
  console.log(result === "started" ? "Daemon started" : "Daemon already running");
95091
96095
  }
95092
- async function waitForDshHost(socketPath, timeoutMs = 8e3) {
96096
+ async function waitForDshHost(socketPath, timeoutMs = 3e4) {
95093
96097
  const deadline = Date.now() + timeoutMs;
95094
96098
  let lastError = null;
95095
96099
  while (Date.now() < deadline) {
@@ -95129,7 +96133,7 @@ async function ensureDshHostForCommand(stateDir, session) {
95129
96133
  const socketPath = dshHostSocketPath(defaultStateRoot());
95130
96134
  const matchesSession = (status) => {
95131
96135
  const expectedRoot = dshAccountRoot(session, defaultStateRoot());
95132
- return status.account.server_key === basename3(dirname6(expectedRoot)) && status.account.user_id === session.user_id;
96136
+ return status.account.server_key === basename2(dirname6(expectedRoot)) && status.account.user_id === session.user_id;
95133
96137
  };
95134
96138
  let active = null;
95135
96139
  try {
@@ -95288,8 +96292,14 @@ async function main() {
95288
96292
  const client = new KeySyncApiClient({ baseUrl });
95289
96293
  const previousSession = await readSession(stateDir);
95290
96294
  const result = await client.post("/auth/login", { email, password });
96295
+ diagnosticAccessToken = result.access_token;
96296
+ diagnosticReporter.setContext({ user_id: result.user.id });
96297
+ await diagnosticReporter.claimContext().catch(() => void 0);
96298
+ await diagnosticReporter.flush({ force: true }).catch(() => void 0);
95291
96299
  const session = { baseUrl, access_token: result.access_token, refresh_token: result.refresh_token, user_id: result.user.id };
95292
- await unlockOrBootstrapVault(clientFromSession(session), vaultPassword);
96300
+ const vaultClient = clientFromSession(session);
96301
+ const vaultStatus = await vaultClient.get("/vault/initialized");
96302
+ await accessVaultMaterial(vaultClient, vaultPassword, vaultStatus.initialized ? "unlock" : "setup");
95293
96303
  await stopActiveDshHost();
95294
96304
  await stopDaemon(stateDir);
95295
96305
  if (previousSession?.user_id && previousSession.user_id !== result.user.id) await clearDeviceState(stateDir);
@@ -95322,7 +96332,11 @@ async function main() {
95322
96332
  const previousSession = await readSession(stateDir);
95323
96333
  if (previousSession?.user_id && result.user?.id && previousSession.user_id !== result.user.id) await clearDeviceState(stateDir);
95324
96334
  const session = { baseUrl, access_token: result.access_token, refresh_token: result.refresh_token, user_id: result.user?.id };
95325
- await unlockOrBootstrapVault(clientFromSession(session), vaultPassword);
96335
+ diagnosticAccessToken = result.access_token;
96336
+ diagnosticReporter.setContext({ user_id: result.user?.id ?? null });
96337
+ await diagnosticReporter.claimContext().catch(() => void 0);
96338
+ await diagnosticReporter.flush({ force: true }).catch(() => void 0);
96339
+ await accessVaultMaterial(clientFromSession(session), vaultPassword, "setup");
95326
96340
  await stopActiveDshHost();
95327
96341
  await stopDaemon(stateDir);
95328
96342
  await writeSession(session, stateDir);
@@ -95330,7 +96344,7 @@ async function main() {
95330
96344
  if (!process.argv.includes("--no-start-daemon")) await startDaemonAfterAuth(session, stateDir, vaultPassword);
95331
96345
  return;
95332
96346
  }
95333
- await rm6(join9(stateDir, "session.json"), { force: true });
96347
+ await rm6(join10(stateDir, "session.json"), { force: true });
95334
96348
  await clearDeviceState(stateDir);
95335
96349
  console.log(`Account created. Check ${result.email} for a verification code, then run keysync login after verifying your email.`);
95336
96350
  return;
@@ -95367,7 +96381,7 @@ async function main() {
95367
96381
  } catch {
95368
96382
  }
95369
96383
  }
95370
- await rm6(join9(stateDir, "session.json"), { force: true });
96384
+ await rm6(join10(stateDir, "session.json"), { force: true });
95371
96385
  if (daemonWasRunning) console.log("Daemon stopped");
95372
96386
  console.log("Logged out");
95373
96387
  return;
@@ -95384,24 +96398,27 @@ async function main() {
95384
96398
  const stateDir = commandStateDir();
95385
96399
  const session = await requireSession(stateDir);
95386
96400
  const client = clientFromSession(session);
95387
- const install_id_hash = await installIdHashForSession(session, stateDir);
95388
96401
  const existingDeviceId = arg("--device-id");
95389
96402
  if (existingDeviceId) {
95390
96403
  const current = await client.get(`/devices/${existingDeviceId}`);
96404
+ const match = current.install_id_hash ? (await installCandidatesForServer(session.baseUrl, stateDir)).find(({ install }) => installIdHashFor(install.install_id) === current.install_id_hash) : null;
96405
+ if (current.install_id_hash && !match) throw new Error("This device's previous installation identity is unavailable locally.");
96406
+ const install_id_hash2 = match ? installIdHashFor(match.install.install_id) : await installIdHashForSession(session, stateDir);
95391
96407
  const result2 = await client.post("/devices/register", {
95392
96408
  name: current.name,
95393
96409
  platform: current.platform,
95394
96410
  hostname: current.hostname ?? null,
95395
96411
  client_version: version,
95396
- install_id_hash,
96412
+ install_id_hash: install_id_hash2,
95397
96413
  existing_device_id: existingDeviceId
95398
96414
  });
95399
- await writeDeviceState({ device_id: result2.id, user_id: session.user_id }, stateDir);
96415
+ await writeDeviceState({ device_id: result2.id, user_id: session.user_id, install_id_hash: install_id_hash2, ...result2.claim_ticket ? { claim_ticket: result2.claim_ticket } : {} }, stateDir);
95400
96416
  console.log(`Device bound: ${result2.id}`);
95401
96417
  return;
95402
96418
  }
95403
96419
  const registration = await detectDeviceRegistrationInfo();
95404
96420
  const name = arg("--name", registration.name);
96421
+ const install_id_hash = await installIdHashForSession(session, stateDir);
95405
96422
  const result = await client.post("/devices/register", {
95406
96423
  name,
95407
96424
  platform: registration.platform,
@@ -95409,11 +96426,15 @@ async function main() {
95409
96426
  client_version: version,
95410
96427
  install_id_hash
95411
96428
  });
95412
- await writeDeviceState({ device_id: result.id, user_id: session.user_id }, stateDir);
96429
+ await writeDeviceState({ device_id: result.id, user_id: session.user_id, install_id_hash, ...result.claim_ticket ? { claim_ticket: result.claim_ticket } : {} }, stateDir);
95413
96430
  console.log(`Device bound: ${result.id}`);
95414
96431
  return;
95415
96432
  }
95416
96433
  if (command === "sync") {
96434
+ const forceApp = arg("--app");
96435
+ if (forceApp && !agentAppNames.includes(forceApp)) {
96436
+ throw new Error(`Unknown app: ${forceApp}. Expected one of ${agentAppNames.join(", ")}.`);
96437
+ }
95417
96438
  const stateDir = commandStateDir();
95418
96439
  const vaultPassword = requiredVaultPassword("sync");
95419
96440
  const session = await requireSession(stateDir);
@@ -95421,7 +96442,12 @@ async function main() {
95421
96442
  const asJson = hasFlag2("--json");
95422
96443
  const envTrigger = process.env.KEYSYNC_APPLY_TRIGGER;
95423
96444
  const trigger = envTrigger === "manual_sync" || envTrigger === "manual_reapply" || envTrigger === "daemon_auto" || envTrigger === "cli_sync" ? envTrigger : "cli_sync";
95424
- const result = await syncAndApply(session, deviceId, stateDir, { vaultPassword, force: hasFlag2("--force"), trigger });
96445
+ const result = await syncAndApply(session, deviceId, stateDir, {
96446
+ vaultPassword,
96447
+ force: hasFlag2("--force") && !forceApp,
96448
+ forceAppNames: forceApp ? [forceApp] : void 0,
96449
+ trigger
96450
+ });
95425
96451
  if (asJson) {
95426
96452
  printJson({
95427
96453
  device_id: deviceId,
@@ -95507,8 +96533,8 @@ async function main() {
95507
96533
  const baseUrl = session?.baseUrl ?? selectedBaseUrl;
95508
96534
  if (!baseUrl) throw new Error("Missing server URL. Set KEYSYNC_API_URL or KEYSYNC_DEFAULT_API_URL in environment.");
95509
96535
  const client = new KeySyncApiClient({ baseUrl, accessToken: session?.access_token });
95510
- const [health, server_version] = await Promise.all([client.get("/health"), client.get("/version")]);
95511
- console.log(JSON.stringify({ server_url: baseUrl, state_dir: stateDir, health, server_version }, null, 2));
96536
+ const [health, server_version, diagnostics] = await Promise.all([client.get("/health"), client.get("/version"), diagnosticReporter.status()]);
96537
+ console.log(JSON.stringify({ server_url: baseUrl, state_dir: stateDir, health, server_version, diagnostics }, null, 2));
95512
96538
  return;
95513
96539
  }
95514
96540
  if (command === "self-update" || command === "update") {
@@ -95593,6 +96619,15 @@ async function main() {
95593
96619
  if (command === "daemon") {
95594
96620
  const stateDir = commandStateDir();
95595
96621
  const session = await requireSession(stateDir);
96622
+ const interruptedRun = await interruptedDaemonRun(stateDir);
96623
+ if (interruptedRun) {
96624
+ await reportCliFailure(Object.assign(new Error("The previous daemon run ended without a clean shutdown."), { code: "PREVIOUS_RUN_INTERRUPTED" }), {
96625
+ failure_stage: "startup_sentinel",
96626
+ previous_pid: interruptedRun.pid,
96627
+ previous_started_at: interruptedRun.started_at ?? null,
96628
+ previous_run_id: interruptedRun.run_id ?? null
96629
+ });
96630
+ }
95596
96631
  if (!await acquireDaemonLock(stateDir)) {
95597
96632
  console.log("Daemon already running");
95598
96633
  return;
@@ -95606,6 +96641,9 @@ async function main() {
95606
96641
  try {
95607
96642
  const result = await runDaemonTick(session, stateDir, currentDeviceId);
95608
96643
  currentDeviceId = result.deviceId;
96644
+ diagnosticReporter.setContext({ user_id: session.user_id ?? null, device_id: await verifiedDiagnosticDeviceId(session, stateDir) });
96645
+ await diagnosticReporter.claimContext().catch(() => void 0);
96646
+ await diagnosticReporter.flush({ force: true }).catch(() => void 0);
95609
96647
  await writeDaemonStatus(stateDir, { running: true, last_tick_completed_at: (/* @__PURE__ */ new Date()).toISOString(), last_error: null });
95610
96648
  if (result.mode === "discovery") console.log("Daemon tick complete. Discovery updated.");
95611
96649
  else console.log(`Daemon tick complete. Snapshot: ${result.snapshotId}`);
@@ -95616,6 +96654,7 @@ async function main() {
95616
96654
  await clearDeviceState(stateDir);
95617
96655
  }
95618
96656
  await writeDaemonStatus(stateDir, { running: true, last_error: message });
96657
+ await reportCliFailure(error, { failure_stage: "daemon_tick", device_id: currentDeviceId, retryable: !isFatalDaemonError(error) });
95619
96658
  console.error(`Daemon tick failed: ${message}`);
95620
96659
  if (isFatalDaemonError(error)) throw error;
95621
96660
  }
@@ -95641,8 +96680,8 @@ async function main() {
95641
96680
  let dshRpc = null;
95642
96681
  let stopped = false;
95643
96682
  let stopNow = () => void 0;
95644
- const stopPromise = new Promise((resolve2) => {
95645
- stopNow = resolve2;
96683
+ const stopPromise = new Promise((resolve3) => {
96684
+ stopNow = resolve3;
95646
96685
  });
95647
96686
  process.once("SIGINT", () => {
95648
96687
  stopped = true;
@@ -95680,6 +96719,7 @@ async function main() {
95680
96719
  await activeHost.reconcile();
95681
96720
  } catch (error) {
95682
96721
  await writeDaemonStatus(stateDir, { running: true, last_error: `DSH recovery check failed: ${formatError(error)}` });
96722
+ await reportCliFailure(error, { failure_stage: "dsh_recovery", retryable: true });
95683
96723
  }
95684
96724
  }
95685
96725
  })();
@@ -95690,27 +96730,33 @@ async function main() {
95690
96730
  } finally {
95691
96731
  stopped = true;
95692
96732
  stopNow();
95693
- await dshHost?.dispatch("dsh.cancel-install").catch(() => void 0);
96733
+ await dshHost?.dispatch("dsh.cancel-install").catch(async (error) => {
96734
+ await reportCliFailure(error, { failure_stage: "daemon_cancel_install" });
96735
+ });
95694
96736
  await dshRecoveryLoop;
95695
96737
  let cleanupError;
95696
96738
  try {
95697
96739
  await dshHost?.shutdown();
95698
96740
  } catch (error) {
96741
+ await reportCliFailure(error, { failure_stage: "daemon_shutdown_dsh" });
95699
96742
  cleanupError = error;
95700
96743
  }
95701
96744
  try {
95702
96745
  await dshRpc?.close();
95703
96746
  } catch (error) {
96747
+ await reportCliFailure(error, { failure_stage: "daemon_shutdown_rpc" });
95704
96748
  cleanupError ??= error;
95705
96749
  }
95706
96750
  try {
95707
96751
  await writeDaemonStatus(stateDir, { running: false, stopped_at: (/* @__PURE__ */ new Date()).toISOString() });
95708
96752
  } catch (error) {
96753
+ await reportCliFailure(error, { failure_stage: "daemon_shutdown_status_write" });
95709
96754
  cleanupError ??= error;
95710
96755
  }
95711
96756
  try {
95712
96757
  await releaseDaemonLock(stateDir, process.pid);
95713
96758
  } catch (error) {
96759
+ await reportCliFailure(error, { failure_stage: "daemon_shutdown_lock_release" });
95714
96760
  cleanupError ??= error;
95715
96761
  }
95716
96762
  if (cleanupError) throw cleanupError;
@@ -95720,7 +96766,27 @@ async function main() {
95720
96766
  }
95721
96767
  throw new Error(`Unknown command: ${command}`);
95722
96768
  }
95723
- main().catch((error) => {
96769
+ process.on("uncaughtException", (error) => {
96770
+ void reportCliFailure(error, { failure_stage: "uncaught_exception" }).finally(() => {
96771
+ process.exitCode = 1;
96772
+ });
96773
+ });
96774
+ process.on("unhandledRejection", (reason) => {
96775
+ void reportCliFailure(reason, { failure_stage: "unhandled_rejection" }).finally(() => {
96776
+ process.exitCode = 1;
96777
+ });
96778
+ });
96779
+ main().catch(async (error) => {
96780
+ if (process.argv[2] === "daemon") {
96781
+ const message = daemonFailureMessage(error);
96782
+ try {
96783
+ const stateDir = commandStateDir();
96784
+ await writeDaemonStatus(stateDir, { running: false, stopped_at: (/* @__PURE__ */ new Date()).toISOString(), last_error: message });
96785
+ await releaseDaemonLock(stateDir, process.pid);
96786
+ } catch {
96787
+ }
96788
+ }
96789
+ await reportCliFailure(error, { failure_stage: "command" });
95724
96790
  console.error(error instanceof ApiError ? formatError(error) : error instanceof Error ? error.message : error);
95725
96791
  process.exitCode = 1;
95726
96792
  });