@ai-outfitter/outfitter 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/code/pi-extension/src/outfitter-extension.js +9 -1
- package/dist/agents/AgentLaunch.d.ts +2 -2
- package/dist/agents/AgentLaunch.js +4 -3
- package/dist/agents/AgentLaunch.js.map +1 -1
- package/dist/agents/ClaudeStatePersistence.d.ts +18 -0
- package/dist/agents/ClaudeStatePersistence.js +225 -0
- package/dist/agents/ClaudeStatePersistence.js.map +1 -0
- package/dist/cli/OutfitterCli.js +1 -1
- package/dist/cli/OutfitterCli.js.map +1 -1
- package/dist/cli/commands/RunAgentCommand.js +76 -15
- package/dist/cli/commands/RunAgentCommand.js.map +1 -1
- package/dist/cli/commands/SyncCommand.d.ts +7 -3
- package/dist/cli/commands/SyncCommand.js +110 -20
- package/dist/cli/commands/SyncCommand.js.map +1 -1
- package/dist/extensions/PiExtensionCache.d.ts +11 -1
- package/dist/extensions/PiExtensionCache.js +129 -13
- package/dist/extensions/PiExtensionCache.js.map +1 -1
- package/dist/projection/CodexMcp.d.ts +11 -0
- package/dist/projection/CodexMcp.js +143 -0
- package/dist/projection/CodexMcp.js.map +1 -0
- package/dist/projection/Materialize.d.ts +3 -1
- package/dist/projection/Materialize.js +10 -7
- package/dist/projection/Materialize.js.map +1 -1
- package/dist/projection/ProjectHarness.js +75 -32
- package/dist/projection/ProjectHarness.js.map +1 -1
- package/dist/projection/Projection.d.ts +2 -0
- package/dist/projection/Tools.d.ts +91 -0
- package/dist/projection/Tools.js +129 -0
- package/dist/projection/Tools.js.map +1 -0
- package/dist/resolver/AgentDefinition.d.ts +11 -0
- package/dist/resolver/AgentDefinition.js +91 -12
- package/dist/resolver/AgentDefinition.js.map +1 -1
- package/dist/resolver/Layer.d.ts +5 -2
- package/dist/resolver/Layer.js +47 -8
- package/dist/resolver/Layer.js.map +1 -1
- package/dist/resolver/ResolverContext.d.ts +1 -1
- package/dist/resolver/ResolverContext.js +1 -1
- package/dist/resolver/ResolverContext.js.map +1 -1
- package/dist/resolver/ResolverValidation.d.ts +12 -1
- package/dist/resolver/ResolverValidation.js +30 -20
- package/dist/resolver/ResolverValidation.js.map +1 -1
- package/dist/schemas/agent.schema.json +8 -2
- package/dist/schemas/settings.schema.json +1 -1
- package/dist/schemas/system-extension-hook.schema.json +38 -0
- package/dist/settings/Settings.d.ts +1 -1
- package/dist/settings/Settings.js +1 -1
- package/dist/settings/Settings.js.map +1 -1
- package/dist/settings/SettingsLoader.js.map +1 -1
- package/dist/setup/DefaultCatalog.d.ts +18 -0
- package/dist/setup/DefaultCatalog.js +74 -13
- package/dist/setup/DefaultCatalog.js.map +1 -1
- package/dist/sources/SourceCache.d.ts +20 -0
- package/dist/sources/SourceCache.js +31 -1
- package/dist/sources/SourceCache.js.map +1 -1
- package/dist/sources/TransitiveSources.d.ts +43 -0
- package/dist/sources/TransitiveSources.js +177 -0
- package/dist/sources/TransitiveSources.js.map +1 -0
- package/dist/system/SystemExtensionHook.d.ts +34 -0
- package/dist/system/SystemExtensionHook.js +190 -0
- package/dist/system/SystemExtensionHook.js.map +1 -0
- package/dist/validation/SchemaValidator.d.ts +1 -1
- package/dist/validation/SchemaValidator.js +2 -0
- package/dist/validation/SchemaValidator.js.map +1 -1
- package/docs/architecture/state_writeback_strategy.md +15 -3
- package/docs/documentation/README.md +15 -2
- package/docs/documentation/catalogs.md +61 -2
- package/docs/documentation/cli.md +2 -1
- package/docs/documentation/concepts.md +2 -2
- package/docs/documentation/containers.md +63 -14
- package/docs/documentation/hooks.md +33 -0
- package/docs/documentation/in-cluster.md +2 -0
- package/docs/documentation/migration.md +1 -1
- package/docs/documentation/personas.md +1 -1
- package/docs/documentation/settings.md +1 -1
- package/docs/documentation/state.md +27 -0
- package/docs/documentation/support-matrix.md +36 -23
- package/docs/documentation/usecases/org-onboarding-sdlc-report.md +143 -0
- package/docs/documentation/usecases/persona-reviews.md +1 -1
- package/docs/philosophy.md +20 -0
- package/package.json +1 -1
- package/src/schemas/agent.schema.json +8 -2
- package/src/schemas/settings.schema.json +1 -1
- package/src/schemas/system-extension-hook.schema.json +38 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { syncRemoteRepositoryAtomically } from '../../sources/GitRepository.js';
|
|
1
2
|
import { type GitHubRepositoryVisibilityClassifier, type PrivateCatalogPrompt, type PrivateCatalogSourceGate } from '../../sources/PrivateCatalogGate.js';
|
|
2
3
|
import type { CommandObject } from './CommandObject.js';
|
|
3
4
|
export type SyncStatus = 'updated' | 'unchanged' | 'skipped' | 'failed';
|
|
4
|
-
export type SyncSourceKind = 'remote_settings' | 'source';
|
|
5
|
+
export type SyncSourceKind = 'remote_settings' | 'source' | 'transitive';
|
|
5
6
|
export interface SyncSourceResult {
|
|
6
7
|
readonly kind: SyncSourceKind;
|
|
7
8
|
readonly uri: string;
|
|
@@ -18,6 +19,8 @@ export interface SyncCommandInput {
|
|
|
18
19
|
readonly homeDirectory: string;
|
|
19
20
|
readonly projectDirectory: string;
|
|
20
21
|
}
|
|
22
|
+
/** Fetches one repository into the cache. Injectable so tests exercise the closure hermetically. */
|
|
23
|
+
export type RepositorySync = typeof syncRemoteRepositoryAtomically;
|
|
21
24
|
export interface SyncCommandDependencies {
|
|
22
25
|
readonly homeDirectory?: string;
|
|
23
26
|
readonly projectDirectory?: string;
|
|
@@ -25,7 +28,8 @@ export interface SyncCommandDependencies {
|
|
|
25
28
|
readonly prompt?: PrivateCatalogPrompt;
|
|
26
29
|
readonly privateCatalogGate?: PrivateCatalogSourceGate;
|
|
27
30
|
readonly writeLine?: (message: string) => void;
|
|
31
|
+
readonly syncRepository?: RepositorySync;
|
|
28
32
|
}
|
|
29
|
-
/** Validates
|
|
30
|
-
export declare const executeSyncCommand: (input: SyncCommandInput, dependencies?: Pick<SyncCommandDependencies, "classifier" | "prompt" | "privateCatalogGate">) => SyncCommandResult;
|
|
33
|
+
/** Validates settings, syncs remote settings, reloads, syncs remote sources, then their closure. */
|
|
34
|
+
export declare const executeSyncCommand: (input: SyncCommandInput, dependencies?: Pick<SyncCommandDependencies, "classifier" | "prompt" | "privateCatalogGate" | "syncRepository">) => SyncCommandResult;
|
|
31
35
|
export declare const createSyncCommand: (dependencies?: SyncCommandDependencies) => CommandObject;
|
|
@@ -5,9 +5,10 @@ import { remoteSourceLayer } from '../../resolver/Layer.js';
|
|
|
5
5
|
import { resolveResources } from '../../resolver/Resolver.js';
|
|
6
6
|
import { validateEffectiveSet } from '../../resolver/ResolverValidation.js';
|
|
7
7
|
import { createSettingsLoadPlan, discoverSettingsLoadPlan, formatSettingsIssue, loadSettings, loadSettingsFiles, loadSettingsWithCachedRemoteSettings, resolveRemoteSettingsPath, } from '../../settings/SettingsLoader.js';
|
|
8
|
-
import { syncRemoteRepositoryAtomically } from '../../sources/GitRepository.js';
|
|
8
|
+
import { syncRemoteRepositoryAtomically, tryReadCleanHead } from '../../sources/GitRepository.js';
|
|
9
9
|
import { createEnterprisePrivateCatalogGate, } from '../../sources/PrivateCatalogGate.js';
|
|
10
|
-
import { createRemoteRepositoryCachePath, formatRemoteSourceDisplay, isRemoteSource, redactEmbeddedSourceCredentials, } from '../../sources/SourceCache.js';
|
|
10
|
+
import { createRemoteRepositoryCachePath, encodeRemoteSourceSelection, formatRemoteSourceDisplay, isRemoteSource, redactEmbeddedSourceCredentials, resolveSourcePayloadRoot, } from '../../sources/SourceCache.js';
|
|
11
|
+
import { readDeclaredRemoteSources } from '../../sources/TransitiveSources.js';
|
|
11
12
|
import { resolveHomeDirectory, resolveProjectDirectory } from './ProcessDefaults.js';
|
|
12
13
|
const formatSettingsIssues = (heading, issues) => [
|
|
13
14
|
heading,
|
|
@@ -36,7 +37,10 @@ const validateSourceCheckout = (repositoryPath, source) => {
|
|
|
36
37
|
if (!existsSync(layer.root)) {
|
|
37
38
|
throw new Error(`Configured source path '${source.path}' was not found in the fetched repository.`);
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
// A source is validated in isolation here, so an unresolved loadout slug is deferred (a warning,
|
|
41
|
+
// not a failure): a catalog may reference a skill supplied by a catalog it declares as a transitive
|
|
42
|
+
// dependency (OFTR-004.6), and loadout wholeness is validated against the merged tree at resolution.
|
|
43
|
+
const findings = validateEffectiveSet(resolveResources([layer]), undefined, { deferLoadoutResolution: true });
|
|
40
44
|
const errors = findings.filter((finding) => finding.severity === 'error');
|
|
41
45
|
if (errors.length > 0) {
|
|
42
46
|
throw new Error(`Fetched source is invalid: ${errors.map((finding) => `${finding.resource} ${finding.message}`).join('; ')}`);
|
|
@@ -46,41 +50,103 @@ const validateSourceCheckout = (repositoryPath, source) => {
|
|
|
46
50
|
/** Runs the private-catalog gate over one phase's sources, then fetches everything it allowed. */
|
|
47
51
|
const syncPhase = (input) => {
|
|
48
52
|
const gated = input.gate.filter(input.sources, input.cacheDirectory);
|
|
53
|
+
// Pair each source with its own result so a later step reads the checkout by identity, never by a
|
|
54
|
+
// display string (which omits a `uri:` source's ref and would collide across refs).
|
|
49
55
|
const synced = gated.allowedSources.map((source) => {
|
|
50
56
|
const cachePath = createRemoteRepositoryCachePath(input.homeDirectory, source, input.cacheDirectory);
|
|
51
57
|
const uri = formatRemoteSourceDisplay(source);
|
|
52
58
|
try {
|
|
53
|
-
const result =
|
|
59
|
+
const result = input.syncRepository({
|
|
54
60
|
cachePath,
|
|
55
61
|
source,
|
|
56
62
|
validate: (repositoryPath) => input.validate(repositoryPath, source),
|
|
57
63
|
});
|
|
58
64
|
return {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
source,
|
|
66
|
+
result: {
|
|
67
|
+
kind: input.kind,
|
|
68
|
+
uri,
|
|
69
|
+
cachePath,
|
|
70
|
+
status: result.status,
|
|
71
|
+
message: result.warnings === 0 ? undefined : `validated with ${result.warnings} warning(s)`,
|
|
72
|
+
},
|
|
64
73
|
};
|
|
65
74
|
}
|
|
66
75
|
catch (error) {
|
|
67
76
|
return {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
source,
|
|
78
|
+
result: {
|
|
79
|
+
kind: input.kind,
|
|
80
|
+
uri,
|
|
81
|
+
cachePath,
|
|
82
|
+
status: 'failed',
|
|
83
|
+
message: redactEmbeddedSourceCredentials(formatCaughtError(error)),
|
|
84
|
+
},
|
|
73
85
|
};
|
|
74
86
|
}
|
|
75
87
|
});
|
|
76
88
|
return {
|
|
77
89
|
allowedSources: gated.allowedSources,
|
|
78
90
|
messages: gated.messages,
|
|
79
|
-
results: [
|
|
91
|
+
results: [
|
|
92
|
+
...gated.skippedResults.map((result) => ({ ...result, kind: input.kind })),
|
|
93
|
+
...synced.map((s) => s.result),
|
|
94
|
+
],
|
|
95
|
+
// A source is traversable when its checkout is readable on disk *now* — not only when this run
|
|
96
|
+
// refreshed it. A failed refresh leaves the prior valid cache in place (atomic sync) and
|
|
97
|
+
// resolution keeps using it, so its declared dependencies must still be discovered; a failed
|
|
98
|
+
// first fetch leaves no checkout, so it is correctly excluded.
|
|
99
|
+
readableSources: synced.filter((s) => tryReadCleanHead(s.result.cachePath) !== undefined).map((s) => s.source),
|
|
80
100
|
};
|
|
81
101
|
};
|
|
82
|
-
|
|
83
|
-
|
|
102
|
+
/** Fetches sources declared by synced catalogs until no new sources remain (OFTR-004.6.7). */
|
|
103
|
+
const syncTransitiveClosure = (input) => {
|
|
104
|
+
const messages = [];
|
|
105
|
+
const results = [];
|
|
106
|
+
const visited = new Set(input.directRemoteSources.map(encodeRemoteSourceSelection));
|
|
107
|
+
let frontier = input.sourcePhase.readableSources;
|
|
108
|
+
while (frontier.length > 0) {
|
|
109
|
+
const discovered = [];
|
|
110
|
+
for (const parent of frontier) {
|
|
111
|
+
const checkoutRoot = createRemoteRepositoryCachePath(input.homeDirectory, parent, input.cacheDirectory);
|
|
112
|
+
// An absolute or escaping `path:` on a directly configured source throws here; skip it rather
|
|
113
|
+
// than crash sync (transitive sources are path-less and never reach this).
|
|
114
|
+
let payloadRoot;
|
|
115
|
+
try {
|
|
116
|
+
payloadRoot = resolveSourcePayloadRoot(checkoutRoot, parent);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const declared = readDeclaredRemoteSources(payloadRoot, checkoutRoot, formatRemoteSourceDisplay(parent));
|
|
122
|
+
messages.push(...declared.warnings.map((warning) => `warning: ${warning}`));
|
|
123
|
+
for (const entry of declared.sources) {
|
|
124
|
+
const key = encodeRemoteSourceSelection(entry.source);
|
|
125
|
+
if (visited.has(key))
|
|
126
|
+
continue;
|
|
127
|
+
visited.add(key);
|
|
128
|
+
discovered.push(entry.source);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (discovered.length === 0)
|
|
132
|
+
break;
|
|
133
|
+
const phase = syncPhase({
|
|
134
|
+
kind: 'transitive',
|
|
135
|
+
sources: discovered,
|
|
136
|
+
homeDirectory: input.homeDirectory,
|
|
137
|
+
cacheDirectory: input.cacheDirectory,
|
|
138
|
+
gate: input.gate,
|
|
139
|
+
syncRepository: input.syncRepository,
|
|
140
|
+
validate: validateSourceCheckout,
|
|
141
|
+
});
|
|
142
|
+
messages.push(...phase.messages, ...phase.results.map(formatResult));
|
|
143
|
+
results.push(...phase.results);
|
|
144
|
+
frontier = phase.readableSources;
|
|
145
|
+
}
|
|
146
|
+
return { messages, results };
|
|
147
|
+
};
|
|
148
|
+
const finishSync = (mergedIssues, remoteSettingsPhase, sourcePhase, transitiveClosure) => {
|
|
149
|
+
const results = [...remoteSettingsPhase.results, ...sourcePhase.results, ...transitiveClosure.results];
|
|
84
150
|
const settingsMessages = mergedIssues.length === 0
|
|
85
151
|
? []
|
|
86
152
|
: formatSettingsIssues('Merged settings contain errors after remote-settings synchronization.', mergedIssues);
|
|
@@ -90,6 +156,7 @@ const finishSync = (mergedIssues, remoteSettingsPhase, sourcePhase) => {
|
|
|
90
156
|
...settingsMessages,
|
|
91
157
|
...sourcePhase.messages,
|
|
92
158
|
...sourcePhase.results.map(formatResult),
|
|
159
|
+
...transitiveClosure.messages,
|
|
93
160
|
];
|
|
94
161
|
if (results.length === 0 && mergedIssues.length === 0) {
|
|
95
162
|
messages.push('No remote sources are configured.');
|
|
@@ -100,7 +167,7 @@ const finishSync = (mergedIssues, remoteSettingsPhase, sourcePhase) => {
|
|
|
100
167
|
results,
|
|
101
168
|
};
|
|
102
169
|
};
|
|
103
|
-
/** Validates
|
|
170
|
+
/** Validates settings, syncs remote settings, reloads, syncs remote sources, then their closure. */
|
|
104
171
|
export const executeSyncCommand = (input, dependencies = {}) => {
|
|
105
172
|
const local = loadSettings(discoverSettingsLoadPlan(input));
|
|
106
173
|
if (local.issues.length > 0) {
|
|
@@ -116,27 +183,50 @@ export const executeSyncCommand = (input, dependencies = {}) => {
|
|
|
116
183
|
classifier: dependencies.classifier,
|
|
117
184
|
prompt: dependencies.prompt,
|
|
118
185
|
});
|
|
186
|
+
const syncRepository = dependencies.syncRepository ?? syncRemoteRepositoryAtomically;
|
|
119
187
|
const remoteSettingsPhase = syncPhase({
|
|
120
188
|
kind: 'remote_settings',
|
|
189
|
+
// Non-null is safe: `emptySettings()` (Settings.ts) always defaults `remoteSettings` to `[]`.
|
|
121
190
|
sources: local.settings.remoteSettings,
|
|
122
191
|
homeDirectory: input.homeDirectory,
|
|
123
192
|
cacheDirectory: local.settings.cacheDirectory,
|
|
124
193
|
gate,
|
|
194
|
+
syncRepository,
|
|
125
195
|
validate: validateRemoteSettingsCheckout,
|
|
126
196
|
});
|
|
127
197
|
const merged = loadSettingsWithCachedRemoteSettings(input, {
|
|
128
198
|
remoteSettingsReferences: remoteSettingsPhase.allowedSources,
|
|
129
199
|
localSettings: local,
|
|
130
200
|
});
|
|
201
|
+
// Non-null is safe: `emptySettings()` (Settings.ts) always defaults `sources` to `[]`. Deduplicate
|
|
202
|
+
// exact-duplicate configured sources so an identical entry listed twice is fetched once, not twice
|
|
203
|
+
// (OFTR-004.6.6); distinct subpaths of one repo+ref stay distinct (path-aware key).
|
|
204
|
+
const seenSources = new Set();
|
|
205
|
+
const directRemoteSources = merged.settings.sources.filter(isRemoteSource).filter((source) => {
|
|
206
|
+
const key = encodeRemoteSourceSelection(source);
|
|
207
|
+
if (seenSources.has(key))
|
|
208
|
+
return false;
|
|
209
|
+
seenSources.add(key);
|
|
210
|
+
return true;
|
|
211
|
+
});
|
|
131
212
|
const sourcePhase = syncPhase({
|
|
132
213
|
kind: 'source',
|
|
133
|
-
sources:
|
|
214
|
+
sources: directRemoteSources,
|
|
134
215
|
homeDirectory: input.homeDirectory,
|
|
135
216
|
cacheDirectory: merged.settings.cacheDirectory,
|
|
136
217
|
gate,
|
|
218
|
+
syncRepository,
|
|
137
219
|
validate: validateSourceCheckout,
|
|
138
220
|
});
|
|
139
|
-
|
|
221
|
+
const transitiveClosure = syncTransitiveClosure({
|
|
222
|
+
homeDirectory: input.homeDirectory,
|
|
223
|
+
cacheDirectory: merged.settings.cacheDirectory,
|
|
224
|
+
gate,
|
|
225
|
+
syncRepository,
|
|
226
|
+
directRemoteSources,
|
|
227
|
+
sourcePhase,
|
|
228
|
+
});
|
|
229
|
+
return finishSync(merged.issues, remoteSettingsPhase, sourcePhase, transitiveClosure);
|
|
140
230
|
};
|
|
141
231
|
export const createSyncCommand = (dependencies = {}) => ({
|
|
142
232
|
name: 'sync',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SyncCommand.js","sourceRoot":"","sources":["../../../src/cli/commands/SyncCommand.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,oCAAoC,EACpC,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"SyncCommand.js","sourceRoot":"","sources":["../../../src/cli/commands/SyncCommand.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,oCAAoC,EACpC,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,8BAA8B,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClG,OAAO,EACL,kCAAkC,GAInC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,+BAA+B,EAC/B,2BAA2B,EAC3B,yBAAyB,EACzB,cAAc,EACd,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAE/E,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AA6CrF,MAAM,oBAAoB,GAAG,CAAC,OAAe,EAAE,MAAoC,EAAqB,EAAE,CAAC;IACzG,OAAO;IACP,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;CAClE,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,MAAwB,EAAU,EAAE,CACxD,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,GAAG,OAAO,MAAM,CAAC,SAAS,GACnE,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,GACzD,EAAE,CAAC;AAEL,MAAM,iBAAiB,GAAG,CAAC,KAAc,EAAU,EAAE;IACnD,0EAA0E;IAC1E,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IACjD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,8BAA8B,GAAG,CAAC,cAAsB,EAAE,MAA+B,EAAU,EAAE;IACzG,MAAM,YAAY,GAAG,yBAAyB,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5E,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,IAAI,4CAA4C,CAAC,CAAC;IACpG,CAAC;IACD,MAAM,MAAM,GAAG,iBAAiB,CAAC,sBAAsB,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC;IACpG,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,wCAAwC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/G,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAAC,cAAsB,EAAE,MAA6B,EAAU,EAAE;IAC/F,MAAM,KAAK,GAAG,iBAAiB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IACxD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,2BAA2B,MAAM,CAAC,IAAI,4CAA4C,CAAC,CAAC;IACtG,CAAC;IAED,iGAAiG;IACjG,oGAAoG;IACpG,qGAAqG;IACrG,MAAM,QAAQ,GAAG,oBAAoB,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9G,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IAC1E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,8BAA8B,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7G,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,CAAC;AACzB,CAAC,CAAC;AAYF,kGAAkG;AAClG,MAAM,SAAS,GAAG,CAAkC,KAAwB,EAAsB,EAAE;IAClG,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IACrE,kGAAkG;IAClG,oFAAoF;IACpF,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAA6D,EAAE;QAC5G,MAAM,SAAS,GAAG,+BAA+B,CAAC,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;QACrG,MAAM,GAAG,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;QAE9C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;gBAClC,SAAS;gBACT,MAAM;gBACN,QAAQ,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;aACrE,CAAC,CAAC;YACH,OAAO;gBACL,MAAM;gBACN,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,GAAG;oBACH,SAAS;oBACT,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,OAAO,EAAE,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,MAAM,CAAC,QAAQ,aAAa;iBAC5F;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,MAAM;gBACN,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,GAAG;oBACH,SAAS;oBACT,MAAM,EAAE,QAAQ;oBAChB,OAAO,EAAE,+BAA+B,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;iBACnE;aACF,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE;YACP,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1E,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;SAC/B;QACD,+FAA+F;QAC/F,yFAAyF;QACzF,6FAA6F;QAC7F,+DAA+D;QAC/D,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;KAC/G,CAAC;AACJ,CAAC,CAAC;AAiBF,8FAA8F;AAC9F,MAAM,qBAAqB,GAAG,CAAC,KAA6B,EAA2B,EAAE;IACvF,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC,CAAC;IAEpF,IAAI,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC;IACjD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,UAAU,GAA4B,EAAE,CAAC;QAE/C,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC9B,MAAM,YAAY,GAAG,+BAA+B,CAAC,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;YACxG,8FAA8F;YAC9F,2EAA2E;YAC3E,IAAI,WAAmB,CAAC;YACxB,IAAI,CAAC;gBACH,WAAW,GAAG,wBAAwB,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAC/D,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,yBAAyB,CAAC,WAAW,EAAE,YAAY,EAAE,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC;YACzG,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC,CAAC;YAE5E,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACrC,MAAM,GAAG,GAAG,2BAA2B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACtD,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC/B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACjB,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAEnC,MAAM,KAAK,GAAG,SAAS,CAAC;YACtB,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,UAAU;YACnB,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,sBAAsB;SACjC,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,QAAQ,GAAG,KAAK,CAAC,eAAe,CAAC;IACnC,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CACjB,YAA0C,EAC1C,mBAA6D,EAC7D,WAAmD,EACnD,iBAA0C,EACvB,EAAE;IACrB,MAAM,OAAO,GAAG,CAAC,GAAG,mBAAmB,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACvG,MAAM,gBAAgB,GACpB,YAAY,CAAC,MAAM,KAAK,CAAC;QACvB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,oBAAoB,CAAC,uEAAuE,EAAE,YAAY,CAAC,CAAC;IAClH,MAAM,QAAQ,GAAG;QACf,GAAG,mBAAmB,CAAC,QAAQ;QAC/B,GAAG,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAChD,GAAG,gBAAgB;QACnB,GAAG,WAAW,CAAC,QAAQ;QACvB,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACxC,GAAG,iBAAiB,CAAC,QAAQ;KAC9B,CAAC;IAEF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,QAAQ,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IACrD,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjG,QAAQ;QACR,OAAO;KACR,CAAC;AACJ,CAAC,CAAC;AAEF,oGAAoG;AACpG,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAuB,EACvB,eAAiH,EAAE,EAChG,EAAE;IACrB,MAAM,KAAK,GAAG,YAAY,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,QAAQ,EAAE,CAAC;YACX,QAAQ,EAAE,oBAAoB,CAAC,2DAA2D,EAAE,KAAK,CAAC,MAAM,CAAC;YACzG,OAAO,EAAE,EAAE;SACZ,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GACR,YAAY,CAAC,kBAAkB;QAC/B,kCAAkC,CAAC;YACjC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,UAAU,EAAE,YAAY,CAAC,UAAU;YACnC,MAAM,EAAE,YAAY,CAAC,MAAM;SAC5B,CAAC,CAAC;IACL,MAAM,cAAc,GAAG,YAAY,CAAC,cAAc,IAAI,8BAA8B,CAAC;IACrF,MAAM,mBAAmB,GAAG,SAAS,CAAC;QACpC,IAAI,EAAE,iBAAiB;QACvB,8FAA8F;QAC9F,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAe;QACvC,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc;QAC7C,IAAI;QACJ,cAAc;QACd,QAAQ,EAAE,8BAA8B;KACzC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,oCAAoC,CAAC,KAAK,EAAE;QACzD,wBAAwB,EAAE,mBAAmB,CAAC,cAAc;QAC5D,aAAa,EAAE,KAAK;KACrB,CAAC,CAAC;IACH,mGAAmG;IACnG,mGAAmG;IACnG,oFAAoF;IACpF,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,MAAM,mBAAmB,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;QAC5F,MAAM,GAAG,GAAG,2BAA2B,CAAC,MAAM,CAAC,CAAC;QAChD,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACvC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,SAAS,CAAC;QAC5B,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,mBAAmB;QAC5B,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,cAAc;QAC9C,IAAI;QACJ,cAAc;QACd,QAAQ,EAAE,sBAAsB;KACjC,CAAC,CAAC;IACH,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;QAC9C,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,cAAc;QAC9C,IAAI;QACJ,cAAc;QACd,mBAAmB;QACnB,WAAW;KACZ,CAAC,CAAC;IACH,OAAO,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACxF,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,eAAwC,EAAE,EAAiB,EAAE,CAAC,CAAC;IAC/F,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,0EAA0E;IACvF,QAAQ,CAAC,OAAgB;QACvB,OAAO,CAAC,UAAU,CAChB,IAAI,OAAO,CAAC,MAAM,CAAC;aAChB,WAAW,CAAC,0EAA0E,CAAC;aACvF,MAAM,CAAC,GAAG,EAAE;YACX,MAAM,MAAM,GAAG,kBAAkB,CAC/B;gBACE,aAAa,EAAE,oBAAoB,CAAC,YAAY,CAAC,aAAa,CAAC;gBAC/D,gBAAgB,EAAE,uBAAuB,CAAC,YAAY,CAAC,gBAAgB,CAAC;aACzE,EACD,YAAY,CACb,CAAC;YACF,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACtC,uFAAuF;gBACvF,CAAC,YAAY,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;YACnD,CAAC;YACD,6EAA6E;YAC7E,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC;gBAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChE,CAAC,CAAC,CACL,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -18,13 +18,23 @@ export interface EnsurePiExtensionsResult {
|
|
|
18
18
|
interface PiExtensionSource {
|
|
19
19
|
readonly source: string;
|
|
20
20
|
readonly installSegments: readonly string[];
|
|
21
|
-
/** Exact semver to verify against a cached install; undefined for ranges
|
|
21
|
+
/** Exact semver to verify against a cached npm install; undefined for ranges and tags. */
|
|
22
22
|
readonly pinnedVersion?: string;
|
|
23
|
+
/** The `@ref` of a git specifier, to verify against a cached checkout; undefined when unpinned. */
|
|
24
|
+
readonly pinnedGitRef?: string;
|
|
23
25
|
}
|
|
24
26
|
/** Translates an Outfitter extension specifier to a pi `install` source + its cache install path. */
|
|
25
27
|
export declare const mapSpecifierToPiSource: (specifier: string) => PiExtensionSource | {
|
|
26
28
|
readonly unsupported: string;
|
|
27
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Defense in depth behind segment validation: refuses an install dir that resolves outside the
|
|
32
|
+
* cache root before any filesystem access (the stale-git path runs `rm -rf` on the install dir).
|
|
33
|
+
* Strict containment also keeps the `<installDir>.outfitter-ref.json` marker sibling inside the
|
|
34
|
+
* cache, since a contained install dir is at least one level below the root. Exported for tests;
|
|
35
|
+
* segment validation in mapSpecifierToPiSource should make this unreachable.
|
|
36
|
+
*/
|
|
37
|
+
export declare const assertInstallDirInsideCache: (installDir: string, cacheAgentDir: string, specifier: string) => void;
|
|
28
38
|
/** Ensures each pi extension is cached (installing when online) and returns its load directory. */
|
|
29
39
|
export declare const ensurePiExtensions: (specifiers: readonly string[], input: EnsurePiExtensionsInput) => Promise<EnsurePiExtensionsResult>;
|
|
30
40
|
export {};
|
|
@@ -5,10 +5,43 @@
|
|
|
5
5
|
// npm: <cacheAgentDir>/npm/node_modules/<pkg>
|
|
6
6
|
// Outfitter's `git:`/`npm:` specifiers are already pi's `install` source grammar, so the source is
|
|
7
7
|
// passed through unchanged; only the install directory is reconstructed to check the cache.
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
//
|
|
9
|
+
// A cached git checkout is only trusted when it still matches the specifier's `@ref` pin:
|
|
10
|
+
// - full-SHA ref: `git -C <installDir> rev-parse HEAD` is compared against the pin (offline-safe,
|
|
11
|
+
// no network); a mismatch or an unreadable HEAD reinstalls when online.
|
|
12
|
+
// - branch/tag ref: the ref string is recorded in a `<installDir>.outfitter-ref.json` marker at
|
|
13
|
+
// install time and only a *changed* ref string triggers a reinstall — a moved remote branch tip
|
|
14
|
+
// is deliberately not chased, so pinned launches never hit the network.
|
|
15
|
+
// - branch/tag ref with no marker (a pre-marker cache): served as-is when offline (no evidence it
|
|
16
|
+
// is wrong), refreshed once when online, which writes the marker.
|
|
17
|
+
// Offline, a checkout that provably mismatches its pin is dropped with a warning — the same
|
|
18
|
+
// severity the offline path already applies to a missing extension (fatal only under `--strict`).
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import { join, resolve, sep } from 'node:path';
|
|
10
21
|
import { launchThroughSpawn, spawnLauncher } from '../agents/AgentLaunch.js';
|
|
22
|
+
import { runGit } from '../sources/GitRepository.js';
|
|
11
23
|
const exactSemverPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
|
|
24
|
+
const fullShaPattern = /^[0-9a-f]{40}$/iu;
|
|
25
|
+
// Specifier text becomes filesystem path segments (and the stale-reinstall path runs `rm -rf` on
|
|
26
|
+
// the joined result), so a segment that traverses (`..`, `.`) or smuggles a separator (`\`, which
|
|
27
|
+
// `join` does not split but Windows resolves) must be rejected before any path is built.
|
|
28
|
+
const unsafePathSegment = (segment) => segment === '.' || segment === '..' || segment.includes('\\');
|
|
29
|
+
const mapGitSpecifier = (specifier) => {
|
|
30
|
+
const rest = specifier.slice('git:'.length);
|
|
31
|
+
const pathPart = rest.includes('@') ? rest.slice(0, rest.lastIndexOf('@')) : rest;
|
|
32
|
+
const ref = rest.includes('@') ? rest.slice(rest.lastIndexOf('@') + 1) : '';
|
|
33
|
+
const segments = pathPart.split('/').filter((part) => part !== '');
|
|
34
|
+
if (segments.length < 2)
|
|
35
|
+
return { unsupported: `extension '${specifier}' is not a valid git source` };
|
|
36
|
+
if (segments.some(unsafePathSegment)) {
|
|
37
|
+
return { unsupported: `extension '${specifier}' contains an unsafe path segment` };
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
source: specifier,
|
|
41
|
+
installSegments: ['git', ...segments],
|
|
42
|
+
pinnedGitRef: ref === '' ? undefined : ref,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
12
45
|
/** Translates an Outfitter extension specifier to a pi `install` source + its cache install path. */
|
|
13
46
|
export const mapSpecifierToPiSource = (specifier) => {
|
|
14
47
|
if (specifier.startsWith('npm:')) {
|
|
@@ -16,6 +49,9 @@ export const mapSpecifierToPiSource = (specifier) => {
|
|
|
16
49
|
const name = rest.replace(/@[^@/]+$/u, ''); // strip a trailing @version, keep a scope's leading @
|
|
17
50
|
if (name === '')
|
|
18
51
|
return { unsupported: `extension '${specifier}' has no package name` };
|
|
52
|
+
if (name.split('/').some(unsafePathSegment)) {
|
|
53
|
+
return { unsupported: `extension '${specifier}' contains an unsafe path segment` };
|
|
54
|
+
}
|
|
19
55
|
const version = rest.length > name.length ? rest.slice(name.length + 1) : undefined;
|
|
20
56
|
return {
|
|
21
57
|
source: specifier,
|
|
@@ -23,14 +59,8 @@ export const mapSpecifierToPiSource = (specifier) => {
|
|
|
23
59
|
pinnedVersion: version !== undefined && exactSemverPattern.test(version) ? version : undefined,
|
|
24
60
|
};
|
|
25
61
|
}
|
|
26
|
-
if (specifier.startsWith('git:'))
|
|
27
|
-
|
|
28
|
-
const pathPart = rest.includes('@') ? rest.slice(0, rest.lastIndexOf('@')) : rest;
|
|
29
|
-
const segments = pathPart.split('/').filter((part) => part !== '');
|
|
30
|
-
if (segments.length < 2)
|
|
31
|
-
return { unsupported: `extension '${specifier}' is not a valid git source` };
|
|
32
|
-
return { source: specifier, installSegments: ['git', ...segments] };
|
|
33
|
-
}
|
|
62
|
+
if (specifier.startsWith('git:'))
|
|
63
|
+
return mapGitSpecifier(specifier);
|
|
34
64
|
return { unsupported: `extension '${specifier}' uses an unsupported source (only git: and npm: project to pi)` };
|
|
35
65
|
};
|
|
36
66
|
const installedVersion = (installDir) => {
|
|
@@ -42,6 +72,88 @@ const installedVersion = (installDir) => {
|
|
|
42
72
|
return undefined;
|
|
43
73
|
}
|
|
44
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* Defense in depth behind segment validation: refuses an install dir that resolves outside the
|
|
77
|
+
* cache root before any filesystem access (the stale-git path runs `rm -rf` on the install dir).
|
|
78
|
+
* Strict containment also keeps the `<installDir>.outfitter-ref.json` marker sibling inside the
|
|
79
|
+
* cache, since a contained install dir is at least one level below the root. Exported for tests;
|
|
80
|
+
* segment validation in mapSpecifierToPiSource should make this unreachable.
|
|
81
|
+
*/
|
|
82
|
+
export const assertInstallDirInsideCache = (installDir, cacheAgentDir, specifier) => {
|
|
83
|
+
const root = resolve(cacheAgentDir);
|
|
84
|
+
if (!resolve(installDir).startsWith(root + sep)) {
|
|
85
|
+
throw new Error(`extension '${specifier}' resolves outside the extension cache; refusing to touch it.`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/** Marker recording which git ref an install satisfied, kept beside (never inside) the checkout. */
|
|
89
|
+
const refMarkerPath = (installDir) => `${installDir}.outfitter-ref.json`;
|
|
90
|
+
const cachedGitHead = (installDir) => {
|
|
91
|
+
try {
|
|
92
|
+
return runGit(['-C', installDir, 'rev-parse', 'HEAD']);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const recordedInstallRef = (installDir) => {
|
|
99
|
+
try {
|
|
100
|
+
const marker = JSON.parse(readFileSync(refMarkerPath(installDir), 'utf8'));
|
|
101
|
+
return marker.ref;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
/** After installing a branch/tag-pinned git extension, records the ref (and resolved SHA). */
|
|
108
|
+
const recordInstalledGitRef = (installDir, mapped) => {
|
|
109
|
+
if (mapped.pinnedGitRef === undefined || fullShaPattern.test(mapped.pinnedGitRef))
|
|
110
|
+
return;
|
|
111
|
+
const marker = { ref: mapped.pinnedGitRef, headSha: cachedGitHead(installDir) };
|
|
112
|
+
writeFileSync(refMarkerPath(installDir), JSON.stringify(marker));
|
|
113
|
+
};
|
|
114
|
+
// Remove-then-install for a stale git pin: `pi install` owns the directory layout, so
|
|
115
|
+
// GitRepository's atomic fetch-and-swap cannot apply here. A failure between the remove and the
|
|
116
|
+
// install leaves neither a directory nor a marker, which the next run detects as plain missing.
|
|
117
|
+
// The npm path keeps its original behavior: `pi install` is spawned over the existing dir.
|
|
118
|
+
const removeStaleGitInstall = (installDir, mapped) => {
|
|
119
|
+
if (mapped.pinnedGitRef === undefined)
|
|
120
|
+
return;
|
|
121
|
+
rmSync(installDir, { recursive: true, force: true });
|
|
122
|
+
rmSync(refMarkerPath(installDir), { force: true });
|
|
123
|
+
};
|
|
124
|
+
/** Compares a cached checkout against its `@ref` pin per the policy in the file header. */
|
|
125
|
+
const gitCacheStatus = (installDir, ref) => {
|
|
126
|
+
if (fullShaPattern.test(ref)) {
|
|
127
|
+
const head = cachedGitHead(installDir);
|
|
128
|
+
return {
|
|
129
|
+
state: head?.toLowerCase() === ref.toLowerCase() ? 'fresh' : 'stale',
|
|
130
|
+
found: head ?? 'no readable git HEAD',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const recorded = recordedInstallRef(installDir);
|
|
134
|
+
if (recorded === undefined)
|
|
135
|
+
return { state: 'unverified', found: 'no recorded install ref' };
|
|
136
|
+
return { state: recorded === ref ? 'fresh' : 'stale', found: recorded };
|
|
137
|
+
};
|
|
138
|
+
/** Decides whether an existing install satisfies the specifier's pin (git ref or exact semver). */
|
|
139
|
+
const evaluateCachedInstall = (installDir, mapped, offline) => {
|
|
140
|
+
if (!existsSync(installDir))
|
|
141
|
+
return { serve: false };
|
|
142
|
+
if (mapped.pinnedGitRef === undefined) {
|
|
143
|
+
return { serve: mapped.pinnedVersion === undefined || installedVersion(installDir) === mapped.pinnedVersion };
|
|
144
|
+
}
|
|
145
|
+
const status = gitCacheStatus(installDir, mapped.pinnedGitRef);
|
|
146
|
+
if (status.state === 'fresh' || (status.state === 'unverified' && offline))
|
|
147
|
+
return { serve: true };
|
|
148
|
+
if (offline) {
|
|
149
|
+
return {
|
|
150
|
+
serve: false,
|
|
151
|
+
staleWarning: `extension '${mapped.source}' is cached at the wrong revision ` +
|
|
152
|
+
`(pinned ${mapped.pinnedGitRef}, found ${status.found}) and cannot be reinstalled offline.`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return { serve: false };
|
|
156
|
+
};
|
|
45
157
|
/* v8 ignore start -- real `pi install` subprocess; ensurePiExtensions is unit-tested with a fake spawner. */
|
|
46
158
|
const defaultSpawner = ({ source, cacheAgentDir }) => launchThroughSpawn(spawnLauncher, {
|
|
47
159
|
command: 'pi',
|
|
@@ -54,12 +166,15 @@ const ensureOneExtension = async (specifier, input, spawn) => {
|
|
|
54
166
|
if ('unsupported' in mapped)
|
|
55
167
|
return { warning: mapped.unsupported };
|
|
56
168
|
const installDir = join(input.cacheAgentDir, ...mapped.installSegments);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (
|
|
169
|
+
assertInstallDirInsideCache(installDir, input.cacheAgentDir, specifier);
|
|
170
|
+
const decision = evaluateCachedInstall(installDir, mapped, input.offline);
|
|
171
|
+
if (decision.serve)
|
|
60
172
|
return { loadDir: installDir };
|
|
173
|
+
if (decision.staleWarning !== undefined)
|
|
174
|
+
return { warning: decision.staleWarning };
|
|
61
175
|
if (input.offline)
|
|
62
176
|
return { warning: `extension '${specifier}' is not cached and cannot be installed offline.` };
|
|
177
|
+
removeStaleGitInstall(installDir, mapped);
|
|
63
178
|
mkdirSync(input.cacheAgentDir, { recursive: true });
|
|
64
179
|
try {
|
|
65
180
|
const exitCode = await spawn({ source: mapped.source, cacheAgentDir: input.cacheAgentDir });
|
|
@@ -70,6 +185,7 @@ const ensureOneExtension = async (specifier, input, spawn) => {
|
|
|
70
185
|
catch (error) {
|
|
71
186
|
return { warning: `extension '${specifier}' failed to install (${String(error)}).` };
|
|
72
187
|
}
|
|
188
|
+
recordInstalledGitRef(installDir, mapped);
|
|
73
189
|
return { loadDir: installDir };
|
|
74
190
|
};
|
|
75
191
|
/** Ensures each pi extension is cached (installing when online) and returns its load directory. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PiExtensionCache.js","sourceRoot":"","sources":["../../src/extensions/PiExtensionCache.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,qGAAqG;AACrG,oFAAoF;AACpF,oDAAoD;AACpD,iDAAiD;AACjD,mGAAmG;AACnG,4FAA4F;AAC5F,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"PiExtensionCache.js","sourceRoot":"","sources":["../../src/extensions/PiExtensionCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface CodexMcpProjection {
|
|
2
|
+
readonly args: readonly string[];
|
|
3
|
+
readonly warnings: readonly string[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Codex CLI `-c` values are TOML, not JSON. HTTP header values written as `${ENV_NAME}` are
|
|
7
|
+
* translated to `env_http_headers`, and `Authorization: Bearer ${ENV_NAME}` becomes
|
|
8
|
+
* `bearer_token_env_var`, so the secret itself stays out of argv. Other values must use Codex's
|
|
9
|
+
* literal `http_headers` because the protocol definition contains no environment reference.
|
|
10
|
+
*/
|
|
11
|
+
export declare const projectCodexMcpServers: (selectedIds: readonly string[], servers: Readonly<Record<string, unknown>>) => CodexMcpProjection;
|