@serviceme/devtools-cli 0.4.13 → 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 +1639 -161
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
9
16
|
var __copyProps = (to, from, except, desc) => {
|
|
10
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,6 +30,197 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
30
|
mod
|
|
24
31
|
));
|
|
25
32
|
|
|
33
|
+
// src/bridge/CopilotContentBridgeHandler.ts
|
|
34
|
+
var CopilotContentBridgeHandler_exports = {};
|
|
35
|
+
__export(CopilotContentBridgeHandler_exports, {
|
|
36
|
+
CopilotContentBridgeHandler: () => CopilotContentBridgeHandler
|
|
37
|
+
});
|
|
38
|
+
async function readOwnershipConfig(configPath) {
|
|
39
|
+
try {
|
|
40
|
+
const raw = await fsp.readFile(configPath, "utf8");
|
|
41
|
+
const parsed = JSON.parse(raw);
|
|
42
|
+
const ownershipNode = parsed._serviceme;
|
|
43
|
+
const ownership = ownershipNode && typeof ownershipNode === "object" ? normalizeOwnership(ownershipNode) ?? {} : {};
|
|
44
|
+
return { exists: true, configPath, ownership };
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error.code === "ENOENT") {
|
|
47
|
+
return { exists: false, configPath, ownership: {} };
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function normalizeOwnership(node) {
|
|
53
|
+
if (node === null || typeof node !== "object" || Array.isArray(node)) {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
const result = {};
|
|
57
|
+
for (const [identity, names] of Object.entries(node)) {
|
|
58
|
+
if (Array.isArray(names) && names.every((name) => typeof name === "string")) {
|
|
59
|
+
result[identity] = names;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
var fsp, path2, import_devtools_core2, CopilotContentBridgeHandler;
|
|
65
|
+
var init_CopilotContentBridgeHandler = __esm({
|
|
66
|
+
"src/bridge/CopilotContentBridgeHandler.ts"() {
|
|
67
|
+
"use strict";
|
|
68
|
+
fsp = __toESM(require("fs/promises"));
|
|
69
|
+
path2 = __toESM(require("path"));
|
|
70
|
+
import_devtools_core2 = require("@serviceme/devtools-core");
|
|
71
|
+
CopilotContentBridgeHandler = class {
|
|
72
|
+
constructor(opts) {
|
|
73
|
+
this.repoManager = new import_devtools_core2.RepoManager({
|
|
74
|
+
store: opts.store,
|
|
75
|
+
gitClient: opts.gitClient,
|
|
76
|
+
skipClone: opts.skipClone ?? false
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Ensure every declared repository is checked out at its pinned commit. */
|
|
80
|
+
async ensureRepositories(manifest) {
|
|
81
|
+
const sources = /* @__PURE__ */ new Map();
|
|
82
|
+
for (const source of (0, import_devtools_core2.getWorkspaceManifestSources)(
|
|
83
|
+
manifest ?? { version: 2, sources: [], plugins: [] }
|
|
84
|
+
)) {
|
|
85
|
+
if (source.type === "catalog") {
|
|
86
|
+
const localPath = path2.resolve((0, import_devtools_core2.getReposDir)(), source.id);
|
|
87
|
+
const stat4 = await fsp.stat(localPath).catch(() => null);
|
|
88
|
+
sources.set(
|
|
89
|
+
source.id,
|
|
90
|
+
stat4?.isDirectory() ? { ready: true } : {
|
|
91
|
+
ready: false,
|
|
92
|
+
error: "Source not materialized under ~/.serviceme/repos"
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
await this.repoManager.ensureAtCommit({
|
|
99
|
+
repository: {
|
|
100
|
+
id: source.id,
|
|
101
|
+
url: source.url,
|
|
102
|
+
useProxy: true
|
|
103
|
+
},
|
|
104
|
+
commit: source.commit
|
|
105
|
+
});
|
|
106
|
+
sources.set(source.id, { ready: true });
|
|
107
|
+
} catch (error) {
|
|
108
|
+
sources.set(source.id, {
|
|
109
|
+
ready: false,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error)
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return sources;
|
|
115
|
+
}
|
|
116
|
+
async reconcile(workspaceDir) {
|
|
117
|
+
const reconciler = await this.makeReconciler(workspaceDir);
|
|
118
|
+
return reconciler.reconcile();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Reconciler with machine-local disable marks applied: identities
|
|
122
|
+
* disabled for THIS workspace behave as if undeclared (links are
|
|
123
|
+
* removed and never resurrected) while the manifest stays intact.
|
|
124
|
+
*/
|
|
125
|
+
async makeReconciler(workspaceDir) {
|
|
126
|
+
const disabledStore = new import_devtools_core2.DisabledContentStore();
|
|
127
|
+
const marks = await disabledStore.list();
|
|
128
|
+
const matches = (identity) => {
|
|
129
|
+
const segments = identity.split("::");
|
|
130
|
+
if (segments.length < 3) return false;
|
|
131
|
+
const [repoId, pluginId, kindName] = segments;
|
|
132
|
+
return marks.some(
|
|
133
|
+
(mark) => mark.scope === "workspace" && mark.workspaceDir === workspaceDir && mark.repoId === repoId && mark.name === pluginId && kindName === mark.kind
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
return new import_devtools_core2.WorkspaceCopilotContentReconciler({
|
|
137
|
+
workspaceDir,
|
|
138
|
+
ensureRepository: (manifest) => this.ensureRepositories(manifest),
|
|
139
|
+
isDisabled: async (identity) => matches(identity)
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/** `copilotContent.restore` — reconcile declared content now. */
|
|
143
|
+
async restore(params) {
|
|
144
|
+
return this.reconcile(params.workspaceDir);
|
|
145
|
+
}
|
|
146
|
+
/** `copilotContent.status` — declaration + current status without mutations. */
|
|
147
|
+
async status(params) {
|
|
148
|
+
const manifest = await (0, import_devtools_core2.loadWorkspaceCopilotManifest)(params.workspaceDir);
|
|
149
|
+
if (!manifest) {
|
|
150
|
+
return { changed: false, entries: [] };
|
|
151
|
+
}
|
|
152
|
+
return this.reconcile(params.workspaceDir);
|
|
153
|
+
}
|
|
154
|
+
/** `copilotContent.approve` — record local approval, then re-reconcile. */
|
|
155
|
+
async approve(params) {
|
|
156
|
+
const reconciler = await this.makeReconciler(params.workspaceDir);
|
|
157
|
+
return reconciler.approve({ identities: params.identities });
|
|
158
|
+
}
|
|
159
|
+
/** `copilotContent.migrateLegacy` — report legacy ~/.agents links for migration. */
|
|
160
|
+
async migrateLegacy(params) {
|
|
161
|
+
const manifest = await (0, import_devtools_core2.loadWorkspaceCopilotManifest)(params.workspaceDir);
|
|
162
|
+
if (!manifest) {
|
|
163
|
+
return { entries: [] };
|
|
164
|
+
}
|
|
165
|
+
const plan = await (0, import_devtools_core2.resolveWorkspaceContentPlan)({
|
|
166
|
+
manifest,
|
|
167
|
+
reposDir: (0, import_devtools_core2.getReposDir)()
|
|
168
|
+
});
|
|
169
|
+
const materializer = new import_devtools_core2.CopilotLinkMaterializer();
|
|
170
|
+
const entries = [];
|
|
171
|
+
for (const entry of plan.entries) {
|
|
172
|
+
const legacy = await materializer.inspectLegacyUserLink({
|
|
173
|
+
homeDir: (0, import_devtools_core2.getServicemeHome)(),
|
|
174
|
+
entry
|
|
175
|
+
});
|
|
176
|
+
entries.push({
|
|
177
|
+
identity: legacy.identity,
|
|
178
|
+
status: legacy.status,
|
|
179
|
+
message: legacy.message
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return { entries };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* `copilotContent.integrationStatus` — read-only diagnostics for the
|
|
186
|
+
* machine-local MCP / hook integrations of a declared workspace.
|
|
187
|
+
*
|
|
188
|
+
* The state store provides the declared identities and approval flags;
|
|
189
|
+
* the two generated config files and their `_serviceme` ownership maps
|
|
190
|
+
* prove which names are actually present on this machine. This method
|
|
191
|
+
* never mutates state or configuration.
|
|
192
|
+
*/
|
|
193
|
+
async integrationStatus(params) {
|
|
194
|
+
const state = await new import_devtools_core2.WorkspaceContentStateStore({
|
|
195
|
+
workspaceDir: params.workspaceDir
|
|
196
|
+
}).read();
|
|
197
|
+
const mcp = await readOwnershipConfig(
|
|
198
|
+
path2.join(params.workspaceDir, ".vscode", "mcp.serviceme.json")
|
|
199
|
+
);
|
|
200
|
+
const hooks = await readOwnershipConfig(
|
|
201
|
+
path2.join(params.workspaceDir, ".vscode", "hooks.serviceme.json")
|
|
202
|
+
);
|
|
203
|
+
const integrations = [];
|
|
204
|
+
for (const entry of state.entries) {
|
|
205
|
+
if (entry.kind !== "mcp" && entry.kind !== "hook") continue;
|
|
206
|
+
const config = entry.kind === "mcp" ? mcp : hooks;
|
|
207
|
+
const owned = config.ownership[entry.identity] ?? [];
|
|
208
|
+
const active = config.exists && owned.length > 0;
|
|
209
|
+
const status = active ? "active" : entry.approved ? "missing_local_configuration" : "pending_approval";
|
|
210
|
+
integrations.push({
|
|
211
|
+
identity: entry.identity,
|
|
212
|
+
kind: entry.kind,
|
|
213
|
+
configPath: config.configPath,
|
|
214
|
+
names: owned,
|
|
215
|
+
status
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return { integrations };
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
26
224
|
// ../../packages/serviceme-protocol/src/auth.ts
|
|
27
225
|
var AUTH_PROVIDERS = ["github", "microsoft"];
|
|
28
226
|
function isAuthProvider(value) {
|
|
@@ -31,7 +229,7 @@ function isAuthProvider(value) {
|
|
|
31
229
|
|
|
32
230
|
// ../../packages/serviceme-protocol/src/toolbox.ts
|
|
33
231
|
var TOOLBOX_SCOPES = ["user", "workspace"];
|
|
34
|
-
function
|
|
232
|
+
function isRecord2(value) {
|
|
35
233
|
return typeof value === "object" && value !== null;
|
|
36
234
|
}
|
|
37
235
|
function isStringOrUndefined(value) {
|
|
@@ -44,7 +242,7 @@ function isToolboxScope(value) {
|
|
|
44
242
|
return value === "user" || value === "workspace";
|
|
45
243
|
}
|
|
46
244
|
function isExternalTool(value) {
|
|
47
|
-
if (!
|
|
245
|
+
if (!isRecord2(value)) return false;
|
|
48
246
|
if (typeof value.id !== "string") return false;
|
|
49
247
|
if (value.id.length === 0) return false;
|
|
50
248
|
if (typeof value.name !== "string") return false;
|
|
@@ -62,7 +260,7 @@ function isExternalTool(value) {
|
|
|
62
260
|
return true;
|
|
63
261
|
}
|
|
64
262
|
function isExternalToolPatch(value) {
|
|
65
|
-
if (!
|
|
263
|
+
if (!isRecord2(value)) return false;
|
|
66
264
|
if (value.name !== void 0) {
|
|
67
265
|
if (typeof value.name !== "string" || value.name.length === 0) return false;
|
|
68
266
|
}
|
|
@@ -84,7 +282,7 @@ function isExternalToolPatch(value) {
|
|
|
84
282
|
|
|
85
283
|
// ../../packages/serviceme-protocol/src/bridge.ts
|
|
86
284
|
var SERVICEME_PROTOCOL_VERSION = 2;
|
|
87
|
-
function
|
|
285
|
+
function isRecord3(value) {
|
|
88
286
|
return typeof value === "object" && value !== null;
|
|
89
287
|
}
|
|
90
288
|
function isValidProtocolVersion(value) {
|
|
@@ -97,11 +295,23 @@ var BRIDGE_METHODS = [
|
|
|
97
295
|
"task.execute",
|
|
98
296
|
"task.cancel",
|
|
99
297
|
"task.list-running",
|
|
298
|
+
"copilotContent.list",
|
|
299
|
+
"copilotContent.get",
|
|
300
|
+
"copilotContent.install",
|
|
301
|
+
"copilotContent.convertToSymlink",
|
|
302
|
+
"copilotContent.uninstall",
|
|
303
|
+
"copilotContent.setEntryEnabled",
|
|
304
|
+
"copilotContent.listLinked",
|
|
305
|
+
"copilotContent.draft.create",
|
|
306
|
+
"copilotContent.draft.commit",
|
|
307
|
+
"copilotContent.draft.list",
|
|
308
|
+
"copilotContent.draft.delete",
|
|
100
309
|
"skillRepo.list",
|
|
101
310
|
"skillRepo.get",
|
|
102
311
|
"skillRepo.install",
|
|
103
312
|
"skillRepo.convertToSymlink",
|
|
104
313
|
"skillRepo.uninstall",
|
|
314
|
+
"skillRepo.setEntryEnabled",
|
|
105
315
|
"skillRepo.listLinked",
|
|
106
316
|
"skillRepo.draft.create",
|
|
107
317
|
"skillRepo.draft.commit",
|
|
@@ -115,6 +325,25 @@ var BRIDGE_METHODS = [
|
|
|
115
325
|
"repo.update",
|
|
116
326
|
"repo.sync",
|
|
117
327
|
"repo.syncAll",
|
|
328
|
+
"repo.resetParseCache",
|
|
329
|
+
"copilotContent.status",
|
|
330
|
+
"copilotContent.restore",
|
|
331
|
+
"copilotContent.approve",
|
|
332
|
+
"copilotContent.migrateLegacy",
|
|
333
|
+
"copilotContent.integrationStatus",
|
|
334
|
+
"copilotContent.customizations.list",
|
|
335
|
+
"copilotContent.package.install",
|
|
336
|
+
"copilotContent.package.update",
|
|
337
|
+
"copilotContent.package.uninstall",
|
|
338
|
+
"copilotContent.package.move",
|
|
339
|
+
"copilotPlugin.list",
|
|
340
|
+
"copilotPlugin.register",
|
|
341
|
+
"copilotPlugin.unregister",
|
|
342
|
+
"copilotContent.legacy.preview",
|
|
343
|
+
"copilotContent.legacy.migrate",
|
|
344
|
+
"copilotContent.sources.list",
|
|
345
|
+
"copilotContent.sources.remove",
|
|
346
|
+
"copilotContent.package.previewUpdate",
|
|
118
347
|
"auth.status",
|
|
119
348
|
"auth.login",
|
|
120
349
|
"auth.logout",
|
|
@@ -132,7 +361,7 @@ function isBridgeMethod(value) {
|
|
|
132
361
|
return typeof value === "string" && BRIDGE_METHODS.includes(value);
|
|
133
362
|
}
|
|
134
363
|
function isBridgeRequest(value) {
|
|
135
|
-
if (!
|
|
364
|
+
if (!isRecord3(value)) {
|
|
136
365
|
return false;
|
|
137
366
|
}
|
|
138
367
|
return isValidProtocolVersion(value.protocolVersion) && value.kind === "request" && typeof value.id === "string" && isBridgeMethod(value.method) && "params" in value;
|
|
@@ -165,6 +394,7 @@ var KNOWN_ENVIRONMENT_TOOLS = [
|
|
|
165
394
|
"nrm",
|
|
166
395
|
"rtk",
|
|
167
396
|
"codegraph",
|
|
397
|
+
"ocx",
|
|
168
398
|
"dotnet",
|
|
169
399
|
"nuget"
|
|
170
400
|
];
|
|
@@ -218,14 +448,14 @@ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
218
448
|
"executor_timeout",
|
|
219
449
|
"internal_error"
|
|
220
450
|
]);
|
|
221
|
-
function
|
|
451
|
+
function isRecord4(value) {
|
|
222
452
|
return typeof value === "object" && value !== null;
|
|
223
453
|
}
|
|
224
454
|
function isServicemeErrorCode(value) {
|
|
225
455
|
return typeof value === "string" && SERVICEME_ERROR_CODES.includes(value);
|
|
226
456
|
}
|
|
227
457
|
function isServicemeErrorDetails(value) {
|
|
228
|
-
if (!
|
|
458
|
+
if (!isRecord4(value)) {
|
|
229
459
|
return false;
|
|
230
460
|
}
|
|
231
461
|
return isServicemeErrorCode(value.code) && typeof value.message === "string" && typeof value.retryable === "boolean";
|
|
@@ -265,7 +495,7 @@ function normalizeServicemeError(error, fallbackCode = "internal_error") {
|
|
|
265
495
|
if (isServicemeErrorDetails(error)) {
|
|
266
496
|
return error;
|
|
267
497
|
}
|
|
268
|
-
if (
|
|
498
|
+
if (isRecord4(error)) {
|
|
269
499
|
const code = isServicemeErrorCode(error.code) ? error.code : fallbackCode;
|
|
270
500
|
const message = typeof error.message === "string" ? error.message : "Unexpected serviceme error.";
|
|
271
501
|
const retryable = typeof error.retryable === "boolean" ? error.retryable : isRetryableErrorCode(code);
|
|
@@ -771,14 +1001,867 @@ async function handleSwitch(parsed) {
|
|
|
771
1001
|
}
|
|
772
1002
|
|
|
773
1003
|
// src/commands/bridge.ts
|
|
774
|
-
var
|
|
1004
|
+
var import_devtools_core7 = require("@serviceme/devtools-core");
|
|
775
1005
|
|
|
776
1006
|
// src/bridge/BridgeServer.ts
|
|
777
1007
|
var readline = __toESM(require("readline"));
|
|
778
1008
|
|
|
779
1009
|
// src/version.ts
|
|
780
1010
|
var SERVICEME_CLI_NAME = "serviceme";
|
|
781
|
-
var SERVICEME_CLI_VERSION = "0.
|
|
1011
|
+
var SERVICEME_CLI_VERSION = "2.0.0";
|
|
1012
|
+
|
|
1013
|
+
// src/bridge/BridgeServer.ts
|
|
1014
|
+
init_CopilotContentBridgeHandler();
|
|
1015
|
+
|
|
1016
|
+
// src/bridge/CopilotCustomizationsBridgeHandler.ts
|
|
1017
|
+
var fsp2 = __toESM(require("fs/promises"));
|
|
1018
|
+
var path3 = __toESM(require("path"));
|
|
1019
|
+
var import_devtools_core3 = require("@serviceme/devtools-core");
|
|
1020
|
+
var import_skill_linker = require("@serviceme/devtools-core/skill-linker");
|
|
1021
|
+
function createCopilotCustomizationsProductionDeps(options) {
|
|
1022
|
+
return {
|
|
1023
|
+
snapshot: async (params) => {
|
|
1024
|
+
if (params.scope === "personal") {
|
|
1025
|
+
return createFullPersonalSnapshot(options, await options.readers.listPersonalLinks());
|
|
1026
|
+
}
|
|
1027
|
+
return createWorkspaceSnapshot(
|
|
1028
|
+
options.repoDisplayName,
|
|
1029
|
+
options.readers,
|
|
1030
|
+
params.workspaceDir ?? process.cwd()
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
var CopilotCustomizationsBridgeHandler = class {
|
|
1036
|
+
constructor(deps) {
|
|
1037
|
+
if ("snapshot" in deps && typeof deps.snapshot === "function") {
|
|
1038
|
+
this.snapshot = deps.snapshot;
|
|
1039
|
+
this.listAvailablePackages = deps.listAvailablePackages;
|
|
1040
|
+
this.mutations = createUnimplementedMutations(
|
|
1041
|
+
"Injected snapshot deps do not support package mutations"
|
|
1042
|
+
);
|
|
1043
|
+
} else if ("store" in deps && deps.store !== void 0) {
|
|
1044
|
+
this.snapshot = createCopilotCustomizationsProductionDeps({
|
|
1045
|
+
repoDisplayName: (repoId) => deps.store.get(repoId)?.name,
|
|
1046
|
+
readers: createDefaultProductionReaders(),
|
|
1047
|
+
personal: createDefaultPersonalSnapshotInput(deps.store)
|
|
1048
|
+
}).snapshot;
|
|
1049
|
+
this.mutations = createProductionMutations(deps.store);
|
|
1050
|
+
this.listAvailablePackages = createAvailablePackagesEnumerator(deps.store);
|
|
1051
|
+
} else {
|
|
1052
|
+
this.snapshot = async () => emptySnapshot();
|
|
1053
|
+
this.mutations = createUnimplementedMutations(
|
|
1054
|
+
"Package mutations require production dependencies"
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
async list(params) {
|
|
1059
|
+
const snapshot = await this.snapshot(params);
|
|
1060
|
+
const shared = snapshot.workspaceManifest !== void 0 ? { workspaceManifest: snapshot.workspaceManifest } : {};
|
|
1061
|
+
const availablePackages = await this.listAvailablePackages?.(params, shared).catch(
|
|
1062
|
+
() => void 0
|
|
1063
|
+
);
|
|
1064
|
+
return {
|
|
1065
|
+
view: (0, import_devtools_core3.buildCopilotCustomizationView)({
|
|
1066
|
+
scope: params.scope,
|
|
1067
|
+
sources: snapshot.sources.map(toPublicSource),
|
|
1068
|
+
packages: snapshot.packages.map(toPublicPackage),
|
|
1069
|
+
installations: snapshot.installations.map(toPublicInstallation),
|
|
1070
|
+
statesByArtifactId: toPublicStates(snapshot.statesByArtifactId),
|
|
1071
|
+
legacyCount: snapshot.legacyCount,
|
|
1072
|
+
generatedAt: snapshot.generatedAt
|
|
1073
|
+
}),
|
|
1074
|
+
...availablePackages ? { availablePackages } : {}
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
async install(params) {
|
|
1078
|
+
return this.mutations.install(params);
|
|
1079
|
+
}
|
|
1080
|
+
async update(params) {
|
|
1081
|
+
return this.mutations.update(params);
|
|
1082
|
+
}
|
|
1083
|
+
async uninstall(params) {
|
|
1084
|
+
const result = await this.mutations.uninstall(params);
|
|
1085
|
+
try {
|
|
1086
|
+
const registrar = new import_devtools_core3.CopilotPluginRegistrar({
|
|
1087
|
+
copilotDir: path3.join((0, import_devtools_core3.getHomeDir)(), ".copilot")
|
|
1088
|
+
});
|
|
1089
|
+
await registrar.unregister({
|
|
1090
|
+
registrationId: params.packageId.replace("::", ":"),
|
|
1091
|
+
scope: params.scope
|
|
1092
|
+
});
|
|
1093
|
+
const otherScope = params.scope === "personal" ? "workspace" : "personal";
|
|
1094
|
+
await registrar.unregister({
|
|
1095
|
+
registrationId: params.packageId.replace("::", ":"),
|
|
1096
|
+
scope: otherScope
|
|
1097
|
+
});
|
|
1098
|
+
} catch {
|
|
1099
|
+
}
|
|
1100
|
+
return result;
|
|
1101
|
+
}
|
|
1102
|
+
async move(params) {
|
|
1103
|
+
return this.mutations.move(params);
|
|
1104
|
+
}
|
|
1105
|
+
async legacyPreview(params) {
|
|
1106
|
+
return this.mutations.legacyPreview(params);
|
|
1107
|
+
}
|
|
1108
|
+
async legacyMigrate(params) {
|
|
1109
|
+
return this.mutations.legacyMigrate(params);
|
|
1110
|
+
}
|
|
1111
|
+
async sources(params) {
|
|
1112
|
+
return this.mutations.sources(params);
|
|
1113
|
+
}
|
|
1114
|
+
async removeSource(params) {
|
|
1115
|
+
return this.mutations.removeSource(params);
|
|
1116
|
+
}
|
|
1117
|
+
async previewUpdate(params) {
|
|
1118
|
+
return this.mutations.previewUpdate(params);
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
function createDefaultPersonalSnapshotInput(store) {
|
|
1122
|
+
return {
|
|
1123
|
+
// Capabilities are intentionally omitted: createFullPersonalSnapshot
|
|
1124
|
+
// falls back to the real default host-capability provider, which
|
|
1125
|
+
// claims the verified ~/.copilot agent/skill targets. Pinning an
|
|
1126
|
+
// empty personalTargets map here regressed every link artifact to
|
|
1127
|
+
// unsupported. The explicit package resolver is the part Task 8
|
|
1128
|
+
// flagged as silently missing.
|
|
1129
|
+
resolvePersonalPackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store)
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
async function resolvePersonalPackageFromRepos(packageId, store) {
|
|
1133
|
+
const [sourceId, pluginId] = packageId.split("::");
|
|
1134
|
+
if (!sourceId || !pluginId) return [];
|
|
1135
|
+
const repoRoot = path3.resolve((0, import_devtools_core3.getReposDir)(), sourceId);
|
|
1136
|
+
const stat4 = await fsp2.stat(repoRoot).catch(() => null);
|
|
1137
|
+
if (!stat4?.isDirectory()) return [];
|
|
1138
|
+
const repo = store.get(sourceId);
|
|
1139
|
+
const manifest = {
|
|
1140
|
+
version: 1,
|
|
1141
|
+
repositories: [
|
|
1142
|
+
{
|
|
1143
|
+
id: sourceId,
|
|
1144
|
+
url: repo?.url ?? `https://serviceme.local/catalog/${encodeURIComponent(sourceId)}`,
|
|
1145
|
+
commit: repo?.lastSyncCommitSha ?? "".padEnd(40, "0")
|
|
1146
|
+
}
|
|
1147
|
+
],
|
|
1148
|
+
plugins: [
|
|
1149
|
+
{
|
|
1150
|
+
repository: sourceId,
|
|
1151
|
+
id: pluginId,
|
|
1152
|
+
artifacts: {
|
|
1153
|
+
agent: true,
|
|
1154
|
+
skill: true,
|
|
1155
|
+
instruction: true,
|
|
1156
|
+
prompt: true,
|
|
1157
|
+
hook: true,
|
|
1158
|
+
mcp: true
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
]
|
|
1162
|
+
};
|
|
1163
|
+
const resolved = await (0, import_devtools_core3.resolveWorkspaceContentPlan)({
|
|
1164
|
+
manifest,
|
|
1165
|
+
reposDir: (0, import_devtools_core3.getReposDir)()
|
|
1166
|
+
}).catch(() => ({ entries: [] }));
|
|
1167
|
+
return resolved.entries;
|
|
1168
|
+
}
|
|
1169
|
+
function createAvailablePackagesEnumerator(store) {
|
|
1170
|
+
const catalog = (0, import_devtools_core3.createPluginCatalogService)();
|
|
1171
|
+
const personalStore = new import_devtools_core3.PersonalInstallationStore({});
|
|
1172
|
+
const pluginRegistrar = new import_devtools_core3.CopilotPluginRegistrar({
|
|
1173
|
+
copilotDir: path3.join((0, import_devtools_core3.getHomeDir)(), ".copilot")
|
|
1174
|
+
});
|
|
1175
|
+
return async (params, shared) => {
|
|
1176
|
+
const repos = store.list().filter((repo) => repo.enabled).map((repo) => ({ id: repo.id }));
|
|
1177
|
+
if (repos.length === 0) return [];
|
|
1178
|
+
const catalogPackages = await catalog.list({
|
|
1179
|
+
reposDir: (0, import_devtools_core3.getReposDir)(),
|
|
1180
|
+
repos
|
|
1181
|
+
});
|
|
1182
|
+
const [workspaceManifest, personalIntent, registrations] = await Promise.all([
|
|
1183
|
+
// Shared read from the same list() call when provided; only a
|
|
1184
|
+
// standalone enumerator invocation (personal scope, tests,
|
|
1185
|
+
// direct calls) hits the disk here.
|
|
1186
|
+
shared?.workspaceManifest !== void 0 ? Promise.resolve(shared.workspaceManifest) : params.workspaceDir ? (0, import_devtools_core3.loadWorkspaceCopilotManifest)(params.workspaceDir).catch(() => void 0) : Promise.resolve(void 0),
|
|
1187
|
+
personalStore.read().catch(() => void 0),
|
|
1188
|
+
// Whole-package registry — prune-on-list keeps it clean.
|
|
1189
|
+
pluginRegistrar.list({}).catch(() => [])
|
|
1190
|
+
]);
|
|
1191
|
+
const manifest = workspaceManifest ?? {
|
|
1192
|
+
version: 1,
|
|
1193
|
+
repositories: [],
|
|
1194
|
+
plugins: []
|
|
1195
|
+
};
|
|
1196
|
+
const workspaceInstalled = new Set(
|
|
1197
|
+
manifest.plugins.map(
|
|
1198
|
+
(plugin) => `${(0, import_devtools_core3.getWorkspaceManifestPluginSourceId)(manifest, plugin)}::${plugin.id}`
|
|
1199
|
+
)
|
|
1200
|
+
);
|
|
1201
|
+
const personalInstalled = new Set(
|
|
1202
|
+
(personalIntent?.installations ?? []).filter((installation) => installation.scope === "personal").map((installation) => installation.packageId)
|
|
1203
|
+
);
|
|
1204
|
+
const registeredScopes = /* @__PURE__ */ new Map();
|
|
1205
|
+
for (const reg of registrations) {
|
|
1206
|
+
const packageId = `${reg.repoId}::${reg.pluginId}`;
|
|
1207
|
+
const scopes = registeredScopes.get(packageId) ?? /* @__PURE__ */ new Set();
|
|
1208
|
+
for (const scope of reg.scopes) scopes.add(scope);
|
|
1209
|
+
registeredScopes.set(packageId, scopes);
|
|
1210
|
+
}
|
|
1211
|
+
return catalogPackages.map((pkg) => {
|
|
1212
|
+
const installed = registeredScopes.get(pkg.packageId) !== void 0 || workspaceInstalled.has(pkg.packageId) || personalInstalled.has(pkg.packageId);
|
|
1213
|
+
return {
|
|
1214
|
+
...pkg,
|
|
1215
|
+
installedScopes: installed ? ["personal"] : []
|
|
1216
|
+
};
|
|
1217
|
+
});
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
function createProductionMutations(store) {
|
|
1221
|
+
let servicePromise;
|
|
1222
|
+
let sourceCatalogPromise;
|
|
1223
|
+
const service = () => servicePromise ??= createPackageInstallationService(store);
|
|
1224
|
+
const sourceCatalog = () => sourceCatalogPromise ??= createSourceCatalogService(store);
|
|
1225
|
+
return {
|
|
1226
|
+
install: async (params) => toResult(await (await service()).install(params)),
|
|
1227
|
+
update: async (params) => toResult(await (await service()).update(params)),
|
|
1228
|
+
uninstall: async (params) => toResult(await (await service()).uninstall(params)),
|
|
1229
|
+
move: async (params) => toResult(await (await service()).move(params)),
|
|
1230
|
+
legacyPreview: async () => {
|
|
1231
|
+
const preview = await (await service()).previewLegacy();
|
|
1232
|
+
return {
|
|
1233
|
+
sourceLabel: preview.sourceLabel,
|
|
1234
|
+
count: preview.entries.length,
|
|
1235
|
+
artifacts: preview.entries.map((entry) => ({
|
|
1236
|
+
id: entry.artifactId,
|
|
1237
|
+
packageId: "legacy",
|
|
1238
|
+
kind: entry.kind,
|
|
1239
|
+
displayName: entry.name,
|
|
1240
|
+
installStrategy: "link",
|
|
1241
|
+
risk: "none"
|
|
1242
|
+
}))
|
|
1243
|
+
};
|
|
1244
|
+
},
|
|
1245
|
+
legacyMigrate: async (params) => toResult(await (await service()).migrateLegacy(params)),
|
|
1246
|
+
sources: async (params) => {
|
|
1247
|
+
const records = await (await sourceCatalog()).listSources({
|
|
1248
|
+
scope: params.scope,
|
|
1249
|
+
...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
|
|
1250
|
+
});
|
|
1251
|
+
return {
|
|
1252
|
+
sources: records.map((record) => ({
|
|
1253
|
+
id: record.id,
|
|
1254
|
+
type: record.type,
|
|
1255
|
+
displayName: record.displayName,
|
|
1256
|
+
updateCapability: record.updateCapability,
|
|
1257
|
+
available: record.available,
|
|
1258
|
+
declaredIn: [...record.declaredIn]
|
|
1259
|
+
}))
|
|
1260
|
+
};
|
|
1261
|
+
},
|
|
1262
|
+
removeSource: async (params) => {
|
|
1263
|
+
await (await sourceCatalog()).removeSource(params.sourceId, params.workspaceDir);
|
|
1264
|
+
return { removed: true };
|
|
1265
|
+
},
|
|
1266
|
+
previewUpdate: async (params) => ({
|
|
1267
|
+
preview: await (await sourceCatalog()).previewUpdate(params)
|
|
1268
|
+
})
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
async function createSourceCatalogService(store) {
|
|
1272
|
+
const personalStore = new import_devtools_core3.PersonalInstallationStore({});
|
|
1273
|
+
const loadManifest = async (workspaceDir) => {
|
|
1274
|
+
return (0, import_devtools_core3.loadWorkspaceCopilotManifest)(workspaceDir).catch(() => void 0);
|
|
1275
|
+
};
|
|
1276
|
+
return new import_devtools_core3.CopilotSourceCatalogService({
|
|
1277
|
+
personalStore,
|
|
1278
|
+
// The source manager is the single repository management surface;
|
|
1279
|
+
// surface every ReposStore entry (built-in defaults + user git
|
|
1280
|
+
// repos) so they can be synced / toggled / edited from there even
|
|
1281
|
+
// when no installation or workspace declaration references them.
|
|
1282
|
+
// Read per call: repos.json can change under the long-lived bridge.
|
|
1283
|
+
listStoreSources: async () => store.list().map((repo) => ({
|
|
1284
|
+
id: repo.id,
|
|
1285
|
+
name: repo.name,
|
|
1286
|
+
enabled: repo.enabled
|
|
1287
|
+
})),
|
|
1288
|
+
resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store),
|
|
1289
|
+
resolveActualRevision: async (packageId) => {
|
|
1290
|
+
const [sourceId] = packageId.split("::");
|
|
1291
|
+
if (!sourceId) return void 0;
|
|
1292
|
+
const repo = store.get(sourceId);
|
|
1293
|
+
if (repo?.lastSyncCommitSha) return repo.lastSyncCommitSha;
|
|
1294
|
+
const repoDir = path3.resolve((0, import_devtools_core3.getReposDir)(), sourceId);
|
|
1295
|
+
const head = await new import_devtools_core3.GitClient({ serverProxyBase: "" }).revParseHead(repoDir).catch(() => void 0);
|
|
1296
|
+
return head;
|
|
1297
|
+
},
|
|
1298
|
+
listWorkspaceSources: async (workspaceDir) => {
|
|
1299
|
+
const manifest = await loadManifest(workspaceDir);
|
|
1300
|
+
if (!manifest) return [];
|
|
1301
|
+
return (0, import_devtools_core3.getWorkspaceManifestSources)(manifest).map((source) => source.id);
|
|
1302
|
+
},
|
|
1303
|
+
readWorkspaceInstallations: async (workspaceDir) => {
|
|
1304
|
+
const manifest = await loadManifest(workspaceDir);
|
|
1305
|
+
if (!manifest) return [];
|
|
1306
|
+
return manifest.plugins.map((plugin) => {
|
|
1307
|
+
const sourceId = (0, import_devtools_core3.getWorkspaceManifestPluginSourceId)(manifest, plugin);
|
|
1308
|
+
const source = (0, import_devtools_core3.getWorkspaceManifestSources)(manifest).find((s) => s.id === sourceId);
|
|
1309
|
+
return {
|
|
1310
|
+
packageId: `${sourceId}::${plugin.id}`,
|
|
1311
|
+
scope: "workspace",
|
|
1312
|
+
selectedArtifactIds: Object.entries(plugin.artifacts).filter(([, enabled]) => enabled !== false).map(([kind]) => `${sourceId}::${plugin.id}::${kind}:`),
|
|
1313
|
+
pinnedVersion: source?.type === "git" ? source.commit : void 0
|
|
1314
|
+
};
|
|
1315
|
+
});
|
|
1316
|
+
},
|
|
1317
|
+
artifactSelected: (marker, artifactId) => artifactId.startsWith(marker),
|
|
1318
|
+
ensureCatalogSource: async (sourceId) => {
|
|
1319
|
+
const mgr = new import_devtools_core3.RepoManager({
|
|
1320
|
+
store,
|
|
1321
|
+
gitClient: new import_devtools_core3.GitClient({ serverProxyBase: "" })
|
|
1322
|
+
});
|
|
1323
|
+
return mgr.ensureCatalogSource({
|
|
1324
|
+
sourceId,
|
|
1325
|
+
provider: {
|
|
1326
|
+
// Placeholder staging: the marketplace catalog client is
|
|
1327
|
+
// not yet wired to per-source payloads, so the first
|
|
1328
|
+
// materialization establishes the directory contract.
|
|
1329
|
+
// Real payload extraction lands with the catalog client.
|
|
1330
|
+
materialize: async (targetDir) => {
|
|
1331
|
+
await fsp2.mkdir(targetDir, { recursive: true });
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
},
|
|
1336
|
+
isCatalogSource: async (sourceId, workspaceDir) => {
|
|
1337
|
+
if (!workspaceDir) return false;
|
|
1338
|
+
const declared = await loadManifest(workspaceDir);
|
|
1339
|
+
const source = declared ? (0, import_devtools_core3.getWorkspaceManifestSources)(declared).find((s) => s.id === sourceId) : void 0;
|
|
1340
|
+
return source?.type === "catalog";
|
|
1341
|
+
},
|
|
1342
|
+
hasCatalogPayload: async (localPath) => {
|
|
1343
|
+
const pluginsDir = path3.join(localPath, "plugins");
|
|
1344
|
+
const entries = await fsp2.readdir(pluginsDir).catch(() => []);
|
|
1345
|
+
if (entries.length === 0) return false;
|
|
1346
|
+
for (const entry of entries) {
|
|
1347
|
+
const stat4 = await fsp2.stat(path3.join(pluginsDir, entry)).catch(() => null);
|
|
1348
|
+
if (stat4?.isDirectory()) {
|
|
1349
|
+
const manifest = await fsp2.stat(path3.join(pluginsDir, entry, "plugin.json")).catch(() => null);
|
|
1350
|
+
if (manifest?.isFile()) return true;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return false;
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
async function createPackageInstallationService(store) {
|
|
1358
|
+
const personalStore = new import_devtools_core3.PersonalInstallationStore({});
|
|
1359
|
+
const personalReconciler = new import_devtools_core3.PersonalCopilotContentReconciler({
|
|
1360
|
+
capabilities: await (0, import_devtools_core3.createDefaultCopilotHostCapabilities)(),
|
|
1361
|
+
resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store)
|
|
1362
|
+
});
|
|
1363
|
+
return new import_devtools_core3.PackageInstallationService({
|
|
1364
|
+
personalStore,
|
|
1365
|
+
personalReconciler,
|
|
1366
|
+
userHomeDir: (0, import_devtools_core3.getHomeDir)(),
|
|
1367
|
+
resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store),
|
|
1368
|
+
resolveWorkspaceSource: async (sourceId) => {
|
|
1369
|
+
const repo = store.get(sourceId);
|
|
1370
|
+
if (!repo) return void 0;
|
|
1371
|
+
const repoDir = path3.resolve((0, import_devtools_core3.getReposDir)(), sourceId);
|
|
1372
|
+
const stat4 = await fsp2.stat(repoDir).catch(() => null);
|
|
1373
|
+
if (!stat4?.isDirectory()) return void 0;
|
|
1374
|
+
return {
|
|
1375
|
+
id: sourceId,
|
|
1376
|
+
url: repo.url,
|
|
1377
|
+
commit: repo.lastSyncCommitSha ?? "0".repeat(40)
|
|
1378
|
+
};
|
|
1379
|
+
},
|
|
1380
|
+
createWorkspaceReconciler: (workspaceDir) => new import_devtools_core3.WorkspaceCopilotContentReconciler({ workspaceDir }),
|
|
1381
|
+
readWorkspaceView: async (workspaceDir) => {
|
|
1382
|
+
const snapshot = await createWorkspaceSnapshot(
|
|
1383
|
+
(repoId) => store.get(repoId)?.name,
|
|
1384
|
+
createDefaultProductionReaders(),
|
|
1385
|
+
workspaceDir
|
|
1386
|
+
);
|
|
1387
|
+
return (0, import_devtools_core3.buildCopilotCustomizationView)({
|
|
1388
|
+
scope: "workspace",
|
|
1389
|
+
sources: snapshot.sources,
|
|
1390
|
+
packages: snapshot.packages,
|
|
1391
|
+
installations: snapshot.installations,
|
|
1392
|
+
statesByArtifactId: snapshot.statesByArtifactId,
|
|
1393
|
+
generatedAt: snapshot.generatedAt
|
|
1394
|
+
});
|
|
1395
|
+
},
|
|
1396
|
+
readWorkspaceInstallations: import_devtools_core3.readDeclaredWorkspaceInstallations
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
function toResult(view) {
|
|
1400
|
+
return { view: toPublicView(view) };
|
|
1401
|
+
}
|
|
1402
|
+
function toPublicView(view) {
|
|
1403
|
+
return {
|
|
1404
|
+
scope: view.scope,
|
|
1405
|
+
generatedAt: view.generatedAt,
|
|
1406
|
+
sources: view.sources.map((source) => ({
|
|
1407
|
+
...toPublicSource(source),
|
|
1408
|
+
packages: source.packages.map((pkg) => toPublicPackageView(pkg))
|
|
1409
|
+
})),
|
|
1410
|
+
packages: view.packages.map((pkg) => ({
|
|
1411
|
+
...toPublicPackageView(pkg)
|
|
1412
|
+
})),
|
|
1413
|
+
attention: view.attention.map(({ artifact, state }) => ({
|
|
1414
|
+
artifact,
|
|
1415
|
+
state
|
|
1416
|
+
})),
|
|
1417
|
+
summary: view.summary,
|
|
1418
|
+
...view.legacyMigration ? { legacyMigration: view.legacyMigration } : {}
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
function toPublicPackageView(pkg) {
|
|
1422
|
+
return {
|
|
1423
|
+
definition: toPublicPackage(pkg.definition),
|
|
1424
|
+
installation: toPublicInstallation(pkg.installation),
|
|
1425
|
+
artifacts: pkg.artifacts.map(({ artifact, state }) => ({
|
|
1426
|
+
artifact,
|
|
1427
|
+
state
|
|
1428
|
+
})),
|
|
1429
|
+
selectedArtifactCount: pkg.selectedArtifactCount,
|
|
1430
|
+
status: pkg.status
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
function createUnimplementedMutations(reason) {
|
|
1434
|
+
const reject = async () => {
|
|
1435
|
+
throw new Error(reason);
|
|
1436
|
+
};
|
|
1437
|
+
return {
|
|
1438
|
+
install: reject,
|
|
1439
|
+
update: reject,
|
|
1440
|
+
uninstall: reject,
|
|
1441
|
+
move: reject,
|
|
1442
|
+
legacyPreview: reject,
|
|
1443
|
+
legacyMigrate: reject,
|
|
1444
|
+
sources: reject,
|
|
1445
|
+
removeSource: reject,
|
|
1446
|
+
previewUpdate: reject
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
async function mergeRegistrarPackages(sources, packages, installations, statesByArtifactId) {
|
|
1450
|
+
const registrar = new import_devtools_core3.CopilotPluginRegistrar({
|
|
1451
|
+
copilotDir: path3.join((0, import_devtools_core3.getHomeDir)(), ".copilot")
|
|
1452
|
+
});
|
|
1453
|
+
const registrations = await registrar.list({}).catch(() => []);
|
|
1454
|
+
if (registrations.length === 0) return;
|
|
1455
|
+
const asMap = packages instanceof Map ? packages : new Map(packages.map((p) => [p.id, p]));
|
|
1456
|
+
const knownIds = new Set(asMap.keys());
|
|
1457
|
+
const pushed = packages instanceof Map ? void 0 : packages;
|
|
1458
|
+
for (const reg of registrations) {
|
|
1459
|
+
const packageId = `${reg.repoId}::${reg.pluginId}`;
|
|
1460
|
+
if (knownIds.has(packageId)) continue;
|
|
1461
|
+
const pluginJsonPath = path3.join(reg.pluginDir, "plugin.json");
|
|
1462
|
+
let definition;
|
|
1463
|
+
try {
|
|
1464
|
+
const raw = JSON.parse(await fsp2.readFile(pluginJsonPath, "utf8"));
|
|
1465
|
+
const repoRoot = path3.basename(path3.dirname(reg.pluginDir)) === "plugins" ? path3.dirname(path3.dirname(reg.pluginDir)) : reg.pluginDir;
|
|
1466
|
+
const entries = await (0, import_devtools_core3.resolvePluginEntriesLenient)({
|
|
1467
|
+
repoRoot,
|
|
1468
|
+
repositoryId: reg.repoId,
|
|
1469
|
+
pluginId: reg.pluginId,
|
|
1470
|
+
manifest: {
|
|
1471
|
+
name: typeof raw.name === "string" && raw.name.trim() !== "" ? raw.name : reg.pluginId,
|
|
1472
|
+
...typeof raw.description === "string" ? { description: raw.description } : {},
|
|
1473
|
+
...typeof raw.version === "string" ? { version: raw.version } : {},
|
|
1474
|
+
extensions: raw.extensions ?? {}
|
|
1475
|
+
}
|
|
1476
|
+
}).catch(() => []);
|
|
1477
|
+
const artifacts = entries.map(import_devtools_core3.toArtifactSummary);
|
|
1478
|
+
if (artifacts.length > 0) {
|
|
1479
|
+
definition = {
|
|
1480
|
+
id: packageId,
|
|
1481
|
+
sourceId: reg.repoId,
|
|
1482
|
+
displayName: typeof raw.name === "string" && raw.name || reg.displayName || reg.pluginId,
|
|
1483
|
+
...typeof raw.description === "string" ? { description: raw.description } : {},
|
|
1484
|
+
...typeof raw.version === "string" ? { version: raw.version } : {},
|
|
1485
|
+
artifacts,
|
|
1486
|
+
wholePackage: true
|
|
1487
|
+
};
|
|
1488
|
+
for (const artifact of artifacts) {
|
|
1489
|
+
statesByArtifactId[artifact.id] = {
|
|
1490
|
+
intent: "selected",
|
|
1491
|
+
health: "healthy",
|
|
1492
|
+
gate: "ready"
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
} else {
|
|
1496
|
+
const artifact = {
|
|
1497
|
+
id: `${packageId}::package:${reg.pluginId}`,
|
|
1498
|
+
packageId,
|
|
1499
|
+
kind: "skill",
|
|
1500
|
+
displayName: reg.pluginId,
|
|
1501
|
+
installStrategy: "link",
|
|
1502
|
+
risk: "none"
|
|
1503
|
+
};
|
|
1504
|
+
definition = {
|
|
1505
|
+
id: packageId,
|
|
1506
|
+
sourceId: reg.repoId,
|
|
1507
|
+
displayName: typeof raw.name === "string" && raw.name || reg.displayName || reg.pluginId,
|
|
1508
|
+
...typeof raw.description === "string" ? { description: raw.description } : {},
|
|
1509
|
+
...typeof raw.version === "string" ? { version: raw.version } : {},
|
|
1510
|
+
artifacts: [artifact],
|
|
1511
|
+
wholePackage: true
|
|
1512
|
+
};
|
|
1513
|
+
statesByArtifactId[artifact.id] = {
|
|
1514
|
+
intent: "selected",
|
|
1515
|
+
health: "healthy",
|
|
1516
|
+
gate: "ready"
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
} catch {
|
|
1520
|
+
definition = {
|
|
1521
|
+
id: packageId,
|
|
1522
|
+
sourceId: reg.repoId,
|
|
1523
|
+
displayName: reg.displayName || reg.pluginId,
|
|
1524
|
+
...reg.version !== void 0 ? { version: reg.version } : {},
|
|
1525
|
+
artifacts: [],
|
|
1526
|
+
wholePackage: true
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
if (pushed) {
|
|
1530
|
+
pushed.push(definition);
|
|
1531
|
+
} else {
|
|
1532
|
+
asMap.set(packageId, definition);
|
|
1533
|
+
}
|
|
1534
|
+
if (!sources.some((source) => source.id === reg.repoId)) {
|
|
1535
|
+
sources.push({
|
|
1536
|
+
id: reg.repoId,
|
|
1537
|
+
type: "git",
|
|
1538
|
+
displayName: reg.repoId,
|
|
1539
|
+
updateCapability: "pinned"
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
installations.push({
|
|
1543
|
+
packageId,
|
|
1544
|
+
scope: "personal",
|
|
1545
|
+
selectedArtifactIds: definition.artifacts.map((artifact) => artifact.id)
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
async function createWorkspaceSnapshot(repoDisplayName, readers, workspaceDir) {
|
|
1550
|
+
const manifest = await readers.loadWorkspaceManifest(workspaceDir);
|
|
1551
|
+
if (!manifest) return emptySnapshot();
|
|
1552
|
+
const sources = (0, import_devtools_core3.getWorkspaceManifestSources)(manifest).map(
|
|
1553
|
+
(source) => ({
|
|
1554
|
+
id: source.id,
|
|
1555
|
+
type: source.type === "catalog" ? "marketplace" : "git",
|
|
1556
|
+
displayName: source.type === "catalog" ? source.catalogId : repoDisplayName(source.id) ?? source.id,
|
|
1557
|
+
updateCapability: "pinned"
|
|
1558
|
+
})
|
|
1559
|
+
);
|
|
1560
|
+
const state = await readers.readWorkspaceState(workspaceDir);
|
|
1561
|
+
const packages = [];
|
|
1562
|
+
const installations = [];
|
|
1563
|
+
const statesByArtifactId = {};
|
|
1564
|
+
const resolvedPlugins = await Promise.all(
|
|
1565
|
+
manifest.plugins.map(async (plugin) => {
|
|
1566
|
+
const sourceId = (0, import_devtools_core3.getWorkspaceManifestPluginSourceId)(manifest, plugin);
|
|
1567
|
+
const packageId = `${sourceId}::${plugin.id}`;
|
|
1568
|
+
let fallbackHealth;
|
|
1569
|
+
let entries;
|
|
1570
|
+
if (!await readers.isSourceAvailable(sourceId)) {
|
|
1571
|
+
fallbackHealth = "source-unavailable";
|
|
1572
|
+
entries = fallbackEntries(plugin, packageId);
|
|
1573
|
+
} else {
|
|
1574
|
+
try {
|
|
1575
|
+
entries = await readers.resolveWorkspacePlugin({ manifest, plugin });
|
|
1576
|
+
} catch {
|
|
1577
|
+
fallbackHealth = "conflict";
|
|
1578
|
+
entries = fallbackEntries(plugin, packageId);
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
return { plugin, sourceId, packageId, entries, fallbackHealth };
|
|
1582
|
+
})
|
|
1583
|
+
);
|
|
1584
|
+
for (const resolved of resolvedPlugins) {
|
|
1585
|
+
const { plugin, sourceId, packageId, entries, fallbackHealth } = resolved;
|
|
1586
|
+
const artifacts = entries.map(import_devtools_core3.toArtifactSummary);
|
|
1587
|
+
packages.push({
|
|
1588
|
+
id: packageId,
|
|
1589
|
+
sourceId,
|
|
1590
|
+
displayName: entries[0]?.packageDisplayName ?? plugin.id,
|
|
1591
|
+
...entries[0]?.packageDescription ? { description: entries[0].packageDescription } : {},
|
|
1592
|
+
...entries[0]?.packageVersion ? { version: entries[0].packageVersion } : {},
|
|
1593
|
+
artifacts
|
|
1594
|
+
});
|
|
1595
|
+
installations.push({
|
|
1596
|
+
packageId,
|
|
1597
|
+
scope: "workspace",
|
|
1598
|
+
selectedArtifactIds: artifacts.map((artifact) => artifact.id)
|
|
1599
|
+
});
|
|
1600
|
+
for (const entry of entries) {
|
|
1601
|
+
const previous = state.entries.find((candidate) => candidate.identity === entry.artifactId);
|
|
1602
|
+
statesByArtifactId[entry.artifactId] = fallbackHealth ? { intent: "selected", health: fallbackHealth, gate: "ready" } : toArtifactState(entry, previous);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
await mergeRegistrarPackages(sources, packages, installations, statesByArtifactId);
|
|
1606
|
+
return { sources, packages, installations, statesByArtifactId, workspaceManifest: manifest };
|
|
1607
|
+
}
|
|
1608
|
+
async function createFullPersonalSnapshot(options, legacyLinks) {
|
|
1609
|
+
const capabilities = options.personal?.capabilities ?? await (0, import_devtools_core3.createDefaultCopilotHostCapabilities)();
|
|
1610
|
+
const homeDir = options.personal?.homeDir;
|
|
1611
|
+
const store = new import_devtools_core3.PersonalInstallationStore(homeDir !== void 0 ? { homeDir } : {});
|
|
1612
|
+
const reconciler = new import_devtools_core3.PersonalCopilotContentReconciler({
|
|
1613
|
+
capabilities,
|
|
1614
|
+
...homeDir ? { homeDir } : {},
|
|
1615
|
+
...options.personal?.resolvePersonalPackage ? { resolvePackage: options.personal.resolvePersonalPackage } : {}
|
|
1616
|
+
});
|
|
1617
|
+
const intent = await store.read();
|
|
1618
|
+
const inspection = await reconciler.inspect();
|
|
1619
|
+
const sources = [];
|
|
1620
|
+
const sourceIds = /* @__PURE__ */ new Set();
|
|
1621
|
+
const packages = /* @__PURE__ */ new Map();
|
|
1622
|
+
const statesByArtifactId = {
|
|
1623
|
+
...inspection.statesByArtifactId
|
|
1624
|
+
};
|
|
1625
|
+
let legacyCount = 0;
|
|
1626
|
+
for (const link of legacyLinks) {
|
|
1627
|
+
if (link.legacy) legacyCount += 1;
|
|
1628
|
+
}
|
|
1629
|
+
for (const installation of intent.installations) {
|
|
1630
|
+
if (installation.scope !== "personal") continue;
|
|
1631
|
+
const resolved = options.personal ? await options.personal.resolvePersonalPackage(installation.packageId) : [];
|
|
1632
|
+
const artifacts = resolved.filter((entry) => installation.selectedArtifactIds.includes(entry.artifactId)).map(import_devtools_core3.toArtifactSummary);
|
|
1633
|
+
const [sourceId] = installation.packageId.split("::");
|
|
1634
|
+
if (sourceId && !sourceIds.has(sourceId)) {
|
|
1635
|
+
sourceIds.add(sourceId);
|
|
1636
|
+
sources.push({
|
|
1637
|
+
id: sourceId,
|
|
1638
|
+
type: "git",
|
|
1639
|
+
displayName: options.repoDisplayName(sourceId) ?? sourceId,
|
|
1640
|
+
updateCapability: "pinned"
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
if (!packages.has(installation.packageId) && artifacts.length > 0) {
|
|
1644
|
+
packages.set(installation.packageId, {
|
|
1645
|
+
id: installation.packageId,
|
|
1646
|
+
sourceId: sourceId ?? "personal-local",
|
|
1647
|
+
displayName: resolved[0]?.packageDisplayName ?? installation.packageId,
|
|
1648
|
+
...resolved[0]?.packageDescription ? { description: resolved[0].packageDescription } : {},
|
|
1649
|
+
...resolved[0]?.packageVersion ? { version: resolved[0].packageVersion } : {},
|
|
1650
|
+
artifacts
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
await mergeRegistrarPackages(sources, packages, intent.installations, statesByArtifactId);
|
|
1655
|
+
return {
|
|
1656
|
+
sources,
|
|
1657
|
+
packages: [...packages.values()],
|
|
1658
|
+
installations: intent.installations.filter((installation) => installation.scope === "personal"),
|
|
1659
|
+
statesByArtifactId,
|
|
1660
|
+
...legacyCount > 0 ? { legacyCount } : {}
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
function fallbackEntries(plugin, packageId) {
|
|
1664
|
+
const kinds = Object.keys(plugin.artifacts);
|
|
1665
|
+
return kinds.filter((kind) => plugin.artifacts[kind] !== false).map((kind) => ({
|
|
1666
|
+
identity: `${packageId}::${kind}:${plugin.id}`,
|
|
1667
|
+
artifactId: `${packageId}::${kind}:${plugin.id}`,
|
|
1668
|
+
packageId,
|
|
1669
|
+
packageDisplayName: plugin.id,
|
|
1670
|
+
repositoryId: packageId.split("::")[0] ?? packageId,
|
|
1671
|
+
pluginId: plugin.id,
|
|
1672
|
+
kind,
|
|
1673
|
+
sourcePath: "",
|
|
1674
|
+
sourceIsFile: false,
|
|
1675
|
+
name: plugin.id,
|
|
1676
|
+
digest: "",
|
|
1677
|
+
requiresApproval: kind === "hook" || kind === "mcp"
|
|
1678
|
+
}));
|
|
1679
|
+
}
|
|
1680
|
+
function toArtifactState(entry, previous) {
|
|
1681
|
+
if (!previous) {
|
|
1682
|
+
return {
|
|
1683
|
+
intent: "selected",
|
|
1684
|
+
health: "missing",
|
|
1685
|
+
gate: entry.requiresApproval ? "approval-required" : "ready"
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
return {
|
|
1689
|
+
intent: "selected",
|
|
1690
|
+
health: previous.digest === entry.digest ? "healthy" : "drifted",
|
|
1691
|
+
gate: entry.requiresApproval && !previous.approved ? "approval-required" : "ready"
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
function createDefaultProductionReaders() {
|
|
1695
|
+
return {
|
|
1696
|
+
loadWorkspaceManifest: import_devtools_core3.loadWorkspaceCopilotManifest,
|
|
1697
|
+
readWorkspaceState: async (workspaceDir) => new import_devtools_core3.WorkspaceContentStateStore({ workspaceDir }).read(),
|
|
1698
|
+
isSourceAvailable: async (sourceId) => {
|
|
1699
|
+
const stat4 = await fsp2.stat(path3.join((0, import_devtools_core3.getReposDir)(), sourceId)).catch(() => void 0);
|
|
1700
|
+
return stat4?.isDirectory() === true;
|
|
1701
|
+
},
|
|
1702
|
+
resolveWorkspacePlugin: async ({ manifest, plugin }) => {
|
|
1703
|
+
const scopedManifest = manifest.version === 1 ? { ...manifest, plugins: [plugin] } : { ...manifest, plugins: [plugin] };
|
|
1704
|
+
return (await (0, import_devtools_core3.resolveWorkspaceContentPlan)({
|
|
1705
|
+
manifest: scopedManifest,
|
|
1706
|
+
reposDir: (0, import_devtools_core3.getReposDir)()
|
|
1707
|
+
})).entries;
|
|
1708
|
+
},
|
|
1709
|
+
listPersonalLinks: async () => [
|
|
1710
|
+
...(await (0, import_skill_linker.listLinkedSkills)("", "skill", "user")).map((link) => ({
|
|
1711
|
+
repoId: link.repoId,
|
|
1712
|
+
name: link.skillName,
|
|
1713
|
+
kind: "skill",
|
|
1714
|
+
legacy: link.linkPath.includes(`${path3.sep}.agents${path3.sep}`)
|
|
1715
|
+
})),
|
|
1716
|
+
...(await (0, import_skill_linker.listLinkedSkills)("", "agent", "user")).map((link) => ({
|
|
1717
|
+
repoId: link.repoId,
|
|
1718
|
+
name: link.skillName,
|
|
1719
|
+
kind: "agent",
|
|
1720
|
+
legacy: link.linkPath.includes(`${path3.sep}.agents${path3.sep}`)
|
|
1721
|
+
}))
|
|
1722
|
+
]
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
function emptySnapshot() {
|
|
1726
|
+
return {
|
|
1727
|
+
sources: [],
|
|
1728
|
+
packages: [],
|
|
1729
|
+
installations: [],
|
|
1730
|
+
statesByArtifactId: {}
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
function toPublicSource(source) {
|
|
1734
|
+
return {
|
|
1735
|
+
id: source.id,
|
|
1736
|
+
type: source.type,
|
|
1737
|
+
displayName: source.displayName,
|
|
1738
|
+
updateCapability: source.updateCapability
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
function toPublicPackage(pkg) {
|
|
1742
|
+
return {
|
|
1743
|
+
id: pkg.id,
|
|
1744
|
+
sourceId: pkg.sourceId,
|
|
1745
|
+
displayName: pkg.displayName,
|
|
1746
|
+
...pkg.description !== void 0 ? { description: pkg.description } : {},
|
|
1747
|
+
...pkg.version !== void 0 ? { version: pkg.version } : {},
|
|
1748
|
+
artifacts: pkg.artifacts.map((artifact) => ({
|
|
1749
|
+
id: artifact.id,
|
|
1750
|
+
packageId: artifact.packageId,
|
|
1751
|
+
kind: artifact.kind,
|
|
1752
|
+
displayName: artifact.displayName,
|
|
1753
|
+
...artifact.description !== void 0 ? { description: artifact.description } : {},
|
|
1754
|
+
installStrategy: artifact.installStrategy,
|
|
1755
|
+
risk: artifact.risk
|
|
1756
|
+
})),
|
|
1757
|
+
...pkg.wholePackage === true ? { wholePackage: true } : {}
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
function toPublicInstallation(installation) {
|
|
1761
|
+
return {
|
|
1762
|
+
packageId: installation.packageId,
|
|
1763
|
+
scope: installation.scope,
|
|
1764
|
+
selectedArtifactIds: [...installation.selectedArtifactIds],
|
|
1765
|
+
...installation.pinnedVersion !== void 0 ? { pinnedVersion: installation.pinnedVersion } : {}
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
function toPublicStates(states) {
|
|
1769
|
+
return Object.fromEntries(
|
|
1770
|
+
Object.entries(states).map(([artifactId, state]) => [artifactId, { ...state }])
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
// src/bridge/CopilotPluginBridgeHandler.ts
|
|
1775
|
+
var fs2 = __toESM(require("fs/promises"));
|
|
1776
|
+
var path4 = __toESM(require("path"));
|
|
1777
|
+
var import_devtools_core4 = require("@serviceme/devtools-core");
|
|
1778
|
+
async function readManifestInfo(pluginDir) {
|
|
1779
|
+
try {
|
|
1780
|
+
const raw = JSON.parse(await fs2.readFile(path4.join(pluginDir, "plugin.json"), "utf8"));
|
|
1781
|
+
let mcpServers = [];
|
|
1782
|
+
const mcpPath = path4.join(pluginDir, "mcp.json");
|
|
1783
|
+
try {
|
|
1784
|
+
const mcp = JSON.parse(await fs2.readFile(mcpPath, "utf8"));
|
|
1785
|
+
mcpServers = Object.keys(mcp.mcpServers ?? {});
|
|
1786
|
+
} catch {
|
|
1787
|
+
}
|
|
1788
|
+
return {
|
|
1789
|
+
...typeof raw.name === "string" ? { displayName: raw.name } : {},
|
|
1790
|
+
...typeof raw.version === "string" ? { version: raw.version } : {},
|
|
1791
|
+
mcpServers,
|
|
1792
|
+
hasExtensions: Boolean(raw.extensions?.["com.github.copilot"])
|
|
1793
|
+
};
|
|
1794
|
+
} catch {
|
|
1795
|
+
return { mcpServers: [], hasExtensions: false };
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
function toPayload(reg, info) {
|
|
1799
|
+
return {
|
|
1800
|
+
registrationId: reg.registrationId,
|
|
1801
|
+
repoId: reg.repoId,
|
|
1802
|
+
pluginId: reg.pluginId,
|
|
1803
|
+
...reg.displayName ?? info.displayName ? { displayName: reg.displayName ?? info.displayName } : {},
|
|
1804
|
+
...reg.version ?? info.version ? { version: reg.version ?? info.version } : {},
|
|
1805
|
+
scopes: reg.scopes,
|
|
1806
|
+
mcpServers: info.mcpServers,
|
|
1807
|
+
hasExtensions: info.hasExtensions
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
var CopilotPluginBridgeHandler = class {
|
|
1811
|
+
constructor(options = {}) {
|
|
1812
|
+
const homeDir = options.homeDir ?? (0, import_devtools_core4.getHomeDir)();
|
|
1813
|
+
this.registrar = new import_devtools_core4.CopilotPluginRegistrar({
|
|
1814
|
+
// copilotDir is the migration source only — projections live
|
|
1815
|
+
// under the SERVICEME home, out of VS Code's ~/.copilot
|
|
1816
|
+
// reconciliation reach.
|
|
1817
|
+
copilotDir: path4.join(homeDir, ".copilot"),
|
|
1818
|
+
pluginsDir: path4.join(homeDir, ".serviceme", "copilot-plugins")
|
|
1819
|
+
});
|
|
1820
|
+
this.reposDir = path4.join(homeDir, ".serviceme", "repos");
|
|
1821
|
+
}
|
|
1822
|
+
pluginDir(repoId, pluginId) {
|
|
1823
|
+
return path4.join(this.reposDir, (0, import_devtools_core4.assertSafeRepoId)(repoId), "plugins", pluginId);
|
|
1824
|
+
}
|
|
1825
|
+
async list(_params) {
|
|
1826
|
+
const registrations = await this.registrar.list({});
|
|
1827
|
+
const plugins = await Promise.all(
|
|
1828
|
+
registrations.map(async (reg) => toPayload(reg, await readManifestInfo(reg.pluginDir)))
|
|
1829
|
+
);
|
|
1830
|
+
return { plugins };
|
|
1831
|
+
}
|
|
1832
|
+
async register(params) {
|
|
1833
|
+
const pluginDir = this.pluginDir(params.repoId, params.pluginId);
|
|
1834
|
+
const info = await readManifestInfo(pluginDir);
|
|
1835
|
+
await this.registrar.register({
|
|
1836
|
+
repoId: params.repoId,
|
|
1837
|
+
pluginId: params.pluginId,
|
|
1838
|
+
pluginDir,
|
|
1839
|
+
scope: params.scope,
|
|
1840
|
+
...info.displayName !== void 0 ? { displayName: info.displayName } : {},
|
|
1841
|
+
...info.version !== void 0 ? { version: info.version } : {}
|
|
1842
|
+
});
|
|
1843
|
+
return this.list({});
|
|
1844
|
+
}
|
|
1845
|
+
async unregister(params) {
|
|
1846
|
+
const normalized = params.registrationId.replace("::", ":");
|
|
1847
|
+
await this.registrar.unregister({
|
|
1848
|
+
registrationId: normalized,
|
|
1849
|
+
scope: params.scope
|
|
1850
|
+
});
|
|
1851
|
+
if (normalized !== params.registrationId) {
|
|
1852
|
+
await this.registrar.unregister({
|
|
1853
|
+
registrationId: params.registrationId,
|
|
1854
|
+
scope: params.scope
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
const otherScope = params.scope === "personal" ? "workspace" : "personal";
|
|
1858
|
+
await this.registrar.unregister({
|
|
1859
|
+
registrationId: normalized,
|
|
1860
|
+
scope: otherScope
|
|
1861
|
+
});
|
|
1862
|
+
return this.list({});
|
|
1863
|
+
}
|
|
1864
|
+
};
|
|
782
1865
|
|
|
783
1866
|
// src/bridge/handlers/AuthBridgeHandlers.ts
|
|
784
1867
|
var import_auth3 = require("@serviceme/devtools-core/auth");
|
|
@@ -893,12 +1976,12 @@ function writeBridgeMessage(message) {
|
|
|
893
1976
|
}
|
|
894
1977
|
|
|
895
1978
|
// src/bridge/TaskBridgeHandler.ts
|
|
896
|
-
var
|
|
1979
|
+
var import_devtools_core5 = require("@serviceme/devtools-core");
|
|
897
1980
|
var TaskBridgeHandler = class {
|
|
898
1981
|
constructor(logger, emitEvent) {
|
|
899
1982
|
this.logger = logger;
|
|
900
1983
|
this.emitEvent = emitEvent;
|
|
901
|
-
this.engine = new
|
|
1984
|
+
this.engine = new import_devtools_core5.TaskExecutionEngine((taskType) => (0, import_devtools_core5.getExecutor)(taskType));
|
|
902
1985
|
this.engine.setListener({
|
|
903
1986
|
onStarted: (params) => this.emitEvent("task.started", params),
|
|
904
1987
|
onOutput: (params) => this.emitEvent("task.output", params),
|
|
@@ -941,6 +2024,7 @@ var CAPABILITIES = {
|
|
|
941
2024
|
tasks: 1,
|
|
942
2025
|
skillRepo: 1,
|
|
943
2026
|
repoMgmt: 1,
|
|
2027
|
+
copilotContent: 1,
|
|
944
2028
|
auth: 1,
|
|
945
2029
|
device: 1,
|
|
946
2030
|
toolbox: 1
|
|
@@ -953,6 +2037,9 @@ var BridgeServer = class {
|
|
|
953
2037
|
this.writeEvent(event, params);
|
|
954
2038
|
});
|
|
955
2039
|
this.skillRepoHandler = opts.skillRepoHandler;
|
|
2040
|
+
this.copilotContentHandler = opts.skillRepoHandler ? new CopilotContentBridgeHandler(opts.skillRepoHandler.copilotContentDependencies) : void 0;
|
|
2041
|
+
this.copilotCustomizationsHandler = opts.skillRepoHandler ? new CopilotCustomizationsBridgeHandler(opts.skillRepoHandler.copilotContentDependencies) : void 0;
|
|
2042
|
+
this.copilotPluginHandler = opts.skillRepoHandler ? new CopilotPluginBridgeHandler() : void 0;
|
|
956
2043
|
this.authHandler = new AuthBridgeHandlers();
|
|
957
2044
|
this.deviceHandler = new DeviceBridgeHandlers();
|
|
958
2045
|
this.toolboxHandler = new ToolboxBridgeHandlers();
|
|
@@ -962,12 +2049,12 @@ var BridgeServer = class {
|
|
|
962
2049
|
input: process.stdin,
|
|
963
2050
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
964
2051
|
});
|
|
965
|
-
await new Promise((
|
|
2052
|
+
await new Promise((resolve4) => {
|
|
966
2053
|
reader.on("line", (line) => {
|
|
967
2054
|
void this.handleLine(line);
|
|
968
2055
|
});
|
|
969
2056
|
reader.on("close", () => {
|
|
970
|
-
|
|
2057
|
+
resolve4();
|
|
971
2058
|
});
|
|
972
2059
|
});
|
|
973
2060
|
}
|
|
@@ -1058,6 +2145,7 @@ var BridgeServer = class {
|
|
|
1058
2145
|
this.writeSuccess(request.id, result);
|
|
1059
2146
|
return;
|
|
1060
2147
|
}
|
|
2148
|
+
case "copilotContent.list":
|
|
1061
2149
|
case "skillRepo.list": {
|
|
1062
2150
|
const r = request;
|
|
1063
2151
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1065,6 +2153,7 @@ var BridgeServer = class {
|
|
|
1065
2153
|
this.writeSuccess(request.id, result);
|
|
1066
2154
|
return;
|
|
1067
2155
|
}
|
|
2156
|
+
case "copilotContent.get":
|
|
1068
2157
|
case "skillRepo.get": {
|
|
1069
2158
|
const r = request;
|
|
1070
2159
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1072,6 +2161,7 @@ var BridgeServer = class {
|
|
|
1072
2161
|
this.writeSuccess(request.id, result);
|
|
1073
2162
|
return;
|
|
1074
2163
|
}
|
|
2164
|
+
case "copilotContent.install":
|
|
1075
2165
|
case "skillRepo.install": {
|
|
1076
2166
|
const r = request;
|
|
1077
2167
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1079,6 +2169,7 @@ var BridgeServer = class {
|
|
|
1079
2169
|
this.writeSuccess(request.id, result);
|
|
1080
2170
|
return;
|
|
1081
2171
|
}
|
|
2172
|
+
case "copilotContent.convertToSymlink":
|
|
1082
2173
|
case "skillRepo.convertToSymlink": {
|
|
1083
2174
|
const r = request;
|
|
1084
2175
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1086,6 +2177,7 @@ var BridgeServer = class {
|
|
|
1086
2177
|
this.writeSuccess(request.id, result);
|
|
1087
2178
|
return;
|
|
1088
2179
|
}
|
|
2180
|
+
case "copilotContent.uninstall":
|
|
1089
2181
|
case "skillRepo.uninstall": {
|
|
1090
2182
|
const r = request;
|
|
1091
2183
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1093,6 +2185,15 @@ var BridgeServer = class {
|
|
|
1093
2185
|
this.writeSuccess(request.id, result);
|
|
1094
2186
|
return;
|
|
1095
2187
|
}
|
|
2188
|
+
case "copilotContent.setEntryEnabled":
|
|
2189
|
+
case "skillRepo.setEntryEnabled": {
|
|
2190
|
+
const r = request;
|
|
2191
|
+
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
2192
|
+
const result = await this.skillRepoHandler.setEntryEnabled(r.params);
|
|
2193
|
+
this.writeSuccess(request.id, result);
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
case "copilotContent.listLinked":
|
|
1096
2197
|
case "skillRepo.listLinked": {
|
|
1097
2198
|
const r = request;
|
|
1098
2199
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1100,6 +2201,7 @@ var BridgeServer = class {
|
|
|
1100
2201
|
this.writeSuccess(request.id, result);
|
|
1101
2202
|
return;
|
|
1102
2203
|
}
|
|
2204
|
+
case "copilotContent.draft.create":
|
|
1103
2205
|
case "skillRepo.draft.create": {
|
|
1104
2206
|
const r = request;
|
|
1105
2207
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1107,6 +2209,7 @@ var BridgeServer = class {
|
|
|
1107
2209
|
this.writeSuccess(request.id, result);
|
|
1108
2210
|
return;
|
|
1109
2211
|
}
|
|
2212
|
+
case "copilotContent.draft.commit":
|
|
1110
2213
|
case "skillRepo.draft.commit": {
|
|
1111
2214
|
const r = request;
|
|
1112
2215
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1114,6 +2217,7 @@ var BridgeServer = class {
|
|
|
1114
2217
|
this.writeSuccess(request.id, result);
|
|
1115
2218
|
return;
|
|
1116
2219
|
}
|
|
2220
|
+
case "copilotContent.draft.list":
|
|
1117
2221
|
case "skillRepo.draft.list": {
|
|
1118
2222
|
const r = request;
|
|
1119
2223
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1121,6 +2225,7 @@ var BridgeServer = class {
|
|
|
1121
2225
|
this.writeSuccess(request.id, result);
|
|
1122
2226
|
return;
|
|
1123
2227
|
}
|
|
2228
|
+
case "copilotContent.draft.delete":
|
|
1124
2229
|
case "skillRepo.draft.delete": {
|
|
1125
2230
|
const r = request;
|
|
1126
2231
|
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
@@ -1184,6 +2289,140 @@ var BridgeServer = class {
|
|
|
1184
2289
|
this.writeSuccess(request.id, result);
|
|
1185
2290
|
return;
|
|
1186
2291
|
}
|
|
2292
|
+
case "repo.resetParseCache": {
|
|
2293
|
+
const r = request;
|
|
2294
|
+
if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
|
|
2295
|
+
const result = await this.skillRepoHandler.repoResetParseCache(r.params);
|
|
2296
|
+
this.writeSuccess(request.id, result);
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
case "copilotPlugin.list": {
|
|
2300
|
+
const r = request;
|
|
2301
|
+
if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
|
|
2302
|
+
const listResult = await this.copilotPluginHandler.list(r.params);
|
|
2303
|
+
this.writeSuccess(request.id, listResult);
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
case "copilotPlugin.register": {
|
|
2307
|
+
const r = request;
|
|
2308
|
+
if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
|
|
2309
|
+
const regResult = await this.copilotPluginHandler.register(r.params);
|
|
2310
|
+
this.writeSuccess(request.id, regResult);
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
case "copilotPlugin.unregister": {
|
|
2314
|
+
const r = request;
|
|
2315
|
+
if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
|
|
2316
|
+
const unregResult = await this.copilotPluginHandler.unregister(r.params);
|
|
2317
|
+
this.writeSuccess(request.id, unregResult);
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
// ── copilotContent.* (declarative reconciliation) ──────────────
|
|
2321
|
+
case "copilotContent.status": {
|
|
2322
|
+
const r = request;
|
|
2323
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
2324
|
+
const result = await this.copilotContentHandler.status(r.params);
|
|
2325
|
+
this.writeSuccess(request.id, result);
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
case "copilotContent.restore": {
|
|
2329
|
+
const r = request;
|
|
2330
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
2331
|
+
const result = await this.copilotContentHandler.restore(r.params);
|
|
2332
|
+
this.writeSuccess(request.id, result);
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
case "copilotContent.approve": {
|
|
2336
|
+
const r = request;
|
|
2337
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
2338
|
+
const result = await this.copilotContentHandler.approve(r.params);
|
|
2339
|
+
this.writeSuccess(request.id, result);
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
case "copilotContent.migrateLegacy": {
|
|
2343
|
+
const r = request;
|
|
2344
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
2345
|
+
const result = await this.copilotContentHandler.migrateLegacy(r.params);
|
|
2346
|
+
this.writeSuccess(request.id, result);
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2349
|
+
case "copilotContent.integrationStatus": {
|
|
2350
|
+
const r = request;
|
|
2351
|
+
if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
|
|
2352
|
+
const result = await this.copilotContentHandler.integrationStatus(r.params);
|
|
2353
|
+
this.writeSuccess(request.id, result);
|
|
2354
|
+
return;
|
|
2355
|
+
}
|
|
2356
|
+
case "copilotContent.customizations.list": {
|
|
2357
|
+
const r = request;
|
|
2358
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2359
|
+
const result = await this.copilotCustomizationsHandler.list(r.params);
|
|
2360
|
+
this.writeSuccess(request.id, result);
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
case "copilotContent.package.install": {
|
|
2364
|
+
const r = request;
|
|
2365
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2366
|
+
const result = await this.copilotCustomizationsHandler.install(r.params);
|
|
2367
|
+
this.writeSuccess(request.id, result);
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
case "copilotContent.package.update": {
|
|
2371
|
+
const r = request;
|
|
2372
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2373
|
+
const result = await this.copilotCustomizationsHandler.update(r.params);
|
|
2374
|
+
this.writeSuccess(request.id, result);
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
case "copilotContent.package.uninstall": {
|
|
2378
|
+
const r = request;
|
|
2379
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2380
|
+
const result = await this.copilotCustomizationsHandler.uninstall(r.params);
|
|
2381
|
+
this.writeSuccess(request.id, result);
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2384
|
+
case "copilotContent.package.move": {
|
|
2385
|
+
const r = request;
|
|
2386
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2387
|
+
const result = await this.copilotCustomizationsHandler.move(r.params);
|
|
2388
|
+
this.writeSuccess(request.id, result);
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
case "copilotContent.legacy.preview": {
|
|
2392
|
+
const r = request;
|
|
2393
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2394
|
+
const result = await this.copilotCustomizationsHandler.legacyPreview(r.params);
|
|
2395
|
+
this.writeSuccess(request.id, result);
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
case "copilotContent.legacy.migrate": {
|
|
2399
|
+
const r = request;
|
|
2400
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2401
|
+
const result = await this.copilotCustomizationsHandler.legacyMigrate(r.params);
|
|
2402
|
+
this.writeSuccess(request.id, result);
|
|
2403
|
+
return;
|
|
2404
|
+
}
|
|
2405
|
+
case "copilotContent.sources.list": {
|
|
2406
|
+
const r = request;
|
|
2407
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2408
|
+
const result = await this.copilotCustomizationsHandler.sources(r.params);
|
|
2409
|
+
this.writeSuccess(request.id, result);
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
case "copilotContent.sources.remove": {
|
|
2413
|
+
const r = request;
|
|
2414
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2415
|
+
const result = await this.copilotCustomizationsHandler.removeSource(r.params);
|
|
2416
|
+
this.writeSuccess(request.id, result);
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
case "copilotContent.package.previewUpdate": {
|
|
2420
|
+
const r = request;
|
|
2421
|
+
if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
|
|
2422
|
+
const result = await this.copilotCustomizationsHandler.previewUpdate(r.params);
|
|
2423
|
+
this.writeSuccess(request.id, result);
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
1187
2426
|
// ── auth.* (Phase 5.4) ─────────────────────────────────────────
|
|
1188
2427
|
case "auth.status": {
|
|
1189
2428
|
const r = request;
|
|
@@ -1287,6 +2526,15 @@ var BridgeServer = class {
|
|
|
1287
2526
|
)
|
|
1288
2527
|
);
|
|
1289
2528
|
}
|
|
2529
|
+
unsupportedCopilotContent(id) {
|
|
2530
|
+
this.writeError(
|
|
2531
|
+
id,
|
|
2532
|
+
createServicemeError(
|
|
2533
|
+
"invalid_params",
|
|
2534
|
+
"copilotContent.* methods require v2 CLI deps; the bridge was started without a CopilotContentBridgeHandler."
|
|
2535
|
+
)
|
|
2536
|
+
);
|
|
2537
|
+
}
|
|
1290
2538
|
writeError(id, error) {
|
|
1291
2539
|
writeBridgeMessage({
|
|
1292
2540
|
protocolVersion: SERVICEME_PROTOCOL_VERSION,
|
|
@@ -1307,16 +2555,37 @@ var BridgeServer = class {
|
|
|
1307
2555
|
};
|
|
1308
2556
|
|
|
1309
2557
|
// src/bridge/SkillRepoBridgeHandler.ts
|
|
1310
|
-
var
|
|
1311
|
-
var
|
|
2558
|
+
var fs3 = __toESM(require("fs/promises"));
|
|
2559
|
+
var path5 = __toESM(require("path"));
|
|
2560
|
+
var import_devtools_core6 = require("@serviceme/devtools-core");
|
|
2561
|
+
var import_skill_linker2 = require("@serviceme/devtools-core/skill-linker");
|
|
1312
2562
|
var import_skill_store = require("@serviceme/devtools-core/skill-store");
|
|
1313
2563
|
var import_submit = require("@serviceme/devtools-core/submit");
|
|
2564
|
+
var DEFAULT_SKILL_STORE_CACHE_TTL_MS = 5e3;
|
|
1314
2565
|
var SkillRepoBridgeHandler = class {
|
|
1315
2566
|
constructor(opts) {
|
|
1316
2567
|
this.reposStore = opts.reposStore;
|
|
1317
2568
|
this.gitClient = opts.gitClient;
|
|
1318
2569
|
this.draftsStore = opts.draftsStore;
|
|
1319
2570
|
this.submitClient = opts.submitClient;
|
|
2571
|
+
this.skillStoreCacheTtlMs = opts.skillStoreCacheTtlMs ?? DEFAULT_SKILL_STORE_CACHE_TTL_MS;
|
|
2572
|
+
}
|
|
2573
|
+
/** Shared deps for sibling handlers (copilotContent.*). */
|
|
2574
|
+
get dependencies() {
|
|
2575
|
+
return {
|
|
2576
|
+
reposStore: this.reposStore,
|
|
2577
|
+
gitClient: this.gitClient,
|
|
2578
|
+
draftsStore: this.draftsStore,
|
|
2579
|
+
submitClient: this.submitClient
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
/** Narrowed deps for CopilotContentBridgeHandler construction. */
|
|
2583
|
+
get copilotContentDependencies() {
|
|
2584
|
+
return {
|
|
2585
|
+
store: this.reposStore,
|
|
2586
|
+
gitClient: this.gitClient,
|
|
2587
|
+
skipClone: true
|
|
2588
|
+
};
|
|
1320
2589
|
}
|
|
1321
2590
|
// ─────────────────────────────────────────────────────────────────
|
|
1322
2591
|
// skillRepo.*
|
|
@@ -1326,11 +2595,22 @@ var SkillRepoBridgeHandler = class {
|
|
|
1326
2595
|
* filter by `repoId` or `kind`. Local-only — no git, no network.
|
|
1327
2596
|
*/
|
|
1328
2597
|
async list(params) {
|
|
1329
|
-
const store = this.buildSkillStore(
|
|
2598
|
+
const store = this.buildSkillStore();
|
|
1330
2599
|
const entries = params.repoId ? await store.listByRepo(params.repoId) : await store.listAll();
|
|
1331
2600
|
const filtered = params.kind ? entries.filter((e) => e.kind === params.kind) : entries;
|
|
2601
|
+
let marks = [];
|
|
2602
|
+
try {
|
|
2603
|
+
marks = await new import_devtools_core6.DisabledContentStore().list();
|
|
2604
|
+
} catch {
|
|
2605
|
+
}
|
|
2606
|
+
const isDisabled = (entry) => marks.some(
|
|
2607
|
+
(mark) => mark.repoId === entry.repoId && mark.name === entry.name && mark.kind === entry.kind && (mark.scope === "user" || params.workspaceDir !== void 0 && mark.workspaceDir === params.workspaceDir)
|
|
2608
|
+
);
|
|
1332
2609
|
return {
|
|
1333
|
-
entries: filtered.map(
|
|
2610
|
+
entries: filtered.map((entry) => ({
|
|
2611
|
+
...toBridgeEntry(entry),
|
|
2612
|
+
disabled: isDisabled(entry)
|
|
2613
|
+
}))
|
|
1334
2614
|
};
|
|
1335
2615
|
}
|
|
1336
2616
|
/**
|
|
@@ -1338,7 +2618,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1338
2618
|
* Throws `skill_not_found` when the entry doesn't exist.
|
|
1339
2619
|
*/
|
|
1340
2620
|
async get(params) {
|
|
1341
|
-
const store = this.buildSkillStore(
|
|
2621
|
+
const store = this.buildSkillStore();
|
|
1342
2622
|
let detail;
|
|
1343
2623
|
try {
|
|
1344
2624
|
detail = await store.get(params.repoId, params.name);
|
|
@@ -1367,31 +2647,100 @@ var SkillRepoBridgeHandler = class {
|
|
|
1367
2647
|
async install(params) {
|
|
1368
2648
|
const kind = params.kind ?? "skill";
|
|
1369
2649
|
const source = await this.resolveEntrySource(params.repoId, params.name, kind);
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
2650
|
+
if ((params.scope ?? "workspace") === "workspace") {
|
|
2651
|
+
return this.installThroughDeclaration(params, kind, source);
|
|
2652
|
+
}
|
|
2653
|
+
return this.installUserScope(params, kind, source);
|
|
2654
|
+
}
|
|
2655
|
+
/**
|
|
2656
|
+
* Workspace installs go through the shared declaration: pin the
|
|
2657
|
+
* repository at its current commit, add the selection to
|
|
2658
|
+
* .github/serviceme-plugins.json, then let the reconciler create
|
|
2659
|
+
* the owned link. This keeps the manifest the single source of
|
|
2660
|
+
* truth teammates restore from.
|
|
2661
|
+
*/
|
|
2662
|
+
async installThroughDeclaration(params, kind, source) {
|
|
2663
|
+
const repoRoot = (0, import_devtools_core6.getRepoDir)(params.repoId);
|
|
2664
|
+
const repo = this.reposStore.get(params.repoId);
|
|
2665
|
+
if (!repo) {
|
|
2666
|
+
throw createServicemeError("not_found", `Repository ${params.repoId} not found`);
|
|
2667
|
+
}
|
|
2668
|
+
const commit = await this.resolvePinnedCommit(params.workspaceDir, repoRoot, params.repoId);
|
|
2669
|
+
if (!/^[0-9a-f]{40}$/.test(commit)) {
|
|
2670
|
+
throw createServicemeError(
|
|
2671
|
+
"internal_error",
|
|
2672
|
+
`Cannot pin repository ${params.repoId}: git returned an invalid commit '${commit}'`
|
|
2673
|
+
);
|
|
2674
|
+
}
|
|
2675
|
+
await (0, import_devtools_core6.upsertWorkspaceContentSelection)({
|
|
2676
|
+
workspaceDir: params.workspaceDir,
|
|
2677
|
+
repository: { id: repo.id, url: repo.url, commit },
|
|
2678
|
+
pluginId: params.name,
|
|
2679
|
+
kind
|
|
2680
|
+
});
|
|
2681
|
+
const restored = await this.reconcileInstalledContent(params.workspaceDir);
|
|
2682
|
+
const linkBasename = source.sourceIsFile ? path5.basename(source.sourcePath) : params.name;
|
|
2683
|
+
const expectedIdentity = `${params.repoId}::${params.name}::${kind}:${stripAgentSuffix(linkBasename)}`;
|
|
2684
|
+
const entry = restored.entries.find((candidate) => candidate.identity === expectedIdentity);
|
|
2685
|
+
if (!entry || entry.status !== "restored" && entry.status !== "adopted") {
|
|
2686
|
+
throw createServicemeError(
|
|
2687
|
+
"internal_error",
|
|
2688
|
+
`Copilot content ${expectedIdentity} failed to activate: ${entry?.message ?? "no matching entry after reconcile"}`
|
|
2689
|
+
);
|
|
2690
|
+
}
|
|
2691
|
+
const linkPath = path5.join(
|
|
2692
|
+
params.workspaceDir,
|
|
2693
|
+
".github",
|
|
2694
|
+
kind === "agent" ? "agents" : "skills",
|
|
2695
|
+
linkBasename
|
|
2696
|
+
);
|
|
2697
|
+
const targetStat = await fs3.lstat(linkPath);
|
|
2698
|
+
if (!targetStat.isSymbolicLink() && !targetStat.isDirectory()) {
|
|
2699
|
+
throw createServicemeError(
|
|
2700
|
+
"internal_error",
|
|
2701
|
+
`Copilot content link at ${linkPath} was not created by reconcile`
|
|
2702
|
+
);
|
|
2703
|
+
}
|
|
2704
|
+
const target = await fs3.readlink(linkPath).catch(() => linkPath);
|
|
2705
|
+
return { mode: "symlink", linkPath, targetPath: target };
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* User installs create a local ~/.copilot link without touching the shared
|
|
2709
|
+
* declaration. Idempotent: an already-correct link is a no-op, any other
|
|
2710
|
+
* stale entry at the path is replaced. No copy fallback.
|
|
2711
|
+
*/
|
|
2712
|
+
async installUserScope(params, kind, source) {
|
|
2713
|
+
const userRoot = path5.join((0, import_devtools_core6.getHomeDir)(), ".copilot", kind === "agent" ? "agents" : "skills");
|
|
2714
|
+
const linkPath = path5.join(
|
|
2715
|
+
userRoot,
|
|
2716
|
+
source.sourceIsFile ? path5.basename(source.sourcePath) : params.name
|
|
2717
|
+
);
|
|
2718
|
+
await fs3.mkdir(path5.dirname(linkPath), { recursive: true });
|
|
2719
|
+
const existingTarget = await fs3.readlink(linkPath).catch(() => void 0);
|
|
2720
|
+
if (existingTarget !== void 0) {
|
|
2721
|
+
const normalizedExisting = path5.resolve(path5.dirname(linkPath), existingTarget);
|
|
2722
|
+
if (normalizedExisting === path5.resolve(source.sourcePath)) {
|
|
2723
|
+
return { mode: "symlink", linkPath, targetPath: source.sourcePath };
|
|
1392
2724
|
}
|
|
1393
|
-
|
|
2725
|
+
await fs3.rm(linkPath, { recursive: true, force: true });
|
|
2726
|
+
}
|
|
2727
|
+
await fs3.symlink(source.sourcePath, linkPath, source.sourceIsFile ? "file" : "dir");
|
|
2728
|
+
return { mode: "symlink", linkPath, targetPath: source.sourcePath };
|
|
2729
|
+
}
|
|
2730
|
+
async resolvePinnedCommit(workspaceDir, repoRoot, repoId) {
|
|
2731
|
+
const existing = await (0, import_devtools_core6.loadWorkspaceCopilotManifest)(workspaceDir);
|
|
2732
|
+
if (existing) {
|
|
2733
|
+
const declared = (0, import_devtools_core6.getWorkspaceManifestSources)(existing).find(
|
|
2734
|
+
(source) => source.id === repoId && source.type === "git"
|
|
2735
|
+
);
|
|
2736
|
+
if (declared) return declared.commit;
|
|
1394
2737
|
}
|
|
2738
|
+
return this.gitClient.revParseHead(repoRoot);
|
|
2739
|
+
}
|
|
2740
|
+
async reconcileInstalledContent(workspaceDir) {
|
|
2741
|
+
const { CopilotContentBridgeHandler: CopilotContentBridgeHandler2 } = await Promise.resolve().then(() => (init_CopilotContentBridgeHandler(), CopilotContentBridgeHandler_exports));
|
|
2742
|
+
const handler = new CopilotContentBridgeHandler2(this.copilotContentDependencies);
|
|
2743
|
+
return handler.restore({ workspaceDir });
|
|
1395
2744
|
}
|
|
1396
2745
|
/**
|
|
1397
2746
|
* Replace an existing, real (non-symlink) skill/agent directory at
|
|
@@ -1403,7 +2752,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1403
2752
|
const kind = params.kind ?? "skill";
|
|
1404
2753
|
const source = await this.resolveEntrySource(params.repoId, params.name, kind);
|
|
1405
2754
|
try {
|
|
1406
|
-
const result = await (0,
|
|
2755
|
+
const result = await (0, import_skill_linker2.convertToSymlink)({
|
|
1407
2756
|
repoId: params.repoId,
|
|
1408
2757
|
skillName: params.name,
|
|
1409
2758
|
workspaceDir: params.workspaceDir,
|
|
@@ -1419,7 +2768,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1419
2768
|
targetPath: result.targetPath
|
|
1420
2769
|
};
|
|
1421
2770
|
} catch (err) {
|
|
1422
|
-
if (err instanceof
|
|
2771
|
+
if (err instanceof import_skill_linker2.LinkError) {
|
|
1423
2772
|
throw createServicemeError(
|
|
1424
2773
|
"internal_error",
|
|
1425
2774
|
`Failed to link ${params.repoId}/${params.name}: ${err.message}`
|
|
@@ -1433,12 +2782,22 @@ var SkillRepoBridgeHandler = class {
|
|
|
1433
2782
|
}
|
|
1434
2783
|
/** Inverse of `install`. Throws when no link exists. */
|
|
1435
2784
|
async uninstall(params) {
|
|
2785
|
+
const kind = params.kind ?? "skill";
|
|
2786
|
+
if ((params.scope ?? "workspace") === "workspace") {
|
|
2787
|
+
await (0, import_devtools_core6.removeWorkspaceContentSelection)({
|
|
2788
|
+
workspaceDir: params.workspaceDir,
|
|
2789
|
+
repositoryId: params.repoId,
|
|
2790
|
+
pluginId: params.name,
|
|
2791
|
+
kind
|
|
2792
|
+
});
|
|
2793
|
+
await this.reconcileInstalledContent(params.workspaceDir);
|
|
2794
|
+
}
|
|
1436
2795
|
try {
|
|
1437
|
-
await (0,
|
|
2796
|
+
await (0, import_skill_linker2.uninstallSkillFromWorkspace)({
|
|
1438
2797
|
repoId: params.repoId,
|
|
1439
2798
|
skillName: params.name,
|
|
1440
2799
|
workspaceDir: params.workspaceDir,
|
|
1441
|
-
kind
|
|
2800
|
+
kind,
|
|
1442
2801
|
scope: params.scope
|
|
1443
2802
|
});
|
|
1444
2803
|
} catch (err) {
|
|
@@ -1452,6 +2811,69 @@ var SkillRepoBridgeHandler = class {
|
|
|
1452
2811
|
}
|
|
1453
2812
|
return { removed: true };
|
|
1454
2813
|
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Enable/disable a per-artifact skill/agent WITHOUT dropping its
|
|
2816
|
+
* installation. Disable records a machine-local mark and removes the
|
|
2817
|
+
* materialized link (the workspace manifest declaration stays); the
|
|
2818
|
+
* reconciler treats marked identities as undeclared, so a restore
|
|
2819
|
+
* never resurrects them. Enable clears the mark and re-links.
|
|
2820
|
+
*/
|
|
2821
|
+
async setEntryEnabled(params) {
|
|
2822
|
+
const kind = params.kind ?? "skill";
|
|
2823
|
+
const scope = params.scope ?? "workspace";
|
|
2824
|
+
if (scope === "workspace" && !params.workspaceDir) {
|
|
2825
|
+
throw createServicemeError("invalid_params", "workspaceDir is required in workspace scope");
|
|
2826
|
+
}
|
|
2827
|
+
const store = new import_devtools_core6.DisabledContentStore();
|
|
2828
|
+
const mark = {
|
|
2829
|
+
scope,
|
|
2830
|
+
repoId: params.repoId,
|
|
2831
|
+
name: params.name,
|
|
2832
|
+
kind,
|
|
2833
|
+
...scope === "workspace" && params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
|
|
2834
|
+
};
|
|
2835
|
+
if (params.enabled) {
|
|
2836
|
+
await store.remove(mark);
|
|
2837
|
+
if (scope === "workspace") {
|
|
2838
|
+
await this.reconcileInstalledContent(params.workspaceDir);
|
|
2839
|
+
} else {
|
|
2840
|
+
const source = await this.resolveEntrySource(params.repoId, params.name, kind);
|
|
2841
|
+
await this.installUserScope(
|
|
2842
|
+
{ ...params, workspaceDir: params.workspaceDir ?? "", scope: "user" },
|
|
2843
|
+
kind,
|
|
2844
|
+
source
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
return { enabled: true };
|
|
2848
|
+
}
|
|
2849
|
+
await store.add(mark);
|
|
2850
|
+
if (scope === "workspace") {
|
|
2851
|
+
const stateStore = new import_devtools_core6.WorkspaceContentStateStore({
|
|
2852
|
+
workspaceDir: params.workspaceDir
|
|
2853
|
+
});
|
|
2854
|
+
const state = await stateStore.read();
|
|
2855
|
+
for (const previous of state.entries) {
|
|
2856
|
+
if (previous.repositoryId !== params.repoId || previous.pluginId !== params.name || previous.kind !== kind || !previous.linkPath.replace(/\\/g, "/").includes(".github")) {
|
|
2857
|
+
continue;
|
|
2858
|
+
}
|
|
2859
|
+
await fs3.rm(path5.resolve(params.workspaceDir, previous.linkPath), {
|
|
2860
|
+
force: true,
|
|
2861
|
+
recursive: true
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
await this.reconcileInstalledContent(params.workspaceDir);
|
|
2865
|
+
} else {
|
|
2866
|
+
let base = params.name;
|
|
2867
|
+
try {
|
|
2868
|
+
const source = await this.resolveEntrySource(params.repoId, params.name, kind);
|
|
2869
|
+
base = source.sourceIsFile ? path5.basename(source.sourcePath) : params.name;
|
|
2870
|
+
} catch {
|
|
2871
|
+
}
|
|
2872
|
+
const userRoot = path5.join((0, import_devtools_core6.getHomeDir)(), ".copilot", kind === "agent" ? "agents" : "skills");
|
|
2873
|
+
await fs3.rm(path5.join(userRoot, base), { force: true, recursive: true });
|
|
2874
|
+
}
|
|
2875
|
+
return { enabled: false };
|
|
2876
|
+
}
|
|
1455
2877
|
/**
|
|
1456
2878
|
* List every skill currently linked into a workspace and/or the
|
|
1457
2879
|
* global user scope. `scope` defaults to `"workspace"` for
|
|
@@ -1462,7 +2884,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1462
2884
|
const kind = params.kind ?? "skill";
|
|
1463
2885
|
const scope = params.scope ?? "workspace";
|
|
1464
2886
|
const scopesToScan = scope === "all" ? ["workspace", "user"] : [scope];
|
|
1465
|
-
const linked = (await Promise.all(scopesToScan.map((s) => (0,
|
|
2887
|
+
const linked = (await Promise.all(scopesToScan.map((s) => (0, import_skill_linker2.listLinkedSkills)(params.workspaceDir, kind, s)))).flat();
|
|
1466
2888
|
return {
|
|
1467
2889
|
links: linked.map((l) => ({
|
|
1468
2890
|
repoId: l.repoId,
|
|
@@ -1492,7 +2914,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1492
2914
|
});
|
|
1493
2915
|
return { id };
|
|
1494
2916
|
} catch (err) {
|
|
1495
|
-
if (err instanceof
|
|
2917
|
+
if (err instanceof import_devtools_core6.InvalidDraftError) {
|
|
1496
2918
|
throw createServicemeError("invalid_params", err.message);
|
|
1497
2919
|
}
|
|
1498
2920
|
throw err;
|
|
@@ -1517,6 +2939,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1517
2939
|
detail.files,
|
|
1518
2940
|
{ branch: params.branch, skipPush: params.skipPush }
|
|
1519
2941
|
);
|
|
2942
|
+
this.invalidateSkillStoreCache();
|
|
1520
2943
|
return {
|
|
1521
2944
|
repoId: result.repoId,
|
|
1522
2945
|
skillName: result.skillName,
|
|
@@ -1638,6 +3061,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1638
3061
|
async repoSync(params) {
|
|
1639
3062
|
const repoManager = this.buildRepoManager();
|
|
1640
3063
|
const result = await repoManager.pullOne(params.repoId);
|
|
3064
|
+
this.invalidateSkillStoreCache();
|
|
1641
3065
|
return {
|
|
1642
3066
|
pulls: [
|
|
1643
3067
|
{
|
|
@@ -1652,6 +3076,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1652
3076
|
async repoSyncAll(_params) {
|
|
1653
3077
|
const repoManager = this.buildRepoManager();
|
|
1654
3078
|
const report = await repoManager.pullAll();
|
|
3079
|
+
this.invalidateSkillStoreCache();
|
|
1655
3080
|
return {
|
|
1656
3081
|
pulls: report.pulls.map((p) => ({
|
|
1657
3082
|
repoId: p.repoId,
|
|
@@ -1661,19 +3086,69 @@ var SkillRepoBridgeHandler = class {
|
|
|
1661
3086
|
}))
|
|
1662
3087
|
};
|
|
1663
3088
|
}
|
|
3089
|
+
/**
|
|
3090
|
+
* `repo.resetParseCache` — recover from historical-parser drift.
|
|
3091
|
+
* Deletes each enabled repo's local checkout so the follow-up
|
|
3092
|
+
* pull re-clones it, then re-lists through the current SkillStore
|
|
3093
|
+
* parsing rules. Fresh mtimes also invalidate the plugin-catalog
|
|
3094
|
+
* mtime cache, so every downstream list rebuilds from scratch.
|
|
3095
|
+
*/
|
|
3096
|
+
async repoResetParseCache(_params) {
|
|
3097
|
+
const repoManager = this.buildRepoManager();
|
|
3098
|
+
const pulls = [];
|
|
3099
|
+
for (const repo of this.reposStore.list().filter((r) => r.enabled)) {
|
|
3100
|
+
const checkout = (0, import_devtools_core6.getRepoDir)(repo.id);
|
|
3101
|
+
await fs3.rm(checkout, { recursive: true, force: true });
|
|
3102
|
+
try {
|
|
3103
|
+
const result = await repoManager.pullOne(repo.id);
|
|
3104
|
+
pulls.push({
|
|
3105
|
+
repoId: repo.id,
|
|
3106
|
+
status: "ok",
|
|
3107
|
+
commitSha: result.commitSha || void 0
|
|
3108
|
+
});
|
|
3109
|
+
} catch (error) {
|
|
3110
|
+
pulls.push({
|
|
3111
|
+
repoId: repo.id,
|
|
3112
|
+
status: "error",
|
|
3113
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3114
|
+
});
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
this.invalidateSkillStoreCache();
|
|
3118
|
+
return { pulls };
|
|
3119
|
+
}
|
|
1664
3120
|
// ─────────────────────────────────────────────────────────────────
|
|
1665
3121
|
// Internals
|
|
1666
3122
|
// ─────────────────────────────────────────────────────────────────
|
|
1667
3123
|
/**
|
|
1668
|
-
*
|
|
1669
|
-
*
|
|
1670
|
-
*
|
|
1671
|
-
*
|
|
3124
|
+
* Return a SkillStore over the current enabled repos, reusing the
|
|
3125
|
+
* previously built one while the enabled-repo set is unchanged and
|
|
3126
|
+
* the reuse window hasn't elapsed. The store is read-only — it does
|
|
3127
|
+
* not mutate the repos store — and its memoized catalog walk is the
|
|
3128
|
+
* expensive part, so reuse is what keeps the webview's startup
|
|
3129
|
+
* polling from rescanning every checkout. A `repoIdFilter` no longer
|
|
3130
|
+
* narrows the constructed store: the shared store spans all enabled
|
|
3131
|
+
* repos and the caller walks just the one it asked for, which keeps
|
|
3132
|
+
* cache reuse across differently-filtered calls. Disabled repos are
|
|
3133
|
+
* never in the store, so a filtered walk over one yields [] exactly
|
|
3134
|
+
* as before.
|
|
1672
3135
|
*/
|
|
1673
|
-
buildSkillStore(
|
|
1674
|
-
const
|
|
1675
|
-
const
|
|
1676
|
-
|
|
3136
|
+
buildSkillStore() {
|
|
3137
|
+
const enabled = this.reposStore.list().filter((r) => r.enabled);
|
|
3138
|
+
const signature = enabled.map((r) => r.id).sort().join("\0");
|
|
3139
|
+
const cached = this.skillStoreCache;
|
|
3140
|
+
if (this.skillStoreCacheTtlMs > 0 && cached && cached.signature === signature && Date.now() - cached.computedAt < this.skillStoreCacheTtlMs) {
|
|
3141
|
+
return cached.store;
|
|
3142
|
+
}
|
|
3143
|
+
const store = new import_skill_store.SkillStore({
|
|
3144
|
+
repos: enabled.map((r) => ({ id: r.id, rootPath: (0, import_devtools_core6.getRepoDir)(r.id) }))
|
|
3145
|
+
});
|
|
3146
|
+
this.skillStoreCache = { signature, store, computedAt: Date.now() };
|
|
3147
|
+
return store;
|
|
3148
|
+
}
|
|
3149
|
+
/** Drop the cached store so the next list/get re-walks from disk. */
|
|
3150
|
+
invalidateSkillStoreCache() {
|
|
3151
|
+
this.skillStoreCache = void 0;
|
|
1677
3152
|
}
|
|
1678
3153
|
/**
|
|
1679
3154
|
* Resolve an entry's actual on-disk location via `SkillStore` for
|
|
@@ -1687,7 +3162,7 @@ var SkillRepoBridgeHandler = class {
|
|
|
1687
3162
|
* for.
|
|
1688
3163
|
*/
|
|
1689
3164
|
async resolveEntrySource(repoId, name, kind) {
|
|
1690
|
-
const store = this.buildSkillStore(
|
|
3165
|
+
const store = this.buildSkillStore();
|
|
1691
3166
|
let entry;
|
|
1692
3167
|
try {
|
|
1693
3168
|
entry = await store.get(repoId, name);
|
|
@@ -1708,9 +3183,12 @@ var SkillRepoBridgeHandler = class {
|
|
|
1708
3183
|
store: this.reposStore,
|
|
1709
3184
|
gitClient: this.gitClient
|
|
1710
3185
|
};
|
|
1711
|
-
return new
|
|
3186
|
+
return new import_devtools_core6.RepoManager(opts);
|
|
1712
3187
|
}
|
|
1713
3188
|
};
|
|
3189
|
+
function stripAgentSuffix(basename4) {
|
|
3190
|
+
return basename4.replace(/\.agent\.md$/, "");
|
|
3191
|
+
}
|
|
1714
3192
|
function toBridgeEntry(e) {
|
|
1715
3193
|
return {
|
|
1716
3194
|
repoId: e.repoId,
|
|
@@ -1744,20 +3222,20 @@ function toBridgeRepoEntry(repo) {
|
|
|
1744
3222
|
// src/commands/bridge.ts
|
|
1745
3223
|
var DEFAULT_GIT_PROXY_BASE = "http://127.0.0.1:3000/git-proxy";
|
|
1746
3224
|
async function runBridgeCommand() {
|
|
1747
|
-
const logger = (0,
|
|
1748
|
-
const configPath = (0,
|
|
1749
|
-
const reposStore = new
|
|
1750
|
-
loader: new
|
|
3225
|
+
const logger = (0, import_devtools_core7.createConsoleLogger)("serviceme:bridge");
|
|
3226
|
+
const configPath = (0, import_devtools_core7.getReposConfigPath)();
|
|
3227
|
+
const reposStore = new import_devtools_core7.ReposStore({
|
|
3228
|
+
loader: new import_devtools_core7.ReposLoader({ configPath })
|
|
1751
3229
|
});
|
|
1752
3230
|
try {
|
|
1753
|
-
await (0,
|
|
3231
|
+
await (0, import_devtools_core7.bootstrapDefaults)(reposStore);
|
|
1754
3232
|
} catch (err) {
|
|
1755
3233
|
logger.warn("repos.json bootstrap failed; continuing with empty config", err);
|
|
1756
3234
|
}
|
|
1757
3235
|
const serverProxyBase = process.env.SERVICEME_GIT_PROXY_BASE ?? DEFAULT_GIT_PROXY_BASE;
|
|
1758
|
-
const gitClient = new
|
|
1759
|
-
const draftsStore = new
|
|
1760
|
-
const submitClient = new
|
|
3236
|
+
const gitClient = new import_devtools_core7.GitClient({ serverProxyBase });
|
|
3237
|
+
const draftsStore = new import_devtools_core7.DraftsStore();
|
|
3238
|
+
const submitClient = new import_devtools_core7.SubmitClient({ gitClient });
|
|
1761
3239
|
const skillRepoHandler = new SkillRepoBridgeHandler({
|
|
1762
3240
|
reposStore,
|
|
1763
3241
|
gitClient,
|
|
@@ -1769,7 +3247,7 @@ async function runBridgeCommand() {
|
|
|
1769
3247
|
}
|
|
1770
3248
|
|
|
1771
3249
|
// src/commands/copilot.ts
|
|
1772
|
-
var
|
|
3250
|
+
var import_devtools_core8 = require("@serviceme/devtools-core");
|
|
1773
3251
|
async function runCopilotCommand(parsed) {
|
|
1774
3252
|
const action = parsed.positionals[1];
|
|
1775
3253
|
switch (action) {
|
|
@@ -1784,12 +3262,12 @@ async function runCopilotCommand(parsed) {
|
|
|
1784
3262
|
}
|
|
1785
3263
|
}
|
|
1786
3264
|
async function handleDoctor() {
|
|
1787
|
-
const result = await (0,
|
|
3265
|
+
const result = await (0, import_devtools_core8.copilotDoctor)();
|
|
1788
3266
|
if (!result.installed) {
|
|
1789
|
-
throw (0,
|
|
3267
|
+
throw (0, import_devtools_core8.createCopilotNotInstalledError)();
|
|
1790
3268
|
}
|
|
1791
3269
|
if (!result.authenticated) {
|
|
1792
|
-
throw (0,
|
|
3270
|
+
throw (0, import_devtools_core8.createCopilotAuthRequiredError)();
|
|
1793
3271
|
}
|
|
1794
3272
|
writeSuccess(result);
|
|
1795
3273
|
}
|
|
@@ -1806,7 +3284,7 @@ async function handlePrompt(parsed) {
|
|
|
1806
3284
|
const allowTools = allowToolRaw ? String(allowToolRaw).split(",") : void 0;
|
|
1807
3285
|
const model = parsed.flags.get("model");
|
|
1808
3286
|
const agent = parsed.flags.get("agent");
|
|
1809
|
-
const result = await (0,
|
|
3287
|
+
const result = await (0, import_devtools_core8.copilotPrompt)({
|
|
1810
3288
|
prompt,
|
|
1811
3289
|
workspace,
|
|
1812
3290
|
autopilot,
|
|
@@ -1882,9 +3360,9 @@ async function handleRotateSecret(parsed) {
|
|
|
1882
3360
|
}
|
|
1883
3361
|
|
|
1884
3362
|
// src/commands/env.ts
|
|
1885
|
-
var
|
|
3363
|
+
var import_devtools_core9 = require("@serviceme/devtools-core");
|
|
1886
3364
|
async function runEnvCommand(parsed) {
|
|
1887
|
-
const inspector = new
|
|
3365
|
+
const inspector = new import_devtools_core9.EnvironmentInspector();
|
|
1888
3366
|
const action = parsed.positionals[1];
|
|
1889
3367
|
if (action !== "check") {
|
|
1890
3368
|
throw new Error("Unsupported env command. Use check.");
|
|
@@ -1901,10 +3379,10 @@ async function runEnvCommand(parsed) {
|
|
|
1901
3379
|
}
|
|
1902
3380
|
|
|
1903
3381
|
// src/commands/image.ts
|
|
1904
|
-
var
|
|
3382
|
+
var import_devtools_core10 = require("@serviceme/devtools-core");
|
|
1905
3383
|
var IMAGE_FORMATS = /* @__PURE__ */ new Set(["jpeg", "png", "webp"]);
|
|
1906
3384
|
async function runImageCommand(parsed) {
|
|
1907
|
-
const imageTools = (0,
|
|
3385
|
+
const imageTools = (0, import_devtools_core10.createImageTools)();
|
|
1908
3386
|
const action = parsed.positionals[1];
|
|
1909
3387
|
const filePath = getStringFlag(parsed, "file");
|
|
1910
3388
|
const sharpModulePath = getStringFlag(parsed, "sharpModulePath");
|
|
@@ -1939,13 +3417,13 @@ async function runImageCommand(parsed) {
|
|
|
1939
3417
|
}
|
|
1940
3418
|
|
|
1941
3419
|
// src/commands/json.ts
|
|
1942
|
-
var
|
|
3420
|
+
var import_devtools_core11 = require("@serviceme/devtools-core");
|
|
1943
3421
|
|
|
1944
3422
|
// src/input.ts
|
|
1945
|
-
var
|
|
3423
|
+
var fs4 = __toESM(require("fs/promises"));
|
|
1946
3424
|
async function readCommandInput(options) {
|
|
1947
3425
|
if (options.filePath) {
|
|
1948
|
-
return
|
|
3426
|
+
return fs4.readFile(options.filePath, "utf8");
|
|
1949
3427
|
}
|
|
1950
3428
|
if (options.stdin) {
|
|
1951
3429
|
return readStdin();
|
|
@@ -1962,7 +3440,7 @@ async function readStdin() {
|
|
|
1962
3440
|
|
|
1963
3441
|
// src/commands/json.ts
|
|
1964
3442
|
async function runJsonCommand(parsed) {
|
|
1965
|
-
const jsonTools = (0,
|
|
3443
|
+
const jsonTools = (0, import_devtools_core11.createJsonTools)();
|
|
1966
3444
|
const action = parsed.positionals[1];
|
|
1967
3445
|
const input = await readCommandInput({
|
|
1968
3446
|
stdin: getBooleanFlag(parsed, "stdin"),
|
|
@@ -1998,9 +3476,9 @@ async function runJsonCommand(parsed) {
|
|
|
1998
3476
|
}
|
|
1999
3477
|
|
|
2000
3478
|
// src/commands/project.ts
|
|
2001
|
-
var
|
|
3479
|
+
var import_devtools_core12 = require("@serviceme/devtools-core");
|
|
2002
3480
|
async function runProjectCommand(parsed) {
|
|
2003
|
-
const projectTools = (0,
|
|
3481
|
+
const projectTools = (0, import_devtools_core12.createProjectTools)();
|
|
2004
3482
|
const action = parsed.positionals[1];
|
|
2005
3483
|
const workspacePath = getStringFlag(parsed, "workspacePath");
|
|
2006
3484
|
if (!workspacePath) {
|
|
@@ -2051,7 +3529,7 @@ async function runProjectCommand(parsed) {
|
|
|
2051
3529
|
}
|
|
2052
3530
|
|
|
2053
3531
|
// src/commands/repos.ts
|
|
2054
|
-
var
|
|
3532
|
+
var import_devtools_core13 = require("@serviceme/devtools-core");
|
|
2055
3533
|
function requireRepoId(parsed) {
|
|
2056
3534
|
const repoId = getStringFlag(parsed, "repo-id");
|
|
2057
3535
|
if (!repoId) {
|
|
@@ -2061,10 +3539,10 @@ function requireRepoId(parsed) {
|
|
|
2061
3539
|
}
|
|
2062
3540
|
function newStoreAndManager(parsed) {
|
|
2063
3541
|
const proxyBase = getStringFlag(parsed, "git-proxy-base") ?? "http://127.0.0.1:3000/git-proxy";
|
|
2064
|
-
const loader = new
|
|
2065
|
-
const store = new
|
|
2066
|
-
const gitClient = new
|
|
2067
|
-
const manager = new
|
|
3542
|
+
const loader = new import_devtools_core13.ReposLoader({ configPath: (0, import_devtools_core13.getReposConfigPath)() });
|
|
3543
|
+
const store = new import_devtools_core13.ReposStore({ loader });
|
|
3544
|
+
const gitClient = new import_devtools_core13.GitClient({ serverProxyBase: proxyBase });
|
|
3545
|
+
const manager = new import_devtools_core13.RepoManager({ store, gitClient });
|
|
2068
3546
|
return { store, manager };
|
|
2069
3547
|
}
|
|
2070
3548
|
async function ensureStore(store) {
|
|
@@ -2083,7 +3561,7 @@ async function handleList2(parsed) {
|
|
|
2083
3561
|
enabled: r.enabled,
|
|
2084
3562
|
writeEnabled: r.writeEnabled,
|
|
2085
3563
|
source: r.source,
|
|
2086
|
-
description: (0,
|
|
3564
|
+
description: (0, import_devtools_core13.isDefaultRepo)(r) ? r.description : void 0,
|
|
2087
3565
|
addedAt: r.addedAt,
|
|
2088
3566
|
lastSyncAt: r.lastSyncAt,
|
|
2089
3567
|
lastSyncCommitSha: r.lastSyncCommitSha,
|
|
@@ -2136,7 +3614,7 @@ async function handleEnable(parsed) {
|
|
|
2136
3614
|
enabled: repo.enabled,
|
|
2137
3615
|
writeEnabled: repo.writeEnabled,
|
|
2138
3616
|
source: repo.source,
|
|
2139
|
-
description: (0,
|
|
3617
|
+
description: (0, import_devtools_core13.isDefaultRepo)(repo) ? repo.description : void 0,
|
|
2140
3618
|
addedAt: repo.addedAt
|
|
2141
3619
|
}
|
|
2142
3620
|
};
|
|
@@ -2153,7 +3631,7 @@ async function handleDisable(parsed) {
|
|
|
2153
3631
|
enabled: repo.enabled,
|
|
2154
3632
|
writeEnabled: repo.writeEnabled,
|
|
2155
3633
|
source: repo.source,
|
|
2156
|
-
description: (0,
|
|
3634
|
+
description: (0, import_devtools_core13.isDefaultRepo)(repo) ? repo.description : void 0,
|
|
2157
3635
|
addedAt: repo.addedAt
|
|
2158
3636
|
}
|
|
2159
3637
|
};
|
|
@@ -2222,9 +3700,9 @@ async function runReposCommand(parsed) {
|
|
|
2222
3700
|
}
|
|
2223
3701
|
|
|
2224
3702
|
// src/commands/schedule.ts
|
|
2225
|
-
var
|
|
2226
|
-
var
|
|
2227
|
-
var
|
|
3703
|
+
var fs5 = __toESM(require("fs"));
|
|
3704
|
+
var path6 = __toESM(require("path"));
|
|
3705
|
+
var import_devtools_core14 = require("@serviceme/devtools-core");
|
|
2228
3706
|
async function runScheduleCommand(parsed) {
|
|
2229
3707
|
const action = parsed.positionals[1];
|
|
2230
3708
|
if (getBooleanFlag(parsed, "describe")) {
|
|
@@ -2268,7 +3746,7 @@ function requireWorkspace(parsed) {
|
|
|
2268
3746
|
if (!wp) {
|
|
2269
3747
|
throw createServicemeError("invalid_params", "Missing required flag: --workspacePath");
|
|
2270
3748
|
}
|
|
2271
|
-
if (!
|
|
3749
|
+
if (!fs5.existsSync(wp)) {
|
|
2272
3750
|
throw createServicemeError("workspace_not_found", `Workspace path does not exist: ${wp}`);
|
|
2273
3751
|
}
|
|
2274
3752
|
return wp;
|
|
@@ -2407,7 +3885,7 @@ async function handleCreate(parsed) {
|
|
|
2407
3885
|
const payload = parsePayload(parsed, taskType);
|
|
2408
3886
|
const description = getStringFlag(parsed, "description");
|
|
2409
3887
|
const enabled = getStringFlag(parsed, "enabled") !== "false";
|
|
2410
|
-
const mgr = new
|
|
3888
|
+
const mgr = new import_devtools_core14.TaskConfigManager();
|
|
2411
3889
|
const existing = mgr.getTaskByName(name);
|
|
2412
3890
|
if (existing) {
|
|
2413
3891
|
throw createServicemeError(
|
|
@@ -2424,7 +3902,7 @@ async function handleCreate(parsed) {
|
|
|
2424
3902
|
payload,
|
|
2425
3903
|
workspace: {
|
|
2426
3904
|
path: wp,
|
|
2427
|
-
name:
|
|
3905
|
+
name: path6.basename(wp) || wp
|
|
2428
3906
|
},
|
|
2429
3907
|
enabled
|
|
2430
3908
|
};
|
|
@@ -2449,7 +3927,7 @@ async function handleCreate(parsed) {
|
|
|
2449
3927
|
}
|
|
2450
3928
|
function handleList3(parsed) {
|
|
2451
3929
|
requireWorkspace(parsed);
|
|
2452
|
-
const mgr = new
|
|
3930
|
+
const mgr = new import_devtools_core14.TaskConfigManager();
|
|
2453
3931
|
const tasks = mgr.listTasks();
|
|
2454
3932
|
const fields = getStringFlag(parsed, "fields");
|
|
2455
3933
|
const limitStr = getStringFlag(parsed, "limit");
|
|
@@ -2465,7 +3943,7 @@ function handleList3(parsed) {
|
|
|
2465
3943
|
function handleGet(parsed) {
|
|
2466
3944
|
requireWorkspace(parsed);
|
|
2467
3945
|
const id = requireId(parsed);
|
|
2468
|
-
const mgr = new
|
|
3946
|
+
const mgr = new import_devtools_core14.TaskConfigManager();
|
|
2469
3947
|
const task = mgr.getTask(id);
|
|
2470
3948
|
if (!task) {
|
|
2471
3949
|
throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
|
|
@@ -2477,7 +3955,7 @@ function handleGet(parsed) {
|
|
|
2477
3955
|
async function handleEdit(parsed) {
|
|
2478
3956
|
requireWorkspace(parsed);
|
|
2479
3957
|
const id = requireId(parsed);
|
|
2480
|
-
const mgr = new
|
|
3958
|
+
const mgr = new import_devtools_core14.TaskConfigManager();
|
|
2481
3959
|
const existing = mgr.getTask(id);
|
|
2482
3960
|
if (!existing) {
|
|
2483
3961
|
throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
|
|
@@ -2522,7 +4000,7 @@ function handleDelete(parsed) {
|
|
|
2522
4000
|
requireWorkspace(parsed);
|
|
2523
4001
|
const id = requireId(parsed);
|
|
2524
4002
|
requireConfirmation(parsed);
|
|
2525
|
-
const mgr = new
|
|
4003
|
+
const mgr = new import_devtools_core14.TaskConfigManager();
|
|
2526
4004
|
const existing = mgr.getTask(id);
|
|
2527
4005
|
if (!existing) {
|
|
2528
4006
|
throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
|
|
@@ -2542,7 +4020,7 @@ function handleToggle(parsed) {
|
|
|
2542
4020
|
throw createServicemeError("invalid_params", "Missing required flag: --enabled <true|false>");
|
|
2543
4021
|
}
|
|
2544
4022
|
const enabled = enabledStr !== "false";
|
|
2545
|
-
const mgr = new
|
|
4023
|
+
const mgr = new import_devtools_core14.TaskConfigManager();
|
|
2546
4024
|
const existing = mgr.getTask(id);
|
|
2547
4025
|
if (!existing) {
|
|
2548
4026
|
throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
|
|
@@ -2553,22 +4031,22 @@ function handleToggle(parsed) {
|
|
|
2553
4031
|
async function handleTrigger(parsed) {
|
|
2554
4032
|
requireWorkspace(parsed);
|
|
2555
4033
|
const id = requireId(parsed);
|
|
2556
|
-
const mgr = new
|
|
4034
|
+
const mgr = new import_devtools_core14.TaskConfigManager();
|
|
2557
4035
|
const task = mgr.getTask(id);
|
|
2558
4036
|
if (!task) {
|
|
2559
4037
|
throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
|
|
2560
4038
|
}
|
|
2561
4039
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2562
|
-
const executionPayload = (0,
|
|
4040
|
+
const executionPayload = (0, import_devtools_core14.resolveTaskExecutionPayload)(
|
|
2563
4041
|
task.taskType,
|
|
2564
4042
|
task.payload,
|
|
2565
4043
|
task.workspace.path
|
|
2566
4044
|
);
|
|
2567
|
-
(0,
|
|
2568
|
-
const executor = (0,
|
|
4045
|
+
(0, import_devtools_core14.validateTaskPayload)(task.taskType, executionPayload);
|
|
4046
|
+
const executor = (0, import_devtools_core14.getExecutor)(task.taskType);
|
|
2569
4047
|
const result = await executor.execute(executionPayload);
|
|
2570
4048
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2571
|
-
const logMgr = new
|
|
4049
|
+
const logMgr = new import_devtools_core14.TaskLogManager();
|
|
2572
4050
|
const log = logMgr.appendLog({
|
|
2573
4051
|
taskId: task.id,
|
|
2574
4052
|
taskName: task.name,
|
|
@@ -2586,7 +4064,7 @@ function handleLogs(parsed) {
|
|
|
2586
4064
|
const limitStr = getStringFlag(parsed, "limit");
|
|
2587
4065
|
const limit = limitStr ? Math.min(Number(limitStr), 200) : 20;
|
|
2588
4066
|
const fields = getStringFlag(parsed, "fields");
|
|
2589
|
-
const logMgr = new
|
|
4067
|
+
const logMgr = new import_devtools_core14.TaskLogManager();
|
|
2590
4068
|
const { logs, total } = logMgr.getLogs({ taskId, limit });
|
|
2591
4069
|
const result = logs.map((l) => filterFields(l, fields));
|
|
2592
4070
|
writeSuccess({ logs: result, total });
|
|
@@ -2758,9 +4236,9 @@ function writeDescribe(action) {
|
|
|
2758
4236
|
|
|
2759
4237
|
// src/commands/scheduler.ts
|
|
2760
4238
|
var import_node_child_process = require("child_process");
|
|
2761
|
-
var
|
|
2762
|
-
var
|
|
2763
|
-
var
|
|
4239
|
+
var fs6 = __toESM(require("fs"));
|
|
4240
|
+
var path7 = __toESM(require("path"));
|
|
4241
|
+
var import_devtools_core15 = require("@serviceme/devtools-core");
|
|
2764
4242
|
var REPO_SYNC_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2765
4243
|
async function runSchedulerCommand(parsed) {
|
|
2766
4244
|
const action = parsed.positionals[1];
|
|
@@ -2798,25 +4276,25 @@ async function runSchedulerCommand(parsed) {
|
|
|
2798
4276
|
}
|
|
2799
4277
|
}
|
|
2800
4278
|
function listTasks() {
|
|
2801
|
-
const mgr = new
|
|
4279
|
+
const mgr = new import_devtools_core15.TaskConfigManager();
|
|
2802
4280
|
const tasks = mgr.listTasks();
|
|
2803
4281
|
return [...tasks].sort((a, b) => a.name.localeCompare(b.name));
|
|
2804
4282
|
}
|
|
2805
4283
|
function readLogs(options = {}) {
|
|
2806
|
-
const mgr = new
|
|
4284
|
+
const mgr = new import_devtools_core15.TaskLogManager();
|
|
2807
4285
|
const limit = options.limit && options.limit > 0 ? Math.min(options.limit, 200) : 50;
|
|
2808
4286
|
return mgr.getLogs({ taskId: options.taskId, limit });
|
|
2809
4287
|
}
|
|
2810
4288
|
function getSchedulerStatus() {
|
|
2811
|
-
const pidMgr = new
|
|
2812
|
-
const configMgr = new
|
|
4289
|
+
const pidMgr = new import_devtools_core15.PidManager("", { pidPath: (0, import_devtools_core15.getSchedulerPidPath)() });
|
|
4290
|
+
const configMgr = new import_devtools_core15.TaskConfigManager();
|
|
2813
4291
|
const pid = pidMgr.getRunningPid();
|
|
2814
4292
|
const config = configMgr.readConfig();
|
|
2815
4293
|
let uptimeSeconds = null;
|
|
2816
4294
|
if (pid !== null) {
|
|
2817
4295
|
try {
|
|
2818
|
-
const
|
|
2819
|
-
uptimeSeconds = Math.floor((Date.now() -
|
|
4296
|
+
const stat4 = fs6.statSync(pidMgr.getPidPath());
|
|
4297
|
+
uptimeSeconds = Math.floor((Date.now() - stat4.mtimeMs) / 1e3);
|
|
2820
4298
|
} catch {
|
|
2821
4299
|
uptimeSeconds = null;
|
|
2822
4300
|
}
|
|
@@ -2833,7 +4311,7 @@ function getSchedulerStatus() {
|
|
|
2833
4311
|
};
|
|
2834
4312
|
}
|
|
2835
4313
|
function handleStart(_parsed) {
|
|
2836
|
-
const pidMgr = new
|
|
4314
|
+
const pidMgr = new import_devtools_core15.PidManager("", { pidPath: (0, import_devtools_core15.getSchedulerPidPath)() });
|
|
2837
4315
|
const existingPid = pidMgr.getRunningPid();
|
|
2838
4316
|
if (existingPid !== null) {
|
|
2839
4317
|
writeSuccess({
|
|
@@ -2846,15 +4324,15 @@ function handleStart(_parsed) {
|
|
|
2846
4324
|
if (!cliPath) {
|
|
2847
4325
|
throw createServicemeError("internal_error", "Cannot determine CLI path for daemon spawn");
|
|
2848
4326
|
}
|
|
2849
|
-
const logPath = (0,
|
|
2850
|
-
const logDir =
|
|
2851
|
-
if (!
|
|
2852
|
-
|
|
4327
|
+
const logPath = (0, import_devtools_core15.getSchedulerLogPath)();
|
|
4328
|
+
const logDir = path7.dirname(logPath);
|
|
4329
|
+
if (!fs6.existsSync(logDir)) {
|
|
4330
|
+
fs6.mkdirSync(logDir, { recursive: true });
|
|
2853
4331
|
}
|
|
2854
4332
|
const spawnCmd = process.execPath;
|
|
2855
4333
|
const spawnArgs = [cliPath, "scheduler", "__daemon", "--logPath", logPath];
|
|
2856
4334
|
if (process.platform === "win32") {
|
|
2857
|
-
|
|
4335
|
+
fs6.appendFileSync(
|
|
2858
4336
|
logPath,
|
|
2859
4337
|
`[scheduler:start] spawning daemon via hidden PowerShell Start-Process: cmd=${spawnCmd}, args=${JSON.stringify(spawnArgs)}, platform=${process.platform}, windowsHide=true
|
|
2860
4338
|
`
|
|
@@ -2864,9 +4342,9 @@ function handleStart(_parsed) {
|
|
|
2864
4342
|
writeSuccess({ pid: pid2, status: "started" });
|
|
2865
4343
|
return;
|
|
2866
4344
|
}
|
|
2867
|
-
const out =
|
|
2868
|
-
const err =
|
|
2869
|
-
|
|
4345
|
+
const out = fs6.openSync(logPath, "a");
|
|
4346
|
+
const err = fs6.openSync(logPath, "a");
|
|
4347
|
+
fs6.appendFileSync(
|
|
2870
4348
|
logPath,
|
|
2871
4349
|
`[scheduler:start] spawning daemon: cmd=${spawnCmd}, args=${JSON.stringify(spawnArgs)}, platform=${process.platform}, detached=true, windowsHide=true
|
|
2872
4350
|
`
|
|
@@ -2945,7 +4423,7 @@ function handleStop(parsed) {
|
|
|
2945
4423
|
);
|
|
2946
4424
|
}
|
|
2947
4425
|
}
|
|
2948
|
-
const pidMgr = new
|
|
4426
|
+
const pidMgr = new import_devtools_core15.PidManager("", { pidPath: (0, import_devtools_core15.getSchedulerPidPath)() });
|
|
2949
4427
|
const pid = pidMgr.getRunningPid();
|
|
2950
4428
|
if (pid === null) {
|
|
2951
4429
|
throw createServicemeError("daemon_not_running", "Scheduler daemon is not running");
|
|
@@ -2987,7 +4465,7 @@ async function runDaemon(parsed) {
|
|
|
2987
4465
|
if (logPath) {
|
|
2988
4466
|
process.env.SERVICEME_SCHEDULER_LOG_PATH = logPath;
|
|
2989
4467
|
}
|
|
2990
|
-
const pidMgr = new
|
|
4468
|
+
const pidMgr = new import_devtools_core15.PidManager("", { pidPath: (0, import_devtools_core15.getSchedulerPidPath)() });
|
|
2991
4469
|
const existingPid = pidMgr.getRunningPid();
|
|
2992
4470
|
if (existingPid !== null && existingPid !== process.pid) {
|
|
2993
4471
|
process.exit(0);
|
|
@@ -3012,11 +4490,11 @@ function startRepoSyncTick() {
|
|
|
3012
4490
|
}
|
|
3013
4491
|
async function runRepoSyncOnce() {
|
|
3014
4492
|
const proxyBase = process.env.SERVICEME_GIT_PROXY_BASE ?? "http://127.0.0.1:3000/git-proxy";
|
|
3015
|
-
const loader = new
|
|
3016
|
-
const store = new
|
|
4493
|
+
const loader = new import_devtools_core15.ReposLoader({ configPath: (0, import_devtools_core15.getReposConfigPath)() });
|
|
4494
|
+
const store = new import_devtools_core15.ReposStore({ loader });
|
|
3017
4495
|
await store.ensureLoaded();
|
|
3018
|
-
const gitClient = new
|
|
3019
|
-
const manager = new
|
|
4496
|
+
const gitClient = new import_devtools_core15.GitClient({ serverProxyBase: proxyBase });
|
|
4497
|
+
const manager = new import_devtools_core15.RepoManager({ store, gitClient });
|
|
3020
4498
|
const report = await manager.pullAll();
|
|
3021
4499
|
const ok = report.pulls.filter((p) => p.status === "ok").length;
|
|
3022
4500
|
const err = report.pulls.filter((p) => p.status === "error").length;
|
|
@@ -3026,8 +4504,8 @@ async function runRepoSyncOnce() {
|
|
|
3026
4504
|
}
|
|
3027
4505
|
function appendSchedulerLog(message) {
|
|
3028
4506
|
try {
|
|
3029
|
-
const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH ?? (0,
|
|
3030
|
-
|
|
4507
|
+
const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH ?? (0, import_devtools_core15.getSchedulerLogPath)();
|
|
4508
|
+
fs6.appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${message}
|
|
3031
4509
|
`, "utf8");
|
|
3032
4510
|
} catch {
|
|
3033
4511
|
}
|
|
@@ -3086,10 +4564,10 @@ function writeDescribe2(action) {
|
|
|
3086
4564
|
}
|
|
3087
4565
|
|
|
3088
4566
|
// src/commands/skill.ts
|
|
3089
|
-
var
|
|
4567
|
+
var fs7 = __toESM(require("fs/promises"));
|
|
3090
4568
|
var os2 = __toESM(require("os"));
|
|
3091
|
-
var
|
|
3092
|
-
var
|
|
4569
|
+
var path8 = __toESM(require("path"));
|
|
4570
|
+
var import_devtools_core16 = require("@serviceme/devtools-core");
|
|
3093
4571
|
function normalizeSkillIdOrThrow(store, remoteId) {
|
|
3094
4572
|
try {
|
|
3095
4573
|
return store.normalizeRemoteSkillId(remoteId);
|
|
@@ -3103,14 +4581,14 @@ async function runSkillCommand(parsed) {
|
|
|
3103
4581
|
if (!workspacePath) {
|
|
3104
4582
|
throw createServicemeError("invalid_params", "Expected --workspacePath <path>.");
|
|
3105
4583
|
}
|
|
3106
|
-
const store = new
|
|
4584
|
+
const store = new import_devtools_core16.SkillStore({
|
|
3107
4585
|
workspacePath,
|
|
3108
|
-
userSkillsRoot:
|
|
4586
|
+
userSkillsRoot: path8.join(os2.homedir(), ".copilot", "skills")
|
|
3109
4587
|
});
|
|
3110
|
-
const catalogClient = new
|
|
4588
|
+
const catalogClient = new import_devtools_core16.SkillCatalogClient({
|
|
3111
4589
|
baseUrl: getStringFlag(parsed, "baseUrl")
|
|
3112
4590
|
});
|
|
3113
|
-
const reconciler = new
|
|
4591
|
+
const reconciler = new import_devtools_core16.SkillReconciler({
|
|
3114
4592
|
skillStore: store,
|
|
3115
4593
|
catalogClient
|
|
3116
4594
|
});
|
|
@@ -3219,11 +4697,11 @@ async function handleUninstall2(store, workspacePath, parsed) {
|
|
|
3219
4697
|
throw createServicemeError("invalid_params", "Expected --id <remoteSkillId>.");
|
|
3220
4698
|
}
|
|
3221
4699
|
const skillId = normalizeSkillIdOrThrow(store, remoteId);
|
|
3222
|
-
await
|
|
4700
|
+
await fs7.rm(path8.join(workspacePath, store.getWorkspaceSkillPath(skillId)), {
|
|
3223
4701
|
recursive: true,
|
|
3224
4702
|
force: true
|
|
3225
4703
|
});
|
|
3226
|
-
await
|
|
4704
|
+
await fs7.rm(store.getUserSkillPath(skillId), {
|
|
3227
4705
|
recursive: true,
|
|
3228
4706
|
force: true
|
|
3229
4707
|
});
|
|
@@ -3245,7 +4723,7 @@ async function handleMove2(store, workspacePath, parsed) {
|
|
|
3245
4723
|
throw createServicemeError("invalid_params", "Expected --to value to be workspace or user.");
|
|
3246
4724
|
}
|
|
3247
4725
|
const skillId = normalizeSkillIdOrThrow(store, remoteId);
|
|
3248
|
-
const workspacePathForSkill =
|
|
4726
|
+
const workspacePathForSkill = path8.join(workspacePath, store.getWorkspaceSkillPath(skillId));
|
|
3249
4727
|
const userPathForSkill = store.getUserSkillPath(skillId);
|
|
3250
4728
|
if (to === "user") {
|
|
3251
4729
|
await moveDirectory(workspacePathForSkill, userPathForSkill);
|
|
@@ -3266,12 +4744,12 @@ async function handleMove2(store, workspacePath, parsed) {
|
|
|
3266
4744
|
};
|
|
3267
4745
|
}
|
|
3268
4746
|
async function moveDirectory(fromPath, toPath) {
|
|
3269
|
-
await
|
|
4747
|
+
await fs7.mkdir(path8.dirname(toPath), { recursive: true });
|
|
3270
4748
|
try {
|
|
3271
|
-
await
|
|
4749
|
+
await fs7.rename(fromPath, toPath);
|
|
3272
4750
|
} catch {
|
|
3273
|
-
await
|
|
3274
|
-
await
|
|
4751
|
+
await fs7.cp(fromPath, toPath, { recursive: true });
|
|
4752
|
+
await fs7.rm(fromPath, { recursive: true, force: true });
|
|
3275
4753
|
}
|
|
3276
4754
|
}
|
|
3277
4755
|
async function handleMarketplace2(store, catalogClient) {
|
|
@@ -3372,15 +4850,15 @@ async function handlePublishable(store, workspacePath) {
|
|
|
3372
4850
|
skills: workspaceSkillIds.map((skillId) => ({
|
|
3373
4851
|
id: skillId,
|
|
3374
4852
|
displayName: skillId,
|
|
3375
|
-
path:
|
|
4853
|
+
path: path8.join(workspacePath, store.getWorkspaceSkillPath(skillId))
|
|
3376
4854
|
}))
|
|
3377
4855
|
};
|
|
3378
4856
|
}
|
|
3379
4857
|
|
|
3380
4858
|
// src/commands/skills.ts
|
|
3381
|
-
var
|
|
3382
|
-
var
|
|
3383
|
-
var
|
|
4859
|
+
var fs8 = __toESM(require("fs/promises"));
|
|
4860
|
+
var path9 = __toESM(require("path"));
|
|
4861
|
+
var import_devtools_core17 = require("@serviceme/devtools-core");
|
|
3384
4862
|
function requireFlag(parsed, name) {
|
|
3385
4863
|
const value = getStringFlag(parsed, name);
|
|
3386
4864
|
if (!value) {
|
|
@@ -3421,13 +4899,13 @@ function parseMode(raw) {
|
|
|
3421
4899
|
}
|
|
3422
4900
|
async function newSkillRepoHandler(parsed) {
|
|
3423
4901
|
const proxyBase = getStringFlag(parsed, "git-proxy-base") ?? "http://127.0.0.1:3000/git-proxy";
|
|
3424
|
-
const reposStore = new
|
|
3425
|
-
loader: new
|
|
4902
|
+
const reposStore = new import_devtools_core17.ReposStore({
|
|
4903
|
+
loader: new import_devtools_core17.ReposLoader({ configPath: (0, import_devtools_core17.getReposConfigPath)() })
|
|
3426
4904
|
});
|
|
3427
4905
|
await reposStore.ensureLoaded();
|
|
3428
|
-
const gitClient = new
|
|
3429
|
-
const draftsStore = new
|
|
3430
|
-
const submitClient = new
|
|
4906
|
+
const gitClient = new import_devtools_core17.GitClient({ serverProxyBase: proxyBase });
|
|
4907
|
+
const draftsStore = new import_devtools_core17.DraftsStore();
|
|
4908
|
+
const submitClient = new import_devtools_core17.SubmitClient({ gitClient });
|
|
3431
4909
|
return new SkillRepoBridgeHandler({
|
|
3432
4910
|
reposStore,
|
|
3433
4911
|
gitClient,
|
|
@@ -3436,9 +4914,9 @@ async function newSkillRepoHandler(parsed) {
|
|
|
3436
4914
|
});
|
|
3437
4915
|
}
|
|
3438
4916
|
async function readDraftFilesFromDir(dirPath) {
|
|
3439
|
-
let
|
|
4917
|
+
let stat4;
|
|
3440
4918
|
try {
|
|
3441
|
-
|
|
4919
|
+
stat4 = await fs8.stat(dirPath);
|
|
3442
4920
|
} catch (err) {
|
|
3443
4921
|
const code = err.code;
|
|
3444
4922
|
throw createServicemeError(
|
|
@@ -3446,17 +4924,17 @@ async function readDraftFilesFromDir(dirPath) {
|
|
|
3446
4924
|
`--dir '${dirPath}' is not readable${code === "ENOENT" ? " (path does not exist)" : `: ${err.message}`}`
|
|
3447
4925
|
);
|
|
3448
4926
|
}
|
|
3449
|
-
if (!
|
|
4927
|
+
if (!stat4.isDirectory()) {
|
|
3450
4928
|
throw createServicemeError("invalid_params", `--dir '${dirPath}' is not a directory`);
|
|
3451
4929
|
}
|
|
3452
4930
|
const files = [];
|
|
3453
4931
|
let manifestFound = false;
|
|
3454
4932
|
async function walk(currentAbs, currentRel) {
|
|
3455
|
-
const entries = await
|
|
4933
|
+
const entries = await fs8.readdir(currentAbs, { withFileTypes: true });
|
|
3456
4934
|
for (const entry of entries) {
|
|
3457
4935
|
if (entry.name.startsWith(".")) continue;
|
|
3458
4936
|
if (entry.name === "node_modules") continue;
|
|
3459
|
-
const childAbs =
|
|
4937
|
+
const childAbs = path9.join(currentAbs, entry.name);
|
|
3460
4938
|
const childRel = currentRel ? `${currentRel}/${entry.name}` : entry.name;
|
|
3461
4939
|
if (entry.isSymbolicLink()) continue;
|
|
3462
4940
|
if (entry.isDirectory()) {
|
|
@@ -3464,7 +4942,7 @@ async function readDraftFilesFromDir(dirPath) {
|
|
|
3464
4942
|
continue;
|
|
3465
4943
|
}
|
|
3466
4944
|
if (!entry.isFile()) continue;
|
|
3467
|
-
const content = await
|
|
4945
|
+
const content = await fs8.readFile(childAbs, "utf8");
|
|
3468
4946
|
if (childRel === "SKILL.md" || childRel === "AGENT.md") {
|
|
3469
4947
|
manifestFound = true;
|
|
3470
4948
|
}
|