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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +15 -0
  3. package/README.md +111 -0
  4. package/README.ru.md +57 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +460 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/cli/render.d.ts +45 -0
  10. package/dist/cli/render.d.ts.map +1 -0
  11. package/dist/cli/render.js +167 -0
  12. package/dist/cli/render.js.map +1 -0
  13. package/dist/cli/theme.d.ts +45 -0
  14. package/dist/cli/theme.d.ts.map +1 -0
  15. package/dist/cli/theme.js +88 -0
  16. package/dist/cli/theme.js.map +1 -0
  17. package/dist/core/backup.d.ts +13 -0
  18. package/dist/core/backup.d.ts.map +1 -0
  19. package/dist/core/backup.js +102 -0
  20. package/dist/core/backup.js.map +1 -0
  21. package/dist/core/fsx.d.ts +81 -0
  22. package/dist/core/fsx.d.ts.map +1 -0
  23. package/dist/core/fsx.js +353 -0
  24. package/dist/core/fsx.js.map +1 -0
  25. package/dist/core/git.d.ts +62 -0
  26. package/dist/core/git.d.ts.map +1 -0
  27. package/dist/core/git.js +162 -0
  28. package/dist/core/git.js.map +1 -0
  29. package/dist/core/guard.d.ts +5 -0
  30. package/dist/core/guard.d.ts.map +1 -0
  31. package/dist/core/guard.js +97 -0
  32. package/dist/core/guard.js.map +1 -0
  33. package/dist/core/health.d.ts +18 -0
  34. package/dist/core/health.d.ts.map +1 -0
  35. package/dist/core/health.js +88 -0
  36. package/dist/core/health.js.map +1 -0
  37. package/dist/core/host.d.ts +20 -0
  38. package/dist/core/host.d.ts.map +1 -0
  39. package/dist/core/host.js +50 -0
  40. package/dist/core/host.js.map +1 -0
  41. package/dist/core/index.d.ts +18 -0
  42. package/dist/core/index.d.ts.map +1 -0
  43. package/dist/core/index.js +18 -0
  44. package/dist/core/index.js.map +1 -0
  45. package/dist/core/jsonc.d.ts +20 -0
  46. package/dist/core/jsonc.d.ts.map +1 -0
  47. package/dist/core/jsonc.js +115 -0
  48. package/dist/core/jsonc.js.map +1 -0
  49. package/dist/core/lock.d.ts +14 -0
  50. package/dist/core/lock.d.ts.map +1 -0
  51. package/dist/core/lock.js +111 -0
  52. package/dist/core/lock.js.map +1 -0
  53. package/dist/core/overrides.d.ts +43 -0
  54. package/dist/core/overrides.d.ts.map +1 -0
  55. package/dist/core/overrides.js +169 -0
  56. package/dist/core/overrides.js.map +1 -0
  57. package/dist/core/paths.d.ts +36 -0
  58. package/dist/core/paths.d.ts.map +1 -0
  59. package/dist/core/paths.js +55 -0
  60. package/dist/core/paths.js.map +1 -0
  61. package/dist/core/preview.d.ts +3 -0
  62. package/dist/core/preview.d.ts.map +1 -0
  63. package/dist/core/preview.js +56 -0
  64. package/dist/core/preview.js.map +1 -0
  65. package/dist/core/repo.d.ts +57 -0
  66. package/dist/core/repo.d.ts.map +1 -0
  67. package/dist/core/repo.js +196 -0
  68. package/dist/core/repo.js.map +1 -0
  69. package/dist/core/reporter.d.ts +50 -0
  70. package/dist/core/reporter.d.ts.map +1 -0
  71. package/dist/core/reporter.js +63 -0
  72. package/dist/core/reporter.js.map +1 -0
  73. package/dist/core/sessions.d.ts +83 -0
  74. package/dist/core/sessions.d.ts.map +1 -0
  75. package/dist/core/sessions.js +508 -0
  76. package/dist/core/sessions.js.map +1 -0
  77. package/dist/core/settings.d.ts +68 -0
  78. package/dist/core/settings.d.ts.map +1 -0
  79. package/dist/core/settings.js +126 -0
  80. package/dist/core/settings.js.map +1 -0
  81. package/dist/core/setup.d.ts +48 -0
  82. package/dist/core/setup.d.ts.map +1 -0
  83. package/dist/core/setup.js +186 -0
  84. package/dist/core/setup.js.map +1 -0
  85. package/dist/core/sqlite.d.ts +37 -0
  86. package/dist/core/sqlite.d.ts.map +1 -0
  87. package/dist/core/sqlite.js +108 -0
  88. package/dist/core/sqlite.js.map +1 -0
  89. package/dist/core/stage.d.ts +44 -0
  90. package/dist/core/stage.d.ts.map +1 -0
  91. package/dist/core/stage.js +304 -0
  92. package/dist/core/stage.js.map +1 -0
  93. package/dist/core/sync.d.ts +52 -0
  94. package/dist/core/sync.d.ts.map +1 -0
  95. package/dist/core/sync.js +423 -0
  96. package/dist/core/sync.js.map +1 -0
  97. package/dist/plugin/index.d.ts +29 -0
  98. package/dist/plugin/index.d.ts.map +1 -0
  99. package/dist/plugin/index.js +88 -0
  100. package/dist/plugin/index.js.map +1 -0
  101. package/docs/MIGRATION.md +29 -0
  102. package/docs/ROADMAP.md +29 -0
  103. package/docs/SAFETY.md +31 -0
  104. package/examples/home-laptop.jsonc +31 -0
  105. package/examples/opencode-sync.overrides.jsonc +4 -0
  106. package/examples/work-laptop.jsonc +26 -0
  107. package/package.json +81 -0
