@akira-tl/forgerelay 1.1.2 → 1.2.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/CHANGELOG.md +38 -0
- package/README.md +14 -0
- package/dist/activity/runtime/mcp-query-tools.js +15 -5
- package/dist/cli/config/inspect.js +331 -0
- package/dist/cli/config/migrate.js +351 -0
- package/dist/cli/init/setup-config.js +41 -0
- package/dist/cli/init.js +128 -130
- package/dist/cli/maintenance.js +15 -5
- package/dist/cli/mcp/external-mcp.js +14 -10
- package/dist/cli/mcp/status.js +11 -6
- package/dist/cli.js +40 -7
- package/dist/lsp/language-server-config.js +66 -144
- package/dist/lsp/runtime/manager.js +35 -4
- package/dist/mcp/hooks/config.js +141 -0
- package/dist/mcp/hooks/external-mcp-transform.js +22 -13
- package/dist/mcp/hooks/hook-cli.js +62 -52
- package/dist/mcp/hooks/hooks.js +111 -85
- package/dist/mcp/operations/external-mcp/external-mcp-runtime.js +8 -5
- package/dist/mcp/operations/external-mcp/external-mcp.js +9 -2
- package/dist/mcp/server/core/capability-support.js +7 -0
- package/dist/runtime/config/config.js +142 -64
- package/dist/runtime/config/definition/catalog.js +12 -0
- package/dist/runtime/config/definition/definition.js +115 -0
- package/dist/runtime/config/definition/external-mcp.js +58 -0
- package/dist/runtime/config/definition/general-config.js +258 -0
- package/dist/runtime/config/definition/language-servers.js +116 -0
- package/dist/runtime/config/definition/schema.js +113 -0
- package/dist/runtime/config/definition/types.js +2 -0
- package/dist/runtime/config/external-mcp-auth-store.js +105 -29
- package/dist/runtime/config/external-mcp-config.js +39 -0
- package/dist/runtime/config/external-mcp-registry.js +284 -106
- package/dist/runtime/config/inspection/live.js +77 -0
- package/dist/runtime/config/resolution/general.js +93 -0
- package/dist/runtime/config/resolution/hooks.js +315 -0
- package/dist/runtime/config/resolution/language-servers.js +237 -0
- package/dist/runtime/config/resolution/project-sources.js +159 -0
- package/dist/runtime/config/resolution/resolver.js +485 -0
- package/dist/runtime/config/resolution/types.js +1 -0
- package/dist/runtime/config/runtime/config-runtime.js +74 -0
- package/dist/runtime/config/runtime/source-refresh.js +263 -0
- package/dist/runtime/config/user-config.js +38 -31
- package/dist/runtime/security/project-execution-trust.js +34 -0
- package/dist/runtime/testing/server-fixture.js +9 -8
- package/dist/server.js +10 -6
- package/dist/subagents/profiles.js +336 -53
- package/dist/subagents/sessions/execution.js +6 -35
- package/dist/subagents/sessions/manager.js +14 -2
- package/dist/ui/.vite/manifest.json +275 -275
- package/dist/ui/activity-panel-app.html +1 -1
- package/dist/ui/assets/{activity-panel-app-Bw7ZFX2g.js → activity-panel-app-BeDiwd7D.js} +3 -3
- package/dist/ui/assets/{angular-html-DeUNP12X.js → angular-html-DuoCE7nn.js} +1 -1
- package/dist/ui/assets/{angular-ts-B3v8FKi_.js → angular-ts-D5tGk0hL.js} +1 -1
- package/dist/ui/assets/{apl-Bs3iQm4P.js → apl-lciOTPbw.js} +1 -1
- package/dist/ui/assets/{astro-D6GHecBY.js → astro-vJbqfNnJ.js} +1 -1
- package/dist/ui/assets/{blade-BMNbVOHv.js → blade-C7655Q4K.js} +1 -1
- package/dist/ui/assets/{c-BquuA6bv.js → c-DdHp1Jrw.js} +1 -1
- package/dist/ui/assets/{cobol-BuZ6kfps.js → cobol-DgvzQxZP.js} +1 -1
- package/dist/ui/assets/{coffee-DgC5uBzL.js → coffee-BIjVUOUd.js} +1 -1
- package/dist/ui/assets/{cpp-BdvWqCaB.js → cpp-CU8tGzvI.js} +1 -1
- package/dist/ui/assets/{crystal-0Q5qBvNI.js → crystal-CUQAwsg9.js} +1 -1
- package/dist/ui/assets/{css-Be_T8idh.js → css-DtMTqD3F.js} +1 -1
- package/dist/ui/assets/{edge-DaGLdgh_.js → edge-DoCX9RnL.js} +1 -1
- package/dist/ui/assets/{elixir-CMgiFIzB.js → elixir-DJxV9_U6.js} +1 -1
- package/dist/ui/assets/{elm-B-ufMlMC.js → elm-DIgWqK4r.js} +1 -1
- package/dist/ui/assets/{erb-CaGiOZM-.js → erb-BHxP0c_e.js} +1 -1
- package/dist/ui/assets/{git-rebase-CpwUWCzK.js → git-rebase-HbI_Vo8d.js} +1 -1
- package/dist/ui/assets/{glimmer-js-BRxccvnE.js → glimmer-js-CthY4Ty_.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-CgQbPzlT.js → glimmer-ts-B2-_A1Xz.js} +1 -1
- package/dist/ui/assets/{glsl-D7JmHFr0.js → glsl-D9V9gq98.js} +1 -1
- package/dist/ui/assets/{graphql-C8n0eUBK.js → graphql-CddeAaS3.js} +1 -1
- package/dist/ui/assets/{hack-lVN7KVQv.js → hack-D_loGkOT.js} +1 -1
- package/dist/ui/assets/{haml-h18lG0pS.js → haml-BoWDczd0.js} +1 -1
- package/dist/ui/assets/{handlebars-lRFjRPfj.js → handlebars-ZoSOvw9y.js} +1 -1
- package/dist/ui/assets/{heavy-payload-Cqx9MOkH.js → heavy-payload-DO34LwoZ.js} +3 -3
- package/dist/ui/assets/{html-DtILqTE1.js → html-CE-obXVo.js} +1 -1
- package/dist/ui/assets/{html-derivative-CSTSmPup.js → html-derivative-Cd-smAbW.js} +1 -1
- package/dist/ui/assets/{http-DNI8fsbY.js → http-CZrkbrSl.js} +1 -1
- package/dist/ui/assets/{hurl-DgRi60JG.js → hurl-CxTuxBt1.js} +1 -1
- package/dist/ui/assets/{java-CW9pZ5bh.js → java-BshLjCat.js} +1 -1
- package/dist/ui/assets/{javascript-Cvg2UrWL.js → javascript-D2pxQTiX.js} +1 -1
- package/dist/ui/assets/{jinja-CZw_S1FB.js → jinja-kbfPP7-F.js} +1 -1
- package/dist/ui/assets/{jison-C56u_FZv.js → jison-C2mt9l1M.js} +1 -1
- package/dist/ui/assets/{json-DW2UTv1q.js → json-DU_rg_AU.js} +1 -1
- package/dist/ui/assets/{jsx-Zj5gobdD.js → jsx-D_8aQMlx.js} +1 -1
- package/dist/ui/assets/{julia-Cvcrre-L.js → julia-BmefTaVV.js} +1 -1
- package/dist/ui/assets/{just-8CZ2u6Tj.js → just-DXV8vk1S.js} +1 -1
- package/dist/ui/assets/{latex-D289AXWG.js → latex-Cjm2MvGW.js} +1 -1
- package/dist/ui/assets/{liquid-CFEX3SkZ.js → liquid-DyQDlo0u.js} +1 -1
- package/dist/ui/assets/{lua-BKpJ0ybO.js → lua-B2A1vzzy.js} +1 -1
- package/dist/ui/assets/{marko-D9maZ0a6.js → marko-B9vOkXOU.js} +1 -1
- package/dist/ui/assets/{mdc-rM9l0qWR.js → mdc-D42m6spU.js} +1 -1
- package/dist/ui/assets/{nginx-C9w6p2C9.js → nginx-TpfVxSUY.js} +1 -1
- package/dist/ui/assets/{nim-BRePHk0J.js → nim-CJr5T1FK.js} +1 -1
- package/dist/ui/assets/{perl-CskTmiPQ.js → perl-cJZcsdm9.js} +1 -1
- package/dist/ui/assets/{php-DCU_FvNI.js → php-CSZL5KRT.js} +1 -1
- package/dist/ui/assets/{pug-XLaVxTvv.js → pug-B1eMKzuA.js} +1 -1
- package/dist/ui/assets/{qml-CaONKSl-.js → qml-Dl2_7zc9.js} +1 -1
- package/dist/ui/assets/{r-BCVR0ZE3.js → r-F0aaO9gG.js} +1 -1
- package/dist/ui/assets/{razor-DDnGXxnU.js → razor-BPnkSMCw.js} +1 -1
- package/dist/ui/assets/{regexp-DWGTYYVx.js → regexp-BnGHMFbv.js} +1 -1
- package/dist/ui/assets/{rst-Dz3GWvQm.js → rst-B3CO7WYO.js} +1 -1
- package/dist/ui/assets/{ruby-DC3jGtMY.js → ruby-B6VMzT1T.js} +1 -1
- package/dist/ui/assets/{sas-7mNpvsjZ.js → sas-Df2lHVuU.js} +1 -1
- package/dist/ui/assets/{scss-uBsoCJVy.js → scss-4XxI6sgu.js} +1 -1
- package/dist/ui/assets/{shellscript-BCN9N_Jf.js → shellscript-DYASCN4r.js} +1 -1
- package/dist/ui/assets/{shellsession-Hsw46VXV.js → shellsession-Br6AGJO_.js} +1 -1
- package/dist/ui/assets/{soy-D6KV69HU.js → soy-p3ZNq1We.js} +1 -1
- package/dist/ui/assets/{sql-CVzVQkJh.js → sql-CAdv2OXL.js} +1 -1
- package/dist/ui/assets/{stata-BwUjXNhI.js → stata-Dx7yUbG_.js} +1 -1
- package/dist/ui/assets/{surrealql-CgloWqRa.js → surrealql-6YsE8HQa.js} +1 -1
- package/dist/ui/assets/{svelte-DcC3n6IC.js → svelte-YgzedwpD.js} +1 -1
- package/dist/ui/assets/{templ-DOFfnWTJ.js → templ-Bahqx3uD.js} +1 -1
- package/dist/ui/assets/{tex-ln89VPaX.js → tex-o6fTQjkH.js} +1 -1
- package/dist/ui/assets/{ts-tags-CWMzCstH.js → ts-tags-BO-Co0Ju.js} +1 -1
- package/dist/ui/assets/{tsx-DgzfiGeK.js → tsx-DtQe5AoH.js} +1 -1
- package/dist/ui/assets/{twig-Bi2itZL9.js → twig-BcUs6Kqc.js} +1 -1
- package/dist/ui/assets/{typescript-CQ24f4zW.js → typescript-BPtM8h4u.js} +1 -1
- package/dist/ui/assets/{vue-BS8jE5u6.js → vue-5KkGUzJJ.js} +1 -1
- package/dist/ui/assets/{vue-html-o38xqhyB.js → vue-html-Df_Xfsz9.js} +1 -1
- package/dist/ui/assets/{vue-vine-D2NvKv50.js → vue-vine-hUIe_u_Y.js} +1 -1
- package/dist/ui/assets/{xml-_44rx9_2.js → xml-C3Jy7as5.js} +1 -1
- package/dist/ui/assets/{xsl-B5QWQPoi.js → xsl-BnF9OuFo.js} +1 -1
- package/dist/ui/assets/{yaml-CKL2pEWe.js → yaml-DyrFgCou.js} +1 -1
- package/dist/workspaces/conversation-test-support.js +3 -11
- package/dist/workspaces/sessions.js +2 -0
- package/dist/workspaces/state/project-context.js +221 -0
- package/dist/workspaces.js +3 -1
- package/docs/chatgpt-coding-workflow.md +8 -3
- package/docs/configuration.md +99 -41
- package/docs/gotchas.md +24 -5
- package/docs/security.md +14 -3
- package/docs/setup.md +12 -7
- package/package.json +12 -3
- package/schemas/v1/config.project-local.schema.json +14 -0
- package/schemas/v1/config.project.schema.json +14 -0
- package/schemas/v1/config.user.schema.json +538 -0
- package/schemas/v1/hooks.project-local.schema.json +118 -0
- package/schemas/v1/hooks.project.schema.json +118 -0
- package/schemas/v1/hooks.user.schema.json +118 -0
- package/schemas/v1/language-servers.project-local.schema.json +230 -0
- package/schemas/v1/language-servers.project.schema.json +230 -0
- package/schemas/v1/language-servers.user.schema.json +230 -0
- package/schemas/v1/mcp.project-local.schema.json +143 -0
- package/schemas/v1/mcp.project.schema.json +143 -0
- package/schemas/v1/mcp.user.schema.json +143 -0
- package/scripts/ci/architecture.mjs +54 -4
- package/scripts/ci/cmd-acceptance.mjs +9 -9
- package/scripts/ci/config-v2-product-acceptance.mjs +639 -0
- package/scripts/ci/gates/acceptance-artifact.mjs +66 -0
- package/scripts/ci/gates/core-contract.mjs +27 -0
- package/scripts/ci/gates/platform-tests.mjs +38 -0
- package/scripts/ci/gates/prepare-windows-product.mjs +57 -0
- package/scripts/ci/gates/test-shard.mjs +71 -0
- package/scripts/ci/powershell51-acceptance.mjs +9 -9
- package/scripts/ci/pwsh-acceptance.mjs +9 -9
- package/scripts/ci/verify.mjs +2 -0
- package/scripts/ci/windows-product-acceptance.mjs +9 -10
- package/scripts/config/schema-text.mjs +4 -0
- package/scripts/config/schema-text.test.mjs +18 -0
- package/scripts/config/schema.mjs +74 -0
- package/scripts/lsp/interop.mjs +4 -1
- package/scripts/release/pack.mjs +64 -4
- package/scripts/release/release-gate.test.mjs +93 -27
- package/scripts/release-parity.mjs +19 -2
- package/templates/instructions/cmd.md +12 -0
- package/templates/instructions/fish.md +11 -0
- package/templates/instructions/powershell.md +14 -0
- package/templates/instructions/pwsh.md +14 -0
- package/templates/instructions/zsh.md +11 -0
- package/dist/runtime/instructions/powershell-skill.js +0 -65
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
|
|
5
|
+
export function resolveAcceptancePrefix(repoRoot) {
|
|
6
|
+
const configuredPrefix = process.env.FORGERELAY_ACCEPTANCE_PREFIX?.trim();
|
|
7
|
+
return configuredPrefix ? resolve(repoRoot, configuredPrefix) : undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function resolveAcceptanceTarball({ repoRoot, artifactDir, npmCli }) {
|
|
11
|
+
const configuredTarball = process.env.FORGERELAY_ACCEPTANCE_TARBALL?.trim();
|
|
12
|
+
const configuredDir = process.env.FORGERELAY_ACCEPTANCE_ARTIFACT_DIR?.trim();
|
|
13
|
+
if (configuredTarball && configuredDir) {
|
|
14
|
+
throw new Error("Set only one of FORGERELAY_ACCEPTANCE_TARBALL or FORGERELAY_ACCEPTANCE_ARTIFACT_DIR.");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (configuredTarball) {
|
|
18
|
+
const tarball = resolve(repoRoot, configuredTarball);
|
|
19
|
+
assertTarball(tarball, "configured acceptance tarball");
|
|
20
|
+
console.log(`Using prebuilt npm artifact: ${tarball}`);
|
|
21
|
+
return tarball;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (configuredDir) {
|
|
25
|
+
const directory = resolve(repoRoot, configuredDir);
|
|
26
|
+
const packages = existsSync(directory)
|
|
27
|
+
? readdirSync(directory).filter((name) => name.endsWith(".tgz"))
|
|
28
|
+
: [];
|
|
29
|
+
if (packages.length !== 1) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Acceptance artifact directory must contain exactly one .tgz package; found ${packages.length}: ${directory}`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const tarball = resolve(directory, packages[0]);
|
|
35
|
+
assertTarball(tarball, "downloaded acceptance tarball");
|
|
36
|
+
console.log(`Using prebuilt npm artifact: ${tarball}`);
|
|
37
|
+
return tarball;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const result = spawnSync(
|
|
41
|
+
process.execPath,
|
|
42
|
+
[npmCli, "pack", "--json", "--pack-destination", artifactDir],
|
|
43
|
+
{
|
|
44
|
+
cwd: repoRoot,
|
|
45
|
+
env: process.env,
|
|
46
|
+
encoding: "utf8",
|
|
47
|
+
windowsHide: true,
|
|
48
|
+
shell: false,
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
if (result.error || result.status !== 0) {
|
|
52
|
+
throw new Error(`npm pack failed: ${result.error?.message ?? result.stderr ?? result.stdout ?? result.status}`);
|
|
53
|
+
}
|
|
54
|
+
const report = JSON.parse(result.stdout);
|
|
55
|
+
const filename = report?.[0]?.filename;
|
|
56
|
+
if (!filename) throw new Error(`npm pack did not report a package filename: ${result.stdout}`);
|
|
57
|
+
const tarball = resolve(artifactDir, filename);
|
|
58
|
+
assertTarball(tarball, "locally packed acceptance tarball");
|
|
59
|
+
return tarball;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function assertTarball(tarball, label) {
|
|
63
|
+
if (!tarball.endsWith(".tgz") || !existsSync(tarball)) {
|
|
64
|
+
throw new Error(`${label} is missing or is not an npm .tgz artifact: ${tarball}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
const npmCli = process.env.npm_execpath;
|
|
6
|
+
if (!npmCli) throw new Error("ci:contract must run through npm so npm_execpath is available");
|
|
7
|
+
|
|
8
|
+
console.log(`Core CI contract: ${process.platform}/${process.arch} ${process.version}`);
|
|
9
|
+
runNpm(["--version"], "npm version");
|
|
10
|
+
runNpm(["run", "config:schema:check"], "Config schemas");
|
|
11
|
+
runNpm(["run", "architecture:check"], "Architecture");
|
|
12
|
+
runNpm(["run", "release:check"], "Release metadata");
|
|
13
|
+
runNpm(["run", "typecheck"], "Typecheck");
|
|
14
|
+
console.log("Core CI contract passed.");
|
|
15
|
+
|
|
16
|
+
function runNpm(args, label) {
|
|
17
|
+
console.log(`\n== ${label} ==`);
|
|
18
|
+
const result = spawnSync(process.execPath, [npmCli, ...args], {
|
|
19
|
+
cwd: process.cwd(),
|
|
20
|
+
env: process.env,
|
|
21
|
+
stdio: "inherit",
|
|
22
|
+
windowsHide: true,
|
|
23
|
+
shell: false,
|
|
24
|
+
});
|
|
25
|
+
if (result.error) throw result.error;
|
|
26
|
+
if (result.status !== 0) throw new Error(`${label} failed with exit ${result.status ?? "unknown"}`);
|
|
27
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
const npmCli = process.env.npm_execpath;
|
|
6
|
+
if (!npmCli) throw new Error("ci:platform-tests must run through npm so npm_execpath is available");
|
|
7
|
+
|
|
8
|
+
const tests = [
|
|
9
|
+
"src/cli/mcp/diagnostics.test.ts",
|
|
10
|
+
"src/runtime/shell/command-shell-runtime.test.ts",
|
|
11
|
+
"src/runtime/state/lock/file-lock.test.ts",
|
|
12
|
+
"src/runtime/state/runtime-lease.test.ts",
|
|
13
|
+
"src/runtime/state/db/migrations.test.ts",
|
|
14
|
+
"src/mcp/process/process-platform.test.ts",
|
|
15
|
+
"src/mcp/process/process-sessions.test.ts",
|
|
16
|
+
"src/mcp/filesystem/roots.test.ts",
|
|
17
|
+
"src/mcp/filesystem/file-mutations.test.ts",
|
|
18
|
+
"src/workspaces/state/workspace-store.test.ts",
|
|
19
|
+
"src/workspaces.test.ts",
|
|
20
|
+
"src/workspaces/conversation-checkout.test.ts",
|
|
21
|
+
"src/workspaces/conversation-worktree.test.ts",
|
|
22
|
+
"src/workspaces/git/worktree-recovery.test.ts",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
console.log(`Platform-sensitive CI tests: ${process.platform}/${process.arch} (${tests.length} files).`);
|
|
26
|
+
for (const path of tests) {
|
|
27
|
+
console.log(`\n== ${path} ==`);
|
|
28
|
+
const result = spawnSync(process.execPath, [npmCli, "exec", "--", "tsx", path], {
|
|
29
|
+
cwd: process.cwd(),
|
|
30
|
+
env: process.env,
|
|
31
|
+
stdio: "inherit",
|
|
32
|
+
windowsHide: true,
|
|
33
|
+
shell: false,
|
|
34
|
+
});
|
|
35
|
+
if (result.error) throw result.error;
|
|
36
|
+
if (result.status !== 0) throw new Error(`Platform-sensitive test failed with exit ${result.status ?? "unknown"}: ${path}`);
|
|
37
|
+
}
|
|
38
|
+
console.log("Platform-sensitive CI tests passed.");
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import assert from "node:assert/strict";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { existsSync, rmSync } from "node:fs";
|
|
6
|
+
import { mkdir } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { resolveAcceptancePrefix, resolveAcceptanceTarball } from "./acceptance-artifact.mjs";
|
|
9
|
+
|
|
10
|
+
if (process.platform !== "win32") {
|
|
11
|
+
console.log("Prepared Windows product install skipped outside Windows.");
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const npmCli = process.env.npm_execpath;
|
|
16
|
+
if (!npmCli) throw new Error("Windows product preparation must run through npm so npm_execpath is available");
|
|
17
|
+
|
|
18
|
+
const repoRoot = process.cwd();
|
|
19
|
+
const prefix = resolveAcceptancePrefix(repoRoot);
|
|
20
|
+
if (!prefix) {
|
|
21
|
+
throw new Error("FORGERELAY_ACCEPTANCE_PREFIX is required for the shared Windows product install.");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
rmSync(prefix, { recursive: true, force: true });
|
|
25
|
+
await mkdir(prefix, { recursive: true });
|
|
26
|
+
const artifactDir = join(prefix, ".artifact-fallback");
|
|
27
|
+
await mkdir(artifactDir, { recursive: true });
|
|
28
|
+
const tarball = resolveAcceptanceTarball({ repoRoot, artifactDir, npmCli });
|
|
29
|
+
|
|
30
|
+
const install = spawnSync(
|
|
31
|
+
process.execPath,
|
|
32
|
+
[npmCli, "install", "--global", "--prefix", prefix, "--no-audit", "--no-fund", tarball],
|
|
33
|
+
{
|
|
34
|
+
cwd: repoRoot,
|
|
35
|
+
env: process.env,
|
|
36
|
+
encoding: "utf8",
|
|
37
|
+
windowsHide: true,
|
|
38
|
+
shell: false,
|
|
39
|
+
timeout: 240_000,
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
if (install.error || install.status !== 0) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Shared Windows product install failed: ${install.error?.message ?? install.stderr ?? install.stdout ?? install.status}`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const installedRoot = join(prefix, "node_modules", "@akira-tl", "forgerelay");
|
|
49
|
+
for (const expected of [
|
|
50
|
+
join(installedRoot, "dist", "cli.js"),
|
|
51
|
+
join(prefix, "forgerelay.cmd"),
|
|
52
|
+
join(prefix, "forgerelay.ps1"),
|
|
53
|
+
]) {
|
|
54
|
+
assert.ok(existsSync(expected), `Prepared Windows product install is missing: ${expected}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(`Prepared shared Windows product install from ${tarball} at ${prefix}.`);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
|
|
7
|
+
const npmCli = process.env.npm_execpath;
|
|
8
|
+
if (!npmCli) throw new Error("ci:test-shard must run through npm so npm_execpath is available");
|
|
9
|
+
|
|
10
|
+
const shard = process.argv[2];
|
|
11
|
+
const allowed = new Set(["runtime-config", "workspace-mcp", "lsp", "subagent-ui-cli"]);
|
|
12
|
+
if (!allowed.has(shard)) {
|
|
13
|
+
throw new Error(`Unknown CI test shard '${shard ?? ""}'. Expected one of: ${[...allowed].join(", ")}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const repoRoot = process.cwd();
|
|
17
|
+
const pkg = JSON.parse(readFileSync(resolve(repoRoot, "package.json"), "utf8"));
|
|
18
|
+
const commands = String(pkg.scripts?.test ?? "").split(" && ").filter(Boolean);
|
|
19
|
+
if (commands.length === 0) throw new Error("package.json test script did not contain any commands");
|
|
20
|
+
|
|
21
|
+
const selected = commands.filter((command) => classify(command) === shard);
|
|
22
|
+
if (selected.length === 0) throw new Error(`CI test shard '${shard}' is empty`);
|
|
23
|
+
|
|
24
|
+
console.log(`CI test shard ${shard}: ${selected.length}/${commands.length} test commands.`);
|
|
25
|
+
for (const command of selected) runCommand(command);
|
|
26
|
+
console.log(`CI test shard ${shard} passed.`);
|
|
27
|
+
|
|
28
|
+
function classify(command) {
|
|
29
|
+
if (command === "npm run build:app") return "workspace-mcp";
|
|
30
|
+
if (command.includes("src/lsp/")) return "lsp";
|
|
31
|
+
if (
|
|
32
|
+
command.includes("src/workspaces/")
|
|
33
|
+
|| command.includes("src/workspaces.test")
|
|
34
|
+
|| command.includes("src/mcp/")
|
|
35
|
+
|| command.includes("src/activity/")
|
|
36
|
+
) return "workspace-mcp";
|
|
37
|
+
if (command.includes("src/runtime/") || command.startsWith("node --test scripts/")) {
|
|
38
|
+
return "runtime-config";
|
|
39
|
+
}
|
|
40
|
+
return "subagent-ui-cli";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function runCommand(command) {
|
|
44
|
+
console.log(`\n== ${command} ==`);
|
|
45
|
+
let executable;
|
|
46
|
+
let args;
|
|
47
|
+
if (command.startsWith("node --test ")) {
|
|
48
|
+
executable = process.execPath;
|
|
49
|
+
args = ["--test", ...command.slice("node --test ".length).split(" ").filter(Boolean)];
|
|
50
|
+
} else if (command.startsWith("tsx ")) {
|
|
51
|
+
executable = process.execPath;
|
|
52
|
+
args = [npmCli, "exec", "--", "tsx", ...command.slice(4).split(" ").filter(Boolean)];
|
|
53
|
+
} else if (command.startsWith("npm run ")) {
|
|
54
|
+
executable = process.execPath;
|
|
55
|
+
args = [npmCli, "run", command.slice("npm run ".length).trim()];
|
|
56
|
+
} else {
|
|
57
|
+
throw new Error(`Unsupported command in package.json test script: ${command}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync(executable, args, {
|
|
61
|
+
cwd: repoRoot,
|
|
62
|
+
env: process.env,
|
|
63
|
+
stdio: "inherit",
|
|
64
|
+
windowsHide: true,
|
|
65
|
+
shell: false,
|
|
66
|
+
});
|
|
67
|
+
if (result.error) throw result.error;
|
|
68
|
+
if (result.status !== 0) {
|
|
69
|
+
throw new Error(`CI test shard '${shard}' command failed with exit ${result.status ?? "unknown"}: ${command}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -6,6 +6,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { resolveAcceptancePrefix, resolveAcceptanceTarball } from "./gates/acceptance-artifact.mjs";
|
|
9
10
|
|
|
10
11
|
if (process.platform !== "win32") {
|
|
11
12
|
console.log("Windows PowerShell 5.1 packaged acceptance skipped outside Windows.");
|
|
@@ -362,7 +363,8 @@ async function exerciseHookRuntime(runtime) {
|
|
|
362
363
|
|
|
363
364
|
async function exercisePackagedPowerShellShim(powershell, expectedVersion) {
|
|
364
365
|
const artifactDir = join(root, "artifact");
|
|
365
|
-
const
|
|
366
|
+
const sharedPrefix = resolveAcceptancePrefix(process.cwd());
|
|
367
|
+
const prefix = sharedPrefix ?? join(root, "prefix");
|
|
366
368
|
const configDir = join(root, "config");
|
|
367
369
|
const stateDir = join(root, "state");
|
|
368
370
|
await Promise.all([
|
|
@@ -372,14 +374,12 @@ async function exercisePackagedPowerShellShim(powershell, expectedVersion) {
|
|
|
372
374
|
mkdir(stateDir, { recursive: true }),
|
|
373
375
|
]);
|
|
374
376
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
runNpm(["install", "--global", "--prefix", prefix, tarball]);
|
|
377
|
+
if (!sharedPrefix) {
|
|
378
|
+
const tarball = resolveAcceptanceTarball({ repoRoot: process.cwd(), artifactDir, npmCli });
|
|
379
|
+
runNpm(["install", "--global", "--prefix", prefix, tarball]);
|
|
380
|
+
} else {
|
|
381
|
+
console.log(`Using prepared acceptance install: ${prefix}`);
|
|
382
|
+
}
|
|
383
383
|
const shim = join(prefix, "forgerelay.ps1");
|
|
384
384
|
assert.ok(existsSync(shim), `npm did not create the PowerShell launcher shim: ${shim}`);
|
|
385
385
|
|
|
@@ -6,6 +6,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { resolveAcceptancePrefix, resolveAcceptanceTarball } from "./gates/acceptance-artifact.mjs";
|
|
9
10
|
|
|
10
11
|
if (process.platform !== "win32") {
|
|
11
12
|
console.log("PowerShell 7 packaged acceptance skipped outside Windows.");
|
|
@@ -338,7 +339,8 @@ async function exerciseHookRuntime(runtime) {
|
|
|
338
339
|
|
|
339
340
|
async function exercisePackagedPowerShellShim(pwsh, expectedVersion) {
|
|
340
341
|
const artifactDir = join(root, "artifact");
|
|
341
|
-
const
|
|
342
|
+
const sharedPrefix = resolveAcceptancePrefix(process.cwd());
|
|
343
|
+
const prefix = sharedPrefix ?? join(root, "prefix");
|
|
342
344
|
const configDir = join(root, "config");
|
|
343
345
|
const stateDir = join(root, "state");
|
|
344
346
|
await Promise.all([
|
|
@@ -348,14 +350,12 @@ async function exercisePackagedPowerShellShim(pwsh, expectedVersion) {
|
|
|
348
350
|
mkdir(stateDir, { recursive: true }),
|
|
349
351
|
]);
|
|
350
352
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
runNpm(["install", "--global", "--prefix", prefix, tarball]);
|
|
353
|
+
if (!sharedPrefix) {
|
|
354
|
+
const tarball = resolveAcceptanceTarball({ repoRoot: process.cwd(), artifactDir, npmCli });
|
|
355
|
+
runNpm(["install", "--global", "--prefix", prefix, tarball]);
|
|
356
|
+
} else {
|
|
357
|
+
console.log(`Using prepared acceptance install: ${prefix}`);
|
|
358
|
+
}
|
|
359
359
|
const shim = join(prefix, "forgerelay.ps1");
|
|
360
360
|
assert.ok(existsSync(shim), `npm did not create the PowerShell launcher shim: ${shim}`);
|
|
361
361
|
|
package/scripts/ci/verify.mjs
CHANGED
|
@@ -9,11 +9,13 @@ if (!npmCli) {
|
|
|
9
9
|
|
|
10
10
|
console.log(`CI environment: ${process.platform}/${process.arch} ${process.version}`);
|
|
11
11
|
runNpm(["--version"], "npm version");
|
|
12
|
+
runNpm(["run", "config:schema:check"], "Config schemas");
|
|
12
13
|
runNpm(["run", "architecture:check"], "Architecture");
|
|
13
14
|
runNpm(["run", "release:check"], "Release metadata");
|
|
14
15
|
runNpm(["run", "typecheck"], "Typecheck");
|
|
15
16
|
runNpm(["test"], "Full test suite");
|
|
16
17
|
runNpm(["run", "build"], "Build");
|
|
18
|
+
runNpm(["run", "config:product-accept"], "Config v2 packaged product acceptance");
|
|
17
19
|
if (process.platform === "win32") runNpm(["run", "pwsh:accept"], "PowerShell 7 packaged acceptance");
|
|
18
20
|
// The traffic audit is an end-to-end wire-budget check, not a platform feature
|
|
19
21
|
// matrix. Run it once on Linux so regressions are gated without tripling the
|
|
@@ -7,6 +7,7 @@ import { tmpdir } from "node:os";
|
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
|
+
import { resolveAcceptancePrefix, resolveAcceptanceTarball } from "./gates/acceptance-artifact.mjs";
|
|
10
11
|
|
|
11
12
|
if (process.platform !== "win32") {
|
|
12
13
|
console.log("Windows packaged product acceptance skipped outside Windows.");
|
|
@@ -22,7 +23,8 @@ const root = await import("node:fs/promises").then(({ mkdtemp }) =>
|
|
|
22
23
|
|
|
23
24
|
try {
|
|
24
25
|
const artifactDir = join(root, "artifact");
|
|
25
|
-
const
|
|
26
|
+
const sharedPrefix = resolveAcceptancePrefix(process.cwd());
|
|
27
|
+
const prefix = sharedPrefix ?? join(root, "prefix");
|
|
26
28
|
const projectRoot = join(root, "project");
|
|
27
29
|
await Promise.all([
|
|
28
30
|
mkdir(artifactDir, { recursive: true }),
|
|
@@ -31,14 +33,12 @@ try {
|
|
|
31
33
|
]);
|
|
32
34
|
await writeFile(join(projectRoot, "README.md"), "Windows product acceptance workspace\n", "utf8");
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
runNpm(["install", "--global", "--prefix", prefix, tarball]);
|
|
36
|
+
if (!sharedPrefix) {
|
|
37
|
+
const tarball = resolveAcceptanceTarball({ repoRoot: process.cwd(), artifactDir, npmCli });
|
|
38
|
+
runNpm(["install", "--global", "--prefix", prefix, tarball]);
|
|
39
|
+
} else {
|
|
40
|
+
console.log(`Using prepared acceptance install: ${prefix}`);
|
|
41
|
+
}
|
|
42
42
|
const shim = join(prefix, "forgerelay.cmd");
|
|
43
43
|
assert.ok(existsSync(shim), `npm did not create the Windows launcher shim: ${shim}`);
|
|
44
44
|
|
|
@@ -105,7 +105,6 @@ async function exercisePackagedRuntime({ family, executable, doctorIdentity, roo
|
|
|
105
105
|
executable,
|
|
106
106
|
},
|
|
107
107
|
shellInstructions: true,
|
|
108
|
-
skillsEnabled: false,
|
|
109
108
|
}, null, 2),
|
|
110
109
|
"utf8",
|
|
111
110
|
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { generatedSchemaTextMatches } from "./schema-text.mjs";
|
|
4
|
+
|
|
5
|
+
test("generated schema freshness accepts CRLF checkout line endings", () => {
|
|
6
|
+
const expected = "{\n \"title\": \"Config\"\n}\n";
|
|
7
|
+
const windowsCheckout = expected.replaceAll("\n", "\r\n");
|
|
8
|
+
|
|
9
|
+
assert.equal(generatedSchemaTextMatches(windowsCheckout, expected), true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("generated schema freshness still rejects semantic content changes", () => {
|
|
13
|
+
const expected = "{\n \"title\": \"Config\"\n}\n";
|
|
14
|
+
const changed = "{\r\n \"title\": \"Changed\"\r\n}\r\n";
|
|
15
|
+
|
|
16
|
+
assert.equal(generatedSchemaTextMatches(changed, expected), false);
|
|
17
|
+
assert.equal(generatedSchemaTextMatches(undefined, expected), false);
|
|
18
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { CONFIG_DEFINITION_CATALOG } from "../../src/runtime/config/definition/catalog.ts";
|
|
7
|
+
import { generateConfigSchemaFiles } from "../../src/runtime/config/definition/schema.ts";
|
|
8
|
+
import { generatedSchemaTextMatches } from "./schema-text.mjs";
|
|
9
|
+
|
|
10
|
+
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
11
|
+
const command = process.argv[2] ?? "check";
|
|
12
|
+
const generated = generateConfigSchemaFiles(CONFIG_DEFINITION_CATALOG)
|
|
13
|
+
.map((entry) => ({
|
|
14
|
+
...entry,
|
|
15
|
+
content: `${JSON.stringify(entry.schema, null, 2)}\n`,
|
|
16
|
+
}))
|
|
17
|
+
.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
18
|
+
|
|
19
|
+
if (command === "write") {
|
|
20
|
+
for (const entry of generated) {
|
|
21
|
+
const path = resolve(repoRoot, entry.relativePath);
|
|
22
|
+
await mkdir(dirname(path), { recursive: true });
|
|
23
|
+
await writeFile(path, entry.content, "utf8");
|
|
24
|
+
}
|
|
25
|
+
await removeStaleCurrentMajorSchemas();
|
|
26
|
+
console.log(`Generated ${generated.length} Config System v1 schema files.`);
|
|
27
|
+
} else if (command === "check") {
|
|
28
|
+
const mismatches = [];
|
|
29
|
+
for (const entry of generated) {
|
|
30
|
+
const path = resolve(repoRoot, entry.relativePath);
|
|
31
|
+
let actual;
|
|
32
|
+
try {
|
|
33
|
+
actual = await readFile(path, "utf8");
|
|
34
|
+
} catch {
|
|
35
|
+
actual = undefined;
|
|
36
|
+
}
|
|
37
|
+
if (!generatedSchemaTextMatches(actual, entry.content)) mismatches.push(entry.relativePath);
|
|
38
|
+
}
|
|
39
|
+
const expected = new Set(generated.map((entry) => entry.relativePath));
|
|
40
|
+
for (const path of await currentMajorSchemaPaths()) {
|
|
41
|
+
if (!expected.has(path)) mismatches.push(path);
|
|
42
|
+
}
|
|
43
|
+
if (mismatches.length > 0) {
|
|
44
|
+
console.error("Generated Config schemas are out of date:");
|
|
45
|
+
for (const path of [...new Set(mismatches)].sort()) console.error(`- ${path}`);
|
|
46
|
+
console.error("Run: npm run config:schema:generate");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
console.log(`Config schema check passed: ${generated.length} generated v1 schema files are current.`);
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error(`Unknown schema command ${JSON.stringify(command)}; expected check or write.`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function removeStaleCurrentMajorSchemas() {
|
|
55
|
+
const expected = new Set(generated.map((entry) => entry.relativePath));
|
|
56
|
+
const { rm } = await import("node:fs/promises");
|
|
57
|
+
for (const relativePath of await currentMajorSchemaPaths()) {
|
|
58
|
+
if (!expected.has(relativePath)) await rm(resolve(repoRoot, relativePath));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function currentMajorSchemaPaths() {
|
|
63
|
+
const directory = resolve(repoRoot, "schemas/v1");
|
|
64
|
+
let names;
|
|
65
|
+
try {
|
|
66
|
+
names = await readdir(directory);
|
|
67
|
+
} catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
return names
|
|
71
|
+
.filter((name) => name.endsWith(".schema.json"))
|
|
72
|
+
.map((name) => `schemas/v1/${name}`)
|
|
73
|
+
.sort();
|
|
74
|
+
}
|
package/scripts/lsp/interop.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { CodeIntelligenceManager } from "../../dist/lsp/runtime/manager.js";
|
|
6
|
+
import { ConfigRuntime } from "../../dist/runtime/config/runtime/config-runtime.js";
|
|
6
7
|
import { findExecutable, probeExecutable } from "./interop-support.mjs";
|
|
7
8
|
|
|
8
9
|
const repoRoot = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
|
@@ -121,8 +122,10 @@ for (const fixture of fixtures) {
|
|
|
121
122
|
|
|
122
123
|
const root = join(interopRoot, fixture.id);
|
|
123
124
|
fixture.setup(root);
|
|
125
|
+
const configDir = join(root, ".forgerelay-user");
|
|
126
|
+
mkdirSync(configDir, { recursive: true });
|
|
124
127
|
const manager = new CodeIntelligenceManager(
|
|
125
|
-
{
|
|
128
|
+
{ configDir, configRuntime: new ConfigRuntime() },
|
|
126
129
|
{
|
|
127
130
|
startTimeoutMs: 30_000,
|
|
128
131
|
requestTimeoutMs: 30_000,
|
package/scripts/release/pack.mjs
CHANGED
|
@@ -9,23 +9,27 @@ if (!npmCli) {
|
|
|
9
9
|
throw new Error("release:pack must be launched through npm so npm_execpath is available");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const repoRoot = process.cwd();
|
|
13
|
+
const outputDir = resolve(repoRoot, process.env.RELEASE_ARTIFACT_DIR ?? ".release-artifacts");
|
|
13
14
|
rmSync(outputDir, { recursive: true, force: true });
|
|
14
15
|
mkdirSync(outputDir, { recursive: true });
|
|
15
16
|
|
|
16
17
|
const result = spawnSync(
|
|
17
18
|
process.execPath,
|
|
18
|
-
[npmCli, "pack", "--pack-destination", outputDir],
|
|
19
|
+
[npmCli, "pack", "--json", "--pack-destination", outputDir],
|
|
19
20
|
{
|
|
20
|
-
cwd:
|
|
21
|
+
cwd: repoRoot,
|
|
21
22
|
env: process.env,
|
|
22
|
-
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
23
25
|
windowsHide: true,
|
|
24
26
|
shell: false,
|
|
25
27
|
},
|
|
26
28
|
);
|
|
29
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
27
30
|
if (result.error) throw result.error;
|
|
28
31
|
if (result.status !== 0) {
|
|
32
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
29
33
|
throw new Error(`npm pack failed with exit ${result.status ?? "unknown"}`);
|
|
30
34
|
}
|
|
31
35
|
|
|
@@ -33,4 +37,60 @@ const packages = readdirSync(outputDir).filter((name) => name.endsWith(".tgz"));
|
|
|
33
37
|
if (packages.length !== 1) {
|
|
34
38
|
throw new Error(`release:pack expected exactly one .tgz artifact, found ${packages.length}`);
|
|
35
39
|
}
|
|
40
|
+
|
|
41
|
+
const report = parsePackReport(result.stdout);
|
|
42
|
+
const packagedPaths = new Set(report.files.map((file) => file.path));
|
|
43
|
+
const expectedSchemas = readdirSync(resolve(repoRoot, "schemas", "v1"))
|
|
44
|
+
.filter((name) => name.endsWith(".schema.json"))
|
|
45
|
+
.map((name) => `schemas/v1/${name}`)
|
|
46
|
+
.sort();
|
|
47
|
+
if (expectedSchemas.length === 0) {
|
|
48
|
+
throw new Error("release:pack expected at least one generated Config System v1 schema");
|
|
49
|
+
}
|
|
50
|
+
const missingSchemas = expectedSchemas.filter((path) => !packagedPaths.has(path));
|
|
51
|
+
if (missingSchemas.length > 0) {
|
|
52
|
+
throw new Error(`release:pack omitted generated Config schemas: ${missingSchemas.join(", ")}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const expectedRuntimeResources = [
|
|
56
|
+
"dist/cli.js",
|
|
57
|
+
...collectPackageFiles(resolve(repoRoot, "templates"), "templates"),
|
|
58
|
+
...collectPackageFiles(resolve(repoRoot, "capabilities"), "capabilities"),
|
|
59
|
+
].sort();
|
|
60
|
+
const missingRuntimeResources = expectedRuntimeResources.filter((path) => !packagedPaths.has(path));
|
|
61
|
+
if (missingRuntimeResources.length > 0) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`release:pack omitted ForgeRelay-owned runtime resources: ${missingRuntimeResources.join(", ")}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
36
67
|
console.log(`Verified npm artifact: ${packages[0]}`);
|
|
68
|
+
console.log(`Verified packaged Config schemas: ${expectedSchemas.join(", ")}`);
|
|
69
|
+
console.log(`Verified packaged ForgeRelay-owned runtime resources: ${expectedRuntimeResources.join(", ")}`);
|
|
70
|
+
|
|
71
|
+
function collectPackageFiles(directory, prefix) {
|
|
72
|
+
const files = [];
|
|
73
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
74
|
+
const relative = `${prefix}/${entry.name}`;
|
|
75
|
+
if (entry.isDirectory()) {
|
|
76
|
+
files.push(...collectPackageFiles(resolve(directory, entry.name), relative));
|
|
77
|
+
} else if (entry.isFile()) {
|
|
78
|
+
files.push(relative);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return files;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parsePackReport(stdout) {
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = JSON.parse(stdout);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
90
|
+
throw new Error(`npm pack did not return valid JSON metadata: ${reason}`);
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(parsed) || parsed.length !== 1 || !Array.isArray(parsed[0]?.files)) {
|
|
93
|
+
throw new Error("npm pack returned an unexpected JSON report");
|
|
94
|
+
}
|
|
95
|
+
return parsed[0];
|
|
96
|
+
}
|