@akira-tl/forgerelay 0.5.4 → 0.5.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akira-tl/forgerelay",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "Local development control plane for MCP coding agents.",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/Akira-TL/forgerelay#readme",
@@ -44,7 +44,7 @@
44
44
  "release:parity": "node scripts/release-parity.mjs",
45
45
  "postinstall": "node scripts/fix-node-pty-permissions.mjs",
46
46
  "start": "node dist/cli.js serve",
47
- "test": "node --test scripts/release-proof.test.mjs && tsx src/config.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/logger.test.ts && tsx src/proxy-trust.test.ts && tsx src/mcp-app-template.test.ts && tsx src/hooks.test.ts && tsx src/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/roots.test.ts && tsx src/file-mutations.test.ts && tsx src/skills.test.ts && tsx src/workspace-store.test.ts && tsx src/activity/audit-store.test.ts && tsx src/activity/bash-output-store.test.ts && tsx src/activity/lifecycle.test.ts && tsx src/activity/query-service.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts",
47
+ "test": "node --test scripts/release-proof.test.mjs && tsx src/config.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/logger.test.ts && tsx src/proxy-trust.test.ts && tsx src/mcp-app-template.test.ts && tsx src/hooks.test.ts && tsx src/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/roots.test.ts && tsx src/file-mutations.test.ts && tsx src/operations/edit-preflight.test.ts && tsx src/skills.test.ts && tsx src/db/migrations.test.ts && tsx src/workspace-store.test.ts && tsx src/activity/audit-store.test.ts && tsx src/activity/bash-output-store.test.ts && tsx src/activity/lifecycle.test.ts && tsx src/activity/query-service.test.ts && tsx src/operations/core-operation-executor.test.ts && tsx src/operations/bulk-mutation.test.ts && tsx src/operations/batch/scheduler.test.ts && tsx src/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts",
48
48
  "typecheck": "tsc -p tsconfig.json --noEmit",
49
49
  "release:check": "node scripts/release-version.mjs check",
