@agentage/extension 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/agcid/current.d.ts +7 -0
- package/dist/agcid/current.js +4 -0
- package/dist/agcid/scope.d.ts +0 -1
- package/dist/agcid/scope.js +2 -4
- package/dist/auth/route-access.d.ts +2 -0
- package/dist/auth/route-access.js +21 -0
- package/dist/auth/token-check.d.ts +10 -0
- package/dist/auth/token-check.js +18 -0
- package/dist/command/args.d.ts +14 -0
- package/dist/command/args.js +60 -0
- package/dist/command/check-cli.d.ts +1 -0
- package/dist/command/check-cli.js +19 -0
- package/dist/command/command.types.d.ts +14 -0
- package/dist/command/command.types.js +2 -0
- package/dist/command/out.d.ts +19 -0
- package/dist/command/out.js +32 -0
- package/dist/command/run-command.d.ts +16 -0
- package/dist/command/run-command.js +43 -0
- package/dist/context/context.d.ts +8 -14
- package/dist/context/context.js +20 -26
- package/dist/context/settings.d.ts +5 -0
- package/dist/context/settings.js +32 -0
- package/dist/context/test-context.d.ts +44 -0
- package/dist/context/test-context.js +75 -0
- package/dist/discovery/discovery.d.ts +12 -1
- package/dist/discovery/discovery.js +15 -6
- package/dist/discovery/discovery.schema.d.ts +7 -0
- package/dist/discovery/discovery.schema.js +4 -2
- package/dist/discovery/openapi.d.ts +3 -0
- package/dist/discovery/openapi.js +19 -0
- package/dist/health/health.schema.js +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.js +16 -3
- package/dist/log/log.js +1 -1
- package/dist/manifest/entry.d.ts +5 -0
- package/dist/manifest/entry.js +29 -0
- package/dist/manifest/manifest.schema.d.ts +18 -0
- package/dist/manifest/manifest.schema.js +1 -0
- package/dist/manifest/parts.d.ts +1 -0
- package/dist/manifest/parts.js +1 -0
- package/dist/mcp/mcp-app.d.ts +8 -0
- package/dist/mcp/mcp-app.js +94 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +38 -0
- package/dist/mcp/snapshot.d.ts +11 -0
- package/dist/mcp/snapshot.js +69 -0
- package/dist/mcp/tools.d.ts +26 -0
- package/dist/mcp/tools.js +21 -0
- package/dist/pages/pages-app.d.ts +8 -0
- package/dist/pages/pages-app.js +29 -0
- package/dist/pages/pages.d.ts +4 -0
- package/dist/pages/pages.js +16 -0
- package/dist/runtime/part-runners.d.ts +20 -0
- package/dist/runtime/part-runners.js +63 -0
- package/dist/runtime/run-part.d.ts +7 -3
- package/dist/runtime/run-part.js +67 -29
- package/dist/server/app.d.ts +20 -2
- package/dist/server/app.js +39 -11
- package/dist/store/migrate.d.ts +2 -0
- package/dist/store/migrate.js +22 -0
- package/dist/store/migrations.d.ts +12 -0
- package/dist/store/migrations.js +81 -0
- package/dist/store/store.d.ts +16 -0
- package/dist/store/store.js +55 -0
- package/dist/store/store.types.d.ts +7 -0
- package/dist/store/store.types.js +1 -0
- package/dist/store/test-store.d.ts +5 -0
- package/dist/store/test-store.js +39 -0
- package/dist/testing/test-server.d.ts +6 -0
- package/dist/testing/test-server.js +31 -9
- package/dist/tool/build.d.ts +4 -0
- package/dist/tool/build.js +21 -26
- 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 +5 -0
- package/dist/tool/exec.js +48 -9
- package/dist/tool/index.d.ts +8 -0
- package/dist/tool/index.js +8 -0
- package/dist/tool/kit-version.d.ts +1 -0
- package/dist/tool/kit-version.js +3 -0
- package/dist/tool/main.d.ts +6 -0
- package/dist/tool/main.js +60 -35
- package/dist/tool/record-extras.d.ts +4 -0
- package/dist/tool/record-extras.js +56 -0
- package/dist/tool/stage.d.ts +4 -0
- package/dist/tool/stage.js +39 -21
- package/dist/tool/timeouts.d.ts +10 -0
- package/dist/tool/timeouts.js +19 -0
- package/dist/tool/up/compose-run.d.ts +15 -0
- package/dist/tool/up/compose-run.js +25 -0
- package/dist/tool/up/compose.d.ts +19 -0
- package/dist/tool/up/compose.js +80 -0
- package/dist/tool/up/dev-issuer-app.d.ts +22 -0
- package/dist/tool/up/dev-issuer-app.js +80 -0
- package/dist/tool/up/dev-issuer.d.ts +2 -0
- package/dist/tool/up/dev-issuer.js +6 -0
- package/dist/tool/up/down.d.ts +13 -0
- package/dist/tool/up/down.js +47 -0
- package/dist/tool/up/gateway.d.ts +36 -0
- package/dist/tool/up/gateway.js +60 -0
- package/dist/tool/up/lines.d.ts +2 -0
- package/dist/tool/up/lines.js +3 -0
- package/dist/tool/up/local-record.d.ts +44 -0
- package/dist/tool/up/local-record.js +28 -0
- 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.d.ts +6 -0
- package/dist/tool/up/token.js +25 -0
- package/dist/tool/up/up.d.ts +16 -0
- package/dist/tool/up/up.js +84 -0
- package/dist/tool/up/wait.d.ts +8 -0
- package/dist/tool/up/wait.js +26 -0
- package/dist/ui/ui-app.d.ts +8 -0
- package/dist/ui/ui-app.js +39 -0
- package/dist/worker/cron.d.ts +11 -0
- package/dist/worker/cron.js +73 -0
- package/dist/worker/job-options.d.ts +9 -0
- package/dist/worker/job-options.js +17 -0
- package/dist/worker/run-log.d.ts +9 -0
- package/dist/worker/run-log.js +38 -0
- package/dist/worker/runs.d.ts +20 -0
- package/dist/worker/runs.js +59 -0
- package/dist/worker/scheduler.d.ts +20 -0
- package/dist/worker/scheduler.js +106 -0
- package/dist/worker/worker.types.d.ts +5 -0
- package/dist/worker/worker.types.js +2 -0
- package/package.json +11 -2
- package/schemas/extension-discovery.schema.json +5 -0
- package/schemas/extension-package.schema.json +3 -0
- package/schemas/extension-record.schema.json +5 -0
- package/schemas/health.schema.json +1 -1
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {} from 'node:net';
|
|
2
|
-
import {
|
|
2
|
+
import {} from 'express';
|
|
3
|
+
import {} from '../context/context.js';
|
|
4
|
+
import { createTestContext } from '../context/test-context.js';
|
|
3
5
|
import {} from '../manifest/manifest.schema.js';
|
|
6
|
+
import { createMcpApp } from '../mcp/mcp-app.js';
|
|
7
|
+
import { createToolRegistry } from '../mcp/tools.js';
|
|
4
8
|
import { createApiApp } from '../server/app.js';
|
|
5
9
|
import {} from '../server/part.types.js';
|
|
6
10
|
import { listen } from '../server/serve.js';
|
|
7
11
|
const TEST_BUILD = { commit: '0'.repeat(40), buildTime: '2026-01-01T00:00:00Z' };
|
|
8
|
-
const testPackage = (ctx) => ({
|
|
12
|
+
const testPackage = (ctx, parts) => ({
|
|
9
13
|
name: `@agentage/extension-${ctx.extension.id}`,
|
|
10
14
|
version: ctx.extension.version,
|
|
11
|
-
engines: { agentage: '^0.
|
|
12
|
-
agentage: { id: ctx.extension.id, parts
|
|
15
|
+
engines: { agentage: '^0.2.0' },
|
|
16
|
+
agentage: { id: ctx.extension.id, parts },
|
|
13
17
|
});
|
|
14
|
-
|
|
15
|
-
export const startTestServer = async (options) => {
|
|
16
|
-
const ctx = options.ctx ?? createTestContext();
|
|
17
|
-
const router = await options.create(ctx);
|
|
18
|
-
const app = createApiApp({ pkg: testPackage(ctx), build: TEST_BUILD, router, log: ctx.log });
|
|
18
|
+
const startOn = async (app) => {
|
|
19
19
|
const server = await listen(app, 0, '127.0.0.1');
|
|
20
20
|
const url = `http://127.0.0.1:${server.address().port}`;
|
|
21
21
|
return {
|
|
@@ -24,3 +24,25 @@ export const startTestServer = async (options) => {
|
|
|
24
24
|
close: () => new Promise((resolve) => server.close(() => resolve())),
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
|
+
// Runs an api part's create(ctx) inside the kit's real app on 127.0.0.1, port chosen by the OS.
|
|
28
|
+
export const startTestServer = async (options) => {
|
|
29
|
+
const ctx = options.ctx ?? createTestContext();
|
|
30
|
+
const router = await options.create(ctx);
|
|
31
|
+
const pkg = testPackage(ctx, { api: true });
|
|
32
|
+
return startOn(createApiApp({ pkg, build: TEST_BUILD, router, log: ctx.log }));
|
|
33
|
+
};
|
|
34
|
+
// Runs an mcp part's create(ctx) inside the kit's real /mcp app: tools named {id}__{name}.
|
|
35
|
+
export const startTestMcp = async (options) => {
|
|
36
|
+
const ctx = options.ctx ?? createTestContext();
|
|
37
|
+
const registry = createToolRegistry();
|
|
38
|
+
await options.create({ ...ctx, tool: registry.tool });
|
|
39
|
+
const pkg = testPackage(ctx, { mcp: true });
|
|
40
|
+
const app = createMcpApp({
|
|
41
|
+
pkg,
|
|
42
|
+
build: TEST_BUILD,
|
|
43
|
+
log: ctx.log,
|
|
44
|
+
tools: registry.tools,
|
|
45
|
+
listing: 'open',
|
|
46
|
+
});
|
|
47
|
+
return startOn(app);
|
|
48
|
+
};
|
package/dist/tool/build.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { type BuildInfo } from '../discovery/discovery.js';
|
|
2
|
+
import { type ExtensionRecord } from '../discovery/discovery.schema.js';
|
|
1
3
|
import { type ExtensionPackage } from '../manifest/manifest.schema.js';
|
|
2
4
|
import { type ImagePartKind } from '../manifest/primitives.schema.js';
|
|
3
5
|
import { type Env } from '../runtime/build-info.js';
|
|
4
6
|
import { type Docker } from './docker.js';
|
|
5
7
|
import { type Run } from './exec.js';
|
|
8
|
+
export declare const DEFAULT_REGISTRY = "ghcr.io/agentage";
|
|
6
9
|
export interface BuildOptions {
|
|
7
10
|
dir?: string;
|
|
8
11
|
push?: boolean;
|
|
@@ -29,4 +32,5 @@ export interface BuildResult {
|
|
|
29
32
|
recordFile?: string;
|
|
30
33
|
}
|
|
31
34
|
export declare const imageTag: (registry: string, pkg: ExtensionPackage, part: ImagePartKind) => string;
|
|
35
|
+
export declare const recordFor: (dir: string, pkg: ExtensionPackage, info: BuildInfo, images: BuiltImage[]) => Promise<ExtensionRecord>;
|
|
32
36
|
export declare const build: (options?: BuildOptions, deps?: BuildDeps) => Promise<BuildResult>;
|
package/dist/tool/build.js
CHANGED
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
import { rm, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { recordOf, validateRecord } from '../discovery/discovery.js';
|
|
4
|
+
import {} from '../discovery/discovery.schema.js';
|
|
4
5
|
import {} from '../manifest/manifest.schema.js';
|
|
5
6
|
import { imageParts } from '../manifest/parts.js';
|
|
6
7
|
import {} from '../manifest/primitives.schema.js';
|
|
7
8
|
import { readManifest } from '../manifest/read-manifest.js';
|
|
8
9
|
import {} from '../runtime/build-info.js';
|
|
9
10
|
import { resolveBuildInfo } from './build-info.js';
|
|
11
|
+
import { assertConforms } from './check.js';
|
|
10
12
|
import { createDocker } from './docker.js';
|
|
11
13
|
import { mustRun, runCommand } from './exec.js';
|
|
12
14
|
import { labelsOf } from './labels.js';
|
|
15
|
+
import { recordExtras } from './record-extras.js';
|
|
13
16
|
import { RECORD_FILE, stageContext } from './stage.js';
|
|
14
|
-
|
|
15
|
-
const
|
|
17
|
+
import { TIMEOUTS } from './timeouts.js';
|
|
18
|
+
export const DEFAULT_REGISTRY = 'ghcr.io/agentage';
|
|
16
19
|
export const imageTag = (registry, pkg, part) => `${registry}/${pkg.agentage.id}-${part}:${pkg.version}`;
|
|
17
|
-
const partsToBuild = (pkg) => {
|
|
18
|
-
const parts = imageParts(pkg);
|
|
19
|
-
const unsupported = parts.filter((part) => !SUPPORTED_PARTS.includes(part));
|
|
20
|
-
if (unsupported.length > 0) {
|
|
21
|
-
throw new Error(`@agentage/extension 0.1 builds the api part only, not: ${unsupported.join(', ')}`);
|
|
22
|
-
}
|
|
23
|
-
return parts;
|
|
24
|
-
};
|
|
25
20
|
const compile = async (dir, pkg, run) => {
|
|
26
21
|
const scripts = pkg.scripts;
|
|
27
22
|
if (typeof scripts?.['build'] === 'string')
|
|
28
|
-
await mustRun(run, 'npm', ['run', 'build'], { cwd: dir });
|
|
23
|
+
await mustRun(run, 'npm', ['run', 'build'], { cwd: dir, timeoutMs: TIMEOUTS.compile });
|
|
29
24
|
};
|
|
30
25
|
const buildImage = async (job, docker, tmpRoot) => {
|
|
31
26
|
const context = await stageContext(job.dir, job.pkg, job.part, tmpRoot);
|
|
@@ -35,12 +30,8 @@ const buildImage = async (job, docker, tmpRoot) => {
|
|
|
35
30
|
BUILD_TIME: job.info.buildTime,
|
|
36
31
|
IMAGE: job.tag,
|
|
37
32
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
tag: job.tag,
|
|
41
|
-
buildArgs,
|
|
42
|
-
labels: labelsOf(job.pkg, job.part, job.info),
|
|
43
|
-
});
|
|
33
|
+
const labels = labelsOf(job.pkg, job.part, job.info);
|
|
34
|
+
await docker.build({ context, tag: job.tag, buildArgs, labels });
|
|
44
35
|
}
|
|
45
36
|
finally {
|
|
46
37
|
await rm(context, { recursive: true, force: true });
|
|
@@ -51,32 +42,36 @@ const pushImage = async (job, docker) => {
|
|
|
51
42
|
const repository = job.tag.slice(0, job.tag.lastIndexOf(':'));
|
|
52
43
|
return { part: job.part, tag: job.tag, digest, image: `${repository}@${digest}` };
|
|
53
44
|
};
|
|
54
|
-
|
|
45
|
+
// The record from the code and the images by digest; `up` writes the same shape from local digests.
|
|
46
|
+
export const recordFor = async (dir, pkg, info, images) => {
|
|
55
47
|
const byPart = Object.fromEntries(images.map((built) => [built.part, built.image]));
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
return validateRecord(recordOf(pkg, info, byPart, await recordExtras(dir, pkg)));
|
|
49
|
+
};
|
|
50
|
+
const writeRecord = async (dir, record, file = join(dir, 'dist', RECORD_FILE)) => {
|
|
58
51
|
await writeFile(file, `${JSON.stringify(record, null, 2)}\n`);
|
|
59
52
|
return file;
|
|
60
53
|
};
|
|
61
|
-
const buildParts = async (base,
|
|
54
|
+
const buildParts = async (base, options, deps) => {
|
|
62
55
|
const docker = deps.docker ?? createDocker(deps.run);
|
|
63
56
|
const registry = options.registry ?? DEFAULT_REGISTRY;
|
|
64
57
|
const images = [];
|
|
65
|
-
for (const part of
|
|
58
|
+
for (const part of imageParts(base.pkg)) {
|
|
66
59
|
const job = { ...base, part, tag: imageTag(registry, base.pkg, part) };
|
|
67
60
|
await buildImage(job, docker, deps.tmpRoot);
|
|
68
61
|
images.push(options.push ? await pushImage(job, docker) : { part, tag: job.tag });
|
|
69
62
|
}
|
|
70
63
|
return images;
|
|
71
64
|
};
|
|
65
|
+
// Validate, compile, check, one image per part from the kit recipe; --push pushes, writes the record.
|
|
72
66
|
export const build = async (options = {}, deps = { run: runCommand }) => {
|
|
73
67
|
const dir = resolve(options.dir ?? '.');
|
|
74
68
|
const pkg = await readManifest(dir);
|
|
75
|
-
const parts = partsToBuild(pkg);
|
|
76
69
|
const info = await resolveBuildInfo(dir, options.env ?? process.env, deps.run);
|
|
77
70
|
await compile(dir, pkg, deps.run);
|
|
78
|
-
|
|
79
|
-
const
|
|
71
|
+
await assertConforms({ dir, pkg });
|
|
72
|
+
const images = await buildParts({ dir, pkg, info }, options, deps);
|
|
80
73
|
const result = { id: pkg.agentage.id, version: pkg.version, ...info, images };
|
|
81
|
-
|
|
74
|
+
if (!options.push)
|
|
75
|
+
return result;
|
|
76
|
+
return { ...result, recordFile: await writeRecord(dir, await recordFor(dir, pkg, info, images)) };
|
|
82
77
|
};
|
|
@@ -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
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
export interface RunOptions {
|
|
2
2
|
cwd?: string;
|
|
3
3
|
echo?: boolean;
|
|
4
|
+
quiet?: boolean;
|
|
5
|
+
env?: Record<string, string | undefined>;
|
|
6
|
+
timeoutMs?: number;
|
|
4
7
|
}
|
|
5
8
|
export interface RunResult {
|
|
6
9
|
code: number;
|
|
7
10
|
stdout: string;
|
|
11
|
+
stderr?: string;
|
|
8
12
|
}
|
|
9
13
|
export type Run = (command: string, args: string[], options?: RunOptions) => Promise<RunResult>;
|
|
10
14
|
export declare const runCommand: Run;
|
|
11
15
|
export declare const mustRun: (run: Run, command: string, args: string[], options?: RunOptions) => Promise<string>;
|
|
16
|
+
export declare const quietly: (run: Run) => Run;
|
package/dist/tool/exec.js
CHANGED
|
@@ -1,20 +1,59 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import {} from 'node:stream';
|
|
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: '' };
|
|
6
24
|
child.stdout.on('data', (chunk) => {
|
|
7
|
-
stdout += chunk.toString();
|
|
8
|
-
if (
|
|
25
|
+
out.stdout += chunk.toString();
|
|
26
|
+
if (echo)
|
|
9
27
|
process.stderr.write(chunk);
|
|
10
28
|
});
|
|
11
|
-
child.on('
|
|
12
|
-
|
|
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
|
+
});
|
|
13
49
|
});
|
|
50
|
+
const tail = (text) => text ? `\n${text.trim().split('\n').slice(-15).join('\n')}` : '';
|
|
14
51
|
export const mustRun = async (run, command, args, options) => {
|
|
15
52
|
const result = await run(command, args, options);
|
|
16
53
|
if (result.code !== 0) {
|
|
17
|
-
throw new Error(`${command} ${args[0] ?? ''} failed with exit code ${result.code}`);
|
|
54
|
+
throw new Error(`${command} ${args[0] ?? ''} failed with exit code ${result.code}${tail(result.stderr)}`);
|
|
18
55
|
}
|
|
19
56
|
return result.stdout;
|
|
20
57
|
};
|
|
58
|
+
// Every command a runner starts runs quiet: what `up` and `down` print are their own lines.
|
|
59
|
+
export const quietly = (run) => (command, args, options) => run(command, args, { ...options, quiet: true });
|
package/dist/tool/index.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
export { checkCli } from '../command/check-cli.js';
|
|
2
|
+
export { runCommand, type RunCommandOptions } from '../command/run-command.js';
|
|
3
|
+
export { type ExtensionPackage, type CommandSpec } from '../manifest/manifest.schema.js';
|
|
4
|
+
export { readManifest, validateManifest } from '../manifest/read-manifest.js';
|
|
1
5
|
export { build, type BuildDeps, type BuildOptions, type BuildResult, type BuiltImage, } from './build.js';
|
|
2
6
|
export { buildLines } from './format.js';
|
|
7
|
+
export { KIT_VERSION } from './kit-version.js';
|
|
8
|
+
export { down, type DownOptions, type DownResult } from './up/down.js';
|
|
9
|
+
export { token, type TokenOptions } from './up/token.js';
|
|
10
|
+
export { up, type UpDeps, type UpOptions, type UpResult } from './up/up.js';
|
package/dist/tool/index.js
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
export { checkCli } from '../command/check-cli.js';
|
|
2
|
+
export { runCommand } from '../command/run-command.js';
|
|
3
|
+
export {} from '../manifest/manifest.schema.js';
|
|
4
|
+
export { readManifest, validateManifest } from '../manifest/read-manifest.js';
|
|
1
5
|
export { build, } from './build.js';
|
|
2
6
|
export { buildLines } from './format.js';
|
|
7
|
+
export { KIT_VERSION } from './kit-version.js';
|
|
8
|
+
export { down } from './up/down.js';
|
|
9
|
+
export { token } from './up/token.js';
|
|
10
|
+
export { up } from './up/up.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const KIT_VERSION: string;
|
package/dist/tool/main.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { type BuildOptions, type BuildResult } from './build.js';
|
|
2
|
+
import { type DownOptions } from './up/down.js';
|
|
3
|
+
import { type TokenOptions } from './up/token.js';
|
|
4
|
+
import { type UpOptions } from './up/up.js';
|
|
2
5
|
export interface MainDeps {
|
|
3
6
|
build: (options: BuildOptions) => Promise<BuildResult>;
|
|
7
|
+
up: (options: UpOptions) => Promise<unknown>;
|
|
8
|
+
down: (options: DownOptions) => Promise<unknown>;
|
|
9
|
+
token: (options: TokenOptions) => Promise<string>;
|
|
4
10
|
out: (line: string) => void;
|
|
5
11
|
err: (line: string) => void;
|
|
6
12
|
}
|
package/dist/tool/main.js
CHANGED
|
@@ -1,56 +1,81 @@
|
|
|
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';
|
|
7
|
+
import { token } from './up/token.js';
|
|
8
|
+
import { up } from './up/up.js';
|
|
9
|
+
const USAGE = `usage: agentage-extension <build|up|down|token> [options]
|
|
5
10
|
|
|
6
|
-
build one image per
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
build one image per part from the kit recipe
|
|
12
|
+
--push push the images and write dist/agentage-extension.json with their digests
|
|
13
|
+
--registry image registry and owner (default: ghcr.io/agentage)
|
|
14
|
+
up the release images on Docker: store, dev issuer, gateway on localhost:4280
|
|
15
|
+
down remove what up started; --keep-data keeps the store volume
|
|
16
|
+
token a dev token: --scope <id>:<verb> [--issuer <url>]
|
|
17
|
+
--dir the extension package folder (default: the current folder)`;
|
|
10
18
|
const defaultDeps = {
|
|
11
|
-
build
|
|
19
|
+
build,
|
|
20
|
+
up,
|
|
21
|
+
down,
|
|
22
|
+
token,
|
|
12
23
|
out: (line) => void process.stdout.write(`${line}\n`),
|
|
13
24
|
err: (line) => void process.stderr.write(`${line}\n`),
|
|
14
25
|
};
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
const OPTIONS = {
|
|
27
|
+
push: { type: 'boolean' },
|
|
28
|
+
dir: { type: 'string' },
|
|
29
|
+
registry: { type: 'string' },
|
|
30
|
+
'keep-data': { type: 'boolean' },
|
|
31
|
+
scope: { type: 'string' },
|
|
32
|
+
issuer: { type: 'string' },
|
|
33
|
+
help: { type: 'boolean', short: 'h' },
|
|
34
|
+
};
|
|
35
|
+
const parse = (argv) => parseArgs({ args: argv, allowPositionals: true, options: OPTIONS });
|
|
36
|
+
const COMMANDS = {
|
|
37
|
+
build: async (v, deps) => {
|
|
38
|
+
for (const line of buildLines(await deps.build({ push: v.push, dir: v.dir, registry: v.registry })))
|
|
39
|
+
deps.out(line);
|
|
40
|
+
},
|
|
41
|
+
up: async (v, deps) => void (await deps.up({ dir: v.dir, onLine: deps.out })),
|
|
42
|
+
down: async (v, deps) => void (await deps.down({ dir: v.dir, keepData: v['keep-data'], onLine: deps.out })),
|
|
43
|
+
token: async (v, deps) => {
|
|
44
|
+
if (!v.scope)
|
|
45
|
+
throw new UsageError('token needs --scope <id>:<verb>');
|
|
46
|
+
deps.out(await deps.token({ scope: v.scope, issuer: v.issuer }));
|
|
23
47
|
},
|
|
24
|
-
}
|
|
25
|
-
const
|
|
48
|
+
};
|
|
49
|
+
const runOne = async (name, values, deps) => {
|
|
26
50
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return 0;
|
|
51
|
+
await COMMANDS[name]?.(values, deps);
|
|
52
|
+
return EXIT_OK;
|
|
30
53
|
}
|
|
31
54
|
catch (err) {
|
|
32
|
-
|
|
33
|
-
|
|
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;
|
|
34
58
|
}
|
|
35
59
|
};
|
|
36
|
-
|
|
37
|
-
export const main = async (argv, deps = defaultDeps) => {
|
|
38
|
-
let parsed;
|
|
60
|
+
const parsedOrUsage = (argv, deps) => {
|
|
39
61
|
try {
|
|
40
|
-
|
|
62
|
+
return parse(argv);
|
|
41
63
|
}
|
|
42
64
|
catch (err) {
|
|
43
65
|
deps.err(`${err.message}\n${USAGE}`);
|
|
44
|
-
return
|
|
66
|
+
return null;
|
|
45
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;
|
|
46
74
|
const { positionals, values } = parsed;
|
|
47
|
-
if (values.help)
|
|
48
|
-
deps.out(USAGE);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return 2;
|
|
54
|
-
}
|
|
55
|
-
return runBuild({ push: values.push, dir: values.dir, registry: values.registry }, deps);
|
|
75
|
+
if (values.help)
|
|
76
|
+
return (deps.out(USAGE), EXIT_OK);
|
|
77
|
+
const name = positionals[0] ?? '';
|
|
78
|
+
if (!(name in COMMANDS) || positionals.length !== 1)
|
|
79
|
+
return (deps.err(USAGE), EXIT_USAGE);
|
|
80
|
+
return runOne(name, values, deps);
|
|
56
81
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type RecordExtras } from '../discovery/discovery.js';
|
|
2
|
+
import { type ExtensionPackage } from '../manifest/manifest.schema.js';
|
|
3
|
+
export declare const toolNames: (dir: string, pkg: ExtensionPackage) => Promise<string[]>;
|
|
4
|
+
export declare const recordExtras: (dir: string, pkg: ExtensionPackage) => Promise<RecordExtras>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { createContext } from '../context/context.js';
|
|
4
|
+
import {} from '../discovery/discovery.js';
|
|
5
|
+
import { WidgetSchema } from '../discovery/discovery.schema.js';
|
|
6
|
+
import { OPENAPI_PATH, readOpenapi } from '../discovery/openapi.js';
|
|
7
|
+
import { createLog } from '../log/log.js';
|
|
8
|
+
import { loadCreate } from '../manifest/entry.js';
|
|
9
|
+
import { readJsonIfExists } from '../manifest/read-manifest.js';
|
|
10
|
+
import {} from '../manifest/manifest.schema.js';
|
|
11
|
+
import { hasPostgres, normalizeParts } from '../manifest/parts.js';
|
|
12
|
+
import { createToolRegistry, prefixed } from '../mcp/tools.js';
|
|
13
|
+
import { sqlFiles } from '../store/migrations.js';
|
|
14
|
+
import {} from '../store/store.types.js';
|
|
15
|
+
const WidgetsSchema = z.array(WidgetSchema).min(1);
|
|
16
|
+
const noStore = {
|
|
17
|
+
query: async () => {
|
|
18
|
+
throw new Error('the store is not open while the build lists tools');
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const silent = createLog({ extension: 'build', part: 'mcp' }, () => undefined);
|
|
22
|
+
// The tool names the mcp part registers, read by running its create(ctx) with a recording ctx.
|
|
23
|
+
export const toolNames = async (dir, pkg) => {
|
|
24
|
+
const registry = createToolRegistry();
|
|
25
|
+
const base = createContext({
|
|
26
|
+
pkg,
|
|
27
|
+
part: 'mcp',
|
|
28
|
+
origin: 'http://127.0.0.1',
|
|
29
|
+
commit: '0'.repeat(40),
|
|
30
|
+
log: silent,
|
|
31
|
+
store: noStore,
|
|
32
|
+
});
|
|
33
|
+
await (await loadCreate(dir, pkg, 'mcp'))({ ...base, tool: registry.tool });
|
|
34
|
+
return registry.tools.map((tool) => prefixed(pkg.agentage.id, tool.name));
|
|
35
|
+
};
|
|
36
|
+
const widgetsOf = async (dir) => {
|
|
37
|
+
const value = await readJsonIfExists(join(dir, 'dist', 'ui', 'widgets.json'));
|
|
38
|
+
return value === undefined ? undefined : WidgetsSchema.parse(value);
|
|
39
|
+
};
|
|
40
|
+
// What the code serves, for the record: tools, widgets and the migration count.
|
|
41
|
+
export const recordExtras = async (dir, pkg) => {
|
|
42
|
+
const { code } = normalizeParts(pkg.agentage.parts);
|
|
43
|
+
const extras = {};
|
|
44
|
+
if (code.mcp) {
|
|
45
|
+
const tools = await toolNames(dir, pkg);
|
|
46
|
+
if (tools.length > 0)
|
|
47
|
+
extras.tools = tools;
|
|
48
|
+
}
|
|
49
|
+
if (code.ui)
|
|
50
|
+
extras.widgets = await widgetsOf(dir);
|
|
51
|
+
if (code.api && (await readOpenapi(dir)))
|
|
52
|
+
extras.openapi = OPENAPI_PATH;
|
|
53
|
+
if (hasPostgres(pkg))
|
|
54
|
+
extras.migrations = (await sqlFiles(join(dir, 'migrations'))).length;
|
|
55
|
+
return extras;
|
|
56
|
+
};
|
package/dist/tool/stage.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type ExtensionPackage } from '../manifest/manifest.schema.js';
|
|
2
2
|
import { type ImagePartKind } from '../manifest/primitives.schema.js';
|
|
3
3
|
export declare const RECORD_FILE = "agentage-extension.json";
|
|
4
|
+
export declare const MIGRATE_SHIM = "// Generated by @agentage/extension build.\nimport '@agentage/extension/migrate';\n";
|
|
4
5
|
export declare const localTarballs: (pkg: ExtensionPackage) => string[];
|
|
6
|
+
export declare const exists: (path: string) => Promise<boolean>;
|
|
7
|
+
export declare const appPaths: (dir: string, pkg: ExtensionPackage, part: ImagePartKind) => Promise<string[]>;
|
|
8
|
+
export declare const assertBuilt: (dir: string, pkg: ExtensionPackage, part: ImagePartKind) => Promise<void>;
|
|
5
9
|
export declare const stageContext: (dir: string, pkg: ExtensionPackage, part: ImagePartKind, tmpRoot?: string) => Promise<string>;
|