opencode-synced 0.5.0 → 0.6.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/dist/sync/apply.js +2 -2
- package/dist/sync/config.d.ts +2 -0
- package/dist/sync/config.js +31 -22
- package/dist/sync/mcp-secrets.js +2 -10
- package/dist/sync/service.js +10 -1
- package/dist/sync/utils.js +8 -3
- package/package.json +1 -1
package/dist/sync/apply.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { deepMerge, parseJsonc, pathExists, stripOverrides, writeJsonFile } from './config.js';
|
|
3
|
+
import { deepMerge, hasOwn, parseJsonc, pathExists, stripOverrides, writeJsonFile, } from './config.js';
|
|
4
4
|
import { extractMcpSecrets, hasOverrides, mergeOverrides, stripOverrideKeys, } from './mcp-secrets.js';
|
|
5
5
|
import { normalizePath } from './paths.js';
|
|
6
6
|
export async function syncRepoToLocal(plan, overrides) {
|
|
@@ -214,7 +214,7 @@ function isDeepEqual(left, right) {
|
|
|
214
214
|
if (leftKeys.length !== rightKeys.length)
|
|
215
215
|
return false;
|
|
216
216
|
for (const key of leftKeys) {
|
|
217
|
-
if (!
|
|
217
|
+
if (!hasOwn(right, key))
|
|
218
218
|
return false;
|
|
219
219
|
if (!isDeepEqual(left[key], right[key])) {
|
|
220
220
|
return false;
|
package/dist/sync/config.d.ts
CHANGED
|
@@ -35,3 +35,5 @@ export declare function writeJsonFile(filePath: string, data: unknown, options?:
|
|
|
35
35
|
jsonc: boolean;
|
|
36
36
|
mode?: number;
|
|
37
37
|
}): Promise<void>;
|
|
38
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
39
|
+
export declare function hasOwn(target: Record<string, unknown>, key: string): boolean;
|
package/dist/sync/config.js
CHANGED
|
@@ -106,31 +106,14 @@ export function stripOverrides(localConfig, overrides, baseConfig) {
|
|
|
106
106
|
return result;
|
|
107
107
|
}
|
|
108
108
|
export function parseJsonc(content) {
|
|
109
|
-
const stripped = stripJsonComments(content);
|
|
110
|
-
return JSON.parse(stripped);
|
|
111
|
-
}
|
|
112
|
-
export async function writeJsonFile(filePath, data, options = { jsonc: false }) {
|
|
113
|
-
const json = JSON.stringify(data, null, 2);
|
|
114
|
-
const content = options.jsonc ? `// Generated by opencode-synced\n${json}\n` : `${json}\n`;
|
|
115
|
-
await fs.writeFile(filePath, content, 'utf8');
|
|
116
|
-
if (options.mode !== undefined) {
|
|
117
|
-
await fs.chmod(filePath, options.mode);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
function isPlainObject(value) {
|
|
121
|
-
if (!value || typeof value !== 'object')
|
|
122
|
-
return false;
|
|
123
|
-
return Object.getPrototypeOf(value) === Object.prototype;
|
|
124
|
-
}
|
|
125
|
-
function stripJsonComments(input) {
|
|
126
109
|
let output = '';
|
|
127
110
|
let inString = false;
|
|
128
111
|
let inSingleLine = false;
|
|
129
112
|
let inMultiLine = false;
|
|
130
113
|
let escapeNext = false;
|
|
131
|
-
for (let i = 0; i <
|
|
132
|
-
const current =
|
|
133
|
-
const next =
|
|
114
|
+
for (let i = 0; i < content.length; i += 1) {
|
|
115
|
+
const current = content[i];
|
|
116
|
+
const next = content[i + 1];
|
|
134
117
|
if (inSingleLine) {
|
|
135
118
|
if (current === '\n') {
|
|
136
119
|
inSingleLine = false;
|
|
@@ -160,7 +143,7 @@ function stripJsonComments(input) {
|
|
|
160
143
|
}
|
|
161
144
|
continue;
|
|
162
145
|
}
|
|
163
|
-
if (current === '"'
|
|
146
|
+
if (current === '"') {
|
|
164
147
|
inString = true;
|
|
165
148
|
output += current;
|
|
166
149
|
continue;
|
|
@@ -175,7 +158,33 @@ function stripJsonComments(input) {
|
|
|
175
158
|
i += 1;
|
|
176
159
|
continue;
|
|
177
160
|
}
|
|
161
|
+
if (current === ',') {
|
|
162
|
+
let nextIndex = i + 1;
|
|
163
|
+
while (nextIndex < content.length && /\s/.test(content[nextIndex])) {
|
|
164
|
+
nextIndex += 1;
|
|
165
|
+
}
|
|
166
|
+
const nextChar = content[nextIndex];
|
|
167
|
+
if (nextChar === '}' || nextChar === ']') {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
178
171
|
output += current;
|
|
179
172
|
}
|
|
180
|
-
return output;
|
|
173
|
+
return JSON.parse(output);
|
|
174
|
+
}
|
|
175
|
+
export async function writeJsonFile(filePath, data, options = { jsonc: false }) {
|
|
176
|
+
const json = JSON.stringify(data, null, 2);
|
|
177
|
+
const content = options.jsonc ? `// Generated by opencode-synced\n${json}\n` : `${json}\n`;
|
|
178
|
+
await fs.writeFile(filePath, content, 'utf8');
|
|
179
|
+
if (options.mode !== undefined) {
|
|
180
|
+
await fs.chmod(filePath, options.mode);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export function isPlainObject(value) {
|
|
184
|
+
if (!value || typeof value !== 'object')
|
|
185
|
+
return false;
|
|
186
|
+
return Object.getPrototypeOf(value) === Object.prototype;
|
|
187
|
+
}
|
|
188
|
+
export function hasOwn(target, key) {
|
|
189
|
+
return Object.hasOwn(target, key);
|
|
181
190
|
}
|
package/dist/sync/mcp-secrets.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deepMerge } from './config.js';
|
|
1
|
+
import { deepMerge, hasOwn, isPlainObject } from './config.js';
|
|
2
2
|
const ENV_PLACEHOLDER_PATTERN = /\{env:[^}]+\}/i;
|
|
3
3
|
export function extractMcpSecrets(config) {
|
|
4
4
|
const sanitizedConfig = cloneConfig(config);
|
|
@@ -88,15 +88,7 @@ function setNestedValue(target, path, value) {
|
|
|
88
88
|
function getPlainObject(value) {
|
|
89
89
|
return isPlainObject(value) ? value : null;
|
|
90
90
|
}
|
|
91
|
-
function isPlainObject(value) {
|
|
92
|
-
if (!value || typeof value !== 'object')
|
|
93
|
-
return false;
|
|
94
|
-
return Object.getPrototypeOf(value) === Object.prototype;
|
|
95
|
-
}
|
|
96
91
|
function cloneConfig(config) {
|
|
97
|
-
if (typeof structuredClone === 'function') {
|
|
98
|
-
return structuredClone(config);
|
|
99
|
-
}
|
|
100
92
|
return JSON.parse(JSON.stringify(config));
|
|
101
93
|
}
|
|
102
94
|
export function mergeOverrides(base, extra) {
|
|
@@ -108,7 +100,7 @@ export function stripOverrideKeys(base, toRemove) {
|
|
|
108
100
|
}
|
|
109
101
|
const result = { ...base };
|
|
110
102
|
for (const [key, removeValue] of Object.entries(toRemove)) {
|
|
111
|
-
if (!
|
|
103
|
+
if (!hasOwn(result, key))
|
|
112
104
|
continue;
|
|
113
105
|
const currentValue = result[key];
|
|
114
106
|
if (isPlainObject(removeValue) && isPlainObject(currentValue)) {
|
package/dist/sync/service.js
CHANGED
|
@@ -10,7 +10,16 @@ export function createSyncService(ctx) {
|
|
|
10
10
|
const log = createLogger(ctx.client);
|
|
11
11
|
return {
|
|
12
12
|
startupSync: async () => {
|
|
13
|
-
|
|
13
|
+
let config = null;
|
|
14
|
+
try {
|
|
15
|
+
config = await loadSyncConfig(locations);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const message = `Failed to load opencode-synced config: ${formatError(error)}`;
|
|
19
|
+
log.error(message, { path: locations.syncConfigPath });
|
|
20
|
+
await showToast(ctx.client, `Failed to load opencode-synced config. Check ${locations.syncConfigPath} for JSON errors.`, 'error');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
14
23
|
if (!config) {
|
|
15
24
|
await showToast(ctx.client, 'Configure opencode-synced with /sync-init or link to an existing repo with /sync-link', 'info');
|
|
16
25
|
return;
|
package/dist/sync/utils.js
CHANGED
|
@@ -23,9 +23,14 @@ function log(client, level, message, extra) {
|
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
export async function showToast(client, message, variant) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
try {
|
|
27
|
+
await client.tui.showToast({
|
|
28
|
+
body: { title: 'opencode-synced plugin', message, variant },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Ignore toast failures (e.g. headless mode or early startup).
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
export function unwrapData(response) {
|
|
31
36
|
if (!response || typeof response !== 'object')
|