@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.
- package/README.md +1 -1
- package/dist/keysync-cli.mjs +1580 -518
- package/package.json +1 -1
package/dist/keysync-cli.mjs
CHANGED
|
@@ -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
|
|
17214
|
-
if (!
|
|
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
|
|
17267
|
-
if (!
|
|
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((
|
|
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((
|
|
17903
|
+
return new Promise((resolve3) => {
|
|
17719
17904
|
const timer = setTimeout(() => {
|
|
17720
17905
|
if (authenticatedUrlWaiter === finish) authenticatedUrlWaiter = null;
|
|
17721
|
-
|
|
17906
|
+
resolve3(null);
|
|
17722
17907
|
}, timeoutMs);
|
|
17723
17908
|
timer.unref?.();
|
|
17724
17909
|
const finish = (url) => {
|
|
17725
17910
|
clearTimeout(timer);
|
|
17726
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
18762
|
+
const available = await new Promise((resolve3) => {
|
|
18578
18763
|
const server = net.createServer();
|
|
18579
18764
|
server.unref();
|
|
18580
|
-
server.once("error", () =>
|
|
18581
|
-
server.listen({ host: "127.0.0.1", port, exclusive: true }, () => server.close(() =>
|
|
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
|
|
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(
|
|
24211
|
-
|
|
24395
|
+
return value instanceof P ? value : new P(function(resolve3) {
|
|
24396
|
+
resolve3(value);
|
|
24212
24397
|
});
|
|
24213
24398
|
}
|
|
24214
|
-
return new (P || (P = Promise))(function(
|
|
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 ?
|
|
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(
|
|
24422
|
-
v = o[n](v), settle(
|
|
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(
|
|
24611
|
+
function settle(resolve3, reject, d, v) {
|
|
24427
24612
|
Promise.resolve(v).then(function(v2) {
|
|
24428
|
-
|
|
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(
|
|
31598
|
-
|
|
31782
|
+
return value instanceof P ? value : new P(function(resolve3) {
|
|
31783
|
+
resolve3(value);
|
|
31599
31784
|
});
|
|
31600
31785
|
}
|
|
31601
|
-
return new (P || (P = Promise))(function(
|
|
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 ?
|
|
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(
|
|
31796
|
-
v = o[n](v), settle(
|
|
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(
|
|
31985
|
+
function settle(resolve3, reject, d, v) {
|
|
31801
31986
|
Promise.resolve(v).then(function(v2) {
|
|
31802
|
-
|
|
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,
|
|
48372
|
+
fs.stat(value.path, function(err, stat7) {
|
|
48188
48373
|
if (err) {
|
|
48189
48374
|
callback(err);
|
|
48190
48375
|
return;
|
|
48191
48376
|
}
|
|
48192
|
-
var fileSize =
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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(
|
|
48707
|
-
|
|
48891
|
+
return value instanceof P ? value : new P(function(resolve3) {
|
|
48892
|
+
resolve3(value);
|
|
48708
48893
|
});
|
|
48709
48894
|
}
|
|
48710
|
-
return new (P || (P = Promise))(function(
|
|
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 ?
|
|
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(
|
|
49318
|
-
return useNativeURL ? new URL2(
|
|
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(
|
|
51058
|
+
function settle(resolve3, reject, response) {
|
|
50874
51059
|
const validateStatus = response.config.validateStatus;
|
|
50875
51060
|
if (!response.status || !validateStatus || validateStatus(response.status)) {
|
|
50876
|
-
|
|
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
|
|
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
|
-
|
|
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 -
|
|
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((
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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((
|
|
53244
|
-
settle(
|
|
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(
|
|
53656
|
-
resolvePromise =
|
|
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((
|
|
53670
|
-
token.subscribe(
|
|
53671
|
-
_resolve =
|
|
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((
|
|
53902
|
-
setTimeout(
|
|
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((
|
|
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
|
|
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((
|
|
54172
|
-
setTimeout(
|
|
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
|
|
72855
|
-
if (!isNaN(
|
|
72856
|
-
date = new Date(
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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((
|
|
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((
|
|
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
|
-
|
|
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
|
|
74861
|
-
import { basename as
|
|
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
|
-
|
|
74921
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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://
|
|
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\
|
|
84572
|
-
"\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\
|
|
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\
|
|
84780
|
-
"\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\
|
|
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\
|
|
84805
|
-
"\u56DE\u5230\u8FD9\u91CC\u9009\u62E9\u201C\u6211\u5DF2\u7ECF\u6709\u201D\uFF0C\u4ECE\u672C\
|
|
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((
|
|
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
|
|
87740
|
-
|
|
87741
|
-
|
|
87742
|
-
|
|
87743
|
-
|
|
87744
|
-
|
|
87745
|
-
|
|
87746
|
-
|
|
87747
|
-
|
|
87748
|
-
|
|
87749
|
-
|
|
87750
|
-
|
|
87751
|
-
|
|
87752
|
-
|
|
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 {
|
|
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 {
|
|
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,
|
|
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"
|
|
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"
|
|
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: "
|
|
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,
|
|
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
|
|
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) :
|
|
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
|
-
|
|
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
|
|
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 =
|
|
89230
|
-
const temporaryPath = `${sessionPath}.${process.pid}.${
|
|
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
|
|
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
|
|
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(
|
|
90074
|
+
await writeFile3(join7(stateDir, "device.json"), JSON.stringify(device, null, 2));
|
|
89268
90075
|
}
|
|
89269
90076
|
async function clearDeviceState(stateDir = defaultStateDir()) {
|
|
89270
|
-
await rm3(
|
|
90077
|
+
await rm3(join7(stateDir, "device.json"), { force: true });
|
|
89271
90078
|
}
|
|
89272
90079
|
function localDeviceSettingsPath(stateDir = defaultStateDir()) {
|
|
89273
|
-
return
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
90239
|
+
return join7(stateDir, "local-apply");
|
|
89428
90240
|
}
|
|
89429
90241
|
function localApplyEventPath(stateDir) {
|
|
89430
|
-
return
|
|
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
|
|
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
|
|
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_${
|
|
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
|
|
90537
|
+
return join7(localApplyDir(stateDir), "fingerprint.key");
|
|
89726
90538
|
}
|
|
89727
90539
|
function localApplyRecordPath(stateDir, deviceId, appId, mode) {
|
|
89728
|
-
return
|
|
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
|
|
90545
|
+
return Buffer.from((await readFile7(path, "utf8")).trim(), "base64");
|
|
89734
90546
|
} catch {
|
|
89735
|
-
const key =
|
|
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
|
|
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(
|
|
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
|
|
89894
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
91024
|
+
return join7(stateDir, "bridge", "bifrost");
|
|
90184
91025
|
}
|
|
90185
91026
|
function bridgeBifrostConfigPath(stateDir) {
|
|
90186
|
-
return
|
|
91027
|
+
return join7(bridgeDir(stateDir), "config.json");
|
|
90187
91028
|
}
|
|
90188
91029
|
function bridgeRouteManifestPath(stateDir) {
|
|
90189
|
-
return
|
|
91030
|
+
return join7(bridgeDir(stateDir), "keysync-routes.json");
|
|
90190
91031
|
}
|
|
90191
91032
|
function bridgeRuntimePath(stateDir) {
|
|
90192
|
-
return
|
|
91033
|
+
return join7(bridgeDir(stateDir), "runtime.json");
|
|
90193
91034
|
}
|
|
90194
91035
|
function bridgeStartupStdoutPath(stateDir) {
|
|
90195
|
-
return
|
|
91036
|
+
return join7(bridgeDir(stateDir), "bridge-startup.stdout.log");
|
|
90196
91037
|
}
|
|
90197
91038
|
function bridgeStartupStderrPath(stateDir) {
|
|
90198
|
-
return
|
|
91039
|
+
return join7(bridgeDir(stateDir), "bridge-startup.stderr.log");
|
|
90199
91040
|
}
|
|
90200
91041
|
function bridgeTokenPath(stateDir) {
|
|
90201
|
-
return
|
|
91042
|
+
return join7(bridgeDir(stateDir), "token.json");
|
|
90202
91043
|
}
|
|
90203
91044
|
function bridgeGenerationPath(stateDir) {
|
|
90204
|
-
return
|
|
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
|
|
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()}.${
|
|
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
|
|
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
|
|
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-${
|
|
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(
|
|
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
|
|
91420
|
+
return join7(stateDir, "sync-apply.lock");
|
|
90580
91421
|
}
|
|
90581
91422
|
async function readSyncApplyLockOwner(lockDir) {
|
|
90582
91423
|
try {
|
|
90583
|
-
const parsed = JSON.parse(await
|
|
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(
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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((
|
|
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
|
|
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 ?
|
|
91353
|
-
|
|
91354
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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(() =>
|
|
92008
|
-
else
|
|
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 =
|
|
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
|
|
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
|
|
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}.${
|
|
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
|
|
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((
|
|
92612
|
-
release =
|
|
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 =
|
|
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_${
|
|
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 (!
|
|
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
|
-
|
|
92872
|
-
|
|
92873
|
-
|
|
92874
|
-
|
|
92875
|
-
|
|
92876
|
-
|
|
92877
|
-
|
|
92878
|
-
|
|
92879
|
-
|
|
92880
|
-
|
|
92881
|
-
|
|
92882
|
-
|
|
92883
|
-
|
|
92884
|
-
|
|
92885
|
-
|
|
92886
|
-
|
|
92887
|
-
|
|
92888
|
-
|
|
92889
|
-
|
|
92890
|
-
|
|
92891
|
-
|
|
92892
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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_${
|
|
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
|
-
//
|
|
93592
|
-
var
|
|
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 ||
|
|
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
|
|
93628
|
-
import { dirname as dirname4, join as
|
|
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
|
|
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:
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
93765
|
-
const ownerPath =
|
|
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
|
|
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 =
|
|
93793
|
-
const recordedMigrationComplete = await pathExists(
|
|
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
|
|
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
|
|
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(
|
|
93819
|
-
await atomicPrivateWrite(
|
|
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(
|
|
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 =
|
|
94708
|
+
const retainedPath = join8(accountRoot, retainedName);
|
|
93838
94709
|
if (await pathExists(retainedPath)) {
|
|
93839
|
-
await cp(retainedPath,
|
|
94710
|
+
await cp(retainedPath, join8(stagingRoot, retainedName), { force: true, preserveTimestamps: true });
|
|
93840
94711
|
}
|
|
93841
94712
|
}
|
|
93842
|
-
await atomicPrivateWrite(
|
|
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
|
|
93845
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
93895
|
-
const desiredPath =
|
|
93896
|
-
const secretPath =
|
|
93897
|
-
const hostLogPath =
|
|
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:
|
|
93921
|
-
pnpmLockPath:
|
|
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
|
|
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((
|
|
95141
|
+
await new Promise((resolve3, reject) => {
|
|
94241
95142
|
server.once("error", reject);
|
|
94242
95143
|
server.listen(socketPath, () => {
|
|
94243
95144
|
server.off("error", reject);
|
|
94244
|
-
|
|
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((
|
|
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((
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
94433
|
-
const envPaths = [
|
|
94434
|
-
if (process.env.KEYSYNC_UNIFIED_WORKSPACE === "1") envPaths.push(
|
|
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(
|
|
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
|
|
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((
|
|
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)
|
|
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 =
|
|
94733
|
-
if (existsSync4(
|
|
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(
|
|
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
|
|
94744
|
-
await
|
|
94745
|
-
await rm6(
|
|
94746
|
-
await cp2(
|
|
94747
|
-
if (existsSync4(
|
|
94748
|
-
await chmod5(
|
|
94749
|
-
await chmod5(
|
|
94750
|
-
await chmod5(
|
|
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(
|
|
94778
|
-
const archivePath =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
94842
|
-
|
|
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
|
-
|
|
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 =
|
|
94900
|
-
const reclaimPath =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
94978
|
-
if (expectedPid === void 0) await rm6(
|
|
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
|
|
95960
|
+
return join10(stateDir, "daemon.log");
|
|
94982
95961
|
}
|
|
94983
95962
|
async function activeDaemonLock(stateDir) {
|
|
94984
95963
|
try {
|
|
94985
|
-
const lock = parseDaemonLock(await
|
|
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 =
|
|
95982
|
+
const statusPath = join10(stateDir, "daemon-status.json");
|
|
94994
95983
|
let current = { running: false };
|
|
94995
95984
|
try {
|
|
94996
|
-
current = JSON.parse(await
|
|
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((
|
|
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 =
|
|
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 ===
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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, {
|
|
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((
|
|
95649
|
-
stopNow =
|
|
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(() =>
|
|
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
|
-
|
|
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
|
});
|