@serviceme/devtools-core 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +189 -4
- package/dist/index.d.ts +189 -4
- package/dist/index.js +672 -269
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +665 -271
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -139,13 +139,16 @@ __export(src_exports, {
|
|
|
139
139
|
assertSafeRepoId: () => assertSafeRepoId,
|
|
140
140
|
bootstrapDefaults: () => bootstrapDefaults,
|
|
141
141
|
bootstrapPhase5Placeholders: () => bootstrapPhase5Placeholders,
|
|
142
|
+
buildContentIdentity: () => buildContentIdentity,
|
|
142
143
|
buildCopilotCustomizationView: () => buildCopilotCustomizationView,
|
|
143
144
|
buildDefaultReposFile: () => buildDefaultReposFile,
|
|
145
|
+
buildDisabledIdentityMatcher: () => buildDisabledIdentityMatcher,
|
|
144
146
|
buildGitHubLocalEmail: () => buildGitHubLocalEmail,
|
|
145
147
|
buildGitProxyBase: () => buildGitProxyBase,
|
|
146
148
|
buildSignedHeaders: () => buildSignedHeaders,
|
|
147
149
|
copilotDoctor: () => copilotDoctor,
|
|
148
150
|
copilotPrompt: () => copilotPrompt,
|
|
151
|
+
coveredTargetsOf: () => coveredTargetsOf,
|
|
149
152
|
createConsoleLogger: () => createConsoleLogger,
|
|
150
153
|
createCopilotAuthRequiredError: () => createCopilotAuthRequiredError,
|
|
151
154
|
createCopilotNotInstalledError: () => createCopilotNotInstalledError,
|
|
@@ -156,10 +159,13 @@ __export(src_exports, {
|
|
|
156
159
|
createPluginCatalogService: () => createPluginCatalogService,
|
|
157
160
|
createProjectTools: () => createProjectTools,
|
|
158
161
|
createReposStore: () => createReposStore,
|
|
162
|
+
dedupeAdoptionsByPluginManifests: () => dedupeAdoptionsByPluginManifests,
|
|
159
163
|
defaultRepoConfigSchema: () => defaultRepoSchema,
|
|
160
164
|
deriveCopilotUserStatus: () => deriveCopilotUserStatus,
|
|
161
165
|
deriveInstallationId: () => deriveInstallationId,
|
|
166
|
+
detectUnmanagedWorkspaceContent: () => detectUnmanagedWorkspaceContent,
|
|
162
167
|
ensureDefaultsInstalled: () => ensureDefaultsInstalled,
|
|
168
|
+
findPluginCoveringArtifact: () => findPluginCoveringArtifact,
|
|
163
169
|
findPluginMcpJson: () => findPluginMcpJson,
|
|
164
170
|
findRepoHooksJson: () => findRepoHooksJson,
|
|
165
171
|
fingerprintSource: () => fingerprintSource,
|
|
@@ -213,6 +219,7 @@ __export(src_exports, {
|
|
|
213
219
|
narrowRepoConfig: () => narrowRepoConfig,
|
|
214
220
|
noopLogger: () => noopLogger,
|
|
215
221
|
parseAgentToolPermissions: () => parseAgentToolPermissions,
|
|
222
|
+
parseContentIdentity: () => parseContentIdentity,
|
|
216
223
|
parseHooksJson: () => parseHooksJson,
|
|
217
224
|
parseMcpJson: () => parseMcpJson,
|
|
218
225
|
randomInstallationId: () => randomInstallationId,
|
|
@@ -229,6 +236,7 @@ __export(src_exports, {
|
|
|
229
236
|
resolveTaskExecutionPayload: () => resolveTaskExecutionPayload,
|
|
230
237
|
resolveWorkspaceContentPlan: () => resolveWorkspaceContentPlan,
|
|
231
238
|
setUserHomeOverrides: () => setUserHomeOverrides,
|
|
239
|
+
setWorkspacePluginArtifactsDisabled: () => setWorkspacePluginArtifactsDisabled,
|
|
232
240
|
sortByRecentFirst: () => sortByRecentFirst,
|
|
233
241
|
sortByUserOrder: () => sortByUserOrder,
|
|
234
242
|
supportsPersonalIntegrationKind: () => supportsPersonalIntegrationKind,
|
|
@@ -236,6 +244,7 @@ __export(src_exports, {
|
|
|
236
244
|
toArtifactSummary: () => toArtifactSummary,
|
|
237
245
|
toFileUrl: () => toFileUrl,
|
|
238
246
|
touchLastUsedAt: () => touchLastUsedAt,
|
|
247
|
+
unmanagedDetectionSignature: () => unmanagedDetectionSignature,
|
|
239
248
|
unzipFile: () => unzipFile,
|
|
240
249
|
upsertWorkspaceContentSelection: () => upsertWorkspaceContentSelection,
|
|
241
250
|
userRepoConfigSchema: () => userRepoSchema,
|
|
@@ -1552,7 +1561,7 @@ var GitHubAuthProvider = class {
|
|
|
1552
1561
|
}
|
|
1553
1562
|
};
|
|
1554
1563
|
function sleep(ms) {
|
|
1555
|
-
return new Promise((
|
|
1564
|
+
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
1556
1565
|
}
|
|
1557
1566
|
|
|
1558
1567
|
// src/auth/providers/MicrosoftAuthProvider.ts
|
|
@@ -1631,7 +1640,7 @@ function scheduleForceKill(child) {
|
|
|
1631
1640
|
return timer;
|
|
1632
1641
|
}
|
|
1633
1642
|
async function runCommand(command, options = {}) {
|
|
1634
|
-
return new Promise((
|
|
1643
|
+
return new Promise((resolve13, reject) => {
|
|
1635
1644
|
const child = (0, import_node_child_process.spawn)(command, options.args ?? [], {
|
|
1636
1645
|
cwd: options.cwd,
|
|
1637
1646
|
env: options.env,
|
|
@@ -1678,7 +1687,7 @@ async function runCommand(command, options = {}) {
|
|
|
1678
1687
|
}
|
|
1679
1688
|
finish(() => {
|
|
1680
1689
|
if (code === 0) {
|
|
1681
|
-
|
|
1690
|
+
resolve13({
|
|
1682
1691
|
stdout,
|
|
1683
1692
|
stderr,
|
|
1684
1693
|
code: 0
|
|
@@ -2006,8 +2015,8 @@ async function createDefaultCopilotHostCapabilities(options) {
|
|
|
2006
2015
|
for (const kind of allPersonalLinkKinds) {
|
|
2007
2016
|
if (kind !== "agent" && kind !== "skill") continue;
|
|
2008
2017
|
const target = path3.join(home, ".copilot", kind === "agent" ? "agents" : "skills");
|
|
2009
|
-
const
|
|
2010
|
-
if (
|
|
2018
|
+
const stat16 = await fsp.stat(target).catch(() => null);
|
|
2019
|
+
if (stat16?.isDirectory() === true) {
|
|
2011
2020
|
personalTargets[kind] = target;
|
|
2012
2021
|
}
|
|
2013
2022
|
}
|
|
@@ -2060,8 +2069,8 @@ var CopilotLinkMaterializer = class {
|
|
|
2060
2069
|
const previous = input.previousState.entries.find(
|
|
2061
2070
|
(state) => state.identity === entry.identity
|
|
2062
2071
|
);
|
|
2063
|
-
const
|
|
2064
|
-
if (
|
|
2072
|
+
const stat16 = await fsp2.lstat(linkPath).catch(() => null);
|
|
2073
|
+
if (stat16 && !stat16.isSymbolicLink()) {
|
|
2065
2074
|
results.push({
|
|
2066
2075
|
identity: entry.identity,
|
|
2067
2076
|
status: previous ? "drifted" : "conflict",
|
|
@@ -2069,7 +2078,7 @@ var CopilotLinkMaterializer = class {
|
|
|
2069
2078
|
});
|
|
2070
2079
|
continue;
|
|
2071
2080
|
}
|
|
2072
|
-
if (
|
|
2081
|
+
if (stat16?.isSymbolicLink()) {
|
|
2073
2082
|
const currentTarget = await fsp2.readlink(linkPath);
|
|
2074
2083
|
if (currentTarget !== entry.sourcePath) {
|
|
2075
2084
|
if (previous?.linkPath === this.toStateLinkPath(linkPath) && previous.sourcePath === entry.sourcePath) {
|
|
@@ -2108,8 +2117,8 @@ var CopilotLinkMaterializer = class {
|
|
|
2108
2117
|
/** Adopt a matching legacy link without recreating it. */
|
|
2109
2118
|
async adoptLegacyLink(input) {
|
|
2110
2119
|
const linkPath = this.resolveLinkPath(input.workspaceDir, input.entry);
|
|
2111
|
-
const
|
|
2112
|
-
if (
|
|
2120
|
+
const stat16 = await fsp2.lstat(linkPath).catch(() => null);
|
|
2121
|
+
if (stat16?.isSymbolicLink() && await fsp2.readlink(linkPath) === input.entry.sourcePath) {
|
|
2113
2122
|
return { identity: input.entry.identity, status: "adopted" };
|
|
2114
2123
|
}
|
|
2115
2124
|
return {
|
|
@@ -2127,8 +2136,8 @@ var CopilotLinkMaterializer = class {
|
|
|
2127
2136
|
kindDir,
|
|
2128
2137
|
input.entry.sourceIsFile ? path4.basename(input.entry.sourcePath) : input.entry.name
|
|
2129
2138
|
);
|
|
2130
|
-
const
|
|
2131
|
-
if (
|
|
2139
|
+
const stat16 = await fsp2.lstat(legacyPath).catch(() => null);
|
|
2140
|
+
if (stat16?.isSymbolicLink() && await fsp2.readlink(legacyPath) === input.entry.sourcePath) {
|
|
2132
2141
|
return {
|
|
2133
2142
|
identity: input.entry.identity,
|
|
2134
2143
|
status: "adopted",
|
|
@@ -2180,8 +2189,8 @@ var CopilotLinkMaterializer = class {
|
|
|
2180
2189
|
return toPosix(linkPath);
|
|
2181
2190
|
}
|
|
2182
2191
|
const workspaceRoot = path4.dirname(cursor);
|
|
2183
|
-
const
|
|
2184
|
-
return
|
|
2192
|
+
const relative5 = toPosix(path4.relative(workspaceRoot, linkPath));
|
|
2193
|
+
return relative5 === "" ? toPosix(linkPath) : relative5;
|
|
2185
2194
|
}
|
|
2186
2195
|
};
|
|
2187
2196
|
function toPosix(value) {
|
|
@@ -2227,8 +2236,8 @@ async function resolveSource(entry, pluginDir, repoRoot) {
|
|
|
2227
2236
|
candidates.push(path5.join(repoRoot, "agents", `${base}.agent.md`));
|
|
2228
2237
|
}
|
|
2229
2238
|
for (const candidate of candidates) {
|
|
2230
|
-
const
|
|
2231
|
-
if (
|
|
2239
|
+
const stat16 = await fs2.stat(candidate).catch(() => void 0);
|
|
2240
|
+
if (stat16) return candidate;
|
|
2232
2241
|
}
|
|
2233
2242
|
return null;
|
|
2234
2243
|
}
|
|
@@ -2241,8 +2250,8 @@ function normalizeRefPath(entry) {
|
|
|
2241
2250
|
async function copyProjectionEntry(source, dest) {
|
|
2242
2251
|
await fs2.mkdir(path5.dirname(dest), { recursive: true });
|
|
2243
2252
|
await fs2.rm(dest, { force: true, recursive: true });
|
|
2244
|
-
const
|
|
2245
|
-
if (
|
|
2253
|
+
const stat16 = await fs2.stat(source);
|
|
2254
|
+
if (stat16.isDirectory()) {
|
|
2246
2255
|
await fs2.cp(source, dest, { recursive: true, dereference: true });
|
|
2247
2256
|
} else {
|
|
2248
2257
|
await fs2.copyFile(source, dest);
|
|
@@ -2299,13 +2308,15 @@ async function materializePluginProjection(input) {
|
|
|
2299
2308
|
};
|
|
2300
2309
|
await fs2.writeFile(
|
|
2301
2310
|
inside("plugin.json"),
|
|
2302
|
-
JSON.stringify(servedPluginManifest(raw), null, 2)
|
|
2311
|
+
`${JSON.stringify(servedPluginManifest(raw), null, 2)}
|
|
2312
|
+
`
|
|
2303
2313
|
);
|
|
2304
2314
|
const pluginMarkerDir = inside(PLUGIN_MARKER_DIR);
|
|
2305
2315
|
await fs2.mkdir(pluginMarkerDir, { recursive: true });
|
|
2306
2316
|
await fs2.writeFile(
|
|
2307
2317
|
path5.join(pluginMarkerDir, "plugin.json"),
|
|
2308
|
-
JSON.stringify(servedPluginManifest(raw), null, 2)
|
|
2318
|
+
`${JSON.stringify(servedPluginManifest(raw), null, 2)}
|
|
2319
|
+
`
|
|
2309
2320
|
);
|
|
2310
2321
|
const mcpJson = await fs2.stat(path5.join(pluginDir, "mcp.json")).catch(() => void 0);
|
|
2311
2322
|
if (mcpJson?.isFile()) {
|
|
@@ -2317,8 +2328,8 @@ async function materializePluginProjection(input) {
|
|
|
2317
2328
|
return result;
|
|
2318
2329
|
}
|
|
2319
2330
|
async function isMaterializedProjection(dir) {
|
|
2320
|
-
const
|
|
2321
|
-
if (!
|
|
2331
|
+
const stat16 = await fs2.lstat(dir).catch(() => void 0);
|
|
2332
|
+
if (!stat16?.isDirectory() || stat16.isSymbolicLink()) return false;
|
|
2322
2333
|
const root = path5.resolve(dir);
|
|
2323
2334
|
const markerPath = path5.resolve(root, PLUGIN_MARKER_DIR, MARKER_FILE);
|
|
2324
2335
|
if (!markerPath.startsWith(root + path5.sep)) return false;
|
|
@@ -2428,8 +2439,8 @@ var CopilotPluginRegistrar = class {
|
|
|
2428
2439
|
merged.set(id, reg);
|
|
2429
2440
|
}
|
|
2430
2441
|
const legacyLink = path6.join(legacyDir, id);
|
|
2431
|
-
const
|
|
2432
|
-
if (
|
|
2442
|
+
const stat16 = await fs3.lstat(legacyLink).catch(() => void 0);
|
|
2443
|
+
if (stat16?.isSymbolicLink()) {
|
|
2433
2444
|
await this.writeRegistry(merged);
|
|
2434
2445
|
await fs3.mkdir(dest, { recursive: true });
|
|
2435
2446
|
await fs3.rm(this.linkPath(id), { force: true, recursive: true });
|
|
@@ -2579,6 +2590,7 @@ function buildCopilotCustomizationView(input) {
|
|
|
2579
2590
|
throw new Error(`Unknown Copilot source ${definition.sourceId} for package ${definition.id}`);
|
|
2580
2591
|
}
|
|
2581
2592
|
}
|
|
2593
|
+
const disabledArtifactIds = new Set(input.disabledArtifactIds ?? []);
|
|
2582
2594
|
const packages = input.installations.filter((installation) => installation.scope === input.scope).map((installation) => {
|
|
2583
2595
|
const definition = requirePackage(input.packages, installation.packageId);
|
|
2584
2596
|
const artifacts = installation.selectedArtifactIds.map((artifactId) => {
|
|
@@ -2602,6 +2614,11 @@ function buildCopilotCustomizationView(input) {
|
|
|
2602
2614
|
return status === "action-required" || status === "blocked";
|
|
2603
2615
|
})
|
|
2604
2616
|
);
|
|
2617
|
+
const packageCount = packages.filter((pkg) => pkg.definition.wholePackage === true).length;
|
|
2618
|
+
const enabledArtifactCount = packages.reduce(
|
|
2619
|
+
(count, pkg) => count + pkg.artifacts.filter(({ artifact }) => !disabledArtifactIds.has(artifact.id)).length,
|
|
2620
|
+
0
|
|
2621
|
+
);
|
|
2605
2622
|
const sources = input.sources.map((source) => ({
|
|
2606
2623
|
...source,
|
|
2607
2624
|
packages: packages.filter((pkg) => pkg.definition.sourceId === source.id)
|
|
@@ -2613,11 +2630,16 @@ function buildCopilotCustomizationView(input) {
|
|
|
2613
2630
|
packages,
|
|
2614
2631
|
attention,
|
|
2615
2632
|
summary: {
|
|
2616
|
-
packageCount
|
|
2617
|
-
enabledArtifactCount
|
|
2633
|
+
packageCount,
|
|
2634
|
+
enabledArtifactCount,
|
|
2618
2635
|
attentionCount: attention.length
|
|
2619
2636
|
},
|
|
2620
|
-
...input.legacyCount ? {
|
|
2637
|
+
...input.legacyCount ? {
|
|
2638
|
+
legacyMigration: {
|
|
2639
|
+
count: input.legacyCount,
|
|
2640
|
+
sourceLabel: "~/.agents"
|
|
2641
|
+
}
|
|
2642
|
+
} : {}
|
|
2621
2643
|
};
|
|
2622
2644
|
}
|
|
2623
2645
|
function requirePackage(packages, packageId) {
|
|
@@ -2641,6 +2663,33 @@ function requireState(statesByArtifactId, artifactId) {
|
|
|
2641
2663
|
// src/copilot-content/disabled-content-store.ts
|
|
2642
2664
|
var fsp3 = __toESM(require("fs/promises"));
|
|
2643
2665
|
var path7 = __toESM(require("path"));
|
|
2666
|
+
|
|
2667
|
+
// src/copilot-content/types.ts
|
|
2668
|
+
function buildContentIdentity(identity) {
|
|
2669
|
+
return `${identity.repoId}::${identity.pluginId}::${identity.kind}:${identity.name}`;
|
|
2670
|
+
}
|
|
2671
|
+
function parseContentIdentity(value) {
|
|
2672
|
+
const segments = value.split("::");
|
|
2673
|
+
if (segments.length !== 3) return null;
|
|
2674
|
+
const repoId = segments[0] ?? "";
|
|
2675
|
+
const pluginId = segments[1] ?? "";
|
|
2676
|
+
const tail = (segments[2] ?? "").split(":");
|
|
2677
|
+
const kind = tail[0] ?? "";
|
|
2678
|
+
const name = tail.slice(1).join(":");
|
|
2679
|
+
if (!repoId || !pluginId || !kind || !name) return null;
|
|
2680
|
+
return { repoId, pluginId, kind, name };
|
|
2681
|
+
}
|
|
2682
|
+
function getWorkspaceManifestSources(manifest) {
|
|
2683
|
+
return manifest.version === 1 ? manifest.repositories.map((repository) => ({
|
|
2684
|
+
...repository,
|
|
2685
|
+
type: "git"
|
|
2686
|
+
})) : manifest.sources;
|
|
2687
|
+
}
|
|
2688
|
+
function getWorkspaceManifestPluginSourceId(manifest, plugin) {
|
|
2689
|
+
return manifest.version === 1 ? plugin.repository : plugin.source;
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
// src/copilot-content/disabled-content-store.ts
|
|
2644
2693
|
function sameEntry(a, b) {
|
|
2645
2694
|
if (a.scope !== b.scope || a.repoId !== b.repoId || a.name !== b.name || a.kind !== b.kind) {
|
|
2646
2695
|
return false;
|
|
@@ -2650,6 +2699,15 @@ function sameEntry(a, b) {
|
|
|
2650
2699
|
}
|
|
2651
2700
|
return true;
|
|
2652
2701
|
}
|
|
2702
|
+
function buildDisabledIdentityMatcher(workspaceDir, marks) {
|
|
2703
|
+
return (identity) => {
|
|
2704
|
+
const parsed = parseContentIdentity(identity);
|
|
2705
|
+
if (parsed === null) return false;
|
|
2706
|
+
return marks.some(
|
|
2707
|
+
(mark) => mark.repoId === parsed.repoId && mark.kind === parsed.kind && mark.name === parsed.name && (mark.scope === "user" || mark.workspaceDir === workspaceDir)
|
|
2708
|
+
);
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2653
2711
|
var DisabledContentStore = class {
|
|
2654
2712
|
constructor(options = {}) {
|
|
2655
2713
|
this.homeDir = options.homeDir ?? getServicemeHome();
|
|
@@ -2670,9 +2728,6 @@ var DisabledContentStore = class {
|
|
|
2670
2728
|
throw error;
|
|
2671
2729
|
}
|
|
2672
2730
|
}
|
|
2673
|
-
async has(matcher) {
|
|
2674
|
-
return this.list().then((entries) => entries.some(matcher));
|
|
2675
|
-
}
|
|
2676
2731
|
async add(entry) {
|
|
2677
2732
|
const entries = await this.list();
|
|
2678
2733
|
if (entries.some((candidate) => sameEntry(candidate, entry))) {
|
|
@@ -2688,6 +2743,30 @@ var DisabledContentStore = class {
|
|
|
2688
2743
|
}
|
|
2689
2744
|
await this.write(next);
|
|
2690
2745
|
}
|
|
2746
|
+
/**
|
|
2747
|
+
* Lifecycle cleanup: installing or uninstalling an artifact clears
|
|
2748
|
+
* its disable marks for that scope — a mark without its artifact is
|
|
2749
|
+
* stale garbage that keeps the home page showing a disabled row for
|
|
2750
|
+
* content the catalog already reports as gone. User scope matches
|
|
2751
|
+
* any workspace; workspace scope matches the exact workspace.
|
|
2752
|
+
*/
|
|
2753
|
+
async removeForArtifact(input) {
|
|
2754
|
+
const entries = await this.list();
|
|
2755
|
+
const next = entries.filter((candidate) => {
|
|
2756
|
+
const sameArtifact = candidate.repoId === input.repoId && candidate.name === input.name && candidate.kind === input.kind;
|
|
2757
|
+
if (!sameArtifact || candidate.scope !== input.scope) {
|
|
2758
|
+
return true;
|
|
2759
|
+
}
|
|
2760
|
+
if (input.scope === "user") {
|
|
2761
|
+
return false;
|
|
2762
|
+
}
|
|
2763
|
+
return candidate.workspaceDir !== input.workspaceDir;
|
|
2764
|
+
});
|
|
2765
|
+
if (next.length === entries.length) {
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
await this.write(next);
|
|
2769
|
+
}
|
|
2691
2770
|
async write(entries) {
|
|
2692
2771
|
const statePath = await this.path();
|
|
2693
2772
|
await fsp3.mkdir(path7.dirname(statePath), { recursive: true });
|
|
@@ -2872,9 +2951,9 @@ async function copyDirPreservingMode(sourceDir, targetDir) {
|
|
|
2872
2951
|
continue;
|
|
2873
2952
|
}
|
|
2874
2953
|
if (!dirent.isFile()) continue;
|
|
2875
|
-
const
|
|
2954
|
+
const stat16 = await fsp4.stat(sourceChild);
|
|
2876
2955
|
await fsp4.copyFile(sourceChild, targetChild);
|
|
2877
|
-
await fsp4.chmod(targetChild,
|
|
2956
|
+
await fsp4.chmod(targetChild, stat16.mode & 511);
|
|
2878
2957
|
}
|
|
2879
2958
|
}
|
|
2880
2959
|
|
|
@@ -2989,7 +3068,7 @@ async function defaultResolveGitExcludePath(workspaceDir) {
|
|
|
2989
3068
|
);
|
|
2990
3069
|
}
|
|
2991
3070
|
async function resolveGitExcludeViaRevParse(workspaceDir) {
|
|
2992
|
-
const result = await new Promise((
|
|
3071
|
+
const result = await new Promise((resolve13, reject) => {
|
|
2993
3072
|
const child = (0, import_node_child_process2.spawn)("git", ["rev-parse", "--git-path", "info/exclude"], {
|
|
2994
3073
|
cwd: workspaceDir
|
|
2995
3074
|
});
|
|
@@ -2998,7 +3077,7 @@ async function resolveGitExcludeViaRevParse(workspaceDir) {
|
|
|
2998
3077
|
stdout += chunk;
|
|
2999
3078
|
});
|
|
3000
3079
|
child.on("error", reject);
|
|
3001
|
-
child.on("close", (code) =>
|
|
3080
|
+
child.on("close", (code) => resolve13({ code: code ?? -1, stdout }));
|
|
3002
3081
|
});
|
|
3003
3082
|
if (result.code !== 0) {
|
|
3004
3083
|
throw new Error(`git rev-parse --git-path failed with exit code ${result.code}`);
|
|
@@ -3085,17 +3164,26 @@ var catalogSourceSchema = import_zod.z.object({
|
|
|
3085
3164
|
revision: import_zod.z.string().regex(CATALOG_REVISION_PATTERN, "invalid catalog revision"),
|
|
3086
3165
|
digest: import_zod.z.string().regex(SHA256_DIGEST_PATTERN, "invalid catalog digest")
|
|
3087
3166
|
});
|
|
3167
|
+
var artifactTargetSchema = import_zod.z.string().refine((value) => {
|
|
3168
|
+
const separator = value.indexOf(":");
|
|
3169
|
+
if (separator <= 0 || separator === value.length - 1) return false;
|
|
3170
|
+
const kind = value.slice(0, separator);
|
|
3171
|
+
const name = value.slice(separator + 1);
|
|
3172
|
+
return artifactKindSchema.safeParse(kind).success && !/[\\/\0]/.test(name) && name !== "." && name !== "..";
|
|
3173
|
+
}, "unsafe artifact target");
|
|
3088
3174
|
var pluginSchema = import_zod.z.object({
|
|
3089
3175
|
repository: import_zod.z.string().min(1),
|
|
3090
3176
|
id: import_zod.z.string().regex(PLUGIN_ID_PATTERN, "unsafe plugin id"),
|
|
3091
3177
|
artifacts: import_zod.z.record(import_zod.z.string(), import_zod.z.boolean()),
|
|
3092
|
-
artifactIds: import_zod.z.array(artifactIdSchema).optional()
|
|
3178
|
+
artifactIds: import_zod.z.array(artifactIdSchema).optional(),
|
|
3179
|
+
disabledArtifacts: import_zod.z.array(artifactTargetSchema).optional()
|
|
3093
3180
|
});
|
|
3094
3181
|
var pluginV2Schema = import_zod.z.object({
|
|
3095
3182
|
source: import_zod.z.string().min(1),
|
|
3096
3183
|
id: import_zod.z.string().regex(PLUGIN_ID_PATTERN, "unsafe plugin id"),
|
|
3097
3184
|
artifacts: import_zod.z.record(import_zod.z.string(), import_zod.z.boolean()),
|
|
3098
|
-
artifactIds: import_zod.z.array(artifactIdSchema).optional()
|
|
3185
|
+
artifactIds: import_zod.z.array(artifactIdSchema).optional(),
|
|
3186
|
+
disabledArtifacts: import_zod.z.array(artifactTargetSchema).optional()
|
|
3099
3187
|
});
|
|
3100
3188
|
function validatePluginArtifacts(plugins, ctx) {
|
|
3101
3189
|
for (const plugin of plugins) {
|
|
@@ -3306,6 +3394,34 @@ async function removeWorkspaceContentSelection(input) {
|
|
|
3306
3394
|
await writeWorkspaceCopilotManifest(input.workspaceDir, next);
|
|
3307
3395
|
return next;
|
|
3308
3396
|
}
|
|
3397
|
+
async function setWorkspacePluginArtifactsDisabled(input) {
|
|
3398
|
+
const manifest = await loadWorkspaceCopilotManifest(input.workspaceDir).catch(() => void 0);
|
|
3399
|
+
if (!manifest) return void 0;
|
|
3400
|
+
const withDisabledTarget = (plugin) => {
|
|
3401
|
+
const current = new Set(plugin.disabledArtifacts ?? []);
|
|
3402
|
+
if (input.disabled) {
|
|
3403
|
+
current.add(input.target);
|
|
3404
|
+
} else {
|
|
3405
|
+
current.delete(input.target);
|
|
3406
|
+
}
|
|
3407
|
+
const { disabledArtifacts: _dropped, ...rest } = plugin;
|
|
3408
|
+
return current.size > 0 ? { ...rest, disabledArtifacts: [...current].sort() } : rest;
|
|
3409
|
+
};
|
|
3410
|
+
if (manifest.version === 2) {
|
|
3411
|
+
const plugins2 = manifest.plugins.map(
|
|
3412
|
+
(plugin) => plugin.source === input.repositoryId && plugin.id === input.pluginId ? withDisabledTarget({ ...plugin, repository: plugin.source }) : plugin
|
|
3413
|
+
);
|
|
3414
|
+
const next2 = { ...manifest, plugins: plugins2 };
|
|
3415
|
+
await writeWorkspaceCopilotManifest(input.workspaceDir, next2);
|
|
3416
|
+
return next2;
|
|
3417
|
+
}
|
|
3418
|
+
const plugins = manifest.plugins.map(
|
|
3419
|
+
(plugin) => plugin.repository === input.repositoryId && plugin.id === input.pluginId ? withDisabledTarget(plugin) : plugin
|
|
3420
|
+
);
|
|
3421
|
+
const next = { ...manifest, plugins };
|
|
3422
|
+
await writeWorkspaceCopilotManifest(input.workspaceDir, next);
|
|
3423
|
+
return next;
|
|
3424
|
+
}
|
|
3309
3425
|
|
|
3310
3426
|
// src/copilot-content/workspace-state-store.ts
|
|
3311
3427
|
var import_node_crypto = require("crypto");
|
|
@@ -3521,8 +3637,8 @@ var PackageInstallationService = class {
|
|
|
3521
3637
|
const dirents = await fsp9.readdir(kindDir, { withFileTypes: true }).catch(() => []);
|
|
3522
3638
|
for (const dirent of dirents) {
|
|
3523
3639
|
const legacyPath = path13.join(kindDir, dirent.name);
|
|
3524
|
-
const
|
|
3525
|
-
const isLink =
|
|
3640
|
+
const stat16 = await fsp9.lstat(legacyPath).catch(() => null);
|
|
3641
|
+
const isLink = stat16?.isSymbolicLink() === true;
|
|
3526
3642
|
const targetPath = isLink ? await fsp9.readlink(legacyPath).catch(() => "") : "";
|
|
3527
3643
|
const resolved = isLink ? this.resolveLegacyTarget(legacyPath, targetPath) : "";
|
|
3528
3644
|
const targetStat = resolved !== "" ? await fsp9.stat(resolved).catch(() => null) : null;
|
|
@@ -3548,8 +3664,8 @@ var PackageInstallationService = class {
|
|
|
3548
3664
|
if (!entry?.eligible) {
|
|
3549
3665
|
throw new Error(`Legacy entry ${artifactId} is not eligible for migration`);
|
|
3550
3666
|
}
|
|
3551
|
-
const
|
|
3552
|
-
if (!
|
|
3667
|
+
const stat16 = await fsp9.lstat(entry.sourcePath).catch(() => null);
|
|
3668
|
+
if (!stat16?.isSymbolicLink()) {
|
|
3553
3669
|
throw new Error(`Legacy entry ${entry.name} is no longer a link`);
|
|
3554
3670
|
}
|
|
3555
3671
|
const currentTarget = this.resolveLegacyTarget(
|
|
@@ -4077,8 +4193,8 @@ var PersonalCopilotContentReconciler = class {
|
|
|
4077
4193
|
const target = this.capabilities.personalTargets[previous.kind];
|
|
4078
4194
|
if (target === void 0) return;
|
|
4079
4195
|
const linkPath = path15.join(target, path15.basename(previous.linkPath));
|
|
4080
|
-
const
|
|
4081
|
-
if (!
|
|
4196
|
+
const stat16 = await fsp11.lstat(linkPath).catch(() => null);
|
|
4197
|
+
if (!stat16?.isSymbolicLink()) return;
|
|
4082
4198
|
const currentTarget = await fsp11.readlink(linkPath).catch(() => null);
|
|
4083
4199
|
if (currentTarget !== previous.sourcePath) return;
|
|
4084
4200
|
await fsp11.rm(linkPath, { force: true });
|
|
@@ -4157,16 +4273,6 @@ var path17 = __toESM(require("path"));
|
|
|
4157
4273
|
var import_node_crypto2 = require("crypto");
|
|
4158
4274
|
var fsp12 = __toESM(require("fs/promises"));
|
|
4159
4275
|
var path16 = __toESM(require("path"));
|
|
4160
|
-
|
|
4161
|
-
// src/copilot-content/types.ts
|
|
4162
|
-
function getWorkspaceManifestSources(manifest) {
|
|
4163
|
-
return manifest.version === 1 ? manifest.repositories.map((repository) => ({ ...repository, type: "git" })) : manifest.sources;
|
|
4164
|
-
}
|
|
4165
|
-
function getWorkspaceManifestPluginSourceId(manifest, plugin) {
|
|
4166
|
-
return manifest.version === 1 ? plugin.repository : plugin.source;
|
|
4167
|
-
}
|
|
4168
|
-
|
|
4169
|
-
// src/copilot-content/plugin-resolver.ts
|
|
4170
4276
|
var AWESOME_COPILOT_NAMESPACE = "com.github.awesome-copilot";
|
|
4171
4277
|
var PATH_KINDS = {
|
|
4172
4278
|
agent: "agents",
|
|
@@ -4180,6 +4286,7 @@ var EXECUTABLE_SUFFIXES = /* @__PURE__ */ new Set([".py", ".rb"]);
|
|
|
4180
4286
|
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".vscode", "dist", "build", "out", "plugins"]);
|
|
4181
4287
|
async function resolveWorkspaceContentPlan(input) {
|
|
4182
4288
|
const entries = [];
|
|
4289
|
+
const conflicts = [];
|
|
4183
4290
|
const seenTargets = /* @__PURE__ */ new Map();
|
|
4184
4291
|
const sources = getWorkspaceManifestSources(input.manifest);
|
|
4185
4292
|
for (const plugin of input.manifest.plugins) {
|
|
@@ -4229,15 +4336,17 @@ async function resolveWorkspaceContentPlan(input) {
|
|
|
4229
4336
|
const targetKey = entry.kind === "mcp" || entry.kind === "hook" ? `${entry.kind}:${plugin.id}:${entry.name}` : `${entry.kind}:${entry.name}`;
|
|
4230
4337
|
const owner = seenTargets.get(targetKey);
|
|
4231
4338
|
if (owner !== void 0) {
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4339
|
+
conflicts.push({
|
|
4340
|
+
identity: entry.identity,
|
|
4341
|
+
message: `Duplicate ${entry.kind} target ${entry.name} from plugins ${owner} and ${plugin.id}`
|
|
4342
|
+
});
|
|
4343
|
+
continue;
|
|
4235
4344
|
}
|
|
4236
4345
|
seenTargets.set(targetKey, plugin.id);
|
|
4237
4346
|
entries.push(entry);
|
|
4238
4347
|
}
|
|
4239
4348
|
}
|
|
4240
|
-
return { entries };
|
|
4349
|
+
return { entries, conflicts };
|
|
4241
4350
|
}
|
|
4242
4351
|
async function resolvePluginEntriesLenient(input) {
|
|
4243
4352
|
const namespace = input.manifest.extensions[AWESOME_COPILOT_NAMESPACE];
|
|
@@ -4247,6 +4356,7 @@ async function resolvePluginEntriesLenient(input) {
|
|
|
4247
4356
|
path16.join(input.repoRoot, "plugins", input.pluginId),
|
|
4248
4357
|
input.repositoryId,
|
|
4249
4358
|
input.pluginId,
|
|
4359
|
+
void 0,
|
|
4250
4360
|
input.manifest
|
|
4251
4361
|
);
|
|
4252
4362
|
}
|
|
@@ -4255,9 +4365,9 @@ async function resolvePluginEntriesLenient(input) {
|
|
|
4255
4365
|
if (kind === "mcp" || kind === "hook") continue;
|
|
4256
4366
|
const declared = namespace[manifestKey];
|
|
4257
4367
|
if (!Array.isArray(declared)) continue;
|
|
4258
|
-
for (const
|
|
4259
|
-
if (typeof
|
|
4260
|
-
const effective = await resolveDeclaredRelative(input.repoRoot,
|
|
4368
|
+
for (const relative5 of declared) {
|
|
4369
|
+
if (typeof relative5 !== "string") continue;
|
|
4370
|
+
const effective = await resolveDeclaredRelative(input.repoRoot, relative5);
|
|
4261
4371
|
const sourcePath = path16.resolve(input.repoRoot, effective);
|
|
4262
4372
|
if (!sourcePath.startsWith(`${input.repoRoot}${path16.sep}`)) continue;
|
|
4263
4373
|
try {
|
|
@@ -4297,10 +4407,12 @@ async function tryReadPluginManifest(repoRoot, pluginId) {
|
|
|
4297
4407
|
function selectedKind(artifacts, kind) {
|
|
4298
4408
|
return artifacts?.[kind] !== false;
|
|
4299
4409
|
}
|
|
4300
|
-
async function resolveConventionEntries(repoRoot, pluginDir, repositoryId, pluginId, manifest) {
|
|
4410
|
+
async function resolveConventionEntries(repoRoot, pluginDir, repositoryId, pluginId, artifacts, manifest) {
|
|
4301
4411
|
const entries = [];
|
|
4302
4412
|
const seen = /* @__PURE__ */ new Set();
|
|
4413
|
+
const wants = artifacts === void 0 || Object.keys(artifacts).length === 0 ? () => true : (kind) => artifacts[kind] === true;
|
|
4303
4414
|
const push = async (kind, base, subdir, options = {}) => {
|
|
4415
|
+
if (!wants(kind)) return;
|
|
4304
4416
|
const dir = path16.join(base, subdir);
|
|
4305
4417
|
const dirents = await fsp12.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
4306
4418
|
for (const dirent of dirents) {
|
|
@@ -4327,11 +4439,18 @@ async function resolveConventionEntries(repoRoot, pluginDir, repositoryId, plugi
|
|
|
4327
4439
|
await push("prompt", base, "commands");
|
|
4328
4440
|
await push("hook", base, "hooks");
|
|
4329
4441
|
}
|
|
4330
|
-
|
|
4442
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
4443
|
+
for (const entry of entries) {
|
|
4444
|
+
const key = `${entry.kind}:${entry.name}`;
|
|
4445
|
+
if (!byTarget.has(key)) {
|
|
4446
|
+
byTarget.set(key, entry);
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
return [...byTarget.values()];
|
|
4331
4450
|
}
|
|
4332
|
-
async function resolveDeclaredRelative(repoRoot,
|
|
4333
|
-
if (await pathExists2(path16.resolve(repoRoot,
|
|
4334
|
-
const normalized =
|
|
4451
|
+
async function resolveDeclaredRelative(repoRoot, relative5) {
|
|
4452
|
+
if (await pathExists2(path16.resolve(repoRoot, relative5))) return relative5;
|
|
4453
|
+
const normalized = relative5.replace(/^\.\//, "");
|
|
4335
4454
|
if (normalized.startsWith("agents/") && normalized.endsWith(".md")) {
|
|
4336
4455
|
const base = path16.basename(normalized, ".md");
|
|
4337
4456
|
const candidate = path16.join(path16.dirname(normalized), `${base}.agent.md`);
|
|
@@ -4339,7 +4458,7 @@ async function resolveDeclaredRelative(repoRoot, relative4) {
|
|
|
4339
4458
|
return `./${candidate}`;
|
|
4340
4459
|
}
|
|
4341
4460
|
}
|
|
4342
|
-
return
|
|
4461
|
+
return relative5;
|
|
4343
4462
|
}
|
|
4344
4463
|
async function resolvePluginManifestEntries(repoRoot, repositoryId, pluginId, artifacts, pluginManifest) {
|
|
4345
4464
|
const namespace = pluginManifest.extensions[AWESOME_COPILOT_NAMESPACE];
|
|
@@ -4349,6 +4468,7 @@ async function resolvePluginManifestEntries(repoRoot, repositoryId, pluginId, ar
|
|
|
4349
4468
|
path16.join(repoRoot, "plugins", pluginId),
|
|
4350
4469
|
repositoryId,
|
|
4351
4470
|
pluginId,
|
|
4471
|
+
artifacts,
|
|
4352
4472
|
pluginManifest
|
|
4353
4473
|
);
|
|
4354
4474
|
}
|
|
@@ -4357,8 +4477,8 @@ async function resolvePluginManifestEntries(repoRoot, repositoryId, pluginId, ar
|
|
|
4357
4477
|
if (!selectedKind(artifacts, kind)) continue;
|
|
4358
4478
|
const declared = namespace[manifestKey];
|
|
4359
4479
|
if (!Array.isArray(declared)) continue;
|
|
4360
|
-
for (const
|
|
4361
|
-
if (typeof
|
|
4480
|
+
for (const relative5 of declared) {
|
|
4481
|
+
if (typeof relative5 !== "string") {
|
|
4362
4482
|
throw new Error(`Plugin ${pluginId} declares a non-string ${manifestKey} path`);
|
|
4363
4483
|
}
|
|
4364
4484
|
entries.push(
|
|
@@ -4367,7 +4487,7 @@ async function resolvePluginManifestEntries(repoRoot, repositoryId, pluginId, ar
|
|
|
4367
4487
|
repositoryId,
|
|
4368
4488
|
pluginId,
|
|
4369
4489
|
kind,
|
|
4370
|
-
await resolveDeclaredRelative(repoRoot,
|
|
4490
|
+
await resolveDeclaredRelative(repoRoot, relative5),
|
|
4371
4491
|
pluginManifest
|
|
4372
4492
|
)
|
|
4373
4493
|
);
|
|
@@ -4473,23 +4593,23 @@ async function findFlatFile(repoRoot, filename, preferredDir) {
|
|
|
4473
4593
|
}
|
|
4474
4594
|
return null;
|
|
4475
4595
|
}
|
|
4476
|
-
async function materializePathEntry(repoRoot, repositoryId, pluginId, kind,
|
|
4477
|
-
const sourcePath = path16.resolve(repoRoot,
|
|
4596
|
+
async function materializePathEntry(repoRoot, repositoryId, pluginId, kind, relative5, pluginManifest) {
|
|
4597
|
+
const sourcePath = path16.resolve(repoRoot, relative5);
|
|
4478
4598
|
if (sourcePath !== repoRoot && !sourcePath.startsWith(`${repoRoot}${path16.sep}`)) {
|
|
4479
|
-
throw new Error(`Plugin ${pluginId} path escapes repository root: ${
|
|
4599
|
+
throw new Error(`Plugin ${pluginId} path escapes repository root: ${relative5}`);
|
|
4480
4600
|
}
|
|
4481
|
-
const
|
|
4482
|
-
if (!
|
|
4483
|
-
throw new Error(`Plugin ${pluginId} source missing: ${
|
|
4601
|
+
const stat16 = await fsp12.stat(sourcePath).catch(() => null);
|
|
4602
|
+
if (!stat16) {
|
|
4603
|
+
throw new Error(`Plugin ${pluginId} source missing: ${relative5}`);
|
|
4484
4604
|
}
|
|
4485
4605
|
if (kind === "skill") {
|
|
4486
|
-
if (!
|
|
4487
|
-
throw new Error(`Plugin ${pluginId} skill source has no SKILL.md: ${
|
|
4606
|
+
if (!stat16.isDirectory() || !await pathExists2(path16.join(sourcePath, "SKILL.md"))) {
|
|
4607
|
+
throw new Error(`Plugin ${pluginId} skill source has no SKILL.md: ${relative5}`);
|
|
4488
4608
|
}
|
|
4489
4609
|
return [await buildEntry(repositoryId, pluginId, kind, sourcePath, false, pluginManifest)];
|
|
4490
4610
|
}
|
|
4491
|
-
if (!
|
|
4492
|
-
throw new Error(`Plugin ${pluginId} ${kind} source is not a file: ${
|
|
4611
|
+
if (!stat16.isFile()) {
|
|
4612
|
+
throw new Error(`Plugin ${pluginId} ${kind} source is not a file: ${relative5}`);
|
|
4493
4613
|
}
|
|
4494
4614
|
return [await buildEntry(repositoryId, pluginId, kind, sourcePath, true, pluginManifest)];
|
|
4495
4615
|
}
|
|
@@ -4611,9 +4731,9 @@ async function resolveCatalogPackage(sourceId, repoRoot, pluginId, cache) {
|
|
|
4611
4731
|
}
|
|
4612
4732
|
const cacheKey = `${sourceId}/${pluginId}`;
|
|
4613
4733
|
try {
|
|
4614
|
-
const
|
|
4734
|
+
const stat16 = await fsp13.stat(manifestPath);
|
|
4615
4735
|
const cached = cache.get(cacheKey);
|
|
4616
|
-
if (cached && cached.mtimeMs ===
|
|
4736
|
+
if (cached && cached.mtimeMs === stat16.mtimeMs && cached.size === stat16.size) {
|
|
4617
4737
|
return cached.pkg;
|
|
4618
4738
|
}
|
|
4619
4739
|
const parsed = JSON.parse(await fsp13.readFile(manifestPath, "utf8"));
|
|
@@ -4642,7 +4762,7 @@ async function resolveCatalogPackage(sourceId, repoRoot, pluginId, cache) {
|
|
|
4642
4762
|
...typeof parsed.version === "string" && parsed.version.trim().length > 0 ? { version: parsed.version.trim() } : {},
|
|
4643
4763
|
artifacts: pluginEntries.map(toArtifactSummary)
|
|
4644
4764
|
};
|
|
4645
|
-
cache.set(cacheKey, { mtimeMs:
|
|
4765
|
+
cache.set(cacheKey, { mtimeMs: stat16.mtimeMs, size: stat16.size, pkg });
|
|
4646
4766
|
return pkg;
|
|
4647
4767
|
} catch {
|
|
4648
4768
|
cache.delete(cacheKey);
|
|
@@ -4729,8 +4849,8 @@ var CopilotSourceCatalogService = class {
|
|
|
4729
4849
|
* exactly like git checkouts, so availability is a directory check.
|
|
4730
4850
|
*/
|
|
4731
4851
|
async isSourceAvailable(sourceId) {
|
|
4732
|
-
const
|
|
4733
|
-
return
|
|
4852
|
+
const stat16 = await fsp14.stat(path18.join(this.reposDir, sourceId)).catch(() => null);
|
|
4853
|
+
return stat16?.isDirectory() === true;
|
|
4734
4854
|
}
|
|
4735
4855
|
/**
|
|
4736
4856
|
* Deterministic, side-effect-free update preview: compares the
|
|
@@ -4807,9 +4927,243 @@ var CopilotSourceCatalogService = class {
|
|
|
4807
4927
|
}
|
|
4808
4928
|
};
|
|
4809
4929
|
|
|
4810
|
-
// src/copilot-content/workspace-
|
|
4930
|
+
// src/copilot-content/workspace-content-detector.ts
|
|
4931
|
+
var import_node_crypto3 = require("crypto");
|
|
4811
4932
|
var fsp15 = __toESM(require("fs/promises"));
|
|
4812
4933
|
var path19 = __toESM(require("path"));
|
|
4934
|
+
function unmanagedDetectionSignature(detection) {
|
|
4935
|
+
const parts = [
|
|
4936
|
+
...detection.adoptions.map((a) => `a:${a.kind}:${a.name}`),
|
|
4937
|
+
...detection.ambiguous.map((a) => `b:${a.kind}:${a.name}`),
|
|
4938
|
+
...detection.integrations.map((i) => `i:${i.kind}:${i.pluginId}`)
|
|
4939
|
+
].sort();
|
|
4940
|
+
return parts.join("|");
|
|
4941
|
+
}
|
|
4942
|
+
function coveredTargetsOf(manifest) {
|
|
4943
|
+
const targets = /* @__PURE__ */ new Set();
|
|
4944
|
+
const extensions = manifest.extensions;
|
|
4945
|
+
if (!extensions || typeof extensions !== "object") return targets;
|
|
4946
|
+
for (const namespace of Object.values(extensions)) {
|
|
4947
|
+
if (!namespace || typeof namespace !== "object") continue;
|
|
4948
|
+
for (const [field, refs] of Object.entries(namespace)) {
|
|
4949
|
+
if (!Array.isArray(refs)) continue;
|
|
4950
|
+
for (const ref of refs) {
|
|
4951
|
+
if (typeof ref !== "string" || !ref.startsWith("./")) continue;
|
|
4952
|
+
const normalized = ref.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
4953
|
+
const segments = normalized.split("/");
|
|
4954
|
+
const dir = segments[0];
|
|
4955
|
+
const base = (segments[1] ?? "").replace(/\.(agent\.)?md$/i, "");
|
|
4956
|
+
if (!base) continue;
|
|
4957
|
+
if (dir === "skills" || field === "skills") targets.add(`skill:${base}`);
|
|
4958
|
+
if (dir === "agents" || field === "agents") targets.add(`agent:${base}`);
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
}
|
|
4962
|
+
return targets;
|
|
4963
|
+
}
|
|
4964
|
+
async function findPluginCoveringArtifact(repoRoot, kind, name) {
|
|
4965
|
+
const root = path19.resolve(repoRoot);
|
|
4966
|
+
const pluginsDir = path19.join(root, "plugins");
|
|
4967
|
+
const dirents = await fsp15.readdir(pluginsDir, { withFileTypes: true }).catch(() => []);
|
|
4968
|
+
const target = `${kind}:${name}`;
|
|
4969
|
+
for (const dirent of dirents) {
|
|
4970
|
+
if (!dirent.isDirectory()) continue;
|
|
4971
|
+
const pluginDir = path19.join(pluginsDir, dirent.name);
|
|
4972
|
+
let pluginName;
|
|
4973
|
+
let pluginManifest;
|
|
4974
|
+
for (const manifestRel of ["plugin.json", path19.join(".plugin", "plugin.json")]) {
|
|
4975
|
+
const manifestPath = path19.resolve(pluginDir, manifestRel);
|
|
4976
|
+
if (!manifestPath.startsWith(root + path19.sep)) continue;
|
|
4977
|
+
try {
|
|
4978
|
+
const parsed = JSON.parse(await fsp15.readFile(manifestPath, "utf8"));
|
|
4979
|
+
if (typeof parsed?.name !== "string" || parsed.name.length === 0) continue;
|
|
4980
|
+
pluginName = parsed.name;
|
|
4981
|
+
pluginManifest = parsed;
|
|
4982
|
+
break;
|
|
4983
|
+
} catch {
|
|
4984
|
+
}
|
|
4985
|
+
}
|
|
4986
|
+
if (pluginName === void 0 || pluginManifest === void 0) continue;
|
|
4987
|
+
if (coveredTargetsOf(pluginManifest).has(target)) {
|
|
4988
|
+
return pluginName;
|
|
4989
|
+
}
|
|
4990
|
+
const conventionBase = pluginManifest.extensions && typeof pluginManifest.extensions === "object" ? void 0 : path19.join(pluginDir, kind === "skill" ? "skills" : "agents");
|
|
4991
|
+
if (conventionBase !== void 0) {
|
|
4992
|
+
const conventionPath = path19.resolve(
|
|
4993
|
+
conventionBase,
|
|
4994
|
+
kind === "skill" ? path19.join(name, "SKILL.md") : `${name}.agent.md`
|
|
4995
|
+
);
|
|
4996
|
+
if (!conventionPath.startsWith(root + path19.sep)) continue;
|
|
4997
|
+
if (await fsp15.stat(conventionPath).then(
|
|
4998
|
+
() => true,
|
|
4999
|
+
() => false
|
|
5000
|
+
)) {
|
|
5001
|
+
return pluginName;
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
return null;
|
|
5006
|
+
}
|
|
5007
|
+
function dedupeAdoptionsByPluginManifests(entries, pluginManifests) {
|
|
5008
|
+
const covered = /* @__PURE__ */ new Map();
|
|
5009
|
+
for (const [key, manifest] of pluginManifests) {
|
|
5010
|
+
covered.set(key, coveredTargetsOf(manifest));
|
|
5011
|
+
}
|
|
5012
|
+
const kept = [];
|
|
5013
|
+
const dropped = [];
|
|
5014
|
+
for (const entry of entries) {
|
|
5015
|
+
let owner;
|
|
5016
|
+
for (const other of entries) {
|
|
5017
|
+
if (other.repoId !== entry.repoId || other.name === entry.name) continue;
|
|
5018
|
+
const targets = covered.get(`${entry.repoId}::${other.name}`);
|
|
5019
|
+
if (targets?.has(`${entry.kind}:${entry.name}`)) {
|
|
5020
|
+
owner = other.name;
|
|
5021
|
+
break;
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
5024
|
+
if (owner === void 0) {
|
|
5025
|
+
kept.push(entry);
|
|
5026
|
+
} else {
|
|
5027
|
+
dropped.push({ entry, coveredBy: owner });
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
return { kept, dropped };
|
|
5031
|
+
}
|
|
5032
|
+
async function digestFile2(file) {
|
|
5033
|
+
const hash = (0, import_node_crypto3.createHash)("sha256");
|
|
5034
|
+
hash.update(await fsp15.readFile(file));
|
|
5035
|
+
return hash.digest("hex");
|
|
5036
|
+
}
|
|
5037
|
+
async function digestTree(dir) {
|
|
5038
|
+
const hash = (0, import_node_crypto3.createHash)("sha256");
|
|
5039
|
+
await digestInto2(dir, hash);
|
|
5040
|
+
return hash.digest("hex");
|
|
5041
|
+
}
|
|
5042
|
+
async function digestInto2(dir, hash) {
|
|
5043
|
+
const dirents = await fsp15.readdir(dir, { withFileTypes: true });
|
|
5044
|
+
dirents.sort((a, b) => a.name.localeCompare(b.name));
|
|
5045
|
+
for (const dirent of dirents) {
|
|
5046
|
+
const child = path19.join(dir, dirent.name);
|
|
5047
|
+
hash.update(dirent.name);
|
|
5048
|
+
if (dirent.isFile()) {
|
|
5049
|
+
hash.update(await fsp15.readFile(child));
|
|
5050
|
+
} else if (dirent.isDirectory()) {
|
|
5051
|
+
await digestInto2(child, hash);
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
5055
|
+
async function digestCatalogEntry(entry) {
|
|
5056
|
+
return entry.dir === entry.manifestPath ? digestFile2(entry.manifestPath) : digestTree(entry.dir);
|
|
5057
|
+
}
|
|
5058
|
+
function entryNameFromLinkBasename(basename12, kind) {
|
|
5059
|
+
if (kind === "agent") {
|
|
5060
|
+
return basename12.replace(/\.agent\.md$/i, "");
|
|
5061
|
+
}
|
|
5062
|
+
return basename12;
|
|
5063
|
+
}
|
|
5064
|
+
async function readServicemeIdentities(configPath) {
|
|
5065
|
+
try {
|
|
5066
|
+
const raw = await fsp15.readFile(configPath, "utf8");
|
|
5067
|
+
const parsed = JSON.parse(raw);
|
|
5068
|
+
if (parsed._serviceme && typeof parsed._serviceme === "object") {
|
|
5069
|
+
return new Map(Object.entries(parsed._serviceme));
|
|
5070
|
+
}
|
|
5071
|
+
} catch {
|
|
5072
|
+
}
|
|
5073
|
+
return /* @__PURE__ */ new Map();
|
|
5074
|
+
}
|
|
5075
|
+
async function detectUnmanagedWorkspaceContent(input) {
|
|
5076
|
+
const detection = {
|
|
5077
|
+
adoptions: [],
|
|
5078
|
+
ambiguous: [],
|
|
5079
|
+
integrations: [],
|
|
5080
|
+
unmatched: []
|
|
5081
|
+
};
|
|
5082
|
+
const catalogByName = /* @__PURE__ */ new Map();
|
|
5083
|
+
for (const entry of input.catalog) {
|
|
5084
|
+
if (!input.knownRepoIds.has(entry.repoId)) continue;
|
|
5085
|
+
const key = `${entry.kind}:${entry.name}`;
|
|
5086
|
+
catalogByName.set(key, [...catalogByName.get(key) ?? [], entry]);
|
|
5087
|
+
}
|
|
5088
|
+
for (const kind of ["skill", "agent"]) {
|
|
5089
|
+
const scanDir = path19.join(
|
|
5090
|
+
input.workspaceDir,
|
|
5091
|
+
".github",
|
|
5092
|
+
kind === "agent" ? "agents" : "skills"
|
|
5093
|
+
);
|
|
5094
|
+
const children = await fsp15.readdir(scanDir, { withFileTypes: true }).catch(() => []);
|
|
5095
|
+
for (const child of children) {
|
|
5096
|
+
const childPath = path19.join(scanDir, child.name);
|
|
5097
|
+
const name = entryNameFromLinkBasename(child.name, kind);
|
|
5098
|
+
const stat16 = await fsp15.lstat(childPath).catch(() => null);
|
|
5099
|
+
if (!stat16) continue;
|
|
5100
|
+
if (stat16.isSymbolicLink()) {
|
|
5101
|
+
const target = await fsp15.readlink(childPath);
|
|
5102
|
+
const resolved = path19.resolve(scanDir, target);
|
|
5103
|
+
const relative5 = path19.relative(input.reposDir, resolved);
|
|
5104
|
+
if (relative5.startsWith("..") || path19.isAbsolute(relative5)) {
|
|
5105
|
+
detection.unmatched.push({ name, kind, reason: "foreign-symlink" });
|
|
5106
|
+
continue;
|
|
5107
|
+
}
|
|
5108
|
+
const repoId = relative5.split(path19.sep)[0] ?? "";
|
|
5109
|
+
if (!repoId || !input.knownRepoIds.has(repoId)) {
|
|
5110
|
+
detection.unmatched.push({ name, kind, reason: "foreign-symlink" });
|
|
5111
|
+
continue;
|
|
5112
|
+
}
|
|
5113
|
+
detection.adoptions.push({ repoId, name, kind, source: "symlink" });
|
|
5114
|
+
continue;
|
|
5115
|
+
}
|
|
5116
|
+
const digest = stat16.isFile() ? await digestFile2(childPath) : await digestTree(childPath);
|
|
5117
|
+
const candidates = catalogByName.get(`${kind}:${name}`) ?? [];
|
|
5118
|
+
const matches = [];
|
|
5119
|
+
for (const candidate of candidates) {
|
|
5120
|
+
if (await digestCatalogEntry(candidate) === digest) {
|
|
5121
|
+
matches.push(candidate.repoId);
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
if (matches.length === 1) {
|
|
5125
|
+
detection.adoptions.push({
|
|
5126
|
+
repoId: matches[0] ?? "",
|
|
5127
|
+
name,
|
|
5128
|
+
kind,
|
|
5129
|
+
source: "fingerprint"
|
|
5130
|
+
});
|
|
5131
|
+
} else if (matches.length > 1) {
|
|
5132
|
+
detection.ambiguous.push({ name, kind, candidates: matches.sort() });
|
|
5133
|
+
} else {
|
|
5134
|
+
detection.unmatched.push({ name, kind, reason: "no-catalog-match" });
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
for (const [configFile, kind] of [
|
|
5139
|
+
["hooks.serviceme.json", "hook"],
|
|
5140
|
+
["mcp.serviceme.json", "mcp"]
|
|
5141
|
+
]) {
|
|
5142
|
+
const identities = await readServicemeIdentities(
|
|
5143
|
+
path19.join(input.workspaceDir, ".vscode", configFile)
|
|
5144
|
+
);
|
|
5145
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5146
|
+
for (const identity of identities.keys()) {
|
|
5147
|
+
const segments = identity.split("::");
|
|
5148
|
+
if (segments.length < 3) continue;
|
|
5149
|
+
const repoId = segments[0] ?? "";
|
|
5150
|
+
const pluginId = segments[1] ?? "";
|
|
5151
|
+
const kindPart = (segments[2] ?? "").split(":")[0] ?? "";
|
|
5152
|
+
if (!repoId || !pluginId || kindPart !== kind || !input.knownRepoIds.has(repoId)) {
|
|
5153
|
+
continue;
|
|
5154
|
+
}
|
|
5155
|
+
const dedupe = `${repoId}::${pluginId}::${kind}`;
|
|
5156
|
+
if (seen.has(dedupe)) continue;
|
|
5157
|
+
seen.add(dedupe);
|
|
5158
|
+
detection.integrations.push({ repoId, pluginId, kind });
|
|
5159
|
+
}
|
|
5160
|
+
}
|
|
5161
|
+
return detection;
|
|
5162
|
+
}
|
|
5163
|
+
|
|
5164
|
+
// src/copilot-content/workspace-copilot-content-reconciler.ts
|
|
5165
|
+
var fsp16 = __toESM(require("fs/promises"));
|
|
5166
|
+
var path20 = __toESM(require("path"));
|
|
4813
5167
|
var LINK_KINDS = /* @__PURE__ */ new Set(["agent", "skill", "instruction", "prompt"]);
|
|
4814
5168
|
var INTEGRATION_KINDS = /* @__PURE__ */ new Set(["mcp", "hook"]);
|
|
4815
5169
|
var WorkspaceCopilotContentReconciler = class {
|
|
@@ -4848,21 +5202,28 @@ var WorkspaceCopilotContentReconciler = class {
|
|
|
4848
5202
|
}
|
|
4849
5203
|
return { changed: false, entries };
|
|
4850
5204
|
}
|
|
4851
|
-
const reposDir =
|
|
5205
|
+
const reposDir = path20.join(this.homeDir, "repos");
|
|
4852
5206
|
const plan = await resolveWorkspaceContentPlan({ manifest, reposDir });
|
|
5207
|
+
const sharedDisabled = /* @__PURE__ */ new Set();
|
|
5208
|
+
for (const plugin of manifest.plugins) {
|
|
5209
|
+
for (const target of plugin.disabledArtifacts ?? []) {
|
|
5210
|
+
sharedDisabled.add(`${plugin.id}|${target}`);
|
|
5211
|
+
}
|
|
5212
|
+
}
|
|
4853
5213
|
let linkEntries = plan.entries.filter((entry) => LINK_KINDS.has(entry.kind));
|
|
4854
5214
|
let integrationEntries = plan.entries.filter((entry) => INTEGRATION_KINDS.has(entry.kind));
|
|
5215
|
+
const isPlanEntryDisabled = (entry) => sharedDisabled.has(`${entry.pluginId}|${entry.kind}:${entry.name}`) || this.isDisabled !== void 0 && disabledIdentities.has(entry.identity);
|
|
5216
|
+
const disabledIdentities = /* @__PURE__ */ new Set();
|
|
4855
5217
|
if (this.isDisabled !== void 0) {
|
|
4856
|
-
const disabled = /* @__PURE__ */ new Set();
|
|
4857
5218
|
for (const entry of plan.entries) {
|
|
4858
5219
|
if (await this.isDisabled(entry.identity)) {
|
|
4859
|
-
|
|
5220
|
+
disabledIdentities.add(entry.identity);
|
|
4860
5221
|
}
|
|
4861
5222
|
}
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
5223
|
+
}
|
|
5224
|
+
if (sharedDisabled.size > 0 || disabledIdentities.size > 0) {
|
|
5225
|
+
linkEntries = linkEntries.filter((entry) => !isPlanEntryDisabled(entry));
|
|
5226
|
+
integrationEntries = integrationEntries.filter((entry) => !isPlanEntryDisabled(entry));
|
|
4866
5227
|
}
|
|
4867
5228
|
const scopeOverlaps = previousState.entries.filter(
|
|
4868
5229
|
(entry) => LINK_KINDS.has(entry.kind) && plan.entries.some((planned) => planned.identity === entry.identity) && !isWorkspaceScopePath(entry.linkPath)
|
|
@@ -4878,22 +5239,25 @@ var WorkspaceCopilotContentReconciler = class {
|
|
|
4878
5239
|
});
|
|
4879
5240
|
return { changed: false, entries };
|
|
4880
5241
|
}
|
|
4881
|
-
const excludeStore = new WorkspaceExcludeStore({
|
|
5242
|
+
const excludeStore = new WorkspaceExcludeStore({
|
|
5243
|
+
workspaceDir: this.workspaceDir
|
|
5244
|
+
});
|
|
4882
5245
|
const linkMaterialized = await this.materializer.reconcile({
|
|
4883
5246
|
workspaceDir: this.workspaceDir,
|
|
4884
5247
|
entries: linkEntries,
|
|
4885
5248
|
previousState
|
|
4886
5249
|
});
|
|
5250
|
+
const conflictedIds = new Set(plan.conflicts.map((conflict) => conflict.identity));
|
|
4887
5251
|
const effectiveLinkIds = new Set(linkEntries.map((entry) => entry.identity));
|
|
4888
5252
|
let removedLinks = 0;
|
|
4889
5253
|
for (const previous of previousState.entries) {
|
|
4890
5254
|
if (!LINK_KINDS.has(previous.kind) || effectiveLinkIds.has(previous.identity)) {
|
|
4891
5255
|
continue;
|
|
4892
5256
|
}
|
|
4893
|
-
if (!isWorkspaceScopePath(previous.linkPath)) {
|
|
5257
|
+
if (conflictedIds.has(previous.identity) || !isWorkspaceScopePath(previous.linkPath)) {
|
|
4894
5258
|
continue;
|
|
4895
5259
|
}
|
|
4896
|
-
await
|
|
5260
|
+
await fsp16.rm(path20.resolve(this.workspaceDir, previous.linkPath), {
|
|
4897
5261
|
force: true,
|
|
4898
5262
|
recursive: true
|
|
4899
5263
|
});
|
|
@@ -4926,7 +5290,15 @@ var WorkspaceCopilotContentReconciler = class {
|
|
|
4926
5290
|
await excludeStore.reconcile(managedPaths);
|
|
4927
5291
|
return {
|
|
4928
5292
|
changed: linkMaterialized.changed || integrationMaterialized.changed || removedLinks > 0,
|
|
4929
|
-
entries: [
|
|
5293
|
+
entries: [
|
|
5294
|
+
...linkMaterialized.entries,
|
|
5295
|
+
...integrationMaterialized.entries,
|
|
5296
|
+
...plan.conflicts.map((conflict) => ({
|
|
5297
|
+
identity: conflict.identity,
|
|
5298
|
+
status: "conflict",
|
|
5299
|
+
message: conflict.message
|
|
5300
|
+
}))
|
|
5301
|
+
]
|
|
4930
5302
|
};
|
|
4931
5303
|
}
|
|
4932
5304
|
/**
|
|
@@ -4971,14 +5343,18 @@ var WorkspaceCopilotContentReconciler = class {
|
|
|
4971
5343
|
}
|
|
4972
5344
|
try {
|
|
4973
5345
|
if (entry.kind === "mcp") {
|
|
4974
|
-
const raw = await
|
|
5346
|
+
const raw = await fsp16.readFile(entry.sourcePath, "utf8");
|
|
4975
5347
|
for (const server of parseMcpJson(raw)) {
|
|
4976
|
-
await mcpAdapter.applyServer({
|
|
5348
|
+
await mcpAdapter.applyServer({
|
|
5349
|
+
workspaceDir: this.workspaceDir,
|
|
5350
|
+
entry,
|
|
5351
|
+
server
|
|
5352
|
+
});
|
|
4977
5353
|
changed = true;
|
|
4978
5354
|
}
|
|
4979
5355
|
} else {
|
|
4980
|
-
const hooksJsonPath = await findRepoHooksJson(
|
|
4981
|
-
const raw = await
|
|
5356
|
+
const hooksJsonPath = await findRepoHooksJson(path20.resolve(entry.sourcePath, "..", ".."), entry.name) ?? path20.join(entry.sourcePath, "hooks.json");
|
|
5357
|
+
const raw = await fsp16.readFile(hooksJsonPath, "utf8");
|
|
4982
5358
|
await hookAdapter.applyHook({
|
|
4983
5359
|
workspaceDir: this.workspaceDir,
|
|
4984
5360
|
entry,
|
|
@@ -5032,14 +5408,17 @@ var WorkspaceCopilotContentReconciler = class {
|
|
|
5032
5408
|
return this.reconcile();
|
|
5033
5409
|
}
|
|
5034
5410
|
async defaultEnsureRepository(manifest) {
|
|
5035
|
-
const reposDir =
|
|
5411
|
+
const reposDir = path20.join(this.homeDir, "repos");
|
|
5036
5412
|
const sources = /* @__PURE__ */ new Map();
|
|
5037
5413
|
for (const source of getWorkspaceManifestSources(manifest)) {
|
|
5038
|
-
const localPath =
|
|
5039
|
-
const
|
|
5414
|
+
const localPath = path20.resolve(reposDir, source.id);
|
|
5415
|
+
const stat16 = await fsp16.stat(localPath).catch(() => null);
|
|
5040
5416
|
sources.set(
|
|
5041
5417
|
source.id,
|
|
5042
|
-
|
|
5418
|
+
stat16?.isDirectory() ? { ready: true, localPath } : {
|
|
5419
|
+
ready: false,
|
|
5420
|
+
error: "Source not materialized under ~/.serviceme/repos"
|
|
5421
|
+
}
|
|
5043
5422
|
);
|
|
5044
5423
|
}
|
|
5045
5424
|
return sources;
|
|
@@ -5088,7 +5467,7 @@ function declaredPinnedVersion(manifest, sourceId) {
|
|
|
5088
5467
|
}
|
|
5089
5468
|
|
|
5090
5469
|
// src/device/deviceAuth.ts
|
|
5091
|
-
var
|
|
5470
|
+
var import_node_crypto4 = require("crypto");
|
|
5092
5471
|
var DeviceAuthHeaders = {
|
|
5093
5472
|
deviceId: "x-ms-device-id",
|
|
5094
5473
|
deviceSecret: "x-ms-device-secret",
|
|
@@ -5104,7 +5483,7 @@ function createDeviceRequestSignature(params) {
|
|
|
5104
5483
|
params.body,
|
|
5105
5484
|
params.secret
|
|
5106
5485
|
].join("\n");
|
|
5107
|
-
return (0,
|
|
5486
|
+
return (0, import_node_crypto4.createHash)("sha256").update(basis).digest("hex");
|
|
5108
5487
|
}
|
|
5109
5488
|
function buildSignedHeaders(params) {
|
|
5110
5489
|
const timestamp = params.timestamp ?? Date.now();
|
|
@@ -5125,10 +5504,10 @@ function buildSignedHeaders(params) {
|
|
|
5125
5504
|
}
|
|
5126
5505
|
|
|
5127
5506
|
// src/device/Enroller.ts
|
|
5128
|
-
var
|
|
5507
|
+
var import_node_crypto6 = require("crypto");
|
|
5129
5508
|
|
|
5130
5509
|
// src/device/InstallationId.ts
|
|
5131
|
-
var
|
|
5510
|
+
var import_node_crypto5 = require("crypto");
|
|
5132
5511
|
var os3 = __toESM(require("os"));
|
|
5133
5512
|
function fingerprintMaterial() {
|
|
5134
5513
|
let username = "unknown";
|
|
@@ -5147,11 +5526,11 @@ function fingerprintMaterial() {
|
|
|
5147
5526
|
}
|
|
5148
5527
|
function deriveInstallationId() {
|
|
5149
5528
|
const material = fingerprintMaterial();
|
|
5150
|
-
const digest = (0,
|
|
5529
|
+
const digest = (0, import_node_crypto5.createHash)("sha256").update(material).digest("hex");
|
|
5151
5530
|
return formatAsV4(digest.slice(0, 32));
|
|
5152
5531
|
}
|
|
5153
5532
|
function randomInstallationId() {
|
|
5154
|
-
return (0,
|
|
5533
|
+
return (0, import_node_crypto5.randomUUID)();
|
|
5155
5534
|
}
|
|
5156
5535
|
function fingerprintSource() {
|
|
5157
5536
|
return fingerprintMaterial();
|
|
@@ -5172,7 +5551,7 @@ function formatAsV4(hex32) {
|
|
|
5172
5551
|
var SECRET_BYTES = 32;
|
|
5173
5552
|
var PUBLIC_ID_BYTES = 16;
|
|
5174
5553
|
var defaultRandomBytes = (size) => {
|
|
5175
|
-
return (0,
|
|
5554
|
+
return (0, import_node_crypto6.randomBytes)(size);
|
|
5176
5555
|
};
|
|
5177
5556
|
var DeviceReenrollRequiresAuthError = class extends Error {
|
|
5178
5557
|
constructor(message = "Re-enroll on a claimed device requires current device credentials or the bound user") {
|
|
@@ -5382,9 +5761,9 @@ async function emptyIdentity(random) {
|
|
|
5382
5761
|
}
|
|
5383
5762
|
|
|
5384
5763
|
// src/device/IdentityStore.ts
|
|
5385
|
-
var
|
|
5764
|
+
var fsp17 = __toESM(require("fs/promises"));
|
|
5386
5765
|
var os4 = __toESM(require("os"));
|
|
5387
|
-
var
|
|
5766
|
+
var path21 = __toESM(require("path"));
|
|
5388
5767
|
var import_promises = require("timers/promises");
|
|
5389
5768
|
|
|
5390
5769
|
// src/device/types.ts
|
|
@@ -5400,7 +5779,7 @@ var TMP_SUFFIX = ".tmp";
|
|
|
5400
5779
|
var FsIdentityFileBackend = class {
|
|
5401
5780
|
async exists(filePath) {
|
|
5402
5781
|
try {
|
|
5403
|
-
await
|
|
5782
|
+
await fsp17.access(filePath);
|
|
5404
5783
|
return true;
|
|
5405
5784
|
} catch {
|
|
5406
5785
|
return false;
|
|
@@ -5408,7 +5787,7 @@ var FsIdentityFileBackend = class {
|
|
|
5408
5787
|
}
|
|
5409
5788
|
async read(filePath) {
|
|
5410
5789
|
try {
|
|
5411
|
-
const buf = await
|
|
5790
|
+
const buf = await fsp17.readFile(filePath, "utf8");
|
|
5412
5791
|
const parsed = JSON.parse(buf);
|
|
5413
5792
|
return migratePersistedIdentity(parsed);
|
|
5414
5793
|
} catch (err) {
|
|
@@ -5417,23 +5796,23 @@ var FsIdentityFileBackend = class {
|
|
|
5417
5796
|
}
|
|
5418
5797
|
}
|
|
5419
5798
|
async write(filePath, payload) {
|
|
5420
|
-
await
|
|
5799
|
+
await fsp17.mkdir(path21.dirname(filePath), { recursive: true });
|
|
5421
5800
|
const tmpPath = `${filePath}${TMP_SUFFIX}`;
|
|
5422
5801
|
const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
|
|
5423
|
-
await
|
|
5424
|
-
const handle = await
|
|
5802
|
+
await fsp17.rm(tmpPath, { force: true });
|
|
5803
|
+
const handle = await fsp17.open(tmpPath, "w", FILE_MODE);
|
|
5425
5804
|
try {
|
|
5426
5805
|
await handle.writeFile(bytes);
|
|
5427
5806
|
await handle.sync();
|
|
5428
5807
|
} finally {
|
|
5429
5808
|
await handle.close();
|
|
5430
5809
|
}
|
|
5431
|
-
await
|
|
5432
|
-
await
|
|
5810
|
+
await fsp17.rename(tmpPath, filePath);
|
|
5811
|
+
await fsp17.chmod(filePath, FILE_MODE).catch(() => void 0);
|
|
5433
5812
|
return { bytesWritten: bytes.byteLength, tmpPath };
|
|
5434
5813
|
}
|
|
5435
5814
|
async delete(filePath) {
|
|
5436
|
-
await
|
|
5815
|
+
await fsp17.rm(filePath, { force: true });
|
|
5437
5816
|
}
|
|
5438
5817
|
};
|
|
5439
5818
|
function migratePersistedIdentity(parsed) {
|
|
@@ -5471,7 +5850,7 @@ var FileLock = class {
|
|
|
5471
5850
|
constructor(filePath, timeoutMs, retryMs) {
|
|
5472
5851
|
this.acquired = false;
|
|
5473
5852
|
this.dirPath = `${filePath}.lock`;
|
|
5474
|
-
this.pidFilePath =
|
|
5853
|
+
this.pidFilePath = path21.join(this.dirPath, LOCK_PID_FILE);
|
|
5475
5854
|
this.timeoutMs = timeoutMs;
|
|
5476
5855
|
this.retryMs = retryMs;
|
|
5477
5856
|
}
|
|
@@ -5479,8 +5858,8 @@ var FileLock = class {
|
|
|
5479
5858
|
const start = Date.now();
|
|
5480
5859
|
while (true) {
|
|
5481
5860
|
try {
|
|
5482
|
-
await
|
|
5483
|
-
await
|
|
5861
|
+
await fsp17.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });
|
|
5862
|
+
await fsp17.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
5484
5863
|
this.acquired = true;
|
|
5485
5864
|
return;
|
|
5486
5865
|
} catch (err) {
|
|
@@ -5489,7 +5868,7 @@ var FileLock = class {
|
|
|
5489
5868
|
}
|
|
5490
5869
|
const stale = await this.isStaleLock();
|
|
5491
5870
|
if (stale) {
|
|
5492
|
-
await
|
|
5871
|
+
await fsp17.rm(this.dirPath, { recursive: true, force: true });
|
|
5493
5872
|
continue;
|
|
5494
5873
|
}
|
|
5495
5874
|
if (Date.now() - start >= this.timeoutMs) {
|
|
@@ -5502,11 +5881,11 @@ var FileLock = class {
|
|
|
5502
5881
|
async isStaleLock() {
|
|
5503
5882
|
let pidStr;
|
|
5504
5883
|
try {
|
|
5505
|
-
pidStr = await
|
|
5884
|
+
pidStr = await fsp17.readFile(this.pidFilePath, "utf8");
|
|
5506
5885
|
} catch {
|
|
5507
5886
|
try {
|
|
5508
|
-
const
|
|
5509
|
-
return Date.now() -
|
|
5887
|
+
const stat16 = await fsp17.stat(this.dirPath);
|
|
5888
|
+
return Date.now() - stat16.mtimeMs > LOCK_STALE_GRACE_MS;
|
|
5510
5889
|
} catch {
|
|
5511
5890
|
return false;
|
|
5512
5891
|
}
|
|
@@ -5518,7 +5897,7 @@ var FileLock = class {
|
|
|
5518
5897
|
async release() {
|
|
5519
5898
|
if (!this.acquired) return;
|
|
5520
5899
|
this.acquired = false;
|
|
5521
|
-
await
|
|
5900
|
+
await fsp17.rm(this.dirPath, { recursive: true, force: true });
|
|
5522
5901
|
}
|
|
5523
5902
|
};
|
|
5524
5903
|
var IdentityStore = class {
|
|
@@ -5608,8 +5987,8 @@ var IdentityStore = class {
|
|
|
5608
5987
|
* Useful when the bootstrap phase5 placeholder wasn't run yet.
|
|
5609
5988
|
*/
|
|
5610
5989
|
async ensureHome() {
|
|
5611
|
-
await
|
|
5612
|
-
await
|
|
5990
|
+
await fsp17.mkdir(getServicemeHome(), { recursive: true });
|
|
5991
|
+
await fsp17.mkdir(path21.dirname(this.filePath), { recursive: true });
|
|
5613
5992
|
}
|
|
5614
5993
|
};
|
|
5615
5994
|
|
|
@@ -5721,15 +6100,15 @@ function projectMetadata(stored) {
|
|
|
5721
6100
|
// src/drafts/index.ts
|
|
5722
6101
|
var crypto = __toESM(require("crypto"));
|
|
5723
6102
|
var fs6 = __toESM(require("fs/promises"));
|
|
5724
|
-
var
|
|
6103
|
+
var path24 = __toESM(require("path"));
|
|
5725
6104
|
|
|
5726
6105
|
// src/skill-store/index.ts
|
|
5727
6106
|
var fs5 = __toESM(require("fs/promises"));
|
|
5728
|
-
var
|
|
6107
|
+
var path23 = __toESM(require("path"));
|
|
5729
6108
|
|
|
5730
6109
|
// src/repo-layout/index.ts
|
|
5731
6110
|
var fs4 = __toESM(require("fs/promises"));
|
|
5732
|
-
var
|
|
6111
|
+
var path22 = __toESM(require("path"));
|
|
5733
6112
|
|
|
5734
6113
|
// src/skill-store/index.ts
|
|
5735
6114
|
function extractFrontmatter(raw) {
|
|
@@ -5806,7 +6185,7 @@ function generateDraftId() {
|
|
|
5806
6185
|
}
|
|
5807
6186
|
function resolveDraftDir(kind, id) {
|
|
5808
6187
|
const root = kind === "skill" ? getSkillDraftsDir() : getAgentDraftsDir();
|
|
5809
|
-
return
|
|
6188
|
+
return path24.join(root, id);
|
|
5810
6189
|
}
|
|
5811
6190
|
var DraftsStore = class {
|
|
5812
6191
|
/**
|
|
@@ -5834,10 +6213,10 @@ var DraftsStore = class {
|
|
|
5834
6213
|
async tryReadSummary(kind, id) {
|
|
5835
6214
|
const dir = resolveDraftDir(kind, id);
|
|
5836
6215
|
const manifestFilename = kind === "skill" ? "SKILL.md" : "AGENT.md";
|
|
5837
|
-
const manifestPath =
|
|
5838
|
-
let
|
|
6216
|
+
const manifestPath = path24.join(dir, manifestFilename);
|
|
6217
|
+
let stat16;
|
|
5839
6218
|
try {
|
|
5840
|
-
|
|
6219
|
+
stat16 = await fs6.stat(manifestPath);
|
|
5841
6220
|
} catch {
|
|
5842
6221
|
return null;
|
|
5843
6222
|
}
|
|
@@ -5856,7 +6235,7 @@ var DraftsStore = class {
|
|
|
5856
6235
|
dir,
|
|
5857
6236
|
name,
|
|
5858
6237
|
description,
|
|
5859
|
-
modifiedAt:
|
|
6238
|
+
modifiedAt: stat16.mtime.toISOString()
|
|
5860
6239
|
};
|
|
5861
6240
|
}
|
|
5862
6241
|
/** Single draft detail (summary + all files). Throws when missing. */
|
|
@@ -5895,8 +6274,8 @@ var DraftsStore = class {
|
|
|
5895
6274
|
await fs6.rm(dir, { recursive: true, force: true });
|
|
5896
6275
|
await fs6.mkdir(dir, { recursive: true });
|
|
5897
6276
|
for (const f of opts.files) {
|
|
5898
|
-
const full =
|
|
5899
|
-
await fs6.mkdir(
|
|
6277
|
+
const full = path24.join(dir, f.path);
|
|
6278
|
+
await fs6.mkdir(path24.dirname(full), { recursive: true });
|
|
5900
6279
|
const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
|
|
5901
6280
|
await fs6.writeFile(tmp, f.content, "utf8");
|
|
5902
6281
|
await fs6.rename(tmp, full);
|
|
@@ -5924,7 +6303,7 @@ var DraftsStore = class {
|
|
|
5924
6303
|
}
|
|
5925
6304
|
for (const n of names) {
|
|
5926
6305
|
if (n.endsWith(".tmp")) {
|
|
5927
|
-
await fs6.rm(
|
|
6306
|
+
await fs6.rm(path24.join(dir, n), { force: true }).catch(() => void 0);
|
|
5928
6307
|
}
|
|
5929
6308
|
}
|
|
5930
6309
|
}
|
|
@@ -5938,12 +6317,12 @@ async function collectRecursive(absDir, root, out) {
|
|
|
5938
6317
|
}
|
|
5939
6318
|
for (const d of dirents) {
|
|
5940
6319
|
if (d.name.endsWith(".tmp")) continue;
|
|
5941
|
-
const full =
|
|
6320
|
+
const full = path24.join(absDir, d.name);
|
|
5942
6321
|
if (d.isDirectory()) {
|
|
5943
6322
|
await collectRecursive(full, root, out);
|
|
5944
6323
|
} else if (d.isFile()) {
|
|
5945
6324
|
const content = await fs6.readFile(full, "utf8");
|
|
5946
|
-
out.push({ path:
|
|
6325
|
+
out.push({ path: path24.relative(root, full), content });
|
|
5947
6326
|
}
|
|
5948
6327
|
}
|
|
5949
6328
|
}
|
|
@@ -6287,7 +6666,7 @@ var EnvironmentInspector = class {
|
|
|
6287
6666
|
|
|
6288
6667
|
// src/git-client/index.ts
|
|
6289
6668
|
var import_node_child_process3 = require("child_process");
|
|
6290
|
-
var
|
|
6669
|
+
var path25 = __toESM(require("path"));
|
|
6291
6670
|
|
|
6292
6671
|
// src/git-client/types.ts
|
|
6293
6672
|
var GitError = class extends Error {
|
|
@@ -6481,8 +6860,8 @@ var GitClient = class {
|
|
|
6481
6860
|
throw new GitError(["rev-parse", "--git-path", pathspec], result);
|
|
6482
6861
|
}
|
|
6483
6862
|
const resolved = result.stdout.trim();
|
|
6484
|
-
if (!
|
|
6485
|
-
return
|
|
6863
|
+
if (!path25.isAbsolute(resolved)) {
|
|
6864
|
+
return path25.resolve(localPath, resolved);
|
|
6486
6865
|
}
|
|
6487
6866
|
return resolved;
|
|
6488
6867
|
}
|
|
@@ -6544,7 +6923,7 @@ var GitClient = class {
|
|
|
6544
6923
|
};
|
|
6545
6924
|
var NodeGitSpawner = class {
|
|
6546
6925
|
async spawn(args, opts) {
|
|
6547
|
-
return new Promise((
|
|
6926
|
+
return new Promise((resolve13, reject) => {
|
|
6548
6927
|
const child = (0, import_node_child_process3.spawn)("git", args, {
|
|
6549
6928
|
cwd: opts.cwd,
|
|
6550
6929
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6556,7 +6935,7 @@ var NodeGitSpawner = class {
|
|
|
6556
6935
|
child.stderr?.on("data", (c) => stderrChunks.push(c));
|
|
6557
6936
|
child.on("error", reject);
|
|
6558
6937
|
child.on("close", (code) => {
|
|
6559
|
-
|
|
6938
|
+
resolve13({
|
|
6560
6939
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
6561
6940
|
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
6562
6941
|
code: code ?? 1
|
|
@@ -6585,7 +6964,7 @@ var StubGitSpawner = class {
|
|
|
6585
6964
|
}
|
|
6586
6965
|
};
|
|
6587
6966
|
function toFileUrl(absolutePath) {
|
|
6588
|
-
const normalized =
|
|
6967
|
+
const normalized = path25.resolve(absolutePath);
|
|
6589
6968
|
if (process.platform === "win32") {
|
|
6590
6969
|
return `file:///${normalized.replace(/\\/g, "/")}`;
|
|
6591
6970
|
}
|
|
@@ -6598,7 +6977,7 @@ function buildGitProxyBase(serverBaseUrl) {
|
|
|
6598
6977
|
|
|
6599
6978
|
// src/image/imageTools.ts
|
|
6600
6979
|
var fs7 = __toESM(require("fs/promises"));
|
|
6601
|
-
var
|
|
6980
|
+
var path26 = __toESM(require("path"));
|
|
6602
6981
|
var import_devtools_protocol5 = require("@serviceme/devtools-protocol");
|
|
6603
6982
|
var SUPPORTED_FORMATS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tiff"];
|
|
6604
6983
|
var DEFAULT_MINIMUM_COMPRESSION_RATIO = 5;
|
|
@@ -6611,7 +6990,7 @@ var ImageTools = class {
|
|
|
6611
6990
|
if (!await this.pathExists(filePath)) {
|
|
6612
6991
|
return { valid: false };
|
|
6613
6992
|
}
|
|
6614
|
-
if (!SUPPORTED_FORMATS.includes(
|
|
6993
|
+
if (!SUPPORTED_FORMATS.includes(path26.extname(filePath).toLowerCase())) {
|
|
6615
6994
|
return { valid: false };
|
|
6616
6995
|
}
|
|
6617
6996
|
await this.getInfo(filePath, sharpModulePath);
|
|
@@ -6663,7 +7042,7 @@ var ImageTools = class {
|
|
|
6663
7042
|
async compressWithSharp(imagePath, options) {
|
|
6664
7043
|
const sharp = this.loadSharp(options.sharpModulePath);
|
|
6665
7044
|
let pipeline = sharp(imagePath);
|
|
6666
|
-
switch (options.format ??
|
|
7045
|
+
switch (options.format ?? path26.extname(imagePath).toLowerCase().slice(1)) {
|
|
6667
7046
|
case "jpg":
|
|
6668
7047
|
case "jpeg":
|
|
6669
7048
|
pipeline = pipeline.jpeg({ quality: options.quality });
|
|
@@ -6689,10 +7068,18 @@ var ImageTools = class {
|
|
|
6689
7068
|
if (options.replaceOriginImage) {
|
|
6690
7069
|
return inputPath;
|
|
6691
7070
|
}
|
|
6692
|
-
const dir =
|
|
6693
|
-
const ext =
|
|
6694
|
-
const name =
|
|
6695
|
-
|
|
7071
|
+
const dir = path26.dirname(inputPath);
|
|
7072
|
+
const ext = path26.extname(inputPath);
|
|
7073
|
+
const name = path26.basename(inputPath, ext);
|
|
7074
|
+
const outputPath = path26.join(dir, `${name}_compressed${ext}`);
|
|
7075
|
+
const resolvedDir = path26.resolve(dir);
|
|
7076
|
+
if (!path26.resolve(outputPath).startsWith(resolvedDir + path26.sep)) {
|
|
7077
|
+
throw (0, import_devtools_protocol5.createServicemeError)(
|
|
7078
|
+
"invalid_params",
|
|
7079
|
+
"Derived output path escapes the image directory."
|
|
7080
|
+
);
|
|
7081
|
+
}
|
|
7082
|
+
return outputPath;
|
|
6696
7083
|
}
|
|
6697
7084
|
async pathExists(targetPath) {
|
|
6698
7085
|
try {
|
|
@@ -6800,10 +7187,10 @@ function detectIndent(text) {
|
|
|
6800
7187
|
}
|
|
6801
7188
|
|
|
6802
7189
|
// src/paths/serverProxyGlobal.ts
|
|
6803
|
-
var
|
|
7190
|
+
var import_node_crypto7 = require("crypto");
|
|
6804
7191
|
var fs8 = __toESM(require("fs/promises"));
|
|
6805
7192
|
var import_promises2 = require("fs/promises");
|
|
6806
|
-
var
|
|
7193
|
+
var path27 = __toESM(require("path"));
|
|
6807
7194
|
async function readServerProxyGlobal() {
|
|
6808
7195
|
const filePath = getServerProxyGlobalPath();
|
|
6809
7196
|
try {
|
|
@@ -6822,7 +7209,7 @@ async function readServerProxyGlobal() {
|
|
|
6822
7209
|
}
|
|
6823
7210
|
async function writeServerProxyGlobal(patch) {
|
|
6824
7211
|
const filePath = getServerProxyGlobalPath();
|
|
6825
|
-
const dirPath =
|
|
7212
|
+
const dirPath = path27.dirname(filePath);
|
|
6826
7213
|
await fs8.mkdir(dirPath, { recursive: true });
|
|
6827
7214
|
const current = await readServerProxyGlobal() ?? {
|
|
6828
7215
|
enabled: false,
|
|
@@ -6836,7 +7223,7 @@ async function writeServerProxyGlobal(patch) {
|
|
|
6836
7223
|
lastServerUrl: patch.lastServerUrl === void 0 ? current.lastServerUrl : patch.lastServerUrl === null ? void 0 : patch.lastServerUrl,
|
|
6837
7224
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6838
7225
|
};
|
|
6839
|
-
const tmpPath = `${filePath}.tmp-${(0,
|
|
7226
|
+
const tmpPath = `${filePath}.tmp-${(0, import_node_crypto7.randomUUID)()}`;
|
|
6840
7227
|
const fh = await (0, import_promises2.open)(tmpPath, "w");
|
|
6841
7228
|
try {
|
|
6842
7229
|
await fh.writeFile(JSON.stringify(next, null, " "), "utf8");
|
|
@@ -6875,9 +7262,9 @@ function isENOENT(err) {
|
|
|
6875
7262
|
}
|
|
6876
7263
|
|
|
6877
7264
|
// src/phase5/bootstrap.ts
|
|
6878
|
-
var
|
|
7265
|
+
var import_node_crypto8 = require("crypto");
|
|
6879
7266
|
var fs9 = __toESM(require("fs/promises"));
|
|
6880
|
-
var
|
|
7267
|
+
var path28 = __toESM(require("path"));
|
|
6881
7268
|
function getPhase5FileSpecs() {
|
|
6882
7269
|
return [
|
|
6883
7270
|
{
|
|
@@ -6908,7 +7295,7 @@ function getPhase5FileSpecs() {
|
|
|
6908
7295
|
path: getMachineIdPath(),
|
|
6909
7296
|
// Random uuid, written as a bare string. Subsequent
|
|
6910
7297
|
// activations see the file and skip re-randomizing.
|
|
6911
|
-
defaultContent: (0,
|
|
7298
|
+
defaultContent: (0, import_node_crypto8.randomUUID)()
|
|
6912
7299
|
},
|
|
6913
7300
|
{
|
|
6914
7301
|
path: getProfilesJsonPath(),
|
|
@@ -6933,7 +7320,7 @@ async function bootstrapPhase5Placeholders() {
|
|
|
6933
7320
|
result.skipped.push(spec.path);
|
|
6934
7321
|
} catch {
|
|
6935
7322
|
try {
|
|
6936
|
-
await fs9.mkdir(
|
|
7323
|
+
await fs9.mkdir(path28.dirname(spec.path), { recursive: true });
|
|
6937
7324
|
await fs9.writeFile(spec.path, spec.defaultContent, "utf8");
|
|
6938
7325
|
result.created.push(spec.path);
|
|
6939
7326
|
} catch (writeErr) {
|
|
@@ -6946,7 +7333,7 @@ async function bootstrapPhase5Placeholders() {
|
|
|
6946
7333
|
|
|
6947
7334
|
// src/project/projectTools.ts
|
|
6948
7335
|
var fs10 = __toESM(require("fs/promises"));
|
|
6949
|
-
var
|
|
7336
|
+
var path29 = __toESM(require("path"));
|
|
6950
7337
|
var import_devtools_protocol7 = require("@serviceme/devtools-protocol");
|
|
6951
7338
|
|
|
6952
7339
|
// src/utils/fileUtils.ts
|
|
@@ -6955,7 +7342,7 @@ var import_promises3 = require("fs/promises");
|
|
|
6955
7342
|
var import_node_path2 = require("path");
|
|
6956
7343
|
var import_yauzl = __toESM(require("yauzl"));
|
|
6957
7344
|
var unzipFile = (zipPath, dest) => {
|
|
6958
|
-
return new Promise((
|
|
7345
|
+
return new Promise((resolve13, reject) => {
|
|
6959
7346
|
import_yauzl.default.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
6960
7347
|
if (err) return reject(err);
|
|
6961
7348
|
if (!zipfile) return reject(new Error("Failed to open zip file."));
|
|
@@ -6986,7 +7373,7 @@ var unzipFile = (zipPath, dest) => {
|
|
|
6986
7373
|
}
|
|
6987
7374
|
});
|
|
6988
7375
|
zipfile.on("end", () => {
|
|
6989
|
-
|
|
7376
|
+
resolve13();
|
|
6990
7377
|
});
|
|
6991
7378
|
zipfile.on("error", (zipError) => {
|
|
6992
7379
|
reject(zipError);
|
|
@@ -7056,7 +7443,7 @@ var moveFiles = async (sourceDir, destDir, overwrite = false) => {
|
|
|
7056
7443
|
var ProjectTools = class {
|
|
7057
7444
|
async extractTemplate(zipPath, workspacePath, tempExtractDir, input) {
|
|
7058
7445
|
await unzipFile(zipPath, tempExtractDir);
|
|
7059
|
-
let sourceDir =
|
|
7446
|
+
let sourceDir = path29.join(tempExtractDir, input.extractedDirName);
|
|
7060
7447
|
let actualDirName = input.extractedDirName;
|
|
7061
7448
|
if (!await this.pathExists(sourceDir)) {
|
|
7062
7449
|
const entries = await fs10.readdir(tempExtractDir, { withFileTypes: true });
|
|
@@ -7071,7 +7458,7 @@ var ProjectTools = class {
|
|
|
7071
7458
|
);
|
|
7072
7459
|
if (selectedDirectory) {
|
|
7073
7460
|
actualDirName = selectedDirectory;
|
|
7074
|
-
sourceDir =
|
|
7461
|
+
sourceDir = path29.join(tempExtractDir, actualDirName);
|
|
7075
7462
|
} else if (directories.length === 0) {
|
|
7076
7463
|
throw new Error(
|
|
7077
7464
|
`No directory found after extraction. Expected directory: ${input.extractedDirName}`
|
|
@@ -7094,6 +7481,13 @@ var ProjectTools = class {
|
|
|
7094
7481
|
if (!command) {
|
|
7095
7482
|
throw (0, import_devtools_protocol7.createServicemeError)("invalid_params", "Expected install command.");
|
|
7096
7483
|
}
|
|
7484
|
+
const hasControlCharacter = [...command].some((character) => {
|
|
7485
|
+
const code = character.charCodeAt(0);
|
|
7486
|
+
return code > 0 && code < 32 && character !== " ";
|
|
7487
|
+
});
|
|
7488
|
+
if (hasControlCharacter) {
|
|
7489
|
+
throw (0, import_devtools_protocol7.createServicemeError)("invalid_params", "Install command contains control characters.");
|
|
7490
|
+
}
|
|
7097
7491
|
await runCommand(command, {
|
|
7098
7492
|
cwd: workspacePath,
|
|
7099
7493
|
shell: true
|
|
@@ -7169,7 +7563,7 @@ var ProjectTools = class {
|
|
|
7169
7563
|
};
|
|
7170
7564
|
}
|
|
7171
7565
|
async ensurePresetManifest(workspacePath, preset) {
|
|
7172
|
-
const presetManifestPath =
|
|
7566
|
+
const presetManifestPath = path29.join(
|
|
7173
7567
|
workspacePath,
|
|
7174
7568
|
".ms-scaffold",
|
|
7175
7569
|
"presets",
|
|
@@ -7178,7 +7572,7 @@ var ProjectTools = class {
|
|
|
7178
7572
|
if (await this.pathExists(presetManifestPath)) {
|
|
7179
7573
|
return;
|
|
7180
7574
|
}
|
|
7181
|
-
const projectModePath =
|
|
7575
|
+
const projectModePath = path29.join(workspacePath, ".ms-scaffold", "project-mode.json");
|
|
7182
7576
|
if (!await this.pathExists(projectModePath)) {
|
|
7183
7577
|
return;
|
|
7184
7578
|
}
|
|
@@ -7194,7 +7588,7 @@ var ProjectTools = class {
|
|
|
7194
7588
|
mergeManagedFiles: [],
|
|
7195
7589
|
userOwnedPaths: []
|
|
7196
7590
|
};
|
|
7197
|
-
await fs10.mkdir(
|
|
7591
|
+
await fs10.mkdir(path29.dirname(presetManifestPath), { recursive: true });
|
|
7198
7592
|
await fs10.writeFile(
|
|
7199
7593
|
presetManifestPath,
|
|
7200
7594
|
`${JSON.stringify(synthesizedPreset, null, 2)}
|
|
@@ -7211,7 +7605,7 @@ var ProjectTools = class {
|
|
|
7211
7605
|
return results;
|
|
7212
7606
|
}
|
|
7213
7607
|
for (const entry of entries) {
|
|
7214
|
-
const fullPath =
|
|
7608
|
+
const fullPath = path29.join(dir, entry.name);
|
|
7215
7609
|
if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
7216
7610
|
results.push(...await this.findScripts(fullPath, extensions));
|
|
7217
7611
|
} else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
@@ -7239,7 +7633,7 @@ var ProjectTools = class {
|
|
|
7239
7633
|
const matches = [];
|
|
7240
7634
|
for (const directoryName of directoryNames) {
|
|
7241
7635
|
if (await this.directoryMatchesProjectPattern(
|
|
7242
|
-
|
|
7636
|
+
path29.join(tempExtractDir, directoryName),
|
|
7243
7637
|
projectFilePattern
|
|
7244
7638
|
)) {
|
|
7245
7639
|
matches.push(directoryName);
|
|
@@ -7265,7 +7659,7 @@ function createProjectTools() {
|
|
|
7265
7659
|
|
|
7266
7660
|
// src/repo-manager/index.ts
|
|
7267
7661
|
var fs11 = __toESM(require("fs/promises"));
|
|
7268
|
-
var
|
|
7662
|
+
var path30 = __toESM(require("path"));
|
|
7269
7663
|
|
|
7270
7664
|
// src/repos/types.ts
|
|
7271
7665
|
function isDefaultRepo(repo) {
|
|
@@ -7357,7 +7751,7 @@ var RepoManager = class {
|
|
|
7357
7751
|
}
|
|
7358
7752
|
try {
|
|
7359
7753
|
if (!this.skipClone) {
|
|
7360
|
-
await fs11.mkdir(
|
|
7754
|
+
await fs11.mkdir(path30.dirname(localPath), { recursive: true });
|
|
7361
7755
|
await this.git.clone(repo.id, repo.url, localPath, repo.branch, true);
|
|
7362
7756
|
}
|
|
7363
7757
|
await this.store.updateRepo(repo.id, {
|
|
@@ -7399,7 +7793,7 @@ var RepoManager = class {
|
|
|
7399
7793
|
await fs11.rm(localPath, { recursive: true, force: true });
|
|
7400
7794
|
}
|
|
7401
7795
|
if (!exists || !await this.pathExists(localPath)) {
|
|
7402
|
-
await fs11.mkdir(
|
|
7796
|
+
await fs11.mkdir(path30.dirname(localPath), { recursive: true });
|
|
7403
7797
|
if (!this.skipClone) {
|
|
7404
7798
|
await this.git.clone(proxyId, repo.url, localPath, repo.branch, useProxy);
|
|
7405
7799
|
}
|
|
@@ -7466,7 +7860,7 @@ var RepoManager = class {
|
|
|
7466
7860
|
await fs11.rm(localPath, { recursive: true, force: true });
|
|
7467
7861
|
}
|
|
7468
7862
|
if (!exists || !await this.pathExists(localPath)) {
|
|
7469
|
-
await fs11.mkdir(
|
|
7863
|
+
await fs11.mkdir(path30.dirname(localPath), { recursive: true });
|
|
7470
7864
|
if (!this.skipClone) {
|
|
7471
7865
|
await this.git.clone(
|
|
7472
7866
|
input.repository.useProxy ?? true ? input.repository.id : input.repository.id,
|
|
@@ -7613,7 +8007,7 @@ var RepoManager = class {
|
|
|
7613
8007
|
let cloned = false;
|
|
7614
8008
|
if (!this.skipClone) {
|
|
7615
8009
|
const localPath = getRepoDir(uniqueId);
|
|
7616
|
-
await fs11.mkdir(
|
|
8010
|
+
await fs11.mkdir(path30.dirname(localPath), { recursive: true });
|
|
7617
8011
|
await this.git.clone(userProxyId, url, localPath, branch, useProxy);
|
|
7618
8012
|
cloned = true;
|
|
7619
8013
|
}
|
|
@@ -7669,7 +8063,7 @@ var RepoManager = class {
|
|
|
7669
8063
|
* (e.g. from an interrupted clone) return `false`.
|
|
7670
8064
|
*/
|
|
7671
8065
|
async isValidGitRepo(p) {
|
|
7672
|
-
return this.pathExists(
|
|
8066
|
+
return this.pathExists(path30.join(p, ".git"));
|
|
7673
8067
|
}
|
|
7674
8068
|
async pathExists(p) {
|
|
7675
8069
|
try {
|
|
@@ -7836,7 +8230,7 @@ function resolveDefaultRepoId(existing, existingIds) {
|
|
|
7836
8230
|
|
|
7837
8231
|
// src/repos/loader.ts
|
|
7838
8232
|
var fs12 = __toESM(require("fs/promises"));
|
|
7839
|
-
var
|
|
8233
|
+
var path31 = __toESM(require("path"));
|
|
7840
8234
|
var import_zod2 = require("zod");
|
|
7841
8235
|
var ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})$/;
|
|
7842
8236
|
var repoIdSchema = import_zod2.z.string().min(1).max(64).regex(SAFE_REPO_ID_PATTERN, "repo id must match /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/");
|
|
@@ -7972,7 +8366,7 @@ var ReposLoader = class {
|
|
|
7972
8366
|
*/
|
|
7973
8367
|
async save(config) {
|
|
7974
8368
|
const validated = reposFileSchema.parse(config);
|
|
7975
|
-
const dir =
|
|
8369
|
+
const dir = path31.dirname(this.configPath);
|
|
7976
8370
|
await this.fileSystem.mkdir(dir, { recursive: true });
|
|
7977
8371
|
const serialized = `${JSON.stringify(validated, null, 2)}
|
|
7978
8372
|
`;
|
|
@@ -8333,14 +8727,14 @@ async function bootstrapDefaults(store) {
|
|
|
8333
8727
|
|
|
8334
8728
|
// src/scheduled-tasks/daemon/DaemonLogger.ts
|
|
8335
8729
|
var fs14 = __toESM(require("fs"));
|
|
8336
|
-
var
|
|
8730
|
+
var path32 = __toESM(require("path"));
|
|
8337
8731
|
var CONFIG_DIR = ".serviceme";
|
|
8338
8732
|
var LOG_FILE = "scheduler.log";
|
|
8339
8733
|
var MAX_LOG_SIZE = 1024 * 1024;
|
|
8340
8734
|
var DaemonLogger = class {
|
|
8341
8735
|
constructor(workspacePath, options = {}) {
|
|
8342
|
-
this.logPath = options.logPath ??
|
|
8343
|
-
const dir =
|
|
8736
|
+
this.logPath = options.logPath ?? path32.join(workspacePath, CONFIG_DIR, LOG_FILE);
|
|
8737
|
+
const dir = path32.dirname(this.logPath);
|
|
8344
8738
|
if (!fs14.existsSync(dir)) {
|
|
8345
8739
|
fs14.mkdirSync(dir, { recursive: true });
|
|
8346
8740
|
}
|
|
@@ -8372,31 +8766,31 @@ var DaemonLogger = class {
|
|
|
8372
8766
|
|
|
8373
8767
|
// src/scheduled-tasks/daemon/PidManager.ts
|
|
8374
8768
|
var fs15 = __toESM(require("fs"));
|
|
8375
|
-
var
|
|
8769
|
+
var path33 = __toESM(require("path"));
|
|
8376
8770
|
var CONFIG_DIR2 = ".serviceme";
|
|
8377
8771
|
var PID_FILE = "scheduler.pid";
|
|
8378
8772
|
var PidManager = class {
|
|
8379
8773
|
constructor(workspacePath, options = {}) {
|
|
8380
|
-
this.pidPath = options.pidPath ??
|
|
8774
|
+
this.pidPath = options.pidPath ?? path33.join(workspacePath, CONFIG_DIR2, PID_FILE);
|
|
8381
8775
|
}
|
|
8382
8776
|
getPidPath() {
|
|
8383
8777
|
return this.pidPath;
|
|
8384
8778
|
}
|
|
8385
8779
|
writePid(pid) {
|
|
8386
|
-
const dir =
|
|
8780
|
+
const dir = path33.dirname(this.pidPath);
|
|
8387
8781
|
if (!fs15.existsSync(dir)) {
|
|
8388
8782
|
fs15.mkdirSync(dir, { recursive: true });
|
|
8389
8783
|
}
|
|
8390
8784
|
fs15.writeFileSync(this.pidPath, String(pid), "utf-8");
|
|
8391
8785
|
}
|
|
8392
8786
|
readPid() {
|
|
8393
|
-
let
|
|
8787
|
+
let stat16;
|
|
8394
8788
|
try {
|
|
8395
|
-
|
|
8789
|
+
stat16 = fs15.statSync(this.pidPath);
|
|
8396
8790
|
} catch {
|
|
8397
8791
|
return null;
|
|
8398
8792
|
}
|
|
8399
|
-
if (!
|
|
8793
|
+
if (!stat16.isFile()) return null;
|
|
8400
8794
|
const raw = fs15.readFileSync(this.pidPath, "utf-8").trim();
|
|
8401
8795
|
const pid = Number.parseInt(raw, 10);
|
|
8402
8796
|
return Number.isNaN(pid) ? null : pid;
|
|
@@ -8426,7 +8820,7 @@ var PidManager = class {
|
|
|
8426
8820
|
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
8427
8821
|
var fs20 = __toESM(require("fs"));
|
|
8428
8822
|
var os6 = __toESM(require("os"));
|
|
8429
|
-
var
|
|
8823
|
+
var path37 = __toESM(require("path"));
|
|
8430
8824
|
|
|
8431
8825
|
// src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts
|
|
8432
8826
|
var import_node_child_process4 = require("child_process");
|
|
@@ -8517,16 +8911,16 @@ var GithubCopilotCliExecutor = class {
|
|
|
8517
8911
|
executeStreaming(payload, onOutput, abortSignal) {
|
|
8518
8912
|
const p = payload;
|
|
8519
8913
|
const execution = resolveGithubCopilotCliExecution(p);
|
|
8520
|
-
let
|
|
8914
|
+
let resolve13;
|
|
8521
8915
|
const resultPromise = new Promise((r) => {
|
|
8522
|
-
|
|
8916
|
+
resolve13 = r;
|
|
8523
8917
|
});
|
|
8524
8918
|
let settled = false;
|
|
8525
8919
|
const settle = (result) => {
|
|
8526
8920
|
if (settled) return;
|
|
8527
8921
|
settled = true;
|
|
8528
8922
|
if (timer) clearTimeout(timer);
|
|
8529
|
-
|
|
8923
|
+
resolve13?.(result);
|
|
8530
8924
|
};
|
|
8531
8925
|
if (abortSignal?.aborted) {
|
|
8532
8926
|
return {
|
|
@@ -8672,7 +9066,7 @@ ${body}`.trim()
|
|
|
8672
9066
|
// src/scheduled-tasks/executors/ShellExecutor.ts
|
|
8673
9067
|
var import_node_child_process5 = require("child_process");
|
|
8674
9068
|
var fs17 = __toESM(require("fs"));
|
|
8675
|
-
var
|
|
9069
|
+
var path34 = __toESM(require("path"));
|
|
8676
9070
|
var MAX_OUTPUT_BYTES2 = 1024 * 1024;
|
|
8677
9071
|
var DEFAULT_TIMEOUT_MS4 = 6e4;
|
|
8678
9072
|
var POSIX_SHELL_CANDIDATES = ["bash.exe", "sh.exe"];
|
|
@@ -8724,10 +9118,10 @@ function findWindowsPosixShell(env, fileExists) {
|
|
|
8724
9118
|
if (fileExists(candidate)) return candidate;
|
|
8725
9119
|
}
|
|
8726
9120
|
const pathValue = env.Path ?? env.PATH ?? "";
|
|
8727
|
-
for (const dir of pathValue.split(
|
|
9121
|
+
for (const dir of pathValue.split(path34.win32.delimiter)) {
|
|
8728
9122
|
if (!dir) continue;
|
|
8729
9123
|
for (const executable of POSIX_SHELL_CANDIDATES) {
|
|
8730
|
-
const candidate =
|
|
9124
|
+
const candidate = path34.win32.join(dir, executable);
|
|
8731
9125
|
if (fileExists(candidate) && !isWindowsWslLauncher(candidate)) {
|
|
8732
9126
|
return candidate;
|
|
8733
9127
|
}
|
|
@@ -8736,7 +9130,7 @@ function findWindowsPosixShell(env, fileExists) {
|
|
|
8736
9130
|
return null;
|
|
8737
9131
|
}
|
|
8738
9132
|
function isWindowsWslLauncher(candidate) {
|
|
8739
|
-
const normalized =
|
|
9133
|
+
const normalized = path34.win32.normalize(candidate).toLowerCase();
|
|
8740
9134
|
return normalized.endsWith("\\windows\\system32\\bash.exe") || normalized.endsWith("\\windows\\syswow64\\bash.exe");
|
|
8741
9135
|
}
|
|
8742
9136
|
function writeDiagnostic2(message) {
|
|
@@ -8763,16 +9157,16 @@ var ShellExecutor = class {
|
|
|
8763
9157
|
executeStreaming(payload, onOutput, abortSignal) {
|
|
8764
9158
|
const p = payload;
|
|
8765
9159
|
const timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS4);
|
|
8766
|
-
let
|
|
9160
|
+
let resolve13;
|
|
8767
9161
|
const resultPromise = new Promise((r) => {
|
|
8768
|
-
|
|
9162
|
+
resolve13 = r;
|
|
8769
9163
|
});
|
|
8770
9164
|
let settled = false;
|
|
8771
9165
|
const settle = (result) => {
|
|
8772
9166
|
if (settled) return;
|
|
8773
9167
|
settled = true;
|
|
8774
9168
|
if (timer) clearTimeout(timer);
|
|
8775
|
-
|
|
9169
|
+
resolve13?.(result);
|
|
8776
9170
|
};
|
|
8777
9171
|
if (abortSignal?.aborted) {
|
|
8778
9172
|
return {
|
|
@@ -8884,10 +9278,10 @@ function getExecutor(taskType) {
|
|
|
8884
9278
|
}
|
|
8885
9279
|
|
|
8886
9280
|
// src/scheduled-tasks/TaskConfigManager.ts
|
|
8887
|
-
var
|
|
9281
|
+
var import_node_crypto9 = require("crypto");
|
|
8888
9282
|
var fs18 = __toESM(require("fs"));
|
|
8889
9283
|
var os5 = __toESM(require("os"));
|
|
8890
|
-
var
|
|
9284
|
+
var path35 = __toESM(require("path"));
|
|
8891
9285
|
var import_devtools_protocol8 = require("@serviceme/devtools-protocol");
|
|
8892
9286
|
function emptyConfig() {
|
|
8893
9287
|
return { version: 2, tasks: [] };
|
|
@@ -8911,7 +9305,7 @@ function v1ContainerShape(value) {
|
|
|
8911
9305
|
}
|
|
8912
9306
|
function defaultWorkspaceContext() {
|
|
8913
9307
|
const home = os5.homedir() || "/";
|
|
8914
|
-
return { path: home, name:
|
|
9308
|
+
return { path: home, name: path35.basename(home) || home };
|
|
8915
9309
|
}
|
|
8916
9310
|
function requireNonEmptyString(payload, field, taskType) {
|
|
8917
9311
|
if (!isRecord2(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
|
|
@@ -9015,7 +9409,7 @@ var TaskConfigManager = class {
|
|
|
9015
9409
|
snippet: raw.slice(0, 500),
|
|
9016
9410
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9017
9411
|
});
|
|
9018
|
-
fs18.mkdirSync(
|
|
9412
|
+
fs18.mkdirSync(path35.dirname(target), { recursive: true });
|
|
9019
9413
|
fs18.writeFileSync(target, JSON.stringify(failures, null, " "), "utf-8");
|
|
9020
9414
|
} catch (writeError) {
|
|
9021
9415
|
this.warn(
|
|
@@ -9024,7 +9418,7 @@ var TaskConfigManager = class {
|
|
|
9024
9418
|
}
|
|
9025
9419
|
}
|
|
9026
9420
|
writeConfig(config) {
|
|
9027
|
-
const dir =
|
|
9421
|
+
const dir = path35.dirname(this.configPath);
|
|
9028
9422
|
if (!fs18.existsSync(dir)) {
|
|
9029
9423
|
fs18.mkdirSync(dir, { recursive: true });
|
|
9030
9424
|
}
|
|
@@ -9052,7 +9446,7 @@ var TaskConfigManager = class {
|
|
|
9052
9446
|
const config = this.readConfig();
|
|
9053
9447
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9054
9448
|
const task = {
|
|
9055
|
-
id: (0,
|
|
9449
|
+
id: (0, import_node_crypto9.randomUUID)(),
|
|
9056
9450
|
name: input.name,
|
|
9057
9451
|
description: input.description,
|
|
9058
9452
|
enabled: input.enabled ?? true,
|
|
@@ -9307,9 +9701,9 @@ var TaskExecutionEngine = class {
|
|
|
9307
9701
|
};
|
|
9308
9702
|
|
|
9309
9703
|
// src/scheduled-tasks/TaskLogManager.ts
|
|
9310
|
-
var
|
|
9704
|
+
var import_node_crypto10 = require("crypto");
|
|
9311
9705
|
var fs19 = __toESM(require("fs"));
|
|
9312
|
-
var
|
|
9706
|
+
var path36 = __toESM(require("path"));
|
|
9313
9707
|
var MAX_LOGS = 200;
|
|
9314
9708
|
function emptyLogFile() {
|
|
9315
9709
|
return { logs: [] };
|
|
@@ -9366,7 +9760,7 @@ var TaskLogManager = class {
|
|
|
9366
9760
|
}
|
|
9367
9761
|
}
|
|
9368
9762
|
writeLogFile(file) {
|
|
9369
|
-
const dir =
|
|
9763
|
+
const dir = path36.dirname(this.logPath);
|
|
9370
9764
|
if (!fs19.existsSync(dir)) {
|
|
9371
9765
|
fs19.mkdirSync(dir, { recursive: true });
|
|
9372
9766
|
}
|
|
@@ -9377,7 +9771,7 @@ var TaskLogManager = class {
|
|
|
9377
9771
|
appendLog(input) {
|
|
9378
9772
|
const file = this.readLogFile();
|
|
9379
9773
|
const log = {
|
|
9380
|
-
id: (0,
|
|
9774
|
+
id: (0, import_node_crypto10.randomUUID)(),
|
|
9381
9775
|
taskId: input.taskId,
|
|
9382
9776
|
taskName: input.taskName,
|
|
9383
9777
|
startedAt: input.startedAt,
|
|
@@ -9439,7 +9833,7 @@ var SchedulerDaemonV2 = class {
|
|
|
9439
9833
|
this.logManager = options.logManager ?? new TaskLogManager();
|
|
9440
9834
|
this.pidManager = options.pidManager ?? new PidManager("", { pidPath: getSchedulerPidPath() });
|
|
9441
9835
|
this.logger = options.logger ?? new DaemonLogger(os6.homedir(), {
|
|
9442
|
-
logPath:
|
|
9836
|
+
logPath: path37.join(path37.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
|
|
9443
9837
|
});
|
|
9444
9838
|
this.getExecutor = options.getExecutor ?? getExecutor;
|
|
9445
9839
|
this.tryAcquireLock = options.tryAcquireLock ?? (() => true);
|
|
@@ -9655,14 +10049,14 @@ function matchCronField(field, value) {
|
|
|
9655
10049
|
|
|
9656
10050
|
// src/scheduled-tasks/migration/MigrateToGlobal.ts
|
|
9657
10051
|
var fs21 = __toESM(require("fs"));
|
|
9658
|
-
var
|
|
10052
|
+
var path38 = __toESM(require("path"));
|
|
9659
10053
|
var import_devtools_protocol9 = require("@serviceme/devtools-protocol");
|
|
9660
10054
|
var WORKSPACE_DIR = ".serviceme";
|
|
9661
10055
|
var V1_FILENAME = "scheduled-tasks.json";
|
|
9662
10056
|
function defaultProbe(workspacePath) {
|
|
9663
10057
|
return {
|
|
9664
10058
|
path: workspacePath,
|
|
9665
|
-
name:
|
|
10059
|
+
name: path38.basename(workspacePath) || workspacePath
|
|
9666
10060
|
};
|
|
9667
10061
|
}
|
|
9668
10062
|
function readV1Config(v1Path) {
|
|
@@ -9696,7 +10090,7 @@ function safeDelete(filePath) {
|
|
|
9696
10090
|
}
|
|
9697
10091
|
}
|
|
9698
10092
|
function ensureDir(filePath) {
|
|
9699
|
-
const dir =
|
|
10093
|
+
const dir = path38.dirname(filePath);
|
|
9700
10094
|
if (!fs21.existsSync(dir)) {
|
|
9701
10095
|
fs21.mkdirSync(dir, { recursive: true });
|
|
9702
10096
|
}
|
|
@@ -9741,7 +10135,7 @@ async function migrateToGlobal(options) {
|
|
|
9741
10135
|
const conflicts = [];
|
|
9742
10136
|
const issues = [];
|
|
9743
10137
|
for (const workspacePath of options.workspacePaths) {
|
|
9744
|
-
const v1Path =
|
|
10138
|
+
const v1Path = path38.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
|
|
9745
10139
|
if (!fs21.existsSync(v1Path)) continue;
|
|
9746
10140
|
const v1 = readV1Config(v1Path);
|
|
9747
10141
|
if (!v1.ok) {
|
|
@@ -9803,7 +10197,7 @@ async function migrateToGlobal(options) {
|
|
|
9803
10197
|
// src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts
|
|
9804
10198
|
var import_node_child_process6 = require("child_process");
|
|
9805
10199
|
var fs22 = __toESM(require("fs"));
|
|
9806
|
-
var
|
|
10200
|
+
var path39 = __toESM(require("path"));
|
|
9807
10201
|
var DEFAULT_TIMEOUT_MS5 = 2e3;
|
|
9808
10202
|
var GitTimeoutError = class extends Error {
|
|
9809
10203
|
constructor() {
|
|
@@ -9819,7 +10213,7 @@ function isTimeout(err) {
|
|
|
9819
10213
|
return false;
|
|
9820
10214
|
}
|
|
9821
10215
|
function defaultRunGit(gitBinary, args, cwd, timeoutMs) {
|
|
9822
|
-
return new Promise((
|
|
10216
|
+
return new Promise((resolve13, reject) => {
|
|
9823
10217
|
let settled = false;
|
|
9824
10218
|
const child = (0, import_node_child_process6.spawn)(gitBinary, args, {
|
|
9825
10219
|
cwd,
|
|
@@ -9849,7 +10243,7 @@ function defaultRunGit(gitBinary, args, cwd, timeoutMs) {
|
|
|
9849
10243
|
if (settled) return;
|
|
9850
10244
|
settled = true;
|
|
9851
10245
|
clearTimeout(timer);
|
|
9852
|
-
|
|
10246
|
+
resolve13({ stdout, stderr, code: code ?? 0 });
|
|
9853
10247
|
});
|
|
9854
10248
|
});
|
|
9855
10249
|
}
|
|
@@ -9866,7 +10260,7 @@ var WorkspaceProbe = class {
|
|
|
9866
10260
|
}
|
|
9867
10261
|
}
|
|
9868
10262
|
async probe(workspacePath) {
|
|
9869
|
-
const name =
|
|
10263
|
+
const name = path39.basename(workspacePath) || workspacePath;
|
|
9870
10264
|
if (!workspacePath || !fs22.existsSync(workspacePath)) {
|
|
9871
10265
|
return {
|
|
9872
10266
|
workspace: { path: workspacePath, name },
|
|
@@ -10020,7 +10414,7 @@ var SkillReconciler = class {
|
|
|
10020
10414
|
|
|
10021
10415
|
// src/skills/SkillStore.ts
|
|
10022
10416
|
var fs23 = __toESM(require("fs/promises"));
|
|
10023
|
-
var
|
|
10417
|
+
var path40 = __toESM(require("path"));
|
|
10024
10418
|
var USER_SKILL_MARKER_FILE = ".serviceme-skill.json";
|
|
10025
10419
|
var LEGACY_USER_SKILL_MARKER_FILE = ".ms-devtools-skill.json";
|
|
10026
10420
|
var WORKSPACE_SKILLS_ROOT_RELATIVE = ".github/skills";
|
|
@@ -10056,10 +10450,10 @@ var SkillStore = class {
|
|
|
10056
10450
|
return WORKSPACE_SKILLS_MARKER_RELATIVE;
|
|
10057
10451
|
}
|
|
10058
10452
|
getUserSkillPath(skillId) {
|
|
10059
|
-
return
|
|
10453
|
+
return path40.join(this.userSkillsRoot, skillId);
|
|
10060
10454
|
}
|
|
10061
10455
|
async listWorkspaceSkillIds() {
|
|
10062
|
-
const skillsRootPath =
|
|
10456
|
+
const skillsRootPath = path40.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
|
|
10063
10457
|
try {
|
|
10064
10458
|
const entries = await this.fileSystem.readdir(skillsRootPath, {
|
|
10065
10459
|
withFileTypes: true
|
|
@@ -10083,7 +10477,7 @@ var SkillStore = class {
|
|
|
10083
10477
|
const targetDir = this.getUserSkillPath(skillId);
|
|
10084
10478
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
10085
10479
|
await this.fileSystem.writeFile(
|
|
10086
|
-
|
|
10480
|
+
path40.join(targetDir, USER_SKILL_MARKER_FILE),
|
|
10087
10481
|
JSON.stringify({ skillId, installedBy: "serviceme" }, null, 2),
|
|
10088
10482
|
"utf-8"
|
|
10089
10483
|
);
|
|
@@ -10092,7 +10486,7 @@ var SkillStore = class {
|
|
|
10092
10486
|
await this.migrateLegacyUserSkillMarker(skillId);
|
|
10093
10487
|
try {
|
|
10094
10488
|
const marker = await this.fileSystem.readFile(
|
|
10095
|
-
|
|
10489
|
+
path40.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
|
|
10096
10490
|
"utf-8"
|
|
10097
10491
|
);
|
|
10098
10492
|
const parsed = JSON.parse(marker);
|
|
@@ -10109,8 +10503,8 @@ var SkillStore = class {
|
|
|
10109
10503
|
*/
|
|
10110
10504
|
async migrateLegacyUserSkillMarker(skillId) {
|
|
10111
10505
|
const targetDir = this.getUserSkillPath(skillId);
|
|
10112
|
-
const newPath =
|
|
10113
|
-
const legacyPath =
|
|
10506
|
+
const newPath = path40.join(targetDir, USER_SKILL_MARKER_FILE);
|
|
10507
|
+
const legacyPath = path40.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
|
|
10114
10508
|
try {
|
|
10115
10509
|
await this.fileSystem.readFile(newPath, "utf-8");
|
|
10116
10510
|
return;
|
|
@@ -10123,12 +10517,12 @@ var SkillStore = class {
|
|
|
10123
10517
|
}
|
|
10124
10518
|
}
|
|
10125
10519
|
async writeSkillFiles(skillId, scope, files) {
|
|
10126
|
-
const root = scope === "workspace" ?
|
|
10127
|
-
const targetDir =
|
|
10520
|
+
const root = scope === "workspace" ? path40.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
|
|
10521
|
+
const targetDir = path40.join(root, skillId);
|
|
10128
10522
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
10129
10523
|
for (const file of files) {
|
|
10130
|
-
const filePath =
|
|
10131
|
-
await this.fileSystem.mkdir(
|
|
10524
|
+
const filePath = path40.join(targetDir, file.path);
|
|
10525
|
+
await this.fileSystem.mkdir(path40.dirname(filePath), { recursive: true });
|
|
10132
10526
|
await this.fileSystem.writeFile(filePath, file.content, "utf-8");
|
|
10133
10527
|
if (file.executable) {
|
|
10134
10528
|
try {
|
|
@@ -10141,8 +10535,8 @@ var SkillStore = class {
|
|
|
10141
10535
|
};
|
|
10142
10536
|
async function migrateLegacyUserSkillContent(input) {
|
|
10143
10537
|
const fileSystem = input.fileSystem ?? fs23;
|
|
10144
|
-
const legacyRoot =
|
|
10145
|
-
const targetRoot =
|
|
10538
|
+
const legacyRoot = path40.join(input.homeDir, ...LEGACY_USER_SKILLS_ROOT_RELATIVE.split("/"));
|
|
10539
|
+
const targetRoot = path40.join(input.homeDir, ".copilot", "skills");
|
|
10146
10540
|
let entries;
|
|
10147
10541
|
try {
|
|
10148
10542
|
entries = await fileSystem.readdir(legacyRoot, { withFileTypes: true });
|
|
@@ -10154,8 +10548,8 @@ async function migrateLegacyUserSkillContent(input) {
|
|
|
10154
10548
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
10155
10549
|
result.push({
|
|
10156
10550
|
id: entry.name,
|
|
10157
|
-
legacyPath:
|
|
10158
|
-
targetPath:
|
|
10551
|
+
legacyPath: path40.join(legacyRoot, entry.name),
|
|
10552
|
+
targetPath: path40.join(targetRoot, entry.name),
|
|
10159
10553
|
status: "migration_available"
|
|
10160
10554
|
});
|
|
10161
10555
|
}
|
|
@@ -10164,7 +10558,7 @@ async function migrateLegacyUserSkillContent(input) {
|
|
|
10164
10558
|
|
|
10165
10559
|
// src/submit/index.ts
|
|
10166
10560
|
var fs24 = __toESM(require("fs/promises"));
|
|
10167
|
-
var
|
|
10561
|
+
var path41 = __toESM(require("path"));
|
|
10168
10562
|
|
|
10169
10563
|
// src/submit/types.ts
|
|
10170
10564
|
var SubmitError = class extends Error {
|
|
@@ -10214,11 +10608,11 @@ var SubmitClient = class {
|
|
|
10214
10608
|
throw new SubmitError(v.reason ?? "unknown", v.detail ?? "validation denied");
|
|
10215
10609
|
}
|
|
10216
10610
|
const localRepoPath = getRepoDir(repoId);
|
|
10217
|
-
const targetDir =
|
|
10611
|
+
const targetDir = path41.join(localRepoPath, "skills", skillName);
|
|
10218
10612
|
await fs24.mkdir(targetDir, { recursive: true });
|
|
10219
10613
|
for (const f of files) {
|
|
10220
|
-
const full =
|
|
10221
|
-
await fs24.mkdir(
|
|
10614
|
+
const full = path41.join(targetDir, f.path);
|
|
10615
|
+
await fs24.mkdir(path41.dirname(full), { recursive: true });
|
|
10222
10616
|
const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
|
|
10223
10617
|
await fs24.writeFile(tmp, f.content, "utf8");
|
|
10224
10618
|
await fs24.rename(tmp, full);
|
|
@@ -10289,8 +10683,8 @@ function touchLastUsedAt(tools, id, when = /* @__PURE__ */ new Date()) {
|
|
|
10289
10683
|
}
|
|
10290
10684
|
|
|
10291
10685
|
// src/toolbox/ToolboxStore.ts
|
|
10292
|
-
var
|
|
10293
|
-
var
|
|
10686
|
+
var fsp18 = __toESM(require("fs/promises"));
|
|
10687
|
+
var path42 = __toESM(require("path"));
|
|
10294
10688
|
var import_promises4 = require("timers/promises");
|
|
10295
10689
|
|
|
10296
10690
|
// src/toolbox/types.ts
|
|
@@ -10326,19 +10720,19 @@ var DEFAULT_LOCK_TIMEOUT_MS2 = 5e3;
|
|
|
10326
10720
|
var DEFAULT_LOCK_RETRY_MS2 = 25;
|
|
10327
10721
|
var LOCK_STALE_GRACE_MS2 = 200;
|
|
10328
10722
|
var TMP_SUFFIX2 = ".tmp";
|
|
10329
|
-
var WORKSPACE_TOOLBOX_RELATIVE_PATH =
|
|
10723
|
+
var WORKSPACE_TOOLBOX_RELATIVE_PATH = path42.join(".github", ".serviceme-toolbox.json");
|
|
10330
10724
|
var LEGACY_WORKSPACE_TOOLBOX_FILENAME = ".ms-devtools-toolbox.json";
|
|
10331
10725
|
async function migrateLegacyWorkspaceToolboxFile(filePath) {
|
|
10332
10726
|
if (!filePath) return;
|
|
10333
|
-
const legacyPath =
|
|
10727
|
+
const legacyPath = path42.join(path42.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
|
|
10334
10728
|
if (legacyPath === filePath) return;
|
|
10335
10729
|
try {
|
|
10336
|
-
await
|
|
10730
|
+
await fsp18.access(filePath);
|
|
10337
10731
|
return;
|
|
10338
10732
|
} catch {
|
|
10339
10733
|
}
|
|
10340
10734
|
try {
|
|
10341
|
-
await
|
|
10735
|
+
await fsp18.rename(legacyPath, filePath);
|
|
10342
10736
|
} catch {
|
|
10343
10737
|
}
|
|
10344
10738
|
}
|
|
@@ -10348,7 +10742,7 @@ var FsToolboxFileBackend = class {
|
|
|
10348
10742
|
}
|
|
10349
10743
|
async exists(filePath) {
|
|
10350
10744
|
try {
|
|
10351
|
-
await
|
|
10745
|
+
await fsp18.access(filePath);
|
|
10352
10746
|
return true;
|
|
10353
10747
|
} catch {
|
|
10354
10748
|
return false;
|
|
@@ -10357,7 +10751,7 @@ var FsToolboxFileBackend = class {
|
|
|
10357
10751
|
async read(filePath) {
|
|
10358
10752
|
let buf;
|
|
10359
10753
|
try {
|
|
10360
|
-
buf = await
|
|
10754
|
+
buf = await fsp18.readFile(filePath, "utf8");
|
|
10361
10755
|
} catch (err) {
|
|
10362
10756
|
if (isNodeError2(err) && err.code === "ENOENT") return null;
|
|
10363
10757
|
throw err;
|
|
@@ -10373,43 +10767,43 @@ var FsToolboxFileBackend = class {
|
|
|
10373
10767
|
async backupCorruptedFile(filePath) {
|
|
10374
10768
|
try {
|
|
10375
10769
|
const backupPath = `${filePath}.corrupted.${Date.now()}.bak`;
|
|
10376
|
-
await
|
|
10770
|
+
await fsp18.copyFile(filePath, backupPath);
|
|
10377
10771
|
await this.purgeExcessBackups(filePath);
|
|
10378
10772
|
} catch {
|
|
10379
10773
|
}
|
|
10380
10774
|
}
|
|
10381
10775
|
async purgeExcessBackups(filePath) {
|
|
10382
|
-
const dir =
|
|
10383
|
-
const base =
|
|
10776
|
+
const dir = path42.dirname(filePath);
|
|
10777
|
+
const base = path42.basename(filePath);
|
|
10384
10778
|
let entries;
|
|
10385
10779
|
try {
|
|
10386
|
-
entries = await
|
|
10780
|
+
entries = await fsp18.readdir(dir);
|
|
10387
10781
|
} catch {
|
|
10388
10782
|
return;
|
|
10389
10783
|
}
|
|
10390
|
-
const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath:
|
|
10784
|
+
const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath: path42.join(dir, n) })).sort((a, b) => {
|
|
10391
10785
|
return a.name.localeCompare(b.name);
|
|
10392
10786
|
});
|
|
10393
10787
|
const excess = backups.length - this.maxBackupCount;
|
|
10394
10788
|
if (excess <= 0) return;
|
|
10395
10789
|
await Promise.all(
|
|
10396
|
-
backups.slice(0, excess).map((b) =>
|
|
10790
|
+
backups.slice(0, excess).map((b) => fsp18.rm(b.filePath).catch(() => void 0))
|
|
10397
10791
|
);
|
|
10398
10792
|
}
|
|
10399
10793
|
async write(filePath, payload) {
|
|
10400
|
-
await
|
|
10794
|
+
await fsp18.mkdir(path42.dirname(filePath), { recursive: true });
|
|
10401
10795
|
const tmpPath = `${filePath}${TMP_SUFFIX2}`;
|
|
10402
10796
|
const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
|
|
10403
|
-
await
|
|
10404
|
-
const handle = await
|
|
10797
|
+
await fsp18.rm(tmpPath, { force: true });
|
|
10798
|
+
const handle = await fsp18.open(tmpPath, "w", FILE_MODE2);
|
|
10405
10799
|
try {
|
|
10406
10800
|
await handle.writeFile(bytes);
|
|
10407
10801
|
await handle.sync();
|
|
10408
10802
|
} finally {
|
|
10409
10803
|
await handle.close();
|
|
10410
10804
|
}
|
|
10411
|
-
await
|
|
10412
|
-
await
|
|
10805
|
+
await fsp18.rename(tmpPath, filePath);
|
|
10806
|
+
await fsp18.chmod(filePath, FILE_MODE2).catch(() => void 0);
|
|
10413
10807
|
}
|
|
10414
10808
|
};
|
|
10415
10809
|
function coercePersistedToolbox(parsed) {
|
|
@@ -10441,7 +10835,7 @@ var ToolboxFileLock = class {
|
|
|
10441
10835
|
constructor(filePath, timeoutMs, retryMs) {
|
|
10442
10836
|
this.acquired = false;
|
|
10443
10837
|
this.dirPath = `${filePath}.lock`;
|
|
10444
|
-
this.pidFilePath =
|
|
10838
|
+
this.pidFilePath = path42.join(this.dirPath, "pid");
|
|
10445
10839
|
this.timeoutMs = timeoutMs;
|
|
10446
10840
|
this.retryMs = retryMs;
|
|
10447
10841
|
}
|
|
@@ -10449,15 +10843,15 @@ var ToolboxFileLock = class {
|
|
|
10449
10843
|
const start = Date.now();
|
|
10450
10844
|
while (true) {
|
|
10451
10845
|
try {
|
|
10452
|
-
await
|
|
10453
|
-
await
|
|
10846
|
+
await fsp18.mkdir(this.dirPath, { mode: LOCK_DIR_MODE2 });
|
|
10847
|
+
await fsp18.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
10454
10848
|
this.acquired = true;
|
|
10455
10849
|
return;
|
|
10456
10850
|
} catch (err) {
|
|
10457
10851
|
if (!isNodeError2(err) || err.code !== "EEXIST") throw err;
|
|
10458
10852
|
const stale = await this.isStaleLock();
|
|
10459
10853
|
if (stale) {
|
|
10460
|
-
await
|
|
10854
|
+
await fsp18.rm(this.dirPath, { recursive: true, force: true });
|
|
10461
10855
|
continue;
|
|
10462
10856
|
}
|
|
10463
10857
|
if (Date.now() - start >= this.timeoutMs) {
|
|
@@ -10470,11 +10864,11 @@ var ToolboxFileLock = class {
|
|
|
10470
10864
|
async isStaleLock() {
|
|
10471
10865
|
let pidStr;
|
|
10472
10866
|
try {
|
|
10473
|
-
pidStr = await
|
|
10867
|
+
pidStr = await fsp18.readFile(this.pidFilePath, "utf8");
|
|
10474
10868
|
} catch {
|
|
10475
10869
|
try {
|
|
10476
|
-
const
|
|
10477
|
-
return Date.now() -
|
|
10870
|
+
const stat16 = await fsp18.stat(this.dirPath);
|
|
10871
|
+
return Date.now() - stat16.mtimeMs > LOCK_STALE_GRACE_MS2;
|
|
10478
10872
|
} catch {
|
|
10479
10873
|
return false;
|
|
10480
10874
|
}
|
|
@@ -10486,12 +10880,12 @@ var ToolboxFileLock = class {
|
|
|
10486
10880
|
async release() {
|
|
10487
10881
|
if (!this.acquired) return;
|
|
10488
10882
|
this.acquired = false;
|
|
10489
|
-
await
|
|
10883
|
+
await fsp18.rm(this.dirPath, { recursive: true, force: true });
|
|
10490
10884
|
}
|
|
10491
10885
|
};
|
|
10492
10886
|
function defaultWorkspacePath() {
|
|
10493
10887
|
if (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === "1") return null;
|
|
10494
|
-
return
|
|
10888
|
+
return path42.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
|
|
10495
10889
|
}
|
|
10496
10890
|
var ToolboxStore = class {
|
|
10497
10891
|
constructor(opts = {}) {
|
|
@@ -10580,7 +10974,7 @@ var ToolboxStore = class {
|
|
|
10580
10974
|
async clear(scope) {
|
|
10581
10975
|
const filePath = this.filePathFor(scope);
|
|
10582
10976
|
if (!filePath) return;
|
|
10583
|
-
await
|
|
10977
|
+
await fsp18.rm(filePath, { force: true });
|
|
10584
10978
|
}
|
|
10585
10979
|
/** Test seam — resolve the user-scope file path. */
|
|
10586
10980
|
getUserFilePath() {
|
|
@@ -10842,13 +11236,16 @@ var ToolboxCore = class {
|
|
|
10842
11236
|
assertSafeRepoId,
|
|
10843
11237
|
bootstrapDefaults,
|
|
10844
11238
|
bootstrapPhase5Placeholders,
|
|
11239
|
+
buildContentIdentity,
|
|
10845
11240
|
buildCopilotCustomizationView,
|
|
10846
11241
|
buildDefaultReposFile,
|
|
11242
|
+
buildDisabledIdentityMatcher,
|
|
10847
11243
|
buildGitHubLocalEmail,
|
|
10848
11244
|
buildGitProxyBase,
|
|
10849
11245
|
buildSignedHeaders,
|
|
10850
11246
|
copilotDoctor,
|
|
10851
11247
|
copilotPrompt,
|
|
11248
|
+
coveredTargetsOf,
|
|
10852
11249
|
createConsoleLogger,
|
|
10853
11250
|
createCopilotAuthRequiredError,
|
|
10854
11251
|
createCopilotNotInstalledError,
|
|
@@ -10859,10 +11256,13 @@ var ToolboxCore = class {
|
|
|
10859
11256
|
createPluginCatalogService,
|
|
10860
11257
|
createProjectTools,
|
|
10861
11258
|
createReposStore,
|
|
11259
|
+
dedupeAdoptionsByPluginManifests,
|
|
10862
11260
|
defaultRepoConfigSchema,
|
|
10863
11261
|
deriveCopilotUserStatus,
|
|
10864
11262
|
deriveInstallationId,
|
|
11263
|
+
detectUnmanagedWorkspaceContent,
|
|
10865
11264
|
ensureDefaultsInstalled,
|
|
11265
|
+
findPluginCoveringArtifact,
|
|
10866
11266
|
findPluginMcpJson,
|
|
10867
11267
|
findRepoHooksJson,
|
|
10868
11268
|
fingerprintSource,
|
|
@@ -10916,6 +11316,7 @@ var ToolboxCore = class {
|
|
|
10916
11316
|
narrowRepoConfig,
|
|
10917
11317
|
noopLogger,
|
|
10918
11318
|
parseAgentToolPermissions,
|
|
11319
|
+
parseContentIdentity,
|
|
10919
11320
|
parseHooksJson,
|
|
10920
11321
|
parseMcpJson,
|
|
10921
11322
|
randomInstallationId,
|
|
@@ -10932,6 +11333,7 @@ var ToolboxCore = class {
|
|
|
10932
11333
|
resolveTaskExecutionPayload,
|
|
10933
11334
|
resolveWorkspaceContentPlan,
|
|
10934
11335
|
setUserHomeOverrides,
|
|
11336
|
+
setWorkspacePluginArtifactsDisabled,
|
|
10935
11337
|
sortByRecentFirst,
|
|
10936
11338
|
sortByUserOrder,
|
|
10937
11339
|
supportsPersonalIntegrationKind,
|
|
@@ -10939,6 +11341,7 @@ var ToolboxCore = class {
|
|
|
10939
11341
|
toArtifactSummary,
|
|
10940
11342
|
toFileUrl,
|
|
10941
11343
|
touchLastUsedAt,
|
|
11344
|
+
unmanagedDetectionSignature,
|
|
10942
11345
|
unzipFile,
|
|
10943
11346
|
upsertWorkspaceContentSelection,
|
|
10944
11347
|
userRepoConfigSchema,
|