@akira-tl/forgerelay 0.2.0 → 0.2.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 +24 -0
- package/dist/apply-patch.js +21 -6
- package/dist/artifact-tools.js +9 -4
- package/dist/config.js +8 -6
- package/dist/file-mutations.js +50 -0
- package/dist/hooks.js +4 -1
- package/dist/logger.js +157 -15
- package/dist/mcp/server-instructions.js +13 -9
- package/dist/pi-tools.js +14 -13
- package/dist/roots.js +30 -1
- package/dist/server.js +210 -54
- package/dist/ui/.vite/manifest.json +280 -280
- package/dist/ui/assets/{angular-html-CovppXt5.js → angular-html-CQmCUfkv.js} +1 -1
- package/dist/ui/assets/{angular-ts-0Y9l6YXu.js → angular-ts-BSbfxpMS.js} +1 -1
- package/dist/ui/assets/{apl-BmBhiklA.js → apl-CWCaH-E3.js} +1 -1
- package/dist/ui/assets/{astro-BAmqcqyV.js → astro-R_p4G0xA.js} +1 -1
- package/dist/ui/assets/{blade-BjFcmRJo.js → blade-irHensDY.js} +1 -1
- package/dist/ui/assets/{c-CaOJn4x0.js → c-_XEQUoq6.js} +1 -1
- package/dist/ui/assets/{cobol-DR4zWFMt.js → cobol-Css6VQjJ.js} +1 -1
- package/dist/ui/assets/{coffee-ReGSThmM.js → coffee-CpXUkyDm.js} +1 -1
- package/dist/ui/assets/{cpp-CnlQh6MO.js → cpp-BQHsPWwI.js} +1 -1
- package/dist/ui/assets/{crystal-BrpPbH1U.js → crystal-Cbv8J9Fm.js} +1 -1
- package/dist/ui/assets/{css-DRBEvyPU.js → css-D8DkrS3U.js} +1 -1
- package/dist/ui/assets/{edge-cfwpdvl9.js → edge-bBL_XDAG.js} +1 -1
- package/dist/ui/assets/{elixir-7owrJxb6.js → elixir-CQjsM9sA.js} +1 -1
- package/dist/ui/assets/{elm-udiOjODk.js → elm-DLFl-jEZ.js} +1 -1
- package/dist/ui/assets/{erb-8ctXHMkS.js → erb-BJjsdB_Q.js} +1 -1
- package/dist/ui/assets/{git-rebase-P0V_qK1w.js → git-rebase-CqP5A2s0.js} +1 -1
- package/dist/ui/assets/{glimmer-js-CZ14ED7U.js → glimmer-js-B5Hh_7xe.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-DtBSFP-o.js → glimmer-ts-oaeSAC8s.js} +1 -1
- package/dist/ui/assets/{glsl-BX2BkZJK.js → glsl-BylkOpcF.js} +1 -1
- package/dist/ui/assets/{graphql-Dj_cPv4w.js → graphql-DsVRvP6S.js} +1 -1
- package/dist/ui/assets/{hack-DGRkd8xn.js → hack-D4N5abT_.js} +1 -1
- package/dist/ui/assets/{haml-BAPSvujW.js → haml-CIAJQIox.js} +1 -1
- package/dist/ui/assets/{handlebars-PYksdULG.js → handlebars-DDEZyNXR.js} +1 -1
- package/dist/ui/assets/{heavy-payload-BjE0LSUi.js → heavy-payload-DafM8yjB.js} +1 -1
- package/dist/ui/assets/{html-derivative-C6q86ZrS.js → html-derivative-CZ91Y2d5.js} +1 -1
- package/dist/ui/assets/{html-DloAPGZ1.js → html-zDy-3lvy.js} +1 -1
- package/dist/ui/assets/{http-fPvYDvHA.js → http-ezaodZRf.js} +1 -1
- package/dist/ui/assets/{hurl-Dbir4WvE.js → hurl-BmL0o7TO.js} +1 -1
- package/dist/ui/assets/{java-DvkqXg3z.js → java-Q2C0vwbH.js} +1 -1
- package/dist/ui/assets/{javascript-TONTV6Ty.js → javascript-UDhBkHE3.js} +1 -1
- package/dist/ui/assets/{jinja-OM1F5Gct.js → jinja-BWRskcYS.js} +1 -1
- package/dist/ui/assets/{jison-CBttugts.js → jison-B18BY9Ds.js} +1 -1
- package/dist/ui/assets/{json-Db5oY-7J.js → json-DNj7MpRr.js} +1 -1
- package/dist/ui/assets/{jsx-DKeJCgsc.js → jsx-CVpHJ6cr.js} +1 -1
- package/dist/ui/assets/{julia-NlIPhXaL.js → julia-CJBNBS9c.js} +1 -1
- package/dist/ui/assets/{just-BXBJB-mX.js → just-Bmu2JCQT.js} +1 -1
- package/dist/ui/assets/{latex-CGK-6yDy.js → latex-BB7F93KY.js} +1 -1
- package/dist/ui/assets/{liquid-D7waQ0MY.js → liquid-Btnomc6E.js} +1 -1
- package/dist/ui/assets/{lua-C_kkt4h1.js → lua-D0FYT9Vo.js} +1 -1
- package/dist/ui/assets/{marko-BokXuCts.js → marko-MHLw0ZlK.js} +1 -1
- package/dist/ui/assets/{mdc-Bk0nnUJA.js → mdc-CSr-suGy.js} +1 -1
- package/dist/ui/assets/{nginx-jM6NF9TV.js → nginx-ur1l6E-g.js} +1 -1
- package/dist/ui/assets/{nim-hYsL-WKb.js → nim-B2k0IugA.js} +1 -1
- package/dist/ui/assets/{perl-DBI_cUHJ.js → perl-BqOHxFDn.js} +1 -1
- package/dist/ui/assets/{php-BX_aQhFl.js → php-DzPK6O1_.js} +1 -1
- package/dist/ui/assets/{pug-DwFAYy1e.js → pug-D169OZkr.js} +1 -1
- package/dist/ui/assets/{qml-eLSwQUKt.js → qml-BtemJTC4.js} +1 -1
- package/dist/ui/assets/{r-BMnQsMLf.js → r-BGVHu0bd.js} +1 -1
- package/dist/ui/assets/{razor-CgciiMyw.js → razor-Diisjg1d.js} +1 -1
- package/dist/ui/assets/{regexp-ZrURNrn5.js → regexp-B2F5vMZ6.js} +1 -1
- package/dist/ui/assets/{review-payload-HGkpJjjm.js → review-payload-B-3I3Oj_.js} +1 -1
- package/dist/ui/assets/{rst-EoAOr3M6.js → rst-BxpeZwuT.js} +1 -1
- package/dist/ui/assets/{ruby-Dqx5_Rqw.js → ruby-DrCq4riN.js} +1 -1
- package/dist/ui/assets/{sas-Bf6xPLSX.js → sas-CI_2W-1n.js} +1 -1
- package/dist/ui/assets/{scrollbar-DX4Ujotb.js → scrollbar-DHsDF5w-.js} +3 -3
- package/dist/ui/assets/{scss-DDpW3yTd.js → scss-BxtyHZ1p.js} +1 -1
- package/dist/ui/assets/{shellscript-DayAQ-W9.js → shellscript-BY_YYrHT.js} +1 -1
- package/dist/ui/assets/{shellsession-jPr_lSnA.js → shellsession-C6uBrLmA.js} +1 -1
- package/dist/ui/assets/{soy-X9adks8G.js → soy-BHM9gdPh.js} +1 -1
- package/dist/ui/assets/{sql-Bmt09AIR.js → sql-XmhfEGz_.js} +1 -1
- package/dist/ui/assets/{stata-DVkNpH8r.js → stata-BbHY7NEo.js} +1 -1
- package/dist/ui/assets/{surrealql-eEXzMb8j.js → surrealql-CD49DZ3E.js} +1 -1
- package/dist/ui/assets/{svelte-CFoBxZF3.js → svelte-CbYsEYJY.js} +1 -1
- package/dist/ui/assets/{templ-Da_324JF.js → templ-B5JoUxEB.js} +1 -1
- package/dist/ui/assets/{tex-HAzlmKJ0.js → tex-DZcOfTZK.js} +1 -1
- package/dist/ui/assets/{ts-tags-7iLrNH8p.js → ts-tags-BeU-xqc0.js} +1 -1
- package/dist/ui/assets/{tsx-gZaXf8PJ.js → tsx-V7W1xGl3.js} +1 -1
- package/dist/ui/assets/{twig-CLpQ7p1N.js → twig-MIaJo5BU.js} +1 -1
- package/dist/ui/assets/{typescript-B2WWCRI2.js → typescript-C0WESuVc.js} +1 -1
- package/dist/ui/assets/{vue-No_8Jygd.js → vue-eJHwc9ra.js} +1 -1
- package/dist/ui/assets/{vue-html-kHJg98Kk.js → vue-html-CLmBujTU.js} +1 -1
- package/dist/ui/assets/{vue-vine-Df4cEYaN.js → vue-vine-BP5kjIGb.js} +1 -1
- package/dist/ui/assets/{workspace-app-C_wsC6VL.js → workspace-app-C1qR5cAY.js} +3 -3
- package/dist/ui/assets/{xml-DUZ05dpG.js → xml-j7pmyP48.js} +1 -1
- package/dist/ui/assets/{xsl-Bwp3w2OF.js → xsl-5VFHbg6o.js} +1 -1
- package/dist/ui/assets/{yaml-IA_-TEM1.js → yaml-DxFAt-KU.js} +1 -1
- package/dist/ui/workspace-app.html +1 -1
- package/dist/workspaces.js +19 -6
- package/docs/chatgpt-coding-workflow.md +4 -1
- package/docs/configuration.md +16 -8
- package/docs/debugging.md +6 -5
- package/docs/security.md +18 -7
- package/package.json +2 -2
- package/scripts/debug/accept.mjs +65 -1
package/docs/security.md
CHANGED
|
@@ -17,8 +17,16 @@ Example:
|
|
|
17
17
|
Do not use your entire home directory unless that is intentionally the access
|
|
18
18
|
boundary you want.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
Opening a workspace still requires the path to be inside the configured allowed
|
|
21
|
+
roots. File and search tools additionally accept paths inside the operating
|
|
22
|
+
system temporary directory (for example `/tmp` on Linux) without treating that
|
|
23
|
+
directory as an allowed workspace root. Workspace paths remain confined to the
|
|
24
|
+
opened workspace, and shell working directories are not expanded by this temp
|
|
25
|
+
access.
|
|
26
|
+
|
|
27
|
+
Filesystem-oriented tools canonicalize existing path segments before access so
|
|
28
|
+
symlinks inside either the workspace or OS temp directory cannot escape to
|
|
29
|
+
arbitrary filesystem locations.
|
|
22
30
|
|
|
23
31
|
## Owner-password OAuth
|
|
24
32
|
|
|
@@ -137,15 +145,18 @@ as owner-only files. See [Native File Download](artifact-exchange.md).
|
|
|
137
145
|
|
|
138
146
|
## Logging
|
|
139
147
|
|
|
140
|
-
ForgeRelay
|
|
141
|
-
|
|
148
|
+
ForgeRelay's default human-facing `pretty` logs focus on tool and Hook
|
|
149
|
+
operations; HTTP request records are off by default. Pretty mode includes a
|
|
150
|
+
truncated shell command preview so local operators can see what the Agent ran.
|
|
151
|
+
Command arguments can contain secrets, so disable previews when necessary:
|
|
142
152
|
|
|
143
153
|
```bash
|
|
144
|
-
FORGERELAY_LOG_SHELL_COMMANDS=
|
|
154
|
+
FORGERELAY_LOG_SHELL_COMMANDS=0
|
|
145
155
|
```
|
|
146
156
|
|
|
147
|
-
|
|
148
|
-
|
|
157
|
+
Explicit `json` mode is intended for machine collection. Its default preserves
|
|
158
|
+
HTTP request records and omits shell command previews unless those settings are
|
|
159
|
+
overridden explicitly.
|
|
149
160
|
|
|
150
161
|
## Package provenance
|
|
151
162
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akira-tl/forgerelay",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Local development control plane for MCP coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Akira-TL/forgerelay#readme",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"debug:accept": "node scripts/debug/accept.mjs",
|
|
42
42
|
"postinstall": "node scripts/fix-node-pty-permissions.mjs",
|
|
43
43
|
"start": "node dist/cli.js serve",
|
|
44
|
-
"test": "tsx src/config.test.ts && tsx src/hooks.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts",
|
|
44
|
+
"test": "tsx src/config.test.ts && tsx src/logger.test.ts && tsx src/hooks.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/roots.test.ts && tsx src/file-mutations.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts",
|
|
45
45
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
46
46
|
"release:check": "node scripts/release-version.mjs check",
|
|
47
47
|
"release:tag-check": "node scripts/release-version.mjs tag",
|
package/scripts/debug/accept.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
rmSync,
|
|
11
11
|
writeFileSync,
|
|
12
12
|
} from "node:fs";
|
|
13
|
+
import { homedir, tmpdir } from "node:os";
|
|
13
14
|
import { join, resolve } from "node:path";
|
|
14
15
|
import { setTimeout as delay } from "node:timers/promises";
|
|
15
16
|
import {
|
|
@@ -30,6 +31,7 @@ const gitProject = resolve(acceptanceRoot, "git-project");
|
|
|
30
31
|
const releaseProject = resolve(acceptanceRoot, "release-project");
|
|
31
32
|
const releaseRemote = resolve(acceptanceRoot, "release-remote.git");
|
|
32
33
|
const ownerToken = randomBytes(32).toString("base64url");
|
|
34
|
+
const tempAcceptanceRoot = resolve(tmpdir(), `forgerelay-debug-acceptance-${randomUUID()}`);
|
|
33
35
|
|
|
34
36
|
assertCurlAvailable();
|
|
35
37
|
await assertDebugPortFree();
|
|
@@ -101,7 +103,7 @@ try {
|
|
|
101
103
|
params: {},
|
|
102
104
|
}).message.result.tools;
|
|
103
105
|
const toolNames = tools.map((tool) => tool.name);
|
|
104
|
-
for (const expected of ["open_workspace", "close_worktree", "read", "write", "edit", "grep", "glob", "ls", "bash"]) {
|
|
106
|
+
for (const expected of ["open_workspace", "close_worktree", "read", "write", "edit", "rename", "delete", "grep", "glob", "ls", "bash"]) {
|
|
105
107
|
assert.ok(toolNames.includes(expected), `missing debug tool ${expected}`);
|
|
106
108
|
}
|
|
107
109
|
pass("MCP tools/list", `${toolNames.length} tools: ${toolNames.join(", ")}`);
|
|
@@ -144,6 +146,67 @@ try {
|
|
|
144
146
|
assert.equal(failedEdit.isError, true);
|
|
145
147
|
pass("failed tool path", "edit returned isError=true and triggered AfterToolFailure");
|
|
146
148
|
|
|
149
|
+
const renamedWorkspaceFile = callTool(oauth.accessToken, sessionId, 74, "rename", {
|
|
150
|
+
workspaceId,
|
|
151
|
+
path: "acceptance.txt",
|
|
152
|
+
newPath: "renamed-acceptance.txt",
|
|
153
|
+
});
|
|
154
|
+
assert.equal(renamedWorkspaceFile.isError, undefined);
|
|
155
|
+
assert.equal(readFileSync(join(checkoutWorkspace, "renamed-acceptance.txt"), "utf8"), "forgerelay 7677 acceptance\n");
|
|
156
|
+
const deletedWorkspaceFile = callTool(oauth.accessToken, sessionId, 75, "delete", {
|
|
157
|
+
workspaceId,
|
|
158
|
+
path: "renamed-acceptance.txt",
|
|
159
|
+
});
|
|
160
|
+
assert.equal(deletedWorkspaceFile.isError, undefined);
|
|
161
|
+
assert.equal(existsSync(join(checkoutWorkspace, "renamed-acceptance.txt")), false);
|
|
162
|
+
pass("rename + delete", "workspace file renamed and deleted through MCP");
|
|
163
|
+
|
|
164
|
+
mkdirSync(tempAcceptanceRoot, { recursive: true });
|
|
165
|
+
const tempFile = join(tempAcceptanceRoot, "mcp-temp.txt");
|
|
166
|
+
const tempWritten = callTool(oauth.accessToken, sessionId, 70, "write", {
|
|
167
|
+
workspaceId,
|
|
168
|
+
path: tempFile,
|
|
169
|
+
content: "forgerelay temp before edit\n",
|
|
170
|
+
});
|
|
171
|
+
assert.equal(tempWritten.isError, undefined);
|
|
172
|
+
|
|
173
|
+
const tempRead = callTool(oauth.accessToken, sessionId, 71, "read", {
|
|
174
|
+
workspaceId,
|
|
175
|
+
path: tempFile,
|
|
176
|
+
});
|
|
177
|
+
assert.match(tempRead.structuredContent.result, /forgerelay temp before edit/);
|
|
178
|
+
|
|
179
|
+
const tempEdited = callTool(oauth.accessToken, sessionId, 72, "edit", {
|
|
180
|
+
workspaceId,
|
|
181
|
+
path: tempFile,
|
|
182
|
+
edits: [{ oldText: "before edit", newText: "after edit" }],
|
|
183
|
+
});
|
|
184
|
+
assert.equal(tempEdited.isError, undefined);
|
|
185
|
+
assert.equal(readFileSync(tempFile, "utf8"), "forgerelay temp after edit\n");
|
|
186
|
+
|
|
187
|
+
const renamedTempFile = join(tempAcceptanceRoot, "mcp-temp-renamed.txt");
|
|
188
|
+
const tempRenamed = callTool(oauth.accessToken, sessionId, 76, "rename", {
|
|
189
|
+
workspaceId,
|
|
190
|
+
path: tempFile,
|
|
191
|
+
newPath: renamedTempFile,
|
|
192
|
+
});
|
|
193
|
+
assert.equal(tempRenamed.isError, undefined);
|
|
194
|
+
assert.equal(readFileSync(renamedTempFile, "utf8"), "forgerelay temp after edit\n");
|
|
195
|
+
const tempDeleted = callTool(oauth.accessToken, sessionId, 77, "delete", {
|
|
196
|
+
workspaceId,
|
|
197
|
+
path: renamedTempFile,
|
|
198
|
+
});
|
|
199
|
+
assert.equal(tempDeleted.isError, undefined);
|
|
200
|
+
assert.equal(existsSync(renamedTempFile), false);
|
|
201
|
+
|
|
202
|
+
const outsideRoots = callTool(oauth.accessToken, sessionId, 73, "read", {
|
|
203
|
+
workspaceId,
|
|
204
|
+
path: join(homedir(), "forgerelay-debug-outside-roots.txt"),
|
|
205
|
+
});
|
|
206
|
+
assert.equal(outsideRoots.isError, true);
|
|
207
|
+
assert.match(toolText(outsideRoots), /outside allowed roots/i);
|
|
208
|
+
pass("OS temp file tools", "write + read + edit + rename + delete passed; arbitrary home path rejected");
|
|
209
|
+
|
|
147
210
|
setupGitProject(gitProject);
|
|
148
211
|
const worktreeOpened = callTool(oauth.accessToken, sessionId, 8, "open_workspace", {
|
|
149
212
|
path: gitProject,
|
|
@@ -211,6 +274,7 @@ try {
|
|
|
211
274
|
console.error("\nForgeRelay 7677 acceptance failed.");
|
|
212
275
|
throw error;
|
|
213
276
|
} finally {
|
|
277
|
+
rmSync(tempAcceptanceRoot, { recursive: true, force: true });
|
|
214
278
|
await stopServer(server);
|
|
215
279
|
}
|
|
216
280
|
|