@dsh-workbench/adapter-profile 1.0.0-beta.1 → 1.0.1

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.
@@ -1,5 +1,5 @@
1
1
  import type { PostResource, RouteHandler } from '@dsh-workbench/shared/host';
2
- import type { ConfigSyncAsset, ConfigSyncRow, ConfigSyncSnapshot } from '@dsh-workbench/shared/contract/protocol/config-sync';
2
+ import type { ConfigSyncRow, ConfigSyncSnapshot } from '@dsh-workbench/shared/contract/protocol/config-sync';
3
3
  export interface PatchBlock {
4
4
  readonly key: string;
5
5
  readonly id: string;
@@ -9,7 +9,7 @@ export interface PatchBlock {
9
9
  }
10
10
  export declare function patchBlocks(text: string): PatchBlock[];
11
11
  export declare function normalizeBlock(body: string): string;
12
- export declare function syncRows(source: readonly PatchBlock[], own: readonly PatchBlock[], missing: (block: PatchBlock) => readonly string[], assets?: (block: PatchBlock) => readonly ConfigSyncAsset[]): ConfigSyncRow[];
12
+ export declare function syncRows(source: readonly PatchBlock[], own: readonly PatchBlock[], missing: (block: PatchBlock) => readonly string[]): ConfigSyncRow[];
13
13
  export declare function mergeBlocks(ownText: string, incoming: readonly PatchBlock[], overwrite: ReadonlySet<string>): {
14
14
  ok: true;
15
15
  text: string;
@@ -40,7 +40,6 @@ export declare function configSyncApply(deps: ConfigSyncDeps, payload: unknown):
40
40
  ok: true;
41
41
  written: string[];
42
42
  skipped: string[];
43
- files: string[];
44
43
  verified: boolean;
45
44
  } | {
46
45
  ok: false;
@@ -1,7 +1,6 @@
1
1
  import { existsSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { CONFIG_SYNC_RESOURCE } from '@dsh-workbench/shared/contract/protocol/config-sync';
4
- import { assetMapOf, assetsOf, cleanupFiles, copyPlans, planCopies } from './config-sync-assets.js';
5
4
  const ID_LINE = /^- id:[ \t]*"?([^"\s#]+)/;
6
5
  const INSERT_LINE = /^- insert:[ \t]*$/;
7
6
  const NESTED_ID = /^[ \t]+-[ \t]*id:[ \t]*"?([^"\s#]+)/;
@@ -73,24 +72,25 @@ function rewriteForTarget(body, self) {
73
72
  return body;
74
73
  return body.replace(/(dshHomePath\(\s*['"])workbench(?=[/'"])/g, `$1${self}`);
75
74
  }
76
- export function syncRows(source, own, missing, assets = () => []) {
75
+ function syncable(block) {
76
+ return !block.insert && !ISOLATION_IDS.has(block.id);
77
+ }
78
+ export function syncRows(source, own, missing) {
77
79
  const mine = new Map(own.map((block) => [block.key, block]));
78
80
  const rows = [];
79
81
  for (const block of source) {
82
+ if (!syncable(block))
83
+ continue;
80
84
  const here = mine.get(block.key);
81
- const same = here !== undefined && normalizeBlock(here.body) === normalizeBlock(block.body);
82
- const found = assets(block);
83
- if (same && found.every((asset) => asset.state === 'same'))
85
+ if (here !== undefined && normalizeBlock(here.body) === normalizeBlock(block.body))
84
86
  continue;
85
87
  rows.push({
86
88
  key: block.key,
87
89
  id: block.id,
88
- insert: block.insert,
89
- state: here === undefined ? 'new' : same ? 'same' : 'differs',
90
+ state: here === undefined ? 'new' : 'differs',
90
91
  body: block.body,
91
92
  ownBody: here === undefined ? null : here.body,
92
93
  missing: missing(block),
93
- assets: found,
94
94
  });
95
95
  }
96
96
  return rows;
@@ -211,23 +211,29 @@ export function configSyncSnapshot(deps, asked) {
211
211
  home: deps.home,
212
212
  };
213
213
  if (deps.profileDir === null) {
214
- return { ...base, source: '', profiles: [], rows: [], emptyProviders: false, reason: '拿不到当前 profile 目录' };
214
+ return {
215
+ ...base,
216
+ source: '',
217
+ profiles: [],
218
+ rows: [],
219
+ emptyProviders: false,
220
+ reason: '拿不到当前 profile 目录',
221
+ };
215
222
  }
216
223
  const own = readPatchBlocks(join(deps.profileDir, 'cordis.patch.yml'));
217
224
  const emptyProviders = own.ok && !own.blocks.some((block) => block.key.startsWith('id:llm-'));
218
225
  const ownBlocks = own.ok ? own.blocks : [];
219
226
  const profiles = others.map((name) => {
220
227
  const read = readPatchBlocks(join(profilesDir, name, 'cordis.patch.yml'));
221
- const assetsFor = (block) => assetsOf(block, join(profilesDir, name), deps.profileDir ?? '');
222
228
  const blocks = read.ok
223
229
  ? read.blocks
224
- .filter((block) => !ISOLATION_IDS.has(block.id))
230
+ .filter(syncable)
225
231
  .map((block) => ({ ...block, body: rewriteForTarget(block.body, deps.self) }))
226
232
  : [];
227
233
  return {
228
234
  name,
229
235
  readable: read.ok,
230
- candidates: read.ok ? syncRows(blocks, ownBlocks, () => [], assetsFor).length : 0,
236
+ candidates: read.ok ? syncRows(blocks, ownBlocks, () => []).length : 0,
231
237
  };
232
238
  });
233
239
  if (others.length === 0) {
@@ -251,11 +257,10 @@ export function configSyncSnapshot(deps, asked) {
251
257
  }
252
258
  const declared = profileDependencies(sourceDir);
253
259
  const modulesDir = join(deps.profileDir, 'node_modules');
254
- const assetsFor = (block) => assetsOf(block, sourceDir, deps.profileDir ?? '');
255
260
  const sourceBlocks = read.blocks
256
- .filter((block) => !ISOLATION_IDS.has(block.id))
261
+ .filter(syncable)
257
262
  .map((block) => ({ ...block, body: rewriteForTarget(block.body, deps.self) }));
258
- const rows = syncRows(sourceBlocks, own.blocks, (block) => block.names.filter((name) => declared.has(name) && !existsSync(join(modulesDir, name))), assetsFor);
263
+ const rows = syncRows(sourceBlocks, own.blocks, (block) => block.names.filter((name) => declared.has(name) && !existsSync(join(modulesDir, name))));
259
264
  return { ...base, source, profiles, rows, emptyProviders };
260
265
  }
261
266
  export async function configSyncApply(deps, payload) {
@@ -265,10 +270,8 @@ export async function configSyncApply(deps, payload) {
265
270
  const source = textOf(body['source']);
266
271
  const keys = new Set((Array.isArray(body['keys']) ? body['keys'] : []).map((item) => textOf(item)).filter((item) => item !== ''));
267
272
  const overwrite = new Set((Array.isArray(body['overwrite']) ? body['overwrite'] : []).map((item) => textOf(item)));
268
- const assetKeys = new Set((Array.isArray(body['assets']) ? body['assets'] : []).map((item) => textOf(item)).filter((item) => item !== ''));
269
- const overwriteAssets = new Set((Array.isArray(body['overwriteAssets']) ? body['overwriteAssets'] : []).map((item) => textOf(item)));
270
- if (source === '' || (keys.size === 0 && assetKeys.size === 0)) {
271
- return { ok: false, text: '没说清从哪个 profile 搬哪几行' };
273
+ if (source === '' || keys.size === 0) {
274
+ return { ok: false, text: '没说清从哪个 profile 同步哪几行' };
272
275
  }
273
276
  const profilesDir = join(deps.home, 'profiles');
274
277
  if (!profileNames(profilesDir).includes(source))
@@ -282,65 +285,34 @@ export async function configSyncApply(deps, payload) {
282
285
  const own = readPatchBlocks(join(deps.profileDir, 'cordis.patch.yml'));
283
286
  if (!own.ok)
284
287
  return { ok: false, text: `读不到工作台自己的配置:${own.reason}` };
288
+ if (read.blocks.some((block) => block.insert && keys.has(block.key))) {
289
+ return { ok: false, text: '插件行不同步:先把插件装到工作台这边,再同步它的设置' };
290
+ }
285
291
  const picked = read.blocks
286
- .filter((block) => keys.has(block.key) && !ISOLATION_IDS.has(block.id))
292
+ .filter((block) => keys.has(block.key) && syncable(block))
287
293
  .map((block) => ({ ...block, body: rewriteForTarget(block.body, deps.self) }));
288
- if (keys.size > 0 && picked.length === 0) {
294
+ if (picked.length === 0) {
289
295
  return { ok: false, text: '选中的行在来源里已经找不到了,重新读一遍再试' };
290
296
  }
291
- const available = assetMapOf(read.blocks, sourceDir, deps.profileDir);
292
- const pickedAssets = [];
293
- for (const key of assetKeys) {
294
- const asset = available.get(key);
295
- if (asset === undefined)
296
- return { ok: false, text: '选中的文件在来源里已经找不到了,重新读一遍再试' };
297
- pickedAssets.push(asset);
298
- }
299
297
  const merged = mergeBlocks(own.text, picked, overwrite);
300
298
  if (!merged.ok)
301
299
  return { ok: false, text: merged.reason };
302
- const planned = planCopies(pickedAssets, sourceDir, deps.profileDir, overwriteAssets);
303
- if (!planned.ok)
304
- return { ok: false, text: planned.text };
305
300
  const patchPath = join(deps.profileDir, 'cordis.patch.yml');
306
- let copied = null;
307
- if (planned.plans.length > 0) {
308
- const outcome = copyPlans(planned.plans);
309
- if (!outcome.ok) {
310
- cleanupFiles(outcome.created, outcome.backups);
311
- return { ok: false, text: outcome.text };
312
- }
313
- copied = { files: outcome.files, created: outcome.created, backups: outcome.backups };
314
- }
315
301
  const patchChanged = merged.text !== own.text;
316
302
  try {
317
303
  if (patchChanged)
318
304
  writePatchFile(patchPath, merged.text);
319
305
  }
320
306
  catch (error) {
321
- if (copied !== null)
322
- cleanupFiles(copied.created, copied.backups);
323
307
  return { ok: false, text: `写不进去:${error instanceof Error ? error.message : String(error)}` };
324
308
  }
325
309
  if (deps.reconcile === undefined) {
326
- return {
327
- ok: true,
328
- written: merged.written,
329
- skipped: merged.skipped,
330
- files: copied?.files ?? [],
331
- verified: false,
332
- };
310
+ return { ok: true, written: merged.written, skipped: merged.skipped, verified: false };
333
311
  }
334
312
  try {
335
313
  const verified = await deps.reconcile();
336
314
  if (verified === false) {
337
- return {
338
- ok: true,
339
- written: merged.written,
340
- skipped: merged.skipped,
341
- files: copied?.files ?? [],
342
- verified: false,
343
- };
315
+ return { ok: true, written: merged.written, skipped: merged.skipped, verified: false };
344
316
  }
345
317
  }
346
318
  catch (error) {
@@ -349,13 +321,9 @@ export async function configSyncApply(deps, payload) {
349
321
  writePatchFile(patchPath, own.text);
350
322
  }
351
323
  catch {
352
- if (copied !== null)
353
- cleanupFiles(copied.created, copied.backups);
354
324
  return { ok: false, text: `写进去了,但启动检查没通过:${error instanceof Error ? error.message : String(error)}。原来的设置没能恢复,手动检查一下` };
355
325
  }
356
326
  }
357
- if (copied !== null)
358
- cleanupFiles(copied.created, copied.backups);
359
327
  try {
360
328
  await deps.reconcile();
361
329
  }
@@ -364,13 +332,7 @@ export async function configSyncApply(deps, payload) {
364
332
  }
365
333
  return { ok: false, text: `写进去了,但启动检查没通过:${error instanceof Error ? error.message : String(error)}。已恢复原来的设置` };
366
334
  }
367
- return {
368
- ok: true,
369
- written: merged.written,
370
- skipped: merged.skipped,
371
- files: copied?.files ?? [],
372
- verified: true,
373
- };
335
+ return { ok: true, written: merged.written, skipped: merged.skipped, verified: true };
374
336
  }
375
337
  export function configSyncAction(deps) {
376
338
  return {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './config-sync.js';
2
2
  export * from './profile-registries.js';
3
3
  export * from './profile-create.js';
4
- export * from './config-sync-assets.js';
5
4
  export * from './release.js';
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './config-sync.js';
2
2
  export * from './profile-registries.js';
3
3
  export * from './profile-create.js';
4
- export * from './config-sync-assets.js';
5
4
  export * from './release.js';
@@ -70,9 +70,6 @@ export async function profileCreateApply(deps, payload) {
70
70
  const copy = typeof body['copy'] === 'object' && body['copy'] !== null ? body['copy'] : {};
71
71
  const flags = {
72
72
  settings: copy['settings'] === true,
73
- plugins: copy['plugins'] === true,
74
- projects: copy['projects'] === true,
75
- sessions: copy['sessions'] === true,
76
73
  };
77
74
  if (!PROFILE_NAME.test(profile))
78
75
  return { ok: false, text: '新工作台的名字不合法:只用字母、数字、点、下划线或短横线' };
@@ -87,33 +84,14 @@ export async function profileCreateApply(deps, payload) {
87
84
  return { ok: false, text: `已经有一个叫 ${profile} 的 profile` };
88
85
  const targetHome = join(deps.home, profile);
89
86
  if (existsSync(targetHome))
90
- return { ok: false, text: `数据目录已经存在:${targetHome}` };
91
- const sourceCandidate = join(deps.home, source);
92
- const sourcePatch = (() => {
93
- try {
94
- return readFileSync(join(profilesDir, source, 'cordis.patch.yml'), 'utf8');
95
- }
96
- catch {
97
- return '';
98
- }
99
- })();
100
- const sourceHasIsolation = /id:\s*session-persistence-jsonl/.test(sourcePatch);
101
- const sourceHome = !sourceHasIsolation
102
- ? deps.home
103
- : sourcePatch.includes('DSH_WORKBENCH_HOME') && source !== 'workbench'
104
- ? sourceCandidate
105
- : join(deps.home, 'workbench');
87
+ return { ok: false, text: `运行时目录已经存在:${targetHome}` };
106
88
  const env = {
107
89
  DSH_HOME: deps.home,
108
90
  DSH_WORKBENCH_HOME: targetHome,
109
91
  WORKBENCH_PROFILE: profile,
110
92
  WORKBENCH_SOURCE_PROFILE: source,
111
93
  WB_CLONE: '1',
112
- WB_SOURCE_HOME: sourceHome,
113
94
  WB_COPY_SETTINGS: flags.settings ? '1' : '0',
114
- WB_COPY_PLUGINS: flags.plugins ? '1' : '0',
115
- WB_COPY_PROJECTS: flags.projects ? '1' : '0',
116
- WB_COPY_SESSIONS: flags.sessions ? '1' : '0',
117
95
  };
118
96
  const setup = deps.runSetup ?? ((next) => defaultRunSetup(deps.packageRoot, next));
119
97
  const cleanup = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/adapter-profile",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.1",
4
4
  "description": "Workbench profile adapter package.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -32,8 +32,8 @@
32
32
  "execa": "^10.2.0",
33
33
  "proper-lockfile": "^4.1.2",
34
34
  "write-file-atomic": "^8.0.0",
35
- "@dsh-workbench/adapter-runtime": "1.0.0-beta.1",
36
- "@dsh-workbench/shared": "1.0.0-beta.1"
35
+ "@dsh-workbench/adapter-runtime": "1.0.1",
36
+ "@dsh-workbench/shared": "1.0.1"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsc -p tsconfig.json",