@keysync/cli 0.1.132 → 0.1.134

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,12 +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
90054
  ...typeof parsed.user_id === "string" && parsed.user_id.trim() ? { user_id: parsed.user_id } : {},
89249
- ...typeof parsed.claim_ticket === "string" && parsed.claim_ticket.trim() ? { claim_ticket: parsed.claim_ticket } : {}
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 } : {}
89250
90057
  };
89251
90058
  } catch {
89252
90059
  return null;
@@ -89264,17 +90071,17 @@ async function readDeviceState(stateDir = defaultStateDir(), options = {}) {
89264
90071
  }
89265
90072
  async function writeDeviceState(device, stateDir = defaultStateDir()) {
89266
90073
  await mkdir3(stateDir, { recursive: true });
89267
- await writeFile3(join6(stateDir, "device.json"), JSON.stringify(device, null, 2));
90074
+ await writeFile3(join7(stateDir, "device.json"), JSON.stringify(device, null, 2));
89268
90075
  }
89269
90076
  async function clearDeviceState(stateDir = defaultStateDir()) {
89270
- await rm3(join6(stateDir, "device.json"), { force: true });
90077
+ await rm3(join7(stateDir, "device.json"), { force: true });
89271
90078
  }
89272
90079
  function localDeviceSettingsPath(stateDir = defaultStateDir()) {
89273
- return join6(stateDir, "device-settings.json");
90080
+ return join7(stateDir, "device-settings.json");
89274
90081
  }
89275
90082
  async function readLocalDeviceSettings(stateDir = defaultStateDir()) {
89276
90083
  try {
89277
- const parsed = JSON.parse(await readFile6(localDeviceSettingsPath(stateDir), "utf8"));
90084
+ const parsed = JSON.parse(await readFile7(localDeviceSettingsPath(stateDir), "utf8"));
89278
90085
  return {
89279
90086
  version: 1,
89280
90087
  ...typeof parsed.device_id === "string" && parsed.device_id.trim() ? { device_id: parsed.device_id } : {},
@@ -89291,7 +90098,7 @@ async function readLocalDeviceSettings(stateDir = defaultStateDir()) {
89291
90098
  }
89292
90099
  async function readInstallStateFromDir(installStateDir) {
89293
90100
  try {
89294
- const parsed = JSON.parse(await readFile6(join6(installStateDir, "install.json"), "utf8"));
90101
+ const parsed = JSON.parse(await readFile7(join7(installStateDir, "install.json"), "utf8"));
89295
90102
  if (typeof parsed.install_id !== "string" || !parsed.install_id.trim()) return null;
89296
90103
  if (typeof parsed.created_at !== "string" || !parsed.created_at.trim()) return null;
89297
90104
  return { install_id: parsed.install_id, created_at: parsed.created_at };
@@ -89309,25 +90116,32 @@ async function readInstallState(installStateDir = defaultInstallStateDir(), opti
89309
90116
  }
89310
90117
  return null;
89311
90118
  }
89312
- async function writeInstallState(install, installStateDir = defaultInstallStateDir()) {
89313
- await mkdir3(installStateDir, { recursive: true });
89314
- await writeFile3(join6(installStateDir, "install.json"), JSON.stringify(install, null, 2), { mode: 384 });
89315
- }
89316
90119
  async function ensureInstallState(installStateDir = defaultInstallStateDir(), options = {}) {
89317
- const existing = await readInstallStateFromDir(installStateDir);
89318
- if (existing) return existing;
89319
- const legacy = await readInstallState(installStateDir, options);
89320
- if (legacy) {
89321
- await writeInstallState(legacy, installStateDir);
89322
- return legacy;
89323
- }
89324
- const install = { install_id: randomBytes(32).toString("base64url"), created_at: (/* @__PURE__ */ new Date()).toISOString() };
89325
- await writeInstallState(install, installStateDir);
89326
- return install;
90120
+ return (0, import_install_identity.ensureInstallIdentitySync)(installStateDir, { legacyStateDirs: options.legacyStateDirs });
89327
90121
  }
89328
90122
  function installIdHashFor(installId) {
89329
90123
  return `sha256:${createHash5("sha256").update(`keysync-install-id:${installId}`).digest("hex")}`;
89330
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
+ }
89331
90145
  function clientFromSession(session, options = {}) {
89332
90146
  return new KeySyncApiClient({ ...options, baseUrl: session.baseUrl, accessToken: session.access_token });
89333
90147
  }
@@ -89366,8 +90180,6 @@ async function maybeMigrateCodexSessionsAfterApply(input) {
89366
90180
  sqlite_rows_changed: 0,
89367
90181
  rollout_files_changed: 0,
89368
90182
  rollout_files_skipped: 0,
89369
- backups_created: 0,
89370
- backups_cleaned: 0,
89371
90183
  warnings: [{
89372
90184
  code: "MIGRATION_FAILED",
89373
90185
  message: error instanceof Error ? error.message : "Codex session migration failed."
@@ -89424,10 +90236,10 @@ function canonicalJson(value) {
89424
90236
  return JSON.stringify(canonicalize(value));
89425
90237
  }
89426
90238
  function localApplyDir(stateDir) {
89427
- return join6(stateDir, "local-apply");
90239
+ return join7(stateDir, "local-apply");
89428
90240
  }
89429
90241
  function localApplyEventPath(stateDir) {
89430
- return join6(stateDir, "local-apply-events.jsonl");
90242
+ return join7(stateDir, "local-apply-events.jsonl");
89431
90243
  }
89432
90244
  var REPORT_OUTBOX_MAX_EVENTS = 200;
89433
90245
  var ReportOutboxError = class extends Error {
@@ -89536,7 +90348,7 @@ var reportOutboxDetailKeys = /* @__PURE__ */ new Set([
89536
90348
  "ownership_attempts"
89537
90349
  ]);
89538
90350
  function reportOutboxPath(stateDir) {
89539
- return join6(stateDir, "report-outbox.json");
90351
+ return join7(stateDir, "report-outbox.json");
89540
90352
  }
89541
90353
  function reportOutboxKind(path) {
89542
90354
  if (/^\/devices\/[^/]+\/apps\/[^/]+\/apply-report$/.test(path)) return "apply";
@@ -89579,7 +90391,7 @@ function sanitizeReportOutboxBody(path, body) {
89579
90391
  throw new ReportOutboxError("REPORT_OUTBOX_CORRUPT");
89580
90392
  })
89581
90393
  );
89582
- 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")) {
89583
90395
  throw new ReportOutboxError("REPORT_OUTBOX_CORRUPT");
89584
90396
  }
89585
90397
  return sanitized;
@@ -89593,7 +90405,7 @@ function compactReportOutbox(outbox) {
89593
90405
  async function readReportOutbox(stateDir) {
89594
90406
  let content;
89595
90407
  try {
89596
- content = await readFile6(reportOutboxPath(stateDir), "utf8");
90408
+ content = await readFile7(reportOutboxPath(stateDir), "utf8");
89597
90409
  } catch (error) {
89598
90410
  if (errnoCode(error) === "ENOENT") return { version: 1, events: [] };
89599
90411
  throw new ReportOutboxError("REPORT_OUTBOX_UNREADABLE", error);
@@ -89687,7 +90499,7 @@ async function flushPendingReports(session, stateDir = defaultStateDir()) {
89687
90499
  );
89688
90500
  }
89689
90501
  async function postReportDurably(client, stateDir, path, body) {
89690
- 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")}`;
89691
90503
  const outbox = await readReportOutbox(stateDir);
89692
90504
  if (!outbox.events.some((event) => event.client_event_id === clientEventId)) {
89693
90505
  if (outbox.events.length >= REPORT_OUTBOX_MAX_EVENTS) {
@@ -89722,17 +90534,17 @@ async function postReportGroupDurably(client, stateDir, reports) {
89722
90534
  if (rejection) throw rejection;
89723
90535
  }
89724
90536
  function localApplyKeyPath(stateDir) {
89725
- return join6(localApplyDir(stateDir), "fingerprint.key");
90537
+ return join7(localApplyDir(stateDir), "fingerprint.key");
89726
90538
  }
89727
90539
  function localApplyRecordPath(stateDir, deviceId, appId, mode) {
89728
- return join6(localApplyDir(stateDir), mode, `${sanitizeHostForPath(`${deviceId}-${appId}`)}.json`);
90540
+ return join7(localApplyDir(stateDir), mode, `${sanitizeHostForPath(`${deviceId}-${appId}`)}.json`);
89729
90541
  }
89730
90542
  async function localApplyFingerprintKey(stateDir) {
89731
90543
  const path = localApplyKeyPath(stateDir);
89732
90544
  try {
89733
- return Buffer.from((await readFile6(path, "utf8")).trim(), "base64");
90545
+ return Buffer.from((await readFile7(path, "utf8")).trim(), "base64");
89734
90546
  } catch {
89735
- const key = randomBytes(32);
90547
+ const key = randomBytes2(32);
89736
90548
  await mkdir3(localApplyDir(stateDir), { recursive: true });
89737
90549
  await writeFile3(path, `${key.toString("base64")}
89738
90550
  `, { mode: 384 });
@@ -89841,20 +90653,40 @@ function buildSourceFingerprintInput(appName, appId, source, provider, selectedE
89841
90653
  }
89842
90654
  async function readLocalApplyRecord(stateDir, deviceId, appId, mode) {
89843
90655
  try {
89844
- return JSON.parse(await readFile6(localApplyRecordPath(stateDir, deviceId, appId, mode), "utf8"));
90656
+ return JSON.parse(await readFile7(localApplyRecordPath(stateDir, deviceId, appId, mode), "utf8"));
89845
90657
  } catch {
89846
90658
  return null;
89847
90659
  }
89848
90660
  }
89849
90661
  async function writeLocalApplyRecord(stateDir, record) {
89850
90662
  const path = localApplyRecordPath(stateDir, record.device_id, record.app_id, record.apply_mode);
89851
- await mkdir3(join6(localApplyDir(stateDir), record.apply_mode), { recursive: true });
90663
+ await mkdir3(join7(localApplyDir(stateDir), record.apply_mode), { recursive: true });
89852
90664
  await writeFile3(path, `${JSON.stringify(record, null, 2)}
89853
90665
  `, { mode: 384 });
89854
90666
  }
89855
90667
  async function clearLocalApplyRecord(stateDir, deviceId, appId, mode) {
89856
90668
  await rm3(localApplyRecordPath(stateDir, deviceId, appId, mode), { force: true });
89857
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
+ }
89858
90690
  async function recordApplyAttempt(input) {
89859
90691
  await writeLocalApplyRecord(input.stateDir, {
89860
90692
  version: 2,
@@ -89877,7 +90709,8 @@ async function recordApplyAttempt(input) {
89877
90709
  bridge_generation: input.bridgeGeneration ?? null,
89878
90710
  failure_code: input.failureCode,
89879
90711
  failure_message: input.failureMessage,
89880
- adopted_local: input.adoptedLocal
90712
+ adopted_local: input.adoptedLocal,
90713
+ written_account_key: input.writtenAccountKey
89881
90714
  });
89882
90715
  }
89883
90716
  async function appendLocalApplyEvent(stateDir, event) {
@@ -89888,14 +90721,16 @@ async function appendLocalApplyEvent(stateDir, event) {
89888
90721
  function localApplyEventMessage(status, message) {
89889
90722
  if (status === "success") return "APPLIED";
89890
90723
  const candidate = message?.trim() ?? "";
90724
+ if (status === "conflict") return /^[A-Z0-9_ -]{1,80}$/.test(candidate) ? candidate : "LOCAL_ACCOUNT_MISMATCH";
89891
90725
  return /^[A-Z0-9_ -]{1,80}$/.test(candidate) ? candidate : "APPLY_FAILED";
89892
90726
  }
89893
- async function recordDaemonAutoApplyEvent(input) {
89894
- 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;
89895
90730
  const event = {
89896
90731
  version: 2,
89897
90732
  id: [
89898
- "daemon_auto",
90733
+ trigger,
89899
90734
  input.session.user_id ?? "unknown_user",
89900
90735
  input.deviceId,
89901
90736
  input.app.id,
@@ -89904,7 +90739,7 @@ async function recordDaemonAutoApplyEvent(input) {
89904
90739
  input.status,
89905
90740
  input.reportedAt
89906
90741
  ].map((part) => sanitizeHostForPath(part)).join(":"),
89907
- trigger: "daemon_auto",
90742
+ trigger,
89908
90743
  server_key: serverKeyFromBaseUrl(input.session.baseUrl),
89909
90744
  user_id: input.session.user_id ?? null,
89910
90745
  device_id: input.deviceId,
@@ -89980,12 +90815,12 @@ async function enrichApplyFailureMessage(stateDir, failureCode, message) {
89980
90815
  };
89981
90816
  }
89982
90817
  function randomBase64(byteLength = 32) {
89983
- return randomBytes(byteLength).toString("base64");
90818
+ return randomBytes2(byteLength).toString("base64");
89984
90819
  }
89985
90820
  function encryptWithAesGcm(rawKey, plaintext) {
89986
90821
  const key = Buffer.from(rawKey);
89987
90822
  if (key.length !== 32) throw new Error("AES-256-GCM key must be 32 bytes");
89988
- const iv = randomBytes(12);
90823
+ const iv = randomBytes2(12);
89989
90824
  const cipher = createCipheriv("aes-256-gcm", key, iv);
89990
90825
  const body = Buffer.concat([cipher.update(typeof plaintext === "string" ? Buffer.from(plaintext, "utf8") : Buffer.from(plaintext)), cipher.final()]);
89991
90826
  return {
@@ -90011,7 +90846,7 @@ function deriveVaultKek(vaultPassword, kdf) {
90011
90846
  return pbkdf2Sync(vaultPassword, Buffer.from(kdf.salt, "base64"), iterations, 32, "sha256");
90012
90847
  }
90013
90848
  async function createVaultBootstrapInput(vaultPassword, name = "Personal") {
90014
- const vaultKey = randomBytes(32);
90849
+ const vaultKey = randomBytes2(32);
90015
90850
  const keyPair = generateKeyPairSync("ec", {
90016
90851
  namedCurve: "prime256v1",
90017
90852
  publicKeyEncoding: { type: "spki", format: "der" },
@@ -90051,7 +90886,7 @@ function wrapSecretDataKeyForUser(recipientPublicKeyBase64, secretDataKey) {
90051
90886
  };
90052
90887
  }
90053
90888
  function createProviderSecretEnvelopeV2WithGrants(vaultId, vaultKey, secrets, preservedUserGrants) {
90054
- const secretDataKey = randomBytes(32);
90889
+ const secretDataKey = randomBytes2(32);
90055
90890
  return {
90056
90891
  encrypted_secrets: Object.fromEntries(Object.entries(secrets).map(([key, value]) => [key, encryptWithAesGcm(secretDataKey, value)])),
90057
90892
  grants: [
@@ -90091,6 +90926,15 @@ async function buildProviderSecretEnvelopeForImport(client, providerId, vaultId,
90091
90926
  const preserved = providerId ? await fetchPreservedProviderUserGrants(client, providerId) : [];
90092
90927
  return createProviderSecretEnvelopeV2WithGrants(vaultId, vaultKey, secrets, preserved);
90093
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
+ }
90094
90938
  async function unlockOrBootstrapVault(client, vaultPassword) {
90095
90939
  if (!vaultPassword) throw new Error("Vault Password is required");
90096
90940
  try {
@@ -90100,17 +90944,14 @@ async function unlockOrBootstrapVault(client, vaultPassword) {
90100
90944
  const message = error instanceof Error ? error.message : "";
90101
90945
  if (!isUninitialized && !message.includes("Vault is not initialized")) throw error;
90102
90946
  }
90103
- const { body, vaultKey } = await createVaultBootstrapInput(vaultPassword);
90104
- let bootstrap;
90105
90947
  try {
90106
- bootstrap = await client.post("/vault/bootstrap", body);
90948
+ return await setupVaultMaterial(client, vaultPassword);
90107
90949
  } catch (error) {
90108
90950
  if (error instanceof ApiError && error.code === "VAULT_ALREADY_INITIALIZED") {
90109
90951
  return unlockVaultMaterial(client, vaultPassword);
90110
90952
  }
90111
90953
  throw error;
90112
90954
  }
90113
- return { vaultKeys: /* @__PURE__ */ new Map([[bootstrap.vault_id, vaultKey]]), privateKey: null };
90114
90955
  }
90115
90956
  async function unlockOrBootstrapVaultKeys(client, vaultPassword) {
90116
90957
  return (await unlockOrBootstrapVault(client, vaultPassword)).vaultKeys;
@@ -90180,28 +91021,28 @@ function snapshotProviderStrategyMap(snapshot) {
90180
91021
  return result;
90181
91022
  }
90182
91023
  function bridgeDir(stateDir) {
90183
- return join6(stateDir, "bridge", "bifrost");
91024
+ return join7(stateDir, "bridge", "bifrost");
90184
91025
  }
90185
91026
  function bridgeBifrostConfigPath(stateDir) {
90186
- return join6(bridgeDir(stateDir), "config.json");
91027
+ return join7(bridgeDir(stateDir), "config.json");
90187
91028
  }
90188
91029
  function bridgeRouteManifestPath(stateDir) {
90189
- return join6(bridgeDir(stateDir), "keysync-routes.json");
91030
+ return join7(bridgeDir(stateDir), "keysync-routes.json");
90190
91031
  }
90191
91032
  function bridgeRuntimePath(stateDir) {
90192
- return join6(bridgeDir(stateDir), "runtime.json");
91033
+ return join7(bridgeDir(stateDir), "runtime.json");
90193
91034
  }
90194
91035
  function bridgeStartupStdoutPath(stateDir) {
90195
- return join6(bridgeDir(stateDir), "bridge-startup.stdout.log");
91036
+ return join7(bridgeDir(stateDir), "bridge-startup.stdout.log");
90196
91037
  }
90197
91038
  function bridgeStartupStderrPath(stateDir) {
90198
- return join6(bridgeDir(stateDir), "bridge-startup.stderr.log");
91039
+ return join7(bridgeDir(stateDir), "bridge-startup.stderr.log");
90199
91040
  }
90200
91041
  function bridgeTokenPath(stateDir) {
90201
- return join6(bridgeDir(stateDir), "token.json");
91042
+ return join7(bridgeDir(stateDir), "token.json");
90202
91043
  }
90203
91044
  function bridgeGenerationPath(stateDir) {
90204
- return join6(bridgeDir(stateDir), "generation.json");
91045
+ return join7(bridgeDir(stateDir), "generation.json");
90205
91046
  }
90206
91047
  var bridgeWriteFailureCounts = /* @__PURE__ */ new Map();
90207
91048
  function errnoCode(error) {
@@ -90226,7 +91067,7 @@ function classifyBridgeConfigWriteError(path, code, metadata) {
90226
91067
  }
90227
91068
  async function bridgeFileMetadata(path) {
90228
91069
  try {
90229
- const item = await stat3(path);
91070
+ const item = await stat4(path);
90230
91071
  const windowsReadonly = runtimePlatform() === "win32" ? await windowsReadonlyAttribute(path) : null;
90231
91072
  return {
90232
91073
  exists: true,
@@ -90303,7 +91144,7 @@ function maybeInjectBridgeWriteFailure(path, stage = "before_write") {
90303
91144
  async function writePrivateBridgeFile(path, content, stateDirForDiagnostics) {
90304
91145
  await mkdir3(dirname3(path), { recursive: true });
90305
91146
  const maxAttempts = bridgeWriteRetryAttempts();
90306
- 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`;
90307
91148
  let lastError = null;
90308
91149
  let observedReadonly = false;
90309
91150
  const pathKind = stateDirForDiagnostics ? bridgePathKind(path, stateDirForDiagnostics) : "bridge_private_file";
@@ -90317,7 +91158,7 @@ async function writePrivateBridgeFile(path, content, stateDirForDiagnostics) {
90317
91158
  await writeFile3(tmpPath, content, { mode: 384 });
90318
91159
  await chmod2(tmpPath, 384).catch(() => void 0);
90319
91160
  maybeInjectBridgeWriteFailure(path, "before_rename");
90320
- await rename2(tmpPath, path);
91161
+ await rename3(tmpPath, path);
90321
91162
  await chmod2(path, 384).catch(() => void 0);
90322
91163
  if (stateDirForDiagnostics && (attempt > 1 || before.readonly === true || clearReadonlyResult)) {
90323
91164
  await appendBridgeDiagnostic(stateDirForDiagnostics, "bridge_private_file_write.success", {
@@ -90372,7 +91213,7 @@ function bridgeVirtualKeyId(bridgeProviderKey2) {
90372
91213
  async function readBridgeTokenStore(stateDir) {
90373
91214
  const path = bridgeTokenPath(stateDir);
90374
91215
  try {
90375
- const parsed = JSON.parse(await readFile6(path, "utf8"));
91216
+ const parsed = JSON.parse(await readFile7(path, "utf8"));
90376
91217
  if (parsed.tokens && typeof parsed.tokens === "object" && !Array.isArray(parsed.tokens)) {
90377
91218
  const tokens = {};
90378
91219
  for (const [appId, value] of Object.entries(parsed.tokens)) {
@@ -90413,7 +91254,7 @@ async function bridgeVirtualKey(stateDir, appId) {
90413
91254
  }
90414
91255
  return existing.token;
90415
91256
  }
90416
- const token = `sk-bf-${randomBytes(32).toString("base64url")}`;
91257
+ const token = `sk-bf-${randomBytes2(32).toString("base64url")}`;
90417
91258
  store.tokens[appId] = { token, created_at: (/* @__PURE__ */ new Date()).toISOString() };
90418
91259
  delete store.tokens.legacy;
90419
91260
  await writeBridgeTokenStore(stateDir, store);
@@ -90454,7 +91295,7 @@ async function appendBridgeDiagnostic(stateDir, event, details = {}) {
90454
91295
  try {
90455
91296
  const dir = bridgeDir(stateDir);
90456
91297
  await mkdir3(dir, { recursive: true });
90457
- await appendFile(join6(dir, "bridge-diagnostics.jsonl"), `${JSON.stringify({
91298
+ await appendFile(join7(dir, "bridge-diagnostics.jsonl"), `${JSON.stringify({
90458
91299
  ts: (/* @__PURE__ */ new Date()).toISOString(),
90459
91300
  pid: process.pid,
90460
91301
  platform: runtimePlatform(),
@@ -90576,11 +91417,11 @@ var DEFAULT_SYNC_APPLY_LOCK_OWNER_GRACE_MS = 5e3;
90576
91417
  var DEFAULT_SYNC_APPLY_MANUAL_REAPPLY_WAIT_MS = 5e3;
90577
91418
  var DEFAULT_SYNC_APPLY_LOCK_HEARTBEAT_MS = 3e4;
90578
91419
  function syncApplyLockDir(stateDir) {
90579
- return join6(stateDir, "sync-apply.lock");
91420
+ return join7(stateDir, "sync-apply.lock");
90580
91421
  }
90581
91422
  async function readSyncApplyLockOwner(lockDir) {
90582
91423
  try {
90583
- const parsed = JSON.parse(await readFile6(join6(lockDir, "owner.json"), "utf8"));
91424
+ const parsed = JSON.parse(await readFile7(join7(lockDir, "owner.json"), "utf8"));
90584
91425
  if (!parsed || typeof parsed !== "object") return null;
90585
91426
  const owner = parsed;
90586
91427
  return {
@@ -90608,7 +91449,7 @@ function syncApplyLockHeartbeatMs() {
90608
91449
  return Math.min(DEFAULT_SYNC_APPLY_LOCK_HEARTBEAT_MS, Math.max(1e3, Math.floor(staleMs / 3)));
90609
91450
  }
90610
91451
  async function writeSyncApplyLockOwner(handle, updatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
90611
- await writeFile3(join6(handle.lockDir, "owner.json"), `${JSON.stringify({
91452
+ await writeFile3(join7(handle.lockDir, "owner.json"), `${JSON.stringify({
90612
91453
  version: 1,
90613
91454
  token: handle.token,
90614
91455
  pid: handle.pid,
@@ -90627,7 +91468,7 @@ async function acquireSyncApplyLock(stateDir, deviceId, trigger) {
90627
91468
  const deadline = Date.now() + waitMs;
90628
91469
  await mkdir3(stateDir, { recursive: true });
90629
91470
  for (; ; ) {
90630
- const token = randomBytes(16).toString("hex");
91471
+ const token = randomBytes2(16).toString("hex");
90631
91472
  try {
90632
91473
  await mkdir3(lockDir, { recursive: false });
90633
91474
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -90639,7 +91480,7 @@ async function acquireSyncApplyLock(stateDir, deviceId, trigger) {
90639
91480
  if (code !== "EEXIST") throw error;
90640
91481
  try {
90641
91482
  const owner = await readSyncApplyLockOwner(lockDir);
90642
- const lockStat = await stat3(lockDir);
91483
+ const lockStat = await stat4(lockDir);
90643
91484
  const ownerUpdatedAtMs = owner?.updated_at ? Date.parse(owner.updated_at) : Number.NaN;
90644
91485
  const lockAgeMs = Date.now() - lockStat.mtimeMs;
90645
91486
  const heartbeatAgeMs = Number.isFinite(ownerUpdatedAtMs) ? Date.now() - ownerUpdatedAtMs : lockAgeMs;
@@ -90678,7 +91519,7 @@ async function appendSyncApplyLockDiagnostic(stateDir, event, details = {}) {
90678
91519
  if (process.env.KEYSYNC_SYNC_APPLY_LOCK_DIAGNOSTICS !== "1") return;
90679
91520
  try {
90680
91521
  await mkdir3(stateDir, { recursive: true });
90681
- await appendFile(join6(stateDir, "sync-apply-lock-diagnostics.jsonl"), `${JSON.stringify({
91522
+ await appendFile(join7(stateDir, "sync-apply-lock-diagnostics.jsonl"), `${JSON.stringify({
90682
91523
  ts: (/* @__PURE__ */ new Date()).toISOString(),
90683
91524
  pid: process.pid,
90684
91525
  event,
@@ -90721,7 +91562,7 @@ async function recordBridgeLifecycleEvent(event) {
90721
91562
  }
90722
91563
  async function readBridgeLifecycleLockOwner(lockDir) {
90723
91564
  try {
90724
- const parsed = JSON.parse(await readFile6(join6(lockDir, "owner.json"), "utf8"));
91565
+ const parsed = JSON.parse(await readFile7(join7(lockDir, "owner.json"), "utf8"));
90725
91566
  if (!parsed || typeof parsed !== "object") return null;
90726
91567
  const owner = parsed;
90727
91568
  return {
@@ -90733,7 +91574,7 @@ async function readBridgeLifecycleLockOwner(lockDir) {
90733
91574
  }
90734
91575
  }
90735
91576
  async function withBridgeLifecycleLock(stateDir, fn) {
90736
- const lockDir = join6(bridgeDir(stateDir), ".lifecycle.lock");
91577
+ const lockDir = join7(bridgeDir(stateDir), ".lifecycle.lock");
90737
91578
  await mkdir3(bridgeDir(stateDir), { recursive: true });
90738
91579
  const timeoutMs = Number(process.env.KEYSYNC_BIFROST_LOCK_TIMEOUT_MS ?? 3e4);
90739
91580
  const staleMs = Number(process.env.KEYSYNC_BIFROST_LOCK_STALE_MS ?? DEFAULT_BIFROST_LOCK_STALE_MS);
@@ -90741,7 +91582,7 @@ async function withBridgeLifecycleLock(stateDir, fn) {
90741
91582
  for (; ; ) {
90742
91583
  try {
90743
91584
  await mkdir3(lockDir, { recursive: false });
90744
- 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)}
90745
91586
  `);
90746
91587
  await recordBridgeLifecycleEvent("acquire_lock");
90747
91588
  break;
@@ -90759,7 +91600,7 @@ async function withBridgeLifecycleLock(stateDir, fn) {
90759
91600
  await rm3(lockDir, { recursive: true, force: true });
90760
91601
  continue;
90761
91602
  }
90762
- const lockStat = await stat3(lockDir);
91603
+ const lockStat = await stat4(lockDir);
90763
91604
  if (Date.now() - lockStat.mtimeMs > staleMs) {
90764
91605
  await appendBridgeDiagnostic(stateDir, "bridge_lifecycle_lock.reclaim_stale_mtime", {
90765
91606
  lockDir,
@@ -90860,7 +91701,7 @@ function buildBifrostProviderConfig(plan) {
90860
91701
  }
90861
91702
  async function readBridgeRouteManifest(stateDir) {
90862
91703
  try {
90863
- const parsed = JSON.parse(await readFile6(bridgeRouteManifestPath(stateDir), "utf8"));
91704
+ const parsed = JSON.parse(await readFile7(bridgeRouteManifestPath(stateDir), "utf8"));
90864
91705
  const routes = Array.isArray(parsed.routes) ? parsed.routes.filter((item) => Boolean(item && typeof item === "object" && typeof item.app_id === "string")).map((item) => ({
90865
91706
  ...item,
90866
91707
  app_name: typeof item.app_name === "string" ? item.app_name : "Codex"
@@ -90877,7 +91718,7 @@ async function readBridgeRouteManifest(stateDir) {
90877
91718
  }
90878
91719
  async function readBifrostProviders(stateDir) {
90879
91720
  try {
90880
- const parsed = JSON.parse(await readFile6(bridgeBifrostConfigPath(stateDir), "utf8"));
91721
+ const parsed = JSON.parse(await readFile7(bridgeBifrostConfigPath(stateDir), "utf8"));
90881
91722
  return parsed.providers && typeof parsed.providers === "object" && !Array.isArray(parsed.providers) ? parsed.providers : {};
90882
91723
  } catch {
90883
91724
  return {};
@@ -90922,7 +91763,7 @@ function buildBifrostConfig(stateDir, providers, virtualKeys) {
90922
91763
  enabled: true,
90923
91764
  type: "sqlite",
90924
91765
  config: {
90925
- path: join6(dir, "config.db")
91766
+ path: join7(dir, "config.db")
90926
91767
  }
90927
91768
  },
90928
91769
  logs_store: {
@@ -90930,7 +91771,7 @@ function buildBifrostConfig(stateDir, providers, virtualKeys) {
90930
91771
  type: "sqlite",
90931
91772
  retention_days: 30,
90932
91773
  config: {
90933
- path: join6(dir, "logs.db")
91774
+ path: join7(dir, "logs.db")
90934
91775
  }
90935
91776
  },
90936
91777
  governance: {
@@ -91007,7 +91848,7 @@ function bridgeManifestMatchesActiveAppIds(manifest, activeBridgeAppIds) {
91007
91848
  }
91008
91849
  async function readBridgeRuntimeState(stateDir) {
91009
91850
  try {
91010
- const parsed = JSON.parse(await readFile6(bridgeRuntimePath(stateDir), "utf8"));
91851
+ const parsed = JSON.parse(await readFile7(bridgeRuntimePath(stateDir), "utf8"));
91011
91852
  if (typeof parsed.listen_url !== "string" || !parsed.listen_url.trim()) return null;
91012
91853
  return {
91013
91854
  version: 1,
@@ -91053,14 +91894,14 @@ function bridgeRuntimeBaseState(stateDir, listenUrl, status, current) {
91053
91894
  };
91054
91895
  }
91055
91896
  async function bridgeConfigHash(stateDir) {
91056
- return createHash5("sha256").update(await readFile6(bridgeBifrostConfigPath(stateDir))).digest("hex");
91897
+ return createHash5("sha256").update(await readFile7(bridgeBifrostConfigPath(stateDir))).digest("hex");
91057
91898
  }
91058
91899
  async function readCommittedBridgeGeneration(stateDir) {
91059
91900
  try {
91060
- const parsed = JSON.parse(await readFile6(bridgeGenerationPath(stateDir), "utf8"));
91901
+ const parsed = JSON.parse(await readFile7(bridgeGenerationPath(stateDir), "utf8"));
91061
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;
91062
91903
  if (await bridgeConfigHash(stateDir) !== parsed.config_hash) return null;
91063
- const config = JSON.parse(await readFile6(bridgeBifrostConfigPath(stateDir), "utf8"));
91904
+ const config = JSON.parse(await readFile7(bridgeBifrostConfigPath(stateDir), "utf8"));
91064
91905
  const virtualKeys = Array.isArray(config.governance?.virtual_keys) ? config.governance.virtual_keys : [];
91065
91906
  const providers = config.providers && typeof config.providers === "object" && !Array.isArray(config.providers) ? config.providers : {};
91066
91907
  if (virtualKeys.length !== parsed.route_count) return null;
@@ -91294,7 +92135,7 @@ async function applyBridgeConfigPlan(stateDir, baseUrl, prepared) {
91294
92135
  });
91295
92136
  }
91296
92137
  function sleep2(ms) {
91297
- return new Promise((resolve2) => setTimeout(resolve2, ms));
92138
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
91298
92139
  }
91299
92140
  function truncateText(value, maxLength) {
91300
92141
  if (value.length <= maxLength) return value;
@@ -91305,7 +92146,7 @@ function redactDiagnosticText(value) {
91305
92146
  }
91306
92147
  async function readTextTail(filePath, maxLength = 12e3) {
91307
92148
  try {
91308
- return redactDiagnosticText(truncateText(await readFile6(filePath, "utf8"), maxLength));
92149
+ return redactDiagnosticText(truncateText(await readFile7(filePath, "utf8"), maxLength));
91309
92150
  } catch {
91310
92151
  return null;
91311
92152
  }
@@ -91349,9 +92190,9 @@ function bifrostBinaryPath() {
91349
92190
  const processWithResources = process;
91350
92191
  const resourceBinaryName = runtimePlatform() === "win32" ? "bifrost-http.exe" : "bifrost-http";
91351
92192
  const candidates = [
91352
- processWithResources.resourcesPath ? join6(processWithResources.resourcesPath, "bridge", resourceBinaryName) : null,
91353
- join6(process.cwd(), "resources", "bridge", runtimePlatform(), process.arch, resourceBinaryName),
91354
- 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)
91355
92196
  ].filter((item) => Boolean(item));
91356
92197
  return candidates.find((candidate) => existsSync3(candidate)) ?? null;
91357
92198
  }
@@ -91582,7 +92423,7 @@ function bridgeHostAndPort(baseUrl) {
91582
92423
  return { host, port };
91583
92424
  }
91584
92425
  function execFileText(command, args, timeout = 2e3) {
91585
- return new Promise((resolve2, reject) => {
92426
+ return new Promise((resolve3, reject) => {
91586
92427
  execFile2(command, args, { timeout }, (error, stdout, stderr) => {
91587
92428
  if (error) {
91588
92429
  const enriched = error;
@@ -91590,7 +92431,7 @@ function execFileText(command, args, timeout = 2e3) {
91590
92431
  enriched.stderr = stderr.toString();
91591
92432
  reject(enriched);
91592
92433
  } else {
91593
- resolve2(stdout.toString());
92434
+ resolve3(stdout.toString());
91594
92435
  }
91595
92436
  });
91596
92437
  });
@@ -91996,7 +92837,7 @@ async function stopBridgeProcessesOnPort(stateDir, baseUrl, timeoutMs, knownMana
91996
92837
  async function tcpPortIsBindable(host, port) {
91997
92838
  const portNumber = Number(port);
91998
92839
  if (!Number.isInteger(portNumber) || portNumber <= 0) return false;
91999
- return new Promise((resolve2) => {
92840
+ return new Promise((resolve3) => {
92000
92841
  const server = createServer();
92001
92842
  let settled = false;
92002
92843
  const finish = (result) => {
@@ -92004,8 +92845,8 @@ async function tcpPortIsBindable(host, port) {
92004
92845
  settled = true;
92005
92846
  clearTimeout(timer);
92006
92847
  server.removeAllListeners();
92007
- if (server.listening) server.close(() => resolve2(result));
92008
- else resolve2(result);
92848
+ if (server.listening) server.close(() => resolve3(result));
92849
+ else resolve3(result);
92009
92850
  };
92010
92851
  const timer = setTimeout(() => finish(false), 1e3);
92011
92852
  server.once("error", () => finish(false));
@@ -92050,7 +92891,7 @@ async function resetBridgeConfigStore(stateDir) {
92050
92891
  });
92051
92892
  const deadline = Date.now() + Number(process.env.KEYSYNC_BIFROST_CONFIG_STORE_UNLOCK_TIMEOUT_MS ?? DEFAULT_BIFROST_CONFIG_STORE_UNLOCK_TIMEOUT_MS);
92052
92893
  for (const file of BRIDGE_SQLITE_CONFIG_FILES) {
92053
- const filePath = join6(dir, file);
92894
+ const filePath = join7(dir, file);
92054
92895
  let attempts = 0;
92055
92896
  for (; ; ) {
92056
92897
  try {
@@ -92557,11 +93398,11 @@ function isManuallyMarkedUninstalled(discovery) {
92557
93398
  return discovery?.detection_mode === "manual" && discovery.detected === false;
92558
93399
  }
92559
93400
  function discoveryStabilityPath(stateDir) {
92560
- return join6(stateDir, "app-discovery-state.json");
93401
+ return join7(stateDir, "app-discovery-state.json");
92561
93402
  }
92562
93403
  async function readDiscoveryStability(stateDir) {
92563
93404
  try {
92564
- const parsed = JSON.parse(await readFile6(discoveryStabilityPath(stateDir), "utf8"));
93405
+ const parsed = JSON.parse(await readFile7(discoveryStabilityPath(stateDir), "utf8"));
92565
93406
  return { version: 1, apps: parsed.apps && typeof parsed.apps === "object" ? parsed.apps : {} };
92566
93407
  } catch {
92567
93408
  return { version: 1, apps: {} };
@@ -92570,10 +93411,10 @@ async function readDiscoveryStability(stateDir) {
92570
93411
  async function writeDiscoveryStability(stateDir, state) {
92571
93412
  await mkdir3(stateDir, { recursive: true });
92572
93413
  const path = discoveryStabilityPath(stateDir);
92573
- const tmpPath = `${path}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
93414
+ const tmpPath = `${path}.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`;
92574
93415
  await writeFile3(tmpPath, `${JSON.stringify(state, null, 2)}
92575
93416
  `, { mode: 384 });
92576
- await rename2(tmpPath, path);
93417
+ await rename3(tmpPath, path);
92577
93418
  await chmod2(path, 384).catch(() => void 0);
92578
93419
  }
92579
93420
  function stabilizedAutomaticDetection(app, current, state) {
@@ -92608,8 +93449,8 @@ var appDiscoveryQueues = /* @__PURE__ */ new Map();
92608
93449
  async function withAppDiscoveryQueue(key, task) {
92609
93450
  const previous = appDiscoveryQueues.get(key) ?? Promise.resolve();
92610
93451
  let release;
92611
- const current = new Promise((resolve2) => {
92612
- release = resolve2;
93452
+ const current = new Promise((resolve3) => {
93453
+ release = resolve3;
92613
93454
  });
92614
93455
  const tail = previous.catch(() => void 0).then(() => current);
92615
93456
  appDiscoveryQueues.set(key, tail);
@@ -92749,7 +93590,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92749
93590
  vaultUnlockError = error;
92750
93591
  }
92751
93592
  }
92752
- const outputDir = join6(stateDir, "applied-configs");
93593
+ const outputDir = join7(stateDir, "applied-configs");
92753
93594
  const mode = applyModeFromEnv();
92754
93595
  const localKey = await localApplyFingerprintKey(stateDir);
92755
93596
  const writeStrategies = snapshotWriteStrategies(snapshot);
@@ -92803,6 +93644,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92803
93644
  return leftRank - rightRank;
92804
93645
  });
92805
93646
  for (const app of orderedApps) {
93647
+ const forceThisApp = Boolean(options.force) || (options.forceAppNames?.includes(app.app_name) ?? false);
92806
93648
  if (!app.source) {
92807
93649
  await clearLocalApplyRecord(stateDir, deviceId, app.id, mode);
92808
93650
  appResults.push({ app_name: app.app_name, status: "skipped", message: "APP_NOT_CONFIGURED" });
@@ -92810,13 +93652,13 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92810
93652
  }
92811
93653
  const provider = resolveProviderForApp(snapshot, app.id);
92812
93654
  const sourceIdentityFingerprint = hmacHex(localKey, sourceIdentityInput(snapshot, app, provider));
92813
- const attemptId = `att_${randomBytes(8).toString("hex")}`;
93655
+ const attemptId = `att_${randomBytes2(8).toString("hex")}`;
92814
93656
  const previousRecord = await readLocalApplyRecord(stateDir, deviceId, app.id, mode);
92815
93657
  const currentInstallationStateFingerprint = installationStateFingerprint(app);
92816
93658
  const installationStateChanged = previousRecord?.installation_state_fingerprint !== void 0 && previousRecord.installation_state_fingerprint !== currentInstallationStateFingerprint;
92817
93659
  const adoptionSource = app.source.source_type === "provider" ? app.source : null;
92818
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));
92819
- if (!options.force && adoptionAppliesToCurrentRevision) {
93661
+ if (!forceThisApp && adoptionAppliesToCurrentRevision) {
92820
93662
  if (adoptionSource.adoption_status === "failed") {
92821
93663
  appResults.push({ app_name: app.app_name, status: "skipped", message: "LOCAL_ADOPTION_FAILED_REQUIRES_RETRY" });
92822
93664
  continue;
@@ -92868,56 +93710,59 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92868
93710
  await reportAdoptionFailure("LOCAL_ADOPTION_IDENTITY_MISMATCH");
92869
93711
  continue;
92870
93712
  }
92871
- let adoptionBaselineRecorded = false;
92872
- try {
92873
- const decryptedProvider = await resolveLocalDecryptedProvider(provider, vaultMaterial);
92874
- const routePlan = await buildAppRoutePlan({ app, provider: decryptedProvider, snapshot, stateDir, bridgeBaseUrl: bridgeRuntimeBaseUrl, providerStrategyMap, writeStrategies });
92875
- if (routePlan.route_kind !== "direct") throw new Error("LOCAL_ADOPTION_BRIDGE_UNSUPPORTED");
92876
- const sourceFingerprint = hmacHex(
92877
- localKey,
92878
- buildSourceFingerprintInput(
92879
- app.app_name,
92880
- app.id,
92881
- stableAppSourceForFingerprint(app.source),
92882
- decryptedProvider,
92883
- routePlan.endpoint,
92884
- localKey
92885
- )
92886
- );
92887
- if (!options.localModelConfigProbe) clearLocalModelConfigProbeCache();
92888
- const finalProbe = await adoptionProbe(app.app_name);
92889
- const finalOauth = finalProbe.credentials.find((credential) => credential.kind === "oauth");
92890
- if (finalProbe.state !== "local_oauth" || finalProbe.in_effect !== "oauth" || finalOauth?.import_source_key !== providerSourceKey) {
92891
- await reportAdoptionFailure("LOCAL_ADOPTION_LOCAL_STATE_CHANGED");
92892
- 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);
92893
93763
  }
92894
- await recordApplyAttempt({
92895
- stateDir,
92896
- deviceId,
92897
- app,
92898
- mode,
92899
- snapshotId: snapshot.snapshot_id,
92900
- sourceIdentityFingerprint,
92901
- attemptFingerprint: sourceFingerprint,
92902
- sourceFingerprint,
92903
- status: "success",
92904
- providerId: provider.id,
92905
- selectedEndpoint: routePlan.endpoint,
92906
- adoptedLocal: true
92907
- });
92908
- adoptionBaselineRecorded = true;
92909
- await client.post(`/devices/${deviceId}/apps/${app.id}/adoption-report`, {
92910
- binding_revision: adoptionSource.binding_revision,
92911
- desired_state_revision: app.desired_state_revision,
92912
- status: "confirmed"
92913
- });
92914
- appResults.push({ app_name: app.app_name, status: "success", message: "ADOPTED_LOCAL_CONFIG" });
92915
- } catch (error) {
92916
- if (adoptionBaselineRecorded) throw error;
92917
- const errorCode = error instanceof Error && /^LOCAL_ADOPTION_[A-Z0-9_]+$/.test(error.message) ? error.message : "LOCAL_ADOPTION_ROUTE_INVALID";
92918
- await reportAdoptionFailure(errorCode);
93764
+ continue;
92919
93765
  }
92920
- continue;
92921
93766
  }
92922
93767
  if (app.latest_discovery?.detection_mode === "manual" && app.latest_discovery.detected === false) {
92923
93768
  retainedBridgeAppIds.delete(app.id);
@@ -92925,11 +93770,11 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92925
93770
  continue;
92926
93771
  }
92927
93772
  const previousFailureWasRecoverableBridge = activeBridgeAppIds.has(app.id) && (isRecoverableBridgeFailureCode(previousRecord?.failure_code) || previousRecord?.version !== 2 || previousRecord.desired_state_revision !== (app.desired_state_revision ?? null));
92928
- 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) {
92929
93774
  appResults.push({ app_name: app.app_name, status: "skipped", message: "SKIPPED_FAILED_FINGERPRINT" });
92930
93775
  continue;
92931
93776
  }
92932
- 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)) {
92933
93778
  if (!activeBridgeAppIds.has(app.id)) retainedBridgeAppIds.delete(app.id);
92934
93779
  appResults.push({ app_name: app.app_name, status: "skipped", message: "SKIPPED_SAME_FINGERPRINT" });
92935
93780
  continue;
@@ -92995,7 +93840,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
92995
93840
  }
92996
93841
  }
92997
93842
  ]);
92998
- await recordDaemonAutoApplyEvent({
93843
+ await recordLocalApplyEvent({
92999
93844
  stateDir,
93000
93845
  trigger: options.trigger,
93001
93846
  session: activeSession,
@@ -93074,7 +93919,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93074
93919
  }
93075
93920
  }
93076
93921
  ]);
93077
- await recordDaemonAutoApplyEvent({
93922
+ await recordLocalApplyEvent({
93078
93923
  stateDir,
93079
93924
  trigger: options.trigger,
93080
93925
  session: activeSession,
@@ -93153,7 +93998,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93153
93998
  }
93154
93999
  }
93155
94000
  ]);
93156
- await recordDaemonAutoApplyEvent({
94001
+ await recordLocalApplyEvent({
93157
94002
  stateDir,
93158
94003
  trigger: options.trigger,
93159
94004
  session: activeSession,
@@ -93204,7 +94049,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93204
94049
  route_kind: routePlan.route_kind,
93205
94050
  strategy_id: routePlan.strategy_id
93206
94051
  };
93207
- 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)) {
93208
94053
  const sourceIdentityChanged = previousRecord.source_identity_fingerprint !== sourceIdentityFingerprint;
93209
94054
  const desiredStateRevisionChanged = previousRecord.desired_state_revision !== (app.desired_state_revision ?? null);
93210
94055
  if (routePlan.route_kind === "bridge" && sourceIdentityChanged || desiredStateRevisionChanged) {
@@ -93230,7 +94075,8 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93230
94075
  status: "success",
93231
94076
  providerId: provider.id,
93232
94077
  selectedEndpoint: fingerprintEndpoint,
93233
- bridgeGeneration: routePlan.route_kind === "bridge" ? existingBridgeGenerationHash : null
94078
+ bridgeGeneration: routePlan.route_kind === "bridge" ? existingBridgeGenerationHash : null,
94079
+ writtenAccountKey: writtenAccountKeyFor(provider)
93234
94080
  });
93235
94081
  await postReportDurably(client, stateDir, `/devices/${deviceId}/apps/${app.id}/apply-report`, {
93236
94082
  snapshot_id: snapshot.snapshot_id,
@@ -93253,7 +94099,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93253
94099
  selected_model: providerSelectedModel(applyProvider, fingerprintEndpoint),
93254
94100
  reported_at: (/* @__PURE__ */ new Date()).toISOString()
93255
94101
  });
93256
- await recordDaemonAutoApplyEvent({
94102
+ await recordLocalApplyEvent({
93257
94103
  stateDir,
93258
94104
  trigger: options.trigger,
93259
94105
  session: activeSession,
@@ -93275,6 +94121,55 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93275
94121
  continue;
93276
94122
  }
93277
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
+ }
93278
94173
  if (routePlan.route_kind === "bridge") {
93279
94174
  retainedBridgeAppIds.add(app.id);
93280
94175
  const preparedBridgeConfig = await buildBridgeConfigForPlan(stateDir, routePlan, localKey, retainedBridgeAppIds);
@@ -93310,7 +94205,8 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93310
94205
  status: "success",
93311
94206
  providerId: provider.id,
93312
94207
  selectedEndpoint: fingerprintEndpoint,
93313
- bridgeGeneration: routePlan.route_kind === "bridge" ? existingBridgeGenerationHash : null
94208
+ bridgeGeneration: routePlan.route_kind === "bridge" ? existingBridgeGenerationHash : null,
94209
+ writtenAccountKey: writtenAccountKeyFor(provider)
93314
94210
  });
93315
94211
  await postReportDurably(client, stateDir, `/devices/${deviceId}/apps/${app.id}/apply-report`, {
93316
94212
  snapshot_id: snapshot.snapshot_id,
@@ -93333,7 +94229,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93333
94229
  selected_model: result.selected_model,
93334
94230
  reported_at: (/* @__PURE__ */ new Date()).toISOString()
93335
94231
  });
93336
- await recordDaemonAutoApplyEvent({
94232
+ await recordLocalApplyEvent({
93337
94233
  stateDir,
93338
94234
  trigger: options.trigger,
93339
94235
  session: activeSession,
@@ -93428,7 +94324,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93428
94324
  }
93429
94325
  }
93430
94326
  ]);
93431
- await recordDaemonAutoApplyEvent({
94327
+ await recordLocalApplyEvent({
93432
94328
  stateDir,
93433
94329
  trigger: options.trigger,
93434
94330
  session: activeSession,
@@ -93452,7 +94348,7 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93452
94348
  const removedAppIds = [...new Set(
93453
94349
  (manifestBeforeFinalReconcile?.routes ?? []).map((route) => route.app_id).filter((appId) => !retainedBridgeAppIds.has(appId))
93454
94350
  )].sort();
93455
- const reconciliationAttemptId = `att_${randomBytes(8).toString("hex")}`;
94351
+ const reconciliationAttemptId = `att_${randomBytes2(8).toString("hex")}`;
93456
94352
  try {
93457
94353
  const reconciliation = await buildBridgeReconciliationPlan(stateDir, retainedBridgeAppIds, bridgeRuntimeBaseUrl);
93458
94354
  await applyBridgeConfigPlan(stateDir, bridgeRuntimeBaseUrl, reconciliation);
@@ -93588,44 +94484,19 @@ async function syncAndApply(session, deviceId, stateDir = defaultStateDir(), opt
93588
94484
  };
93589
94485
  }
93590
94486
 
93591
- // apps/cli/package.json
93592
- var package_default = {
93593
- name: "@keysync/cli",
93594
- version: "0.1.132",
93595
- license: "MIT",
93596
- private: true,
93597
- type: "module",
93598
- bin: {
93599
- keysync: "./src/index.ts"
93600
- },
93601
- scripts: {
93602
- cli: "tsx src/index.ts",
93603
- build: "tsc -p tsconfig.json",
93604
- typecheck: "tsc -p tsconfig.json --noEmit",
93605
- test: "vitest run --exclude=dist/** --passWithNoTests"
93606
- },
93607
- dependencies: {
93608
- "@keysync/api-client": "workspace:*",
93609
- "@keysync/client-core": "workspace:*"
93610
- },
93611
- devDependencies: {
93612
- "@types/node": "^24.10.1",
93613
- tsx: "^4.20.6",
93614
- typescript: "^5.9.3",
93615
- vitest: "^4.1.11"
93616
- }
93617
- };
94487
+ // keysync-cli-version:keysync-cli-package.json
94488
+ var keysync_cli_package_default = { name: "@keysync/cli", version: "0.1.134" };
93618
94489
 
93619
94490
  // apps/cli/src/version.ts
93620
94491
  var overrideVersion = process.env.KEYSYNC_ALLOW_CLIENT_VERSION_OVERRIDE === "1" ? process.env.KEYSYNC_CLIENT_VERSION_OVERRIDE?.trim() : "";
93621
- var version = overrideVersion || package_default.version;
94492
+ var version = overrideVersion || keysync_cli_package_default.version;
93622
94493
 
93623
94494
  // apps/cli/src/dsh-host.ts
93624
94495
  import { createServer as createServer2, createConnection } from "node:net";
93625
94496
  import { createHash as createHash6, randomUUID } from "node:crypto";
93626
94497
  import { spawn as spawn2 } from "node:child_process";
93627
- 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";
93628
- 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";
93629
94500
  import { fileURLToPath } from "node:url";
93630
94501
  var import_managed_dsh = __toESM(require_managed_dsh(), 1);
93631
94502
  var import_remote_dsh = __toESM(require_remote_dsh(), 1);
@@ -93681,7 +94552,7 @@ function safeUserId(userId) {
93681
94552
  return sanitizeHostForPath(userId);
93682
94553
  }
93683
94554
  function dshAccountRoot(session, stateRoot = defaultStateRoot()) {
93684
- return join7(
94555
+ return join8(
93685
94556
  stateRoot,
93686
94557
  "managed-agents",
93687
94558
  "deepseek-harness",
@@ -93702,7 +94573,7 @@ function dshDiscoveryHintsJson(accountRoot, input = process.env.KEYSYNC_DISCOVER
93702
94573
  ...hints,
93703
94574
  DeepSeekHarness: {
93704
94575
  ...existingDsh && typeof existingDsh === "object" && !Array.isArray(existingDsh) ? existingDsh : {},
93705
- marker_path: join7(accountRoot, "state.json")
94576
+ marker_path: join8(accountRoot, "state.json")
93706
94577
  }
93707
94578
  });
93708
94579
  }
@@ -93711,10 +94582,10 @@ function dshHostSocketPath(stateRoot = defaultStateRoot()) {
93711
94582
  const suffix2 = createHash6("sha256").update(stateRoot).digest("hex").slice(0, 20);
93712
94583
  return `\\\\.\\pipe\\keysync-dsh-host-${suffix2}`;
93713
94584
  }
93714
- const localPath = join7(stateRoot, "dsh-host.sock");
94585
+ const localPath = join8(stateRoot, "dsh-host.sock");
93715
94586
  if (Buffer.byteLength(localPath) <= 96) return localPath;
93716
94587
  const suffix = createHash6("sha256").update(stateRoot).digest("hex").slice(0, 20);
93717
- return join7("/tmp", `ksdh-${process.getuid?.() ?? "user"}-${suffix}`, "s");
94588
+ return join8("/tmp", `ksdh-${process.getuid?.() ?? "user"}-${suffix}`, "s");
93718
94589
  }
93719
94590
  function bundledAssetDir() {
93720
94591
  if (process.env.KEYSYNC_DSH_ASSET_DIR?.trim()) return process.env.KEYSYNC_DSH_ASSET_DIR;
@@ -93730,7 +94601,7 @@ async function atomicPrivateWrite(filePath, contents) {
93730
94601
  try {
93731
94602
  await writeFile4(temporary, contents, { mode: 384 });
93732
94603
  if (process.platform !== "win32") await chmod3(temporary, 384);
93733
- await rename3(temporary, filePath);
94604
+ await rename4(temporary, filePath);
93734
94605
  } catch (error) {
93735
94606
  await rm4(temporary, { force: true }).catch(() => void 0);
93736
94607
  throw error;
@@ -93753,7 +94624,7 @@ function validLegacyOwner(value) {
93753
94624
  }
93754
94625
  async function pathExists(filePath) {
93755
94626
  try {
93756
- await stat4(filePath);
94627
+ await stat5(filePath);
93757
94628
  return true;
93758
94629
  } catch (error) {
93759
94630
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return false;
@@ -93761,8 +94632,8 @@ async function pathExists(filePath) {
93761
94632
  }
93762
94633
  }
93763
94634
  async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93764
- const legacyRoot = join7(stateRoot, "managed-agents", "deepseek-harness");
93765
- const ownerPath = join7(legacyRoot, ".legacy-owner.json");
94635
+ const legacyRoot = join8(stateRoot, "managed-agents", "deepseek-harness");
94636
+ const ownerPath = join8(legacyRoot, ".legacy-owner.json");
93766
94637
  const completionName = ".legacy-migration-complete.json";
93767
94638
  const excluded = /* @__PURE__ */ new Set(["accounts", ".legacy-owner.json"]);
93768
94639
  let entries;
@@ -93779,7 +94650,7 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93779
94650
  await ensurePrivateDirectory(legacyRoot);
93780
94651
  let recordedOwner = null;
93781
94652
  try {
93782
- const parsed = JSON.parse(await readFile7(ownerPath, "utf8"));
94653
+ const parsed = JSON.parse(await readFile8(ownerPath, "utf8"));
93783
94654
  if (!validLegacyOwner(parsed)) throw new Error("invalid owner");
93784
94655
  recordedOwner = parsed;
93785
94656
  } catch (error) {
@@ -93789,11 +94660,11 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93789
94660
  }
93790
94661
  if (!recordedOwner && preferredServerKey && !currentServerIsPreferred) return;
93791
94662
  if (recordedOwner && !sameLegacyOwner(recordedOwner, expectedOwner)) {
93792
- const recordedAccountRoot = join7(legacyRoot, "accounts", recordedOwner.server_key, recordedOwner.user_id);
93793
- 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));
93794
94665
  const preferredServerCorrectingNonPreferredClaim = currentServerIsPreferred && recordedOwner.server_key !== preferredServerKey;
93795
94666
  if (recordedMigrationComplete && !preferredServerCorrectingNonPreferredClaim) return;
93796
- const ownerMetadata = await stat4(ownerPath);
94667
+ const ownerMetadata = await stat5(ownerPath);
93797
94668
  if (!preferredServerCorrectingNonPreferredClaim && Date.now() - ownerMetadata.mtimeMs < LEGACY_DSH_CLAIM_STALE_MS) return;
93798
94669
  await rm4(ownerPath, { force: true });
93799
94670
  recordedOwner = null;
@@ -93810,13 +94681,13 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93810
94681
  }
93811
94682
  if (process.platform !== "win32") await chmod3(ownerPath, 384);
93812
94683
  try {
93813
- await stat4(join7(accountRoot, completionName));
94684
+ await stat5(join8(accountRoot, completionName));
93814
94685
  return;
93815
94686
  } catch (error) {
93816
94687
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
93817
94688
  }
93818
- if (await pathExists(join7(accountRoot, "state.json"))) {
93819
- 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)}
93820
94691
  `);
93821
94692
  return;
93822
94693
  }
@@ -93826,7 +94697,7 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93826
94697
  await ensurePrivateDirectory(stagingRoot);
93827
94698
  try {
93828
94699
  for (const entry of entries) {
93829
- await cp(join7(legacyRoot, entry.name), join7(stagingRoot, entry.name), {
94700
+ await cp(join8(legacyRoot, entry.name), join8(stagingRoot, entry.name), {
93830
94701
  recursive: true,
93831
94702
  errorOnExist: true,
93832
94703
  force: false,
@@ -93834,23 +94705,23 @@ async function claimLegacyDshRoot(session, stateRoot, accountRoot) {
93834
94705
  });
93835
94706
  }
93836
94707
  for (const retainedName of ["host-desired.json", "remote-dsh.sealed"]) {
93837
- const retainedPath = join7(accountRoot, retainedName);
94708
+ const retainedPath = join8(accountRoot, retainedName);
93838
94709
  if (await pathExists(retainedPath)) {
93839
- await cp(retainedPath, join7(stagingRoot, retainedName), { force: true, preserveTimestamps: true });
94710
+ await cp(retainedPath, join8(stagingRoot, retainedName), { force: true, preserveTimestamps: true });
93840
94711
  }
93841
94712
  }
93842
- 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)}
93843
94714
  `);
93844
- if (await pathExists(accountRoot)) await rename3(accountRoot, displacedRoot);
93845
- await rename3(stagingRoot, accountRoot);
94715
+ if (await pathExists(accountRoot)) await rename4(accountRoot, displacedRoot);
94716
+ await rename4(stagingRoot, accountRoot);
93846
94717
  } catch (error) {
93847
94718
  await rm4(stagingRoot, { recursive: true, force: true }).catch(() => void 0);
93848
94719
  if (!await pathExists(accountRoot) && await pathExists(displacedRoot)) {
93849
- await rename3(displacedRoot, accountRoot).catch(() => void 0);
94720
+ await rename4(displacedRoot, accountRoot).catch(() => void 0);
93850
94721
  }
93851
94722
  if (claim.claim_id) {
93852
94723
  try {
93853
- const currentOwner = JSON.parse(await readFile7(ownerPath, "utf8"));
94724
+ const currentOwner = JSON.parse(await readFile8(ownerPath, "utf8"));
93854
94725
  if (currentOwner.claim_id === claim.claim_id) await rm4(ownerPath, { force: true });
93855
94726
  } catch {
93856
94727
  }
@@ -93869,7 +94740,7 @@ function normalizeDesiredState(value) {
93869
94740
  }
93870
94741
  async function readDesiredState(filePath) {
93871
94742
  try {
93872
- return normalizeDesiredState(JSON.parse(await readFile7(filePath, "utf8")));
94743
+ return normalizeDesiredState(JSON.parse(await readFile8(filePath, "utf8")));
93873
94744
  } catch {
93874
94745
  return normalizeDesiredState(null);
93875
94746
  }
@@ -93891,10 +94762,10 @@ function publicRpcError(error) {
93891
94762
  async function createDshHostController(options) {
93892
94763
  const stateRoot = options.stateRoot ?? defaultStateRoot();
93893
94764
  const accountRoot = dshAccountRoot(options.session, stateRoot);
93894
- const dshHome = join7(accountRoot, "instances", "default", "home");
93895
- const desiredPath = join7(accountRoot, "host-desired.json");
93896
- const secretPath = join7(accountRoot, "remote-dsh.sealed");
93897
- 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");
93898
94769
  const assetDir = options.assetDir ?? bundledAssetDir();
93899
94770
  await ensurePrivateDirectory(stateRoot);
93900
94771
  await claimLegacyDshRoot(options.session, stateRoot, accountRoot);
@@ -93917,8 +94788,8 @@ async function createDshHostController(options) {
93917
94788
  spawn: spawn2,
93918
94789
  fetchImpl: globalThis.fetch,
93919
94790
  windowsZipExtractor: process.env.KEYSYNC_DSH_WINDOWS_ZIP_EXTRACTOR === "tar" ? "tar" : void 0,
93920
- packageLockPath: join7(assetDir, "managed-dsh-official-0.1.2-alpha.2-package-lock.json"),
93921
- 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")
93922
94793
  });
93923
94794
  let releaseCatalog = releaseCatalogFromStatus(managed.status(), true);
93924
94795
  const apiRequest = options.apiRequest ?? (async (method, pathname, body, requestOptions) => withSessionRefresh(
@@ -93926,6 +94797,31 @@ async function createDshHostController(options) {
93926
94797
  (activeSession) => clientFromSession(activeSession).request(method, pathname, body, { timeoutMs: requestOptions?.timeoutMs }),
93927
94798
  options.stateDir
93928
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
+ };
93929
94825
  const remoteFactory = options.remoteFactory ?? import_remote_dsh.default.createRemoteDshTunnelManager;
93930
94826
  const remote = remoteFactory({
93931
94827
  secretPath,
@@ -93986,6 +94882,7 @@ async function createDshHostController(options) {
93986
94882
  event: "release_refresh_failed",
93987
94883
  code: error instanceof ApiError ? error.code : "UNKNOWN_ERROR"
93988
94884
  }).catch(() => void 0);
94885
+ await reportHostFailure("release_refresh", error, "release_refresh");
93989
94886
  }
93990
94887
  };
93991
94888
  const refreshReleaseCatalog = async () => {
@@ -94042,7 +94939,7 @@ async function createDshHostController(options) {
94042
94939
  return { opened: false, error: { code: "data_directory_not_installed", message: "Install DeepSeek Harness before opening its data directory." } };
94043
94940
  }
94044
94941
  try {
94045
- await stat4(dshHome);
94942
+ await stat5(dshHome);
94046
94943
  } catch (error) {
94047
94944
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
94048
94945
  return { opened: false, error: { code: "data_directory_missing", message: "The DeepSeek Harness data directory does not exist." } };
@@ -94124,6 +95021,7 @@ async function createDshHostController(options) {
94124
95021
  } catch (error) {
94125
95022
  nextRecoveryAt = now + recoveryDelayMs;
94126
95023
  await appendHostLog({ source: "managed_dsh", event: "recovery_failed", retry_in_ms: recoveryDelayMs, error: publicRpcError(error) });
95024
+ await reportHostFailure("managed_recovery", error, "background_recovery");
94127
95025
  recoveryDelayMs = Math.min(3e4, recoveryDelayMs * 2);
94128
95026
  }
94129
95027
  }
@@ -94138,6 +95036,7 @@ async function createDshHostController(options) {
94138
95036
  } catch (error) {
94139
95037
  nextRemoteRecoveryAt = now + remoteRecoveryDelayMs;
94140
95038
  await appendHostLog({ source: "remote_dsh", event: "recovery_failed", retry_in_ms: remoteRecoveryDelayMs, error: publicRpcError(error) });
95039
+ await reportHostFailure("remote_recovery", error, "background_recovery");
94141
95040
  remoteRecoveryDelayMs = Math.min(3e4, remoteRecoveryDelayMs * 2);
94142
95041
  }
94143
95042
  }
@@ -94151,6 +95050,7 @@ async function createDshHostController(options) {
94151
95050
  await managed.start();
94152
95051
  } catch (error) {
94153
95052
  await appendHostLog({ source: "managed_dsh", event: "restore_failed", error: publicRpcError(error) });
95053
+ await reportHostFailure("managed_restore", error, "startup_restore");
94154
95054
  }
94155
95055
  }
94156
95056
  if (desired.remote === "enabled") {
@@ -94158,6 +95058,7 @@ async function createDshHostController(options) {
94158
95058
  await remote.start(await resolveDeviceId());
94159
95059
  } catch (error) {
94160
95060
  await appendHostLog({ source: "remote_dsh", event: "restore_failed", error: publicRpcError(error) });
95061
+ await reportHostFailure("remote_restore", error, "startup_restore");
94161
95062
  }
94162
95063
  }
94163
95064
  });
@@ -94237,18 +95138,18 @@ async function startDshHostRpcServer(socketPath, dispatch, requestTimeoutMs = RP
94237
95138
  });
94238
95139
  socket.on("error", () => void 0);
94239
95140
  });
94240
- await new Promise((resolve2, reject) => {
95141
+ await new Promise((resolve3, reject) => {
94241
95142
  server.once("error", reject);
94242
95143
  server.listen(socketPath, () => {
94243
95144
  server.off("error", reject);
94244
- resolve2();
95145
+ resolve3();
94245
95146
  });
94246
95147
  });
94247
95148
  if (process.platform !== "win32") await chmod3(socketPath, 384);
94248
95149
  return {
94249
95150
  server,
94250
95151
  async close() {
94251
- const closed = new Promise((resolve2) => server.close(() => resolve2()));
95152
+ const closed = new Promise((resolve3) => server.close(() => resolve3()));
94252
95153
  await closed;
94253
95154
  if (process.platform !== "win32") await rm4(socketPath, { force: true });
94254
95155
  }
@@ -94256,7 +95157,7 @@ async function startDshHostRpcServer(socketPath, dispatch, requestTimeoutMs = RP
94256
95157
  }
94257
95158
  async function callDshHostRpc(socketPath, method, params, timeoutMs = RPC_TIMEOUT_MS) {
94258
95159
  const id = `rpc_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
94259
- return new Promise((resolve2, reject) => {
95160
+ return new Promise((resolve3, reject) => {
94260
95161
  const socket = createConnection(socketPath);
94261
95162
  socket.setEncoding("utf8");
94262
95163
  let input = "";
@@ -94267,7 +95168,7 @@ async function callDshHostRpc(socketPath, method, params, timeoutMs = RPC_TIMEOU
94267
95168
  settled = true;
94268
95169
  clearTimeout(timer);
94269
95170
  if (error) reject(error);
94270
- else resolve2(result);
95171
+ else resolve3(result);
94271
95172
  };
94272
95173
  socket.once("connect", () => socket.write(`${JSON.stringify({ version: HOST_PROTOCOL_VERSION, id, method, params })}
94273
95174
  `));
@@ -94353,13 +95254,13 @@ function isProcessRunning(pid, options = {}) {
94353
95254
 
94354
95255
  // apps/cli/src/systemd-service.ts
94355
95256
  import { execFile as execFile3 } from "node:child_process";
94356
- 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";
94357
95258
  import { homedir as homedir7 } from "node:os";
94358
- import { dirname as dirname5, join as join8 } from "node:path";
95259
+ import { dirname as dirname5, join as join9 } from "node:path";
94359
95260
  import { promisify as promisify2 } from "node:util";
94360
95261
  var execFileAsync2 = promisify2(execFile3);
94361
95262
  function systemdUserUnitPath(home = homedir7()) {
94362
- return join8(home, ".config", "systemd", "user", "keysync.service");
95263
+ return join9(home, ".config", "systemd", "user", "keysync.service");
94363
95264
  }
94364
95265
  function systemdArgument(value) {
94365
95266
  if (!value || /[\r\n\0]/.test(value)) throw new Error("Invalid systemd command path.");
@@ -94412,7 +95313,7 @@ async function uninstallSystemdUserService(home = homedir7()) {
94412
95313
  await execFileAsync2("systemctl", ["--user", "disable", "--now", "keysync.service"]).catch(() => void 0);
94413
95314
  let removed = false;
94414
95315
  try {
94415
- await readFile8(unitPath);
95316
+ await readFile9(unitPath);
94416
95317
  await rm5(unitPath, { force: true });
94417
95318
  removed = true;
94418
95319
  } catch {
@@ -94429,9 +95330,9 @@ async function systemdUserServiceStatus() {
94429
95330
  // apps/cli/src/index.ts
94430
95331
  function loadEnvFile() {
94431
95332
  const clientRoot = fileURLToPath2(new URL("../../..", import.meta.url));
94432
- const parentRoot = join9(clientRoot, "..");
94433
- const envPaths = [join9(clientRoot, ".env")];
94434
- 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"));
94435
95336
  for (const envPath2 of envPaths) {
94436
95337
  if (!existsSync4(envPath2)) continue;
94437
95338
  for (const rawLine of readFileSync2(envPath2, "utf8").split(/\r?\n/)) {
@@ -94449,6 +95350,25 @@ function loadEnvFile() {
94449
95350
  loadEnvFile();
94450
95351
  var defaultBaseUrl = process.env.KEYSYNC_API_URL || process.env.KEYSYNC_DEFAULT_API_URL || "";
94451
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();
94452
95372
  var heartbeatTimeoutMs = Number(process.env.KEYSYNC_HEARTBEAT_TIMEOUT_MS ?? "5000");
94453
95373
  var execFileAsync3 = promisify3(execFile4);
94454
95374
  function warnIfPasswordCouldBeStronger(password) {
@@ -94513,6 +95433,7 @@ Options:
94513
95433
  --install-dir DIR Override Linux CLI install directory for update testing or custom installs
94514
95434
  --vault-password PASSWORD Unlock Provider secrets for sync/daemon. Can also use KEYSYNC_VAULT_PASSWORD
94515
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
94516
95437
  --no-start-daemon Login/register without automatically starting the background daemon
94517
95438
  --daemon-interval-seconds N Background daemon interval after login/register. Defaults to 30
94518
95439
  --json Print machine-readable DSH status
@@ -94577,6 +95498,32 @@ async function promptForMissingRegisterInput(input) {
94577
95498
  function printJson(value) {
94578
95499
  console.log(JSON.stringify(value, null, 2));
94579
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
+ }
94580
95527
  function summarizeImportPlan(plan) {
94581
95528
  console.log(`CC Switch config: ${plan.config_dir}`);
94582
95529
  console.log(`Database: ${plan.db_path}`);
@@ -94696,11 +95643,11 @@ function selectCliArchive(feed) {
94696
95643
  }
94697
95644
  function safeArchiveFileName(fileUrl) {
94698
95645
  const parsed = new URL(fileUrl, "https://updates.local/");
94699
- const name = decodeURIComponent(basename3(parsed.pathname));
95646
+ const name = decodeURIComponent(basename2(parsed.pathname));
94700
95647
  return name.replace(/[^a-zA-Z0-9._ -]/g, "_") || `keysync-cli-linux-${cliUpdateArch()}.tar.gz`;
94701
95648
  }
94702
95649
  async function sha512Base64(filePath) {
94703
- return createHash7("sha512").update(await readFile9(filePath)).digest("base64");
95650
+ return createHash7("sha512").update(await readFile10(filePath)).digest("base64");
94704
95651
  }
94705
95652
  async function downloadUpdateFile(url, destination) {
94706
95653
  const response = await fetch(url, { cache: "no-store" });
@@ -94709,7 +95656,7 @@ async function downloadUpdateFile(url, destination) {
94709
95656
  await writeFile6(destination, Buffer.from(await response.arrayBuffer()));
94710
95657
  }
94711
95658
  async function runLocalCommand(command, args) {
94712
- await new Promise((resolve2, reject) => {
95659
+ await new Promise((resolve3, reject) => {
94713
95660
  const child = spawn3(command, args, { stdio: ["ignore", "pipe", "pipe"] });
94714
95661
  let stderr = "";
94715
95662
  child.stderr?.on("data", (chunk) => {
@@ -94717,7 +95664,7 @@ async function runLocalCommand(command, args) {
94717
95664
  });
94718
95665
  child.once("error", reject);
94719
95666
  child.once("exit", (code, signal) => {
94720
- if (code === 0) resolve2();
95667
+ if (code === 0) resolve3();
94721
95668
  else reject(new Error(`${command} exited with code=${code ?? "null"} signal=${signal ?? "null"}${stderr ? `: ${stderr.trim()}` : ""}`));
94722
95669
  });
94723
95670
  });
@@ -94729,25 +95676,25 @@ async function findExtractedCliDir(extractDir) {
94729
95676
  const entries = await readdir3(extractDir, { withFileTypes: true });
94730
95677
  for (const entry of entries) {
94731
95678
  if (!entry.isDirectory()) continue;
94732
- const candidate = join9(extractDir, entry.name);
94733
- 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;
94734
95681
  }
94735
95682
  throw new Error("CLI archive did not contain keysync-cli.mjs, keysync, and bridge/bifrost-http");
94736
95683
  }
94737
95684
  async function installCliArchive(archivePath, installDir) {
94738
- const extractDir = await mkdtemp(join9(tmpdir(), "keysync-cli-update-extract-"));
95685
+ const extractDir = await mkdtemp(join10(tmpdir(), "keysync-cli-update-extract-"));
94739
95686
  try {
94740
95687
  await runLocalCommand("tar", ["-xzf", archivePath, "-C", extractDir]);
94741
95688
  const sourceDir = await findExtractedCliDir(extractDir);
94742
95689
  await mkdir6(installDir, { recursive: true });
94743
- await copyFile3(join9(sourceDir, "keysync-cli.mjs"), join9(installDir, "keysync-cli.mjs"));
94744
- await copyFile3(join9(sourceDir, "keysync"), join9(installDir, "keysync"));
94745
- await rm6(join9(installDir, "bridge"), { recursive: true, force: true });
94746
- await cp2(join9(sourceDir, "bridge"), join9(installDir, "bridge"), { recursive: true });
94747
- if (existsSync4(join9(sourceDir, "README.txt"))) await copyFile3(join9(sourceDir, "README.txt"), join9(installDir, "README.txt"));
94748
- await chmod5(join9(installDir, "keysync-cli.mjs"), 493);
94749
- await chmod5(join9(installDir, "keysync"), 493);
94750
- 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);
94751
95698
  } finally {
94752
95699
  await rm6(extractDir, { recursive: true, force: true });
94753
95700
  }
@@ -94774,14 +95721,14 @@ async function updateLinuxCli() {
94774
95721
  }
94775
95722
  console.log(`KeySync CLI ${feed.version} is available (current ${version}).`);
94776
95723
  if (process.argv.includes("--check")) return;
94777
- const workDir = await mkdtemp(join9(tmpdir(), "keysync-cli-update-"));
94778
- 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));
94779
95726
  try {
94780
95727
  await downloadUpdateFile(resolveUpdateArtifactUrl(feedBaseUrl, archive.url), archivePath);
94781
95728
  const checksum = await sha512Base64(archivePath);
94782
95729
  if (checksum !== archive.sha512) throw new Error("Downloaded CLI package checksum did not match update feed");
94783
95730
  const installDir = cliInstallDir();
94784
- const before = existsSync4(installDir) ? await stat5(installDir) : null;
95731
+ const before = existsSync4(installDir) ? await stat6(installDir) : null;
94785
95732
  if (before && !before.isDirectory()) throw new Error(`Install target is not a directory: ${installDir}`);
94786
95733
  await installCliArchive(archivePath, installDir);
94787
95734
  console.log(`Installed KeySync CLI ${feed.version} to ${installDir}.`);
@@ -94815,11 +95762,25 @@ async function requireSession(stateDir) {
94815
95762
  if (device?.device_id && !device.user_id) {
94816
95763
  await writeDeviceState({ ...device, user_id: session.user_id }, stateDir);
94817
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);
94818
95769
  const accountRoot = dshAccountRoot(session, defaultStateRoot());
94819
- process.env.KEYSYNC_DSH_HOME = join9(accountRoot, "instances", "default", "home");
95770
+ process.env.KEYSYNC_DSH_HOME = join10(accountRoot, "instances", "default", "home");
94820
95771
  process.env.KEYSYNC_DISCOVERY_HINTS_JSON = dshDiscoveryHintsJson(accountRoot);
94821
95772
  return session;
94822
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
+ }
94823
95784
  async function requireDeviceId(stateDir) {
94824
95785
  const device = await readDeviceState(stateDir);
94825
95786
  if (!device?.device_id) throw new Error("Device is not bound.");
@@ -94831,16 +95792,18 @@ async function installIdHashForSession(session, stateDir) {
94831
95792
  }
94832
95793
  async function ensureDaemonDeviceId(session, stateDir) {
94833
95794
  const registration = await detectDeviceRegistrationInfo();
94834
- const install_id_hash = await installIdHashForSession(session, stateDir);
94835
95795
  const device = await readDeviceState(stateDir, { legacyStateDirs: legacyStateDirs(session.baseUrl) });
94836
95796
  if (device?.device_id) {
94837
95797
  try {
94838
95798
  const current = await withSessionRefresh(session, async (activeSession) => {
94839
95799
  const activeClient = clientFromSession(activeSession);
94840
95800
  const current2 = await activeClient.get(`/devices/${device.device_id}`);
94841
- if (current2.install_id_hash && current2.install_id_hash !== install_id_hash) {
94842
- throw new Error("Local device is linked to a different install identity.");
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 };
94843
95805
  }
95806
+ const install_id_hash2 = match ? installIdHashFor(match.install.install_id) : await installIdHashForSession(session, stateDir);
94844
95807
  const payload = current2.install_id_hash ? {
94845
95808
  name: current2.name,
94846
95809
  hostname: current2.hostname ?? null
@@ -94848,21 +95811,34 @@ async function ensureDaemonDeviceId(session, stateDir) {
94848
95811
  name: registration.name,
94849
95812
  hostname: registration.hostname
94850
95813
  };
94851
- return activeClient.post("/devices/register", {
95814
+ const result2 = await activeClient.post("/devices/register", {
94852
95815
  ...payload,
94853
95816
  platform: registration.platform,
94854
95817
  client_version: version,
94855
- install_id_hash,
95818
+ install_id_hash: install_id_hash2,
94856
95819
  ...current2.install_id_hash ? {} : { existing_device_id: device.device_id }
94857
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 } : {}
94858
95828
  }, stateDir);
94859
- await writeDeviceState({ device_id: current.id, user_id: session.user_id, ...current.claim_ticket ? { claim_ticket: current.claim_ticket } : {} }, stateDir);
94860
95829
  return current.id;
94861
95830
  } catch (error) {
94862
95831
  if (!(error instanceof ApiError) || error.status !== 404 && error.status !== 410) throw error;
94863
95832
  await clearDeviceState(stateDir);
94864
95833
  }
94865
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);
94866
95842
  const result = await withSessionRefresh(
94867
95843
  session,
94868
95844
  async (activeSession) => clientFromSession(activeSession).post("/devices/register", {
@@ -94874,7 +95850,7 @@ async function ensureDaemonDeviceId(session, stateDir) {
94874
95850
  }),
94875
95851
  stateDir
94876
95852
  );
94877
- await writeDeviceState({ device_id: result.id, user_id: session.user_id, ...result.claim_ticket ? { claim_ticket: result.claim_ticket } : {} }, 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);
94878
95854
  return result.id;
94879
95855
  }
94880
95856
  function parseDaemonLock(raw) {
@@ -94886,6 +95862,8 @@ function parseDaemonLock(raw) {
94886
95862
  if (typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
94887
95863
  return {
94888
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 } : {},
94889
95867
  ...typeof parsed.version === "string" ? { version: parsed.version } : {},
94890
95868
  ...typeof parsed.command === "string" ? { command: parsed.command } : {},
94891
95869
  ...typeof parsed.process_identity === "string" ? { process_identity: parsed.process_identity } : {},
@@ -94896,19 +95874,20 @@ function parseDaemonLock(raw) {
94896
95874
  }
94897
95875
  }
94898
95876
  async function acquireDaemonLock(stateDir) {
94899
- const lockPath = join9(stateDir, "daemon.pid");
94900
- const reclaimPath = join9(stateDir, "daemon.pid.reclaim");
95877
+ const lockPath = join10(stateDir, "daemon.pid");
95878
+ const reclaimPath = join10(stateDir, "daemon.pid.reclaim");
94901
95879
  await mkdir6(stateDir, { recursive: true, mode: 448 });
94902
95880
  if (process.platform !== "win32") await chmod5(stateDir, 448);
94903
95881
  const contents = JSON.stringify({
94904
95882
  pid: process.pid,
95883
+ run_id: `run_${randomBytes3(16).toString("hex")}`,
94905
95884
  version,
94906
95885
  started_at: (/* @__PURE__ */ new Date()).toISOString(),
94907
95886
  command: redactedProcessCommand(process.argv),
94908
95887
  process_identity: processIdentity(process.pid),
94909
95888
  vault_unlocked: Boolean(vaultPasswordFromInput())
94910
95889
  }, null, 2);
94911
- const candidatePath = join9(stateDir, `.daemon.pid.${process.pid}.${Date.now()}`);
95890
+ const candidatePath = join10(stateDir, `.daemon.pid.${process.pid}.${Date.now()}`);
94912
95891
  await writeFile6(candidatePath, contents, { mode: 384, flag: "wx" });
94913
95892
  if (process.platform !== "win32") await chmod5(candidatePath, 384);
94914
95893
  try {
@@ -94916,7 +95895,7 @@ async function acquireDaemonLock(stateDir) {
94916
95895
  let reclaimOwner = null;
94917
95896
  let reclaimPresent = false;
94918
95897
  try {
94919
- reclaimOwner = parseDaemonLock(await readFile9(reclaimPath, "utf8"));
95898
+ reclaimOwner = parseDaemonLock(await readFile10(reclaimPath, "utf8"));
94920
95899
  reclaimPresent = true;
94921
95900
  } catch {
94922
95901
  }
@@ -94935,7 +95914,7 @@ async function acquireDaemonLock(stateDir) {
94935
95914
  }
94936
95915
  let existingLock = null;
94937
95916
  try {
94938
- existingLock = parseDaemonLock(await readFile9(lockPath, "utf8"));
95917
+ existingLock = parseDaemonLock(await readFile10(lockPath, "utf8"));
94939
95918
  } catch {
94940
95919
  }
94941
95920
  const identityMatches = !existingLock?.process_identity || processIdentity(existingLock.pid) === existingLock.process_identity;
@@ -94968,32 +95947,42 @@ async function acquireDaemonLock(stateDir) {
94968
95947
  async function releaseDaemonLock(stateDir, expectedPid) {
94969
95948
  if (expectedPid !== void 0) {
94970
95949
  try {
94971
- const current = parseDaemonLock(await readFile9(join9(stateDir, "daemon.pid"), "utf8"));
95950
+ const current = parseDaemonLock(await readFile10(join10(stateDir, "daemon.pid"), "utf8"));
94972
95951
  if (current?.pid !== expectedPid) return;
94973
95952
  } catch {
94974
95953
  return;
94975
95954
  }
94976
95955
  }
94977
- await rm6(join9(stateDir, "daemon.pid"), { force: true });
94978
- 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 });
94979
95958
  }
94980
95959
  function daemonLogPath(stateDir) {
94981
- return join9(stateDir, "daemon.log");
95960
+ return join10(stateDir, "daemon.log");
94982
95961
  }
94983
95962
  async function activeDaemonLock(stateDir) {
94984
95963
  try {
94985
- const lock = parseDaemonLock(await readFile9(join9(stateDir, "daemon.pid"), "utf8"));
95964
+ const lock = parseDaemonLock(await readFile10(join10(stateDir, "daemon.pid"), "utf8"));
94986
95965
  const identityMatches = !lock?.process_identity || processIdentity(lock.pid) === lock.process_identity;
94987
95966
  if (lock && isProcessRunning(lock.pid) && identityMatches) return lock;
94988
95967
  } catch {
94989
95968
  }
94990
95969
  return null;
94991
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
+ }
94992
95981
  async function writeDaemonStatus(stateDir, patch) {
94993
- const statusPath = join9(stateDir, "daemon-status.json");
95982
+ const statusPath = join10(stateDir, "daemon-status.json");
94994
95983
  let current = { running: false };
94995
95984
  try {
94996
- current = JSON.parse(await readFile9(statusPath, "utf8"));
95985
+ current = JSON.parse(await readFile10(statusPath, "utf8"));
94997
95986
  } catch {
94998
95987
  }
94999
95988
  await mkdir6(stateDir, { recursive: true, mode: 448 });
@@ -95005,12 +95994,20 @@ function formatError(error) {
95005
95994
  if (error instanceof ApiError) return `${error.code} (${error.status}): ${error.message}`;
95006
95995
  return error instanceof Error ? error.message : String(error);
95007
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
+ }
95008
96005
  function isFatalDaemonError(error) {
95009
96006
  if (!(error instanceof ApiError)) return false;
95010
96007
  return error.status === 401 || error.status === 403 || error.status === 410 || error.code === "DEVICE_REMOVED";
95011
96008
  }
95012
96009
  function sleep3(ms) {
95013
- return new Promise((resolve2) => setTimeout(resolve2, ms));
96010
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
95014
96011
  }
95015
96012
  async function stopDaemon(stateDir) {
95016
96013
  const lock = await activeDaemonLock(stateDir);
@@ -95088,12 +96085,15 @@ async function startDetachedDaemon(stateDir, intervalSeconds, vaultPassword) {
95088
96085
  }
95089
96086
  async function startDaemonAfterAuth(session, stateDir, vaultPassword) {
95090
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);
95091
96091
  console.log(`Device ready: ${deviceId}`);
95092
96092
  const intervalSeconds = Number(arg("--daemon-interval-seconds", "30"));
95093
96093
  const result = await startDetachedDaemon(stateDir, Number.isFinite(intervalSeconds) ? intervalSeconds : 30, vaultPassword);
95094
96094
  console.log(result === "started" ? "Daemon started" : "Daemon already running");
95095
96095
  }
95096
- async function waitForDshHost(socketPath, timeoutMs = 8e3) {
96096
+ async function waitForDshHost(socketPath, timeoutMs = 3e4) {
95097
96097
  const deadline = Date.now() + timeoutMs;
95098
96098
  let lastError = null;
95099
96099
  while (Date.now() < deadline) {
@@ -95133,7 +96133,7 @@ async function ensureDshHostForCommand(stateDir, session) {
95133
96133
  const socketPath = dshHostSocketPath(defaultStateRoot());
95134
96134
  const matchesSession = (status) => {
95135
96135
  const expectedRoot = dshAccountRoot(session, defaultStateRoot());
95136
- 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;
95137
96137
  };
95138
96138
  let active = null;
95139
96139
  try {
@@ -95292,8 +96292,14 @@ async function main() {
95292
96292
  const client = new KeySyncApiClient({ baseUrl });
95293
96293
  const previousSession = await readSession(stateDir);
95294
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);
95295
96299
  const session = { baseUrl, access_token: result.access_token, refresh_token: result.refresh_token, user_id: result.user.id };
95296
- 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");
95297
96303
  await stopActiveDshHost();
95298
96304
  await stopDaemon(stateDir);
95299
96305
  if (previousSession?.user_id && previousSession.user_id !== result.user.id) await clearDeviceState(stateDir);
@@ -95326,7 +96332,11 @@ async function main() {
95326
96332
  const previousSession = await readSession(stateDir);
95327
96333
  if (previousSession?.user_id && result.user?.id && previousSession.user_id !== result.user.id) await clearDeviceState(stateDir);
95328
96334
  const session = { baseUrl, access_token: result.access_token, refresh_token: result.refresh_token, user_id: result.user?.id };
95329
- 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");
95330
96340
  await stopActiveDshHost();
95331
96341
  await stopDaemon(stateDir);
95332
96342
  await writeSession(session, stateDir);
@@ -95334,7 +96344,7 @@ async function main() {
95334
96344
  if (!process.argv.includes("--no-start-daemon")) await startDaemonAfterAuth(session, stateDir, vaultPassword);
95335
96345
  return;
95336
96346
  }
95337
- await rm6(join9(stateDir, "session.json"), { force: true });
96347
+ await rm6(join10(stateDir, "session.json"), { force: true });
95338
96348
  await clearDeviceState(stateDir);
95339
96349
  console.log(`Account created. Check ${result.email} for a verification code, then run keysync login after verifying your email.`);
95340
96350
  return;
@@ -95371,7 +96381,7 @@ async function main() {
95371
96381
  } catch {
95372
96382
  }
95373
96383
  }
95374
- await rm6(join9(stateDir, "session.json"), { force: true });
96384
+ await rm6(join10(stateDir, "session.json"), { force: true });
95375
96385
  if (daemonWasRunning) console.log("Daemon stopped");
95376
96386
  console.log("Logged out");
95377
96387
  return;
@@ -95388,24 +96398,27 @@ async function main() {
95388
96398
  const stateDir = commandStateDir();
95389
96399
  const session = await requireSession(stateDir);
95390
96400
  const client = clientFromSession(session);
95391
- const install_id_hash = await installIdHashForSession(session, stateDir);
95392
96401
  const existingDeviceId = arg("--device-id");
95393
96402
  if (existingDeviceId) {
95394
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);
95395
96407
  const result2 = await client.post("/devices/register", {
95396
96408
  name: current.name,
95397
96409
  platform: current.platform,
95398
96410
  hostname: current.hostname ?? null,
95399
96411
  client_version: version,
95400
- install_id_hash,
96412
+ install_id_hash: install_id_hash2,
95401
96413
  existing_device_id: existingDeviceId
95402
96414
  });
95403
- await writeDeviceState({ device_id: result2.id, user_id: session.user_id, ...result2.claim_ticket ? { claim_ticket: result2.claim_ticket } : {} }, 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);
95404
96416
  console.log(`Device bound: ${result2.id}`);
95405
96417
  return;
95406
96418
  }
95407
96419
  const registration = await detectDeviceRegistrationInfo();
95408
96420
  const name = arg("--name", registration.name);
96421
+ const install_id_hash = await installIdHashForSession(session, stateDir);
95409
96422
  const result = await client.post("/devices/register", {
95410
96423
  name,
95411
96424
  platform: registration.platform,
@@ -95413,11 +96426,15 @@ async function main() {
95413
96426
  client_version: version,
95414
96427
  install_id_hash
95415
96428
  });
95416
- await writeDeviceState({ device_id: result.id, user_id: session.user_id, ...result.claim_ticket ? { claim_ticket: result.claim_ticket } : {} }, 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);
95417
96430
  console.log(`Device bound: ${result.id}`);
95418
96431
  return;
95419
96432
  }
95420
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
+ }
95421
96438
  const stateDir = commandStateDir();
95422
96439
  const vaultPassword = requiredVaultPassword("sync");
95423
96440
  const session = await requireSession(stateDir);
@@ -95425,7 +96442,12 @@ async function main() {
95425
96442
  const asJson = hasFlag2("--json");
95426
96443
  const envTrigger = process.env.KEYSYNC_APPLY_TRIGGER;
95427
96444
  const trigger = envTrigger === "manual_sync" || envTrigger === "manual_reapply" || envTrigger === "daemon_auto" || envTrigger === "cli_sync" ? envTrigger : "cli_sync";
95428
- 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
+ });
95429
96451
  if (asJson) {
95430
96452
  printJson({
95431
96453
  device_id: deviceId,
@@ -95511,8 +96533,8 @@ async function main() {
95511
96533
  const baseUrl = session?.baseUrl ?? selectedBaseUrl;
95512
96534
  if (!baseUrl) throw new Error("Missing server URL. Set KEYSYNC_API_URL or KEYSYNC_DEFAULT_API_URL in environment.");
95513
96535
  const client = new KeySyncApiClient({ baseUrl, accessToken: session?.access_token });
95514
- const [health, server_version] = await Promise.all([client.get("/health"), client.get("/version")]);
95515
- 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));
95516
96538
  return;
95517
96539
  }
95518
96540
  if (command === "self-update" || command === "update") {
@@ -95597,6 +96619,15 @@ async function main() {
95597
96619
  if (command === "daemon") {
95598
96620
  const stateDir = commandStateDir();
95599
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
+ }
95600
96631
  if (!await acquireDaemonLock(stateDir)) {
95601
96632
  console.log("Daemon already running");
95602
96633
  return;
@@ -95610,6 +96641,9 @@ async function main() {
95610
96641
  try {
95611
96642
  const result = await runDaemonTick(session, stateDir, currentDeviceId);
95612
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);
95613
96647
  await writeDaemonStatus(stateDir, { running: true, last_tick_completed_at: (/* @__PURE__ */ new Date()).toISOString(), last_error: null });
95614
96648
  if (result.mode === "discovery") console.log("Daemon tick complete. Discovery updated.");
95615
96649
  else console.log(`Daemon tick complete. Snapshot: ${result.snapshotId}`);
@@ -95620,6 +96654,7 @@ async function main() {
95620
96654
  await clearDeviceState(stateDir);
95621
96655
  }
95622
96656
  await writeDaemonStatus(stateDir, { running: true, last_error: message });
96657
+ await reportCliFailure(error, { failure_stage: "daemon_tick", device_id: currentDeviceId, retryable: !isFatalDaemonError(error) });
95623
96658
  console.error(`Daemon tick failed: ${message}`);
95624
96659
  if (isFatalDaemonError(error)) throw error;
95625
96660
  }
@@ -95645,8 +96680,8 @@ async function main() {
95645
96680
  let dshRpc = null;
95646
96681
  let stopped = false;
95647
96682
  let stopNow = () => void 0;
95648
- const stopPromise = new Promise((resolve2) => {
95649
- stopNow = resolve2;
96683
+ const stopPromise = new Promise((resolve3) => {
96684
+ stopNow = resolve3;
95650
96685
  });
95651
96686
  process.once("SIGINT", () => {
95652
96687
  stopped = true;
@@ -95684,6 +96719,7 @@ async function main() {
95684
96719
  await activeHost.reconcile();
95685
96720
  } catch (error) {
95686
96721
  await writeDaemonStatus(stateDir, { running: true, last_error: `DSH recovery check failed: ${formatError(error)}` });
96722
+ await reportCliFailure(error, { failure_stage: "dsh_recovery", retryable: true });
95687
96723
  }
95688
96724
  }
95689
96725
  })();
@@ -95694,27 +96730,33 @@ async function main() {
95694
96730
  } finally {
95695
96731
  stopped = true;
95696
96732
  stopNow();
95697
- 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
+ });
95698
96736
  await dshRecoveryLoop;
95699
96737
  let cleanupError;
95700
96738
  try {
95701
96739
  await dshHost?.shutdown();
95702
96740
  } catch (error) {
96741
+ await reportCliFailure(error, { failure_stage: "daemon_shutdown_dsh" });
95703
96742
  cleanupError = error;
95704
96743
  }
95705
96744
  try {
95706
96745
  await dshRpc?.close();
95707
96746
  } catch (error) {
96747
+ await reportCliFailure(error, { failure_stage: "daemon_shutdown_rpc" });
95708
96748
  cleanupError ??= error;
95709
96749
  }
95710
96750
  try {
95711
96751
  await writeDaemonStatus(stateDir, { running: false, stopped_at: (/* @__PURE__ */ new Date()).toISOString() });
95712
96752
  } catch (error) {
96753
+ await reportCliFailure(error, { failure_stage: "daemon_shutdown_status_write" });
95713
96754
  cleanupError ??= error;
95714
96755
  }
95715
96756
  try {
95716
96757
  await releaseDaemonLock(stateDir, process.pid);
95717
96758
  } catch (error) {
96759
+ await reportCliFailure(error, { failure_stage: "daemon_shutdown_lock_release" });
95718
96760
  cleanupError ??= error;
95719
96761
  }
95720
96762
  if (cleanupError) throw cleanupError;
@@ -95724,7 +96766,27 @@ async function main() {
95724
96766
  }
95725
96767
  throw new Error(`Unknown command: ${command}`);
95726
96768
  }
95727
- 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" });
95728
96790
  console.error(error instanceof ApiError ? formatError(error) : error instanceof Error ? error.message : error);
95729
96791
  process.exitCode = 1;
95730
96792
  });