opencode-synced 0.7.1 → 0.9.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/README.md +5 -1
- package/dist/command/sync-init.md +2 -0
- package/dist/index.js +7 -0
- package/dist/sync/apply.js +102 -25
- package/dist/sync/config.d.ts +2 -0
- package/dist/sync/config.js +3 -0
- package/dist/sync/paths.d.ts +5 -3
- package/dist/sync/paths.js +33 -16
- package/dist/sync/service.d.ts +2 -0
- package/dist/sync/service.js +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,7 +76,9 @@ Create `~/.config/opencode/opencode-synced.jsonc`:
|
|
|
76
76
|
"includeMcpSecrets": false,
|
|
77
77
|
"includeSessions": false,
|
|
78
78
|
"includePromptStash": false,
|
|
79
|
+
"includeModelFavorites": true,
|
|
79
80
|
"extraSecretPaths": [],
|
|
81
|
+
"extraConfigPaths": [],
|
|
80
82
|
}
|
|
81
83
|
```
|
|
82
84
|
|
|
@@ -87,6 +89,8 @@ Create `~/.config/opencode/opencode-synced.jsonc`:
|
|
|
87
89
|
- `~/.config/opencode/opencode.json` and `opencode.jsonc`
|
|
88
90
|
- `~/.config/opencode/AGENTS.md`
|
|
89
91
|
- `~/.config/opencode/agent/`, `command/`, `mode/`, `tool/`, `themes/`, `plugin/`
|
|
92
|
+
- `~/.local/state/opencode/model.json` (model favorites)
|
|
93
|
+
- Any extra paths in `extraConfigPaths` (allowlist, files or folders)
|
|
90
94
|
|
|
91
95
|
### Secrets (private repos only)
|
|
92
96
|
|
|
@@ -94,7 +98,7 @@ Enable secrets with `/sync-enable-secrets` or set `"includeSecrets": true`:
|
|
|
94
98
|
|
|
95
99
|
- `~/.local/share/opencode/auth.json`
|
|
96
100
|
- `~/.local/share/opencode/mcp-auth.json`
|
|
97
|
-
- Any extra paths in `extraSecretPaths` (allowlist)
|
|
101
|
+
- Any extra paths in `extraSecretPaths` (allowlist, files or folders)
|
|
98
102
|
|
|
99
103
|
MCP API keys stored inside `opencode.json(c)` are **not** committed by default. To allow them
|
|
100
104
|
in a private repo, set `"includeMcpSecrets": true` (requires `includeSecrets`).
|
|
@@ -10,3 +10,5 @@ If the user wants an org-owned repo, pass owner="org-name".
|
|
|
10
10
|
If the user wants a public repo, pass private=false.
|
|
11
11
|
Include includeSecrets if the user explicitly opts in.
|
|
12
12
|
Include includeMcpSecrets only if they want MCP secrets committed to a private repo.
|
|
13
|
+
If the user supplies extra config paths, pass extraConfigPaths.
|
|
14
|
+
Model favorites sync is enabled by default; set includeModelFavorites=false to disable.
|
package/dist/index.js
CHANGED
|
@@ -112,9 +112,14 @@ export const opencodeConfigSync = async (ctx) => {
|
|
|
112
112
|
.boolean()
|
|
113
113
|
.optional()
|
|
114
114
|
.describe('Enable prompt stash/history sync (requires includeSecrets)'),
|
|
115
|
+
includeModelFavorites: tool.schema
|
|
116
|
+
.boolean()
|
|
117
|
+
.optional()
|
|
118
|
+
.describe('Sync model favorites (state/model.json)'),
|
|
115
119
|
create: tool.schema.boolean().optional().describe('Create repo if missing'),
|
|
116
120
|
private: tool.schema.boolean().optional().describe('Create repo as private'),
|
|
117
121
|
extraSecretPaths: tool.schema.array(tool.schema.string()).optional(),
|
|
122
|
+
extraConfigPaths: tool.schema.array(tool.schema.string()).optional(),
|
|
118
123
|
localRepoPath: tool.schema.string().optional().describe('Override local repo path'),
|
|
119
124
|
},
|
|
120
125
|
async execute(args) {
|
|
@@ -133,9 +138,11 @@ export const opencodeConfigSync = async (ctx) => {
|
|
|
133
138
|
includeMcpSecrets: args.includeMcpSecrets,
|
|
134
139
|
includeSessions: args.includeSessions,
|
|
135
140
|
includePromptStash: args.includePromptStash,
|
|
141
|
+
includeModelFavorites: args.includeModelFavorites,
|
|
136
142
|
create: args.create,
|
|
137
143
|
private: args.private,
|
|
138
144
|
extraSecretPaths: args.extraSecretPaths,
|
|
145
|
+
extraConfigPaths: args.extraConfigPaths,
|
|
139
146
|
localRepoPath: args.localRepoPath,
|
|
140
147
|
});
|
|
141
148
|
}
|
package/dist/sync/apply.js
CHANGED
|
@@ -7,7 +7,8 @@ export async function syncRepoToLocal(plan, overrides) {
|
|
|
7
7
|
for (const item of plan.items) {
|
|
8
8
|
await copyItem(item.repoPath, item.localPath, item.type);
|
|
9
9
|
}
|
|
10
|
-
await
|
|
10
|
+
await applyExtraPaths(plan, plan.extraConfigs);
|
|
11
|
+
await applyExtraPaths(plan, plan.extraSecrets);
|
|
11
12
|
if (overrides && Object.keys(overrides).length > 0) {
|
|
12
13
|
await applyOverridesToLocalConfig(plan, overrides);
|
|
13
14
|
}
|
|
@@ -49,7 +50,8 @@ export async function syncLocalToRepo(plan, overrides, options = {}) {
|
|
|
49
50
|
}
|
|
50
51
|
await copyItem(item.localPath, item.repoPath, item.type, true);
|
|
51
52
|
}
|
|
52
|
-
await
|
|
53
|
+
await writeExtraPathManifest(plan, plan.extraConfigs);
|
|
54
|
+
await writeExtraPathManifest(plan, plan.extraSecrets);
|
|
53
55
|
}
|
|
54
56
|
async function copyItem(sourcePath, destinationPath, type, removeWhenMissing = false) {
|
|
55
57
|
if (!(await pathExists(sourcePath))) {
|
|
@@ -139,13 +141,13 @@ async function copyDirRecursive(sourcePath, destinationPath) {
|
|
|
139
141
|
async function removePath(targetPath) {
|
|
140
142
|
await fs.rm(targetPath, { recursive: true, force: true });
|
|
141
143
|
}
|
|
142
|
-
async function
|
|
143
|
-
const allowlist =
|
|
144
|
+
async function applyExtraPaths(plan, extra) {
|
|
145
|
+
const allowlist = extra.allowlist;
|
|
144
146
|
if (allowlist.length === 0)
|
|
145
147
|
return;
|
|
146
|
-
if (!(await pathExists(
|
|
148
|
+
if (!(await pathExists(extra.manifestPath)))
|
|
147
149
|
return;
|
|
148
|
-
const manifestContent = await fs.readFile(
|
|
150
|
+
const manifestContent = await fs.readFile(extra.manifestPath, 'utf8');
|
|
149
151
|
const manifest = parseJsonc(manifestContent);
|
|
150
152
|
for (const entry of manifest.entries) {
|
|
151
153
|
const normalized = normalizePath(entry.sourcePath, plan.homeDir, plan.platform);
|
|
@@ -156,41 +158,116 @@ async function applyExtraSecrets(plan, fromRepo) {
|
|
|
156
158
|
? entry.repoPath
|
|
157
159
|
: path.join(plan.repoRoot, entry.repoPath);
|
|
158
160
|
const localPath = entry.sourcePath;
|
|
161
|
+
const entryType = entry.type ?? 'file';
|
|
159
162
|
if (!(await pathExists(repoPath)))
|
|
160
163
|
continue;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (entry.mode !== undefined) {
|
|
164
|
-
await chmodIfExists(localPath, entry.mode);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
164
|
+
await copyItem(repoPath, localPath, entryType);
|
|
165
|
+
await applyExtraPathModes(localPath, entry);
|
|
167
166
|
}
|
|
168
167
|
}
|
|
169
|
-
async function
|
|
170
|
-
const allowlist =
|
|
171
|
-
const extraDir = path.join(path.dirname(
|
|
168
|
+
async function writeExtraPathManifest(plan, extra) {
|
|
169
|
+
const allowlist = extra.allowlist;
|
|
170
|
+
const extraDir = path.join(path.dirname(extra.manifestPath), 'extra');
|
|
172
171
|
if (allowlist.length === 0) {
|
|
173
|
-
await removePath(
|
|
172
|
+
await removePath(extra.manifestPath);
|
|
174
173
|
await removePath(extraDir);
|
|
175
174
|
return;
|
|
176
175
|
}
|
|
177
176
|
await removePath(extraDir);
|
|
178
177
|
const entries = [];
|
|
179
|
-
for (const entry of
|
|
178
|
+
for (const entry of extra.entries) {
|
|
180
179
|
const sourcePath = entry.sourcePath;
|
|
181
180
|
if (!(await pathExists(sourcePath))) {
|
|
182
181
|
continue;
|
|
183
182
|
}
|
|
184
183
|
const stat = await fs.stat(sourcePath);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
sourcePath,
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
if (stat.isDirectory()) {
|
|
185
|
+
await copyDirRecursive(sourcePath, entry.repoPath);
|
|
186
|
+
const items = await collectExtraPathItems(sourcePath, sourcePath);
|
|
187
|
+
entries.push({
|
|
188
|
+
sourcePath,
|
|
189
|
+
repoPath: path.relative(plan.repoRoot, entry.repoPath),
|
|
190
|
+
type: 'dir',
|
|
191
|
+
mode: stat.mode & 0o777,
|
|
192
|
+
items,
|
|
193
|
+
});
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (stat.isFile()) {
|
|
197
|
+
await copyFileWithMode(sourcePath, entry.repoPath);
|
|
198
|
+
entries.push({
|
|
199
|
+
sourcePath,
|
|
200
|
+
repoPath: path.relative(plan.repoRoot, entry.repoPath),
|
|
201
|
+
type: 'file',
|
|
202
|
+
mode: stat.mode & 0o777,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
await fs.mkdir(path.dirname(extra.manifestPath), { recursive: true });
|
|
207
|
+
await writeJsonFile(extra.manifestPath, { entries }, { jsonc: false });
|
|
208
|
+
}
|
|
209
|
+
async function collectExtraPathItems(sourcePath, basePath) {
|
|
210
|
+
const items = [];
|
|
211
|
+
const entries = await fs.readdir(sourcePath, { withFileTypes: true });
|
|
212
|
+
for (const entry of entries) {
|
|
213
|
+
const entrySource = path.join(sourcePath, entry.name);
|
|
214
|
+
const relativePath = path.relative(basePath, entrySource);
|
|
215
|
+
if (entry.isDirectory()) {
|
|
216
|
+
const stat = await fs.stat(entrySource);
|
|
217
|
+
items.push({
|
|
218
|
+
relativePath,
|
|
219
|
+
type: 'dir',
|
|
220
|
+
mode: stat.mode & 0o777,
|
|
221
|
+
});
|
|
222
|
+
const nested = await collectExtraPathItems(entrySource, basePath);
|
|
223
|
+
items.push(...nested);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (entry.isFile()) {
|
|
227
|
+
const stat = await fs.stat(entrySource);
|
|
228
|
+
items.push({
|
|
229
|
+
relativePath,
|
|
230
|
+
type: 'file',
|
|
231
|
+
mode: stat.mode & 0o777,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return items;
|
|
236
|
+
}
|
|
237
|
+
async function applyExtraPathModes(targetPath, entry) {
|
|
238
|
+
if (entry.mode !== undefined) {
|
|
239
|
+
await chmodIfExists(targetPath, entry.mode);
|
|
240
|
+
}
|
|
241
|
+
if (entry.type !== 'dir') {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (!entry.items || entry.items.length === 0) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
for (const item of entry.items) {
|
|
248
|
+
if (item.mode === undefined)
|
|
249
|
+
continue;
|
|
250
|
+
const itemPath = resolveExtraPathItem(targetPath, item.relativePath);
|
|
251
|
+
if (!itemPath)
|
|
252
|
+
continue;
|
|
253
|
+
await chmodIfExists(itemPath, item.mode);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function resolveExtraPathItem(basePath, relativePath) {
|
|
257
|
+
if (!relativePath)
|
|
258
|
+
return null;
|
|
259
|
+
if (path.isAbsolute(relativePath))
|
|
260
|
+
return null;
|
|
261
|
+
const resolvedBase = path.resolve(basePath);
|
|
262
|
+
const resolvedPath = path.resolve(basePath, relativePath);
|
|
263
|
+
const relative = path.relative(resolvedBase, resolvedPath);
|
|
264
|
+
if (relative === '..' || relative.startsWith(`..${path.sep}`)) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
if (path.isAbsolute(relative)) {
|
|
268
|
+
return null;
|
|
191
269
|
}
|
|
192
|
-
|
|
193
|
-
await writeJsonFile(plan.extraSecrets.manifestPath, { entries }, { jsonc: false });
|
|
270
|
+
return resolvedPath;
|
|
194
271
|
}
|
|
195
272
|
function isDeepEqual(left, right) {
|
|
196
273
|
if (left === right)
|
package/dist/sync/config.d.ts
CHANGED
|
@@ -12,7 +12,9 @@ export interface SyncConfig {
|
|
|
12
12
|
includeMcpSecrets?: boolean;
|
|
13
13
|
includeSessions?: boolean;
|
|
14
14
|
includePromptStash?: boolean;
|
|
15
|
+
includeModelFavorites?: boolean;
|
|
15
16
|
extraSecretPaths?: string[];
|
|
17
|
+
extraConfigPaths?: string[];
|
|
16
18
|
}
|
|
17
19
|
export interface SyncState {
|
|
18
20
|
lastPull?: string;
|
package/dist/sync/config.js
CHANGED
|
@@ -22,12 +22,15 @@ export async function chmodIfExists(filePath, mode) {
|
|
|
22
22
|
}
|
|
23
23
|
export function normalizeSyncConfig(config) {
|
|
24
24
|
const includeSecrets = Boolean(config.includeSecrets);
|
|
25
|
+
const includeModelFavorites = config.includeModelFavorites !== false;
|
|
25
26
|
return {
|
|
26
27
|
includeSecrets,
|
|
27
28
|
includeMcpSecrets: includeSecrets ? Boolean(config.includeMcpSecrets) : false,
|
|
28
29
|
includeSessions: Boolean(config.includeSessions),
|
|
29
30
|
includePromptStash: Boolean(config.includePromptStash),
|
|
31
|
+
includeModelFavorites,
|
|
30
32
|
extraSecretPaths: Array.isArray(config.extraSecretPaths) ? config.extraSecretPaths : [],
|
|
33
|
+
extraConfigPaths: Array.isArray(config.extraConfigPaths) ? config.extraConfigPaths : [],
|
|
31
34
|
localRepoPath: config.localRepoPath,
|
|
32
35
|
repo: config.repo,
|
|
33
36
|
};
|
package/dist/sync/paths.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface SyncItem {
|
|
|
21
21
|
isSecret: boolean;
|
|
22
22
|
isConfigFile: boolean;
|
|
23
23
|
}
|
|
24
|
-
export interface
|
|
24
|
+
export interface ExtraPathPlan {
|
|
25
25
|
allowlist: string[];
|
|
26
26
|
manifestPath: string;
|
|
27
27
|
entries: Array<{
|
|
@@ -31,7 +31,8 @@ export interface ExtraSecretPlan {
|
|
|
31
31
|
}
|
|
32
32
|
export interface SyncPlan {
|
|
33
33
|
items: SyncItem[];
|
|
34
|
-
extraSecrets:
|
|
34
|
+
extraSecrets: ExtraPathPlan;
|
|
35
|
+
extraConfigs: ExtraPathPlan;
|
|
35
36
|
repoRoot: string;
|
|
36
37
|
homeDir: string;
|
|
37
38
|
platform: NodeJS.Platform;
|
|
@@ -42,6 +43,7 @@ export declare function resolveSyncLocations(env?: NodeJS.ProcessEnv, platform?:
|
|
|
42
43
|
export declare function expandHome(inputPath: string, homeDir: string): string;
|
|
43
44
|
export declare function normalizePath(inputPath: string, homeDir: string, platform?: NodeJS.Platform): string;
|
|
44
45
|
export declare function isSamePath(left: string, right: string, homeDir: string, platform?: NodeJS.Platform): boolean;
|
|
45
|
-
export declare function
|
|
46
|
+
export declare function encodeExtraPath(inputPath: string): string;
|
|
47
|
+
export declare const encodeSecretPath: typeof encodeExtraPath;
|
|
46
48
|
export declare function resolveRepoRoot(config: SyncConfig | null, locations: SyncLocations): string;
|
|
47
49
|
export declare function buildSyncPlan(config: SyncConfig, locations: SyncLocations, repoRoot: string, platform?: NodeJS.Platform): SyncPlan;
|
package/dist/sync/paths.js
CHANGED
|
@@ -9,6 +9,7 @@ const DEFAULT_STATE_NAME = 'sync-state.json';
|
|
|
9
9
|
const CONFIG_DIRS = ['agent', 'command', 'mode', 'tool', 'themes', 'plugin'];
|
|
10
10
|
const SESSION_DIRS = ['storage/session', 'storage/message', 'storage/part', 'storage/session_diff'];
|
|
11
11
|
const PROMPT_STASH_FILES = ['prompt-stash.jsonl', 'prompt-history.jsonl'];
|
|
12
|
+
const MODEL_FAVORITES_FILE = 'model.json';
|
|
12
13
|
export function resolveHomeDir(env = process.env, platform = process.platform) {
|
|
13
14
|
if (platform === 'win32') {
|
|
14
15
|
return env.USERPROFILE ?? env.HOMEDRIVE ?? env.HOME ?? '';
|
|
@@ -75,13 +76,14 @@ export function normalizePath(inputPath, homeDir, platform = process.platform) {
|
|
|
75
76
|
export function isSamePath(left, right, homeDir, platform = process.platform) {
|
|
76
77
|
return normalizePath(left, homeDir, platform) === normalizePath(right, homeDir, platform);
|
|
77
78
|
}
|
|
78
|
-
export function
|
|
79
|
+
export function encodeExtraPath(inputPath) {
|
|
79
80
|
const normalized = inputPath.replace(/\\/g, '/');
|
|
80
81
|
const safeBase = normalized.replace(/[^a-zA-Z0-9._-]+/g, '_').replace(/^_+/, '');
|
|
81
82
|
const hash = crypto.createHash('sha1').update(normalized).digest('hex').slice(0, 8);
|
|
82
|
-
const base = safeBase ? safeBase.slice(-80) : '
|
|
83
|
+
const base = safeBase ? safeBase.slice(-80) : 'path';
|
|
83
84
|
return `${base}-${hash}`;
|
|
84
85
|
}
|
|
86
|
+
export const encodeSecretPath = encodeExtraPath;
|
|
85
87
|
export function resolveRepoRoot(config, locations) {
|
|
86
88
|
if (config?.localRepoPath) {
|
|
87
89
|
return expandHome(config.localRepoPath, locations.xdg.homeDir);
|
|
@@ -91,11 +93,15 @@ export function resolveRepoRoot(config, locations) {
|
|
|
91
93
|
export function buildSyncPlan(config, locations, repoRoot, platform = process.platform) {
|
|
92
94
|
const configRoot = locations.configRoot;
|
|
93
95
|
const dataRoot = path.join(locations.xdg.dataDir, 'opencode');
|
|
96
|
+
const stateRoot = path.join(locations.xdg.stateDir, 'opencode');
|
|
94
97
|
const repoConfigRoot = path.join(repoRoot, 'config');
|
|
95
98
|
const repoDataRoot = path.join(repoRoot, 'data');
|
|
96
99
|
const repoSecretsRoot = path.join(repoRoot, 'secrets');
|
|
100
|
+
const repoStateRoot = path.join(repoRoot, 'state');
|
|
97
101
|
const repoExtraDir = path.join(repoSecretsRoot, 'extra');
|
|
98
102
|
const manifestPath = path.join(repoSecretsRoot, 'extra-manifest.json');
|
|
103
|
+
const repoConfigExtraDir = path.join(repoConfigRoot, 'extra');
|
|
104
|
+
const configManifestPath = path.join(repoConfigRoot, 'extra-manifest.json');
|
|
99
105
|
const items = [];
|
|
100
106
|
const addFile = (name, isSecret, isConfigFile) => {
|
|
101
107
|
items.push({
|
|
@@ -118,6 +124,15 @@ export function buildSyncPlan(config, locations, repoRoot, platform = process.pl
|
|
|
118
124
|
isConfigFile: false,
|
|
119
125
|
});
|
|
120
126
|
}
|
|
127
|
+
if (config.includeModelFavorites !== false) {
|
|
128
|
+
items.push({
|
|
129
|
+
localPath: path.join(stateRoot, MODEL_FAVORITES_FILE),
|
|
130
|
+
repoPath: path.join(repoStateRoot, MODEL_FAVORITES_FILE),
|
|
131
|
+
type: 'file',
|
|
132
|
+
isSecret: false,
|
|
133
|
+
isConfigFile: false,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
121
136
|
if (config.includeSecrets) {
|
|
122
137
|
items.push({
|
|
123
138
|
localPath: path.join(dataRoot, 'auth.json'),
|
|
@@ -144,8 +159,6 @@ export function buildSyncPlan(config, locations, repoRoot, platform = process.pl
|
|
|
144
159
|
}
|
|
145
160
|
}
|
|
146
161
|
if (config.includePromptStash) {
|
|
147
|
-
const stateRoot = path.join(locations.xdg.stateDir, 'opencode');
|
|
148
|
-
const repoStateRoot = path.join(repoRoot, 'state');
|
|
149
162
|
for (const fileName of PROMPT_STASH_FILES) {
|
|
150
163
|
items.push({
|
|
151
164
|
localPath: path.join(stateRoot, fileName),
|
|
@@ -157,22 +170,26 @@ export function buildSyncPlan(config, locations, repoRoot, platform = process.pl
|
|
|
157
170
|
}
|
|
158
171
|
}
|
|
159
172
|
}
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
: [];
|
|
163
|
-
const entries = allowlist.map((sourcePath) => ({
|
|
164
|
-
sourcePath,
|
|
165
|
-
repoPath: path.join(repoExtraDir, encodeSecretPath(sourcePath)),
|
|
166
|
-
}));
|
|
173
|
+
const extraSecrets = buildExtraPathPlan(config.includeSecrets ? config.extraSecretPaths : [], locations, repoExtraDir, manifestPath, platform);
|
|
174
|
+
const extraConfigs = buildExtraPathPlan(config.extraConfigPaths, locations, repoConfigExtraDir, configManifestPath, platform);
|
|
167
175
|
return {
|
|
168
176
|
items,
|
|
169
|
-
extraSecrets
|
|
170
|
-
|
|
171
|
-
manifestPath,
|
|
172
|
-
entries,
|
|
173
|
-
},
|
|
177
|
+
extraSecrets,
|
|
178
|
+
extraConfigs,
|
|
174
179
|
repoRoot,
|
|
175
180
|
homeDir: locations.xdg.homeDir,
|
|
176
181
|
platform,
|
|
177
182
|
};
|
|
178
183
|
}
|
|
184
|
+
function buildExtraPathPlan(inputPaths, locations, repoExtraDir, manifestPath, platform) {
|
|
185
|
+
const allowlist = (inputPaths ?? []).map((entry) => normalizePath(entry, locations.xdg.homeDir, platform));
|
|
186
|
+
const entries = allowlist.map((sourcePath) => ({
|
|
187
|
+
sourcePath,
|
|
188
|
+
repoPath: path.join(repoExtraDir, encodeExtraPath(sourcePath)),
|
|
189
|
+
}));
|
|
190
|
+
return {
|
|
191
|
+
allowlist,
|
|
192
|
+
manifestPath,
|
|
193
|
+
entries,
|
|
194
|
+
};
|
|
195
|
+
}
|
package/dist/sync/service.d.ts
CHANGED
|
@@ -10,9 +10,11 @@ interface InitOptions {
|
|
|
10
10
|
includeMcpSecrets?: boolean;
|
|
11
11
|
includeSessions?: boolean;
|
|
12
12
|
includePromptStash?: boolean;
|
|
13
|
+
includeModelFavorites?: boolean;
|
|
13
14
|
create?: boolean;
|
|
14
15
|
private?: boolean;
|
|
15
16
|
extraSecretPaths?: string[];
|
|
17
|
+
extraConfigPaths?: string[];
|
|
16
18
|
localRepoPath?: string;
|
|
17
19
|
}
|
|
18
20
|
interface LinkOptions {
|
package/dist/sync/service.js
CHANGED
|
@@ -83,6 +83,7 @@ export function createSyncService(ctx) {
|
|
|
83
83
|
const includeMcpSecrets = config.includeMcpSecrets ? 'enabled' : 'disabled';
|
|
84
84
|
const includeSessions = config.includeSessions ? 'enabled' : 'disabled';
|
|
85
85
|
const includePromptStash = config.includePromptStash ? 'enabled' : 'disabled';
|
|
86
|
+
const includeModelFavorites = config.includeModelFavorites ? 'enabled' : 'disabled';
|
|
86
87
|
const lastPull = state.lastPull ?? 'never';
|
|
87
88
|
const lastPush = state.lastPush ?? 'never';
|
|
88
89
|
let changesLabel = 'clean';
|
|
@@ -104,6 +105,7 @@ export function createSyncService(ctx) {
|
|
|
104
105
|
`MCP secrets: ${includeMcpSecrets}`,
|
|
105
106
|
`Sessions: ${includeSessions}`,
|
|
106
107
|
`Prompt stash: ${includePromptStash}`,
|
|
108
|
+
`Model favorites: ${includeModelFavorites}`,
|
|
107
109
|
`Last pull: ${lastPull}`,
|
|
108
110
|
`Last push: ${lastPush}`,
|
|
109
111
|
`Working tree: ${changesLabel}`,
|
|
@@ -171,6 +173,7 @@ export function createSyncService(ctx) {
|
|
|
171
173
|
includeSessions: false,
|
|
172
174
|
includePromptStash: false,
|
|
173
175
|
extraSecretPaths: [],
|
|
176
|
+
extraConfigPaths: [],
|
|
174
177
|
});
|
|
175
178
|
await writeSyncConfig(locations, config);
|
|
176
179
|
const repoRoot = resolveRepoRoot(config, locations);
|
|
@@ -368,7 +371,9 @@ async function buildConfigFromInit($, options) {
|
|
|
368
371
|
includeMcpSecrets: options.includeMcpSecrets ?? false,
|
|
369
372
|
includeSessions: options.includeSessions ?? false,
|
|
370
373
|
includePromptStash: options.includePromptStash ?? false,
|
|
374
|
+
includeModelFavorites: options.includeModelFavorites ?? true,
|
|
371
375
|
extraSecretPaths: options.extraSecretPaths ?? [],
|
|
376
|
+
extraConfigPaths: options.extraConfigPaths ?? [],
|
|
372
377
|
localRepoPath: options.localRepoPath,
|
|
373
378
|
});
|
|
374
379
|
}
|