50
50
  "release:tag-check": "node scripts/release-version.mjs tag",
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execFileSync, spawnSync } from "node:child_process";
4
+ import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { dirname, join, resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const NODE_VERSION = "22.19.0";
10
+ const NPM_VERSION = "10.9.3";
11
+ const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
12
+ const npx = process.platform === "win32" ? "npx.cmd" : "npx";
13
+
14
+ main();
15
+
16
+ function main() {
17
+ assertCleanReleaseHead();
18
+ const tag = releaseTagFromHookPayload();
19
+ assertTagMatchesReleaseHead(tag);
20
+
21
+ const sandbox = mkdtempSync(join(tmpdir(), "forgerelay-release-hook-ci-"));
22
+ try {
23
+ copyTrackedTree(sandbox);
24
+ const env = {
25
+ ...process.env,
26
+ FORGERELAY_ALLOWED_ROOTS: sandbox,
27
+ FORGERELAY_OAUTH_OWNER_TOKEN: "ci-owner-token-that-is-long-enough",
28
+ FORGERELAY_PUBLIC_BASE_URL: "http://127.0.0.1:7676",
29
+ };
30
+
31
+ runNodeNpm(
32
+ sandbox,
33
+ env,
34
+ ["npm", "ci"],
35
+ `Node ${NODE_VERSION} / npm ${NPM_VERSION} install`,
36
+ );
37
+ runNodeNpm(sandbox, env, ["npm", "run", "release:check"], "Release metadata");
38
+ runNodeNpm(sandbox, env, ["npm", "run", "typecheck"], "Typecheck");
39
+ runNodeNpm(sandbox, env, ["npm", "test"], "Full test suite");
40
+ runNodeNpm(sandbox, env, ["npm", "run", "build"], "Build");
41
+ runNodeNpm(sandbox, env, ["npm", "run", "lsp:interop"], "Optional LSP interoperability");
42
+ runNode(
43
+ sandbox,
44
+ env,
45
+ ["dist/cli.js", "doctor"],
46
+ "Doctor",
47
+ );
48
+
49
+ execFileSync(process.execPath, [join(repoRoot, "scripts", "release-proof.mjs"), "write"], {
50
+ cwd: repoRoot,
51
+ env: process.env,
52
+ stdio: "inherit",
53
+ });
54
+ execFileSync(process.execPath, [join(repoRoot, "scripts", "release-proof.mjs"), "check-hook"], {
55
+ cwd: repoRoot,
56
+ env: process.env,
57
+ stdio: "inherit",
58
+ });
59
+
60
+ console.log(`Release tag local CI passed for ${tag} with cloud-equivalent Node ${NODE_VERSION} checks.`);
61
+ } finally {
62
+ rmSync(sandbox, { recursive: true, force: true, maxRetries: 8, retryDelay: 100 });
63
+ }
64
+ }
65
+
66
+ function git(args) {
67
+ return execFileSync("git", args, {
68
+ cwd: repoRoot,
69
+ encoding: "utf8",
70
+ stdio: ["ignore", "pipe", "pipe"],
71
+ }).trim();
72
+ }
73
+
74
+ function assertCleanReleaseHead() {
75
+ const status = git(["status", "--porcelain", "--untracked-files=all"]);
76
+ if (status) throw new Error("release tag local CI requires a clean working tree");
77
+ }
78
+
79
+ function releaseTagFromHookPayload() {
80
+ let payload;
81
+ try {
82
+ payload = JSON.parse(process.env.FORGERELAY_HOOK_PAYLOAD ?? "{}");
83
+ } catch {
84
+ throw new Error("FORGERELAY_HOOK_PAYLOAD is not valid JSON");
85
+ }
86
+ const command = typeof payload.command === "string" ? payload.command : "";
87
+ const match = /git\s+push\s+origin\s+(v\d+\.\d+\.\d+)/.exec(command);
88
+ if (!match?.[1]) throw new Error("release Hook payload does not contain a stable tag push");
89
+ return match[1];
90
+ }
91
+
92
+ function assertTagMatchesReleaseHead(tag) {
93
+ const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
94
+ const expectedTag = `v${pkg.version}`;
95
+ if (tag !== expectedTag) throw new Error(`tag ${tag} does not match package version ${pkg.version}`);
96
+ const head = git(["rev-parse", "HEAD"]);
97
+ let tagHead;
98
+ try {
99
+ tagHead = git(["rev-parse", `${tag}^{commit}`]);
100
+ } catch {
101
+ throw new Error(`local tag ${tag} does not exist or does not resolve to a commit`);
102
+ }
103
+ if (tagHead !== head) {
104
+ throw new Error(`tag ${tag} points to ${tagHead.slice(0, 12)}, but current HEAD is ${head.slice(0, 12)}`);
105
+ }
106
+ }
107
+
108
+ function copyTrackedTree(sandbox) {
109
+ const files = execFileSync("git", ["ls-files", "-z"], {
110
+ cwd: repoRoot,
111
+ encoding: "utf8",
112
+ }).split("\0").filter(Boolean);
113
+ for (const relativePath of files) {
114
+ const source = join(repoRoot, relativePath);
115
+ const destination = join(sandbox, relativePath);
116
+ mkdirSync(dirname(destination), { recursive: true });
117
+ cpSync(source, destination, { recursive: true, force: true });
118
+ }
119
+ }
120
+
121
+ function runNodeNpm(cwd, env, command, label) {
122
+ run(
123
+ cwd,
124
+ env,
125
+ ["--yes", "-p", `node@${NODE_VERSION}`, "-p", `npm@${NPM_VERSION}`, "--", ...command],
126
+ label,
127
+ );
128
+ }
129
+
130
+ function runNode(cwd, env, args, label) {
131
+ run(cwd, env, ["--yes", `node@${NODE_VERSION}`, ...args], label);
132
+ }
133
+
134
+ function run(cwd, env, args, label) {
135
+ console.log(`\n== ${label} ==`);
136
+ const result = spawnSync(npx, args, {
137
+ cwd,
138
+ env,
139
+ stdio: "inherit",
140
+ windowsHide: true,
141
+ });
142
+ if (result.error) throw result.error;
143
+ if (result.status !== 0) throw new Error(`${label} failed with exit ${result.status ?? "unknown"}`);
144
+ }