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