@akira-tl/forgerelay 0.8.4 → 0.8.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/CHANGELOG.md +30 -0
- package/README.md +10 -10
- package/dist/activity/audit-store.js +44 -6
- package/dist/activity/mcp-query-tools.js +53 -36
- package/dist/activity/query-service.js +41 -11
- package/dist/cli.js +16 -17
- package/dist/composite-activity.js +124 -33
- package/dist/config.js +8 -13
- package/dist/lsp/test-support/server-fixture.js +7 -7
- package/dist/process-sessions.js +2 -2
- package/dist/remote-auth.js +11 -1
- package/dist/remote-mcp-connection-pool.js +90 -0
- package/dist/remote-transport.js +28 -14
- package/dist/remote-workspace-relay.js +203 -45
- package/dist/server.js +134 -64
- package/dist/skills.js +1 -1
- package/dist/subagents/profiles.js +1 -2
- package/dist/subagents/providers/adapters/pi.js +2 -2
- package/dist/subagents/providers/availability.js +2 -2
- package/dist/subagents/providers/path.js +4 -4
- package/dist/ui/.vite/manifest.json +32 -32
- package/dist/ui/activity-panel-app.html +3 -3
- package/dist/ui/assets/{activity-panel-app-E1ju2dqI.js → activity-panel-app-CUAN6zyW.js} +1 -1
- package/dist/ui/assets/{heavy-payload-CeW-n9w5.js → heavy-payload-CgzrutLm.js} +1 -1
- package/dist/ui/assets/{review-payload-B9CO298v.js → review-payload-BrLbezbq.js} +1 -1
- package/dist/ui/assets/{scrollbar-C2twAENW.js → scrollbar-CbhpdW05.js} +1 -1
- package/dist/ui/assets/workspace-app-Bhj96tsR.js +1 -0
- package/dist/ui/assets/{workspace-app-CwbJnb_w.js → workspace-app-CxwJuZyS.js} +1 -1
- package/dist/ui/assets/{workspace-app-BztEvZIC.js → workspace-app-D6UR0AFl.js} +3 -3
- package/dist/ui/assets/workspace-app-ldjBmCJR.css +1 -0
- package/dist/ui/assets/workspace-lifecycle-app-Cqfhx9pV.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 +3 -19
- package/dist/workspace-presentation.js +69 -0
- package/docs/agent-profile-schema.md +4 -9
- package/docs/artifact-exchange.md +2 -1
- package/docs/chatgpt-coding-workflow.md +9 -9
- package/docs/configuration.md +20 -29
- package/docs/gotchas.md +10 -7
- package/docs/roadmap.md +6 -3
- package/docs/security.md +3 -2
- package/docs/setup.md +8 -5
- package/docs/versioning.md +1 -1
- package/package.json +6 -2
- package/scripts/debug/accept.mjs +7 -1
- package/scripts/debug/relay-accept.mjs +889 -0
- package/scripts/debug/runtime.mjs +0 -4
- package/scripts/debug/runtime.test.mjs +0 -2
- package/scripts/debug/traffic/run.sh +5 -0
- package/scripts/debug/traffic/traffic-audit.mjs +907 -0
- package/scripts/release/push-ready.mjs +124 -0
- package/scripts/release/push-ready.test.mjs +108 -0
- package/scripts/release/release-gate.test.mjs +1 -0
- package/scripts/release-proof.mjs +16 -1
- package/scripts/wiki/sync.mjs +246 -0
- package/dist/ui/assets/workspace-app-YnUST8IP.css +0 -1
- package/dist/ui/assets/workspace-app-rKuhdae8.js +0 -1
- package/dist/ui/assets/workspace-lifecycle-app-CEfMdudP.js +0 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const repoRoot = process.cwd();
|
|
8
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const proofScript = resolve(scriptDir, "..", "release-proof.mjs");
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const branch = git(["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
13
|
+
if (branch !== "main" && !branch.startsWith("release/")) {
|
|
14
|
+
throw new Error(`release-ready push must run from main or release/*, not ${branch}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
verifyReleaseProof();
|
|
18
|
+
git(["fetch", "--quiet", "origin", "main"]);
|
|
19
|
+
|
|
20
|
+
const head = git(["rev-parse", "HEAD"]);
|
|
21
|
+
if (!gitSucceeds(["merge-base", "--is-ancestor", "origin/main", head])) {
|
|
22
|
+
throw new Error("release HEAD must fast-forward origin/main; reconcile remote main before publishing");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (branch !== "main") {
|
|
26
|
+
assertLocalMainCanMove(head);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const refspecs = ["HEAD:refs/heads/main"];
|
|
30
|
+
if (branch !== "main") refspecs.push(`HEAD:refs/heads/${branch}`);
|
|
31
|
+
runGit(["push", "--atomic", "origin", ...refspecs]);
|
|
32
|
+
|
|
33
|
+
if (branch !== "main") {
|
|
34
|
+
runGit(["branch", "-f", "main", head]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const remoteMain = git(["rev-parse", "origin/main"]);
|
|
38
|
+
const localMain = git(["rev-parse", "main"]);
|
|
39
|
+
if (remoteMain !== head || localMain !== head) {
|
|
40
|
+
throw new Error("release-ready push completed but local/remote main did not converge on release HEAD");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(`Release-ready branches synchronized at ${head.slice(0, 12)} (${branch} -> origin/main).`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`release-push-ready: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function verifyReleaseProof() {
|
|
50
|
+
const result = spawnSync(process.execPath, [proofScript, "check"], {
|
|
51
|
+
cwd: repoRoot,
|
|
52
|
+
env: process.env,
|
|
53
|
+
encoding: "utf8",
|
|
54
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
55
|
+
windowsHide: true,
|
|
56
|
+
shell: false,
|
|
57
|
+
});
|
|
58
|
+
if (result.error) throw result.error;
|
|
59
|
+
if (result.status !== 0) {
|
|
60
|
+
const detail = `${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim();
|
|
61
|
+
throw new Error(detail || `release proof check failed with exit ${result.status ?? "unknown"}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function assertLocalMainCanMove(head) {
|
|
66
|
+
git(["rev-parse", "--verify", "refs/heads/main"]);
|
|
67
|
+
const uniquePatches = git(["cherry", head, "main"])
|
|
68
|
+
.split("\n")
|
|
69
|
+
.map((line) => line.trim())
|
|
70
|
+
.filter((line) => line.startsWith("+ "));
|
|
71
|
+
if (uniquePatches.length > 0) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`local main has commits with patches not present in release HEAD: ${uniquePatches.map((line) => line.slice(2, 14)).join(", ")}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const mainWorktree = checkedOutBranchWorktree("refs/heads/main");
|
|
78
|
+
if (mainWorktree) {
|
|
79
|
+
throw new Error(`local main is checked out in another worktree (${mainWorktree}); switch it away before release-ready push`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function checkedOutBranchWorktree(branchRef) {
|
|
84
|
+
const records = git(["worktree", "list", "--porcelain"]).split(/\n\n+/);
|
|
85
|
+
for (const record of records) {
|
|
86
|
+
const lines = record.split("\n");
|
|
87
|
+
if (!lines.includes(`branch ${branchRef}`)) continue;
|
|
88
|
+
return lines.find((line) => line.startsWith("worktree "))?.slice("worktree ".length);
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function git(args) {
|
|
94
|
+
return execFileSync("git", args, {
|
|
95
|
+
cwd: repoRoot,
|
|
96
|
+
encoding: "utf8",
|
|
97
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
98
|
+
}).trim();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function gitSucceeds(args) {
|
|
102
|
+
const result = spawnSync("git", args, {
|
|
103
|
+
cwd: repoRoot,
|
|
104
|
+
stdio: "ignore",
|
|
105
|
+
windowsHide: true,
|
|
106
|
+
shell: false,
|
|
107
|
+
});
|
|
108
|
+
if (result.error) throw result.error;
|
|
109
|
+
return result.status === 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function runGit(args) {
|
|
113
|
+
const result = spawnSync("git", args, {
|
|
114
|
+
cwd: repoRoot,
|
|
115
|
+
env: process.env,
|
|
116
|
+
stdio: "inherit",
|
|
117
|
+
windowsHide: true,
|
|
118
|
+
shell: false,
|
|
119
|
+
});
|
|
120
|
+
if (result.error) throw result.error;
|
|
121
|
+
if (result.status !== 0) {
|
|
122
|
+
throw new Error(`git ${args[0]} failed with exit ${result.status ?? "unknown"}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
10
|
+
const pushReadyScript = resolve(repoRoot, "scripts/release/push-ready.mjs");
|
|
11
|
+
const releaseProofScript = resolve(repoRoot, "scripts/release-proof.mjs");
|
|
12
|
+
|
|
13
|
+
function git(cwd, args) {
|
|
14
|
+
return execFileSync("git", args, {
|
|
15
|
+
cwd,
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18
|
+
}).trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function writePackage(root, version = "1.2.3") {
|
|
22
|
+
writeFileSync(join(root, "package.json"), `${JSON.stringify({ version }, null, 2)}\n`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function commitAll(root, message) {
|
|
26
|
+
git(root, ["add", "--all"]);
|
|
27
|
+
git(root, ["commit", "-m", message]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function fixture() {
|
|
31
|
+
const root = mkdtempSync(join(tmpdir(), "forgerelay-release-push-ready-"));
|
|
32
|
+
const origin = join(root, "origin.git");
|
|
33
|
+
const checkout = join(root, "checkout");
|
|
34
|
+
mkdirSync(checkout, { recursive: true });
|
|
35
|
+
git(root, ["init", "--bare", origin]);
|
|
36
|
+
git(checkout, ["init", "-b", "main"]);
|
|
37
|
+
git(checkout, ["config", "user.email", "forgerelay@example.com"]);
|
|
38
|
+
git(checkout, ["config", "user.name", "ForgeRelay Test"]);
|
|
39
|
+
git(checkout, ["remote", "add", "origin", origin]);
|
|
40
|
+
writePackage(checkout);
|
|
41
|
+
writeFileSync(join(checkout, "README.md"), "base\n");
|
|
42
|
+
commitAll(checkout, "base");
|
|
43
|
+
git(checkout, ["push", "-u", "origin", "main"]);
|
|
44
|
+
return { root, origin, checkout };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function writeProof(checkout) {
|
|
48
|
+
execFileSync(process.execPath, [releaseProofScript, "write"], {
|
|
49
|
+
cwd: checkout,
|
|
50
|
+
stdio: "pipe",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
test("release push-ready atomically advances remote main/release and synchronizes local main", () => {
|
|
55
|
+
const context = fixture();
|
|
56
|
+
try {
|
|
57
|
+
const { checkout, origin } = context;
|
|
58
|
+
git(checkout, ["switch", "-c", "release/1.2.3"]);
|
|
59
|
+
writeFileSync(join(checkout, "release.txt"), "release\n");
|
|
60
|
+
commitAll(checkout, "release");
|
|
61
|
+
const releaseHead = git(checkout, ["rev-parse", "HEAD"]);
|
|
62
|
+
writeProof(checkout);
|
|
63
|
+
|
|
64
|
+
execFileSync(process.execPath, [pushReadyScript], {
|
|
65
|
+
cwd: checkout,
|
|
66
|
+
stdio: "pipe",
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
assert.equal(git(checkout, ["rev-parse", "main"]), releaseHead);
|
|
70
|
+
assert.equal(git(checkout, ["rev-parse", "origin/main"]), releaseHead);
|
|
71
|
+
assert.equal(git(checkout, ["rev-parse", "origin/release/1.2.3"]), releaseHead);
|
|
72
|
+
assert.equal(git(origin, ["rev-parse", "refs/heads/main"]), releaseHead);
|
|
73
|
+
assert.equal(git(origin, ["rev-parse", "refs/heads/release/1.2.3"]), releaseHead);
|
|
74
|
+
} finally {
|
|
75
|
+
rmSync(context.root, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("release push-ready refuses to move main when local main has a real unique patch", () => {
|
|
80
|
+
const context = fixture();
|
|
81
|
+
try {
|
|
82
|
+
const { checkout, origin } = context;
|
|
83
|
+
const base = git(checkout, ["rev-parse", "HEAD"]);
|
|
84
|
+
|
|
85
|
+
writeFileSync(join(checkout, "main-only.txt"), "must preserve\n");
|
|
86
|
+
commitAll(checkout, "main only");
|
|
87
|
+
const localMain = git(checkout, ["rev-parse", "HEAD"]);
|
|
88
|
+
|
|
89
|
+
git(checkout, ["switch", "-c", "release/1.2.3", base]);
|
|
90
|
+
writeFileSync(join(checkout, "release.txt"), "release\n");
|
|
91
|
+
commitAll(checkout, "release");
|
|
92
|
+
writeProof(checkout);
|
|
93
|
+
|
|
94
|
+
const result = spawnSync(process.execPath, [pushReadyScript], {
|
|
95
|
+
cwd: checkout,
|
|
96
|
+
encoding: "utf8",
|
|
97
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
assert.notEqual(result.status, 0);
|
|
101
|
+
assert.match(`${result.stdout}\n${result.stderr}`, /local main has commits with patches not present in release HEAD/i);
|
|
102
|
+
assert.equal(git(checkout, ["rev-parse", "main"]), localMain);
|
|
103
|
+
assert.equal(git(origin, ["rev-parse", "refs/heads/main"]), base);
|
|
104
|
+
assert.throws(() => git(origin, ["rev-parse", "refs/heads/release/1.2.3"]));
|
|
105
|
+
} finally {
|
|
106
|
+
rmSync(context.root, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
@@ -34,6 +34,7 @@ test("optional release:verify records proof only after the cloud-equivalent pari
|
|
|
34
34
|
pkg.scripts["release:verify"],
|
|
35
35
|
"npm run release:parity && node scripts/release-proof.mjs write",
|
|
36
36
|
);
|
|
37
|
+
assert.equal(pkg.scripts["release:push-ready"], "node scripts/release/push-ready.mjs");
|
|
37
38
|
});
|
|
38
39
|
|
|
39
40
|
test("cross-platform cloud CI delegates to one shell-free verification entrypoint", async () => {
|
|
@@ -83,6 +83,20 @@ function readProof() {
|
|
|
83
83
|
return parsed;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
function checkProof() {
|
|
87
|
+
assertReleaseTreeClean("using the local release proof");
|
|
88
|
+
const proof = readProof();
|
|
89
|
+
const head = currentHead();
|
|
90
|
+
const version = packageVersion();
|
|
91
|
+
if (proof.head !== head) {
|
|
92
|
+
throw new Error(`release proof is for ${proof.head.slice(0, 12)}, but current HEAD is ${head.slice(0, 12)}. Run npm run release:verify again`);
|
|
93
|
+
}
|
|
94
|
+
if (proof.packageVersion !== version) {
|
|
95
|
+
throw new Error(`release proof is for package ${proof.packageVersion}, but package.json is ${version}. Run npm run release:verify again`);
|
|
96
|
+
}
|
|
97
|
+
console.log(`Release proof OK: ${head.slice(0, 12)} (${version}).`);
|
|
98
|
+
}
|
|
99
|
+
|
|
86
100
|
function hookTag() {
|
|
87
101
|
let payload;
|
|
88
102
|
try {
|
|
@@ -141,8 +155,9 @@ function checkHookTag() {
|
|
|
141
155
|
const action = process.argv[2];
|
|
142
156
|
try {
|
|
143
157
|
if (action === "write") writeProof();
|
|
158
|
+
else if (action === "check") checkProof();
|
|
144
159
|
else if (action === "check-hook") checkHookTag();
|
|
145
|
-
else throw new Error("usage: node scripts/release-proof.mjs <write|check-hook>");
|
|
160
|
+
else throw new Error("usage: node scripts/release-proof.mjs <write|check|check-hook>");
|
|
146
161
|
} catch (error) {
|
|
147
162
|
fail(error instanceof Error ? error.message : String(error));
|
|
148
163
|
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
cpSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdtempSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
statSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { basename, join, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
|
|
17
|
+
const scriptDirectory = resolve(fileURLToPath(new URL(".", import.meta.url)));
|
|
18
|
+
const repositoryRoot = resolve(scriptDirectory, "..", "..");
|
|
19
|
+
const sourceDirectory = join(repositoryRoot, "docs", "wiki");
|
|
20
|
+
const command = process.argv[2] ?? "check";
|
|
21
|
+
|
|
22
|
+
class WikiSyncError extends Error {}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
if (!new Set(["check", "publish"]).has(command)) {
|
|
26
|
+
fail(`Unknown wiki command: ${command}\nUsage: node scripts/wiki/sync.mjs <check|publish>`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sourceFiles = validateWikiSource(sourceDirectory);
|
|
30
|
+
console.log(`Wiki source check passed (${sourceFiles.length} files).`);
|
|
31
|
+
|
|
32
|
+
if (command === "publish") {
|
|
33
|
+
publishWiki();
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error instanceof WikiSyncError) {
|
|
37
|
+
console.error(error.message);
|
|
38
|
+
} else if (error instanceof Error) {
|
|
39
|
+
console.error(error.stack ?? error.message);
|
|
40
|
+
} else {
|
|
41
|
+
console.error(String(error));
|
|
42
|
+
}
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function validateWikiSource(directory) {
|
|
47
|
+
if (!existsSync(directory) || !statSync(directory).isDirectory()) {
|
|
48
|
+
fail(`Wiki source directory does not exist: ${directory}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const files = listFiles(directory);
|
|
52
|
+
const names = new Set(files.map((file) => file.relativePath));
|
|
53
|
+
|
|
54
|
+
for (const required of ["Home.md", "_Sidebar.md"]) {
|
|
55
|
+
if (!names.has(required)) {
|
|
56
|
+
fail(`Wiki source is missing required page: ${required}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const invalidNames = files
|
|
61
|
+
.map((file) => file.relativePath)
|
|
62
|
+
.filter((name) => !isSafeWikiPath(name));
|
|
63
|
+
if (invalidNames.length > 0) {
|
|
64
|
+
fail(`Wiki source contains unsupported paths:\n${invalidNames.map((name) => `- ${name}`).join("\n")}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const markdownNames = new Set(
|
|
68
|
+
files
|
|
69
|
+
.filter((file) => file.relativePath.endsWith(".md"))
|
|
70
|
+
.map((file) => file.relativePath),
|
|
71
|
+
);
|
|
72
|
+
const brokenLinks = [];
|
|
73
|
+
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
if (!file.relativePath.endsWith(".md")) continue;
|
|
76
|
+
const text = readFileSync(file.absolutePath, "utf8");
|
|
77
|
+
for (const match of text.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)) {
|
|
78
|
+
const rawTarget = match[1]?.trim();
|
|
79
|
+
if (!rawTarget || isExternalOrAnchorLink(rawTarget)) continue;
|
|
80
|
+
|
|
81
|
+
const target = decodeWikiTarget(rawTarget);
|
|
82
|
+
if (!target) continue;
|
|
83
|
+
if (target.startsWith("../") || target.startsWith("./")) {
|
|
84
|
+
brokenLinks.push(`${file.relativePath}: relative repository link ${rawTarget}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const candidate = target.endsWith(".md") ? target : `${target}.md`;
|
|
89
|
+
if (!markdownNames.has(candidate)) {
|
|
90
|
+
brokenLinks.push(`${file.relativePath}: ${rawTarget}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (brokenLinks.length > 0) {
|
|
96
|
+
fail(`Wiki source has unresolved local links:\n${brokenLinks.map((entry) => `- ${entry}`).join("\n")}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return files;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function publishWiki() {
|
|
103
|
+
const repository = process.env.GITHUB_REPOSITORY?.trim() || "Akira-TL/forgerelay";
|
|
104
|
+
const remote = process.env.FORGERELAY_WIKI_REMOTE?.trim() || `git@github.com:${repository}.wiki.git`;
|
|
105
|
+
const sourceRef = process.env.FORGERELAY_WIKI_SOURCE_REF?.trim() || currentSourceRef();
|
|
106
|
+
const temporaryRoot = mkdtempSync(join(tmpdir(), "forgerelay-wiki-"));
|
|
107
|
+
const wikiCheckout = join(temporaryRoot, "wiki");
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const clone = runGit(["clone", "--quiet", remote, wikiCheckout], repositoryRoot, { allowFailure: true });
|
|
111
|
+
if (clone.status !== 0) {
|
|
112
|
+
const message = sanitizeGitOutput(`${clone.stdout}\n${clone.stderr}`, remote);
|
|
113
|
+
if (/repository not found|not found/i.test(message)) {
|
|
114
|
+
console.error(
|
|
115
|
+
[
|
|
116
|
+
"ForgeRelay GitHub Wiki has not been initialized yet.",
|
|
117
|
+
`Create the first page once at https://github.com/${repository}/wiki, then rerun wiki publishing.`,
|
|
118
|
+
"GitHub does not create the cloneable .wiki.git repository until the first Wiki page exists.",
|
|
119
|
+
].join("\n"),
|
|
120
|
+
);
|
|
121
|
+
process.exitCode = 2;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
fail(`Could not clone the Wiki repository:\n${message || `git exited with ${clone.status}`}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
mirrorSource(sourceDirectory, wikiCheckout);
|
|
128
|
+
runGit(["add", "--all"], wikiCheckout);
|
|
129
|
+
|
|
130
|
+
const diff = runGit(["diff", "--cached", "--quiet"], wikiCheckout, { allowFailure: true });
|
|
131
|
+
if (diff.status === 0) {
|
|
132
|
+
console.log("GitHub Wiki is already up to date.");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (diff.status !== 1) {
|
|
136
|
+
fail(`Could not inspect Wiki changes (git diff exited with ${diff.status}).`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
runGit(["config", "user.name", process.env.FORGERELAY_WIKI_GIT_NAME?.trim() || "ForgeRelay Wiki Sync"], wikiCheckout);
|
|
140
|
+
runGit(
|
|
141
|
+
[
|
|
142
|
+
"config",
|
|
143
|
+
"user.email",
|
|
144
|
+
process.env.FORGERELAY_WIKI_GIT_EMAIL?.trim() || "41898282+github-actions[bot]@users.noreply.github.com",
|
|
145
|
+
],
|
|
146
|
+
wikiCheckout,
|
|
147
|
+
);
|
|
148
|
+
runGit(["commit", "--quiet", "-m", `Sync Wiki from ${sourceRef}`], wikiCheckout);
|
|
149
|
+
|
|
150
|
+
if (process.env.FORGERELAY_WIKI_DRY_RUN === "1") {
|
|
151
|
+
console.log(`Wiki mirror prepared from ${sourceRef}; dry-run requested, skipping push.`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const push = runGit(["push", "--quiet", "origin", "HEAD"], wikiCheckout, { allowFailure: true });
|
|
156
|
+
if (push.status !== 0) {
|
|
157
|
+
const message = sanitizeGitOutput(`${push.stdout}\n${push.stderr}`, remote);
|
|
158
|
+
fail(`Could not push the Wiki mirror:\n${message || `git exited with ${push.status}`}`);
|
|
159
|
+
}
|
|
160
|
+
console.log(`Published GitHub Wiki from ${sourceRef}.`);
|
|
161
|
+
} finally {
|
|
162
|
+
rmSync(temporaryRoot, { recursive: true, force: true });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function mirrorSource(source, destination) {
|
|
167
|
+
for (const entry of readdirSync(destination)) {
|
|
168
|
+
if (entry === ".git") continue;
|
|
169
|
+
rmSync(join(destination, entry), { recursive: true, force: true });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const entry of readdirSync(source)) {
|
|
173
|
+
cpSync(join(source, entry), join(destination, entry), { recursive: true });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function listFiles(directory, prefix = "") {
|
|
178
|
+
const files = [];
|
|
179
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
180
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
181
|
+
const absolutePath = join(directory, entry.name);
|
|
182
|
+
if (entry.isDirectory()) {
|
|
183
|
+
files.push(...listFiles(absolutePath, relativePath));
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (!entry.isFile()) {
|
|
187
|
+
fail(`Wiki source contains a non-regular file: ${relativePath}`);
|
|
188
|
+
}
|
|
189
|
+
files.push({ relativePath, absolutePath });
|
|
190
|
+
}
|
|
191
|
+
return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function isSafeWikiPath(relativePath) {
|
|
195
|
+
if (!relativePath.endsWith(".md")) return false;
|
|
196
|
+
if (relativePath.includes("/")) return false;
|
|
197
|
+
return !/[\\:*?"<>|]/.test(relativePath) && basename(relativePath) === relativePath;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function isExternalOrAnchorLink(target) {
|
|
201
|
+
return /^(?:https?:|mailto:|#)/i.test(target);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function decodeWikiTarget(target) {
|
|
205
|
+
const withoutAnchor = target.split("#", 1)[0]?.trim();
|
|
206
|
+
if (!withoutAnchor) return "";
|
|
207
|
+
try {
|
|
208
|
+
return decodeURIComponent(withoutAnchor);
|
|
209
|
+
} catch {
|
|
210
|
+
return withoutAnchor;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function currentSourceRef() {
|
|
215
|
+
const result = runGit(["rev-parse", "--short=12", "HEAD"], repositoryRoot, { allowFailure: true });
|
|
216
|
+
return result.status === 0 ? result.stdout.trim() : "local source";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function runGit(args, cwd, { allowFailure = false } = {}) {
|
|
220
|
+
const result = spawnSync("git", args, {
|
|
221
|
+
cwd,
|
|
222
|
+
env: process.env,
|
|
223
|
+
encoding: "utf8",
|
|
224
|
+
windowsHide: true,
|
|
225
|
+
shell: false,
|
|
226
|
+
});
|
|
227
|
+
if (result.error) throw result.error;
|
|
228
|
+
if (!allowFailure && result.status !== 0) {
|
|
229
|
+
const output = sanitizeGitOutput(`${result.stdout}\n${result.stderr}`, args.join(" "));
|
|
230
|
+
fail(`git ${args[0]} failed with exit ${result.status}:\n${output}`);
|
|
231
|
+
}
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function sanitizeGitOutput(output, secretSource) {
|
|
236
|
+
let sanitized = String(output ?? "").trim();
|
|
237
|
+
sanitized = sanitized.replace(/https:\/\/[^/@\s]+@github\.com\//gi, "https://github.com/");
|
|
238
|
+
if (secretSource && secretSource.includes("@") && /^https:\/\//i.test(secretSource)) {
|
|
239
|
+
sanitized = sanitized.split(secretSource).join("<wiki remote>");
|
|
240
|
+
}
|
|
241
|
+
return sanitized;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function fail(message) {
|
|
245
|
+
throw new WikiSyncError(message);
|
|
246
|
+
}
|
|
@@ -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-member{color:var(--color-text-secondary,#d4d4d8);font-family:var(--font-mono,ui-monospace, SFMono-Regular, monospace);font-size:var(--font-text-xs-size,10px);flex:none;line-height:1.4}.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-CwbJnb_w.js";import"./workspace-app-BztEvZIC.js";document.documentElement.dataset.forgerelayApp=`historical-tool-card`;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./workspace-app-CwbJnb_w.js";import"./workspace-app-BztEvZIC.js";document.documentElement.dataset.forgerelayApp=`workspace-lifecycle-compatibility`;
|