@adea-ai/plugins 3.0.9 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -11
- package/dist/agent-plugins-catalog.d.ts +2 -3
- package/dist/agent-plugins-catalog.js +2 -10
- package/dist/agent-plugins-catalog.js.map +1 -1
- package/dist/display-name.js +3 -2
- package/dist/display-name.js.map +1 -1
- package/dist/harness.d.ts +50 -9
- package/dist/harness.js +190 -147
- package/dist/harness.js.map +1 -1
- package/dist/icons.js +5 -27
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +47 -37
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +0 -40
- package/dist/schema.js +0 -36
- package/dist/schema.js.map +1 -1
- package/dist/site-icons.js +18 -7
- package/dist/site-icons.js.map +1 -1
- package/dist/source-digest.d.ts +2 -0
- package/dist/source-digest.js +12 -0
- package/dist/source-digest.js.map +1 -0
- package/dist/sources.js +2 -2
- package/dist/sources.js.map +1 -1
- package/native/README.md +24 -0
- package/native/build-provenance.txt +5 -5
- package/native/staged-reader-win32-arm64.node +0 -0
- package/native/staged-reader-win32-x64.node +0 -0
- package/package.json +3 -10
- package/src/agent-plugins-catalog.ts +3 -10
- package/src/catalog-installation.test.ts +1 -1
- package/src/display-name.test.ts +8 -0
- package/src/display-name.ts +3 -2
- package/src/{agent-plugins-harness.test.ts → harness.test.ts} +1 -1
- package/src/harness.ts +249 -170
- package/src/icons.test.ts +5 -0
- package/src/icons.ts +6 -29
- package/src/index.test.ts +150 -40
- package/src/index.ts +49 -41
- package/src/portable-catalog.test.ts +8 -5
- package/src/schema.ts +0 -40
- package/src/site-icon-fallback.test.ts +36 -19
- package/src/site-icons.test.ts +31 -0
- package/src/site-icons.ts +28 -7
- package/src/source-digest.test.ts +44 -0
- package/src/source-digest.ts +14 -0
- package/src/sources-json.test.ts +36 -0
- package/src/sources.ts +2 -2
- package/src/upstream-headers.test.ts +31 -7
- package/dist/agent-plugins-harness.d.ts +0 -54
- package/dist/agent-plugins-harness.js +0 -212
- package/dist/agent-plugins-harness.js.map +0 -1
- package/src/agent-plugins-harness.ts +0 -281
package/src/site-icons.ts
CHANGED
|
@@ -52,7 +52,10 @@ export function isPublicHostname(hostname: string): boolean {
|
|
|
52
52
|
const host = hostname.toLowerCase().replace(/^\[|\]$/g, '')
|
|
53
53
|
if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local')) return false
|
|
54
54
|
if (host.endsWith('.internal') || host.endsWith('.home.arpa')) return false
|
|
55
|
-
if (
|
|
55
|
+
if (
|
|
56
|
+
host.includes(':') &&
|
|
57
|
+
(host === '::1' || host.startsWith('fe80:') || host.startsWith('fc') || host.startsWith('fd'))
|
|
58
|
+
)
|
|
56
59
|
return false
|
|
57
60
|
const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host)
|
|
58
61
|
if (ipv4) {
|
|
@@ -76,16 +79,34 @@ export function isPublicHostname(hostname: string): boolean {
|
|
|
76
79
|
*/
|
|
77
80
|
export function discoverSiteIcons(html: string, baseUrl: string): readonly string[] {
|
|
78
81
|
const candidates: { href: string; rank: number }[] = []
|
|
79
|
-
const linkTags = html.match(/<link\
|
|
82
|
+
const linkTags = html.match(/<link(?=[ \t\r\n\f/>])(?:"[^"]*"|'[^']*'|[^'">])*>/gi) ?? []
|
|
80
83
|
for (const tag of linkTags) {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
const attributes = new Map<string, string>()
|
|
85
|
+
for (const attribute of tag.matchAll(
|
|
86
|
+
/\s+([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g
|
|
87
|
+
)) {
|
|
88
|
+
const name = attribute[1]!.toLowerCase()
|
|
89
|
+
if (!attributes.has(name))
|
|
90
|
+
attributes.set(name, attribute[2] ?? attribute[3] ?? attribute[4] ?? '')
|
|
91
|
+
}
|
|
92
|
+
const rel = new Set((attributes.get('rel') ?? '').toLowerCase().split(/\s+/))
|
|
93
|
+
if (
|
|
94
|
+
!['apple-touch-icon', 'apple-touch-icon-precomposed', 'icon', 'mask-icon'].some((name) =>
|
|
95
|
+
rel.has(name)
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
continue
|
|
99
|
+
const href = attributes.get('href')
|
|
84
100
|
if (!href) continue
|
|
85
|
-
const sizes =
|
|
101
|
+
const sizes = /^(\d+x\d+)(?:\s|$)/i.exec(attributes.get('sizes') ?? '')?.[1]
|
|
86
102
|
const edge = sizes ? Number.parseInt(sizes, 10) : 180
|
|
87
103
|
// Apple touch icons lead; larger declared sizes lead within a group.
|
|
88
|
-
const rank =
|
|
104
|
+
const rank =
|
|
105
|
+
rel.has('apple-touch-icon') || rel.has('apple-touch-icon-precomposed')
|
|
106
|
+
? 0
|
|
107
|
+
: rel.has('mask-icon')
|
|
108
|
+
? 2
|
|
109
|
+
: 1
|
|
89
110
|
candidates.push({ href, rank: rank * 1000 + Math.max(0, 512 - (edge || 180)) })
|
|
90
111
|
}
|
|
91
112
|
const resolved: { url: string; rank: number }[] = []
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { spawnSync } from 'node:child_process'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
5
|
+
import { expect, test } from 'bun:test'
|
|
6
|
+
|
|
7
|
+
// Exercise the public source identity functions as a Node consumer, where the
|
|
8
|
+
// default ICU locale follows the machine rather than Bun's fixed default.
|
|
9
|
+
test('source digests agree across consumer locales and equivalent Unicode path order', async () => {
|
|
10
|
+
const directory = await mkdtemp(join(import.meta.dirname, '.source-digest-'))
|
|
11
|
+
try {
|
|
12
|
+
const entry = join(directory, 'entry.ts')
|
|
13
|
+
const output = join(directory, 'consumer.mjs')
|
|
14
|
+
await writeFile(
|
|
15
|
+
entry,
|
|
16
|
+
`export { bytesDigest } from '../index.ts'\nexport { sourceDigest } from '../agent-plugins-catalog.ts'\n`
|
|
17
|
+
)
|
|
18
|
+
const build = await Bun.build({ entrypoints: [entry], target: 'node', packages: 'external' })
|
|
19
|
+
expect(build.success).toBe(true)
|
|
20
|
+
await Bun.write(output, build.outputs[0]!)
|
|
21
|
+
const script = `
|
|
22
|
+
import { bytesDigest, sourceDigest } from ${JSON.stringify(pathToFileURL(output).href)};
|
|
23
|
+
const paths = ['z.txt', 'ä.txt', 'é.txt', 'e\\u0301.txt'];
|
|
24
|
+
const entries = paths.map((path) => [path, new TextEncoder().encode(path)]);
|
|
25
|
+
const forward = new Map(entries), reverse = new Map([...entries].reverse());
|
|
26
|
+
console.log(JSON.stringify([
|
|
27
|
+
bytesDigest(forward), bytesDigest(reverse), sourceDigest(forward), sourceDigest(reverse)
|
|
28
|
+
]));
|
|
29
|
+
`
|
|
30
|
+
const results = ['en_US.UTF-8', 'sv_SE.UTF-8'].map((locale) => {
|
|
31
|
+
const result = spawnSync('node', ['--input-type=module', '-e', script], {
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
env: { ...process.env, LANG: locale, LC_ALL: locale },
|
|
34
|
+
})
|
|
35
|
+
if (result.error) throw result.error
|
|
36
|
+
if (result.status !== 0) throw new Error(result.stderr)
|
|
37
|
+
expect(result.status).toBe(0)
|
|
38
|
+
return JSON.parse(result.stdout) as string[]
|
|
39
|
+
})
|
|
40
|
+
expect(new Set(results.flat()).size).toBe(1)
|
|
41
|
+
} finally {
|
|
42
|
+
await rm(directory, { recursive: true, force: true })
|
|
43
|
+
}
|
|
44
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
/** Shared source-byte identity for catalog provenance and canonical package recipes. */
|
|
4
|
+
export function sourceDigest(files: ReadonlyMap<string, Uint8Array>): string {
|
|
5
|
+
const hash = createHash('sha256')
|
|
6
|
+
// English matches the compiler runtime; ordinal ties distinguish equivalent Unicode names.
|
|
7
|
+
for (const [path, bytes] of [...files].toSorted(
|
|
8
|
+
([a], [b]) => a.localeCompare(b, 'en') || (a < b ? -1 : a > b ? 1 : 0)
|
|
9
|
+
)) {
|
|
10
|
+
hash.update(`${path.length}:${path}:${bytes.byteLength}:`)
|
|
11
|
+
hash.update(bytes)
|
|
12
|
+
}
|
|
13
|
+
return `sha256:${hash.digest('hex')}`
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { parseJsonDocument } from './sources.js'
|
|
3
|
+
|
|
4
|
+
describe('JSON document whitespace', () => {
|
|
5
|
+
test('accepts the four JSON whitespace characters around nested values', () => {
|
|
6
|
+
const text = ' \t\r\n{ "plugins"\t:\r[\ntrue, false, null, 1, "text", {} ] }\r\n'
|
|
7
|
+
expect(parseJsonDocument(text)).toEqual(JSON.parse(text))
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test.each(['\u000b', '\u000c', '\u00a0', '\u1680', '\u2028', '\u2029', '\ufeff'])(
|
|
11
|
+
'rejects non-JSON whitespace %j outside strings',
|
|
12
|
+
(space) => {
|
|
13
|
+
for (const text of [
|
|
14
|
+
`${space}{}`,
|
|
15
|
+
`{}${space}`,
|
|
16
|
+
`{${space}"plugins":[]}`,
|
|
17
|
+
`{"plugins"${space}:[]}`,
|
|
18
|
+
`{"plugins":${space}[]}`,
|
|
19
|
+
`[1${space},2]`,
|
|
20
|
+
`[1,${space}2]`,
|
|
21
|
+
]) {
|
|
22
|
+
expect(() => JSON.parse(text)).toThrow()
|
|
23
|
+
expect(() => parseJsonDocument(text)).toThrow('JSON_MALFORMED')
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
test('preserves Unicode whitespace inside JSON strings', () => {
|
|
29
|
+
const text = JSON.stringify({ value: '\u00a0\u1680\u2028\u2029\ufeff' })
|
|
30
|
+
expect(parseJsonDocument(text)).toEqual(JSON.parse(text))
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('continues rejecting escaped duplicate keys', () => {
|
|
34
|
+
expect(() => parseJsonDocument('{"name":1,"na\\u006de":2}')).toThrow('JSON_DUPLICATE_KEY')
|
|
35
|
+
})
|
|
36
|
+
})
|
package/src/sources.ts
CHANGED
|
@@ -82,7 +82,7 @@ export function parseMarketplaceManifest(input: unknown, source: SourceConfig):
|
|
|
82
82
|
export function parseJsonDocument(text: string): unknown {
|
|
83
83
|
let index = 0
|
|
84
84
|
const skip = () => {
|
|
85
|
-
while (
|
|
85
|
+
while (/[ \t\r\n]/.test(text[index] ?? '')) index += 1
|
|
86
86
|
}
|
|
87
87
|
const string = (): string => {
|
|
88
88
|
const start = index
|
|
@@ -158,7 +158,7 @@ export function parseJsonDocument(text: string): unknown {
|
|
|
158
158
|
}
|
|
159
159
|
if (character === '"') return string()
|
|
160
160
|
const start = index
|
|
161
|
-
while (index < text.length && !/[\
|
|
161
|
+
while (index < text.length && !/[ \t\r\n,\]}]/.test(text[index] ?? '')) index += 1
|
|
162
162
|
const token = text.slice(start, index)
|
|
163
163
|
try {
|
|
164
164
|
return JSON.parse(token) as unknown
|
|
@@ -132,17 +132,41 @@ describe('site-icon fetcher bounds', () => {
|
|
|
132
132
|
}
|
|
133
133
|
})
|
|
134
134
|
|
|
135
|
-
test('honours a caller deadline that has already passed', async () => {
|
|
136
|
-
const mock = captureFetch()
|
|
135
|
+
test('honours a caller deadline that has already passed or expires during backoff', async () => {
|
|
136
|
+
const mock = captureFetch(() => {
|
|
137
|
+
throw new DOMException('Aborted', 'AbortError')
|
|
138
|
+
})
|
|
137
139
|
try {
|
|
138
140
|
const fetcher = createSiteIconFetcher(AbortSignal.abort())
|
|
139
|
-
await fetcher.fetchBytes(VENDOR_ICON)
|
|
141
|
+
await expect(fetcher.fetchBytes(VENDOR_ICON)).rejects.toThrow()
|
|
142
|
+
expect(mock.inits).toHaveLength(1)
|
|
140
143
|
} finally {
|
|
141
144
|
mock.restore()
|
|
142
145
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
|
|
147
|
+
const controller = new AbortController()
|
|
148
|
+
let cancelled = false
|
|
149
|
+
const retry = captureFetch(
|
|
150
|
+
() =>
|
|
151
|
+
new Response(
|
|
152
|
+
new ReadableStream({
|
|
153
|
+
cancel() {
|
|
154
|
+
cancelled = true
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
{ status: 503, headers: { 'retry-after': '0.05' } }
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
const timer = setTimeout(() => controller.abort(), 5)
|
|
161
|
+
try {
|
|
162
|
+
await expect(
|
|
163
|
+
createSiteIconFetcher(controller.signal).fetchBytes(VENDOR_ICON)
|
|
164
|
+
).rejects.toThrow()
|
|
165
|
+
expect(retry.inits).toHaveLength(1)
|
|
166
|
+
expect(cancelled).toBe(true)
|
|
167
|
+
} finally {
|
|
168
|
+
clearTimeout(timer)
|
|
169
|
+
retry.restore()
|
|
170
|
+
}
|
|
147
171
|
})
|
|
148
172
|
})
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { type HarnessProfile, type McpServer, type InstallationPlan } from './agent-plugins-schema.js';
|
|
2
|
-
import type { Catalog } from './schema.js';
|
|
3
|
-
export interface InstallationInput {
|
|
4
|
-
readonly pluginId: string;
|
|
5
|
-
readonly releaseId: string;
|
|
6
|
-
readonly instanceId: string;
|
|
7
|
-
readonly source: InstallationPlan['source'];
|
|
8
|
-
readonly package: unknown;
|
|
9
|
-
readonly profile: HarnessProfile;
|
|
10
|
-
readonly requiredCredentials?: readonly string[];
|
|
11
|
-
readonly requiredConnectors?: readonly string[];
|
|
12
|
-
readonly allowPartial?: boolean;
|
|
13
|
-
}
|
|
14
|
-
export interface CatalogInstallationInput {
|
|
15
|
-
readonly catalog: Catalog;
|
|
16
|
-
readonly pluginId: string;
|
|
17
|
-
readonly releaseId?: string;
|
|
18
|
-
readonly instanceId: string;
|
|
19
|
-
readonly profile: HarnessProfile;
|
|
20
|
-
readonly allowPartial?: boolean;
|
|
21
|
-
}
|
|
22
|
-
/** Resolve the exact catalog release before planning. The caller must also authenticate
|
|
23
|
-
* the catalog publisher and verify its artifact integrity before invoking this API.
|
|
24
|
-
*/
|
|
25
|
-
export declare function createCatalogInstallationPlan(input: CatalogInstallationInput): InstallationPlan;
|
|
26
|
-
/** A declarative proposal, never an execution grant. No brand-based support assumptions. */
|
|
27
|
-
export declare function createInstallationPlan(input: InstallationInput): InstallationPlan;
|
|
28
|
-
export interface RuntimeRoots {
|
|
29
|
-
readonly pluginRoot: string;
|
|
30
|
-
readonly pluginData: string;
|
|
31
|
-
}
|
|
32
|
-
/** Exact, single-pass expansion. Replacement text is never scanned recursively. */
|
|
33
|
-
export declare function expandPluginPaths(value: string, roots: RuntimeRoots): string;
|
|
34
|
-
export type McpBinding = {
|
|
35
|
-
type: 'stdio';
|
|
36
|
-
command: string;
|
|
37
|
-
args: string[];
|
|
38
|
-
cwd: string;
|
|
39
|
-
env: Record<string, string>;
|
|
40
|
-
shell: false;
|
|
41
|
-
} | {
|
|
42
|
-
type: 'streamable-http' | 'sse';
|
|
43
|
-
url: string;
|
|
44
|
-
headers: Record<string, string>;
|
|
45
|
-
redirectPolicy: 'same-origin';
|
|
46
|
-
authorization: 'control-plane';
|
|
47
|
-
};
|
|
48
|
-
/** Produce concrete Control Plane launch/connect descriptors, without launching or connecting. */
|
|
49
|
-
export declare function resolveMcpBinding(input: McpServer, roots: RuntimeRoots, baseEnv?: Readonly<Record<string, string>>, platform?: 'posix' | 'win32'): McpBinding;
|
|
50
|
-
/** Mandatory filesystem preflight for Control Plane callers after safe materialization.
|
|
51
|
-
* Call immediately before activation inside the same protected installation boundary;
|
|
52
|
-
* a separate preflight is not a substitute for preventing concurrent filesystem mutation.
|
|
53
|
-
*/
|
|
54
|
-
export declare function verifyBindingPaths(input: McpServer, roots: RuntimeRoots): Promise<void>;
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { isAbsolute, resolve, relative, sep } from 'node:path';
|
|
2
|
-
import { realpath } from 'node:fs/promises';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
|
-
import { HarnessProfileSchema, InstallationPlanSchema, } from './agent-plugins-schema.js';
|
|
5
|
-
import { verifyPortableCatalog, PACKAGE_METADATA_KEY } from './portable-catalog.js';
|
|
6
|
-
import { verifyAgentPackage, validateServer } from './agent-plugins-catalog.js';
|
|
7
|
-
/** Resolve the exact catalog release before planning. The caller must also authenticate
|
|
8
|
-
* the catalog publisher and verify its artifact integrity before invoking this API.
|
|
9
|
-
*/
|
|
10
|
-
export function createCatalogInstallationPlan(input) {
|
|
11
|
-
verifyPortableCatalog(input.catalog);
|
|
12
|
-
const plugin = input.catalog.plugins.find((entry) => entry.pluginId === input.pluginId);
|
|
13
|
-
if (!plugin)
|
|
14
|
-
throw new Error(`PLUGIN_NOT_FOUND: ${input.pluginId}`);
|
|
15
|
-
const release = plugin.availableReleases.find((entry) => entry.releaseId === (input.releaseId ?? plugin.currentReleaseId));
|
|
16
|
-
if (!release)
|
|
17
|
-
throw new Error('RELEASE_NOT_FOUND');
|
|
18
|
-
if (release.contentResolution !== 'complete')
|
|
19
|
-
throw new Error('COMPLETE_SOURCE_REQUIRED');
|
|
20
|
-
const pkg = release.releaseMetadata[PACKAGE_METADATA_KEY];
|
|
21
|
-
if (pkg === undefined)
|
|
22
|
-
throw new Error('PORTABLE_RESYNC_REQUIRED: run a full catalog sync before planning portable installation');
|
|
23
|
-
return createInstallationPlan({
|
|
24
|
-
pluginId: plugin.pluginId,
|
|
25
|
-
releaseId: release.releaseId,
|
|
26
|
-
instanceId: input.instanceId,
|
|
27
|
-
profile: input.profile,
|
|
28
|
-
package: pkg,
|
|
29
|
-
allowPartial: input.allowPartial === true,
|
|
30
|
-
source: {
|
|
31
|
-
repositoryUrl: release.resolvedRepositoryUrl,
|
|
32
|
-
commitSha: release.resolvedCommitSha,
|
|
33
|
-
pluginSubdirectory: release.pluginSubdirectory,
|
|
34
|
-
contentDigest: release.canonicalContentDigest,
|
|
35
|
-
},
|
|
36
|
-
requiredCredentials: release.requiredCredentials,
|
|
37
|
-
requiredConnectors: release.requiredConnectors,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
function identity(...parts) {
|
|
41
|
-
return createHash('sha256').update(JSON.stringify(parts)).digest('hex');
|
|
42
|
-
}
|
|
43
|
-
/** A declarative proposal, never an execution grant. No brand-based support assumptions. */
|
|
44
|
-
export function createInstallationPlan(input) {
|
|
45
|
-
const profile = HarnessProfileSchema.parse(input.profile);
|
|
46
|
-
const pkg = verifyAgentPackage(input.package);
|
|
47
|
-
if (pkg.sourceDigest !== input.source.contentDigest)
|
|
48
|
-
throw new Error('Package recipe does not match the selected immutable release.');
|
|
49
|
-
if (!input.instanceId || input.instanceId.length > 256)
|
|
50
|
-
throw new Error('A stable installation instanceId is required.');
|
|
51
|
-
const disabled = [];
|
|
52
|
-
for (const item of pkg.nonPortable)
|
|
53
|
-
disabled.push({ component: item.kind, reason: item.reason });
|
|
54
|
-
for (const item of pkg.diagnostics.filter((entry) => entry.severity === 'error'))
|
|
55
|
-
disabled.push({ component: item.path, reason: item.message });
|
|
56
|
-
const nativeEligible = profile.agentPlugins.versions.includes(pkg.specVersion) &&
|
|
57
|
-
pkg.nonPortable.length === 0 &&
|
|
58
|
-
!Object.keys(pkg.manifest?.extensions ?? {}).length;
|
|
59
|
-
const nativeCoverage = (profile.agentPlugins.skills ? pkg.skills.length : 0) +
|
|
60
|
-
Object.values(pkg.mcpServers).filter((server) => profile.agentPlugins.mcpTransports.includes(server.type)).length;
|
|
61
|
-
const adapterCoverage = (profile.components.skillDirectories ? pkg.skills.length : 0) +
|
|
62
|
-
Object.values(pkg.mcpServers).filter((server) => profile.components.mcpTransports.includes(server.type)).length;
|
|
63
|
-
const native = nativeEligible && nativeCoverage > 0 && nativeCoverage >= adapterCoverage;
|
|
64
|
-
const strategy = pkg.status === 'unavailable'
|
|
65
|
-
? 'unavailable'
|
|
66
|
-
: native
|
|
67
|
-
? 'native-agent-plugin'
|
|
68
|
-
: 'component-adapter';
|
|
69
|
-
let skillDirectories = [];
|
|
70
|
-
const servers = new Map();
|
|
71
|
-
if (strategy !== 'unavailable') {
|
|
72
|
-
if (native ? profile.agentPlugins.skills : profile.components.skillDirectories)
|
|
73
|
-
skillDirectories = pkg.skills.map((skill) => skill.path);
|
|
74
|
-
else
|
|
75
|
-
for (const skill of pkg.skills)
|
|
76
|
-
disabled.push({
|
|
77
|
-
component: `skill:${skill.name}`,
|
|
78
|
-
reason: 'The selected adapter cannot expose skill directories.',
|
|
79
|
-
});
|
|
80
|
-
for (const [name, server] of Object.entries(pkg.mcpServers)) {
|
|
81
|
-
if ((native ? profile.agentPlugins.mcpTransports : profile.components.mcpTransports).includes(server.type))
|
|
82
|
-
servers.set(name, server);
|
|
83
|
-
else
|
|
84
|
-
disabled.push({
|
|
85
|
-
component: `mcp:${name}`,
|
|
86
|
-
reason: `No ${server.type} MCP binding on this harness adapter.`,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
const hasContent = skillDirectories.length > 0 || servers.size > 0;
|
|
91
|
-
const compatibility = !hasContent ? 'unsupported' : disabled.length > 0 ? 'partial' : 'full';
|
|
92
|
-
const selectedStrategy = !hasContent || (compatibility === 'partial' && !input.allowPartial) ? 'unavailable' : strategy;
|
|
93
|
-
return InstallationPlanSchema.parse({
|
|
94
|
-
planVersion: 2,
|
|
95
|
-
pluginId: input.pluginId,
|
|
96
|
-
releaseId: input.releaseId,
|
|
97
|
-
instanceId: input.instanceId,
|
|
98
|
-
profile,
|
|
99
|
-
source: input.source,
|
|
100
|
-
package: pkg,
|
|
101
|
-
// Git source identity also pins executable modes, which the v1 byte snapshot omits.
|
|
102
|
-
packageKey: pkg.packageDigest
|
|
103
|
-
? `packages/${identity(input.source.repositoryUrl, input.source.commitSha, input.source.pluginSubdirectory, pkg.packageDigest)}`
|
|
104
|
-
: '',
|
|
105
|
-
// Deliberately excludes release, package digest and harness. Switching harnesses or updating
|
|
106
|
-
// the same approved instance preserves data; distinct scopes use distinct instance IDs.
|
|
107
|
-
dataKey: `instances/${identity(input.pluginId, input.instanceId)}`,
|
|
108
|
-
preserveDataAcrossUpdates: true,
|
|
109
|
-
strategy: selectedStrategy,
|
|
110
|
-
compatibility,
|
|
111
|
-
allowedToActivate: false,
|
|
112
|
-
approvalRequired: true,
|
|
113
|
-
selection: selectedStrategy === 'unavailable'
|
|
114
|
-
? { skillDirectories: [], mcpServers: {} }
|
|
115
|
-
: { skillDirectories, mcpServers: Object.fromEntries(servers) },
|
|
116
|
-
disabled,
|
|
117
|
-
requiredCredentials: [...(input.requiredCredentials ?? [])],
|
|
118
|
-
requiredConnectors: [...(input.requiredConnectors ?? [])],
|
|
119
|
-
preconditions: [
|
|
120
|
-
'Verify catalog integrity/provenance and exact source digest before applying the package recipe.',
|
|
121
|
-
'Use an immutable package root; preserve source file modes and complete skill directories/resources.',
|
|
122
|
-
'Create a writable data directory for this instance; never key it by release or copy credentials between instances.',
|
|
123
|
-
'Control Plane must authorize activation, filesystem/network access, executables and credential bindings.',
|
|
124
|
-
'Recheck the actual harness/runtime/adapter profile and realpath containment immediately before activation.',
|
|
125
|
-
'Native mode registers exactly one package root. Component mode registers only the selected skill directories and MCP bindings, never legacy hooks/config files.',
|
|
126
|
-
'Do not install dependencies or run lifecycle scripts implicitly; review required executables and skill compatibility requirements.',
|
|
127
|
-
'Skill allowed-tools metadata is advisory and never grants permission.',
|
|
128
|
-
'Namespace registrations by installation identity; detect skill/server name collisions instead of overwriting existing harness configuration.',
|
|
129
|
-
'Remote authorization is client-managed. Do not expand remote URLs/headers or forward configured headers to another origin without authorization.',
|
|
130
|
-
],
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
/** Exact, single-pass expansion. Replacement text is never scanned recursively. */
|
|
134
|
-
export function expandPluginPaths(value, roots) {
|
|
135
|
-
return value.replace(/\$\{(PLUGIN_ROOT|PLUGIN_DATA)\}/g, (_match, name) => name === 'PLUGIN_ROOT' ? roots.pluginRoot : roots.pluginData);
|
|
136
|
-
}
|
|
137
|
-
function within(path, root) {
|
|
138
|
-
const suffix = relative(root, path);
|
|
139
|
-
return suffix === '' || (suffix !== '..' && !suffix.startsWith(`..${sep}`) && !isAbsolute(suffix));
|
|
140
|
-
}
|
|
141
|
-
/** Produce concrete Control Plane launch/connect descriptors, without launching or connecting. */
|
|
142
|
-
export function resolveMcpBinding(input, roots, baseEnv = {}, platform = process.platform === 'win32' ? 'win32' : 'posix') {
|
|
143
|
-
const server = validateServer(input);
|
|
144
|
-
if (!isAbsolute(roots.pluginRoot) || !isAbsolute(roots.pluginData))
|
|
145
|
-
throw new Error('Runtime plugin roots must be absolute paths.');
|
|
146
|
-
if (server.type !== 'stdio')
|
|
147
|
-
return {
|
|
148
|
-
type: server.type,
|
|
149
|
-
url: server.url,
|
|
150
|
-
headers: { ...server.headers },
|
|
151
|
-
redirectPolicy: 'same-origin',
|
|
152
|
-
authorization: 'control-plane',
|
|
153
|
-
};
|
|
154
|
-
const command = server.command.startsWith('./')
|
|
155
|
-
? resolve(roots.pluginRoot, server.command)
|
|
156
|
-
: server.command;
|
|
157
|
-
if (server.command.startsWith('./') && !within(command, roots.pluginRoot))
|
|
158
|
-
throw new Error('Executable escapes the plugin root.');
|
|
159
|
-
const cwdInput = server.cwd ?? '${PLUGIN_ROOT}';
|
|
160
|
-
const cwdRoot = cwdInput.startsWith('${PLUGIN_DATA}') ? roots.pluginData : roots.pluginRoot;
|
|
161
|
-
const expanded = expandPluginPaths(cwdInput, roots);
|
|
162
|
-
const cwd = resolve(roots.pluginRoot, expanded);
|
|
163
|
-
if (!within(cwd, cwdRoot))
|
|
164
|
-
throw new Error('Working directory escapes its declared root.');
|
|
165
|
-
const env = {};
|
|
166
|
-
const overlay = (name, value) => {
|
|
167
|
-
if (platform === 'win32')
|
|
168
|
-
for (const existing of Object.keys(env))
|
|
169
|
-
if (existing.toUpperCase() === name.toUpperCase())
|
|
170
|
-
delete env[existing];
|
|
171
|
-
Object.defineProperty(env, name, {
|
|
172
|
-
value,
|
|
173
|
-
enumerable: true,
|
|
174
|
-
configurable: true,
|
|
175
|
-
writable: true,
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
|
-
for (const [key, value] of Object.entries(baseEnv))
|
|
179
|
-
overlay(key, value);
|
|
180
|
-
for (const [key, value] of Object.entries(server.env ?? {}))
|
|
181
|
-
overlay(key, expandPluginPaths(value, roots));
|
|
182
|
-
overlay('PLUGIN_ROOT', roots.pluginRoot);
|
|
183
|
-
overlay('PLUGIN_DATA', roots.pluginData);
|
|
184
|
-
return {
|
|
185
|
-
type: 'stdio',
|
|
186
|
-
command,
|
|
187
|
-
args: (server.args ?? []).map((arg) => expandPluginPaths(arg, roots)),
|
|
188
|
-
cwd,
|
|
189
|
-
env,
|
|
190
|
-
shell: false,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
/** Mandatory filesystem preflight for Control Plane callers after safe materialization.
|
|
194
|
-
* Call immediately before activation inside the same protected installation boundary;
|
|
195
|
-
* a separate preflight is not a substitute for preventing concurrent filesystem mutation.
|
|
196
|
-
*/
|
|
197
|
-
export async function verifyBindingPaths(input, roots) {
|
|
198
|
-
const binding = resolveMcpBinding(input, roots);
|
|
199
|
-
if (binding.type !== 'stdio' || input.type !== 'stdio')
|
|
200
|
-
return;
|
|
201
|
-
const root = await realpath(roots.pluginRoot);
|
|
202
|
-
const data = await realpath(roots.pluginData);
|
|
203
|
-
if (root !== roots.pluginRoot || data !== roots.pluginData)
|
|
204
|
-
throw new Error('Supply canonical filesystem-resolved runtime roots.');
|
|
205
|
-
const cwd = await realpath(binding.cwd);
|
|
206
|
-
const expected = input.cwd?.startsWith('${PLUGIN_DATA}') ? data : root;
|
|
207
|
-
if (!within(cwd, expected))
|
|
208
|
-
throw new Error('Working directory resolves outside its declared root.');
|
|
209
|
-
if (input.command.startsWith('./') && !within(await realpath(binding.command), root))
|
|
210
|
-
throw new Error('Executable resolves outside the package root.');
|
|
211
|
-
}
|
|
212
|
-
//# sourceMappingURL=agent-plugins-harness.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-plugins-harness.js","sourceRoot":"","sources":["../src/agent-plugins-harness.ts"],"names":[],"mappings":"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"}
|