opencode-multidevice-sync 0.1.0
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/LICENSE +21 -0
- package/NOTICE +15 -0
- package/README.md +111 -0
- package/README.ru.md +57 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +460 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/render.d.ts +45 -0
- package/dist/cli/render.d.ts.map +1 -0
- package/dist/cli/render.js +167 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/theme.d.ts +45 -0
- package/dist/cli/theme.d.ts.map +1 -0
- package/dist/cli/theme.js +88 -0
- package/dist/cli/theme.js.map +1 -0
- package/dist/core/backup.d.ts +13 -0
- package/dist/core/backup.d.ts.map +1 -0
- package/dist/core/backup.js +102 -0
- package/dist/core/backup.js.map +1 -0
- package/dist/core/fsx.d.ts +81 -0
- package/dist/core/fsx.d.ts.map +1 -0
- package/dist/core/fsx.js +353 -0
- package/dist/core/fsx.js.map +1 -0
- package/dist/core/git.d.ts +62 -0
- package/dist/core/git.d.ts.map +1 -0
- package/dist/core/git.js +162 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/guard.d.ts +5 -0
- package/dist/core/guard.d.ts.map +1 -0
- package/dist/core/guard.js +97 -0
- package/dist/core/guard.js.map +1 -0
- package/dist/core/health.d.ts +18 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +88 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/host.d.ts +20 -0
- package/dist/core/host.d.ts.map +1 -0
- package/dist/core/host.js +50 -0
- package/dist/core/host.js.map +1 -0
- package/dist/core/index.d.ts +18 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +18 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/jsonc.d.ts +20 -0
- package/dist/core/jsonc.d.ts.map +1 -0
- package/dist/core/jsonc.js +115 -0
- package/dist/core/jsonc.js.map +1 -0
- package/dist/core/lock.d.ts +14 -0
- package/dist/core/lock.d.ts.map +1 -0
- package/dist/core/lock.js +111 -0
- package/dist/core/lock.js.map +1 -0
- package/dist/core/overrides.d.ts +43 -0
- package/dist/core/overrides.d.ts.map +1 -0
- package/dist/core/overrides.js +169 -0
- package/dist/core/overrides.js.map +1 -0
- package/dist/core/paths.d.ts +36 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +55 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/preview.d.ts +3 -0
- package/dist/core/preview.d.ts.map +1 -0
- package/dist/core/preview.js +56 -0
- package/dist/core/preview.js.map +1 -0
- package/dist/core/repo.d.ts +57 -0
- package/dist/core/repo.d.ts.map +1 -0
- package/dist/core/repo.js +196 -0
- package/dist/core/repo.js.map +1 -0
- package/dist/core/reporter.d.ts +50 -0
- package/dist/core/reporter.d.ts.map +1 -0
- package/dist/core/reporter.js +63 -0
- package/dist/core/reporter.js.map +1 -0
- package/dist/core/sessions.d.ts +83 -0
- package/dist/core/sessions.d.ts.map +1 -0
- package/dist/core/sessions.js +508 -0
- package/dist/core/sessions.js.map +1 -0
- package/dist/core/settings.d.ts +68 -0
- package/dist/core/settings.d.ts.map +1 -0
- package/dist/core/settings.js +126 -0
- package/dist/core/settings.js.map +1 -0
- package/dist/core/setup.d.ts +48 -0
- package/dist/core/setup.d.ts.map +1 -0
- package/dist/core/setup.js +186 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/sqlite.d.ts +37 -0
- package/dist/core/sqlite.d.ts.map +1 -0
- package/dist/core/sqlite.js +108 -0
- package/dist/core/sqlite.js.map +1 -0
- package/dist/core/stage.d.ts +44 -0
- package/dist/core/stage.d.ts.map +1 -0
- package/dist/core/stage.js +304 -0
- package/dist/core/stage.js.map +1 -0
- package/dist/core/sync.d.ts +52 -0
- package/dist/core/sync.d.ts.map +1 -0
- package/dist/core/sync.js +423 -0
- package/dist/core/sync.js.map +1 -0
- package/dist/plugin/index.d.ts +29 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +88 -0
- package/dist/plugin/index.js.map +1 -0
- package/docs/MIGRATION.md +29 -0
- package/docs/ROADMAP.md +29 -0
- package/docs/SAFETY.md +31 -0
- package/examples/home-laptop.jsonc +31 -0
- package/examples/opencode-sync.overrides.jsonc +4 -0
- package/examples/work-laptop.jsonc +26 -0
- package/package.json +81 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { git } from "./git.js";
|
|
6
|
+
export function assertPrivateRemote(remote, settings) {
|
|
7
|
+
if (!settings.sessions.enabled && !settings.includeCredentials)
|
|
8
|
+
return;
|
|
9
|
+
if (path.isAbsolute(remote) || remote.startsWith("file://"))
|
|
10
|
+
return; // Local test/development repository.
|
|
11
|
+
const match = remote.match(/^(?:https:\/\/github\.com\/|git@github\.com:)([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?$/);
|
|
12
|
+
if (!match)
|
|
13
|
+
throw new Error("Sensitive sync requires a verified private GitHub repo (or local development remote)");
|
|
14
|
+
const check = spawnSync("gh", ["repo", "view", `${match[1]}/${match[2]}`, "--json", "visibility", "--jq", ".visibility"], { encoding: "utf8", timeout: 20000, windowsHide: true });
|
|
15
|
+
if (check.status !== 0 || check.stdout.trim() !== "PRIVATE")
|
|
16
|
+
throw new Error("Cannot verify PRIVATE repository; sync blocked. Check gh authentication and repository visibility.");
|
|
17
|
+
}
|
|
18
|
+
export function assertSafeTrackedFiles(root, settings) {
|
|
19
|
+
const files = git(["ls-files", "-z"], { cwd: root });
|
|
20
|
+
if (!files.ok)
|
|
21
|
+
throw new Error("Cannot inspect staged file list");
|
|
22
|
+
for (const name of files.stdout.split("\0").filter(Boolean)) {
|
|
23
|
+
const normalized = name.replace(/\\/g, "/");
|
|
24
|
+
const credential = /(^|\/)(auth|account)\.json$/i.test(normalized);
|
|
25
|
+
if (credential &&
|
|
26
|
+
settings.includeCredentials &&
|
|
27
|
+
/^_data\/(auth|account)\.json$/.test(normalized))
|
|
28
|
+
continue;
|
|
29
|
+
if (credential ||
|
|
30
|
+
/(^|\/)\.env(?:\.|$)|\.(?:db|sqlite)(?:-|$)|(?:\.backup-|\.bak-)|^_state\/locks\//i.test(normalized))
|
|
31
|
+
throw new Error(`Sensitive/runtime file is staged: ${name}. Untrack it before pushing.`);
|
|
32
|
+
const full = path.join(root, name);
|
|
33
|
+
if (!fs.existsSync(full))
|
|
34
|
+
continue;
|
|
35
|
+
if (fs.lstatSync(full).isSymbolicLink())
|
|
36
|
+
throw new Error(`Symlink cannot be shared safely: ${name}`);
|
|
37
|
+
if (/\.jsonc?$/.test(name) && fs.statSync(full).size < 2 * 1024 * 1024) {
|
|
38
|
+
const text = fs.readFileSync(full, "utf8");
|
|
39
|
+
const pattern = /["'](?:apiKey|api_key|access_token|refresh_token|client_secret|password)["']\s*:\s*["']([^"']+)["']/g;
|
|
40
|
+
for (const m of text.matchAll(pattern))
|
|
41
|
+
if (!/^\{(?:env|file):[^}]+\}$|^<[^>]+>$/.test(m[1] ?? ""))
|
|
42
|
+
throw new Error(`Possible embedded credential in ${name}; use an environment/file reference. Value not logged.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function createConfigBackup(config, data) {
|
|
47
|
+
if (!fs.existsSync(config))
|
|
48
|
+
return;
|
|
49
|
+
const ignored = new Set([
|
|
50
|
+
".git",
|
|
51
|
+
"node_modules",
|
|
52
|
+
"_sessions",
|
|
53
|
+
"_data",
|
|
54
|
+
"_state",
|
|
55
|
+
"_agents",
|
|
56
|
+
".opencode-sync.lock",
|
|
57
|
+
]);
|
|
58
|
+
const list = [];
|
|
59
|
+
function walk(dir) {
|
|
60
|
+
for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
61
|
+
if (ignored.has(item.name))
|
|
62
|
+
continue;
|
|
63
|
+
const full = path.join(dir, item.name);
|
|
64
|
+
if (item.isDirectory())
|
|
65
|
+
walk(full);
|
|
66
|
+
else if (item.isFile())
|
|
67
|
+
list.push(full);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
walk(config);
|
|
71
|
+
if (!list.length)
|
|
72
|
+
return;
|
|
73
|
+
const hash = createHash("sha256");
|
|
74
|
+
for (const file of list.sort()) {
|
|
75
|
+
hash.update(path.relative(config, file));
|
|
76
|
+
hash.update(fs.readFileSync(file));
|
|
77
|
+
}
|
|
78
|
+
const digest = hash.digest("hex");
|
|
79
|
+
const backups = path.join(data, "backups", "opencode-multidevice-sync", "config");
|
|
80
|
+
fs.mkdirSync(backups, { recursive: true, mode: 0o700 });
|
|
81
|
+
const previous = fs
|
|
82
|
+
.readdirSync(backups)
|
|
83
|
+
.filter((n) => n.startsWith("config-"))
|
|
84
|
+
.sort()
|
|
85
|
+
.reverse();
|
|
86
|
+
if (previous[0]?.includes(digest.slice(0, 16)))
|
|
87
|
+
return;
|
|
88
|
+
const dest = path.join(backups, `config-${Date.now()}-${digest.slice(0, 16)}-${randomUUID()}`);
|
|
89
|
+
for (const file of list) {
|
|
90
|
+
const target = path.join(dest, path.relative(config, file));
|
|
91
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
92
|
+
fs.copyFileSync(file, target);
|
|
93
|
+
}
|
|
94
|
+
for (const old of previous.slice(9))
|
|
95
|
+
fs.rmSync(path.join(backups, old), { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/core/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,QAAkB;IACpE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,kBAAkB;QAAE,OAAO;IACvE,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,CAAC,qCAAqC;IAC1G,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CACxB,+FAA+F,CAChG,CAAC;IACF,IAAI,CAAC,KAAK;QACR,MAAM,IAAI,KAAK,CACb,sFAAsF,CACvF,CAAC;IACJ,MAAM,KAAK,GAAG,SAAS,CACrB,IAAI,EACJ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,EAC1F,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CACxD,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS;QACzD,MAAM,IAAI,KAAK,CACb,oGAAoG,CACrG,CAAC;AACN,CAAC;AACD,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAE,QAAkB;IACrE,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAClE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5C,MAAM,UAAU,GAAG,8BAA8B,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACnE,IACE,UAAU;YACV,QAAQ,CAAC,kBAAkB;YAC3B,+BAA+B,CAAC,IAAI,CAAC,UAAU,CAAC;YAEhD,SAAS;QACX,IACE,UAAU;YACV,mFAAmF,CAAC,IAAI,CACtF,UAAU,CACX;YAED,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,8BAA8B,CAAC,CAAC;QAC3F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QACnC,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE;YACrC,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC;QAC9D,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;YACvE,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC3C,MAAM,OAAO,GACX,sGAAsG,CAAC;YACzG,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBACpC,IAAI,CAAC,oCAAoC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBACxD,MAAM,IAAI,KAAK,CACb,mCAAmC,IAAI,wDAAwD,CAChG,CAAC;QACR,CAAC;IACH,CAAC;AACH,CAAC;AACD,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,IAAY;IAC7D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO;IACnC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC;QACtB,MAAM;QACN,cAAc;QACd,WAAW;QACX,OAAO;QACP,QAAQ;QACR,SAAS;QACT,qBAAqB;KACtB,CAAC,CAAC;IACH,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,SAAS,IAAI,CAAC,GAAW;QACvB,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAChE,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAS;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,IAAI,CAAC,WAAW,EAAE;gBAAE,IAAI,CAAC,IAAI,CAAC,CAAC;iBAC9B,IAAI,IAAI,CAAC,MAAM,EAAE;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,CAAC;IACb,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO;IACzB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,2BAA2B,EAAE,QAAQ,CAAC,CAAC;IAClF,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,EAAE;SAChB,WAAW,CAAC,OAAO,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;SACtC,IAAI,EAAE;SACN,OAAO,EAAE,CAAC;IACb,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAAE,OAAO;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,IAAI,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,UAAU,EAAE,EAAE,CAAC,CAAC;IAC/F,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;QAC5D,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACrE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAChC,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACjC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type SqliteDatabase } from "./sqlite.js";
|
|
2
|
+
export interface DatabaseHealth {
|
|
3
|
+
exists: boolean;
|
|
4
|
+
ok: boolean;
|
|
5
|
+
missingTables: string[];
|
|
6
|
+
missingColumns: string[];
|
|
7
|
+
quickCheck: string;
|
|
8
|
+
foreignKeyViolations: number;
|
|
9
|
+
orphanMessages: number;
|
|
10
|
+
orphanParts: number;
|
|
11
|
+
sessions: number;
|
|
12
|
+
messages: number;
|
|
13
|
+
parts: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function inspectDatabase(db: SqliteDatabase): DatabaseHealth;
|
|
16
|
+
export declare function checkDatabaseHealth(file: string): Promise<DatabaseHealth>;
|
|
17
|
+
export declare function healthProblems(h: DatabaseHealth): string[];
|
|
18
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/core/health.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAA8C,MAAM,aAAa,CAAC;AAC9F,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;IACZ,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAOD,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,GAAG,cAAc,CAyClE;AACD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAqB/E;AACD,wBAAgB,cAAc,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CAU1D"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { existingTables, openDatabase, tableColumns } from "./sqlite.js";
|
|
3
|
+
const REQUIRED = {
|
|
4
|
+
project: ["id"],
|
|
5
|
+
session: ["id", "project_id", "directory", "title", "time_updated"],
|
|
6
|
+
message: ["id", "session_id"],
|
|
7
|
+
part: ["id", "session_id", "message_id"],
|
|
8
|
+
};
|
|
9
|
+
export function inspectDatabase(db) {
|
|
10
|
+
const tables = new Set(existingTables(db, Object.keys(REQUIRED)));
|
|
11
|
+
const missingTables = Object.keys(REQUIRED).filter((t) => !tables.has(t));
|
|
12
|
+
const missingColumns = Object.entries(REQUIRED).flatMap(([t, cols]) => tables.has(t)
|
|
13
|
+
? cols.filter((c) => !tableColumns(db, t).includes(c)).map((c) => `${t}.${c}`)
|
|
14
|
+
: []);
|
|
15
|
+
const quick = db
|
|
16
|
+
.prepare("PRAGMA quick_check")
|
|
17
|
+
.all()
|
|
18
|
+
.map((row) => String(Object.values(row)[0]));
|
|
19
|
+
const quickCheck = quick.join("; ");
|
|
20
|
+
const foreignKeyViolations = db.prepare("PRAGMA foreign_key_check").all().length;
|
|
21
|
+
const n = (sql) => Number(db.prepare(sql).all()[0]?.n ?? 0);
|
|
22
|
+
const valid = missingTables.length === 0 && missingColumns.length === 0;
|
|
23
|
+
const orphanMessages = valid
|
|
24
|
+
? n("SELECT COUNT(*) AS n FROM message m LEFT JOIN session s ON s.id=m.session_id WHERE s.id IS NULL")
|
|
25
|
+
: 0;
|
|
26
|
+
const orphanParts = valid
|
|
27
|
+
? n(`SELECT COUNT(*) AS n FROM part p LEFT JOIN session s ON s.id=p.session_id
|
|
28
|
+
LEFT JOIN message m ON m.id=p.message_id WHERE s.id IS NULL OR m.id IS NULL OR m.session_id<>p.session_id`)
|
|
29
|
+
: 0;
|
|
30
|
+
const sessions = tables.has("session") ? n("SELECT COUNT(*) AS n FROM session") : 0;
|
|
31
|
+
const messages = tables.has("message") ? n("SELECT COUNT(*) AS n FROM message") : 0;
|
|
32
|
+
const parts = tables.has("part") ? n("SELECT COUNT(*) AS n FROM part") : 0;
|
|
33
|
+
return {
|
|
34
|
+
exists: true,
|
|
35
|
+
ok: valid && quickCheck === "ok" && !foreignKeyViolations && !orphanMessages && !orphanParts,
|
|
36
|
+
missingTables,
|
|
37
|
+
missingColumns,
|
|
38
|
+
quickCheck,
|
|
39
|
+
foreignKeyViolations,
|
|
40
|
+
orphanMessages,
|
|
41
|
+
orphanParts,
|
|
42
|
+
sessions,
|
|
43
|
+
messages,
|
|
44
|
+
parts,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export async function checkDatabaseHealth(file) {
|
|
48
|
+
if (!fs.existsSync(file))
|
|
49
|
+
return {
|
|
50
|
+
exists: false,
|
|
51
|
+
ok: false,
|
|
52
|
+
missingTables: Object.keys(REQUIRED),
|
|
53
|
+
missingColumns: [],
|
|
54
|
+
quickCheck: "missing",
|
|
55
|
+
foreignKeyViolations: 0,
|
|
56
|
+
orphanMessages: 0,
|
|
57
|
+
orphanParts: 0,
|
|
58
|
+
sessions: 0,
|
|
59
|
+
messages: 0,
|
|
60
|
+
parts: 0,
|
|
61
|
+
};
|
|
62
|
+
const db = await openDatabase(file, { readOnly: true });
|
|
63
|
+
try {
|
|
64
|
+
return inspectDatabase(db);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
db.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function healthProblems(h) {
|
|
71
|
+
const out = [];
|
|
72
|
+
if (!h.exists)
|
|
73
|
+
out.push("OpenCode database does not exist");
|
|
74
|
+
if (h.quickCheck !== "ok")
|
|
75
|
+
out.push(`SQLite quick_check: ${h.quickCheck}`);
|
|
76
|
+
if (h.missingTables.length)
|
|
77
|
+
out.push(`missing tables: ${h.missingTables.join(", ")}`);
|
|
78
|
+
if (h.missingColumns.length)
|
|
79
|
+
out.push(`missing columns: ${h.missingColumns.join(", ")}`);
|
|
80
|
+
if (h.foreignKeyViolations)
|
|
81
|
+
out.push(`${h.foreignKeyViolations} foreign-key violations`);
|
|
82
|
+
if (h.orphanMessages)
|
|
83
|
+
out.push(`${h.orphanMessages} orphan messages`);
|
|
84
|
+
if (h.orphanParts)
|
|
85
|
+
out.push(`${h.orphanParts} orphan/mislinked parts`);
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/core/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A short, stable alias for the current machine.
|
|
3
|
+
*
|
|
4
|
+
* Commit messages record which machine produced a change. Raw hostnames are a
|
|
5
|
+
* bad idea: corporate machines are often named after an asset tag, which is
|
|
6
|
+
* effectively personally identifying information, and the sync repo may not
|
|
7
|
+
* stay private forever. So unless the user picks a name explicitly we derive a
|
|
8
|
+
* stable pseudonym from a hash of the hostname.
|
|
9
|
+
*
|
|
10
|
+
* Precedence:
|
|
11
|
+
* 1. `OPENCODE_SYNC_HOST_ALIAS` environment variable
|
|
12
|
+
* 2. `machine.alias` in the sync config file (passed in as `configured`)
|
|
13
|
+
* 3. `<platform>-<hash>` fallback
|
|
14
|
+
*/
|
|
15
|
+
export declare function hostAlias(configured?: string): string;
|
|
16
|
+
/** Keep aliases filesystem- and commit-message-safe. */
|
|
17
|
+
export declare function sanitizeAlias(value: string): string;
|
|
18
|
+
/** Build a sync commit message: `sync: 2026-07-29 10:04:11 from mac-1a2b3c`. */
|
|
19
|
+
export declare function commitMessage(prefix: string, alias: string): string;
|
|
20
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/core/host.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAQrD;AAaD,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
/**
|
|
4
|
+
* A short, stable alias for the current machine.
|
|
5
|
+
*
|
|
6
|
+
* Commit messages record which machine produced a change. Raw hostnames are a
|
|
7
|
+
* bad idea: corporate machines are often named after an asset tag, which is
|
|
8
|
+
* effectively personally identifying information, and the sync repo may not
|
|
9
|
+
* stay private forever. So unless the user picks a name explicitly we derive a
|
|
10
|
+
* stable pseudonym from a hash of the hostname.
|
|
11
|
+
*
|
|
12
|
+
* Precedence:
|
|
13
|
+
* 1. `OPENCODE_SYNC_HOST_ALIAS` environment variable
|
|
14
|
+
* 2. `machine.alias` in the sync config file (passed in as `configured`)
|
|
15
|
+
* 3. `<platform>-<hash>` fallback
|
|
16
|
+
*/
|
|
17
|
+
export function hostAlias(configured) {
|
|
18
|
+
const fromEnv = process.env.OPENCODE_SYNC_HOST_ALIAS;
|
|
19
|
+
if (fromEnv)
|
|
20
|
+
return sanitizeAlias(fromEnv);
|
|
21
|
+
if (configured)
|
|
22
|
+
return sanitizeAlias(configured);
|
|
23
|
+
const raw = os.hostname() || "unknown";
|
|
24
|
+
const digest = createHash("sha256").update(raw).digest("hex").slice(0, 6);
|
|
25
|
+
return `${platformTag()}-${digest}`;
|
|
26
|
+
}
|
|
27
|
+
function platformTag() {
|
|
28
|
+
switch (process.platform) {
|
|
29
|
+
case "win32":
|
|
30
|
+
return "win";
|
|
31
|
+
case "darwin":
|
|
32
|
+
return "mac";
|
|
33
|
+
default:
|
|
34
|
+
return "linux";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Keep aliases filesystem- and commit-message-safe. */
|
|
38
|
+
export function sanitizeAlias(value) {
|
|
39
|
+
const cleaned = value
|
|
40
|
+
.trim()
|
|
41
|
+
.replace(/[^A-Za-z0-9._-]+/g, "-")
|
|
42
|
+
.replace(/^-+|-+$/g, "");
|
|
43
|
+
return cleaned || "unknown";
|
|
44
|
+
}
|
|
45
|
+
/** Build a sync commit message: `sync: 2026-07-29 10:04:11 from mac-1a2b3c`. */
|
|
46
|
+
export function commitMessage(prefix, alias) {
|
|
47
|
+
const time = new Date().toISOString().replace("T", " ").replace(/\..+/, "");
|
|
48
|
+
return `${prefix}: ${time} from ${alias}`;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/core/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAC,UAAmB;IAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IACrD,IAAI,OAAO;QAAE,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,UAAU;QAAE,OAAO,aAAa,CAAC,UAAU,CAAC,CAAC;IAEjD,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,SAAS,CAAC;IACvC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1E,OAAO,GAAG,WAAW,EAAE,IAAI,MAAM,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,WAAW;IAClB,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,OAAO,GAAG,KAAK;SAClB,IAAI,EAAE;SACN,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC;SACjC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,OAAO,OAAO,IAAI,SAAS,CAAC;AAC9B,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,KAAa;IACzD,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5E,OAAO,GAAG,MAAM,KAAK,IAAI,SAAS,KAAK,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./backup.js";
|
|
2
|
+
export * from "./health.js";
|
|
3
|
+
export * from "./fsx.js";
|
|
4
|
+
export * from "./git.js";
|
|
5
|
+
export * from "./host.js";
|
|
6
|
+
export * from "./jsonc.js";
|
|
7
|
+
export * from "./lock.js";
|
|
8
|
+
export * from "./overrides.js";
|
|
9
|
+
export * from "./paths.js";
|
|
10
|
+
export * from "./repo.js";
|
|
11
|
+
export * from "./reporter.js";
|
|
12
|
+
export * from "./sessions.js";
|
|
13
|
+
export * from "./settings.js";
|
|
14
|
+
export * from "./sqlite.js";
|
|
15
|
+
export * from "./stage.js";
|
|
16
|
+
export * from "./sync.js";
|
|
17
|
+
export * from "./setup.js";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./backup.js";
|
|
2
|
+
export * from "./health.js";
|
|
3
|
+
export * from "./fsx.js";
|
|
4
|
+
export * from "./git.js";
|
|
5
|
+
export * from "./host.js";
|
|
6
|
+
export * from "./jsonc.js";
|
|
7
|
+
export * from "./lock.js";
|
|
8
|
+
export * from "./overrides.js";
|
|
9
|
+
export * from "./paths.js";
|
|
10
|
+
export * from "./repo.js";
|
|
11
|
+
export * from "./reporter.js";
|
|
12
|
+
export * from "./sessions.js";
|
|
13
|
+
export * from "./settings.js";
|
|
14
|
+
export * from "./sqlite.js";
|
|
15
|
+
export * from "./stage.js";
|
|
16
|
+
export * from "./sync.js";
|
|
17
|
+
export * from "./setup.js";
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free JSONC support.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode config files are JSON with comments and trailing commas. We cannot
|
|
5
|
+
* pull in a parser dependency because this module is loaded inside the OpenCode
|
|
6
|
+
* runtime, so the stripping is done by hand. String literals are copied
|
|
7
|
+
* verbatim so `//` or `/*` inside a value is never mistaken for a comment.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseJsonc<T = any>(text: string): T;
|
|
10
|
+
export declare function tryParseJsonc<T = any>(text: string): T | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Recursively merge `patch` into `base`, returning a new object.
|
|
13
|
+
*
|
|
14
|
+
* - Plain objects merge key by key.
|
|
15
|
+
* - Arrays and scalars are replaced wholesale (an override should be able to
|
|
16
|
+
* shorten a list, which a concat-merge could never express).
|
|
17
|
+
* - An explicit `null` in the patch deletes the key.
|
|
18
|
+
*/
|
|
19
|
+
export declare function deepMerge<T extends Record<string, any>>(base: T, patch: Record<string, any>): T;
|
|
20
|
+
//# sourceMappingURL=jsonc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonc.d.ts","sourceRoot":"","sources":["../../src/core/jsonc.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,wBAAgB,UAAU,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAyEnD;AAED,wBAAgB,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAMlE;AAMD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAe/F"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free JSONC support.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode config files are JSON with comments and trailing commas. We cannot
|
|
5
|
+
* pull in a parser dependency because this module is loaded inside the OpenCode
|
|
6
|
+
* runtime, so the stripping is done by hand. String literals are copied
|
|
7
|
+
* verbatim so `//` or `/*` inside a value is never mistaken for a comment.
|
|
8
|
+
*/
|
|
9
|
+
export function parseJsonc(text) {
|
|
10
|
+
let s = String(text);
|
|
11
|
+
if (s.charCodeAt(0) === 0xfeff)
|
|
12
|
+
s = s.slice(1);
|
|
13
|
+
let out = "";
|
|
14
|
+
let i = 0;
|
|
15
|
+
const n = s.length;
|
|
16
|
+
// Index in `out` of a comma that may turn out to be a trailing comma.
|
|
17
|
+
let lastComma = -1;
|
|
18
|
+
while (i < n) {
|
|
19
|
+
const ch = s[i];
|
|
20
|
+
const next = i + 1 < n ? s[i + 1] : "";
|
|
21
|
+
// String literal — copy verbatim, honouring escapes.
|
|
22
|
+
if (ch === '"') {
|
|
23
|
+
out += ch;
|
|
24
|
+
i++;
|
|
25
|
+
while (i < n) {
|
|
26
|
+
const cc = s[i];
|
|
27
|
+
out += cc;
|
|
28
|
+
if (cc === "\\" && i + 1 < n) {
|
|
29
|
+
out += s[i + 1];
|
|
30
|
+
i += 2;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
i++;
|
|
34
|
+
if (cc === '"')
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
lastComma = -1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
// Line comment.
|
|
41
|
+
if (ch === "/" && next === "/") {
|
|
42
|
+
i += 2;
|
|
43
|
+
while (i < n && s[i] !== "\n")
|
|
44
|
+
i++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// Block comment.
|
|
48
|
+
if (ch === "/" && next === "*") {
|
|
49
|
+
i += 2;
|
|
50
|
+
while (i < n && !(s[i] === "*" && s[i + 1] === "/"))
|
|
51
|
+
i++;
|
|
52
|
+
i += 2;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
// Whitespace keeps a pending comma alive.
|
|
56
|
+
if (ch === " " || ch === "\t" || ch === "\r" || ch === "\n") {
|
|
57
|
+
out += ch;
|
|
58
|
+
i++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// A closing bracket retroactively deletes a pending trailing comma.
|
|
62
|
+
if (ch === "}" || ch === "]") {
|
|
63
|
+
if (lastComma !== -1) {
|
|
64
|
+
out = out.slice(0, lastComma) + out.slice(lastComma + 1);
|
|
65
|
+
lastComma = -1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
out += ch;
|
|
69
|
+
lastComma = ch === "," ? out.length - 1 : -1;
|
|
70
|
+
i++;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(out);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
throw new Error(`JSONC parse error: ${e.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function tryParseJsonc(text) {
|
|
80
|
+
try {
|
|
81
|
+
return parseJsonc(text);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function isPlainObject(value) {
|
|
88
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Recursively merge `patch` into `base`, returning a new object.
|
|
92
|
+
*
|
|
93
|
+
* - Plain objects merge key by key.
|
|
94
|
+
* - Arrays and scalars are replaced wholesale (an override should be able to
|
|
95
|
+
* shorten a list, which a concat-merge could never express).
|
|
96
|
+
* - An explicit `null` in the patch deletes the key.
|
|
97
|
+
*/
|
|
98
|
+
export function deepMerge(base, patch) {
|
|
99
|
+
const result = { ...base };
|
|
100
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
101
|
+
if (value === null) {
|
|
102
|
+
delete result[key];
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const current = result[key];
|
|
106
|
+
if (isPlainObject(current) && isPlainObject(value)) {
|
|
107
|
+
result[key] = deepMerge(current, value);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
result[key] = value;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=jsonc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonc.js","sourceRoot":"","sources":["../../src/core/jsonc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SyncLock {
|
|
2
|
+
dir: string;
|
|
3
|
+
id: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AcquireOptions {
|
|
6
|
+
/** How long to wait for an existing holder. Default: fail immediately. */
|
|
7
|
+
waitMs?: number;
|
|
8
|
+
pollMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function acquireSyncLock(configRoot: string, options?: AcquireOptions): SyncLock | null;
|
|
11
|
+
export declare function releaseSyncLock(lock: SyncLock | null): void;
|
|
12
|
+
/** Run `fn` while holding the lock, always releasing it. */
|
|
13
|
+
export declare function withSyncLock<T>(configRoot: string, options: AcquireOptions, fn: () => Promise<T> | T): Promise<T>;
|
|
14
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/core/lock.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAeD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,QAAQ,GAAG,IAAI,CA2BjG;AAiCD,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAS3D;AAED,4DAA4D;AAC5D,wBAAsB,YAAY,CAAC,CAAC,EAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,CAAC,CAAC,CAYZ"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
/**
|
|
6
|
+
* Cross-process advisory lock.
|
|
7
|
+
*
|
|
8
|
+
* `mkdir` is atomic on every platform we target, which makes it a better
|
|
9
|
+
* primitive than an exclusive file open (Windows leaves the file behind after a
|
|
10
|
+
* hard kill and the next process can never acquire it).
|
|
11
|
+
*
|
|
12
|
+
* A stale lock is reclaimed in two situations:
|
|
13
|
+
* - the recorded pid no longer exists, or
|
|
14
|
+
* - the lock is unreadable/corrupt and older than the grace period.
|
|
15
|
+
*/
|
|
16
|
+
const MALFORMED_GRACE_MS = 30_000;
|
|
17
|
+
function lockDir(configRoot) {
|
|
18
|
+
return path.join(configRoot, ".opencode-sync.lock");
|
|
19
|
+
}
|
|
20
|
+
function readOwner(dir) {
|
|
21
|
+
return JSON.parse(fs.readFileSync(path.join(dir, "owner.json"), "utf8"));
|
|
22
|
+
}
|
|
23
|
+
function sleepSync(ms) {
|
|
24
|
+
if (ms <= 0)
|
|
25
|
+
return;
|
|
26
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
27
|
+
}
|
|
28
|
+
export function acquireSyncLock(configRoot, options = {}) {
|
|
29
|
+
const { waitMs = 0, pollMs = 250 } = options;
|
|
30
|
+
const dir = lockDir(configRoot);
|
|
31
|
+
const deadline = Date.now() + waitMs;
|
|
32
|
+
for (;;) {
|
|
33
|
+
try {
|
|
34
|
+
const id = randomUUID();
|
|
35
|
+
fs.mkdirSync(dir, { recursive: false });
|
|
36
|
+
fs.writeFileSync(path.join(dir, "owner.json"), JSON.stringify({
|
|
37
|
+
id,
|
|
38
|
+
pid: process.pid,
|
|
39
|
+
hostname: os.hostname(),
|
|
40
|
+
startedAt: new Date().toISOString(),
|
|
41
|
+
}));
|
|
42
|
+
return { dir, id };
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err?.code !== "EEXIST")
|
|
46
|
+
throw err;
|
|
47
|
+
if (reclaimIfStale(dir))
|
|
48
|
+
continue;
|
|
49
|
+
if (Date.now() >= deadline)
|
|
50
|
+
return null;
|
|
51
|
+
sleepSync(Math.min(pollMs, Math.max(0, deadline - Date.now())));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function reclaimIfStale(dir) {
|
|
56
|
+
try {
|
|
57
|
+
const owner = readOwner(dir);
|
|
58
|
+
if (!Number.isInteger(owner.pid) || owner.pid <= 0) {
|
|
59
|
+
throw new SyntaxError("invalid lock owner");
|
|
60
|
+
}
|
|
61
|
+
// Signal 0 performs the permission/existence check without delivering.
|
|
62
|
+
process.kill(owner.pid, 0);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
if (err?.code === "ESRCH") {
|
|
67
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
const malformed = err instanceof SyntaxError || err?.code === "ENOENT" || err?.code === "ENOTDIR";
|
|
71
|
+
if (malformed) {
|
|
72
|
+
try {
|
|
73
|
+
if (Date.now() - fs.statSync(dir).mtimeMs > MALFORMED_GRACE_MS) {
|
|
74
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Lock vanished underneath us — retry the acquire.
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function releaseSyncLock(lock) {
|
|
87
|
+
if (!lock)
|
|
88
|
+
return;
|
|
89
|
+
try {
|
|
90
|
+
if (readOwner(lock.dir).id === lock.id) {
|
|
91
|
+
fs.rmSync(lock.dir, { recursive: true, force: true });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Already released or reclaimed by someone else.
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Run `fn` while holding the lock, always releasing it. */
|
|
99
|
+
export async function withSyncLock(configRoot, options, fn) {
|
|
100
|
+
const lock = acquireSyncLock(configRoot, options);
|
|
101
|
+
if (!lock) {
|
|
102
|
+
throw new Error("Another opencode-multidevice-sync operation is already running. Wait for it to finish.");
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await fn();
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
releaseSyncLock(lock);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/core/lock.ts"],"names":[],"mappings":"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"}
|