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