@@ -0,0 +1,43 @@
1
+ /** Locate the OpenCode config file inside a directory, if there is one. */
2
+ export declare function findConfigFile(dir: string): string | undefined;
3
+ export declare function hasOverrides(configRoot: string): boolean;
4
+ export declare function loadOverrides(configRoot: string): Record<string, any>;
5
+ /**
6
+ * Merge the local overrides into the config file after a pull.
7
+ *
8
+ * Returns true when the file was rewritten.
9
+ */
10
+ export declare function applyOverrides(configRoot: string): boolean;
11
+ /**
12
+ * Remove override-owned keys from an effective config so it can be pushed.
13
+ *
14
+ * `previous` is the version currently stored in the repository; the values it
15
+ * holds for overridden keys are what gets restored. Keys the repository has
16
+ * never seen are dropped.
17
+ */
18
+ export declare function stripOverrides(effective: Record<string, any>, overrides: Record<string, any>, previous: Record<string, any> | undefined): Record<string, any>;
19
+ /**
20
+ * True when the config on disk is, apart from override-owned keys, exactly what
21
+ * the repository already holds.
22
+ *
23
+ * Once overrides are in use the file on disk permanently differs from the
24
+ * committed version — that is the whole point. Plain `git status` therefore
25
+ * reports it as modified forever, which looks like pending work that never goes
26
+ * away. This lets the status command tell a real edit apart from the expected
27
+ * override difference.
28
+ *
29
+ * Returns `false` when there are no overrides, so callers fall back to git.
30
+ */
31
+ export declare function configMatchesRepo(configRoot: string, previousText: string | undefined): boolean;
32
+ /**
33
+ * Produce the text that should be committed for the config file.
34
+ *
35
+ * `previousText` is the version currently recorded in the repository. It has to
36
+ * be read out of git rather than off disk, because the worktree *is* the config
37
+ * directory — the file on disk is the effective config, overrides and all, so
38
+ * using it as the baseline would quietly publish this machine's overrides.
39
+ *
40
+ * Returns `undefined` when the file can be committed as-is.
41
+ */
42
+ export declare function configTextForRepo(configRoot: string, previousText: string | undefined): string | undefined;
43
+ //# sourceMappingURL=overrides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../src/core/overrides.ts"],"names":[],"mappings":"AA+BA,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM9D;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAKxD;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASrE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAa1D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GACxC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAqBrB;AAkBD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAgB/F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,GAAG,SAAS,CAWpB"}
@@ -0,0 +1,169 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { deepMerge, parseJsonc } from "./jsonc.js";
4
+ import { OVERRIDES_FILE, overridesPath } from "./settings.js";
5
+ /**
6
+ * Per-machine configuration overrides.
7
+ *
8
+ * A synced repository makes every machine identical, which is wrong for the
9
+ * settings that are genuinely machine-specific: a corporate proxy, a local
10
+ * toolchain path, an MCP server that only exists on one box. Overrides restore
11
+ * that freedom by splitting the config into two layers.
12
+ *
13
+ * repo `opencode.jsonc` shared baseline, committed
14
+ * local `opencode-sync.overrides.jsonc` this machine only, never committed
15
+ * ─────────────────────────────── deep merge
16
+ * effective `opencode.jsonc` what OpenCode actually reads
17
+ *
18
+ * The interesting half is push. Because the effective file is what lives on
19
+ * disk, a naive push would upload this machine's overrides to everyone. So on
20
+ * push any key that the overrides file claims is restored to the value the
21
+ * repository already had (or removed entirely if the repository never had it).
22
+ * Overridden keys therefore become invisible to sync in both directions.
23
+ *
24
+ * When no overrides file exists the config is copied byte for byte, which keeps
25
+ * comments and formatting untouched. The structural rewrite only happens for
26
+ * users who actually opted into overrides.
27
+ */
28
+ const CONFIG_FILENAMES = ["opencode.jsonc", "opencode.json"];
29
+ /** Locate the OpenCode config file inside a directory, if there is one. */
30
+ export function findConfigFile(dir) {
31
+ for (const name of CONFIG_FILENAMES) {
32
+ const candidate = path.join(dir, name);
33
+ if (fs.existsSync(candidate))
34
+ return candidate;
35
+ }
36
+ return undefined;
37
+ }
38
+ export function hasOverrides(configRoot) {
39
+ const file = overridesPath(configRoot);
40
+ if (!fs.existsSync(file))
41
+ return false;
42
+ const parsed = loadOverrides(configRoot);
43
+ return Object.keys(parsed).length > 0;
44
+ }
45
+ export function loadOverrides(configRoot) {
46
+ const file = overridesPath(configRoot);
47
+ if (!fs.existsSync(file))
48
+ return {};
49
+ try {
50
+ const parsed = parseJsonc(fs.readFileSync(file, "utf8"));
51
+ return parsed && typeof parsed === "object" ? parsed : {};
52
+ }
53
+ catch (e) {
54
+ throw new Error(`Cannot read ${OVERRIDES_FILE}: ${e.message}`);
55
+ }
56
+ }
57
+ /**
58
+ * Merge the local overrides into the config file after a pull.
59
+ *
60
+ * Returns true when the file was rewritten.
61
+ */
62
+ export function applyOverrides(configRoot) {
63
+ const overrides = loadOverrides(configRoot);
64
+ if (Object.keys(overrides).length === 0)
65
+ return false;
66
+ const configFile = findConfigFile(configRoot);
67
+ if (!configFile)
68
+ return false;
69
+ const base = parseJsonc(fs.readFileSync(configFile, "utf8"));
70
+ const merged = deepMerge(base, overrides);
71
+ const next = `${JSON.stringify(merged, null, 2)}\n`;
72
+ if (fs.readFileSync(configFile, "utf8") === next)
73
+ return false;
74
+ fs.writeFileSync(configFile, next);
75
+ return true;
76
+ }
77
+ /**
78
+ * Remove override-owned keys from an effective config so it can be pushed.
79
+ *
80
+ * `previous` is the version currently stored in the repository; the values it
81
+ * holds for overridden keys are what gets restored. Keys the repository has
82
+ * never seen are dropped.
83
+ */
84
+ export function stripOverrides(effective, overrides, previous) {
85
+ const result = { ...effective };
86
+ for (const [key, overrideValue] of Object.entries(overrides)) {
87
+ const previousValue = previous?.[key];
88
+ const isNestedMerge = isPlainObject(overrideValue) && isPlainObject(result[key]) && isPlainObject(previousValue);
89
+ if (isNestedMerge) {
90
+ result[key] = stripOverrides(result[key], overrideValue, previousValue);
91
+ continue;
92
+ }
93
+ if (previous && Object.hasOwn(previous, key)) {
94
+ result[key] = previousValue;
95
+ }
96
+ else {
97
+ delete result[key];
98
+ }
99
+ }
100
+ return result;
101
+ }
102
+ function isPlainObject(value) {
103
+ return typeof value === "object" && value !== null && !Array.isArray(value);
104
+ }
105
+ /** Serialise with sorted keys, so comparisons ignore key order. */
106
+ function canonical(value) {
107
+ if (Array.isArray(value))
108
+ return `[${value.map(canonical).join(",")}]`;
109
+ if (isPlainObject(value)) {
110
+ const entries = Object.keys(value)
111
+ .sort()
112
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`);
113
+ return `{${entries.join(",")}}`;
114
+ }
115
+ return JSON.stringify(value) ?? "null";
116
+ }
117
+ /**
118
+ * True when the config on disk is, apart from override-owned keys, exactly what
119
+ * the repository already holds.
120
+ *
121
+ * Once overrides are in use the file on disk permanently differs from the
122
+ * committed version — that is the whole point. Plain `git status` therefore
123
+ * reports it as modified forever, which looks like pending work that never goes
124
+ * away. This lets the status command tell a real edit apart from the expected
125
+ * override difference.
126
+ *
127
+ * Returns `false` when there are no overrides, so callers fall back to git.
128
+ */
129
+ export function configMatchesRepo(configRoot, previousText) {
130
+ const overrides = loadOverrides(configRoot);
131
+ if (Object.keys(overrides).length === 0)
132
+ return false;
133
+ if (!previousText)
134
+ return false;
135
+ const configFile = findConfigFile(configRoot);
136
+ if (!configFile)
137
+ return false;
138
+ try {
139
+ const effective = parseJsonc(fs.readFileSync(configFile, "utf8"));
140
+ const previous = parseJsonc(previousText);
141
+ return canonical(stripOverrides(effective, overrides, previous)) === canonical(previous);
142
+ }
143
+ catch {
144
+ // Unparseable on either side: treat it as a genuine difference.
145
+ return false;
146
+ }
147
+ }
148
+ /**
149
+ * Produce the text that should be committed for the config file.
150
+ *
151
+ * `previousText` is the version currently recorded in the repository. It has to
152
+ * be read out of git rather than off disk, because the worktree *is* the config
153
+ * directory — the file on disk is the effective config, overrides and all, so
154
+ * using it as the baseline would quietly publish this machine's overrides.
155
+ *
156
+ * Returns `undefined` when the file can be committed as-is.
157
+ */
158
+ export function configTextForRepo(configRoot, previousText) {
159
+ const overrides = loadOverrides(configRoot);
160
+ if (Object.keys(overrides).length === 0)
161
+ return undefined;
162
+ const configFile = findConfigFile(configRoot);
163
+ if (!configFile)
164
+ return undefined;
165
+ const effective = parseJsonc(fs.readFileSync(configFile, "utf8"));
166
+ const previous = previousText ? parseJsonc(previousText) : undefined;
167
+ return `${JSON.stringify(stripOverrides(effective, overrides, previous), null, 2)}\n`;
168
+ }
169
+ //# sourceMappingURL=overrides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overrides.js","sourceRoot":"","sources":["../../src/core/overrides.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Resolve every directory opencode-multidevice-sync knows about.
3
+ *
4
+ * Each root can be overridden with an environment variable, which is what the
5
+ * test-suite uses to run against a scratch directory instead of the real
6
+ * machine.
7
+ */
8
+ export interface Roots {
9
+ /** `~/.config/opencode` — also the git worktree for the sync repo. */
10
+ config: string;
11
+ /** `~/.local/share/opencode` — auth, account, sqlite database. */
12
+ data: string;
13
+ /** `~/.local/state/opencode` — frecency, kv store, model cache. */
14
+ state: string;
15
+ /** `~/.agents/skills` — skills installed by the `skills` CLI. */
16
+ agents: string;
17
+ /** `~/.cache/opencode` — plugin package cache. */
18
+ cache: string;
19
+ }
20
+ export declare function getConfigRoot(): string;
21
+ export declare function getDataRoot(): string;
22
+ export declare function getStateRoot(): string;
23
+ export declare function getAgentsRoot(): string;
24
+ export declare function getCacheRoot(): string;
25
+ export declare function getRoots(): Roots;
26
+ /** Path of the OpenCode SQLite database that holds all sessions. */
27
+ export declare function getDatabasePath(roots?: Roots): string;
28
+ /** Directory inside the sync repo where session shards are written. */
29
+ export declare const SESSIONS_DIR = "_sessions";
30
+ /** Directory inside the sync repo mirroring the OpenCode data root. */
31
+ export declare const DATA_DIR = "_data";
32
+ /** Directory inside the sync repo mirroring the OpenCode state root. */
33
+ export declare const STATE_DIR = "_state";
34
+ /** Directory inside the sync repo mirroring `~/.agents/skills`. */
35
+ export declare const AGENTS_DIR = "_agents";
36
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,WAAW,KAAK;IACpB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;CACf;AAQD,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAGpC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,QAAQ,IAAI,KAAK,CAQhC;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,KAAK,GAAE,KAAkB,GAAG,MAAM,CAEjE;AAED,uEAAuE;AACvE,eAAO,MAAM,YAAY,cAAc,CAAC;AACxC,uEAAuE;AACvE,eAAO,MAAM,QAAQ,UAAU,CAAC;AAChC,wEAAwE;AACxE,eAAO,MAAM,SAAS,WAAW,CAAC;AAClC,mEAAmE;AACnE,eAAO,MAAM,UAAU,YAAY,CAAC"}
@@ -0,0 +1,55 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ function xdg(envVar, ...fallback) {
4
+ const fromEnv = process.env[envVar];
5
+ if (fromEnv)
6
+ return fromEnv;
7
+ return path.join(os.homedir(), ...fallback);
8
+ }
9
+ export function getConfigRoot() {
10
+ if (process.env.SYNC_CONFIG_ROOT)
11
+ return process.env.SYNC_CONFIG_ROOT;
12
+ return path.join(xdg("XDG_CONFIG_HOME", ".config"), "opencode");
13
+ }
14
+ export function getDataRoot() {
15
+ if (process.env.SYNC_DATA_ROOT)
16
+ return process.env.SYNC_DATA_ROOT;
17
+ return path.join(xdg("XDG_DATA_HOME", ".local", "share"), "opencode");
18
+ }
19
+ export function getStateRoot() {
20
+ if (process.env.SYNC_STATE_ROOT)
21
+ return process.env.SYNC_STATE_ROOT;
22
+ return path.join(xdg("XDG_STATE_HOME", ".local", "state"), "opencode");
23
+ }
24
+ export function getAgentsRoot() {
25
+ if (process.env.SYNC_AGENTS_ROOT)
26
+ return process.env.SYNC_AGENTS_ROOT;
27
+ return path.join(os.homedir(), ".agents", "skills");
28
+ }
29
+ export function getCacheRoot() {
30
+ if (process.env.SYNC_CACHE_ROOT)
31
+ return process.env.SYNC_CACHE_ROOT;
32
+ return path.join(xdg("XDG_CACHE_HOME", ".cache"), "opencode");
33
+ }
34
+ export function getRoots() {
35
+ return {
36
+ config: getConfigRoot(),
37
+ data: getDataRoot(),
38
+ state: getStateRoot(),
39
+ agents: getAgentsRoot(),
40
+ cache: getCacheRoot(),
41
+ };
42
+ }
43
+ /** Path of the OpenCode SQLite database that holds all sessions. */
44
+ export function getDatabasePath(roots = getRoots()) {
45
+ return path.join(roots.data, "opencode.db");
46
+ }
47
+ /** Directory inside the sync repo where session shards are written. */
48
+ export const SESSIONS_DIR = "_sessions";
49
+ /** Directory inside the sync repo mirroring the OpenCode data root. */
50
+ export const DATA_DIR = "_data";
51
+ /** Directory inside the sync repo mirroring the OpenCode state root. */
52
+ export const STATE_DIR = "_state";
53
+ /** Directory inside the sync repo mirroring `~/.agents/skills`. */
54
+ export const AGENTS_DIR = "_agents";
55
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAsB7B,SAAS,GAAG,CAAC,MAAc,EAAE,GAAG,QAAkB;IAChD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACtE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAClE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACpE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACtE,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACpE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO;QACL,MAAM,EAAE,aAAa,EAAE;QACvB,IAAI,EAAE,WAAW,EAAE;QACnB,KAAK,EAAE,YAAY,EAAE;QACrB,MAAM,EAAE,aAAa,EAAE;QACvB,KAAK,EAAE,YAAY,EAAE;KACtB,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,QAAe,QAAQ,EAAE;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AACxC,uEAAuE;AACvE,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC;AAChC,wEAAwE;AACxE,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAC;AAClC,mEAAmE;AACnE,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { SyncOptions, SyncResult } from "./sync.js";
2
+ export declare function previewSync(action: "push" | "pull", options: SyncOptions, run: (o: SyncOptions) => Promise<SyncResult>): Promise<SyncResult>;
3
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/core/preview.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACzD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC,GAC3C,OAAO,CAAC,UAAU,CAAC,CAiDrB"}
@@ -0,0 +1,56 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { createDatabaseBackup } from "./backup.js";
5
+ import { git } from "./git.js";
6
+ import { getRoots } from "./paths.js";
7
+ import { loadSettings } from "./settings.js";
8
+ export async function previewSync(action, options, run) {
9
+ const roots = options.roots ?? getRoots();
10
+ const settings = options.settings ?? loadSettings(roots.config);
11
+ if (settings.extraPaths.length)
12
+ throw new Error("Preview with extraPaths is not supported yet; refusing to touch external files");
13
+ const temp = fs.mkdtempSync(path.join(os.tmpdir(), "omds-preview-"));
14
+ const isolated = {};
15
+ try {
16
+ const marker = path.join(roots.config, ".git");
17
+ if (fs.existsSync(marker) && !fs.lstatSync(marker).isDirectory())
18
+ throw new Error("Preview of linked Git worktrees is not supported");
19
+ for (const key of ["config", "data", "state", "agents", "cache"]) {
20
+ isolated[key] = path.join(temp, key);
21
+ fs.mkdirSync(isolated[key], { recursive: true });
22
+ if (!fs.existsSync(roots[key]) || key === "cache")
23
+ continue;
24
+ fs.cpSync(roots[key], isolated[key], {
25
+ recursive: true,
26
+ dereference: false,
27
+ filter: (p) => {
28
+ const name = path.basename(p);
29
+ return (![
30
+ "node_modules",
31
+ "backups",
32
+ "log",
33
+ "tool-output",
34
+ "snapshot",
35
+ "repos",
36
+ "locks",
37
+ ".opencode-sync.lock",
38
+ ].includes(name) && !/^opencode\.db(?:-|$)/.test(name));
39
+ },
40
+ });
41
+ }
42
+ const database = path.join(roots.data, "opencode.db");
43
+ if (settings.sessions.enabled && fs.existsSync(database)) {
44
+ const snapshot = await createDatabaseBackup(database, path.join(temp, "snapshot"), 1);
45
+ fs.copyFileSync(snapshot.path, path.join(isolated.data, "opencode.db"));
46
+ }
47
+ fs.mkdirSync(path.join(temp, "no-hooks"));
48
+ git(["config", "core.hooksPath", path.join(temp, "no-hooks")], { cwd: isolated.config });
49
+ const result = await run({ ...options, roots: isolated, settings, dryRun: action === "push" });
50
+ return { ...result, message: `Preview only: ${result.message}`, restartRequired: false };
51
+ }
52
+ finally {
53
+ fs.rmSync(temp, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
54
+ }
55
+ }
56
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/core/preview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Files the sync repository must never track.
3
+ *
4
+ * Three categories:
5
+ * - local-only settings (`opencode-sync*.jsonc`)
6
+ * - the SQLite database and its journals, which are gigabytes and are
7
+ * represented instead by the shards in `_sessions/`
8
+ * - caches, logs and OS junk
9
+ */
10
+ export declare const DEFAULT_GITIGNORE = "# opencode-github-sync \u2014 machine-local, never committed\nopencode-sync.jsonc\nopencode-sync.overrides.jsonc\n.opencode-sync.lock\n\n# Runtime / generated\nnode_modules/\n*.backup-*\n*.bak-*\n*.omds-*\n*.edit-tmp\n.env\n.env.*\n_state/locks/\nbun.lock\nlogs/\n**/__pycache__/\n*.pyc\n\n# Session storage lives in _sessions/ as portable shards.\n# The raw database is far too large for git and cannot be merged.\n_data/opencode.db\n_data/opencode.db-shm\n_data/opencode.db-wal\n_data/bin/\n_data/log/\n_data/tool-output/\n_data/snapshot/\n_data/storage/session\n_data/storage/session_diff\n\n# Machine-local UI state\n_state/session.json\n\n# Empty directories recreated by OpenCode at runtime\nplugin/\n\n# OS files\n.DS_Store\nThumbs.db\ndesktop.ini\n";
11
+ export declare const DEFAULT_GITATTRIBUTES = "# Store and check out everything with LF.\n# \"text=auto\" alone would rewrite files to CRLF on Windows, which silently\n# breaks shell scripts shipped inside skills and makes every cross-platform\n# pull look like a whole-file change.\n* text=auto eol=lf\n\n# Append-only history files merge cleanly by taking both sides\n_state/prompt-history.jsonl text eol=lf merge=union\n\n# Session shards are gzipped JSON \u2014 binary, and never line-merged\n_sessions/*.json.gz binary\n";
12
+ /**
13
+ * Paths that exist inside the worktree but must stay out of every commit.
14
+ *
15
+ * These are runtime artefacts of the machine, not configuration. They are
16
+ * excluded from `add`, from stashing and from `clean`, so a sync can never
17
+ * delete a user's local database or tool output.
18
+ */
19
+ export declare const LOCAL_RUNTIME_PATHS: string[];
20
+ export declare function isLocalRuntimePath(candidate: string): boolean;
21
+ export declare function excludePathspecs(): string[];
22
+ /** Unstage runtime paths in a single git call — subprocesses are slow on Windows. */
23
+ export declare function unstageLocalRuntimePaths(root: string): void;
24
+ export interface EnsureRepoOptions {
25
+ root: string;
26
+ remote: string;
27
+ branch: string;
28
+ }
29
+ export type RepoState = "ready" | "adopted" | "fresh";
30
+ /** Configure identity and metadata files. Safe to call repeatedly. */
31
+ export declare function ensureGitIdentity(root: string): void;
32
+ export declare function writeRepoMetadata(root: string): void;
33
+ /**
34
+ * Make `root` a git worktree pointing at `remote`.
35
+ *
36
+ * Returns `adopted` when the remote already had commits (they become the local
37
+ * state) and `fresh` when the remote exists but is empty.
38
+ */
39
+ export declare function initRepo(options: EnsureRepoOptions): RepoState;
40
+ export declare function ensureRepo(options: EnsureRepoOptions): RepoState;
41
+ /** Fail fast when the remote is unreachable, distinguishing auth from network. */
42
+ export declare function assertRemoteReachable(root: string, branch: string, operation: string, allowMissingBranch?: boolean): void;
43
+ /**
44
+ * Verify the commit really landed on the remote.
45
+ *
46
+ * `git push` can exit zero against a misconfigured proxy or a stale credential
47
+ * helper without the remote actually moving, so success is confirmed by reading
48
+ * the remote head back.
49
+ */
50
+ export declare function assertPushLanded(root: string, branch: string, operation: string): void;
51
+ export declare const REPO_DIRS: {
52
+ DATA_DIR: string;
53
+ STATE_DIR: string;
54
+ AGENTS_DIR: string;
55
+ SESSIONS_DIR: string;
56
+ };
57
+ //# sourceMappingURL=repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/core/repo.ts"],"names":[],"mappings":"AAOA;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,yvBAyC7B,CAAC;AAEF,eAAO,MAAM,qBAAqB,meAWjC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,UAQ/B,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAM7D;AAED,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C;AAED,qFAAqF;AACrF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE3D;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CASpD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAYpD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CA0B9D;AAQD,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAUhE;AAED,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,kBAAkB,UAAQ,GACzB,IAAI,CAKN;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAWtF;AAED,eAAO,MAAM,SAAS;;;;;CAAoD,CAAC"}