@agentage/extension 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agcid/scope.d.ts +0 -1
- package/dist/agcid/scope.js +0 -1
- package/dist/auth/part-auth.d.ts +18 -0
- package/dist/auth/part-auth.js +61 -0
- package/dist/auth/revoked.d.ts +9 -0
- package/dist/auth/revoked.js +34 -0
- package/dist/auth/route-access.d.ts +2 -0
- package/dist/auth/route-access.js +21 -0
- package/dist/auth/token-check.d.ts +13 -0
- package/dist/auth/token-check.js +26 -0
- package/dist/auth/verify-token.d.ts +18 -0
- package/dist/auth/verify-token.js +68 -0
- package/dist/command/check-cli.js +2 -7
- package/dist/command/run-command.d.ts +1 -0
- package/dist/command/run-command.js +5 -15
- package/dist/context/context.d.ts +4 -0
- package/dist/context/context.js +3 -1
- package/dist/context/test-context.js +23 -17
- package/dist/discovery/discovery.d.ts +8 -2
- package/dist/discovery/discovery.schema.d.ts +14 -2
- package/dist/discovery/discovery.schema.js +7 -3
- package/dist/envelope/envelope.d.ts +2 -0
- package/dist/envelope/envelope.js +1 -0
- package/dist/manifest/entry.d.ts +5 -0
- package/dist/manifest/entry.js +29 -0
- package/dist/manifest/parts.d.ts +1 -0
- package/dist/manifest/parts.js +1 -0
- package/dist/mcp/mcp-app.js +31 -7
- package/dist/mcp/server.js +20 -6
- package/dist/mcp/tools.d.ts +2 -0
- package/dist/mcp/tools.js +4 -0
- package/dist/runtime/part-runners.d.ts +6 -5
- package/dist/runtime/part-runners.js +20 -27
- package/dist/runtime/run-part.d.ts +2 -2
- package/dist/runtime/run-part.js +55 -28
- package/dist/server/app.d.ts +3 -0
- package/dist/server/app.js +15 -1
- package/dist/server/errors.js +2 -0
- package/dist/store/migrate.js +7 -3
- package/dist/store/migrations.d.ts +2 -0
- package/dist/store/migrations.js +32 -6
- package/dist/store/store.d.ts +7 -1
- package/dist/store/store.js +37 -7
- package/dist/tool/build.d.ts +0 -2
- package/dist/tool/build.js +5 -3
- package/dist/tool/check.d.ts +16 -0
- package/dist/tool/check.js +30 -0
- package/dist/tool/docker.js +3 -2
- package/dist/tool/exec.d.ts +1 -0
- package/dist/tool/exec.js +43 -16
- package/dist/tool/main.js +23 -24
- package/dist/tool/record-extras.d.ts +4 -1
- package/dist/tool/record-extras.js +16 -15
- package/dist/tool/stage.d.ts +1 -0
- package/dist/tool/stage.js +8 -10
- package/dist/tool/timeouts.d.ts +10 -0
- package/dist/tool/timeouts.js +19 -0
- package/dist/tool/up/compose-run.d.ts +2 -1
- package/dist/tool/up/compose-run.js +3 -2
- package/dist/tool/up/compose.d.ts +2 -1
- package/dist/tool/up/compose.js +32 -23
- package/dist/tool/up/dev-issuer-app.d.ts +9 -3
- package/dist/tool/up/dev-issuer-app.js +53 -24
- package/dist/tool/up/dev-issuer.js +2 -1
- package/dist/tool/up/down.d.ts +1 -0
- package/dist/tool/up/down.js +39 -30
- package/dist/tool/up/gateway.d.ts +9 -0
- package/dist/tool/up/gateway.js +11 -17
- package/dist/tool/up/lines.js +1 -1
- package/dist/tool/up/local-record.d.ts +4 -1
- package/dist/tool/up/ports.d.ts +2 -0
- package/dist/tool/up/ports.js +22 -0
- package/dist/tool/up/project.d.ts +4 -0
- package/dist/tool/up/project.js +20 -0
- package/dist/tool/up/store-line.d.ts +11 -0
- package/dist/tool/up/store-line.js +36 -0
- package/dist/tool/up/token.js +9 -2
- package/dist/tool/up/up.d.ts +3 -6
- package/dist/tool/up/up.js +49 -46
- package/dist/tool/up/wait.js +4 -2
- package/dist/ui/ui-app.d.ts +2 -0
- package/dist/ui/ui-app.js +18 -5
- package/dist/worker/runs.js +11 -11
- package/dist/worker/scheduler.js +14 -5
- package/package.json +2 -1
- package/schemas/extension-discovery.schema.json +17 -3
- package/schemas/extension-record.schema.json +17 -3
package/dist/tool/build.d.ts
CHANGED
|
@@ -33,6 +33,4 @@ export interface BuildResult {
|
|
|
33
33
|
}
|
|
34
34
|
export declare const imageTag: (registry: string, pkg: ExtensionPackage, part: ImagePartKind) => string;
|
|
35
35
|
export declare const recordFor: (dir: string, pkg: ExtensionPackage, info: BuildInfo, images: BuiltImage[]) => Promise<ExtensionRecord>;
|
|
36
|
-
declare const writeRecord: (dir: string, record: ExtensionRecord, file?: string) => Promise<string>;
|
|
37
36
|
export declare const build: (options?: BuildOptions, deps?: BuildDeps) => Promise<BuildResult>;
|
|
38
|
-
export { writeRecord };
|
package/dist/tool/build.js
CHANGED
|
@@ -8,17 +8,19 @@ import {} from '../manifest/primitives.schema.js';
|
|
|
8
8
|
import { readManifest } from '../manifest/read-manifest.js';
|
|
9
9
|
import {} from '../runtime/build-info.js';
|
|
10
10
|
import { resolveBuildInfo } from './build-info.js';
|
|
11
|
+
import { assertConforms } from './check.js';
|
|
11
12
|
import { createDocker } from './docker.js';
|
|
12
13
|
import { mustRun, runCommand } from './exec.js';
|
|
13
14
|
import { labelsOf } from './labels.js';
|
|
14
15
|
import { recordExtras } from './record-extras.js';
|
|
15
16
|
import { RECORD_FILE, stageContext } from './stage.js';
|
|
17
|
+
import { TIMEOUTS } from './timeouts.js';
|
|
16
18
|
export const DEFAULT_REGISTRY = 'ghcr.io/agentage';
|
|
17
19
|
export const imageTag = (registry, pkg, part) => `${registry}/${pkg.agentage.id}-${part}:${pkg.version}`;
|
|
18
20
|
const compile = async (dir, pkg, run) => {
|
|
19
21
|
const scripts = pkg.scripts;
|
|
20
22
|
if (typeof scripts?.['build'] === 'string')
|
|
21
|
-
await mustRun(run, 'npm', ['run', 'build'], { cwd: dir });
|
|
23
|
+
await mustRun(run, 'npm', ['run', 'build'], { cwd: dir, timeoutMs: TIMEOUTS.compile });
|
|
22
24
|
};
|
|
23
25
|
const buildImage = async (job, docker, tmpRoot) => {
|
|
24
26
|
const context = await stageContext(job.dir, job.pkg, job.part, tmpRoot);
|
|
@@ -60,16 +62,16 @@ const buildParts = async (base, options, deps) => {
|
|
|
60
62
|
}
|
|
61
63
|
return images;
|
|
62
64
|
};
|
|
63
|
-
// Validate, compile, one image per part from the kit recipe; --push pushes
|
|
65
|
+
// Validate, compile, check, one image per part from the kit recipe; --push pushes, writes the record.
|
|
64
66
|
export const build = async (options = {}, deps = { run: runCommand }) => {
|
|
65
67
|
const dir = resolve(options.dir ?? '.');
|
|
66
68
|
const pkg = await readManifest(dir);
|
|
67
69
|
const info = await resolveBuildInfo(dir, options.env ?? process.env, deps.run);
|
|
68
70
|
await compile(dir, pkg, deps.run);
|
|
71
|
+
await assertConforms({ dir, pkg });
|
|
69
72
|
const images = await buildParts({ dir, pkg, info }, options, deps);
|
|
70
73
|
const result = { id: pkg.agentage.id, version: pkg.version, ...info, images };
|
|
71
74
|
if (!options.push)
|
|
72
75
|
return result;
|
|
73
76
|
return { ...result, recordFile: await writeRecord(dir, await recordFor(dir, pkg, info, images)) };
|
|
74
77
|
};
|
|
75
|
-
export { writeRecord };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ExtensionPackage } from '../manifest/manifest.schema.js';
|
|
2
|
+
export interface CheckInput {
|
|
3
|
+
dir: string;
|
|
4
|
+
pkg: ExtensionPackage;
|
|
5
|
+
}
|
|
6
|
+
export interface CheckRule {
|
|
7
|
+
name: string;
|
|
8
|
+
run(input: CheckInput): Promise<string[]>;
|
|
9
|
+
}
|
|
10
|
+
export interface CheckProblem {
|
|
11
|
+
rule: string;
|
|
12
|
+
problem: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const RULES: readonly CheckRule[];
|
|
15
|
+
export declare const check: (input: CheckInput, rules?: readonly CheckRule[]) => Promise<CheckProblem[]>;
|
|
16
|
+
export declare const assertConforms: (input: CheckInput, rules?: readonly CheckRule[]) => Promise<void>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { checkCli } from '../command/check-cli.js';
|
|
2
|
+
import {} from '../manifest/manifest.schema.js';
|
|
3
|
+
import { imageParts, normalizeParts } from '../manifest/parts.js';
|
|
4
|
+
import { assertBuilt } from './stage.js';
|
|
5
|
+
const problemsOf = (work) => work.then(() => [], (err) => [err.message]);
|
|
6
|
+
// Every image part built where its image looks for it; the cli handlers are exactly the commands.
|
|
7
|
+
const entries = {
|
|
8
|
+
name: 'entries',
|
|
9
|
+
run: async ({ dir, pkg }) => {
|
|
10
|
+
const built = imageParts(pkg).map((part) => problemsOf(assertBuilt(dir, pkg, part)));
|
|
11
|
+
const cli = normalizeParts(pkg.agentage.parts).code.cli ? await checkCli(dir) : null;
|
|
12
|
+
return [...(await Promise.all(built)).flat(), ...(cli ? [`cli part: ${cli}`] : [])];
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
// Kit 0.2 has the entries rule; M4 adds manifest, surface, stack, store and sealing beside it.
|
|
16
|
+
export const RULES = [entries];
|
|
17
|
+
export const check = async (input, rules = RULES) => {
|
|
18
|
+
const found = [];
|
|
19
|
+
for (const rule of rules)
|
|
20
|
+
for (const problem of await rule.run(input))
|
|
21
|
+
found.push({ rule: rule.name, problem });
|
|
22
|
+
return found;
|
|
23
|
+
};
|
|
24
|
+
// What build runs after the compile and before any image: one failure stops everything.
|
|
25
|
+
export const assertConforms = async (input, rules = RULES) => {
|
|
26
|
+
const problems = await check(input, rules);
|
|
27
|
+
if (problems.length === 0)
|
|
28
|
+
return;
|
|
29
|
+
throw new Error(`check failed: ${problems.map((p) => `${p.rule}: ${p.problem}`).join('; ')}`);
|
|
30
|
+
};
|
package/dist/tool/docker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mustRun } from './exec.js';
|
|
2
|
+
import { TIMEOUTS } from './timeouts.js';
|
|
2
3
|
const PLATFORM = 'linux/amd64';
|
|
3
4
|
const flags = (flag, values) => Object.entries(values).flatMap(([key, value]) => [flag, `${key}=${value}`]);
|
|
4
5
|
// No provenance attestation: the pushed digest is then the image itself, as the record expects.
|
|
@@ -21,7 +22,7 @@ export const digestOf = (pushOutput) => {
|
|
|
21
22
|
};
|
|
22
23
|
export const createDocker = (run) => ({
|
|
23
24
|
build: async (options) => {
|
|
24
|
-
await mustRun(run, 'docker', buildArgsOf(options));
|
|
25
|
+
await mustRun(run, 'docker', buildArgsOf(options), { timeoutMs: TIMEOUTS.build });
|
|
25
26
|
},
|
|
26
|
-
push: async (tag) => digestOf(await mustRun(run, 'docker', ['push', tag], { echo: true })),
|
|
27
|
+
push: async (tag) => digestOf(await mustRun(run, 'docker', ['push', tag], { echo: true, timeoutMs: TIMEOUTS.push })),
|
|
27
28
|
});
|
package/dist/tool/exec.d.ts
CHANGED
package/dist/tool/exec.js
CHANGED
|
@@ -1,24 +1,51 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import {} from 'node:stream';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
import { KILL_GRACE_MS, TIMEOUTS } from './timeouts.js';
|
|
4
|
+
const spawnFor = (command, args, options) => spawn(command, args, {
|
|
5
|
+
cwd: options.cwd,
|
|
6
|
+
stdio: ['ignore', 'pipe', options.quiet ? 'pipe' : 'inherit'],
|
|
7
|
+
env: options.env ? { ...process.env, ...options.env } : process.env,
|
|
8
|
+
});
|
|
9
|
+
// Resolves true once the command is past its time and has been told to stop.
|
|
10
|
+
const expiry = (child, ms) => {
|
|
11
|
+
let timer;
|
|
12
|
+
const expired = { value: false };
|
|
13
|
+
timer = setTimeout(() => {
|
|
14
|
+
expired.value = true;
|
|
15
|
+
child.kill('SIGTERM');
|
|
16
|
+
timer = setTimeout(() => child.kill('SIGKILL'), KILL_GRACE_MS).unref();
|
|
17
|
+
}, ms).unref();
|
|
18
|
+
return { expired, clear: () => clearTimeout(timer) };
|
|
19
|
+
};
|
|
20
|
+
const timedOut = (command, args, ms) => new Error(`${command} ${args[0] ?? ''} did not finish in ${Math.round(ms / 1000)} s`);
|
|
21
|
+
// stdout is captured (and echoed to stderr with `echo`); a piped stderr is captured too.
|
|
22
|
+
const collect = (child, echo) => {
|
|
23
|
+
const out = { stdout: '', stderr: '' };
|
|
14
24
|
child.stdout.on('data', (chunk) => {
|
|
15
|
-
stdout += chunk.toString();
|
|
16
|
-
if (
|
|
25
|
+
out.stdout += chunk.toString();
|
|
26
|
+
if (echo)
|
|
17
27
|
process.stderr.write(chunk);
|
|
18
28
|
});
|
|
19
|
-
child.stderr?.on('data', (chunk) => void (stderr += chunk.toString()));
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
child.stderr?.on('data', (chunk) => void (out.stderr += chunk.toString()));
|
|
30
|
+
return out;
|
|
31
|
+
};
|
|
32
|
+
// stderr streams through as progress unless `quiet`; past its timeout the command is killed.
|
|
33
|
+
export const runCommand = (command, args, options = {}) => new Promise((resolve, reject) => {
|
|
34
|
+
const child = spawnFor(command, args, options);
|
|
35
|
+
const ms = options.timeoutMs ?? TIMEOUTS.command;
|
|
36
|
+
const timer = expiry(child, ms);
|
|
37
|
+
const out = collect(child, options.echo);
|
|
38
|
+
child.on('error', (err) => {
|
|
39
|
+
timer.clear();
|
|
40
|
+
reject(err.code === 'ENOENT' ? new Error(`${command} is not installed or not on PATH`) : err);
|
|
41
|
+
});
|
|
42
|
+
child.on('close', (code) => {
|
|
43
|
+
timer.clear();
|
|
44
|
+
if (timer.expired.value)
|
|
45
|
+
return reject(timedOut(command, args, ms));
|
|
46
|
+
const stderr = options.quiet ? { stderr: out.stderr } : {};
|
|
47
|
+
resolve({ code: code ?? 1, stdout: out.stdout, ...stderr });
|
|
48
|
+
});
|
|
22
49
|
});
|
|
23
50
|
const tail = (text) => text ? `\n${text.trim().split('\n').slice(-15).join('\n')}` : '';
|
|
24
51
|
export const mustRun = async (run, command, args, options) => {
|
package/dist/tool/main.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
|
+
import { UsageError } from '../command/args.js';
|
|
3
|
+
import { EXIT_FAILED, EXIT_OK, EXIT_USAGE } from '../command/run-command.js';
|
|
2
4
|
import { build } from './build.js';
|
|
3
5
|
import { buildLines } from './format.js';
|
|
4
6
|
import { down } from './up/down.js';
|
|
@@ -14,10 +16,10 @@ const USAGE = `usage: agentage-extension <build|up|down|token> [options]
|
|
|
14
16
|
token a dev token: --scope <id>:<verb> [--issuer <url>]
|
|
15
17
|
--dir the extension package folder (default: the current folder)`;
|
|
16
18
|
const defaultDeps = {
|
|
17
|
-
build
|
|
18
|
-
up
|
|
19
|
-
down
|
|
20
|
-
token
|
|
19
|
+
build,
|
|
20
|
+
up,
|
|
21
|
+
down,
|
|
22
|
+
token,
|
|
21
23
|
out: (line) => void process.stdout.write(`${line}\n`),
|
|
22
24
|
err: (line) => void process.stderr.write(`${line}\n`),
|
|
23
25
|
};
|
|
@@ -31,8 +33,6 @@ const OPTIONS = {
|
|
|
31
33
|
help: { type: 'boolean', short: 'h' },
|
|
32
34
|
};
|
|
33
35
|
const parse = (argv) => parseArgs({ args: argv, allowPositionals: true, options: OPTIONS });
|
|
34
|
-
class UsageError extends Error {
|
|
35
|
-
}
|
|
36
36
|
const COMMANDS = {
|
|
37
37
|
build: async (v, deps) => {
|
|
38
38
|
for (const line of buildLines(await deps.build({ push: v.push, dir: v.dir, registry: v.registry })))
|
|
@@ -49,34 +49,33 @@ const COMMANDS = {
|
|
|
49
49
|
const runOne = async (name, values, deps) => {
|
|
50
50
|
try {
|
|
51
51
|
await COMMANDS[name]?.(values, deps);
|
|
52
|
-
return
|
|
52
|
+
return EXIT_OK;
|
|
53
53
|
}
|
|
54
54
|
catch (err) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return err instanceof UsageError ? 2 : 1;
|
|
55
|
+
const usage = err instanceof UsageError;
|
|
56
|
+
deps.err(usage ? `${err.message}\n${USAGE}` : `${name} failed: ${err.message}`);
|
|
57
|
+
return usage ? EXIT_USAGE : EXIT_FAILED;
|
|
59
58
|
}
|
|
60
59
|
};
|
|
61
|
-
|
|
62
|
-
export const main = async (argv, deps = defaultDeps) => {
|
|
63
|
-
let parsed;
|
|
60
|
+
const parsedOrUsage = (argv, deps) => {
|
|
64
61
|
try {
|
|
65
|
-
|
|
62
|
+
return parse(argv);
|
|
66
63
|
}
|
|
67
64
|
catch (err) {
|
|
68
65
|
deps.err(`${err.message}\n${USAGE}`);
|
|
69
|
-
return
|
|
66
|
+
return null;
|
|
70
67
|
}
|
|
68
|
+
};
|
|
69
|
+
// Exit codes: 0 ok, 1 the step failed, 2 usage.
|
|
70
|
+
export const main = async (argv, deps = defaultDeps) => {
|
|
71
|
+
const parsed = parsedOrUsage(argv, deps);
|
|
72
|
+
if (!parsed)
|
|
73
|
+
return EXIT_USAGE;
|
|
71
74
|
const { positionals, values } = parsed;
|
|
72
|
-
if (values.help)
|
|
73
|
-
deps.out(USAGE);
|
|
74
|
-
return 0;
|
|
75
|
-
}
|
|
75
|
+
if (values.help)
|
|
76
|
+
return (deps.out(USAGE), EXIT_OK);
|
|
76
77
|
const name = positionals[0] ?? '';
|
|
77
|
-
if (!(name in COMMANDS) || positionals.length !== 1)
|
|
78
|
-
deps.err(USAGE);
|
|
79
|
-
return 2;
|
|
80
|
-
}
|
|
78
|
+
if (!(name in COMMANDS) || positionals.length !== 1)
|
|
79
|
+
return (deps.err(USAGE), EXIT_USAGE);
|
|
81
80
|
return runOne(name, values, deps);
|
|
82
81
|
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { type RecordExtras } from '../discovery/discovery.js';
|
|
2
2
|
import { type ExtensionPackage } from '../manifest/manifest.schema.js';
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const toolsOf: (dir: string, pkg: ExtensionPackage) => Promise<{
|
|
4
|
+
name: string;
|
|
5
|
+
scope: string;
|
|
6
|
+
}[]>;
|
|
4
7
|
export declare const recordExtras: (dir: string, pkg: ExtensionPackage) => Promise<RecordExtras>;
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { pathToFileURL } from 'node:url';
|
|
3
2
|
import { z } from 'zod';
|
|
3
|
+
import { scopeOf } from '../auth/token-check.js';
|
|
4
4
|
import { createContext } from '../context/context.js';
|
|
5
5
|
import {} from '../discovery/discovery.js';
|
|
6
|
+
import { WidgetSchema } from '../discovery/discovery.schema.js';
|
|
6
7
|
import { OPENAPI_PATH, readOpenapi } from '../discovery/openapi.js';
|
|
7
8
|
import { createLog } from '../log/log.js';
|
|
9
|
+
import { loadCreate } from '../manifest/entry.js';
|
|
8
10
|
import { readJsonIfExists } from '../manifest/read-manifest.js';
|
|
9
11
|
import {} from '../manifest/manifest.schema.js';
|
|
10
|
-
import {
|
|
12
|
+
import { hasPostgres, normalizeParts } from '../manifest/parts.js';
|
|
11
13
|
import { createToolRegistry, prefixed } from '../mcp/tools.js';
|
|
12
14
|
import { sqlFiles } from '../store/migrations.js';
|
|
13
15
|
import {} from '../store/store.types.js';
|
|
14
|
-
const WidgetsSchema = z
|
|
15
|
-
.array(z.strictObject({ name: z.string(), element: z.string(), module: z.string() }))
|
|
16
|
-
.min(1);
|
|
16
|
+
const WidgetsSchema = z.array(WidgetSchema).min(1);
|
|
17
17
|
const noStore = {
|
|
18
18
|
query: async () => {
|
|
19
19
|
throw new Error('the store is not open while the build lists tools');
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
22
|
const silent = createLog({ extension: 'build', part: 'mcp' }, () => undefined);
|
|
23
|
-
// The
|
|
24
|
-
export const
|
|
23
|
+
// The tools the mcp part registers and their scopes, read by running its create(ctx) on a recorder.
|
|
24
|
+
export const toolsOf = async (dir, pkg) => {
|
|
25
25
|
const registry = createToolRegistry();
|
|
26
26
|
const base = createContext({
|
|
27
27
|
pkg,
|
|
@@ -31,11 +31,12 @@ export const toolNames = async (dir, pkg) => {
|
|
|
31
31
|
log: silent,
|
|
32
32
|
store: noStore,
|
|
33
33
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
await (await loadCreate(dir, pkg, 'mcp'))({ ...base, tool: registry.tool });
|
|
35
|
+
const { id } = pkg.agentage;
|
|
36
|
+
return registry.tools.map((tool) => ({
|
|
37
|
+
name: prefixed(id, tool.name),
|
|
38
|
+
scope: scopeOf(id, tool.spec.scope),
|
|
39
|
+
}));
|
|
39
40
|
};
|
|
40
41
|
const widgetsOf = async (dir) => {
|
|
41
42
|
const value = await readJsonIfExists(join(dir, 'dist', 'ui', 'widgets.json'));
|
|
@@ -43,10 +44,10 @@ const widgetsOf = async (dir) => {
|
|
|
43
44
|
};
|
|
44
45
|
// What the code serves, for the record: tools, widgets and the migration count.
|
|
45
46
|
export const recordExtras = async (dir, pkg) => {
|
|
46
|
-
const { code
|
|
47
|
+
const { code } = normalizeParts(pkg.agentage.parts);
|
|
47
48
|
const extras = {};
|
|
48
49
|
if (code.mcp) {
|
|
49
|
-
const tools = await
|
|
50
|
+
const tools = await toolsOf(dir, pkg);
|
|
50
51
|
if (tools.length > 0)
|
|
51
52
|
extras.tools = tools;
|
|
52
53
|
}
|
|
@@ -54,7 +55,7 @@ export const recordExtras = async (dir, pkg) => {
|
|
|
54
55
|
extras.widgets = await widgetsOf(dir);
|
|
55
56
|
if (code.api && (await readOpenapi(dir)))
|
|
56
57
|
extras.openapi = OPENAPI_PATH;
|
|
57
|
-
if (
|
|
58
|
+
if (hasPostgres(pkg))
|
|
58
59
|
extras.migrations = (await sqlFiles(join(dir, 'migrations'))).length;
|
|
59
60
|
return extras;
|
|
60
61
|
};
|
package/dist/tool/stage.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { type ImagePartKind } from '../manifest/primitives.schema.js';
|
|
|
3
3
|
export declare const RECORD_FILE = "agentage-extension.json";
|
|
4
4
|
export declare const MIGRATE_SHIM = "// Generated by @agentage/extension build.\nimport '@agentage/extension/migrate';\n";
|
|
5
5
|
export declare const localTarballs: (pkg: ExtensionPackage) => string[];
|
|
6
|
+
export declare const exists: (path: string) => Promise<boolean>;
|
|
6
7
|
export declare const appPaths: (dir: string, pkg: ExtensionPackage, part: ImagePartKind) => Promise<string[]>;
|
|
7
8
|
export declare const assertBuilt: (dir: string, pkg: ExtensionPackage, part: ImagePartKind) => Promise<void>;
|
|
8
9
|
export declare const stageContext: (dir: string, pkg: ExtensionPackage, part: ImagePartKind, tmpRoot?: string) => Promise<string>;
|
package/dist/tool/stage.js
CHANGED
|
@@ -2,7 +2,7 @@ import { access, cp, mkdir, mkdtemp, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { dirname, isAbsolute, join, normalize, sep } from 'node:path';
|
|
4
4
|
import {} from '../manifest/manifest.schema.js';
|
|
5
|
-
import { entryOf,
|
|
5
|
+
import { entryOf, hasPostgres } from '../manifest/parts.js';
|
|
6
6
|
import {} from '../manifest/primitives.schema.js';
|
|
7
7
|
import { OPENAPI_FILE } from '../discovery/openapi.js';
|
|
8
8
|
import { serverPartRecipe } from './recipe.js';
|
|
@@ -25,7 +25,7 @@ export const localTarballs = (pkg) => dependencySpecs(pkg)
|
|
|
25
25
|
}
|
|
26
26
|
return path;
|
|
27
27
|
});
|
|
28
|
-
const exists = (path) => access(path).then(() => true, () => false);
|
|
28
|
+
export const exists = (path) => access(path).then(() => true, () => false);
|
|
29
29
|
const copyInto = async (from, to) => {
|
|
30
30
|
await mkdir(dirname(to), { recursive: true });
|
|
31
31
|
await cp(from, to, { recursive: true });
|
|
@@ -39,25 +39,23 @@ const copyDeps = async (dir, root, pkg) => {
|
|
|
39
39
|
for (const path of localTarballs(pkg))
|
|
40
40
|
await copyInto(join(dir, path), join(root, 'deps', path));
|
|
41
41
|
};
|
|
42
|
-
//
|
|
42
|
+
// The api image runs the migrate job, so it alone carries the migrations; seeds never ship.
|
|
43
|
+
const migrates = (pkg, part) => part === 'api' && hasPostgres(pkg);
|
|
44
|
+
// What a part image carries: its own dist/{part}, the shared dist/lib, and for api the migrations.
|
|
43
45
|
export const appPaths = async (dir, pkg, part) => {
|
|
44
46
|
const paths = ['package.json', join('dist', part)];
|
|
45
47
|
if (part === 'api' && (await exists(join(dir, OPENAPI_FILE))))
|
|
46
48
|
paths.push(OPENAPI_FILE);
|
|
47
49
|
if (await exists(join(dir, 'dist', 'lib')))
|
|
48
50
|
paths.push(join('dist', 'lib'));
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
for (const folder of ['migrations', 'seeds'])
|
|
52
|
-
if (await exists(join(dir, folder)))
|
|
53
|
-
paths.push(folder);
|
|
54
|
-
}
|
|
51
|
+
if (migrates(pkg, part) && (await exists(join(dir, 'migrations'))))
|
|
52
|
+
paths.push('migrations');
|
|
55
53
|
return paths;
|
|
56
54
|
};
|
|
57
55
|
const copyApp = async (dir, root, pkg, part) => {
|
|
58
56
|
for (const path of await appPaths(dir, pkg, part))
|
|
59
57
|
await copyInto(join(dir, path), join(root, 'app', path));
|
|
60
|
-
if (
|
|
58
|
+
if (migrates(pkg, part)) {
|
|
61
59
|
await mkdir(join(root, 'app', 'dist', 'store'), { recursive: true });
|
|
62
60
|
await writeFile(join(root, 'app', 'dist', 'store', 'migrate.js'), MIGRATE_SHIM);
|
|
63
61
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const TIMEOUTS: {
|
|
2
|
+
readonly command: 120000;
|
|
3
|
+
readonly compile: 600000;
|
|
4
|
+
readonly build: 1200000;
|
|
5
|
+
readonly push: 600000;
|
|
6
|
+
readonly start: 600000;
|
|
7
|
+
readonly migrate: 300000;
|
|
8
|
+
readonly issuer: 5000;
|
|
9
|
+
};
|
|
10
|
+
export declare const KILL_GRACE_MS = 5000;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Every outbound call the tool makes and its bound; none retries, since a re-run is safe.
|
|
2
|
+
export const TIMEOUTS = {
|
|
3
|
+
// git rev-parse, docker image inspect, compose ps, logs and down, the port check
|
|
4
|
+
command: 120_000,
|
|
5
|
+
// the package's own npm run build
|
|
6
|
+
compile: 600_000,
|
|
7
|
+
// docker build: a cold cache pulls the node base image and runs npm ci
|
|
8
|
+
build: 1_200_000,
|
|
9
|
+
// docker push to the registry
|
|
10
|
+
push: 600_000,
|
|
11
|
+
// compose up: the store and gateway images are pulled on first use
|
|
12
|
+
start: 600_000,
|
|
13
|
+
// compose run migrate: the store's migrations and seeds
|
|
14
|
+
migrate: 300_000,
|
|
15
|
+
// POST /token to the dev issuer
|
|
16
|
+
issuer: 5_000,
|
|
17
|
+
};
|
|
18
|
+
// SIGTERM first; a command that ignores it is killed this long after.
|
|
19
|
+
export const KILL_GRACE_MS = 5_000;
|
|
@@ -9,6 +9,7 @@ export interface ServiceState {
|
|
|
9
9
|
health: string;
|
|
10
10
|
exitCode: number;
|
|
11
11
|
}
|
|
12
|
-
export declare const
|
|
12
|
+
export declare const composeArgs: (target: ComposeTarget, args: string[]) => string[];
|
|
13
|
+
export declare const compose: (run: Run, target: ComposeTarget, args: string[], timeoutMs?: number) => Promise<string>;
|
|
13
14
|
export declare const parsePs: (stdout: string) => ServiceState[];
|
|
14
15
|
export declare const states: (run: Run, target: ComposeTarget) => Promise<ServiceState[]>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { mustRun } from '../exec.js';
|
|
2
|
-
export const
|
|
2
|
+
export const composeArgs = (target, args) => [
|
|
3
3
|
'compose',
|
|
4
4
|
...(target.file ? ['-f', target.file] : []),
|
|
5
5
|
'-p',
|
|
6
6
|
target.project,
|
|
7
7
|
...args,
|
|
8
|
-
]
|
|
8
|
+
];
|
|
9
|
+
export const compose = (run, target, args, timeoutMs) => mustRun(run, 'docker', composeArgs(target, args), { timeoutMs });
|
|
9
10
|
// `compose ps --format json` prints one object per line (older releases: one array).
|
|
10
11
|
export const parsePs = (stdout) => {
|
|
11
12
|
const text = stdout.trim();
|
|
@@ -2,11 +2,12 @@ import { type ExtensionPackage } from '../../manifest/manifest.schema.js';
|
|
|
2
2
|
export declare const STORE_IMAGE = "postgres:18-alpine";
|
|
3
3
|
export interface ComposeInput {
|
|
4
4
|
pkg: ExtensionPackage;
|
|
5
|
+
project: string;
|
|
5
6
|
tags: Record<string, string>;
|
|
6
7
|
recordFile: string;
|
|
7
8
|
gatewayFile: string;
|
|
9
|
+
seedsDir?: string;
|
|
8
10
|
}
|
|
9
|
-
export declare const projectName: (pkg: ExtensionPackage) => string;
|
|
10
11
|
export declare const composeFile: (input: ComposeInput) => {
|
|
11
12
|
name: string;
|
|
12
13
|
services: Record<string, unknown>;
|
package/dist/tool/up/compose.js
CHANGED
|
@@ -1,38 +1,48 @@
|
|
|
1
1
|
import {} from '../../manifest/manifest.schema.js';
|
|
2
|
-
import {
|
|
2
|
+
import { hasPostgres, imageParts } from '../../manifest/parts.js';
|
|
3
3
|
import {} from '../../manifest/primitives.schema.js';
|
|
4
|
+
import { DEV_AUDIENCE, DEV_ISSUER, UP_ISSUER } from './dev-issuer-app.js';
|
|
4
5
|
import { GATEWAY_PORT, ISSUER_PORT, PART_PORT, TRAEFIK_COMMAND, TRAEFIK_IMAGE } from './gateway.js';
|
|
5
6
|
export const STORE_IMAGE = 'postgres:18-alpine';
|
|
6
7
|
const RECORD_TARGET = '/run/agentage/agentage-extension.json';
|
|
7
8
|
const ISSUER_ENTRY = 'node_modules/@agentage/extension/dist/tool/up/dev-issuer.js';
|
|
8
|
-
export const projectName = (pkg) => `agentage-${pkg.agentage.id}`;
|
|
9
9
|
// A local-only credential: the store has no published port, only the parts' network reaches it.
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const STORE_PASSWORD = 'agentage-local';
|
|
11
|
+
const storeUrl = (id) => `postgres://${id}:${STORE_PASSWORD}@store:5432/${id}`;
|
|
12
|
+
const TOKENS = { AGENTAGE_ISSUER: UP_ISSUER, AGENTAGE_AUDIENCE: DEV_AUDIENCE };
|
|
13
|
+
// Only the ui under `up` names the dev issuer: its CSP then lets the harness ask it for a token.
|
|
12
14
|
const partEnv = (pkg, part) => ({
|
|
13
15
|
AGENTAGE_ORIGIN: `http://localhost:${GATEWAY_PORT}`,
|
|
16
|
+
...TOKENS,
|
|
14
17
|
AGENTAGE_RECORD_FILE: RECORD_TARGET,
|
|
15
|
-
...(
|
|
18
|
+
...(hasPostgres(pkg) && part !== 'ui' ? { AGENTAGE_STORE_URL: storeUrl(pkg.agentage.id) } : {}),
|
|
19
|
+
...(part === 'ui' ? { AGENTAGE_DEV_ISSUER: DEV_ISSUER } : {}),
|
|
16
20
|
});
|
|
21
|
+
const QUICK_CHECK = { interval: '2s', timeout: '3s', retries: 30 };
|
|
17
22
|
const storeService = (id) => ({
|
|
18
23
|
image: STORE_IMAGE,
|
|
19
|
-
environment: { POSTGRES_USER: id, POSTGRES_PASSWORD:
|
|
24
|
+
environment: { POSTGRES_USER: id, POSTGRES_PASSWORD: STORE_PASSWORD, POSTGRES_DB: id },
|
|
20
25
|
volumes: ['store:/var/lib/postgresql'],
|
|
21
26
|
healthcheck: {
|
|
22
27
|
test: ['CMD-SHELL', `pg_isready -U ${id} -d ${id}`],
|
|
23
|
-
|
|
24
|
-
timeout: '3s',
|
|
25
|
-
retries: 30,
|
|
28
|
+
...QUICK_CHECK,
|
|
26
29
|
},
|
|
27
30
|
});
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
...(
|
|
33
|
-
? { depends_on: { store: { condition: 'service_healthy' } } }
|
|
34
|
-
: {}),
|
|
31
|
+
const HEALTHY = { condition: 'service_healthy' };
|
|
32
|
+
// api and mcp read the issuer's keys and revoked list before they serve.
|
|
33
|
+
const dependsOn = (pkg, part) => ({
|
|
34
|
+
...(hasPostgres(pkg) && part !== 'ui' ? { store: HEALTHY } : {}),
|
|
35
|
+
...(part === 'api' || part === 'mcp' ? { issuer: HEALTHY } : {}),
|
|
35
36
|
});
|
|
37
|
+
const partService = (input, part) => {
|
|
38
|
+
const depends_on = dependsOn(input.pkg, part);
|
|
39
|
+
return {
|
|
40
|
+
image: input.tags[part],
|
|
41
|
+
environment: partEnv(input.pkg, part),
|
|
42
|
+
volumes: [`${input.recordFile}:${RECORD_TARGET}:ro`],
|
|
43
|
+
...(Object.keys(depends_on).length > 0 ? { depends_on } : {}),
|
|
44
|
+
};
|
|
45
|
+
};
|
|
36
46
|
const fixedServices = (input, anyImage) => ({
|
|
37
47
|
gateway: {
|
|
38
48
|
image: TRAEFIK_IMAGE,
|
|
@@ -41,9 +51,7 @@ const fixedServices = (input, anyImage) => ({
|
|
|
41
51
|
volumes: [`${input.gatewayFile}:/etc/traefik/dynamic.yml:ro`],
|
|
42
52
|
healthcheck: {
|
|
43
53
|
test: ['CMD', 'wget', '-qO-', 'http://127.0.0.1:8082/ping'],
|
|
44
|
-
|
|
45
|
-
timeout: '3s',
|
|
46
|
-
retries: 30,
|
|
54
|
+
...QUICK_CHECK,
|
|
47
55
|
},
|
|
48
56
|
},
|
|
49
57
|
issuer: {
|
|
@@ -56,7 +64,8 @@ const migrateService = (input) => ({
|
|
|
56
64
|
image: input.tags['api'],
|
|
57
65
|
command: ['node', 'dist/store/migrate.js'],
|
|
58
66
|
environment: partEnv(input.pkg, 'migrate'),
|
|
59
|
-
|
|
67
|
+
...(input.seedsDir ? { volumes: [`${input.seedsDir}:/app/seeds:ro`] } : {}),
|
|
68
|
+
depends_on: { store: HEALTHY },
|
|
60
69
|
profiles: ['jobs'],
|
|
61
70
|
restart: 'no',
|
|
62
71
|
});
|
|
@@ -67,11 +76,11 @@ export const composeFile = (input) => {
|
|
|
67
76
|
for (const part of parts)
|
|
68
77
|
services[part] = partService(input, part);
|
|
69
78
|
Object.assign(services, fixedServices(input, input.tags[parts[0] ?? 'api'] ?? ''));
|
|
70
|
-
if (
|
|
79
|
+
if (hasPostgres(input.pkg)) {
|
|
71
80
|
services['store'] = storeService(input.pkg.agentage.id);
|
|
72
81
|
if (input.tags['api'])
|
|
73
82
|
services['migrate'] = migrateService(input);
|
|
74
83
|
}
|
|
75
|
-
const volumes =
|
|
76
|
-
return { name:
|
|
84
|
+
const volumes = hasPostgres(input.pkg) ? { store: {} } : {};
|
|
85
|
+
return { name: input.project, services, volumes };
|
|
77
86
|
};
|
|
@@ -2,11 +2,17 @@ import { type KeyObject } from 'node:crypto';
|
|
|
2
2
|
import { type Server } from 'node:http';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
export declare const DEV_ISSUER = "http://localhost:4281";
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const UP_ISSUER = "http://issuer:3000";
|
|
6
|
+
export declare const DEV_AUDIENCE = "localhost";
|
|
7
|
+
export type IssuerSettings = {
|
|
8
|
+
issuer: string;
|
|
9
|
+
audience: string;
|
|
10
|
+
};
|
|
6
11
|
declare const TokenRequestSchema: z.ZodObject<{
|
|
7
12
|
scope: z.ZodString;
|
|
8
13
|
sub: z.ZodOptional<z.ZodString>;
|
|
9
14
|
ttlSeconds: z.ZodOptional<z.ZodInt>;
|
|
15
|
+
aud: z.ZodOptional<z.ZodString>;
|
|
10
16
|
}, z.core.$strict>;
|
|
11
17
|
interface Keys {
|
|
12
18
|
kid: string;
|
|
@@ -14,9 +20,9 @@ interface Keys {
|
|
|
14
20
|
jwk: Record<string, unknown>;
|
|
15
21
|
}
|
|
16
22
|
export declare const newKeys: () => Keys;
|
|
17
|
-
export declare const mint: (keys: Keys, request: z.infer<typeof TokenRequestSchema>, now?: number) => {
|
|
23
|
+
export declare const mint: (keys: Keys, request: z.infer<typeof TokenRequestSchema>, settings: IssuerSettings, now?: number) => {
|
|
18
24
|
token: string;
|
|
19
25
|
expiresAt: string;
|
|
20
26
|
};
|
|
21
|
-
export declare const createIssuer: (keys?: Keys) => Server;
|
|
27
|
+
export declare const createIssuer: (settings?: IssuerSettings, keys?: Keys) => Server;
|
|
22
28
|
export {};
|