@akira-tl/forgerelay 0.6.0 → 0.6.2
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 +36 -0
- package/README.md +47 -12
- package/dist/activity/mcp-query-tools.js +48 -2
- package/dist/activity/query-service.js +1 -0
- package/dist/cli.js +181 -4
- package/dist/composite-activity.js +155 -0
- package/dist/composite-workspaces.js +197 -0
- package/dist/config.js +4 -1
- package/dist/oauth/router.js +21 -1
- package/dist/oauth-provider.js +32 -4
- package/dist/oauth-store.js +9 -0
- package/dist/remote-auth.js +110 -0
- package/dist/remote-transport.js +196 -0
- package/dist/remote-workspace-relay.js +473 -0
- package/dist/server.js +824 -121
- package/dist/ui/.vite/manifest.json +33 -33
- package/dist/ui/activity-panel-app.html +3 -3
- package/dist/ui/assets/{activity-panel-app-CjZVvVNc.js → activity-panel-app-E1ju2dqI.js} +1 -1
- package/dist/ui/assets/{heavy-payload-vGgBRvNX.js → heavy-payload-CeW-n9w5.js} +1 -1
- package/dist/ui/assets/{review-payload-4erWKckt.js → review-payload-B9CO298v.js} +1 -1
- package/dist/ui/assets/{scrollbar-CaOPzUJd.js → scrollbar-C2twAENW.js} +1 -1
- package/dist/ui/assets/workspace-app-BztEvZIC.js +5 -0
- package/dist/ui/assets/{workspace-app-DkAiSl_0.js → workspace-app-CwbJnb_w.js} +1 -1
- package/dist/ui/assets/workspace-app-YnUST8IP.css +1 -0
- package/dist/ui/assets/workspace-app-rKuhdae8.js +1 -0
- package/dist/ui/assets/workspace-lifecycle-app-CEfMdudP.js +1 -0
- package/dist/ui/workspace-app.html +4 -4
- package/dist/ui/workspace-lifecycle-app.html +4 -4
- package/dist/user-config.js +131 -5
- package/docs/configuration.md +26 -3
- package/docs/debugging.md +7 -0
- package/docs/versioning.md +11 -19
- package/package.json +5 -2
- package/scripts/ci/verify.mjs +38 -0
- package/scripts/debug/runtime.mjs +23 -1
- package/scripts/debug/runtime.test.mjs +14 -2
- package/scripts/debug/serve.mjs +4 -4
- package/scripts/release/pack.mjs +36 -0
- package/scripts/release/publish.mjs +157 -0
- package/scripts/release/release-gate.test.mjs +73 -16
- package/scripts/release-parity.mjs +5 -13
- package/scripts/release-proof.mjs +28 -19
- package/scripts/release-proof.test.mjs +32 -11
- package/scripts/release-version.mjs +1 -1
- package/dist/ui/assets/workspace-app-CcrHAUIn.css +0 -1
- package/dist/ui/assets/workspace-app-DJmkPYJC.js +0 -1
- package/dist/ui/assets/workspace-app-QyauBrJX.js +0 -5
- package/dist/ui/assets/workspace-lifecycle-app-BIXEo53I.js +0 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
9
|
+
const repository = "Akira-TL/forgerelay";
|
|
10
|
+
const releaseTag = requiredEnv("RELEASE_TAG");
|
|
11
|
+
const packageDir = resolve(repoRoot, process.env.RELEASE_PACKAGE_DIR ?? ".release-package");
|
|
12
|
+
const npmCli = process.env.npm_execpath;
|
|
13
|
+
if (!npmCli) {
|
|
14
|
+
throw new Error("release:publish must be launched through npm so npm_execpath is available");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
|
|
18
|
+
const expectedTag = `v${pkg.version}`;
|
|
19
|
+
if (releaseTag !== expectedTag) {
|
|
20
|
+
throw new Error(`release tag ${releaseTag} does not match package version ${pkg.version}; expected ${expectedTag}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const head = git(["rev-parse", "HEAD"]);
|
|
24
|
+
const tagHead = git(["rev-parse", `${releaseTag}^{commit}`]);
|
|
25
|
+
if (head !== tagHead) {
|
|
26
|
+
throw new Error(`checked-out HEAD ${head.slice(0, 12)} does not match ${releaseTag} at ${tagHead.slice(0, 12)}`);
|
|
27
|
+
}
|
|
28
|
+
if (!gitSucceeds(["merge-base", "--is-ancestor", tagHead, "origin/main"])) {
|
|
29
|
+
throw new Error(`${releaseTag} does not point to a commit contained in origin/main`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const packages = readdirSync(packageDir).filter((name) => name.endsWith(".tgz"));
|
|
33
|
+
if (packages.length !== 1) {
|
|
34
|
+
throw new Error(`release package directory must contain exactly one .tgz artifact, found ${packages.length}`);
|
|
35
|
+
}
|
|
36
|
+
const packagePath = join(packageDir, packages[0]);
|
|
37
|
+
const expectedPackageName = `${pkg.name.replace(/^@/, "").replaceAll("/", "-")}-${pkg.version}.tgz`;
|
|
38
|
+
if (basename(packagePath) !== expectedPackageName) {
|
|
39
|
+
throw new Error(`unexpected release artifact ${basename(packagePath)}; expected ${expectedPackageName}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const npmTag = pkg.version.includes("-rc.") ? "next" : "latest";
|
|
43
|
+
const packageSpec = `${pkg.name}@${pkg.version}`;
|
|
44
|
+
if (npmPackageExists(packageSpec)) {
|
|
45
|
+
console.log(`${packageSpec} is already published; leaving npm unchanged.`);
|
|
46
|
+
} else {
|
|
47
|
+
console.log(`Publishing verified artifact ${basename(packagePath)} with npm tag ${npmTag}.`);
|
|
48
|
+
const publishEnv = { ...process.env };
|
|
49
|
+
if (!publishEnv.NODE_AUTH_TOKEN) delete publishEnv.NODE_AUTH_TOKEN;
|
|
50
|
+
runNpm(["publish", packagePath, "--access", "public", "--tag", npmTag], "npm publish", publishEnv);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (ghReleaseExists(releaseTag)) {
|
|
54
|
+
console.log(`GitHub Release ${releaseTag} already exists; leaving it unchanged.`);
|
|
55
|
+
} else {
|
|
56
|
+
const notesPath = prepareReleaseNotes(releaseTag);
|
|
57
|
+
const args = [
|
|
58
|
+
"release",
|
|
59
|
+
"create",
|
|
60
|
+
releaseTag,
|
|
61
|
+
"--repo",
|
|
62
|
+
repository,
|
|
63
|
+
"--verify-tag",
|
|
64
|
+
"--title",
|
|
65
|
+
releaseTag,
|
|
66
|
+
"--notes-file",
|
|
67
|
+
notesPath,
|
|
68
|
+
];
|
|
69
|
+
if (npmTag === "next") args.push("--prerelease");
|
|
70
|
+
run("gh", args, "GitHub Release");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function requiredEnv(name) {
|
|
74
|
+
const value = process.env[name]?.trim();
|
|
75
|
+
if (!value) throw new Error(`${name} is required`);
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function git(args) {
|
|
80
|
+
return execFileSync("git", args, {
|
|
81
|
+
cwd: repoRoot,
|
|
82
|
+
encoding: "utf8",
|
|
83
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
84
|
+
}).trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function gitSucceeds(args) {
|
|
88
|
+
const result = spawnSync("git", args, {
|
|
89
|
+
cwd: repoRoot,
|
|
90
|
+
stdio: "ignore",
|
|
91
|
+
windowsHide: true,
|
|
92
|
+
shell: false,
|
|
93
|
+
});
|
|
94
|
+
if (result.error) throw result.error;
|
|
95
|
+
return result.status === 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function npmPackageExists(spec) {
|
|
99
|
+
const result = spawnSync(process.execPath, [npmCli, "view", spec, "version", "--json"], {
|
|
100
|
+
cwd: repoRoot,
|
|
101
|
+
env: process.env,
|
|
102
|
+
encoding: "utf8",
|
|
103
|
+
windowsHide: true,
|
|
104
|
+
shell: false,
|
|
105
|
+
});
|
|
106
|
+
if (result.error) throw result.error;
|
|
107
|
+
if (result.status === 0) return true;
|
|
108
|
+
const detail = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
109
|
+
if (/E404|404 Not Found/i.test(detail)) return false;
|
|
110
|
+
throw new Error(`npm view failed while checking ${spec}: ${detail.trim() || `exit ${result.status ?? "unknown"}`}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function runNpm(args, label, env = process.env) {
|
|
114
|
+
run(process.execPath, [npmCli, ...args], label, env);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function ghReleaseExists(tag) {
|
|
118
|
+
const result = spawnSync("gh", ["release", "view", tag, "--repo", repository], {
|
|
119
|
+
cwd: repoRoot,
|
|
120
|
+
env: process.env,
|
|
121
|
+
stdio: "ignore",
|
|
122
|
+
windowsHide: true,
|
|
123
|
+
shell: false,
|
|
124
|
+
});
|
|
125
|
+
if (result.error) throw result.error;
|
|
126
|
+
return result.status === 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function prepareReleaseNotes(tag) {
|
|
130
|
+
const manualNotes = join(repoRoot, "docs", "releases", `${tag}.md`);
|
|
131
|
+
const notes = existsSync(manualNotes)
|
|
132
|
+
? readFileSync(manualNotes, "utf8")
|
|
133
|
+
: execFileSync(process.execPath, ["scripts/release-version.mjs", "notes", tag], {
|
|
134
|
+
cwd: repoRoot,
|
|
135
|
+
encoding: "utf8",
|
|
136
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
137
|
+
});
|
|
138
|
+
const tempRoot = resolve(process.env.RUNNER_TEMP ?? join(repoRoot, ".forgerelay-debug"));
|
|
139
|
+
mkdirSync(tempRoot, { recursive: true });
|
|
140
|
+
const notesPath = join(tempRoot, `release-notes-${tag}.md`);
|
|
141
|
+
writeFileSync(notesPath, notes.endsWith("\n") ? notes : `${notes}\n`);
|
|
142
|
+
return notesPath;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function run(command, args, label, env = process.env) {
|
|
146
|
+
const result = spawnSync(command, args, {
|
|
147
|
+
cwd: repoRoot,
|
|
148
|
+
env,
|
|
149
|
+
stdio: "inherit",
|
|
150
|
+
windowsHide: true,
|
|
151
|
+
shell: false,
|
|
152
|
+
});
|
|
153
|
+
if (result.error) throw result.error;
|
|
154
|
+
if (result.status !== 0) {
|
|
155
|
+
throw new Error(`${label} failed with exit ${result.status ?? "unknown"}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -10,14 +10,25 @@ async function readJson(relativePath) {
|
|
|
10
10
|
return JSON.parse(await readFile(resolve(repoRoot, relativePath), "utf8"));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
test("release tag Hook is a fast
|
|
14
|
-
const hook = await readJson(".forgerelay/hooks/release-tag-
|
|
13
|
+
test("release tag Hook is a fast repository-state gate for common origin tag push forms", async () => {
|
|
14
|
+
const hook = await readJson(".forgerelay/hooks/release-tag-gate.json");
|
|
15
15
|
assert.equal(hook.event, "BeforeTool");
|
|
16
16
|
assert.equal(hook.command, "node scripts/release-proof.mjs check-hook");
|
|
17
17
|
assert.ok(hook.timeoutSeconds <= 30);
|
|
18
|
+
|
|
19
|
+
const matcher = new RegExp(hook.matcher.commandRegex);
|
|
20
|
+
for (const command of [
|
|
21
|
+
"git push origin v1.2.3",
|
|
22
|
+
"git push --atomic origin v1.2.3",
|
|
23
|
+
"git push origin refs/tags/v1.2.3",
|
|
24
|
+
"git status && git push origin tag v1.2.3 && echo done",
|
|
25
|
+
]) {
|
|
26
|
+
assert.match(command, matcher);
|
|
27
|
+
}
|
|
28
|
+
assert.doesNotMatch("git push origin main", matcher);
|
|
18
29
|
});
|
|
19
30
|
|
|
20
|
-
test("release:verify records proof only after the cloud-equivalent parity gate", async () => {
|
|
31
|
+
test("optional release:verify records proof only after the cloud-equivalent parity gate", async () => {
|
|
21
32
|
const pkg = await readJson("package.json");
|
|
22
33
|
assert.equal(
|
|
23
34
|
pkg.scripts["release:verify"],
|
|
@@ -25,19 +36,65 @@ test("release:verify records proof only after the cloud-equivalent parity gate",
|
|
|
25
36
|
);
|
|
26
37
|
});
|
|
27
38
|
|
|
28
|
-
test("
|
|
29
|
-
const
|
|
30
|
-
assert.match(
|
|
31
|
-
assert.match(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
test("cross-platform cloud CI delegates to one shell-free verification entrypoint", async () => {
|
|
40
|
+
const workflow = await readFile(resolve(repoRoot, ".github/workflows/ci.yml"), "utf8");
|
|
41
|
+
assert.match(workflow, /node-version-file:\s*\.nvmrc/);
|
|
42
|
+
assert.match(workflow, /run:\s*npm ci/);
|
|
43
|
+
assert.match(workflow, /run:\s*npm run ci:verify/);
|
|
44
|
+
assert.doesNotMatch(workflow, /shell:/);
|
|
45
|
+
assert.doesNotMatch(workflow, /run:\s*\|/);
|
|
46
|
+
for (const duplicatedCommand of [
|
|
47
|
+
"npm run release:check",
|
|
48
|
+
"npm run typecheck",
|
|
49
|
+
"npm test",
|
|
50
|
+
"npm run build",
|
|
51
|
+
"npm run lsp:interop",
|
|
52
|
+
"node dist/cli.js doctor",
|
|
40
53
|
]) {
|
|
41
|
-
assert.
|
|
54
|
+
assert.equal(
|
|
55
|
+
workflow.includes(`run: ${duplicatedCommand}`),
|
|
56
|
+
false,
|
|
57
|
+
`cloud CI must delegate ${duplicatedCommand} through ci:verify`,
|
|
58
|
+
);
|
|
42
59
|
}
|
|
43
60
|
});
|
|
61
|
+
|
|
62
|
+
test("release runtime and local parity share the checked-in Node contract", async () => {
|
|
63
|
+
const nodeVersion = (await readFile(resolve(repoRoot, ".nvmrc"), "utf8")).trim();
|
|
64
|
+
assert.equal(nodeVersion, "22.19.0");
|
|
65
|
+
|
|
66
|
+
const pkg = await readJson("package.json");
|
|
67
|
+
assert.equal(pkg.scripts["ci:verify"], "node scripts/ci/verify.mjs");
|
|
68
|
+
|
|
69
|
+
const source = await readFile(resolve(repoRoot, "scripts/release-parity.mjs"), "utf8");
|
|
70
|
+
assert.match(source, /readFileSync\(join\(repoRoot, "\.nvmrc"\), "utf8"\)/);
|
|
71
|
+
assert.match(source, /const NPM_VERSION = "10\.9\.3"/);
|
|
72
|
+
assert.ok(source.includes('["npm", "ci", "--no-audit", "--no-fund"]'));
|
|
73
|
+
assert.ok(source.includes('["npm", "run", "ci:verify"]'));
|
|
74
|
+
assert.ok(source.includes('["npm", "run", "release:pack"]'));
|
|
75
|
+
assert.doesNotMatch(source, /\["npm", "run", "typecheck"\]/);
|
|
76
|
+
assert.doesNotMatch(source, /\["npm", "test"\]/);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("cloud verification produces one reusable npm package on Linux", async () => {
|
|
80
|
+
const workflow = await readFile(resolve(repoRoot, ".github/workflows/ci.yml"), "utf8");
|
|
81
|
+
assert.match(workflow, /if:\s*runner\.os == 'Linux'[\s\S]*run:\s*npm run release:pack/);
|
|
82
|
+
assert.match(workflow, /uses:\s*actions\/upload-artifact@v4/);
|
|
83
|
+
assert.match(workflow, /name:\s*npm-package/);
|
|
84
|
+
assert.match(workflow, /include-hidden-files:\s*true/);
|
|
85
|
+
assert.match(workflow, /overwrite:\s*true/);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("release workflow is tag-only and promotes the verified npm artifact without rebuilding", async () => {
|
|
89
|
+
const workflow = await readFile(resolve(repoRoot, ".github/workflows/release.yml"), "utf8");
|
|
90
|
+
assert.doesNotMatch(workflow, /workflow_dispatch:/);
|
|
91
|
+
assert.match(workflow, /needs:\s*verify/);
|
|
92
|
+
assert.match(workflow, /uses:\s*actions\/download-artifact@v5/);
|
|
93
|
+
assert.match(workflow, /name:\s*npm-package/);
|
|
94
|
+
assert.match(workflow, /run:\s*npm run release:publish/);
|
|
95
|
+
assert.match(workflow, /npm install --global npm@11\.19\.1/);
|
|
96
|
+
assert.doesNotMatch(workflow, /run:\s*npm ci/);
|
|
97
|
+
assert.doesNotMatch(workflow, /run:\s*npm run build/);
|
|
98
|
+
assert.doesNotMatch(workflow, /run:\s*\|/);
|
|
99
|
+
assert.doesNotMatch(workflow, /shell:\s*bash/);
|
|
100
|
+
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
4
|
-
import { cpSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
|
+
import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
|
-
const NODE_VERSION = "22.19.0";
|
|
9
8
|
const NPM_VERSION = "10.9.3";
|
|
10
9
|
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
const NODE_VERSION = readFileSync(join(repoRoot, ".nvmrc"), "utf8").trim();
|
|
11
11
|
const debugRoot = join(repoRoot, ".forgerelay-debug");
|
|
12
12
|
const sandbox = mkdtempSync(join(ensureDirectory(debugRoot), "release-parity-node22-"));
|
|
13
13
|
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
@@ -29,15 +29,11 @@ try {
|
|
|
29
29
|
["npm", "ci", "--no-audit", "--no-fund"],
|
|
30
30
|
`Node ${NODE_VERSION} / npm ${NPM_VERSION} install`,
|
|
31
31
|
);
|
|
32
|
-
runNodeNpm(sandbox, env, ["npm", "run", "
|
|
33
|
-
runNodeNpm(sandbox, env, ["npm", "run", "
|
|
34
|
-
runNodeNpm(sandbox, env, ["npm", "test"], "Full test suite");
|
|
35
|
-
runNodeNpm(sandbox, env, ["npm", "run", "build"], "Build");
|
|
36
|
-
runNodeNpm(sandbox, env, ["npm", "run", "lsp:interop"], "Optional LSP interoperability");
|
|
37
|
-
runNode(sandbox, env, ["dist/cli.js", "doctor"], "Doctor");
|
|
32
|
+
runNodeNpm(sandbox, env, ["npm", "run", "ci:verify"], "Cloud verification entrypoint");
|
|
33
|
+
runNodeNpm(sandbox, env, ["npm", "run", "release:pack"], "Cloud release packaging");
|
|
38
34
|
|
|
39
35
|
console.log(
|
|
40
|
-
`Release parity passed
|
|
36
|
+
`Release parity passed through ci:verify and release:pack on Node ${NODE_VERSION} / npm ${NPM_VERSION}.`,
|
|
41
37
|
);
|
|
42
38
|
} finally {
|
|
43
39
|
rmSync(sandbox, {
|
|
@@ -92,10 +88,6 @@ function runNodeNpm(cwd, env, command, label) {
|
|
|
92
88
|
);
|
|
93
89
|
}
|
|
94
90
|
|
|
95
|
-
function runNode(cwd, env, args, label) {
|
|
96
|
-
run(cwd, env, ["--yes", `node@${NODE_VERSION}`, ...args], label);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
91
|
function run(cwd, env, args, label) {
|
|
100
92
|
console.log(`\n== ${label} ==`);
|
|
101
93
|
const result = spawnSync(npx, args, {
|
|
@@ -42,15 +42,15 @@ function currentHead() {
|
|
|
42
42
|
return git(["rev-parse", "HEAD"]);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function assertReleaseTreeClean() {
|
|
45
|
+
function assertReleaseTreeClean(context) {
|
|
46
46
|
const status = git(["status", "--porcelain", "--untracked-files=all"]);
|
|
47
47
|
if (status) {
|
|
48
|
-
throw new Error(
|
|
48
|
+
throw new Error(`working tree differs from HEAD or contains untracked files; commit or remove every release input before ${context}`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function writeProof() {
|
|
53
|
-
assertReleaseTreeClean();
|
|
53
|
+
assertReleaseTreeClean("running release:verify");
|
|
54
54
|
const proof = {
|
|
55
55
|
proofVersion: PROOF_VERSION,
|
|
56
56
|
head: currentHead(),
|
|
@@ -90,28 +90,37 @@ function hookTag() {
|
|
|
90
90
|
} catch {
|
|
91
91
|
throw new Error("FORGERELAY_HOOK_PAYLOAD is not valid JSON");
|
|
92
92
|
}
|
|
93
|
-
const command = typeof payload.
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
const command = typeof payload.originalCommand === "string"
|
|
94
|
+
? payload.originalCommand
|
|
95
|
+
: typeof payload.command === "string"
|
|
96
|
+
? payload.command
|
|
97
|
+
: "";
|
|
98
|
+
const pushMatch = /git\s+push\b([^;&|\n]*)/.exec(command);
|
|
99
|
+
if (!pushMatch?.[0] || !/\borigin\b/.test(pushMatch[0])) {
|
|
100
|
+
throw new Error("release Hook payload does not contain an origin release tag push");
|
|
101
|
+
}
|
|
102
|
+
const pushCommand = pushMatch[0];
|
|
103
|
+
if (/(?:^|\s)(?:-f|--force(?:-with-lease)?)(?:=\S*)?(?=$|\s)/.test(pushCommand)
|
|
104
|
+
|| /(?:^|\s)\+(?:refs\/tags\/)?v\d+\.\d+\.\d+(?:-rc\.\d+)?(?=$|\s)/.test(pushCommand)) {
|
|
105
|
+
throw new Error("force push is not allowed for release tags");
|
|
106
|
+
}
|
|
107
|
+
if (/(?:^|\s)(?:-d|--delete)(?=$|\s)/.test(pushCommand)) {
|
|
108
|
+
throw new Error("deleting a release tag is not allowed");
|
|
109
|
+
}
|
|
110
|
+
const tagMatch = /(?:^|\s)(?:tag\s+)?(?:refs\/tags\/)?(v\d+\.\d+\.\d+(?:-rc\.\d+)?)(?=$|\s)/.exec(pushCommand);
|
|
111
|
+
if (!tagMatch?.[1]) {
|
|
96
112
|
throw new Error("release Hook payload does not contain a release tag push");
|
|
97
113
|
}
|
|
98
|
-
return
|
|
114
|
+
return tagMatch[1];
|
|
99
115
|
}
|
|
100
116
|
|
|
101
|
-
function
|
|
102
|
-
assertReleaseTreeClean();
|
|
103
|
-
const proof = readProof();
|
|
117
|
+
function checkHookTag() {
|
|
118
|
+
assertReleaseTreeClean("pushing a release tag");
|
|
104
119
|
const head = currentHead();
|
|
105
120
|
const version = packageVersion();
|
|
106
121
|
const tag = hookTag();
|
|
107
122
|
const expectedTag = `v${version}`;
|
|
108
123
|
|
|
109
|
-
if (proof.head !== head) {
|
|
110
|
-
throw new Error(`release proof is for ${proof.head.slice(0, 12)}, but current HEAD is ${head.slice(0, 12)}; rerun npm run release:verify`);
|
|
111
|
-
}
|
|
112
|
-
if (proof.packageVersion !== version) {
|
|
113
|
-
throw new Error(`release proof is for package ${proof.packageVersion}, but package.json is ${version}; rerun npm run release:verify`);
|
|
114
|
-
}
|
|
115
124
|
if (tag !== expectedTag) {
|
|
116
125
|
throw new Error(`tag ${tag} does not match package version ${version}; expected ${expectedTag}`);
|
|
117
126
|
}
|
|
@@ -123,16 +132,16 @@ function checkHookProof() {
|
|
|
123
132
|
throw new Error(`local tag ${tag} does not exist or does not resolve to a commit`);
|
|
124
133
|
}
|
|
125
134
|
if (tagHead !== head) {
|
|
126
|
-
throw new Error(`tag ${tag} points to ${tagHead.slice(0, 12)}, but
|
|
135
|
+
throw new Error(`tag ${tag} points to ${tagHead.slice(0, 12)}, but current HEAD is ${head.slice(0, 12)}`);
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
console.log(`Release
|
|
138
|
+
console.log(`Release tag gate OK: ${tag} -> ${head.slice(0, 12)}; cloud CI will perform release verification.`);
|
|
130
139
|
}
|
|
131
140
|
|
|
132
141
|
const action = process.argv[2];
|
|
133
142
|
try {
|
|
134
143
|
if (action === "write") writeProof();
|
|
135
|
-
else if (action === "check-hook")
|
|
144
|
+
else if (action === "check-hook") checkHookTag();
|
|
136
145
|
else throw new Error("usage: node scripts/release-proof.mjs <write|check-hook>");
|
|
137
146
|
} catch (error) {
|
|
138
147
|
fail(error instanceof Error ? error.message : String(error));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
|
-
import { mkdtemp,
|
|
3
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import test from "node:test";
|
|
@@ -21,7 +21,7 @@ async function runProof(cwd, action, env = {}) {
|
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
test("release
|
|
24
|
+
test("release tag hook gate uses repository facts instead of requiring a local proof", async (t) => {
|
|
25
25
|
const root = await mkdtemp(join(tmpdir(), "forgerelay-release-proof-"));
|
|
26
26
|
t.after(() => rm(root, { recursive: true, force: true }));
|
|
27
27
|
await writeFile(join(root, "package.json"), JSON.stringify({ version: "1.2.3" }) + "\n");
|
|
@@ -34,12 +34,37 @@ test("release proof binds a successful local verification to the exact tag HEAD"
|
|
|
34
34
|
|
|
35
35
|
const written = await runProof(root, "write");
|
|
36
36
|
assert.match(written.stdout, /Release verification proof recorded/);
|
|
37
|
+
await rm(join(root, ".git", "forgerelay", "release-proof.json"));
|
|
37
38
|
await git(root, ["tag", "v1.2.3"]);
|
|
38
39
|
|
|
39
40
|
const checked = await runProof(root, "check-hook", {
|
|
40
41
|
FORGERELAY_HOOK_PAYLOAD: JSON.stringify({ command: "git push origin v1.2.3" }),
|
|
41
42
|
});
|
|
42
|
-
assert.match(checked.stdout, /Release
|
|
43
|
+
assert.match(checked.stdout, /Release tag gate OK: v1\.2\.3/);
|
|
44
|
+
|
|
45
|
+
for (const command of [
|
|
46
|
+
"git push --atomic origin v1.2.3",
|
|
47
|
+
"git push origin refs/tags/v1.2.3",
|
|
48
|
+
"git status && git push origin tag v1.2.3 && echo done",
|
|
49
|
+
]) {
|
|
50
|
+
const alternative = await runProof(root, "check-hook", {
|
|
51
|
+
FORGERELAY_HOOK_PAYLOAD: JSON.stringify({ command, originalCommand: command }),
|
|
52
|
+
});
|
|
53
|
+
assert.match(alternative.stdout, /Release tag gate OK: v1\.2\.3/);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await assert.rejects(
|
|
57
|
+
() => runProof(root, "check-hook", {
|
|
58
|
+
FORGERELAY_HOOK_PAYLOAD: JSON.stringify({
|
|
59
|
+
command: "git push origin v1.2.3",
|
|
60
|
+
originalCommand: "git push --force origin v1.2.3",
|
|
61
|
+
}),
|
|
62
|
+
}),
|
|
63
|
+
(error) => {
|
|
64
|
+
assert.match(String(error.stderr ?? error), /force push is not allowed for release tags/);
|
|
65
|
+
return true;
|
|
66
|
+
},
|
|
67
|
+
);
|
|
43
68
|
|
|
44
69
|
await writeFile(join(root, "tracked.txt"), "changed after verification\n");
|
|
45
70
|
await assert.rejects(
|
|
@@ -71,13 +96,13 @@ test("release proof binds a successful local verification to the exact tag HEAD"
|
|
|
71
96
|
FORGERELAY_HOOK_PAYLOAD: JSON.stringify({ command: "git push origin v1.2.3" }),
|
|
72
97
|
}),
|
|
73
98
|
(error) => {
|
|
74
|
-
assert.match(String(error.stderr ?? error), /
|
|
99
|
+
assert.match(String(error.stderr ?? error), /tag v1\.2\.3 points to .* current HEAD is/);
|
|
75
100
|
return true;
|
|
76
101
|
},
|
|
77
102
|
);
|
|
78
103
|
});
|
|
79
104
|
|
|
80
|
-
test("release
|
|
105
|
+
test("release tag hook gate accepts an rc tag for the package version", async (t) => {
|
|
81
106
|
const root = await mkdtemp(join(tmpdir(), "forgerelay-release-proof-"));
|
|
82
107
|
t.after(() => rm(root, { recursive: true, force: true }));
|
|
83
108
|
await writeFile(join(root, "package.json"), JSON.stringify({ version: "0.6.0-rc.1" }) + "\n");
|
|
@@ -86,16 +111,15 @@ test("release proof accepts an rc tag for the verified package version", async (
|
|
|
86
111
|
await git(root, ["config", "user.name", "Release Proof Test"]);
|
|
87
112
|
await git(root, ["add", "."]);
|
|
88
113
|
await git(root, ["commit", "-m", "release 0.6.0-rc.1"]);
|
|
89
|
-
await runProof(root, "write");
|
|
90
114
|
await git(root, ["tag", "v0.6.0-rc.1"]);
|
|
91
115
|
|
|
92
116
|
const checked = await runProof(root, "check-hook", {
|
|
93
117
|
FORGERELAY_HOOK_PAYLOAD: JSON.stringify({ command: "git push origin v0.6.0-rc.1" }),
|
|
94
118
|
});
|
|
95
|
-
assert.match(checked.stdout, /Release
|
|
119
|
+
assert.match(checked.stdout, /Release tag gate OK: v0\.6\.0-rc\.1/);
|
|
96
120
|
});
|
|
97
121
|
|
|
98
|
-
test("release
|
|
122
|
+
test("release tag hook gate rejects a mismatched tag without invoking a remote", async (t) => {
|
|
99
123
|
const root = await mkdtemp(join(tmpdir(), "forgerelay-release-proof-"));
|
|
100
124
|
t.after(() => rm(root, { recursive: true, force: true }));
|
|
101
125
|
await writeFile(join(root, "package.json"), JSON.stringify({ version: "2.0.0" }) + "\n");
|
|
@@ -104,7 +128,6 @@ test("release proof rejects a mismatched tag without invoking a remote", async (
|
|
|
104
128
|
await git(root, ["config", "user.name", "Release Proof Test"]);
|
|
105
129
|
await git(root, ["add", "."]);
|
|
106
130
|
await git(root, ["commit", "-m", "release 2.0.0"]);
|
|
107
|
-
await runProof(root, "write");
|
|
108
131
|
await git(root, ["tag", "v2.0.0"]);
|
|
109
132
|
|
|
110
133
|
await assert.rejects(
|
|
@@ -117,6 +140,4 @@ test("release proof rejects a mismatched tag without invoking a remote", async (
|
|
|
117
140
|
},
|
|
118
141
|
);
|
|
119
142
|
|
|
120
|
-
const proof = JSON.parse(await readFile(join(root, ".git", "forgerelay", "release-proof.json"), "utf8"));
|
|
121
|
-
assert.equal(proof.packageVersion, "2.0.0");
|
|
122
143
|
});
|
|
@@ -171,7 +171,7 @@ async function prepareRelease(state, nextVersion, dryRun) {
|
|
|
171
171
|
|
|
172
172
|
console.log(`${state.pkg.version} -> ${nextVersion}`);
|
|
173
173
|
console.log("updated package.json, package-lock.json, and CHANGELOG.md");
|
|
174
|
-
console.log("next: review the diff, commit the release-ready tree,
|
|
174
|
+
console.log("next: review the diff, commit and push the release-ready tree, then push the matching release tag; cloud CI is the authoritative release verification");
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
function promoteUnreleased(changelog, nextVersion) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.forgerelay-panel{border:1px solid var(--tool-card-border);background:var(--tool-card-header-bg);width:100%;color:var(--color-text-primary,#f5f5f6);border-radius:12px;overflow:hidden}.workspace-panel{background:var(--tool-card-header-bg);width:100%}.workspace-panel-header{grid-template-columns:22px minmax(0,1fr) auto;align-items:center;gap:10px;min-height:58px;padding:9px 12px;display:grid}.workspace-panel-icon{background:color-mix(in srgb, var(--tool-accent) 9%, transparent);width:22px;height:22px;color:color-mix(in srgb, var(--tool-accent) 72%, var(--color-text-tertiary,#a3a3aa));border-radius:6px;place-items:center;display:grid}.workspace-panel-icon-svg{stroke-width:1.8px;width:14px;height:14px}.workspace-panel-title-group{gap:2px;min-width:0;display:grid}.workspace-panel-title{font-size:var(--font-text-sm-size,14px);font-weight:600;line-height:1.3}.workspace-panel-subtitle,.workspace-panel-mode{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,11px);line-height:1.35}.workspace-panel-subtitle{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.workspace-panel-mode{border:1px solid color-mix(in srgb, var(--tool-card-divider) 76%, transparent);white-space:nowrap;border-radius:999px;padding:2px 7px}.workspace-panel .workspace-details{border-top:1px solid var(--tool-card-divider);background:var(--tool-card-body-bg);max-height:none}.workspace-panel-pending-dot{background:var(--color-text-info,#38bdf8);width:8px;height:8px;box-shadow:0 0 0 3px color-mix(in srgb, var(--color-text-info,#38bdf8) 12%, transparent);border-radius:999px;justify-self:center}.activity-panel{border:1px solid var(--tool-card-border);background:var(--tool-card-header-bg);width:100%;color:var(--color-text-primary,#f5f5f6);border-radius:12px;overflow:hidden}.forgerelay-panel .activity-panel{border:0;border-top:1px solid var(--tool-card-divider);border-radius:0}.activity-panel-header{width:100%;min-height:58px;color:inherit;cursor:pointer;font:inherit;text-align:left;background:0 0;border:0;grid-template-columns:14px minmax(0,1fr) auto 20px;align-items:center;gap:10px;padding:9px 12px;display:grid}.activity-panel-header:hover{background:var(--tool-card-hover-bg)}.activity-panel-header:focus-visible{outline:2px solid color-mix(in srgb, var(--color-text-info,#38bdf8) 72%, transparent);outline-offset:-2px}.activity-panel-header-pending{cursor:default}.activity-panel-header-pending:hover{background:0 0}.activity-panel-pending-spacer{width:20px}.activity-panel-status{background:var(--color-text-info,#38bdf8);width:8px;height:8px;box-shadow:0 0 0 3px color-mix(in srgb, var(--color-text-info,#38bdf8) 12%, transparent);border-radius:9999px;justify-self:center}.activity-panel-status.state-done{background:var(--color-success-text,#6fda83);box-shadow:0 0 0 3px color-mix(in srgb, var(--color-success-text,#6fda83) 12%, transparent)}.activity-panel-status.state-error{background:var(--color-danger-text,#ee7676);box-shadow:0 0 0 3px color-mix(in srgb, var(--color-danger-text,#ee7676) 12%, transparent)}.activity-panel-title-group{gap:2px;min-width:0;display:grid}.activity-panel-title{font-size:var(--font-text-sm-size,14px);font-weight:600;line-height:1.3}.activity-panel-subtitle,.activity-panel-count{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,11px);line-height:1.35}.activity-panel-count{white-space:nowrap}.activity-panel-body{border-top:1px solid var(--tool-card-divider);background:var(--tool-card-body-bg);display:grid}.activity-viewport{overscroll-behavior:contain;max-height:420px;overflow:hidden auto}.activity-list{display:grid}.activity-group+.activity-group{border-top:1px solid var(--tool-card-divider)}.activity-group.grouped>.activity-row.parent{background:color-mix(in srgb, var(--color-background-secondary,#272727) 54%, transparent)}.activity-children{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 72%, transparent);display:grid}.activity-row{--activity-accent:var(--color-text-secondary,#b6b6bd);--activity-phase:var(--color-text-tertiary,#a3a3aa);width:100%;min-height:46px;color:inherit;font:inherit;text-align:left;background:0 0;border:0;grid-template-columns:28px minmax(0,1fr) auto 16px;align-items:center;gap:10px;padding:7px 12px;display:grid}.activity-row.interactive{cursor:pointer}.activity-row.interactive:hover{background:var(--tool-card-hover-bg)}.activity-row.interactive:focus-visible{outline:2px solid color-mix(in srgb, var(--activity-accent) 68%, transparent);outline-offset:-2px}.activity-row.child{padding-left:34px;position:relative}.activity-row.child:before{background:color-mix(in srgb, var(--activity-accent) 26%, var(--tool-card-divider));content:"";width:1px;position:absolute;top:0;bottom:0;left:20px}.activity-row.child+.activity-row.child{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 52%, transparent)}.activity-row.kind-read{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 32%, #06b6d4 68%)}.activity-row.kind-write{--activity-accent:var(--color-success-text,#6fda83)}.activity-row.kind-edit,.activity-row.kind-rename{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 28%, #d99742 72%)}.activity-row.kind-delete{--activity-accent:var(--color-danger-text,#ee7676)}.activity-row.kind-shell{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 42%, #64748b 58%)}.activity-row.kind-shell-result{--activity-accent:color-mix(in srgb, var(--color-success-text,#6fda83) 72%, var(--color-text-secondary,#b6b6bd))}.activity-row.kind-capability{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 34%, #3b82f6 66%)}.activity-row.kind-batch{--activity-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 34%, #8b5cf6 66%)}.activity-row.phase-executing{--activity-phase:var(--color-text-info,#38bdf8)}.activity-row.phase-returned{--activity-phase:var(--color-warning-text,#e6b566)}.activity-row.phase-done{--activity-phase:var(--color-success-text,#6fda83)}.activity-row.phase-error{--activity-phase:var(--color-danger-text,#ee7676)}.activity-row.shell-result{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--activity-accent) 68%, transparent)}.activity-icon{border:1px solid color-mix(in srgb, var(--activity-accent) 18%, transparent);background:color-mix(in srgb, var(--activity-accent) 9%, transparent);width:28px;height:28px;color:var(--activity-accent);border-radius:7px;place-items:center;display:grid}.activity-icon-svg{stroke-width:1.8px;width:15px;height:15px}.activity-main{gap:4px;min-width:0;display:grid}.activity-title-line{align-items:baseline;gap:8px;min-width:0;display:flex}.activity-title{color:var(--color-text-primary,#f5f5f6);font-size:var(--font-text-sm-size,12px);flex:none;font-weight:600;line-height:1.35}.activity-target{min-width:0;color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,11px);text-overflow:ellipsis;white-space:nowrap;line-height:1.4;overflow:hidden}.activity-meta{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,10px);font-variant-numeric:tabular-nums;white-space:nowrap;justify-content:flex-end;align-items:center;gap:8px;display:inline-flex}.activity-phase{color:var(--activity-phase);align-items:center;gap:5px;display:inline-flex}.activity-phase:before{content:"";background:currentColor;border-radius:9999px;width:6px;height:6px}.activity-progress-wrap{grid-template-columns:auto minmax(48px,110px);align-items:center;gap:8px;max-width:220px;display:grid}.activity-progress-counts{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,10px);font-variant-numeric:tabular-nums;white-space:nowrap}.activity-progress-track{background:color-mix(in srgb, var(--activity-accent) 14%, var(--tool-card-divider));border-radius:9999px;height:3px;display:block;overflow:hidden}.activity-progress-fill{border-radius:inherit;background:var(--activity-accent);height:100%;display:block}.activity-empty,.activity-refresh-error{color:var(--color-text-secondary,#b7b7bf);font-size:var(--font-text-sm-size,12px);padding:12px}.activity-refresh-error{border-top:1px solid var(--tool-card-divider);color:var(--color-danger-text,#ee7676)}.activity-detail-chevron,.activity-detail-spacer,.activity-detail-chevron.chevron{width:16px;height:16px}.activity-detail-chevron .icon-svg{width:13px;height:13px}.activity-entry.expanded>.activity-row{background:color-mix(in srgb, var(--activity-accent) 6%, transparent)}.activity-detail{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 72%, transparent);background:color-mix(in srgb, var(--color-background-primary,#101114) 90%, transparent);display:grid}.activity-detail-section+.activity-detail-section{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 62%, transparent)}.activity-detail-label{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,10px);letter-spacing:.02em;padding:8px 12px 0;font-weight:600}.activity-detail-section.error .activity-detail-label,.activity-detail-section.error .activity-detail-value{color:var(--color-danger-text,#ee7676)}.activity-detail-value{max-height:260px;color:var(--color-text-secondary,#c7c7ce);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,11px);white-space:pre-wrap;overflow-wrap:break-word;margin:0;padding:6px 12px 10px;line-height:1.5;overflow:auto}.activity-detail-status{color:var(--color-text-secondary,#b7b7bf);font-size:var(--font-text-sm-size,12px);padding:10px 12px}.activity-detail-status.error{color:var(--color-danger-text,#ee7676)}.activity-terminal{background:var(--color-background-primary,#101114)}.activity-terminal-command,.activity-terminal-output{color:var(--color-text-primary,#f5f5f6);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,11px);white-space:pre-wrap;overflow-wrap:break-word;background:0 0;border:0;border-radius:0;margin:0;line-height:1.55}.activity-terminal-command{border-bottom:1px solid color-mix(in srgb, var(--tool-card-divider) 74%, transparent);color:var(--color-text-secondary,#c7c7ce);padding:9px 12px}.activity-terminal-output{max-height:320px;padding:10px 12px;overflow:auto}.activity-terminal-meta{border-top:1px solid color-mix(in srgb, var(--tool-card-divider) 74%, transparent);color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-xs-size,10px);font-variant-numeric:tabular-nums;padding:7px 12px}.activity-terminal-meta.status-running{color:var(--color-text-info,#38bdf8)}.activity-terminal-meta.status-done{color:var(--color-success-text,#6fda83)}.activity-terminal-meta.status-failed{color:var(--color-danger-text,#ee7676)}@media (width<=520px){.activity-panel-header{grid-template-columns:12px minmax(0,1fr) auto 18px;gap:8px;min-height:54px;padding:8px 10px}.activity-panel-subtitle,.activity-panel-count{font-size:10px}.activity-row{grid-template-columns:26px minmax(0,1fr) 18px;gap:6px 8px;min-height:48px;padding:8px 10px}.activity-row.child{padding-left:28px}.activity-row.child:before{left:16px}.activity-icon{align-self:start;width:26px;height:26px}.activity-title-line{gap:2px;display:grid}.activity-meta{grid-column:2;justify-content:flex-start}.activity-detail-chevron,.activity-detail-spacer{grid-area:1/3/span 2}.activity-progress-wrap{grid-template-columns:auto minmax(40px,1fr);max-width:none}}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;font-family:var(--font-sans,ui-sans-serif, system-ui, sans-serif);color:var(--color-text-primary,#f5f5f6);--tool-card-border:color-mix(in srgb, var(--color-border-primary,#414141) 74%, transparent);--tool-card-header-bg:color-mix(in srgb, var(--color-background-secondary,#272727) 88%, transparent);--tool-card-body-bg:color-mix(in srgb, var(--color-background-primary,#181818) 94%, transparent);--tool-card-hover-bg:color-mix(in srgb, var(--color-background-tertiary,#343434) 46%, transparent);--tool-card-divider:color-mix(in srgb, var(--color-border-primary,#414141) 66%, transparent);--tool-accent:var(--color-text-secondary,#b6b6bd);--scrollbar-thumb:color-mix(in srgb, var(--color-text-tertiary,#8a8a8a) 56%, transparent);--scrollbar-thumb-hover:color-mix(in srgb, var(--color-text-secondary,#a8a8a8) 82%, transparent);background:0 0}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}*{box-sizing:border-box}html,body{background:0 0;margin:0;overflow:hidden}.shell{width:100%;padding:0;overflow:hidden}.empty,.tool-card{--tool-accent-soft:color-mix(in srgb, var(--tool-accent) 12%, transparent);border:1px solid var(--tool-card-border);background:var(--tool-card-header-bg);width:100%;box-shadow:none;color:var(--color-text-primary,#f5f5f6);border-radius:12px;overflow:hidden}.tool-card.workspace,.tool-card.directory{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 34%, #3b82f6 66%)}.tool-card.read,.tool-card.search{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 32%, #06b6d4 68%)}.tool-card.write{--tool-accent:var(--color-success-text,#6fda83)}.tool-card.edit,.tool-card.review{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 28%, #d99742 72%)}.tool-card.delete{--tool-accent:var(--color-danger-text,#ee7676)}.tool-card.shell{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 42%, #64748b 58%)}.tool-card.state-success{--tool-accent:var(--color-success-text,#6fda83)}.tool-card.state-error{--tool-accent:var(--color-danger-text,#ee7676)}.tool-card.state-running{--tool-accent:color-mix(in srgb, var(--color-text-primary,#f5f5f6) 30%, #38bdf8 70%)}@supports selector(::-webkit-scrollbar){.pretty-scrollbar::-webkit-scrollbar{width:12px;height:12px}.pretty-scrollbar::-webkit-scrollbar-button{width:0;height:0;display:none}.pretty-scrollbar::-webkit-scrollbar-track{background:0 0}.pretty-scrollbar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb);background-clip:content-box;border:4px solid #0000;border-radius:9999px}.pretty-scrollbar::-webkit-scrollbar-thumb:hover{background-color:var(--scrollbar-thumb-hover)}.pretty-scrollbar::-webkit-scrollbar-thumb:active{background-color:var(--scrollbar-thumb-hover)}.pretty-scrollbar::-webkit-scrollbar-corner{background:0 0}}.empty{color:var(--color-text-secondary,#b6b6bd);font-size:var(--font-text-sm-size,13px);padding:14px 16px}.tool-header{width:100%;min-height:64px;color:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:11px;grid-template-columns:40px minmax(0,1fr) auto 20px;align-items:center;gap:12px;padding:10px 12px;display:grid}.tool-header:focus-visible,.review-diff-file-header:focus-visible,.review-more:focus-visible{outline:2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent);outline-offset:-2px}.tool-header:hover:not(:disabled){background:var(--tool-card-hover-bg)}.tool-header:disabled{cursor:default}.tool-icon{border:1px solid color-mix(in srgb, var(--tool-accent) 18%, transparent);background:var(--tool-accent-soft);width:40px;height:40px;color:var(--tool-accent);border-radius:10px;place-items:center;display:grid}.icon-svg{stroke-width:1.8px;width:20px;height:20px;display:block}.tool-main{gap:2px;min-width:0;display:grid}.tool-title{color:var(--color-text-primary,#f5f5f6);font-size:var(--font-text-sm-size,14px);font-weight:550;line-height:1.3}.tool-label{color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,12px);text-overflow:ellipsis;white-space:nowrap;line-height:1.4;overflow:hidden}.stats{font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,12px);font-variant-numeric:tabular-nums;white-space:nowrap;align-items:center;gap:5px;display:inline-flex}.header-meta{color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-sm-size,12px);text-align:right;white-space:nowrap;line-height:1.35}.header-meta.empty{width:0}.add{color:var(--color-success-text,#6fda83)}.remove{color:var(--color-danger-text,#ee7676)}.chevron{width:20px;height:20px;color:var(--color-text-tertiary,#a3a3aa);border-radius:7px;place-items:center;transition:background .14s,color .14s,transform .14s;display:grid}.tool-header:hover:not(:disabled) .chevron{color:var(--color-text-primary,#f5f5f6)}.chevron .icon-svg{width:15px;height:15px}.chevron.expanded{transform:rotate(180deg)}.chevron.loading{transform:none}.chevron.loading .icon-svg{fill:none;stroke-linecap:round;stroke-dasharray:38 14;animation:.7s linear infinite payload-spinner}@keyframes payload-spinner{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.chevron.loading .icon-svg{animation:none}}.tool-body{border-top:1px solid var(--tool-card-divider);background:var(--tool-card-body-bg)}.workspace-details{max-height:420px;display:grid;overflow:auto}.workspace-rows{padding:4px 0;display:grid}.workspace-row{grid-template-columns:22px minmax(116px,.24fr) minmax(0,1fr);align-items:center;gap:10px;min-height:40px;padding:7px 12px;display:grid}.workspace-row-icon{background:color-mix(in srgb, var(--tool-accent) 9%, transparent);width:22px;height:22px;color:color-mix(in srgb, var(--tool-accent) 72%, var(--color-text-tertiary,#a3a3aa));border-radius:6px;place-items:center;display:grid}.workspace-row-icon-svg{stroke-width:1.8px;width:14px;height:14px}.workspace-key{min-height:22px;color:var(--color-text-tertiary,#a3a3aa);font-size:var(--font-text-sm-size,12px);align-items:center;font-weight:500;display:flex}.workspace-value{min-width:0;color:var(--color-text-secondary,#c7c7ce);font-size:var(--font-text-sm-size,12px);text-overflow:ellipsis;white-space:nowrap;line-height:1.45;overflow:hidden}.workspace-value.mono{font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace)}.workspace-base-value{align-items:center;gap:7px;min-width:0;display:flex}.workspace-base-value .workspace-value{flex:0 auto}.workspace-base-warning{width:18px;height:18px;color:var(--color-warning-text,#e6b566);cursor:help;flex:none;place-items:center;display:grid}.workspace-base-warning-svg{stroke-width:2px;width:14px;height:14px}.workspace-chip-list{flex-wrap:nowrap;align-items:center;gap:6px;min-width:0;display:flex;overflow:hidden}.workspace-chip{border:1px solid color-mix(in srgb, var(--tool-accent) 16%, var(--tool-card-divider));background:color-mix(in srgb, var(--tool-accent) 7%, transparent);max-width:100%;min-height:24px;color:var(--color-text-secondary,#c7c7ce);text-overflow:ellipsis;white-space:nowrap;border-radius:9999px;flex:none;align-items:center;gap:5px;padding:3px 8px;font-size:11px;line-height:1.25;display:inline-flex;overflow:hidden}.workspace-chip-logo{object-fit:contain;flex:none;width:13px;height:13px;display:block}.workspace-chip-label{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.workspace-agent-profile{border:0;border-bottom:1px solid color-mix(in srgb, var(--tool-accent) 34%, var(--tool-card-divider));max-width:100%;min-height:24px;color:var(--color-text-secondary,#c7c7ce);white-space:nowrap;background:0 0;border-radius:0;align-items:center;gap:5px;padding:3px 2px 4px;font-size:11px;line-height:1.25;display:inline-flex;overflow:hidden}.workspace-agent-profile-logo{object-fit:contain;flex:none;width:14px;height:14px;display:block}.workspace-agent-profile:hover{border-bottom-color:var(--tool-accent);color:var(--color-text-primary,#f5f5f6)}.workspace-agent-profile.muted{color:var(--color-text-tertiary,#a3a3aa);opacity:.72;border-bottom-style:dashed}.workspace-provider-logo{cursor:help;flex:none;place-items:center;width:20px;height:24px;display:inline-grid}.workspace-provider-logo-image{object-fit:contain;width:16px;height:16px;display:block}.workspace-provider-logo.muted{opacity:.62}.workspace-chip.muted{color:var(--color-text-tertiary,#a3a3aa);opacity:.72;border-style:dashed}.workspace-skills-row,.workspace-instructions-row,.workspace-agents-row{align-items:start}.workspace-skills-list,.workspace-agents-list{flex-wrap:wrap;overflow:visible}.workspace-instruction-status{border-radius:5px;flex:none;place-items:center;width:18px;height:18px;display:grid}.workspace-instruction-status.loaded{background:color-mix(in srgb, var(--color-success-text,#6fda83) 12%, transparent);color:var(--color-success-text,#6fda83)}.workspace-instruction-status.available{background:color-mix(in srgb, var(--color-text-tertiary,#a3a3aa) 9%, transparent);color:var(--color-text-tertiary,#a3a3aa)}.workspace-instruction-status-svg{stroke-width:1.9px;width:12px;height:12px}.workspace-instruction-list{border:1px solid var(--tool-card-divider);background:color-mix(in srgb, var(--tool-card-body-bg) 88%, transparent);border-radius:9px;min-width:0;display:grid;overflow:hidden}.workspace-instructions-content{min-width:0;display:block}.workspace-instructions-toggle{border:0;border-top:1px solid var(--tool-card-divider);background:color-mix(in srgb, var(--tool-card-body-bg) 72%, transparent);width:100%;min-height:32px;color:var(--tool-accent);cursor:pointer;font:inherit;font-size:var(--font-text-sm-size,11px);white-space:nowrap;border-radius:0 0 9px 9px;justify-content:center;align-items:center;padding:6px 10px;font-weight:550;line-height:1.25;display:flex}.workspace-instructions-toggle:hover{background:var(--tool-card-hover-bg)}.workspace-instructions-toggle:focus-visible{outline:2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent);outline-offset:2px}.workspace-instruction-item+.workspace-instruction-item{border-top:1px solid var(--tool-card-divider)}.workspace-instruction-header{width:100%;min-width:0;color:inherit;font:inherit;text-align:left;background:0 0;border:0;grid-template-columns:22px minmax(0,1fr) 18px;align-items:center;gap:9px;padding:8px 10px;display:grid}.workspace-instruction-header.interactive{cursor:pointer}.workspace-instruction-header.interactive:hover{background:var(--tool-card-hover-bg)}.workspace-instruction-header.interactive:focus-visible{outline:2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent);outline-offset:-2px}.workspace-instruction-text{gap:2px;min-width:0;display:grid}.workspace-instruction-name{min-width:0;color:var(--color-text-primary,#f5f5f6);font-size:var(--font-text-sm-size,12px);text-overflow:ellipsis;white-space:nowrap;font-weight:550;overflow:hidden}.workspace-instruction-path{min-width:0;color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);text-overflow:ellipsis;white-space:nowrap;font-size:10px;line-height:1.35;overflow:hidden}.workspace-instruction-chevron{width:18px;height:18px;color:var(--color-text-tertiary,#a3a3aa);place-items:center;transition:transform .14s;display:grid}.workspace-instruction-item.expanded .workspace-instruction-chevron{transform:rotate(180deg)}.workspace-instruction-chevron-svg{width:14px;height:14px}.workspace-instruction-preview{border-top:1px solid var(--tool-card-divider);background:color-mix(in srgb, var(--color-background-primary,#101114) 92%, transparent);max-height:300px;color:var(--color-text-secondary,#c7c7ce);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);white-space:pre-wrap;overflow-wrap:break-word;margin:0;padding:10px 12px;font-size:11px;line-height:1.55;overflow:auto}.workspace-instruction-preview[hidden]{display:none}.review-header{grid-template-columns:40px minmax(0,1fr) auto 20px}.review-title-group{gap:3px;min-width:0;display:grid}.review-summary{border-top:1px solid var(--tool-card-divider);background:var(--tool-card-body-bg);display:grid}.review-diff-file-stats{align-items:center;gap:8px;display:flex}.review-empty{color:var(--color-text-secondary,#b7b7bf);font-size:var(--font-text-sm-size,13px)}.review-more{border:0;border-top:1px solid var(--tool-card-divider);width:100%;min-height:40px;color:var(--color-text-tertiary,#a3a3aa);cursor:pointer;font:inherit;font-size:var(--font-text-sm-size,12px);text-align:left;background:0 0;padding:0 12px}.review-more:hover{background:var(--tool-card-hover-bg);color:var(--color-text-primary,#f5f5f6)}.review-diff{max-height:520px;display:grid;overflow:hidden auto}.review-diff-files{gap:0;padding:0;display:grid}.review-diff-file{border:0;border-radius:0;overflow:hidden}.review-diff-file+.review-diff-file{border-top:1px solid var(--tool-card-divider)}.review-diff-file-header{width:100%;min-height:42px;color:var(--color-text-primary,#f5f5f6);cursor:pointer;font:inherit;text-align:left;background:0 0;border:0;grid-template-columns:22px minmax(0,1fr) auto;align-items:center;gap:10px;padding:0 12px;display:grid}.review-file-kind{background:color-mix(in srgb, var(--color-text-tertiary,#a3a3aa) 10%, transparent);width:20px;height:20px;color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);border-radius:6px;align-self:center;place-items:center;font-size:10px;font-weight:700;display:grid}.review-file-kind.added{background:color-mix(in srgb, var(--color-success-text,#6fda83) 12%, transparent);color:var(--color-success-text,#6fda83)}.review-file-kind.edited,.review-file-kind.renamed,.review-file-kind.renamed-edited{background:color-mix(in srgb, var(--color-warning-text,#e6b566) 12%, transparent);color:var(--color-warning-text,#e6b566)}.review-file-kind.deleted{background:color-mix(in srgb, var(--color-danger-text,#ee7676) 12%, transparent);color:var(--color-danger-text,#ee7676)}.review-single-file{overflow:hidden}.review-diff-file-header:hover{background:var(--tool-card-hover-bg)}.review-diff-file-name,.review-diff-file-stats{text-overflow:ellipsis;white-space:nowrap;font-size:13px;line-height:20px;overflow:hidden}.review-diff-file-name{font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace)}.review-diff-file-name.renamed{text-overflow:clip;align-items:center;gap:6px;min-width:0;display:flex}.review-diff-file-path{text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:calc(50% - 10px);overflow:hidden}.review-diff-file-path.previous{color:var(--color-text-tertiary,#a3a3aa)}.review-diff-file-path.current{color:var(--color-text-primary,#f5f5f6)}.review-diff-file-arrow{color:var(--color-text-tertiary,#a3a3aa);font-family:var(--font-sans,ui-sans-serif, system-ui, sans-serif);flex:none}.review-diff-file-stats{font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-sm-size,12px);font-variant-numeric:tabular-nums;justify-content:flex-end;overflow:visible}.status{font-size:var(--font-text-sm-size,12px);padding:10px 12px}.status.muted{color:var(--color-text-secondary,#b7b7bf)}.status.error{color:var(--color-danger-text,#ee7676)}.pierre-diff,.pierre-file{--diffs-bg:var(--tool-payload-bg,var(--color-background-primary,#101114));--diffs-light-bg:var(--color-background-primary,#fff);--diffs-dark-bg:var(--tool-payload-bg,var(--color-background-primary,#101114));--diffs-font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);--diffs-header-font-family:var(--font-sans,ui-sans-serif, system-ui, sans-serif);--diffs-font-size:var(--font-text-sm-size,12px);--diffs-line-height:20px;border-bottom-right-radius:8px;border-bottom-left-radius:8px;max-height:420px;display:block;overflow:auto}.text-payload{max-height:420px;color:var(--color-text-secondary,#c7c7ce);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-sm-size,12px);white-space:pre-wrap;overflow-wrap:break-word;margin:0;padding:10px 12px;line-height:1.55;overflow:auto}.text-payload.bash{color:var(--color-text-primary,#f5f5f6);background:var(--color-background-primary,#101114)}@media (width<=520px){.tool-header{grid-template-columns:36px minmax(0,1fr) auto 18px;gap:9px;min-height:58px;padding:9px 10px}.tool-icon{border-radius:9px;width:36px;height:36px}.chevron{width:18px;height:18px}.review-header{grid-template-columns:36px minmax(0,1fr) auto 18px}.workspace-row{grid-template-columns:22px minmax(0,1fr);gap:2px 8px;padding-block:8px}.workspace-row-icon{grid-row:1/span 2;align-self:start}.workspace-row>.workspace-key,.workspace-row>.workspace-value,.workspace-row>.workspace-chip-list,.workspace-row>.workspace-instructions-content{grid-column:2}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./workspace-app-DkAiSl_0.js";import"./workspace-app-QyauBrJX.js";document.documentElement.dataset.forgerelayApp=`historical-tool-card`;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./heavy-payload-vGgBRvNX.js","./scrollbar-CaOPzUJd.js","./chunk-EyZ2wyi3.js","./workspace-app-DkAiSl_0.js","./workspace-app-CcrHAUIn.css","./review-payload-4erWKckt.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{a as e,c as t,d as n,i as r,l as i,n as a,o,s,t as c,u as l}from"./workspace-app-DkAiSl_0.js";function u(e){return e===`open_workspace`||e===`close_workspace`||e===`capability`||e===`apply_patch`||e===`exec_command`||e===`write_stdin`||e===`read`||e===`write`||e===`edit`||e===`rename`||e===`delete`||e===`grep`||e===`glob`||e===`ls`||e===`bash`}function d(e){return e===`read`}function f(e){return e===`write`}function p(e){return e===`edit`}function m(e){return e===`apply_patch`}function ee(e){return e===`bash`||e===`exec_command`||e===`write_stdin`}function h(e){return e.tool===`capability`&&e.capabilityName===`review.changes`}function te(e){return!!(e&&typeof e==`object`)}function ne(e){return e?.content?.map(e=>e.type===`text`?e.text??``:`[${e.mimeType??`image`} image payload]`).filter(Boolean).join(`
|
|
3
|
-
|
|
4
|
-
`)??``}function g(e,t){let n=e?.[t];return typeof n==`number`&&Number.isFinite(n)?n:void 0}function _(e){return e.tool===`open_workspace`?Number(e.summary?.agentsFiles??0)>0||Number(e.summary?.skills??0)>0||Number(e.summary?.agentProviders??0)>0||Number(e.summary?.agents??0)>0||!!e.agentsFiles?.length||!!e.availableAgentsFiles?.length||!!e.skills?.length||!!e.agentProviders?.length||!!e.agents?.length||!!e.worktree||!!e.instruction:h(e)?!!(e.files?.length||e.payload?.patch):m(e.tool)?!!e.payload?.patch:!!e.payload}function re(e){return e.tool===`open_workspace`||h(e)?_(e):m(e.tool)?e.files?.length===1&&_(e):!1}var ie={added:`Added`,edited:`Edited`,deleted:`Deleted`,renamed:`Renamed`,"renamed-edited":`Renamed and edited`};function ae(e,t={}){let n=e.files??[],r=le(n);if(r===0)return{title:t.emptyTitle??`Applied patch`,tone:`edit`};let i=new Set(n.map(v)),a=i.size===1?[...i][0]:void 0,o={title:ue(a,r),tone:de(a)};return a&&a!==`unknown`&&(o.iconKind=a),o}function v(e){switch(e.operation){case`add`:return`added`;case`update`:return`edited`;case`delete`:return`deleted`;case`move`:return`renamed`}switch(e.type){case`new`:return`added`;case`change`:return`edited`;case`deleted`:return`deleted`;case`rename-pure`:return`renamed`;case`rename-changed`:return`renamed-edited`;default:return`unknown`}}function oe(e,t,n){let r=v(t);if(r!==`edited`&&r!==`unknown`)return r;let i=e[n];return i?.operation===`move`&&(!t.path||i.path===t.path)||e.find(e=>e.operation===`move`&&e.path===t.path&&(!t.previousPath||e.previousPath===t.previousPath))?`renamed`:r===`edited`?`edited`:i?v(i):`unknown`}function y(e){let t=e.path??e.previousPath;if(!t)return;let n=e.previousPath;if(!n||n===t)return{current:t,title:t};let r=x(n)===x(t);return{current:r?S(t):t,previous:r?S(n):n,title:`${n} → ${t}`}}function se(e,t,n){let r=e[n],i=(r?.path===t.path?r:e.find(e=>e.path===t.path&&(!t.previousPath||!e.previousPath||e.previousPath===t.previousPath)))??r;return y({path:t.path??i?.path,previousPath:t.previousPath??i?.previousPath})}function ce(e){return e===`unknown`?`Changed`:ie[e]}function le(e){let t=new Set,n=0;for(let r of e){let e=r.path??r.previousPath;e?t.add(e):n+=1}return t.size+n}function ue(e,t){return e&&e!==`unknown`?`${ie[e]} ${t} ${b(t)}`:`Changed ${t} ${b(t)}`}function de(e){return e===`added`?`write`:e===`deleted`?`delete`:`edit`}function b(e){return e===1?`file`:`files`}function x(e){let t=Math.max(e.lastIndexOf(`/`),e.lastIndexOf(`\\`));return t===-1?``:e.slice(0,t)}function S(e){let t=Math.max(e.lastIndexOf(`/`),e.lastIndexOf(`\\`));return t===-1?e:e.slice(t+1)}function fe(e){switch(e.tool){case`open_workspace`:return{icon:e.mode===`worktree`?o.gitBranch:o.folderOpen,title:he(e),label:e.root??e.path,tone:`workspace`};case`close_workspace`:return{icon:e.mode===`worktree`?o.gitBranch:o.folderOpen,title:`Closed workspace`,label:e.sourceRoot??e.root??e.path??e.workspaceId,tone:`workspace`};case`read`:return{icon:o.readFile,title:`Read file`,label:e.path,tone:`read`};case`write`:return{icon:o.writeFile,title:`Wrote file`,label:e.path,tone:`write`};case`edit`:return{icon:o.editFile,title:`Edited file`,label:e.path,tone:`edit`};case`rename`:return{icon:o.editFile,title:`Renamed path`,label:e.path,tone:`edit`};case`delete`:return{icon:o.deleteFile,title:`Deleted path`,label:e.path,tone:`delete`};case`apply_patch`:{let t=ae(e);return{icon:me(t.iconKind),title:t.title,label:C(e),tone:t.tone}}case`grep`:return{icon:o.search,title:`Searched files`,label:ge(e),tone:`search`};case`glob`:return{icon:o.files,title:`Found files`,label:ge(e),tone:`search`};case`ls`:return{icon:o.folderTree,title:`Listed directory`,label:e.path,tone:`directory`};case`bash`:case`exec_command`:return{icon:o.terminalSquare,title:_e(e,`command`),label:w(e),tone:`shell`,state:ve(e)};case`write_stdin`:return{icon:o.terminal,title:_e(e,`process`),label:w(e),tone:`shell`,state:ve(e)};case`capability`:if(h(e)){let t=ae(e,{emptyTitle:`Changes ready`}),n=e.files?.length??0;return{icon:o.diff,title:n>0||e.payload?.patch?t.title:`No changes`,label:C(e),tone:`review`}}return{icon:o.skills,title:e.capabilityName?`Capability: ${e.capabilityName}`:`Capability completed`,tone:`workspace`}}}function pe(e){let t=e.summary??{};if(h(e)||m(e.tool)||p(e.tool)||f(e.tool))return{kind:`diff`,additions:g(t,`additions`)??0,removals:g(t,`removals`)??0};if(e.tool===`open_workspace`){let e=[T(g(t,`agentsFiles`),`instruction`),T(g(t,`skills`),`skill`)].filter(e=>!!e);return e.length>0?{kind:`text`,text:e.join(` · `)}:{kind:`empty`}}if(ee(e.tool)){let e=[T(g(t,`lines`),`line`),ye(g(t,`wallTimeMs`))].filter(e=>!!e);return e.length>0?{kind:`text`,text:e.join(` · `)}:{kind:`empty`}}if(e.tool===`grep`||e.tool===`read`||e.tool===`ls`){let e=T(g(t,`lines`),`line`);return e?{kind:`text`,text:e}:{kind:`empty`}}return{kind:`empty`}}function me(e){return e===`added`?o.writeFile:e===`deleted`?o.deleteFile:e===`renamed`||e===`renamed-edited`?o.files:o.editFile}function he(e){return`${e.workspaceReused?`Reused`:`Opened`} workspace`}function C(e){if(e.files?.length===1)return y(e.files[0])?.title??e.path}function ge(e){let t=e.summary?.pattern,n=e.summary?.scope;return typeof t==`string`?typeof n==`string`&&n!==`.`?`${t} in ${n}`:t:e.path}function _e(e,t){if(e.summary?.running===!0)return t===`command`?`Command running`:`Process running`;let n=g(e.summary,`exitCode`);return n!==void 0&&n!==0?t===`command`?`Command failed`:`Process failed`:t===`command`?`Ran command`:`Process finished`}function ve(e){if(e.summary?.running===!0)return`running`;let t=g(e.summary,`exitCode`);return t!==void 0&&t!==0?`error`:t===0?`success`:void 0}function w(e){let t=e.summary?.command;if(typeof t==`string`)return t;let n=e.summary?.sessionId;return typeof n==`number`||typeof n==`string`?`Session ${String(n)}`:e.path}function T(e,t){if(e!==void 0)return`${e} ${t}${e===1?``:`s`}`}function ye(e){if(e!==void 0)return e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(+(e<1e4))}s`}var E=null,D=!1,O=null,k,A=null,j=!1,M=!1,N=null,P=null,F=null,I=null,L=!1,R=document.querySelector(`#app`);if(!R)throw Error(`Missing #app root element.`);var z=R,B=new c(z);be();async function be(){H(),E=new i({name:`forgerelay-tool-cards`,version:`0.1.0`},{}),E.ontoolresult=e=>{let t=a(B.active,e.structuredContent);if(t===`activity`&&B.accept(e)){A=null,j=!1,M=!1,I=null,L=!1,N=null,H();return}if(t===`preserve-panel`)return;let n=Re(e),r=Le(e),i=r?{...n,...r}:n,o=Ie(e);if(!o||!te(i)){A=null,j=!1,M=!1,I=null,L=!1,N=`No result card is available for this tool result.`,H();return}let s={...i,tool:o};A=s,j=re(s),M=!1,I=null,L=!1,N=null,H()},E.onhostcontextchanged=e=>{k={...k,...e},V(),B.active?B.render():A?.tool!==`open_workspace`&&W()},E.onteardown=async()=>(D=!1,B.detach(),G(),{});try{await E.connect();let e=E.getHostContext();e&&(k=e),V(),D=!0,B.attach(E)}catch(e){O=e instanceof Error?e.message:String(e)}H()}function V(){k?.theme&&l(k.theme),k?.styles?.variables&&t(k.styles.variables),k?.styles?.css?.fonts&&s(k.styles.css.fonts);let e=k?.safeAreaInsets;e&&(document.body.style.padding=`${e.top}px ${e.right}px ${e.bottom}px ${e.left}px`)}function H(){if(G(),O){U(O,`error`);return}if(!D){U(`Connecting to host...`);return}if(B.render())return;if(!A){U(N??`Waiting for a tool result.`,N?`error`:`muted`);return}let t=fe(A);if(h(A)){Ce(A,t);return}let n=_(A),r=$(`main`,{className:`shell`}),i=$(`section`,{className:Te(t)}),a=$(`button`,{className:`tool-header`,type:`button`,ariaExpanded:String(j),disabled:!n});n&&a.addEventListener(`click`,()=>{j=!j,H()});let o=$(`span`,{className:`tool-icon`,ariaHidden:`true`});o.append(e(t.icon));let s=$(`span`,{className:`tool-main`}),c=$(`span`,{className:`tool-title`,text:t.title});if(s.append(c),t.label&&s.append($(`span`,{className:`tool-label`,text:t.label,title:t.label})),a.append(o,s,J(A),we(j,n)),i.append(a),j){let e=$(`div`,{className:`tool-body`});F=e,i.append(e)}r.append(i),z.replaceChildren(r),W()}function U(e,t=`muted`){let n=$(`main`,{className:`shell`});n.append($(`section`,{className:`empty ${t}`,text:e})),z.replaceChildren(n)}async function W(){if(!A||!F||!j)return;let e=F;if(N){q(e,N,`error`);return}if(A.tool===`open_workspace`){Ee(e,A);return}if(xe(A)){if(P){P.update({card:A,hostContext:k,errorMessage:N});return}Y(e,!0);try{let{mountHeavyPayload:t}=await n(async()=>{let{mountHeavyPayload:e}=await import(`./heavy-payload-vGgBRvNX.js`);return{mountHeavyPayload:e}},__vite__mapDeps([0,1,2,3,4]),import.meta.url);if(e!==F||!j||!A)return;Y(e,!1),P=t(e,{card:A,hostContext:k,errorMessage:N})}catch(t){if(e!==F||!j)return;Y(e,!1),q(e,t instanceof Error?t.message:`Unable to load details.`,`error`)}return}if(h(A)||m(A.tool)){let t=h(A)&&!M?Math.max(3,(A.files??[]).slice(0,3).length):void 0;if(P){P.update({card:A,hostContext:k,errorMessage:N,visibleFileCount:t});return}q(e,h(A)?`Loading review...`:`Loading diff...`);let{mountReviewPayload:r}=await n(async()=>{let{mountReviewPayload:e}=await import(`./review-payload-4erWKckt.js`);return{mountReviewPayload:e}},__vite__mapDeps([5,1,2,3,4]),import.meta.url);if(e!==F||!A)return;P=r(e,{card:A,hostContext:k,errorMessage:N,visibleFileCount:t});return}let t=ne(A.payload);if(!t){q(e,`No details available.`);return}Se(e,t,A.tool)}function xe(e){return d(e.tool)||p(e.tool)||f(e.tool)}function G(){K(),P=null,F=null}function K(){P?.unmount(),P=null}function q(e,t,n=`muted`){K(),e.replaceChildren($(`div`,{className:`status ${n}`,text:t}))}function Se(e,t,n){K(),e.replaceChildren($(`pre`,{className:`text-payload pretty-scrollbar ${n}`,text:t}))}function J(e){let t=pe(e);if(t.kind===`diff`){let e=$(`span`,{className:`stats`});return e.setAttribute(`aria-label`,`Diff statistics`),e.append($(`span`,{className:`add`,text:`+${String(t.additions)}`}),$(`span`,{className:`remove`,text:`-${String(t.removals)}`})),e}let n=$(`span`,{className:`header-meta ${t.kind===`empty`?`empty`:``}`,text:t.kind===`text`?t.text:``});return t.kind===`empty`&&n.setAttribute(`aria-hidden`,`true`),n}function Ce(t,n){G();let r=t.files??[],i=M?r:r.slice(0,3),a=Math.max(0,r.length-i.length),o=_(t),s=$(`main`,{className:`shell`}),c=$(`section`,{className:Te(n)}),l=$(`button`,{className:`tool-header review-header`,type:`button`,ariaExpanded:String(j),disabled:!o});o&&l.addEventListener(`click`,()=>{j=!j,H()});let u=$(`span`,{className:`tool-icon`,ariaHidden:`true`});u.append(e(n.icon));let d=$(`span`,{className:`tool-main review-title-group`});if(d.append($(`span`,{className:`tool-title`,text:n.title})),n.label&&d.append($(`span`,{className:`tool-label`,text:n.label,title:n.label})),l.append(u,d,J(t),we(j,o)),c.append(l),j){let e=$(`div`,{className:`review-summary`}),t=$(`div`,{className:`review-payload`});if(F=t,e.append(t),a>0){let t=$(`button`,{className:`review-more`,type:`button`,text:`Show ${a} more ${a===1?`file`:`files`}`});t.addEventListener(`click`,()=>{M=!0,H()}),e.append(t)}c.append(e)}s.append(c),z.replaceChildren(s),W()}function we(t,n){let r=$(`span`,{className:n?`chevron ${t?`expanded`:``}`:`chevron`,ariaHidden:`true`});return n&&r.append(e(o.chevronDown)),r}function Te(e){return[`tool-card`,e.tone,e.state?`state-${e.state}`:void 0].filter(Boolean).join(` `)}function Y(t,n){let r=t.previousElementSibling,i=r?.querySelector(`.chevron`);if(!i)return;i.classList.toggle(`loading`,n),i.replaceChildren(e(n?o.loading:o.chevronDown));let a=r instanceof HTMLButtonElement?r:null;a&&a.setAttribute(`aria-busy`,String(n))}function Ee(t,n){K();let i=$(`div`,{className:`workspace-details pretty-scrollbar`}),a=$(`div`,{className:`workspace-rows`}),s=n.worktree;if(s){let t=[s.baseRef,s.baseSha?.slice(0,8)].filter(e=>!!e).join(` · `)||`Worktree`,n=$(`span`,{className:`workspace-base-value`});if(n.append($(`span`,{className:`workspace-value`,text:t,title:t})),s.dirtySource){let t=$(`span`,{className:`workspace-base-warning`,title:`The source checkout had uncommitted changes when this worktree was created. Those changes are not included here.`,ariaLabel:`Source checkout changes are not included in this worktree`});t.append(e(o.warning,`workspace-base-warning-svg`)),n.append(t)}Z(a,`Base`,n,o.base),s.branch&&X(a,`Worktree branch`,s.branch,o.gitBranch,!1),s.targetBranch&&X(a,`Merge target`,s.targetBranch,o.gitBranch,!1)}n.sourceRoot&&n.sourceRoot!==n.root&&X(a,`Source checkout`,n.sourceRoot,o.sourceCheckout,!0),De(a,n.agentsFiles??[],n.availableAgentsFiles??[]);let c=n.skills??[];c.length>0&&Pe(a,c);let l=n.agentProviders??[],u=(n.agents??[]).map(e=>{let t=e.name??`Unnamed agent`,n=e.provider?.trim(),i=e.providerAvailable===!1,a=[e.description,n?`Provider: ${n}`:void 0,e.model?`Model: ${e.model}`:void 0,e.thinking?`Thinking: ${e.thinking}`:void 0,i?e.providerUnavailableReason??`Provider unavailable`:void 0].filter(e=>!!e).join(`
|
|
5
|
-
`);return{label:t,logo:n?r(n):void 0,profile:!0,tone:i?`muted`:void 0,title:a||void 0}}),d=l.map(e=>{let t=e.name?.trim()||`Unknown provider`,n=e.available===!1,i=r(t);return{label:t,logo:i,bareLogo:!!i,ariaLabel:t,tone:n?`muted`:void 0,title:n?e.reason??`Provider unavailable`:t}});if(u.length>0){let e=Q([...u,...d]);e.classList.add(`workspace-agents-list`),Z(a,`Agents`,e,o.agents,`workspace-agents-row`)}else d.length>0&&Ne(a,`Providers`,d,o.providers);a.childElementCount>0&&i.append(a),i.childElementCount===0&&i.append($(`div`,{className:`status muted`,text:`No workspace details available.`})),t.replaceChildren(i)}function De(e,t,n){let r=[],i=new Set;for(let[e,n]of t.entries())r.push({key:`loaded:${e}`,path:n.path,label:n.path??`Loaded instructions`,content:n.content,status:`loaded`}),n.path&&i.add(n.path);let a=[];for(let[e,t]of n.entries())t.path&&i.has(t.path)||a.push({key:`available:${e}`,path:t.path,label:t.path??`Nested instructions`,status:`available`});if(r.length===0&&a.length===0)return;let s=Oe(L?[...r,...a]:r);if(a.length>0){let e=L,t=$(`button`,{className:`workspace-instructions-toggle`,type:`button`,text:e?`Show less`:`View all`,ariaLabel:e?`Show only loaded instruction files`:`View all ${a.length} available instruction files`,ariaExpanded:String(e)});t.addEventListener(`click`,()=>{L=!L,L||(I=null),H()}),s.append(t)}let c=$(`div`,{className:`workspace-instructions-content`});c.append(s),Z(e,`Instructions`,c,o.instructions,`workspace-instructions-row`)}function Oe(t){let n=$(`span`,{className:`workspace-instruction-list`});for(let r of t){let t=$(`span`,{className:`workspace-instruction-item`});t.dataset.instructionKey=r.key;let i=r.status===`loaded`&&r.content!==void 0,a=$(i?`button`:`span`,{className:`workspace-instruction-header${i?` interactive`:``}`,type:i?`button`:void 0,ariaLabel:i?`View ${r.label}`:void 0,ariaExpanded:i?`false`:void 0}),s=$(`span`,{className:`workspace-instruction-text`}),c=Me(r.label);if(s.append($(`span`,{className:`workspace-instruction-name`,text:c})),r.path&&r.path!==c&&s.append($(`span`,{className:`workspace-instruction-path`,text:r.path,title:r.path})),a.append(Ae(r.status),s),i){let i=$(`span`,{className:`workspace-instruction-chevron`,ariaHidden:`true`});i.append(e(o.chevronDown,`workspace-instruction-chevron-svg`)),a.append(i),a.addEventListener(`click`,()=>{I=I===r.key?null:r.key,ke(n)});let s=$(`pre`,{className:`workspace-instruction-preview pretty-scrollbar`,text:r.content});s.hidden=!0,t.append(a,s)}else t.append(a);n.append(t)}return ke(n),n}function ke(e){for(let t of e.querySelectorAll(`.workspace-instruction-item`)){let e=t.dataset.instructionKey===I;t.classList.toggle(`expanded`,e),t.querySelector(`.workspace-instruction-header.interactive`)?.setAttribute(`aria-expanded`,String(e));let n=t.querySelector(`.workspace-instruction-preview`);n&&(n.hidden=!e)}}function Ae(t){let n=je(t),r=$(`span`,{className:`workspace-instruction-status ${t}`,title:n,ariaLabel:n});return r.setAttribute(`role`,`img`),r.append(e(t===`loaded`?o.instructionLoaded:o.instructionAvailable,`workspace-instruction-status-svg`)),r}function je(e){return e===`loaded`?`Loaded into the current workspace context`:`Available for a nested directory`}function Me(e){return e.replaceAll(`\\`,`/`).split(`/`).filter(Boolean).at(-1)??e}function X(e,t,n,r,i=!1){Z(e,t,$(`span`,{className:`workspace-value${i?` mono`:``}`,text:n,title:n}),r)}function Ne(e,t,n,r){Z(e,t,Q(n),r)}function Z(e,t,n,r,i){let a=$(`div`,{className:[`workspace-row`,i].filter(Boolean).join(` `)});a.append(Fe(r),$(`span`,{className:`workspace-key`,text:t}),n),e.append(a)}function Pe(e,t){let n=Q(t.map(e=>({label:e.name??`Unnamed skill`,title:e.description||void 0})));n.classList.add(`workspace-skills-list`),Z(e,`Skills`,n,o.skills,`workspace-skills-row`)}function Fe(t){let n=$(`span`,{className:`workspace-row-icon`,ariaHidden:`true`});return n.append(e(t,`workspace-row-icon-svg`)),n}function Q(e){let t=$(`span`,{className:`workspace-chip-list`});for(let n of e){let e=!!(n.bareLogo&&n.logo),r=$(`span`,{className:[e?`workspace-provider-logo`:n.profile?`workspace-agent-profile`:`workspace-chip`,n.tone].filter(Boolean).join(` `),title:n.title});if(e&&(r.setAttribute(`role`,`img`),r.setAttribute(`aria-label`,n.ariaLabel??n.label)),n.logo){let t=document.createElement(`img`);t.className=e?`workspace-provider-logo-image`:n.profile?`workspace-agent-profile-logo`:`workspace-chip-logo`,t.src=n.logo,t.alt=``,t.setAttribute(`aria-hidden`,`true`),r.append(t)}e||r.append($(`span`,{className:`workspace-chip-label`,text:n.label})),t.append(r)}return t}function Ie(e){let t=e._meta?.tool;return u(t)?t:void 0}function Le(e){let t=e._meta?.card;return t&&typeof t==`object`?t:void 0}function Re(e){return e.structuredContent}function $(e,t={}){let n=document.createElement(e);return t.className&&(n.className=t.className),t.text!==void 0&&(n.textContent=t.text),t.type!==void 0&&`type`in n&&n.setAttribute(`type`,t.type),t.title!==void 0&&(n.title=t.title),t.ariaHidden!==void 0&&n.setAttribute(`aria-hidden`,t.ariaHidden),t.ariaLabel!==void 0&&n.setAttribute(`aria-label`,t.ariaLabel),t.ariaExpanded!==void 0&&n.setAttribute(`aria-expanded`,t.ariaExpanded),t.disabled!==void 0&&`disabled`in n&&(n.disabled=t.disabled),n}export{d as a,g as c,p as i,oe as n,f as o,se as r,ne as s,ce as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./workspace-app-DkAiSl_0.js";import"./workspace-app-QyauBrJX.js";document.documentElement.dataset.forgerelayApp=`workspace-lifecycle-compatibility`;
|