@serviceme/devtools-cli 1.0.0 → 2.0.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/bridgeServer.js +1237 -10
- package/dist/cli.js +1638 -161
- package/package.json +4 -4
package/dist/bridgeServer.js
CHANGED
|
@@ -40,7 +40,7 @@ var readline = __toESM(require("readline"));
|
|
|
40
40
|
|
|
41
41
|
// ../../packages/serviceme-protocol/src/bridge.ts
|
|
42
42
|
var SERVICEME_PROTOCOL_VERSION = 2;
|
|
43
|
-
function
|
|
43
|
+
function isRecord2(value) {
|
|
44
44
|
return typeof value === "object" && value !== null;
|
|
45
45
|
}
|
|
46
46
|
function isValidProtocolVersion(value) {
|
|
@@ -53,11 +53,23 @@ var BRIDGE_METHODS = [
|
|
|
53
53
|
"task.execute",
|
|
54
54
|
"task.cancel",
|
|
55
55
|
"task.list-running",
|
|
56
|
+
"copilotContent.list",
|
|
57
|
+
"copilotContent.get",
|
|
58
|
+
"copilotContent.install",
|
|
59
|
+
"copilotContent.convertToSymlink",
|
|
60
|
+
"copilotContent.uninstall",
|
|
61
|
+
"copilotContent.setEntryEnabled",
|
|
62
|
+
"copilotContent.listLinked",
|
|
63
|
+
"copilotContent.draft.create",
|
|
64
|
+
"copilotContent.draft.commit",
|
|
65
|
+
"copilotContent.draft.list",
|
|
66
|
+
"copilotContent.draft.delete",
|
|
56
67
|
"skillRepo.list",
|
|
57
68
|
"skillRepo.get",
|
|
58
69
|
"skillRepo.install",
|
|
59
70
|
"skillRepo.convertToSymlink",
|
|
60
71
|
"skillRepo.uninstall",
|
|
72
|
+
"skillRepo.setEntryEnabled",
|
|
61
73
|
"skillRepo.listLinked",
|
|
62
74
|
"skillRepo.draft.create",
|
|
63
75
|
"skillRepo.draft.commit",
|
|
@@ -71,6 +83,25 @@ var BRIDGE_METHODS = [
|
|
|
71
83
|
"repo.update",
|
|
72
84
|
"repo.sync",
|
|
73
85
|
"repo.syncAll",
|
|
86
|
+
"repo.resetParseCache",
|
|
87
|
+
"copilotContent.status",
|
|
88
|
+
"copilotContent.restore",
|
|
89
|
+
"copilotContent.approve",
|
|
90
|
+
"copilotContent.migrateLegacy",
|
|
91
|
+
"copilotContent.integrationStatus",
|
|
92
|
+
"copilotContent.customizations.list",
|
|
93
|
+
"copilotContent.package.install",
|
|
94
|
+
"copilotContent.package.update",
|
|
95
|
+
"copilotContent.package.uninstall",
|
|
96
|
+
"copilotContent.package.move",
|
|
97
|
+
"copilotPlugin.list",
|
|
98
|
+
"copilotPlugin.register",
|
|
99
|
+
"copilotPlugin.unregister",
|
|
100
|
+
"copilotContent.legacy.preview",
|
|
101
|
+
"copilotContent.legacy.migrate",
|
|
102
|
+
"copilotContent.sources.list",
|
|
103
|
+
"copilotContent.sources.remove",
|
|
104
|
+
"copilotContent.package.previewUpdate",
|
|
74
105
|
"auth.status",
|
|
75
106
|
"auth.login",
|
|
76
107
|
"auth.logout",
|
|
@@ -88,7 +119,7 @@ function isBridgeMethod(value) {
|
|
|
88
119
|
return typeof value === "string" && BRIDGE_METHODS.includes(value);
|
|
89
120
|
}
|
|
90
121
|
function isBridgeRequest(value) {
|
|
91
|
-
if (!
|
|
122
|
+
if (!isRecord2(value)) {
|
|
92
123
|
return false;
|
|
93
124
|
}
|
|
94
125
|
return isValidProtocolVersion(value.protocolVersion) && value.kind === "request" && typeof value.id === "string" && isBridgeMethod(value.method) && "params" in value;
|
|
@@ -140,14 +171,14 @@ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
140
171
|
"executor_timeout",
|
|
141
172
|
"internal_error"
|
|
142
173
|
]);
|
|
143
|
-
function
|
|
174
|
+
function isRecord3(value) {
|
|
144
175
|
return typeof value === "object" && value !== null;
|
|
145
176
|
}
|
|
146
177
|
function isServicemeErrorCode(value) {
|
|
147
178
|
return typeof value === "string" && SERVICEME_ERROR_CODES.includes(value);
|
|
148
179
|
}
|
|
149
180
|
function isServicemeErrorDetails(value) {
|
|
150
|
-
if (!
|
|
181
|
+
if (!isRecord3(value)) {
|
|
151
182
|
return false;
|
|
152
183
|
}
|
|
153
184
|
return isServicemeErrorCode(value.code) && typeof value.message === "string" && typeof value.retryable === "boolean";
|
|
@@ -187,7 +218,7 @@ function normalizeServicemeError(error, fallbackCode = "internal_error") {
|
|
|
187
218
|
if (isServicemeErrorDetails(error)) {
|
|
188
219
|
return error;
|
|
189
220
|
}
|
|
190
|
-
if (
|
|
221
|
+
if (isRecord3(error)) {
|
|
191
222
|
const code = isServicemeErrorCode(error.code) ? error.code : fallbackCode;
|
|
192
223
|
const message = typeof error.message === "string" ? error.message : "Unexpected serviceme error.";
|
|
193
224
|
const retryable = typeof error.retryable === "boolean" ? error.retryable : isRetryableErrorCode(code);
|
|
@@ -213,7 +244,1038 @@ function normalizeServicemeError(error, fallbackCode = "internal_error") {
|
|
|
213
244
|
}
|
|
214
245
|
|
|
215
246
|
// src/version.ts
|
|
216
|
-
var SERVICEME_CLI_VERSION = "
|
|
247
|
+
var SERVICEME_CLI_VERSION = "2.0.0";
|
|
248
|
+
|
|
249
|
+
// src/bridge/CopilotContentBridgeHandler.ts
|
|
250
|
+
var fsp = __toESM(require("fs/promises"));
|
|
251
|
+
var path = __toESM(require("path"));
|
|
252
|
+
var import_devtools_core = require("@serviceme/devtools-core");
|
|
253
|
+
var CopilotContentBridgeHandler = class {
|
|
254
|
+
constructor(opts) {
|
|
255
|
+
this.repoManager = new import_devtools_core.RepoManager({
|
|
256
|
+
store: opts.store,
|
|
257
|
+
gitClient: opts.gitClient,
|
|
258
|
+
skipClone: opts.skipClone ?? false
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
/** Ensure every declared repository is checked out at its pinned commit. */
|
|
262
|
+
async ensureRepositories(manifest) {
|
|
263
|
+
const sources = /* @__PURE__ */ new Map();
|
|
264
|
+
for (const source of (0, import_devtools_core.getWorkspaceManifestSources)(
|
|
265
|
+
manifest ?? { version: 2, sources: [], plugins: [] }
|
|
266
|
+
)) {
|
|
267
|
+
if (source.type === "catalog") {
|
|
268
|
+
const localPath = path.resolve((0, import_devtools_core.getReposDir)(), source.id);
|
|
269
|
+
const stat3 = await fsp.stat(localPath).catch(() => null);
|
|
270
|
+
sources.set(
|
|
271
|
+
source.id,
|
|
272
|
+
stat3?.isDirectory() ? { ready: true } : {
|
|
273
|
+
ready: false,
|
|
274
|
+
error: "Source not materialized under ~/.serviceme/repos"
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
await this.repoManager.ensureAtCommit({
|
|
281
|
+
repository: {
|
|
282
|
+
id: source.id,
|
|
283
|
+
url: source.url,
|
|
284
|
+
useProxy: true
|
|
285
|
+
},
|
|
286
|
+
commit: source.commit
|
|
287
|
+
});
|
|
288
|
+
sources.set(source.id, { ready: true });
|
|
289
|
+
} catch (error) {
|
|
290
|
+
sources.set(source.id, {
|
|
291
|
+
ready: false,
|
|
292
|
+
error: error instanceof Error ? error.message : String(error)
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return sources;
|
|
297
|
+
}
|
|
298
|
+
async reconcile(workspaceDir) {
|
|
299
|
+
const reconciler = await this.makeReconciler(workspaceDir);
|
|
300
|
+
return reconciler.reconcile();
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Reconciler with machine-local disable marks applied: identities
|
|
304
|
+
* disabled for THIS workspace behave as if undeclared (links are
|
|
305
|
+
* removed and never resurrected) while the manifest stays intact.
|
|
306
|
+
*/
|
|
307
|
+
async makeReconciler(workspaceDir) {
|
|
308
|
+
const disabledStore = new import_devtools_core.DisabledContentStore();
|
|
309
|
+
const marks = await disabledStore.list();
|
|
310
|
+
const matches = (identity) => {
|
|
311
|
+
const segments = identity.split("::");
|
|
312
|
+
if (segments.length < 3) return false;
|
|
313
|
+
const [repoId, pluginId, kindName] = segments;
|
|
314
|
+
return marks.some(
|
|
315
|
+
(mark) => mark.scope === "workspace" && mark.workspaceDir === workspaceDir && mark.repoId === repoId && mark.name === pluginId && kindName === mark.kind
|
|
316
|
+
);
|
|
317
|
+
};
|
|
318
|
+
return new import_devtools_core.WorkspaceCopilotContentReconciler({
|
|
319
|
+
workspaceDir,
|
|
320
|
+
ensureRepository: (manifest) => this.ensureRepositories(manifest),
|
|
321
|
+
isDisabled: async (identity) => matches(identity)
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
/** `copilotContent.restore` — reconcile declared content now. */
|
|
325
|
+
async restore(params) {
|
|
326
|
+
return this.reconcile(params.workspaceDir);
|
|
327
|
+
}
|
|
328
|
+
/** `copilotContent.status` — declaration + current status without mutations. */
|
|
329
|
+
async status(params) {
|
|
330
|
+
const manifest = await (0, import_devtools_core.loadWorkspaceCopilotManifest)(params.workspaceDir);
|
|
331
|
+
if (!manifest) {
|
|
332
|
+
return { changed: false, entries: [] };
|
|
333
|
+
}
|
|
334
|
+
return this.reconcile(params.workspaceDir);
|
|
335
|
+
}
|
|
336
|
+
/** `copilotContent.approve` — record local approval, then re-reconcile. */
|
|
337
|
+
async approve(params) {
|
|
338
|
+
const reconciler = await this.makeReconciler(params.workspaceDir);
|
|
339
|
+
return reconciler.approve({ identities: params.identities });
|
|
340
|
+
}
|
|
341
|
+
/** `copilotContent.migrateLegacy` — report legacy ~/.agents links for migration. */
|
|
342
|
+
async migrateLegacy(params) {
|
|
343
|
+
const manifest = await (0, import_devtools_core.loadWorkspaceCopilotManifest)(params.workspaceDir);
|
|
344
|
+
if (!manifest) {
|
|
345
|
+
return { entries: [] };
|
|
346
|
+
}
|
|
347
|
+
const plan = await (0, import_devtools_core.resolveWorkspaceContentPlan)({
|
|
348
|
+
manifest,
|
|
349
|
+
reposDir: (0, import_devtools_core.getReposDir)()
|
|
350
|
+
});
|
|
351
|
+
const materializer = new import_devtools_core.CopilotLinkMaterializer();
|
|
352
|
+
const entries = [];
|
|
353
|
+
for (const entry of plan.entries) {
|
|
354
|
+
const legacy = await materializer.inspectLegacyUserLink({
|
|
355
|
+
homeDir: (0, import_devtools_core.getServicemeHome)(),
|
|
356
|
+
entry
|
|
357
|
+
});
|
|
358
|
+
entries.push({
|
|
359
|
+
identity: legacy.identity,
|
|
360
|
+
status: legacy.status,
|
|
361
|
+
message: legacy.message
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
return { entries };
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* `copilotContent.integrationStatus` — read-only diagnostics for the
|
|
368
|
+
* machine-local MCP / hook integrations of a declared workspace.
|
|
369
|
+
*
|
|
370
|
+
* The state store provides the declared identities and approval flags;
|
|
371
|
+
* the two generated config files and their `_serviceme` ownership maps
|
|
372
|
+
* prove which names are actually present on this machine. This method
|
|
373
|
+
* never mutates state or configuration.
|
|
374
|
+
*/
|
|
375
|
+
async integrationStatus(params) {
|
|
376
|
+
const state = await new import_devtools_core.WorkspaceContentStateStore({
|
|
377
|
+
workspaceDir: params.workspaceDir
|
|
378
|
+
}).read();
|
|
379
|
+
const mcp = await readOwnershipConfig(
|
|
380
|
+
path.join(params.workspaceDir, ".vscode", "mcp.serviceme.json")
|
|
381
|
+
);
|
|
382
|
+
const hooks = await readOwnershipConfig(
|
|
383
|
+
path.join(params.workspaceDir, ".vscode", "hooks.serviceme.json")
|
|
384
|
+
);
|
|
385
|
+
const integrations = [];
|
|
386
|
+
for (const entry of state.entries) {
|
|
387
|
+
if (entry.kind !== "mcp" && entry.kind !== "hook") continue;
|
|
388
|
+
const config = entry.kind === "mcp" ? mcp : hooks;
|
|
389
|
+
const owned = config.ownership[entry.identity] ?? [];
|
|
390
|
+
const active = config.exists && owned.length > 0;
|
|
391
|
+
const status = active ? "active" : entry.approved ? "missing_local_configuration" : "pending_approval";
|
|
392
|
+
integrations.push({
|
|
393
|
+
identity: entry.identity,
|
|
394
|
+
kind: entry.kind,
|
|
395
|
+
configPath: config.configPath,
|
|
396
|
+
names: owned,
|
|
397
|
+
status
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return { integrations };
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
async function readOwnershipConfig(configPath) {
|
|
404
|
+
try {
|
|
405
|
+
const raw = await fsp.readFile(configPath, "utf8");
|
|
406
|
+
const parsed = JSON.parse(raw);
|
|
407
|
+
const ownershipNode = parsed._serviceme;
|
|
408
|
+
const ownership = ownershipNode && typeof ownershipNode === "object" ? normalizeOwnership(ownershipNode) ?? {} : {};
|
|
409
|
+
return { exists: true, configPath, ownership };
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (error.code === "ENOENT") {
|
|
412
|
+
return { exists: false, configPath, ownership: {} };
|
|
413
|
+
}
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function normalizeOwnership(node) {
|
|
418
|
+
if (node === null || typeof node !== "object" || Array.isArray(node)) {
|
|
419
|
+
return void 0;
|
|
420
|
+
}
|
|
421
|
+
const result = {};
|
|
422
|
+
for (const [identity, names] of Object.entries(node)) {
|
|
423
|
+
if (Array.isArray(names) && names.every((name) => typeof name === "string")) {
|
|
424
|
+
result[identity] = names;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/bridge/CopilotCustomizationsBridgeHandler.ts
|
|
431
|
+
var fsp2 = __toESM(require("fs/promises"));
|
|
432
|
+
var path2 = __toESM(require("path"));
|
|
433
|
+
var import_devtools_core2 = require("@serviceme/devtools-core");
|
|
434
|
+
var import_skill_linker = require("@serviceme/devtools-core/skill-linker");
|
|
435
|
+
function createCopilotCustomizationsProductionDeps(options) {
|
|
436
|
+
return {
|
|
437
|
+
snapshot: async (params) => {
|
|
438
|
+
if (params.scope === "personal") {
|
|
439
|
+
return createFullPersonalSnapshot(options, await options.readers.listPersonalLinks());
|
|
440
|
+
}
|
|
441
|
+
return createWorkspaceSnapshot(
|
|
442
|
+
options.repoDisplayName,
|
|
443
|
+
options.readers,
|
|
444
|
+
params.workspaceDir ?? process.cwd()
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
var CopilotCustomizationsBridgeHandler = class {
|
|
450
|
+
constructor(deps) {
|
|
451
|
+
if ("snapshot" in deps && typeof deps.snapshot === "function") {
|
|
452
|
+
this.snapshot = deps.snapshot;
|
|
453
|
+
this.listAvailablePackages = deps.listAvailablePackages;
|
|
454
|
+
this.mutations = createUnimplementedMutations(
|
|
455
|
+
"Injected snapshot deps do not support package mutations"
|
|
456
|
+
);
|
|
457
|
+
} else if ("store" in deps && deps.store !== void 0) {
|
|
458
|
+
this.snapshot = createCopilotCustomizationsProductionDeps({
|
|
459
|
+
repoDisplayName: (repoId) => deps.store.get(repoId)?.name,
|
|
460
|
+
readers: createDefaultProductionReaders(),
|
|
461
|
+
personal: createDefaultPersonalSnapshotInput(deps.store)
|
|
462
|
+
}).snapshot;
|
|
463
|
+
this.mutations = createProductionMutations(deps.store);
|
|
464
|
+
this.listAvailablePackages = createAvailablePackagesEnumerator(deps.store);
|
|
465
|
+
} else {
|
|
466
|
+
this.snapshot = async () => emptySnapshot();
|
|
467
|
+
this.mutations = createUnimplementedMutations(
|
|
468
|
+
"Package mutations require production dependencies"
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async list(params) {
|
|
473
|
+
const snapshot = await this.snapshot(params);
|
|
474
|
+
const shared = snapshot.workspaceManifest !== void 0 ? { workspaceManifest: snapshot.workspaceManifest } : {};
|
|
475
|
+
const availablePackages = await this.listAvailablePackages?.(params, shared).catch(
|
|
476
|
+
() => void 0
|
|
477
|
+
);
|
|
478
|
+
return {
|
|
479
|
+
view: (0, import_devtools_core2.buildCopilotCustomizationView)({
|
|
480
|
+
scope: params.scope,
|
|
481
|
+
sources: snapshot.sources.map(toPublicSource),
|
|
482
|
+
packages: snapshot.packages.map(toPublicPackage),
|
|
483
|
+
installations: snapshot.installations.map(toPublicInstallation),
|
|
484
|
+
statesByArtifactId: toPublicStates(snapshot.statesByArtifactId),
|
|
485
|
+
legacyCount: snapshot.legacyCount,
|
|
486
|
+
generatedAt: snapshot.generatedAt
|
|
487
|
+
}),
|
|
488
|
+
...availablePackages ? { availablePackages } : {}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
async install(params) {
|
|
492
|
+
return this.mutations.install(params);
|
|
493
|
+
}
|
|
494
|
+
async update(params) {
|
|
495
|
+
return this.mutations.update(params);
|
|
496
|
+
}
|
|
497
|
+
async uninstall(params) {
|
|
498
|
+
const result = await this.mutations.uninstall(params);
|
|
499
|
+
try {
|
|
500
|
+
const registrar = new import_devtools_core2.CopilotPluginRegistrar({
|
|
501
|
+
copilotDir: path2.join((0, import_devtools_core2.getHomeDir)(), ".copilot")
|
|
502
|
+
});
|
|
503
|
+
await registrar.unregister({
|
|
504
|
+
registrationId: params.packageId.replace("::", ":"),
|
|
505
|
+
scope: params.scope
|
|
506
|
+
});
|
|
507
|
+
const otherScope = params.scope === "personal" ? "workspace" : "personal";
|
|
508
|
+
await registrar.unregister({
|
|
509
|
+
registrationId: params.packageId.replace("::", ":"),
|
|
510
|
+
scope: otherScope
|
|
511
|
+
});
|
|
512
|
+
} catch {
|
|
513
|
+
}
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
async move(params) {
|
|
517
|
+
return this.mutations.move(params);
|
|
518
|
+
}
|
|
519
|
+
async legacyPreview(params) {
|
|
520
|
+
return this.mutations.legacyPreview(params);
|
|
521
|
+
}
|
|
522
|
+
async legacyMigrate(params) {
|
|
523
|
+
return this.mutations.legacyMigrate(params);
|
|
524
|
+
}
|
|
525
|
+
async sources(params) {
|
|
526
|
+
return this.mutations.sources(params);
|
|
527
|
+
}
|
|
528
|
+
async removeSource(params) {
|
|
529
|
+
return this.mutations.removeSource(params);
|
|
530
|
+
}
|
|
531
|
+
async previewUpdate(params) {
|
|
532
|
+
return this.mutations.previewUpdate(params);
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
function createDefaultPersonalSnapshotInput(store) {
|
|
536
|
+
return {
|
|
537
|
+
// Capabilities are intentionally omitted: createFullPersonalSnapshot
|
|
538
|
+
// falls back to the real default host-capability provider, which
|
|
539
|
+
// claims the verified ~/.copilot agent/skill targets. Pinning an
|
|
540
|
+
// empty personalTargets map here regressed every link artifact to
|
|
541
|
+
// unsupported. The explicit package resolver is the part Task 8
|
|
542
|
+
// flagged as silently missing.
|
|
543
|
+
resolvePersonalPackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store)
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
async function resolvePersonalPackageFromRepos(packageId, store) {
|
|
547
|
+
const [sourceId, pluginId] = packageId.split("::");
|
|
548
|
+
if (!sourceId || !pluginId) return [];
|
|
549
|
+
const repoRoot = path2.resolve((0, import_devtools_core2.getReposDir)(), sourceId);
|
|
550
|
+
const stat3 = await fsp2.stat(repoRoot).catch(() => null);
|
|
551
|
+
if (!stat3?.isDirectory()) return [];
|
|
552
|
+
const repo = store.get(sourceId);
|
|
553
|
+
const manifest = {
|
|
554
|
+
version: 1,
|
|
555
|
+
repositories: [
|
|
556
|
+
{
|
|
557
|
+
id: sourceId,
|
|
558
|
+
url: repo?.url ?? `https://serviceme.local/catalog/${encodeURIComponent(sourceId)}`,
|
|
559
|
+
commit: repo?.lastSyncCommitSha ?? "".padEnd(40, "0")
|
|
560
|
+
}
|
|
561
|
+
],
|
|
562
|
+
plugins: [
|
|
563
|
+
{
|
|
564
|
+
repository: sourceId,
|
|
565
|
+
id: pluginId,
|
|
566
|
+
artifacts: {
|
|
567
|
+
agent: true,
|
|
568
|
+
skill: true,
|
|
569
|
+
instruction: true,
|
|
570
|
+
prompt: true,
|
|
571
|
+
hook: true,
|
|
572
|
+
mcp: true
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
]
|
|
576
|
+
};
|
|
577
|
+
const resolved = await (0, import_devtools_core2.resolveWorkspaceContentPlan)({
|
|
578
|
+
manifest,
|
|
579
|
+
reposDir: (0, import_devtools_core2.getReposDir)()
|
|
580
|
+
}).catch(() => ({ entries: [] }));
|
|
581
|
+
return resolved.entries;
|
|
582
|
+
}
|
|
583
|
+
function createAvailablePackagesEnumerator(store) {
|
|
584
|
+
const catalog = (0, import_devtools_core2.createPluginCatalogService)();
|
|
585
|
+
const personalStore = new import_devtools_core2.PersonalInstallationStore({});
|
|
586
|
+
const pluginRegistrar = new import_devtools_core2.CopilotPluginRegistrar({
|
|
587
|
+
copilotDir: path2.join((0, import_devtools_core2.getHomeDir)(), ".copilot")
|
|
588
|
+
});
|
|
589
|
+
return async (params, shared) => {
|
|
590
|
+
const repos = store.list().filter((repo) => repo.enabled).map((repo) => ({ id: repo.id }));
|
|
591
|
+
if (repos.length === 0) return [];
|
|
592
|
+
const catalogPackages = await catalog.list({
|
|
593
|
+
reposDir: (0, import_devtools_core2.getReposDir)(),
|
|
594
|
+
repos
|
|
595
|
+
});
|
|
596
|
+
const [workspaceManifest, personalIntent, registrations] = await Promise.all([
|
|
597
|
+
// Shared read from the same list() call when provided; only a
|
|
598
|
+
// standalone enumerator invocation (personal scope, tests,
|
|
599
|
+
// direct calls) hits the disk here.
|
|
600
|
+
shared?.workspaceManifest !== void 0 ? Promise.resolve(shared.workspaceManifest) : params.workspaceDir ? (0, import_devtools_core2.loadWorkspaceCopilotManifest)(params.workspaceDir).catch(() => void 0) : Promise.resolve(void 0),
|
|
601
|
+
personalStore.read().catch(() => void 0),
|
|
602
|
+
// Whole-package registry — prune-on-list keeps it clean.
|
|
603
|
+
pluginRegistrar.list({}).catch(() => [])
|
|
604
|
+
]);
|
|
605
|
+
const manifest = workspaceManifest ?? {
|
|
606
|
+
version: 1,
|
|
607
|
+
repositories: [],
|
|
608
|
+
plugins: []
|
|
609
|
+
};
|
|
610
|
+
const workspaceInstalled = new Set(
|
|
611
|
+
manifest.plugins.map(
|
|
612
|
+
(plugin) => `${(0, import_devtools_core2.getWorkspaceManifestPluginSourceId)(manifest, plugin)}::${plugin.id}`
|
|
613
|
+
)
|
|
614
|
+
);
|
|
615
|
+
const personalInstalled = new Set(
|
|
616
|
+
(personalIntent?.installations ?? []).filter((installation) => installation.scope === "personal").map((installation) => installation.packageId)
|
|
617
|
+
);
|
|
618
|
+
const registeredScopes = /* @__PURE__ */ new Map();
|
|
619
|
+
for (const reg of registrations) {
|
|
620
|
+
const packageId = `${reg.repoId}::${reg.pluginId}`;
|
|
621
|
+
const scopes = registeredScopes.get(packageId) ?? /* @__PURE__ */ new Set();
|
|
622
|
+
for (const scope of reg.scopes) scopes.add(scope);
|
|
623
|
+
registeredScopes.set(packageId, scopes);
|
|
624
|
+
}
|
|
625
|
+
return catalogPackages.map((pkg) => {
|
|
626
|
+
const installed = registeredScopes.get(pkg.packageId) !== void 0 || workspaceInstalled.has(pkg.packageId) || personalInstalled.has(pkg.packageId);
|
|
627
|
+
return {
|
|
628
|
+
...pkg,
|
|
629
|
+
installedScopes: installed ? ["personal"] : []
|
|
630
|
+
};
|
|
631
|
+
});
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function createProductionMutations(store) {
|
|
635
|
+
let servicePromise;
|
|
636
|
+
let sourceCatalogPromise;
|
|
637
|
+
const service = () => servicePromise ??= createPackageInstallationService(store);
|
|
638
|
+
const sourceCatalog = () => sourceCatalogPromise ??= createSourceCatalogService(store);
|
|
639
|
+
return {
|
|
640
|
+
install: async (params) => toResult(await (await service()).install(params)),
|
|
641
|
+
update: async (params) => toResult(await (await service()).update(params)),
|
|
642
|
+
uninstall: async (params) => toResult(await (await service()).uninstall(params)),
|
|
643
|
+
move: async (params) => toResult(await (await service()).move(params)),
|
|
644
|
+
legacyPreview: async () => {
|
|
645
|
+
const preview = await (await service()).previewLegacy();
|
|
646
|
+
return {
|
|
647
|
+
sourceLabel: preview.sourceLabel,
|
|
648
|
+
count: preview.entries.length,
|
|
649
|
+
artifacts: preview.entries.map((entry) => ({
|
|
650
|
+
id: entry.artifactId,
|
|
651
|
+
packageId: "legacy",
|
|
652
|
+
kind: entry.kind,
|
|
653
|
+
displayName: entry.name,
|
|
654
|
+
installStrategy: "link",
|
|
655
|
+
risk: "none"
|
|
656
|
+
}))
|
|
657
|
+
};
|
|
658
|
+
},
|
|
659
|
+
legacyMigrate: async (params) => toResult(await (await service()).migrateLegacy(params)),
|
|
660
|
+
sources: async (params) => {
|
|
661
|
+
const records = await (await sourceCatalog()).listSources({
|
|
662
|
+
scope: params.scope,
|
|
663
|
+
...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
|
|
664
|
+
});
|
|
665
|
+
return {
|
|
666
|
+
sources: records.map((record) => ({
|
|
667
|
+
id: record.id,
|
|
668
|
+
type: record.type,
|
|
669
|
+
displayName: record.displayName,
|
|
670
|
+
updateCapability: record.updateCapability,
|
|
671
|
+
available: record.available,
|
|
672
|
+
declaredIn: [...record.declaredIn]
|
|
673
|
+
}))
|
|
674
|
+
};
|
|
675
|
+
},
|
|
676
|
+
removeSource: async (params) => {
|
|
677
|
+
await (await sourceCatalog()).removeSource(params.sourceId, params.workspaceDir);
|
|
678
|
+
return { removed: true };
|
|
679
|
+
},
|
|
680
|
+
previewUpdate: async (params) => ({
|
|
681
|
+
preview: await (await sourceCatalog()).previewUpdate(params)
|
|
682
|
+
})
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
async function createSourceCatalogService(store) {
|
|
686
|
+
const personalStore = new import_devtools_core2.PersonalInstallationStore({});
|
|
687
|
+
const loadManifest = async (workspaceDir) => {
|
|
688
|
+
return (0, import_devtools_core2.loadWorkspaceCopilotManifest)(workspaceDir).catch(() => void 0);
|
|
689
|
+
};
|
|
690
|
+
return new import_devtools_core2.CopilotSourceCatalogService({
|
|
691
|
+
personalStore,
|
|
692
|
+
// The source manager is the single repository management surface;
|
|
693
|
+
// surface every ReposStore entry (built-in defaults + user git
|
|
694
|
+
// repos) so they can be synced / toggled / edited from there even
|
|
695
|
+
// when no installation or workspace declaration references them.
|
|
696
|
+
// Read per call: repos.json can change under the long-lived bridge.
|
|
697
|
+
listStoreSources: async () => store.list().map((repo) => ({
|
|
698
|
+
id: repo.id,
|
|
699
|
+
name: repo.name,
|
|
700
|
+
enabled: repo.enabled
|
|
701
|
+
})),
|
|
702
|
+
resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store),
|
|
703
|
+
resolveActualRevision: async (packageId) => {
|
|
704
|
+
const [sourceId] = packageId.split("::");
|
|
705
|
+
if (!sourceId) return void 0;
|
|
706
|
+
const repo = store.get(sourceId);
|
|
707
|
+
if (repo?.lastSyncCommitSha) return repo.lastSyncCommitSha;
|
|
708
|
+
const repoDir = path2.resolve((0, import_devtools_core2.getReposDir)(), sourceId);
|
|
709
|
+
const head = await new import_devtools_core2.GitClient({ serverProxyBase: "" }).revParseHead(repoDir).catch(() => void 0);
|
|
710
|
+
return head;
|
|
711
|
+
},
|
|
712
|
+
listWorkspaceSources: async (workspaceDir) => {
|
|
713
|
+
const manifest = await loadManifest(workspaceDir);
|
|
714
|
+
if (!manifest) return [];
|
|
715
|
+
return (0, import_devtools_core2.getWorkspaceManifestSources)(manifest).map((source) => source.id);
|
|
716
|
+
},
|
|
717
|
+
readWorkspaceInstallations: async (workspaceDir) => {
|
|
718
|
+
const manifest = await loadManifest(workspaceDir);
|
|
719
|
+
if (!manifest) return [];
|
|
720
|
+
return manifest.plugins.map((plugin) => {
|
|
721
|
+
const sourceId = (0, import_devtools_core2.getWorkspaceManifestPluginSourceId)(manifest, plugin);
|
|
722
|
+
const source = (0, import_devtools_core2.getWorkspaceManifestSources)(manifest).find((s) => s.id === sourceId);
|
|
723
|
+
return {
|
|
724
|
+
packageId: `${sourceId}::${plugin.id}`,
|
|
725
|
+
scope: "workspace",
|
|
726
|
+
selectedArtifactIds: Object.entries(plugin.artifacts).filter(([, enabled]) => enabled !== false).map(([kind]) => `${sourceId}::${plugin.id}::${kind}:`),
|
|
727
|
+
pinnedVersion: source?.type === "git" ? source.commit : void 0
|
|
728
|
+
};
|
|
729
|
+
});
|
|
730
|
+
},
|
|
731
|
+
artifactSelected: (marker, artifactId) => artifactId.startsWith(marker),
|
|
732
|
+
ensureCatalogSource: async (sourceId) => {
|
|
733
|
+
const mgr = new import_devtools_core2.RepoManager({
|
|
734
|
+
store,
|
|
735
|
+
gitClient: new import_devtools_core2.GitClient({ serverProxyBase: "" })
|
|
736
|
+
});
|
|
737
|
+
return mgr.ensureCatalogSource({
|
|
738
|
+
sourceId,
|
|
739
|
+
provider: {
|
|
740
|
+
// Placeholder staging: the marketplace catalog client is
|
|
741
|
+
// not yet wired to per-source payloads, so the first
|
|
742
|
+
// materialization establishes the directory contract.
|
|
743
|
+
// Real payload extraction lands with the catalog client.
|
|
744
|
+
materialize: async (targetDir) => {
|
|
745
|
+
await fsp2.mkdir(targetDir, { recursive: true });
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
},
|
|
750
|
+
isCatalogSource: async (sourceId, workspaceDir) => {
|
|
751
|
+
if (!workspaceDir) return false;
|
|
752
|
+
const declared = await loadManifest(workspaceDir);
|
|
753
|
+
const source = declared ? (0, import_devtools_core2.getWorkspaceManifestSources)(declared).find((s) => s.id === sourceId) : void 0;
|
|
754
|
+
return source?.type === "catalog";
|
|
755
|
+
},
|
|
756
|
+
hasCatalogPayload: async (localPath) => {
|
|
757
|
+
const pluginsDir = path2.join(localPath, "plugins");
|
|
758
|
+
const entries = await fsp2.readdir(pluginsDir).catch(() => []);
|
|
759
|
+
if (entries.length === 0) return false;
|
|
760
|
+
for (const entry of entries) {
|
|
761
|
+
const stat3 = await fsp2.stat(path2.join(pluginsDir, entry)).catch(() => null);
|
|
762
|
+
if (stat3?.isDirectory()) {
|
|
763
|
+
const manifest = await fsp2.stat(path2.join(pluginsDir, entry, "plugin.json")).catch(() => null);
|
|
764
|
+
if (manifest?.isFile()) return true;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
async function createPackageInstallationService(store) {
|
|
772
|
+
const personalStore = new import_devtools_core2.PersonalInstallationStore({});
|
|
773
|
+
const personalReconciler = new import_devtools_core2.PersonalCopilotContentReconciler({
|
|
774
|
+
capabilities: await (0, import_devtools_core2.createDefaultCopilotHostCapabilities)(),
|
|
775
|
+
resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store)
|
|
776
|
+
});
|
|
777
|
+
return new import_devtools_core2.PackageInstallationService({
|
|
778
|
+
personalStore,
|
|
779
|
+
personalReconciler,
|
|
780
|
+
userHomeDir: (0, import_devtools_core2.getHomeDir)(),
|
|
781
|
+
resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store),
|
|
782
|
+
resolveWorkspaceSource: async (sourceId) => {
|
|
783
|
+
const repo = store.get(sourceId);
|
|
784
|
+
if (!repo) return void 0;
|
|
785
|
+
const repoDir = path2.resolve((0, import_devtools_core2.getReposDir)(), sourceId);
|
|
786
|
+
const stat3 = await fsp2.stat(repoDir).catch(() => null);
|
|
787
|
+
if (!stat3?.isDirectory()) return void 0;
|
|
788
|
+
return {
|
|
789
|
+
id: sourceId,
|
|
790
|
+
url: repo.url,
|
|
791
|
+
commit: repo.lastSyncCommitSha ?? "0".repeat(40)
|
|
792
|
+
};
|
|
793
|
+
},
|
|
794
|
+
createWorkspaceReconciler: (workspaceDir) => new import_devtools_core2.WorkspaceCopilotContentReconciler({ workspaceDir }),
|
|
795
|
+
readWorkspaceView: async (workspaceDir) => {
|
|
796
|
+
const snapshot = await createWorkspaceSnapshot(
|
|
797
|
+
(repoId) => store.get(repoId)?.name,
|
|
798
|
+
createDefaultProductionReaders(),
|
|
799
|
+
workspaceDir
|
|
800
|
+
);
|
|
801
|
+
return (0, import_devtools_core2.buildCopilotCustomizationView)({
|
|
802
|
+
scope: "workspace",
|
|
803
|
+
sources: snapshot.sources,
|
|
804
|
+
packages: snapshot.packages,
|
|
805
|
+
installations: snapshot.installations,
|
|
806
|
+
statesByArtifactId: snapshot.statesByArtifactId,
|
|
807
|
+
generatedAt: snapshot.generatedAt
|
|
808
|
+
});
|
|
809
|
+
},
|
|
810
|
+
readWorkspaceInstallations: import_devtools_core2.readDeclaredWorkspaceInstallations
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
function toResult(view) {
|
|
814
|
+
return { view: toPublicView(view) };
|
|
815
|
+
}
|
|
816
|
+
function toPublicView(view) {
|
|
817
|
+
return {
|
|
818
|
+
scope: view.scope,
|
|
819
|
+
generatedAt: view.generatedAt,
|
|
820
|
+
sources: view.sources.map((source) => ({
|
|
821
|
+
...toPublicSource(source),
|
|
822
|
+
packages: source.packages.map((pkg) => toPublicPackageView(pkg))
|
|
823
|
+
})),
|
|
824
|
+
packages: view.packages.map((pkg) => ({
|
|
825
|
+
...toPublicPackageView(pkg)
|
|
826
|
+
})),
|
|
827
|
+
attention: view.attention.map(({ artifact, state }) => ({
|
|
828
|
+
artifact,
|
|
829
|
+
state
|
|
830
|
+
})),
|
|
831
|
+
summary: view.summary,
|
|
832
|
+
...view.legacyMigration ? { legacyMigration: view.legacyMigration } : {}
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
function toPublicPackageView(pkg) {
|
|
836
|
+
return {
|
|
837
|
+
definition: toPublicPackage(pkg.definition),
|
|
838
|
+
installation: toPublicInstallation(pkg.installation),
|
|
839
|
+
artifacts: pkg.artifacts.map(({ artifact, state }) => ({
|
|
840
|
+
artifact,
|
|
841
|
+
state
|
|
842
|
+
})),
|
|
843
|
+
selectedArtifactCount: pkg.selectedArtifactCount,
|
|
844
|
+
status: pkg.status
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
function createUnimplementedMutations(reason) {
|
|
848
|
+
const reject = async () => {
|
|
849
|
+
throw new Error(reason);
|
|
850
|
+
};
|
|
851
|
+
return {
|
|
852
|
+
install: reject,
|
|
853
|
+
update: reject,
|
|
854
|
+
uninstall: reject,
|
|
855
|
+
move: reject,
|
|
856
|
+
legacyPreview: reject,
|
|
857
|
+
legacyMigrate: reject,
|
|
858
|
+
sources: reject,
|
|
859
|
+
removeSource: reject,
|
|
860
|
+
previewUpdate: reject
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
async function mergeRegistrarPackages(sources, packages, installations, statesByArtifactId) {
|
|
864
|
+
const registrar = new import_devtools_core2.CopilotPluginRegistrar({
|
|
865
|
+
copilotDir: path2.join((0, import_devtools_core2.getHomeDir)(), ".copilot")
|
|
866
|
+
});
|
|
867
|
+
const registrations = await registrar.list({}).catch(() => []);
|
|
868
|
+
if (registrations.length === 0) return;
|
|
869
|
+
const asMap = packages instanceof Map ? packages : new Map(packages.map((p) => [p.id, p]));
|
|
870
|
+
const knownIds = new Set(asMap.keys());
|
|
871
|
+
const pushed = packages instanceof Map ? void 0 : packages;
|
|
872
|
+
for (const reg of registrations) {
|
|
873
|
+
const packageId = `${reg.repoId}::${reg.pluginId}`;
|
|
874
|
+
if (knownIds.has(packageId)) continue;
|
|
875
|
+
const pluginJsonPath = path2.join(reg.pluginDir, "plugin.json");
|
|
876
|
+
let definition;
|
|
877
|
+
try {
|
|
878
|
+
const raw = JSON.parse(await fsp2.readFile(pluginJsonPath, "utf8"));
|
|
879
|
+
const repoRoot = path2.basename(path2.dirname(reg.pluginDir)) === "plugins" ? path2.dirname(path2.dirname(reg.pluginDir)) : reg.pluginDir;
|
|
880
|
+
const entries = await (0, import_devtools_core2.resolvePluginEntriesLenient)({
|
|
881
|
+
repoRoot,
|
|
882
|
+
repositoryId: reg.repoId,
|
|
883
|
+
pluginId: reg.pluginId,
|
|
884
|
+
manifest: {
|
|
885
|
+
name: typeof raw.name === "string" && raw.name.trim() !== "" ? raw.name : reg.pluginId,
|
|
886
|
+
...typeof raw.description === "string" ? { description: raw.description } : {},
|
|
887
|
+
...typeof raw.version === "string" ? { version: raw.version } : {},
|
|
888
|
+
extensions: raw.extensions ?? {}
|
|
889
|
+
}
|
|
890
|
+
}).catch(() => []);
|
|
891
|
+
const artifacts = entries.map(import_devtools_core2.toArtifactSummary);
|
|
892
|
+
if (artifacts.length > 0) {
|
|
893
|
+
definition = {
|
|
894
|
+
id: packageId,
|
|
895
|
+
sourceId: reg.repoId,
|
|
896
|
+
displayName: typeof raw.name === "string" && raw.name || reg.displayName || reg.pluginId,
|
|
897
|
+
...typeof raw.description === "string" ? { description: raw.description } : {},
|
|
898
|
+
...typeof raw.version === "string" ? { version: raw.version } : {},
|
|
899
|
+
artifacts,
|
|
900
|
+
wholePackage: true
|
|
901
|
+
};
|
|
902
|
+
for (const artifact of artifacts) {
|
|
903
|
+
statesByArtifactId[artifact.id] = {
|
|
904
|
+
intent: "selected",
|
|
905
|
+
health: "healthy",
|
|
906
|
+
gate: "ready"
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
} else {
|
|
910
|
+
const artifact = {
|
|
911
|
+
id: `${packageId}::package:${reg.pluginId}`,
|
|
912
|
+
packageId,
|
|
913
|
+
kind: "skill",
|
|
914
|
+
displayName: reg.pluginId,
|
|
915
|
+
installStrategy: "link",
|
|
916
|
+
risk: "none"
|
|
917
|
+
};
|
|
918
|
+
definition = {
|
|
919
|
+
id: packageId,
|
|
920
|
+
sourceId: reg.repoId,
|
|
921
|
+
displayName: typeof raw.name === "string" && raw.name || reg.displayName || reg.pluginId,
|
|
922
|
+
...typeof raw.description === "string" ? { description: raw.description } : {},
|
|
923
|
+
...typeof raw.version === "string" ? { version: raw.version } : {},
|
|
924
|
+
artifacts: [artifact],
|
|
925
|
+
wholePackage: true
|
|
926
|
+
};
|
|
927
|
+
statesByArtifactId[artifact.id] = {
|
|
928
|
+
intent: "selected",
|
|
929
|
+
health: "healthy",
|
|
930
|
+
gate: "ready"
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
} catch {
|
|
934
|
+
definition = {
|
|
935
|
+
id: packageId,
|
|
936
|
+
sourceId: reg.repoId,
|
|
937
|
+
displayName: reg.displayName || reg.pluginId,
|
|
938
|
+
...reg.version !== void 0 ? { version: reg.version } : {},
|
|
939
|
+
artifacts: [],
|
|
940
|
+
wholePackage: true
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
if (pushed) {
|
|
944
|
+
pushed.push(definition);
|
|
945
|
+
} else {
|
|
946
|
+
asMap.set(packageId, definition);
|
|
947
|
+
}
|
|
948
|
+
if (!sources.some((source) => source.id === reg.repoId)) {
|
|
949
|
+
sources.push({
|
|
950
|
+
id: reg.repoId,
|
|
951
|
+
type: "git",
|
|
952
|
+
displayName: reg.repoId,
|
|
953
|
+
updateCapability: "pinned"
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
installations.push({
|
|
957
|
+
packageId,
|
|
958
|
+
scope: "personal",
|
|
959
|
+
selectedArtifactIds: definition.artifacts.map((artifact) => artifact.id)
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
async function createWorkspaceSnapshot(repoDisplayName, readers, workspaceDir) {
|
|
964
|
+
const manifest = await readers.loadWorkspaceManifest(workspaceDir);
|
|
965
|
+
if (!manifest) return emptySnapshot();
|
|
966
|
+
const sources = (0, import_devtools_core2.getWorkspaceManifestSources)(manifest).map(
|
|
967
|
+
(source) => ({
|
|
968
|
+
id: source.id,
|
|
969
|
+
type: source.type === "catalog" ? "marketplace" : "git",
|
|
970
|
+
displayName: source.type === "catalog" ? source.catalogId : repoDisplayName(source.id) ?? source.id,
|
|
971
|
+
updateCapability: "pinned"
|
|
972
|
+
})
|
|
973
|
+
);
|
|
974
|
+
const state = await readers.readWorkspaceState(workspaceDir);
|
|
975
|
+
const packages = [];
|
|
976
|
+
const installations = [];
|
|
977
|
+
const statesByArtifactId = {};
|
|
978
|
+
const resolvedPlugins = await Promise.all(
|
|
979
|
+
manifest.plugins.map(async (plugin) => {
|
|
980
|
+
const sourceId = (0, import_devtools_core2.getWorkspaceManifestPluginSourceId)(manifest, plugin);
|
|
981
|
+
const packageId = `${sourceId}::${plugin.id}`;
|
|
982
|
+
let fallbackHealth;
|
|
983
|
+
let entries;
|
|
984
|
+
if (!await readers.isSourceAvailable(sourceId)) {
|
|
985
|
+
fallbackHealth = "source-unavailable";
|
|
986
|
+
entries = fallbackEntries(plugin, packageId);
|
|
987
|
+
} else {
|
|
988
|
+
try {
|
|
989
|
+
entries = await readers.resolveWorkspacePlugin({ manifest, plugin });
|
|
990
|
+
} catch {
|
|
991
|
+
fallbackHealth = "conflict";
|
|
992
|
+
entries = fallbackEntries(plugin, packageId);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
return { plugin, sourceId, packageId, entries, fallbackHealth };
|
|
996
|
+
})
|
|
997
|
+
);
|
|
998
|
+
for (const resolved of resolvedPlugins) {
|
|
999
|
+
const { plugin, sourceId, packageId, entries, fallbackHealth } = resolved;
|
|
1000
|
+
const artifacts = entries.map(import_devtools_core2.toArtifactSummary);
|
|
1001
|
+
packages.push({
|
|
1002
|
+
id: packageId,
|
|
1003
|
+
sourceId,
|
|
1004
|
+
displayName: entries[0]?.packageDisplayName ?? plugin.id,
|
|
1005
|
+
...entries[0]?.packageDescription ? { description: entries[0].packageDescription } : {},
|
|
1006
|
+
...entries[0]?.packageVersion ? { version: entries[0].packageVersion } : {},
|
|
1007
|
+
artifacts
|
|
1008
|
+
});
|
|
1009
|
+
installations.push({
|
|
1010
|
+
packageId,
|
|
1011
|
+
scope: "workspace",
|
|
1012
|
+
selectedArtifactIds: artifacts.map((artifact) => artifact.id)
|
|
1013
|
+
});
|
|
1014
|
+
for (const entry of entries) {
|
|
1015
|
+
const previous = state.entries.find((candidate) => candidate.identity === entry.artifactId);
|
|
1016
|
+
statesByArtifactId[entry.artifactId] = fallbackHealth ? { intent: "selected", health: fallbackHealth, gate: "ready" } : toArtifactState(entry, previous);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
await mergeRegistrarPackages(sources, packages, installations, statesByArtifactId);
|
|
1020
|
+
return { sources, packages, installations, statesByArtifactId, workspaceManifest: manifest };
|
|
1021
|
+
}
|
|
1022
|
+
async function createFullPersonalSnapshot(options, legacyLinks) {
|
|
1023
|
+
const capabilities = options.personal?.capabilities ?? await (0, import_devtools_core2.createDefaultCopilotHostCapabilities)();
|
|
1024
|
+
const homeDir = options.personal?.homeDir;
|
|
1025
|
+
const store = new import_devtools_core2.PersonalInstallationStore(homeDir !== void 0 ? { homeDir } : {});
|
|
1026
|
+
const reconciler = new import_devtools_core2.PersonalCopilotContentReconciler({
|
|
1027
|
+
capabilities,
|
|
1028
|
+
...homeDir ? { homeDir } : {},
|
|
1029
|
+
...options.personal?.resolvePersonalPackage ? { resolvePackage: options.personal.resolvePersonalPackage } : {}
|
|
1030
|
+
});
|
|
1031
|
+
const intent = await store.read();
|
|
1032
|
+
const inspection = await reconciler.inspect();
|
|
1033
|
+
const sources = [];
|
|
1034
|
+
const sourceIds = /* @__PURE__ */ new Set();
|
|
1035
|
+
const packages = /* @__PURE__ */ new Map();
|
|
1036
|
+
const statesByArtifactId = {
|
|
1037
|
+
...inspection.statesByArtifactId
|
|
1038
|
+
};
|
|
1039
|
+
let legacyCount = 0;
|
|
1040
|
+
for (const link of legacyLinks) {
|
|
1041
|
+
if (link.legacy) legacyCount += 1;
|
|
1042
|
+
}
|
|
1043
|
+
for (const installation of intent.installations) {
|
|
1044
|
+
if (installation.scope !== "personal") continue;
|
|
1045
|
+
const resolved = options.personal ? await options.personal.resolvePersonalPackage(installation.packageId) : [];
|
|
1046
|
+
const artifacts = resolved.filter((entry) => installation.selectedArtifactIds.includes(entry.artifactId)).map(import_devtools_core2.toArtifactSummary);
|
|
1047
|
+
const [sourceId] = installation.packageId.split("::");
|
|
1048
|
+
if (sourceId && !sourceIds.has(sourceId)) {
|
|
1049
|
+
sourceIds.add(sourceId);
|
|
1050
|
+
sources.push({
|
|
1051
|
+
id: sourceId,
|
|
1052
|
+
type: "git",
|
|
1053
|
+
displayName: options.repoDisplayName(sourceId) ?? sourceId,
|
|
1054
|
+
updateCapability: "pinned"
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
if (!packages.has(installation.packageId) && artifacts.length > 0) {
|
|
1058
|
+
packages.set(installation.packageId, {
|
|
1059
|
+
id: installation.packageId,
|
|
1060
|
+
sourceId: sourceId ?? "personal-local",
|
|
1061
|
+
displayName: resolved[0]?.packageDisplayName ?? installation.packageId,
|
|
1062
|
+
...resolved[0]?.packageDescription ? { description: resolved[0].packageDescription } : {},
|
|
1063
|
+
...resolved[0]?.packageVersion ? { version: resolved[0].packageVersion } : {},
|
|
1064
|
+
artifacts
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
await mergeRegistrarPackages(sources, packages, intent.installations, statesByArtifactId);
|
|
1069
|
+
return {
|
|
1070
|
+
sources,
|
|
1071
|
+
packages: [...packages.values()],
|
|
1072
|
+
installations: intent.installations.filter((installation) => installation.scope === "personal"),
|
|
1073
|
+
statesByArtifactId,
|
|
1074
|
+
...legacyCount > 0 ? { legacyCount } : {}
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function fallbackEntries(plugin, packageId) {
|
|
1078
|
+
const kinds = Object.keys(plugin.artifacts);
|
|
1079
|
+
return kinds.filter((kind) => plugin.artifacts[kind] !== false).map((kind) => ({
|
|
1080
|
+
identity: `${packageId}::${kind}:${plugin.id}`,
|
|
1081
|
+
artifactId: `${packageId}::${kind}:${plugin.id}`,
|
|
1082
|
+
packageId,
|
|
1083
|
+
packageDisplayName: plugin.id,
|
|
1084
|
+
repositoryId: packageId.split("::")[0] ?? packageId,
|
|
1085
|
+
pluginId: plugin.id,
|
|
1086
|
+
kind,
|
|
1087
|
+
sourcePath: "",
|
|
1088
|
+
sourceIsFile: false,
|
|
1089
|
+
name: plugin.id,
|
|
1090
|
+
digest: "",
|
|
1091
|
+
requiresApproval: kind === "hook" || kind === "mcp"
|
|
1092
|
+
}));
|
|
1093
|
+
}
|
|
1094
|
+
function toArtifactState(entry, previous) {
|
|
1095
|
+
if (!previous) {
|
|
1096
|
+
return {
|
|
1097
|
+
intent: "selected",
|
|
1098
|
+
health: "missing",
|
|
1099
|
+
gate: entry.requiresApproval ? "approval-required" : "ready"
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
return {
|
|
1103
|
+
intent: "selected",
|
|
1104
|
+
health: previous.digest === entry.digest ? "healthy" : "drifted",
|
|
1105
|
+
gate: entry.requiresApproval && !previous.approved ? "approval-required" : "ready"
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
function createDefaultProductionReaders() {
|
|
1109
|
+
return {
|
|
1110
|
+
loadWorkspaceManifest: import_devtools_core2.loadWorkspaceCopilotManifest,
|
|
1111
|
+
readWorkspaceState: async (workspaceDir) => new import_devtools_core2.WorkspaceContentStateStore({ workspaceDir }).read(),
|
|
1112
|
+
isSourceAvailable: async (sourceId) => {
|
|
1113
|
+
const stat3 = await fsp2.stat(path2.join((0, import_devtools_core2.getReposDir)(), sourceId)).catch(() => void 0);
|
|
1114
|
+
return stat3?.isDirectory() === true;
|
|
1115
|
+
},
|
|
1116
|
+
resolveWorkspacePlugin: async ({ manifest, plugin }) => {
|
|
1117
|
+
const scopedManifest = manifest.version === 1 ? { ...manifest, plugins: [plugin] } : { ...manifest, plugins: [plugin] };
|
|
1118
|
+
return (await (0, import_devtools_core2.resolveWorkspaceContentPlan)({
|
|
1119
|
+
manifest: scopedManifest,
|
|
1120
|
+
reposDir: (0, import_devtools_core2.getReposDir)()
|
|
1121
|
+
})).entries;
|
|
1122
|
+
},
|
|
1123
|
+
listPersonalLinks: async () => [
|
|
1124
|
+
...(await (0, import_skill_linker.listLinkedSkills)("", "skill", "user")).map((link) => ({
|
|
1125
|
+
repoId: link.repoId,
|
|
1126
|
+
name: link.skillName,
|
|
1127
|
+
kind: "skill",
|
|
1128
|
+
legacy: link.linkPath.includes(`${path2.sep}.agents${path2.sep}`)
|
|
1129
|
+
})),
|
|
1130
|
+
...(await (0, import_skill_linker.listLinkedSkills)("", "agent", "user")).map((link) => ({
|
|
1131
|
+
repoId: link.repoId,
|
|
1132
|
+
name: link.skillName,
|
|
1133
|
+
kind: "agent",
|
|
1134
|
+
legacy: link.linkPath.includes(`${path2.sep}.agents${path2.sep}`)
|
|
1135
|
+
}))
|
|
1136
|
+
]
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
function emptySnapshot() {
|
|
1140
|
+
return {
|
|
1141
|
+
sources: [],
|
|
1142
|
+
packages: [],
|
|
1143
|
+
installations: [],
|
|
1144
|
+
statesByArtifactId: {}
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
function toPublicSource(source) {
|
|
1148
|
+
return {
|
|
1149
|
+
id: source.id,
|
|
1150
|
+
type: source.type,
|
|
1151
|
+
displayName: source.displayName,
|
|
1152
|
+
updateCapability: source.updateCapability
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
function toPublicPackage(pkg) {
|
|
1156
|
+
return {
|
|
1157
|
+
id: pkg.id,
|
|
1158
|
+
sourceId: pkg.sourceId,
|
|
1159
|
+
displayName: pkg.displayName,
|
|
1160
|
+
...pkg.description !== void 0 ? { description: pkg.description } : {},
|
|
1161
|
+
...pkg.version !== void 0 ? { version: pkg.version } : {},
|
|
1162
|
+
artifacts: pkg.artifacts.map((artifact) => ({
|
|
1163
|
+
id: artifact.id,
|
|
1164
|
+
packageId: artifact.packageId,
|
|
1165
|
+
kind: artifact.kind,
|
|
1166
|
+
displayName: artifact.displayName,
|
|
1167
|
+
...artifact.description !== void 0 ? { description: artifact.description } : {},
|
|
1168
|
+
installStrategy: artifact.installStrategy,
|
|
1169
|
+
risk: artifact.risk
|
|
1170
|
+
})),
|
|
1171
|
+
...pkg.wholePackage === true ? { wholePackage: true } : {}
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
function toPublicInstallation(installation) {
|
|
1175
|
+
return {
|
|
1176
|
+
packageId: installation.packageId,
|
|
1177
|
+
scope: installation.scope,
|
|
1178
|
+
selectedArtifactIds: [...installation.selectedArtifactIds],
|
|
1179
|
+
...installation.pinnedVersion !== void 0 ? { pinnedVersion: installation.pinnedVersion } : {}
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
function toPublicStates(states) {
|
|
1183
|
+
return Object.fromEntries(
|
|
1184
|
+
Object.entries(states).map(([artifactId, state]) => [artifactId, { ...state }])
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// src/bridge/CopilotPluginBridgeHandler.ts
|
|
1189
|
+
var fs = __toESM(require("fs/promises"));
|
|
1190
|
+
var path3 = __toESM(require("path"));
|
|
1191
|
+
var import_devtools_core3 = require("@serviceme/devtools-core");
|
|
1192
|
+
async function readManifestInfo(pluginDir) {
|
|
1193
|
+
try {
|
|
1194
|
+
const raw = JSON.parse(await fs.readFile(path3.join(pluginDir, "plugin.json"), "utf8"));
|
|
1195
|
+
let mcpServers = [];
|
|
1196
|
+
const mcpPath = path3.join(pluginDir, "mcp.json");
|
|
1197
|
+
try {
|
|
1198
|
+
const mcp = JSON.parse(await fs.readFile(mcpPath, "utf8"));
|
|
1199
|
+
mcpServers = Object.keys(mcp.mcpServers ?? {});
|
|
1200
|
+
} catch {
|
|
1201
|
+
}
|
|
1202
|
+
return {
|
|
1203
|
+
...typeof raw.name === "string" ? { displayName: raw.name } : {},
|
|
1204
|
+
...typeof raw.version === "string" ? { version: raw.version } : {},
|
|
1205
|
+
mcpServers,
|
|
1206
|
+
hasExtensions: Boolean(raw.extensions?.["com.github.copilot"])
|
|
1207
|
+
};
|
|
1208
|
+
} catch {
|
|
1209
|
+
return { mcpServers: [], hasExtensions: false };
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
function toPayload(reg, info) {
|
|
1213
|
+
return {
|
|
1214
|
+
registrationId: reg.registrationId,
|
|
1215
|
+
repoId: reg.repoId,
|
|
1216
|
+
pluginId: reg.pluginId,
|
|
1217
|
+
...reg.displayName ?? info.displayName ? { displayName: reg.displayName ?? info.displayName } : {},
|
|
1218
|
+
...reg.version ?? info.version ? { version: reg.version ?? info.version } : {},
|
|
1219
|
+
scopes: reg.scopes,
|
|
1220
|
+
mcpServers: info.mcpServers,
|
|
1221
|
+
hasExtensions: info.hasExtensions
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
var CopilotPluginBridgeHandler = class {
|
|
1225
|
+
constructor(options = {}) {
|
|
1226
|
+
const homeDir = options.homeDir ?? (0, import_devtools_core3.getHomeDir)();
|
|
1227
|
+
this.registrar = new import_devtools_core3.CopilotPluginRegistrar({
|
|
1228
|
+
// copilotDir is the migration source only — projections live
|
|
1229
|
+
// under the SERVICEME home, out of VS Code's ~/.copilot
|
|
1230
|
+
// reconciliation reach.
|
|
1231
|
+
copilotDir: path3.join(homeDir, ".copilot"),
|
|
1232
|
+
pluginsDir: path3.join(homeDir, ".serviceme", "copilot-plugins")
|
|
1233
|
+
});
|
|
1234
|
+
this.reposDir = path3.join(homeDir, ".serviceme", "repos");
|
|
1235
|
+
}
|
|
1236
|
+
pluginDir(repoId, pluginId) {
|
|
1237
|
+
return path3.join(this.reposDir, (0, import_devtools_core3.assertSafeRepoId)(repoId), "plugins", pluginId);
|
|
1238
|
+
}
|
|
1239
|
+
async list(_params) {
|
|
1240
|
+
const registrations = await this.registrar.list({});
|
|
1241
|
+
const plugins = await Promise.all(
|
|
1242
|
+
registrations.map(async (reg) => toPayload(reg, await readManifestInfo(reg.pluginDir)))
|
|
1243
|
+
);
|
|
1244
|
+
return { plugins };
|
|
1245
|
+
}
|
|
1246
|
+
async register(params) {
|
|
1247
|
+
const pluginDir = this.pluginDir(params.repoId, params.pluginId);
|
|
1248
|
+
const info = await readManifestInfo(pluginDir);
|
|
1249
|
+
await this.registrar.register({
|
|
1250
|
+
repoId: params.repoId,
|
|
1251
|
+
pluginId: params.pluginId,
|
|
1252
|
+
pluginDir,
|
|
1253
|
+
scope: params.scope,
|
|
1254
|
+
...info.displayName !== void 0 ? { displayName: info.displayName } : {},
|
|
1255
|
+
...info.version !== void 0 ? { version: info.version } : {}
|
|
1256
|
+
});
|
|
1257
|
+
return this.list({});
|
|
1258
|
+
}
|
|
1259
|
+
async unregister(params) {
|
|
1260
|
+
const normalized = params.registrationId.replace("::", ":");
|
|
1261
|
+
await this.registrar.unregister({
|
|
1262
|
+
registrationId: normalized,
|
|
1263
|
+
scope: params.scope
|
|
1264
|
+
});
|
|
1265
|
+
if (normalized !== params.registrationId) {
|
|
1266
|
+
await this.registrar.unregister({
|
|
1267
|
+
registrationId: params.registrationId,
|
|
1268
|
+
scope: params.scope
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
const otherScope = params.scope === "personal" ? "workspace" : "personal";
|
|
1272
|
+
await this.registrar.unregister({
|
|
1273
|
+
registrationId: normalized,
|
|
1274
|
+
scope: otherScope
|
|
1275
|
+
});
|
|
1276
|
+
return this.list({});
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
217
1279
|
|
|
218
1280
|
// src/bridge/handlers/AuthBridgeHandlers.ts
|
|
219
1281
|
var import_auth2 = require("@serviceme/devtools-core/auth");
|
|
@@ -328,12 +1390,12 @@ function writeBridgeMessage(message) {
|
|
|
328
1390
|
}
|
|
329
1391
|
|
|
330
1392
|
// src/bridge/TaskBridgeHandler.ts
|
|
331
|
-
var
|
|
1393
|
+
var import_devtools_core4 = require("@serviceme/devtools-core");
|
|
332
1394
|
var TaskBridgeHandler = class {
|
|
333
1395
|
constructor(logger, emitEvent) {
|
|
334
1396
|
this.logger = logger;
|
|
335
1397
|
this.emitEvent = emitEvent;
|
|
336
|
-
this.engine = new
|
|
1398
|
+
this.engine = new import_devtools_core4.TaskExecutionEngine((taskType) => (0, import_devtools_core4.getExecutor)(taskType));
|
|
337
1399
|
this.engine.setListener({
|
|
338
1400
|
onStarted: (params) => this.emitEvent("task.started", params),
|
|
339
1401
|
onOutput: (params) => this.emitEvent("task.output", params),
|
|
@@ -376,6 +1438,7 @@ var CAPABILITIES = {
|
|
|
376
1438
|
tasks: 1,
|
|
377
1439
|
skillRepo: 1,
|
|
378
1440
|
repoMgmt: 1,
|
|
1441
|
+
copilotContent: 1,
|
|
379
1442
|
auth: 1,
|
|
380
1443
|
device: 1,
|
|
381
1444
|
toolbox: 1
|
|
@@ -388,6 +1451,9 @@ var BridgeServer = class {
|
|
|
388
1451
|
this.writeEvent(event, params);
|
|
389
1452
|
});
|
|
390
1453
|
this.skillRepoHandler = opts.skillRepoHandler;
|
|
1454
|
+
this.copilotContentHandler = opts.skillRepoHandler ? new CopilotContentBridgeHandler(opts.skillRepoHandler.copilotContentDependencies) : void 0;
|
|
1455
|
+
this.copilotCustomizationsHandler = opts.skillRepoHandler ? new CopilotCustomizationsBridgeHandler(opts.skillRepoHandler.copilotContentDependencies) : void 0;
|
|
1456
|
+
this.copilotPluginHandler = opts.skillRepoHandler ? new CopilotPluginBridgeHandler() : void 0;
|
|
391
1457
|
this.authHandler = new AuthBridgeHandlers();
|
|
392
1458
|
this.deviceHandler = new DeviceBridgeHandlers();
|
|
393
1459
|
this.toolboxHandler = new ToolboxBridgeHandlers();
|
|
@@ -397,12 +1463,12 @@ var BridgeServer = class {
|
|
|
397
1463
|
input: process.stdin,
|
|
398
1464
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
399
1465
|
});
|
|
400
|
-
await new Promise((
|
|
1466
|
+
await new Promise((resolve3) => {
|
|
401
1467
|
reader.on("line", (line) => {
|
|
402
1468
|
void this.handleLine(line);
|
|
403
1469
|
});
|
|
404
1470
|
reader.on("close", () => {
|
|
405
|
-
|
|
1471
|
+
resolve3();
|
|
406
1472
|
});
|
|
407
1473
|
});
|
|
408
1474
|
}
|
|
@@ -493,6 +1559,7 @@ var BridgeServer = class {
|
|
|
493
1559
|
this.writeSuccess(request.id, result);
|
|
494
1560
|
return;
|
|
495
1561
|
}
|
|
1562
|
+
case "copilotContent.list":
|
|
496
1563
|
case "skillRepo.list": {
|
|
497
1564
|
const r = request;
|
|
498
1565
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -500,6 +1567,7 @@ var BridgeServer = class {
|
|
|
500
1567
|
this.writeSuccess(request.id, result);
|
|
501
1568
|
return;
|
|
502
1569
|
}
|
|
1570
|
+
case "copilotContent.get":
|
|
503
1571
|
case "skillRepo.get": {
|
|
504
1572
|
const r = request;
|
|
505
1573
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -507,6 +1575,7 @@ var BridgeServer = class {
|
|
|
507
1575
|
this.writeSuccess(request.id, result);
|
|
508
1576
|
return;
|
|
509
1577
|
}
|
|
1578
|
+
case "copilotContent.install":
|
|
510
1579
|
case "skillRepo.install": {
|
|
511
1580
|
const r = request;
|
|
512
1581
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -514,6 +1583,7 @@ var BridgeServer = class {
|
|
|
514
1583
|
this.writeSuccess(request.id, result);
|
|
515
1584
|
return;
|
|
516
1585
|
}
|
|
1586
|
+
case "copilotContent.convertToSymlink":
|
|
517
1587
|
case "skillRepo.convertToSymlink": {
|
|
518
1588
|
const r = request;
|
|
519
1589
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -521,6 +1591,7 @@ var BridgeServer = class {
|
|
|
521
1591
|
this.writeSuccess(request.id, result);
|
|
522
1592
|
return;
|
|
523
1593
|
}
|
|
1594
|
+
case "copilotContent.uninstall":
|
|
524
1595
|
case "skillRepo.uninstall": {
|
|
525
1596
|
const r = request;
|
|
526
1597
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -528,6 +1599,15 @@ var BridgeServer = class {
|
|
|
528
1599
|
this.writeSuccess(request.id, result);
|
|
529
1600
|
return;
|
|
530
1601
|
}
|
|
1602
|
+
case "copilotContent.setEntryEnabled":
|
|
1603
|
+
case "skillRepo.setEntryEnabled": {
|
|
1604
|
+
const r = request;
|
|
1605
|
+
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
1606
|
+
const result = await this.skillRepoHandler.setEntryEnabled(r.params);
|
|
1607
|
+
this.writeSuccess(request.id, result);
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
case "copilotContent.listLinked":
|
|
531
1611
|
case "skillRepo.listLinked": {
|
|
532
1612
|
const r = request;
|
|
533
1613
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -535,6 +1615,7 @@ var BridgeServer = class {
|
|
|
535
1615
|
this.writeSuccess(request.id, result);
|
|
536
1616
|
return;
|
|
537
1617
|
}
|
|
1618
|
+
case "copilotContent.draft.create":
|
|
538
1619
|
case "skillRepo.draft.create": {
|
|
539
1620
|
const r = request;
|
|
540
1621
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -542,6 +1623,7 @@ var BridgeServer = class {
|
|
|
542
1623
|
this.writeSuccess(request.id, result);
|
|
543
1624
|
return;
|
|
544
1625
|
}
|
|
1626
|
+
case "copilotContent.draft.commit":
|
|
545
1627
|
case "skillRepo.draft.commit": {
|
|
546
1628
|
const r = request;
|
|
547
1629
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -549,6 +1631,7 @@ var BridgeServer = class {
|
|
|
549
1631
|
this.writeSuccess(request.id, result);
|
|
550
1632
|
return;
|
|
551
1633
|
}
|
|
1634
|
+
case "copilotContent.draft.list":
|
|
552
1635
|
case "skillRepo.draft.list": {
|
|
553
1636
|
const r = request;
|
|
554
1637
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -556,6 +1639,7 @@ var BridgeServer = class {
|
|
|
556
1639
|
this.writeSuccess(request.id, result);
|
|
557
1640
|
return;
|
|
558
1641
|
}
|
|
1642
|
+
case "copilotContent.draft.delete":
|
|
559
1643
|
case "skillRepo.draft.delete": {
|
|
560
1644
|
const r = request;
|
|
561
1645
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -619,6 +1703,140 @@ var BridgeServer = class {
|
|
|
619
1703
|
this.writeSuccess(request.id, result);
|
|
620
1704
|
return;
|
|
621
1705
|
}
|
|
1706
|
+
case "repo.resetParseCache": {
|
|
1707
|
+
const r = request;
|
|
1708
|
+
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
1709
|
+
const result = await this.skillRepoHandler.repoResetParseCache(r.params);
|
|
1710
|
+
this.writeSuccess(request.id, result);
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
case "copilotPlugin.list": {
|
|
1714
|
+
const r = request;
|
|
1715
|
+
if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
|
|
1716
|
+
const listResult = await this.copilotPluginHandler.list(r.params);
|
|
1717
|
+
this.writeSuccess(request.id, listResult);
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
case "copilotPlugin.register": {
|
|
1721
|
+
const r = request;
|
|
1722
|
+
if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
|
|
1723
|
+
const regResult = await this.copilotPluginHandler.register(r.params);
|
|
1724
|
+
this.writeSuccess(request.id, regResult);
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
case "copilotPlugin.unregister": {
|
|
1728
|
+
const r = request;
|
|
1729
|
+
if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
|
|
1730
|
+
const unregResult = await this.copilotPluginHandler.unregister(r.params);
|
|
1731
|
+
this.writeSuccess(request.id, unregResult);
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
// ── copilotContent.* (declarative reconciliation) ──────────────
|
|
1735
|
+
case "copilotContent.status": {
|
|
1736
|
+
const r = request;
|
|
1737
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
1738
|
+
const result = await this.copilotContentHandler.status(r.params);
|
|
1739
|
+
this.writeSuccess(request.id, result);
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
case "copilotContent.restore": {
|
|
1743
|
+
const r = request;
|
|
1744
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
1745
|
+
const result = await this.copilotContentHandler.restore(r.params);
|
|
1746
|
+
this.writeSuccess(request.id, result);
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
case "copilotContent.approve": {
|
|
1750
|
+
const r = request;
|
|
1751
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
1752
|
+
const result = await this.copilotContentHandler.approve(r.params);
|
|
1753
|
+
this.writeSuccess(request.id, result);
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
case "copilotContent.migrateLegacy": {
|
|
1757
|
+
const r = request;
|
|
1758
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
1759
|
+
const result = await this.copilotContentHandler.migrateLegacy(r.params);
|
|
1760
|
+
this.writeSuccess(request.id, result);
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
case "copilotContent.integrationStatus": {
|
|
1764
|
+
const r = request;
|
|
1765
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
1766
|
+
const result = await this.copilotContentHandler.integrationStatus(r.params);
|
|
1767
|
+
this.writeSuccess(request.id, result);
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
case "copilotContent.customizations.list": {
|
|
1771
|
+
const r = request;
|
|
1772
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1773
|
+
const result = await this.copilotCustomizationsHandler.list(r.params);
|
|
1774
|
+
this.writeSuccess(request.id, result);
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
case "copilotContent.package.install": {
|
|
1778
|
+
const r = request;
|
|
1779
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1780
|
+
const result = await this.copilotCustomizationsHandler.install(r.params);
|
|
1781
|
+
this.writeSuccess(request.id, result);
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
case "copilotContent.package.update": {
|
|
1785
|
+
const r = request;
|
|
1786
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1787
|
+
const result = await this.copilotCustomizationsHandler.update(r.params);
|
|
1788
|
+
this.writeSuccess(request.id, result);
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
case "copilotContent.package.uninstall": {
|
|
1792
|
+
const r = request;
|
|
1793
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1794
|
+
const result = await this.copilotCustomizationsHandler.uninstall(r.params);
|
|
1795
|
+
this.writeSuccess(request.id, result);
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
case "copilotContent.package.move": {
|
|
1799
|
+
const r = request;
|
|
1800
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1801
|
+
const result = await this.copilotCustomizationsHandler.move(r.params);
|
|
1802
|
+
this.writeSuccess(request.id, result);
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
case "copilotContent.legacy.preview": {
|
|
1806
|
+
const r = request;
|
|
1807
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1808
|
+
const result = await this.copilotCustomizationsHandler.legacyPreview(r.params);
|
|
1809
|
+
this.writeSuccess(request.id, result);
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
case "copilotContent.legacy.migrate": {
|
|
1813
|
+
const r = request;
|
|
1814
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1815
|
+
const result = await this.copilotCustomizationsHandler.legacyMigrate(r.params);
|
|
1816
|
+
this.writeSuccess(request.id, result);
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
case "copilotContent.sources.list": {
|
|
1820
|
+
const r = request;
|
|
1821
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1822
|
+
const result = await this.copilotCustomizationsHandler.sources(r.params);
|
|
1823
|
+
this.writeSuccess(request.id, result);
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
case "copilotContent.sources.remove": {
|
|
1827
|
+
const r = request;
|
|
1828
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1829
|
+
const result = await this.copilotCustomizationsHandler.removeSource(r.params);
|
|
1830
|
+
this.writeSuccess(request.id, result);
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
case "copilotContent.package.previewUpdate": {
|
|
1834
|
+
const r = request;
|
|
1835
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
1836
|
+
const result = await this.copilotCustomizationsHandler.previewUpdate(r.params);
|
|
1837
|
+
this.writeSuccess(request.id, result);
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
622
1840
|
// ── auth.* (Phase 5.4) ─────────────────────────────────────────
|
|
623
1841
|
case "auth.status": {
|
|
624
1842
|
const r = request;
|
|
@@ -722,6 +1940,15 @@ var BridgeServer = class {
|
|
|
722
1940
|
)
|
|
723
1941
|
);
|
|
724
1942
|
}
|
|
1943
|
+
unsupportedCopilotContent(id) {
|
|
1944
|
+
this.writeError(
|
|
1945
|
+
id,
|
|
1946
|
+
createServicemeError(
|
|
1947
|
+
"invalid_params",
|
|
1948
|
+
"copilotContent.* methods require v2 CLI deps; the bridge was started without a CopilotContentBridgeHandler."
|
|
1949
|
+
)
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
725
1952
|
writeError(id, error) {
|
|
726
1953
|
writeBridgeMessage({
|
|
727
1954
|
protocolVersion: SERVICEME_PROTOCOL_VERSION,
|