@akira-tl/forgerelay 0.8.7 → 0.8.9
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 +18 -0
- package/README.md +10 -18
- package/capabilities/host-integration/GUIDE.md +2 -2
- package/dist/activity/{audit-store.js → history/audit-store.js} +129 -21
- package/dist/activity/history/bash-output-store.js +250 -0
- package/dist/activity/{host-turn-store.js → history/host-turn-store.js} +4 -1
- package/dist/activity/{query-service.js → history/query-service.js} +13 -7
- package/dist/activity/{lifecycle.js → runtime/lifecycle.js} +1 -1
- package/dist/activity/{mcp-query-tools.js → runtime/mcp-query-tools.js} +3 -3
- package/dist/activity/storage/history.js +12 -0
- package/dist/activity/storage/paths.js +56 -0
- package/dist/activity/storage/segmented-log.js +149 -0
- package/dist/cli/setup-support.js +116 -0
- package/dist/cli.js +18 -126
- package/dist/lsp/code-intelligence.js +1 -1
- package/dist/lsp/test-support/server-fixture.js +9 -9
- package/dist/mcp/filesystem/filesystem-tools.js +245 -0
- package/dist/mcp/hooks/command-runner.js +79 -0
- package/dist/{hook-cli.js → mcp/hooks/hook-cli.js} +1 -1
- package/dist/{hooks.js → mcp/hooks/hooks.js} +3 -80
- package/dist/{oauth-provider.js → mcp/oauth/oauth-provider.js} +1 -1
- package/dist/{oauth-store.js → mcp/oauth/oauth-store.js} +5 -4
- package/dist/{operations → mcp/operations}/native-bulk-mutations.js +2 -2
- package/dist/mcp/panel/app.js +143 -0
- package/dist/{mcp-app-template.js → mcp/panel/mcp-app-template.js} +2 -38
- package/dist/mcp/process/runtime.js +257 -0
- package/dist/mcp/process/tools.js +346 -0
- package/dist/mcp/server/core/activity-support.js +70 -0
- package/dist/{capabilities.js → mcp/server/core/capabilities.js} +5 -3
- package/dist/{capability-registry.js → mcp/server/core/capability-registry.js} +2 -2
- package/dist/mcp/server/core/capability-support.js +90 -0
- package/dist/mcp/server/core/schemas.js +216 -0
- package/dist/mcp/server/core/tool-support.js +207 -0
- package/dist/mcp/server/core/version.js +9 -0
- package/dist/mcp/server/operations/runtime/filesystem-tools.js +400 -0
- package/dist/mcp/server/operations/runtime/operation-runtime.js +523 -0
- package/dist/mcp/server/transport/http-server.js +301 -0
- package/dist/mcp/server/transport/http-support.js +43 -0
- package/dist/mcp/server/workspace/runtime/workspace-open-presentation.js +222 -0
- package/dist/mcp/server/workspace/runtime/workspace-open-schema.js +187 -0
- package/dist/mcp/server/workspace/runtime/workspace-open.js +552 -0
- package/dist/mcp/server/workspace/runtime/workspace-tools.js +601 -0
- package/dist/{config.js → runtime/config/config.js} +3 -3
- package/dist/{user-config.js → runtime/config/user-config.js} +14 -54
- package/dist/{db → runtime/state/db}/client.js +1 -1
- package/dist/{db → runtime/state/db}/migrations.js +34 -3
- package/dist/runtime/state/lock/file-lock.js +130 -0
- package/dist/runtime/testing/server-fixture.js +190 -0
- package/dist/server.js +139 -4192
- package/dist/subagents/providers/adapters/claude.js +86 -52
- package/dist/subagents/providers/adapters/codex.js +103 -39
- package/dist/subagents/providers/adapters/opencode.js +114 -84
- package/dist/subagents/providers/adapters/pi.js +59 -8
- package/dist/subagents/providers/availability.js +15 -30
- package/dist/subagents/providers/runtime/external-command.js +56 -0
- package/dist/subagents/sessions/capability.js +1 -1
- package/dist/subagents/sessions/execution.js +1 -1
- package/dist/subagents/sessions/mcp/activity-audit-test-support.js +30 -0
- package/dist/subagents/sessions/store.js +1 -1
- package/dist/ui/.vite/manifest.json +342 -409
- package/dist/ui/activity-panel-app.html +2 -4
- package/dist/ui/assets/activity-panel-app-7Xe0alhm.css +1 -0
- package/dist/ui/assets/activity-panel-app-Sl1Jro8v.js +115 -0
- package/dist/ui/assets/angular-html-B_yn91wW.js +1 -0
- package/dist/ui/assets/{angular-ts-Bxh4-O1-.js → angular-ts-DSgtloKz.js} +1 -1
- package/dist/ui/assets/{apl-BmYHKQ6m.js → apl-NatWtF8X.js} +1 -1
- package/dist/ui/assets/{astro-C51vqDKq.js → astro-BYFEVBuN.js} +1 -1
- package/dist/ui/assets/{blade-CdhVsmwq.js → blade-DoO7yvMR.js} +1 -1
- package/dist/ui/assets/c-Rl2hMCcg.js +1 -0
- package/dist/ui/assets/{cobol-Cf6azoN_.js → cobol-ByC5mWiB.js} +1 -1
- package/dist/ui/assets/{coffee-vVdGzlmt.js → coffee-Dez77Qov.js} +1 -1
- package/dist/ui/assets/cpp-DtaAQT9h.js +1 -0
- package/dist/ui/assets/{crystal-5UZy6KIX.js → crystal-Bgmbf8ec.js} +1 -1
- package/dist/ui/assets/css-C1tjBtVN.js +1 -0
- package/dist/ui/assets/{edge-CESgo4ia.js → edge-DeH6u8Wg.js} +1 -1
- package/dist/ui/assets/{elixir-9FKfw4cJ.js → elixir-_H3KnmWT.js} +1 -1
- package/dist/ui/assets/{elm-Dv7A0aLa.js → elm-CsCe3U28.js} +1 -1
- package/dist/ui/assets/{erb-DHFmDvM0.js → erb-CbM-L2g8.js} +1 -1
- package/dist/ui/assets/{git-rebase-utbU6CzV.js → git-rebase-D0s5pDh9.js} +1 -1
- package/dist/ui/assets/{glimmer-js-t2kiCkyB.js → glimmer-js-BfOToq_A.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-F0Jv5okV.js → glimmer-ts-CCUOkL2K.js} +1 -1
- package/dist/ui/assets/glsl-DH4ec9dE.js +1 -0
- package/dist/ui/assets/graphql-DilAMBoI.js +1 -0
- package/dist/ui/assets/{hack-8yjdbaSA.js → hack-BDkNDZlF.js} +1 -1
- package/dist/ui/assets/haml-D_QBp6K7.js +1 -0
- package/dist/ui/assets/{handlebars-BBFEg4Lo.js → handlebars-DePUrTpC.js} +1 -1
- package/dist/ui/assets/heavy-payload-DOwZpO6n.js +290 -0
- package/dist/ui/assets/html-By4dNnM4.js +1 -0
- package/dist/ui/assets/{html-derivative-DJAQcZyc.js → html-derivative-DDw9Ef4Q.js} +1 -1
- package/dist/ui/assets/{http-Dg5LSYRK.js → http-B0QxWc9M.js} +1 -1
- package/dist/ui/assets/{hurl-Y5uYzMZR.js → hurl-vMQ4lda1.js} +1 -1
- package/dist/ui/assets/java-DYRe2lWG.js +1 -0
- package/dist/ui/assets/javascript-CxyGqwcL.js +1 -0
- package/dist/ui/assets/{jinja-Bt30h8Ik.js → jinja-JUwV-fZt.js} +1 -1
- package/dist/ui/assets/{jison-D5uZ1Nyo.js → jison-7ZqCEvS8.js} +1 -1
- package/dist/ui/assets/json-dbltgiiA.js +1 -0
- package/dist/ui/assets/jsx-oDopT7YQ.js +1 -0
- package/dist/ui/assets/{julia-76hRaaGz.js → julia-CkKiNSav.js} +1 -1
- package/dist/ui/assets/{just-CDE7W0vW.js → just-DuqCAKJ3.js} +1 -1
- package/dist/ui/assets/{latex-CVO2Byab.js → latex-sudcRDSn.js} +1 -1
- package/dist/ui/assets/{liquid-CA-AS3EX.js → liquid-Di-08Js7.js} +1 -1
- package/dist/ui/assets/lua-YlqdMoV0.js +1 -0
- package/dist/ui/assets/{marko-_BWrPfny.js → marko-CJRNyJDb.js} +1 -1
- package/dist/ui/assets/{mdc-PyJv85eS.js → mdc-t12CzZHs.js} +1 -1
- package/dist/ui/assets/{nginx-DAWVw8yu.js → nginx-57-b4XwT.js} +1 -1
- package/dist/ui/assets/{nim-CKE1dzpU.js → nim-BzrswYQa.js} +1 -1
- package/dist/ui/assets/{perl-BBhWva19.js → perl-D9JR_iIh.js} +1 -1
- package/dist/ui/assets/{php-ChIeaAoF.js → php-Py2gFHil.js} +1 -1
- package/dist/ui/assets/{pug-CMgI5afi.js → pug-PzLLy0h1.js} +1 -1
- package/dist/ui/assets/{qml-CjxTkcFH.js → qml-By8fwgV4.js} +1 -1
- package/dist/ui/assets/r-CcezTbLq.js +1 -0
- package/dist/ui/assets/{razor-i4DbHr8p.js → razor-dG-vdhL_.js} +1 -1
- package/dist/ui/assets/regexp-BlsQ-N_8.js +1 -0
- package/dist/ui/assets/{rst-oy_97Kl4.js → rst-C2awDCat.js} +1 -1
- package/dist/ui/assets/{ruby-C5zYZIyl.js → ruby-zA2W_49d.js} +1 -1
- package/dist/ui/assets/{sas-BnMEh2S3.js → sas-D49bhyaN.js} +1 -1
- package/dist/ui/assets/scss-BQb-cp01.js +1 -0
- package/dist/ui/assets/shellscript-Dga4H_Ni.js +1 -0
- package/dist/ui/assets/{shellsession-BpE5o7_3.js → shellsession-n3-5CqcF.js} +1 -1
- package/dist/ui/assets/{soy-COesmhTY.js → soy-XYBOf_BA.js} +1 -1
- package/dist/ui/assets/sql-ET4u5Cpl.js +1 -0
- package/dist/ui/assets/{stata-CGQEuSZq.js → stata-CLHcJHO_.js} +1 -1
- package/dist/ui/assets/{surrealql-Bz4JbU-9.js → surrealql-BdjQ60T4.js} +1 -1
- package/dist/ui/assets/{svelte-1jM2ejXj.js → svelte-B2RPtKJe.js} +1 -1
- package/dist/ui/assets/{templ-C-VwELcp.js → templ-CrByPW_W.js} +1 -1
- package/dist/ui/assets/{tex-t1rUiQCm.js → tex-DZBkg363.js} +1 -1
- package/dist/ui/assets/{ts-tags-Bewoa6pS.js → ts-tags-DJR3Gphb.js} +1 -1
- package/dist/ui/assets/tsx-CajzROyy.js +1 -0
- package/dist/ui/assets/{twig-DVg-vELl.js → twig-BZjk5Zda.js} +1 -1
- package/dist/ui/assets/typescript-B-EWToLj.js +1 -0
- package/dist/ui/assets/{vue-wPQ7UlMy.js → vue-D_rHCCr8.js} +1 -1
- package/dist/ui/assets/{vue-html-BHtJ8geX.js → vue-html-CC4WlvVt.js} +1 -1
- package/dist/ui/assets/{vue-vine-CNSGAWwc.js → vue-vine-BgNMVgpk.js} +1 -1
- package/dist/ui/assets/xml-BI86-WbH.js +1 -0
- package/dist/ui/assets/{xsl-3_vJ27aE.js → xsl-DXeLZV4J.js} +1 -1
- package/dist/ui/assets/yaml-60_PFlWa.js +1 -0
- package/dist/workspaces/bootstrap.js +72 -0
- package/dist/workspaces/context.js +302 -0
- package/dist/workspaces/conversation-test-support.js +86 -0
- package/dist/{git-worktrees.js → workspaces/git/git-worktrees.js} +1 -1
- package/dist/workspaces/inventory.js +131 -0
- package/dist/workspaces/paths.js +55 -0
- package/dist/{remote-auth.js → workspaces/relay/auth/remote-auth.js} +2 -2
- package/dist/workspaces/relay/result-support.js +111 -0
- package/dist/workspaces/relay/tests/test-support.js +203 -0
- package/dist/{remote-mcp-connection-pool.js → workspaces/relay/transport/remote-mcp-connection-pool.js} +1 -1
- package/dist/workspaces/relay/types.js +1 -0
- package/dist/{remote-workspace-relay.js → workspaces/relay/workspace-relay.js} +23 -172
- package/dist/workspaces/resources/skills.js +285 -0
- package/dist/{review-checkpoints.js → workspaces/review/review-checkpoints.js} +2 -2
- package/dist/workspaces/sessions.js +457 -0
- package/dist/{workspace-store.js → workspaces/state/workspace-store.js} +2 -2
- package/dist/workspaces.js +69 -991
- package/docs/chatgpt-coding-workflow.md +3 -11
- package/docs/configuration.md +12 -25
- package/docs/gotchas.md +0 -30
- package/docs/roadmap.md +6 -10
- package/docs/security.md +1 -5
- package/docs/setup.md +0 -11
- package/package.json +6 -8
- package/scripts/ci/architecture.mjs +109 -0
- package/scripts/ci/verify.mjs +5 -0
- package/scripts/debug/accept/bootstrap.mjs +326 -0
- package/scripts/debug/accept/harness.mjs +76 -0
- package/scripts/debug/accept/support.mjs +432 -0
- package/scripts/debug/accept.mjs +43 -806
- package/scripts/debug/relay-accept/support.mjs +135 -0
- package/scripts/debug/relay-accept.mjs +26 -115
- package/scripts/debug/traffic/support.mjs +167 -0
- package/scripts/debug/traffic/traffic-audit.mjs +4 -161
- package/scripts/{lsp-interop-support.test.mjs → lsp/interop-support.test.mjs} +1 -1
- package/scripts/{lsp-interop.mjs → lsp/interop.mjs} +3 -3
- package/scripts/wiki/sync.mjs +21 -10
- package/dist/activity/bash-output-store.js +0 -127
- package/dist/pi-tools.js +0 -110
- package/dist/skills.js +0 -127
- package/dist/ui/assets/activity-panel-app-CUAN6zyW.js +0 -2
- package/dist/ui/assets/angular-html-BkgDQl-G.js +0 -1
- package/dist/ui/assets/c-IBzBpxAO.js +0 -1
- package/dist/ui/assets/chunk-EyZ2wyi3.js +0 -1
- package/dist/ui/assets/cpp-CVHeO-Jk.js +0 -1
- package/dist/ui/assets/css-DEfus9J5.js +0 -1
- package/dist/ui/assets/glsl-_ooCjaeB.js +0 -1
- package/dist/ui/assets/graphql-B9LqP-cY.js +0 -1
- package/dist/ui/assets/haml-BU5EoZBn.js +0 -1
- package/dist/ui/assets/heavy-payload-CgzrutLm.js +0 -4
- package/dist/ui/assets/html-D-C6YZk4.js +0 -1
- package/dist/ui/assets/java-ppHvycj5.js +0 -1
- package/dist/ui/assets/javascript-DDTjKLlQ.js +0 -1
- package/dist/ui/assets/json-Cw91r8h1.js +0 -1
- package/dist/ui/assets/jsx-04X2FT_1.js +0 -1
- package/dist/ui/assets/lua-CiyBp52Y.js +0 -1
- package/dist/ui/assets/r-DfFga4si.js +0 -1
- package/dist/ui/assets/regexp-iC9_nbSI.js +0 -1
- package/dist/ui/assets/review-payload-BrLbezbq.js +0 -1
- package/dist/ui/assets/scrollbar-CbhpdW05.js +0 -287
- package/dist/ui/assets/scss-BfZXsAWQ.js +0 -1
- package/dist/ui/assets/shellscript-BC2srNaB.js +0 -1
- package/dist/ui/assets/sql-Ccv_yeUm.js +0 -1
- package/dist/ui/assets/tsx-CTXzrBkN.js +0 -1
- package/dist/ui/assets/typescript--ElHoFkw.js +0 -1
- package/dist/ui/assets/workspace-app-Bhj96tsR.js +0 -1
- package/dist/ui/assets/workspace-app-CxwJuZyS.js +0 -111
- package/dist/ui/assets/workspace-app-D6UR0AFl.js +0 -5
- package/dist/ui/assets/workspace-app-ldjBmCJR.css +0 -1
- package/dist/ui/assets/workspace-lifecycle-app-Cqfhx9pV.js +0 -1
- package/dist/ui/assets/xml-JnmX6vyS.js +0 -1
- package/dist/ui/assets/yaml-OAcZkP-w.js +0 -1
- package/dist/ui/workspace-app.html +0 -18
- package/dist/ui/workspace-lifecycle-app.html +0 -18
- package/docs/agent-profile-schema.md +0 -114
- package/docs/artifact-exchange.md +0 -65
- package/docs/versioning.md +0 -205
- /package/capabilities/{managed-worktrees → workspace/managed-worktrees}/GUIDE.md +0 -0
- /package/capabilities/{workspace-tasks → workspace/workspace-tasks}/GUIDE.md +0 -0
- /package/dist/activity/{process-output-audit.js → runtime/process-output-audit.js} +0 -0
- /package/dist/{artifact-error.js → mcp/artifacts/artifact-error.js} +0 -0
- /package/dist/{artifact-tools.js → mcp/artifacts/artifact-tools.js} +0 -0
- /package/dist/{incoming-artifacts.js → mcp/artifacts/incoming-artifacts.js} +0 -0
- /package/dist/{advertised-files.js → mcp/filesystem/advertised-files.js} +0 -0
- /package/dist/{apply-patch.js → mcp/filesystem/apply-patch.js} +0 -0
- /package/dist/{file-mutations.js → mcp/filesystem/file-mutations.js} +0 -0
- /package/dist/{roots.js → mcp/filesystem/roots.js} +0 -0
- /package/dist/{oauth → mcp/oauth}/public-url.js +0 -0
- /package/dist/{oauth → mcp/oauth}/router.js +0 -0
- /package/dist/{operations → mcp/operations}/batch/executor.js +0 -0
- /package/dist/{operations → mcp/operations}/batch/scheduler.js +0 -0
- /package/dist/{operations → mcp/operations}/batch/types.js +0 -0
- /package/dist/{operations → mcp/operations}/bulk-mutation.js +0 -0
- /package/dist/{operations → mcp/operations}/bulk-read.js +0 -0
- /package/dist/{operations → mcp/operations}/core-operation-executor.js +0 -0
- /package/dist/{process-platform.js → mcp/process/process-platform.js} +0 -0
- /package/dist/{process-sessions.js → mcp/process/process-sessions.js} +0 -0
- /package/dist/{request-meta.js → mcp/request-meta.js} +0 -0
- /package/dist/{mcp-sessions.js → mcp/server/transport/mcp-sessions.js} +0 -0
- /package/dist/{server-shutdown.js → mcp/server/transport/server-shutdown.js} +0 -0
- /package/dist/{logger.js → runtime/logging/logger.js} +0 -0
- /package/dist/{db → runtime/state/db}/schema.js +0 -0
- /package/dist/{composite-activity.js → workspaces/composite/composite-activity.js} +0 -0
- /package/dist/{composite-workspaces.js → workspaces/composite/composite-workspaces.js} +0 -0
- /package/dist/{git.js → workspaces/git/git.js} +0 -0
- /package/dist/{workspace-presentation.js → workspaces/presentation/workspace-presentation.js} +0 -0
- /package/dist/{remote-transport.js → workspaces/relay/transport/remote-transport.js} +0 -0
- /package/dist/{workspace-task-reminders.js → workspaces/tasks/workspace-task-reminders.js} +0 -0
- /package/dist/{workspace-tasks.js → workspaces/tasks/workspace-tasks.js} +0 -0
- /package/scripts/{lsp-interop-support.mjs → lsp/interop-support.mjs} +0 -0
|
@@ -14,10 +14,12 @@ import { join, resolve } from "node:path";
|
|
|
14
14
|
import { setTimeout as delay } from "node:timers/promises";
|
|
15
15
|
import { activityRefreshDelayMs } from "../../../src/ui/activity/model.ts";
|
|
16
16
|
import { debugRoot, repoRoot } from "../runtime.mjs";
|
|
17
|
+
import { jsonRequest, curlRequest, jsonLogEntries, requestLogEntries, requestBodiesForRpc, countBy, waitForHealth, stopServer, assertPortsFree, jsonBytes, formatBytes } from "./support.mjs";
|
|
18
|
+
|
|
17
19
|
|
|
18
20
|
const mode = process.argv[2] ?? "all";
|
|
19
21
|
if (!["all", "local", "relay"].includes(mode)) {
|
|
20
|
-
throw new Error("Usage:
|
|
22
|
+
throw new Error("Usage: npm run traffic:audit -- [all|local|relay]");
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const trafficRoot = resolve(debugRoot, "traffic-audit");
|
|
@@ -637,7 +639,7 @@ function authorizeHost(baseUrl, mcpUrl, ownerToken) {
|
|
|
637
639
|
redirect_uri: redirectUri,
|
|
638
640
|
code_challenge: challenge,
|
|
639
641
|
code_challenge_method: "S256",
|
|
640
|
-
scope: "
|
|
642
|
+
scope: "forgerelay",
|
|
641
643
|
resource: mcpUrl,
|
|
642
644
|
state: "traffic-audit",
|
|
643
645
|
owner_token: ownerToken,
|
|
@@ -746,162 +748,3 @@ function parseMcpMessage(body, expectedId) {
|
|
|
746
748
|
return messages.find((message) => message.id === expectedId) ?? messages[0];
|
|
747
749
|
}
|
|
748
750
|
|
|
749
|
-
function jsonRequest(url, options = {}) {
|
|
750
|
-
const response = curlRequest({
|
|
751
|
-
method: options.method ?? "GET",
|
|
752
|
-
url,
|
|
753
|
-
headers: options.headers,
|
|
754
|
-
body: options.body,
|
|
755
|
-
});
|
|
756
|
-
return { ...response, json: JSON.parse(response.body) };
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
function curlRequest({ method = "GET", url, headers = {}, body }) {
|
|
760
|
-
const marker = `__FORGERELAY_TRAFFIC_${randomUUID()}__`;
|
|
761
|
-
const args = [
|
|
762
|
-
"--silent",
|
|
763
|
-
"--show-error",
|
|
764
|
-
"--max-time",
|
|
765
|
-
"20",
|
|
766
|
-
"--request",
|
|
767
|
-
method,
|
|
768
|
-
"--dump-header",
|
|
769
|
-
"-",
|
|
770
|
-
"--output",
|
|
771
|
-
"-",
|
|
772
|
-
"--write-out",
|
|
773
|
-
`\n${marker}%{http_code}|%{size_upload}|%{size_download}|%{size_request}|%{size_header}`,
|
|
774
|
-
];
|
|
775
|
-
for (const [name, value] of Object.entries(headers)) args.push("--header", `${name}: ${value}`);
|
|
776
|
-
if (body !== undefined) args.push("--data-binary", "@-");
|
|
777
|
-
args.push(url);
|
|
778
|
-
|
|
779
|
-
const result = spawnSync("curl", args, {
|
|
780
|
-
cwd: repoRoot,
|
|
781
|
-
input: body,
|
|
782
|
-
encoding: "utf8",
|
|
783
|
-
maxBuffer: 40 * 1024 * 1024,
|
|
784
|
-
});
|
|
785
|
-
if (result.status !== 0) {
|
|
786
|
-
throw new Error(`curl ${method} ${url} failed: ${result.stderr.trim() || `exit ${result.status}`}`);
|
|
787
|
-
}
|
|
788
|
-
const statusMarker = `\n${marker}`;
|
|
789
|
-
const markerIndex = result.stdout.lastIndexOf(statusMarker);
|
|
790
|
-
assert.notEqual(markerIndex, -1, `curl response did not contain status marker for ${url}`);
|
|
791
|
-
const rawResponse = result.stdout.slice(0, markerIndex);
|
|
792
|
-
const stats = result.stdout.slice(markerIndex + statusMarker.length).trim().split("|");
|
|
793
|
-
const [status, sizeUpload, sizeDownload, sizeRequest, sizeHeader] = stats.map(Number);
|
|
794
|
-
const separator = rawResponse.indexOf("\r\n\r\n") >= 0 ? "\r\n\r\n" : "\n\n";
|
|
795
|
-
const headerEnd = rawResponse.indexOf(separator);
|
|
796
|
-
assert.notEqual(headerEnd, -1, `curl response did not contain headers for ${url}`);
|
|
797
|
-
const headerBlock = rawResponse.slice(0, headerEnd);
|
|
798
|
-
const responseBody = rawResponse.slice(headerEnd + separator.length);
|
|
799
|
-
const responseHeaders = new Map();
|
|
800
|
-
for (const line of headerBlock.split(/\r?\n/).slice(1)) {
|
|
801
|
-
const colon = line.indexOf(":");
|
|
802
|
-
if (colon < 0) continue;
|
|
803
|
-
responseHeaders.set(line.slice(0, colon).trim().toLowerCase(), line.slice(colon + 1).trim());
|
|
804
|
-
}
|
|
805
|
-
return {
|
|
806
|
-
status,
|
|
807
|
-
headers: responseHeaders,
|
|
808
|
-
body: responseBody,
|
|
809
|
-
sizeUpload,
|
|
810
|
-
sizeDownload,
|
|
811
|
-
sizeRequest,
|
|
812
|
-
sizeHeader,
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
function jsonLogEntries(path) {
|
|
817
|
-
if (!existsSync(path)) return [];
|
|
818
|
-
return readFileSync(path, "utf8")
|
|
819
|
-
.split(/\r?\n/)
|
|
820
|
-
.filter(Boolean)
|
|
821
|
-
.flatMap((line) => {
|
|
822
|
-
try {
|
|
823
|
-
return [JSON.parse(line)];
|
|
824
|
-
} catch {
|
|
825
|
-
return [];
|
|
826
|
-
}
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
function requestLogEntries(path) {
|
|
831
|
-
return jsonLogEntries(path).filter((entry) => entry.event === "http_request");
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
function requestBodiesForRpc(entries, rpcTarget) {
|
|
835
|
-
const requestIds = new Set(entries
|
|
836
|
-
.filter((entry) => entry.event === "mcp_request" && entry.rpcTarget === rpcTarget)
|
|
837
|
-
.map((entry) => entry.requestId)
|
|
838
|
-
.filter(Boolean));
|
|
839
|
-
return entries
|
|
840
|
-
.filter((entry) => entry.event === "http_request" && requestIds.has(entry.requestId))
|
|
841
|
-
.map((entry) => Number(entry.contentLength ?? 0))
|
|
842
|
-
.filter((value) => Number.isFinite(value));
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
function countBy(values, keyFor) {
|
|
846
|
-
const result = {};
|
|
847
|
-
for (const value of values) {
|
|
848
|
-
const key = keyFor(value);
|
|
849
|
-
result[key] = (result[key] ?? 0) + 1;
|
|
850
|
-
}
|
|
851
|
-
return result;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
async function waitForHealth(child, baseUrl) {
|
|
855
|
-
for (let attempt = 0; attempt < 80; attempt += 1) {
|
|
856
|
-
if (child.exitCode !== null) throw new Error(`debug server exited before health check: ${child.exitCode}`);
|
|
857
|
-
try {
|
|
858
|
-
const response = jsonRequest(`${baseUrl}/healthz`);
|
|
859
|
-
if (response.status === 200) return;
|
|
860
|
-
} catch {
|
|
861
|
-
// still starting
|
|
862
|
-
}
|
|
863
|
-
await delay(100);
|
|
864
|
-
}
|
|
865
|
-
throw new Error(`debug server did not become healthy on ${baseUrl}`);
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
async function stopServer(child) {
|
|
869
|
-
if (child.exitCode !== null) return;
|
|
870
|
-
child.kill("SIGTERM");
|
|
871
|
-
const exited = once(child, "exit");
|
|
872
|
-
await Promise.race([
|
|
873
|
-
exited,
|
|
874
|
-
delay(3000).then(() => {
|
|
875
|
-
if (child.exitCode === null) child.kill("SIGKILL");
|
|
876
|
-
}),
|
|
877
|
-
]);
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
async function assertPortsFree(ports) {
|
|
881
|
-
const script = [
|
|
882
|
-
"const net=require('node:net');",
|
|
883
|
-
`const ports=${JSON.stringify(ports)};`,
|
|
884
|
-
"let pending=ports.length; let bad=[];",
|
|
885
|
-
"for(const port of ports){const s=net.connect({host:'127.0.0.1',port}); s.setTimeout(250);",
|
|
886
|
-
"s.once('connect',()=>{bad.push(port);s.destroy();done();});",
|
|
887
|
-
"s.once('error',()=>{s.destroy();done();}); s.once('timeout',()=>{s.destroy();done();});}",
|
|
888
|
-
"function done(){if(--pending===0){if(bad.length){console.error('busy:'+bad.join(','));process.exit(2)}}}",
|
|
889
|
-
].join("");
|
|
890
|
-
const result = spawnSync(process.execPath, ["-e", script], { encoding: "utf8" });
|
|
891
|
-
if (result.status !== 0) throw new Error(`reserved debug port already in use: ${result.stderr.trim()}`);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
function jsonBytes(value) {
|
|
895
|
-
if (value === undefined) return 0;
|
|
896
|
-
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
function formatBytes(value) {
|
|
900
|
-
const bytes = Number(value);
|
|
901
|
-
if (!Number.isFinite(bytes)) return String(value);
|
|
902
|
-
if (bytes < 1024) return `${bytes.toFixed(0)} B`;
|
|
903
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
904
|
-
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(2)} MiB`;
|
|
905
|
-
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
|
|
906
|
-
}
|
|
907
|
-
|
|
@@ -3,7 +3,7 @@ import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { delimiter, join } from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
|
-
import { findExecutable, probeExecutable } from "./
|
|
6
|
+
import { findExecutable, probeExecutable } from "./interop-support.mjs";
|
|
7
7
|
|
|
8
8
|
const root = mkdtempSync(join(tmpdir(), "forgerelay-lsp-preflight-test-"));
|
|
9
9
|
|
|
@@ -2,10 +2,10 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { CodeIntelligenceManager } from "
|
|
6
|
-
import { findExecutable, probeExecutable } from "./
|
|
5
|
+
import { CodeIntelligenceManager } from "../../dist/lsp/runtime/manager.js";
|
|
6
|
+
import { findExecutable, probeExecutable } from "./interop-support.mjs";
|
|
7
7
|
|
|
8
|
-
const repoRoot = resolve(fileURLToPath(new URL("
|
|
8
|
+
const repoRoot = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
|
9
9
|
const interopRoot = resolve(repoRoot, ".forgerelay-debug", "lsp-interop");
|
|
10
10
|
const fixtures = [
|
|
11
11
|
{
|
package/scripts/wiki/sync.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
copyFileSync,
|
|
5
5
|
existsSync,
|
|
6
6
|
mkdtempSync,
|
|
7
7
|
readdirSync,
|
|
@@ -49,7 +49,13 @@ function validateWikiSource(directory) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const files = listFiles(directory);
|
|
52
|
-
const
|
|
52
|
+
const destinationNames = files.map((file) => wikiDestinationName(file.relativePath));
|
|
53
|
+
const names = new Set(destinationNames);
|
|
54
|
+
if (names.size !== destinationNames.length) {
|
|
55
|
+
const duplicates = destinationNames.filter((name, index) => destinationNames.indexOf(name) !== index);
|
|
56
|
+
fail(`Wiki source contains duplicate flattened page names:
|
|
57
|
+
${[...new Set(duplicates)].map((name) => `- ${name}`).join("\n")}`);
|
|
58
|
+
}
|
|
53
59
|
|
|
54
60
|
for (const required of ["Home.md", "_Sidebar.md"]) {
|
|
55
61
|
if (!names.has(required)) {
|
|
@@ -59,7 +65,7 @@ function validateWikiSource(directory) {
|
|
|
59
65
|
|
|
60
66
|
const invalidNames = files
|
|
61
67
|
.map((file) => file.relativePath)
|
|
62
|
-
.filter((name) => !
|
|
68
|
+
.filter((name) => !isSafeWikiSourcePath(name));
|
|
63
69
|
if (invalidNames.length > 0) {
|
|
64
70
|
fail(`Wiki source contains unsupported paths:\n${invalidNames.map((name) => `- ${name}`).join("\n")}`);
|
|
65
71
|
}
|
|
@@ -67,7 +73,7 @@ function validateWikiSource(directory) {
|
|
|
67
73
|
const markdownNames = new Set(
|
|
68
74
|
files
|
|
69
75
|
.filter((file) => file.relativePath.endsWith(".md"))
|
|
70
|
-
.map((file) => file.relativePath),
|
|
76
|
+
.map((file) => wikiDestinationName(file.relativePath)),
|
|
71
77
|
);
|
|
72
78
|
const brokenLinks = [];
|
|
73
79
|
|
|
@@ -86,7 +92,7 @@ function validateWikiSource(directory) {
|
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
const candidate = target.endsWith(".md") ? target : `${target}.md`;
|
|
89
|
-
if (!markdownNames.has(candidate)) {
|
|
95
|
+
if (!markdownNames.has(wikiDestinationName(candidate))) {
|
|
90
96
|
brokenLinks.push(`${file.relativePath}: ${rawTarget}`);
|
|
91
97
|
}
|
|
92
98
|
}
|
|
@@ -169,8 +175,8 @@ function mirrorSource(source, destination) {
|
|
|
169
175
|
rmSync(join(destination, entry), { recursive: true, force: true });
|
|
170
176
|
}
|
|
171
177
|
|
|
172
|
-
for (const
|
|
173
|
-
|
|
178
|
+
for (const file of listFiles(source)) {
|
|
179
|
+
copyFileSync(file.absolutePath, join(destination, wikiDestinationName(file.relativePath)));
|
|
174
180
|
}
|
|
175
181
|
}
|
|
176
182
|
|
|
@@ -191,10 +197,15 @@ function listFiles(directory, prefix = "") {
|
|
|
191
197
|
return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
192
198
|
}
|
|
193
199
|
|
|
194
|
-
function
|
|
200
|
+
function wikiDestinationName(relativePath) {
|
|
201
|
+
return basename(relativePath);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function isSafeWikiSourcePath(relativePath) {
|
|
195
205
|
if (!relativePath.endsWith(".md")) return false;
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
return relativePath
|
|
207
|
+
.split("/")
|
|
208
|
+
.every((segment) => segment.length > 0 && !/[\\:*?"<>|]/.test(segment));
|
|
198
209
|
}
|
|
199
210
|
|
|
200
211
|
function isExternalOrAnchorLink(target) {
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { StringDecoder } from "node:string_decoder";
|
|
3
|
-
import { openDatabase } from "../db/client.js";
|
|
4
|
-
export class BashOutputStore {
|
|
5
|
-
database;
|
|
6
|
-
now;
|
|
7
|
-
nextOutputId;
|
|
8
|
-
nextSequences = new Map();
|
|
9
|
-
constructor(stateDir, options = {}) {
|
|
10
|
-
this.database = openDatabase(stateDir);
|
|
11
|
-
this.now = options.now ?? (() => new Date());
|
|
12
|
-
this.nextOutputId = options.outputId ?? (() => `out_${randomUUID().replaceAll("-", "")}`);
|
|
13
|
-
}
|
|
14
|
-
begin(input) {
|
|
15
|
-
const outputId = this.nextOutputId();
|
|
16
|
-
const startedAt = this.now().toISOString();
|
|
17
|
-
this.database.sqlite.prepare(`insert into bash_output_streams (
|
|
18
|
-
id, activity_id, turn_id, conversation_scope_id, process_id,
|
|
19
|
-
workspace_id, workspace_root, command, tty, status, timed_out, started_at
|
|
20
|
-
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', 0, ?)`).run(outputId, input.activityId, input.turnId, input.conversationScopeId ?? null, input.processId, input.workspaceId, input.workspaceRoot, input.command, input.tty ? 1 : 0, startedAt);
|
|
21
|
-
this.nextSequences.set(outputId, 1);
|
|
22
|
-
return outputId;
|
|
23
|
-
}
|
|
24
|
-
append(outputId, channel, data) {
|
|
25
|
-
const bytes = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
|
|
26
|
-
if (bytes.length === 0)
|
|
27
|
-
return;
|
|
28
|
-
const sequence = this.nextSequence(outputId);
|
|
29
|
-
this.database.sqlite.prepare(`insert into bash_output_chunks (output_id, sequence, channel, data, created_at)
|
|
30
|
-
values (?, ?, ?, ?, ?)`).run(outputId, sequence, channel, bytes, this.now().toISOString());
|
|
31
|
-
}
|
|
32
|
-
markReturned(outputId) {
|
|
33
|
-
this.database.sqlite.prepare("update bash_output_streams set returned = 1 where id = ?").run(outputId);
|
|
34
|
-
}
|
|
35
|
-
claimCompletion(outputId) {
|
|
36
|
-
const claimedAt = this.now().toISOString();
|
|
37
|
-
const claimed = this.database.sqlite.prepare(`update bash_output_streams
|
|
38
|
-
set completion_claimed_at = ?
|
|
39
|
-
where id = ? and returned = 1 and status != 'running' and completion_claimed_at is null`).run(claimedAt, outputId);
|
|
40
|
-
return claimed.changes === 1 ? this.read(outputId) : undefined;
|
|
41
|
-
}
|
|
42
|
-
finish(outputId, input) {
|
|
43
|
-
const status = input.error || input.timedOut || input.signal || (input.exitCode !== undefined && input.exitCode !== 0)
|
|
44
|
-
? "failed"
|
|
45
|
-
: "done";
|
|
46
|
-
this.database.sqlite.prepare(`update bash_output_streams
|
|
47
|
-
set status = ?, exit_code = ?, signal = ?, timed_out = ?, error = ?, finished_at = ?
|
|
48
|
-
where id = ?`).run(status, input.exitCode ?? null, input.signal ?? null, input.timedOut ? 1 : 0, input.error ?? null, this.now().toISOString(), outputId);
|
|
49
|
-
}
|
|
50
|
-
read(outputId) {
|
|
51
|
-
const stream = this.database.sqlite.prepare("select * from bash_output_streams where id = ?").get(outputId);
|
|
52
|
-
if (!stream)
|
|
53
|
-
return undefined;
|
|
54
|
-
const rows = this.database.sqlite.prepare(`select * from bash_output_chunks
|
|
55
|
-
where output_id = ?
|
|
56
|
-
order by sequence asc`).all(outputId);
|
|
57
|
-
const decoded = decodeChunks(rows);
|
|
58
|
-
return {
|
|
59
|
-
outputId: stream.id,
|
|
60
|
-
activityId: stream.activity_id,
|
|
61
|
-
turnId: stream.turn_id,
|
|
62
|
-
...(stream.conversation_scope_id ? { conversationScopeId: stream.conversation_scope_id } : {}),
|
|
63
|
-
processId: stream.process_id,
|
|
64
|
-
workspaceId: stream.workspace_id,
|
|
65
|
-
workspaceRoot: stream.workspace_root,
|
|
66
|
-
command: stream.command,
|
|
67
|
-
tty: stream.tty === 1,
|
|
68
|
-
output: decoded.map((chunk) => chunk.data).join(""),
|
|
69
|
-
chunks: decoded,
|
|
70
|
-
status: isBashOutputStatus(stream.status) ? stream.status : "failed",
|
|
71
|
-
...(stream.exit_code !== null ? { exitCode: stream.exit_code } : {}),
|
|
72
|
-
...(stream.signal ? { signal: stream.signal } : {}),
|
|
73
|
-
timedOut: stream.timed_out === 1,
|
|
74
|
-
...(stream.error ? { error: stream.error } : {}),
|
|
75
|
-
returned: stream.returned === 1,
|
|
76
|
-
startedAt: stream.started_at,
|
|
77
|
-
...(stream.finished_at ? { finishedAt: stream.finished_at } : {}),
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
close() {
|
|
81
|
-
this.database.close();
|
|
82
|
-
}
|
|
83
|
-
nextSequence(outputId) {
|
|
84
|
-
const known = this.nextSequences.get(outputId);
|
|
85
|
-
if (known !== undefined) {
|
|
86
|
-
this.nextSequences.set(outputId, known + 1);
|
|
87
|
-
return known;
|
|
88
|
-
}
|
|
89
|
-
const row = this.database.sqlite.prepare("select coalesce(max(sequence), 0) as sequence from bash_output_chunks where output_id = ?").get(outputId);
|
|
90
|
-
const sequence = row.sequence + 1;
|
|
91
|
-
this.nextSequences.set(outputId, sequence + 1);
|
|
92
|
-
return sequence;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function decodeChunks(rows) {
|
|
96
|
-
const decoders = new Map();
|
|
97
|
-
const chunks = [];
|
|
98
|
-
const lastChunkIndex = new Map();
|
|
99
|
-
for (const row of rows) {
|
|
100
|
-
if (!isBashOutputChannel(row.channel)) {
|
|
101
|
-
throw new Error(`Unknown Bash output channel: ${row.channel}`);
|
|
102
|
-
}
|
|
103
|
-
const decoder = decoders.get(row.channel) ?? new StringDecoder("utf8");
|
|
104
|
-
decoders.set(row.channel, decoder);
|
|
105
|
-
const data = decoder.write(row.data);
|
|
106
|
-
chunks.push({ sequence: row.sequence, channel: row.channel, data });
|
|
107
|
-
lastChunkIndex.set(row.channel, chunks.length - 1);
|
|
108
|
-
}
|
|
109
|
-
for (const [channel, decoder] of decoders) {
|
|
110
|
-
const tail = decoder.end();
|
|
111
|
-
if (!tail)
|
|
112
|
-
continue;
|
|
113
|
-
const index = lastChunkIndex.get(channel);
|
|
114
|
-
if (index === undefined)
|
|
115
|
-
continue;
|
|
116
|
-
const chunk = chunks[index];
|
|
117
|
-
if (chunk)
|
|
118
|
-
chunk.data += tail;
|
|
119
|
-
}
|
|
120
|
-
return chunks;
|
|
121
|
-
}
|
|
122
|
-
function isBashOutputChannel(value) {
|
|
123
|
-
return value === "stdout" || value === "stderr" || value === "pty" || value === "process";
|
|
124
|
-
}
|
|
125
|
-
function isBashOutputStatus(value) {
|
|
126
|
-
return value === "running" || value === "done" || value === "failed";
|
|
127
|
-
}
|
package/dist/pi-tools.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { constants } from "node:fs";
|
|
2
|
-
import { access as fsAccess, readFile as fsReadFile, realpath as fsRealpath } from "node:fs/promises";
|
|
3
|
-
import { createEditTool, createFindTool, createGrepTool, createLsTool, createReadTool, createWriteTool, } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { resolveCanonicalAllowedPath } from "./roots.js";
|
|
5
|
-
function toMcpContent(result) {
|
|
6
|
-
return result.content.map((content) => {
|
|
7
|
-
if (content.type === "text") {
|
|
8
|
-
return { type: "text", text: content.text };
|
|
9
|
-
}
|
|
10
|
-
return {
|
|
11
|
-
type: "image",
|
|
12
|
-
data: content.data,
|
|
13
|
-
mimeType: content.mimeType,
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
function formatToolError(error) {
|
|
18
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
-
return [{ type: "text", text: message }];
|
|
20
|
-
}
|
|
21
|
-
async function runTool(execute, input, context) {
|
|
22
|
-
try {
|
|
23
|
-
const result = await execute(input);
|
|
24
|
-
return {
|
|
25
|
-
content: toMcpContent(result),
|
|
26
|
-
details: result.details,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
return { content: formatToolError(error), isError: true };
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
export async function readFileTool(input, context) {
|
|
34
|
-
const path = await resolveCanonicalAllowedPath(input.path, context.cwd, context.readRoots ?? [context.root]);
|
|
35
|
-
const tool = createReadTool(context.cwd);
|
|
36
|
-
return runTool((params) => tool.execute("read_file", params), {
|
|
37
|
-
path,
|
|
38
|
-
offset: input.offset,
|
|
39
|
-
limit: input.limit,
|
|
40
|
-
}, context);
|
|
41
|
-
}
|
|
42
|
-
export async function writeFileTool(input, context) {
|
|
43
|
-
const path = await resolveCanonicalAllowedPath(input.path, context.cwd, context.fileRoots ?? [context.root]);
|
|
44
|
-
const tool = createWriteTool(context.cwd);
|
|
45
|
-
return runTool((params) => tool.execute("write_file", params), {
|
|
46
|
-
path,
|
|
47
|
-
content: input.content,
|
|
48
|
-
}, context);
|
|
49
|
-
}
|
|
50
|
-
export async function editFileTool(input, context) {
|
|
51
|
-
const path = await resolveCanonicalAllowedPath(input.path, context.cwd, context.fileRoots ?? [context.root]);
|
|
52
|
-
const tool = createEditTool(context.cwd);
|
|
53
|
-
return runTool((params) => tool.execute("edit_file", params), {
|
|
54
|
-
path,
|
|
55
|
-
edits: input.edits,
|
|
56
|
-
}, context);
|
|
57
|
-
}
|
|
58
|
-
export async function preflightEditFiles(paths, edits, context, signal) {
|
|
59
|
-
const seen = new Map();
|
|
60
|
-
for (const path of paths) {
|
|
61
|
-
const response = await preflightEditFileTool({ path, edits }, context, signal);
|
|
62
|
-
if (response.isError) {
|
|
63
|
-
const message = response.content
|
|
64
|
-
.filter((entry) => entry.type === "text")
|
|
65
|
-
.map((entry) => entry.text)
|
|
66
|
-
.join("\n");
|
|
67
|
-
throw new Error(`Bulk Edit preflight failed for ${path}: ${message}`);
|
|
68
|
-
}
|
|
69
|
-
const absolute = await resolveCanonicalAllowedPath(path, context.cwd, context.fileRoots ?? [context.root]);
|
|
70
|
-
const canonical = await fsRealpath(absolute);
|
|
71
|
-
const previous = seen.get(canonical);
|
|
72
|
-
if (previous) {
|
|
73
|
-
throw new Error(`Bulk Edit targets overlap: ${previous} and ${path} resolve to the same file.`);
|
|
74
|
-
}
|
|
75
|
-
seen.set(canonical, path);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
export async function preflightEditFileTool(input, context, signal) {
|
|
79
|
-
signal?.throwIfAborted();
|
|
80
|
-
const path = await resolveCanonicalAllowedPath(input.path, context.cwd, context.fileRoots ?? [context.root]);
|
|
81
|
-
const tool = createEditTool(context.cwd, {
|
|
82
|
-
operations: {
|
|
83
|
-
readFile: fsReadFile,
|
|
84
|
-
writeFile: async () => { },
|
|
85
|
-
access: (absolutePath) => fsAccess(absolutePath, constants.R_OK | constants.W_OK),
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
const response = await runTool((params) => tool.execute("edit_file", params, signal), { path, edits: input.edits }, context);
|
|
89
|
-
signal?.throwIfAborted();
|
|
90
|
-
return response;
|
|
91
|
-
}
|
|
92
|
-
export async function grepFilesTool(input, context) {
|
|
93
|
-
const path = input.path
|
|
94
|
-
? await resolveCanonicalAllowedPath(input.path, context.cwd, context.fileRoots ?? [context.root])
|
|
95
|
-
: undefined;
|
|
96
|
-
const tool = createGrepTool(context.cwd);
|
|
97
|
-
return runTool((params) => tool.execute("grep_files", params), { ...input, path }, context);
|
|
98
|
-
}
|
|
99
|
-
export async function findFilesTool(input, context) {
|
|
100
|
-
const path = input.path
|
|
101
|
-
? await resolveCanonicalAllowedPath(input.path, context.cwd, context.fileRoots ?? [context.root])
|
|
102
|
-
: undefined;
|
|
103
|
-
const tool = createFindTool(context.cwd);
|
|
104
|
-
return runTool((params) => tool.execute("find_files", params), { ...input, path }, context);
|
|
105
|
-
}
|
|
106
|
-
export async function listDirectoryTool(input, context) {
|
|
107
|
-
const path = await resolveCanonicalAllowedPath(input.path ?? ".", context.cwd, context.fileRoots ?? [context.root]);
|
|
108
|
-
const tool = createLsTool(context.cwd);
|
|
109
|
-
return runTool((params) => tool.execute("list_directory", params), { ...input, path }, context);
|
|
110
|
-
}
|
package/dist/skills.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join, resolve, sep } from "node:path";
|
|
4
|
-
import { markAdvertisedFileSourceActivated, resolveAdvertisedFileReadPath, } from "./advertised-files.js";
|
|
5
|
-
import { loadSkills, } from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import { expandHomePath, isPathInsideRoot } from "./roots.js";
|
|
7
|
-
const SUBAGENT_DELEGATION_NAME = "subagent-delegation";
|
|
8
|
-
export function effectiveSkillPaths(config, cwd) {
|
|
9
|
-
const defaultPathCandidates = [
|
|
10
|
-
join(homedir(), ".agents", "skills"),
|
|
11
|
-
resolve(cwd, ".agents", "skills"),
|
|
12
|
-
config.configSkillsDir,
|
|
13
|
-
join(config.agentDir, "skills"),
|
|
14
|
-
];
|
|
15
|
-
const defaultPaths = defaultPathCandidates.filter((path) => path !== undefined && existsSync(path));
|
|
16
|
-
const seen = new Set();
|
|
17
|
-
return [...defaultPaths, ...config.skillPaths]
|
|
18
|
-
.map((path) => resolveSkillPath(path, cwd))
|
|
19
|
-
.filter((path) => {
|
|
20
|
-
if (seen.has(path))
|
|
21
|
-
return false;
|
|
22
|
-
seen.add(path);
|
|
23
|
-
return true;
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
function resolveSkillPath(path, cwd) {
|
|
27
|
-
return resolve(cwd, expandHomePath(path));
|
|
28
|
-
}
|
|
29
|
-
export function loadWorkspaceSkills(config, cwd) {
|
|
30
|
-
if (!config.skillsEnabled)
|
|
31
|
-
return { skills: [], diagnostics: [] };
|
|
32
|
-
const result = loadSkills({
|
|
33
|
-
cwd,
|
|
34
|
-
agentDir: config.agentDir,
|
|
35
|
-
skillPaths: effectiveSkillPaths(config, cwd),
|
|
36
|
-
includeDefaults: false,
|
|
37
|
-
});
|
|
38
|
-
if (config.subagents)
|
|
39
|
-
return result;
|
|
40
|
-
return {
|
|
41
|
-
skills: result.skills.filter((skill) => skill.name !== SUBAGENT_DELEGATION_NAME),
|
|
42
|
-
diagnostics: result.diagnostics.filter((diagnostic) => {
|
|
43
|
-
const collision = diagnostic.collision;
|
|
44
|
-
return !(collision?.resourceType === "skill" && collision.name === SUBAGENT_DELEGATION_NAME);
|
|
45
|
-
}),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
export function resolveSkillReadPath(skills, activatedSkillDirs, inputPath) {
|
|
49
|
-
const virtualRead = resolveVirtualSkillReadPath(skills, activatedSkillDirs, inputPath);
|
|
50
|
-
if (virtualRead)
|
|
51
|
-
return virtualRead;
|
|
52
|
-
// Compatibility for stale Host metadata that still contains the historical real path.
|
|
53
|
-
const resolution = resolveAdvertisedFileReadPath(skills, activatedSkillDirs, inputPath);
|
|
54
|
-
if (!resolution)
|
|
55
|
-
return undefined;
|
|
56
|
-
return {
|
|
57
|
-
absolutePath: resolution.absolutePath,
|
|
58
|
-
skill: resolution.source,
|
|
59
|
-
isSkillFile: resolution.isEntryFile,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
function resolveVirtualSkillReadPath(skills, activatedSkillDirs, inputPath) {
|
|
63
|
-
const prefix = "skills://";
|
|
64
|
-
if (!inputPath.startsWith(prefix))
|
|
65
|
-
return undefined;
|
|
66
|
-
const requested = inputPath.slice(prefix.length);
|
|
67
|
-
const slashIndex = requested.indexOf("/");
|
|
68
|
-
const encodedName = slashIndex === -1 ? requested : requested.slice(0, slashIndex);
|
|
69
|
-
if (!encodedName)
|
|
70
|
-
throw new Error(`Invalid skill URI: ${inputPath}`);
|
|
71
|
-
let name;
|
|
72
|
-
try {
|
|
73
|
-
name = decodeURIComponent(encodedName);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
throw new Error(`Invalid skill URI: ${inputPath}`);
|
|
77
|
-
}
|
|
78
|
-
const skill = skills.find((candidate) => candidate.name === name);
|
|
79
|
-
if (!skill)
|
|
80
|
-
throw new Error(`Unknown advertised skill: ${name}`);
|
|
81
|
-
if (slashIndex === -1 || slashIndex === requested.length - 1) {
|
|
82
|
-
return {
|
|
83
|
-
absolutePath: resolve(skill.filePath),
|
|
84
|
-
skill,
|
|
85
|
-
isSkillFile: true,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
const relativePart = requested.slice(slashIndex + 1);
|
|
89
|
-
const segments = relativePart.split("/").map((segment) => {
|
|
90
|
-
let decoded;
|
|
91
|
-
try {
|
|
92
|
-
decoded = decodeURIComponent(segment);
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
throw new Error(`Invalid skill URI: ${inputPath}`);
|
|
96
|
-
}
|
|
97
|
-
if (!decoded || decoded === "." || decoded === ".." || decoded.includes("/") || decoded.includes("\\")) {
|
|
98
|
-
throw new Error(`Invalid skill URI: ${inputPath}`);
|
|
99
|
-
}
|
|
100
|
-
return decoded;
|
|
101
|
-
});
|
|
102
|
-
const baseDir = resolve(skill.baseDir);
|
|
103
|
-
if (!activatedSkillDirs.has(baseDir))
|
|
104
|
-
return undefined;
|
|
105
|
-
const absolutePath = resolve(baseDir, ...segments);
|
|
106
|
-
if (!isPathInsideRoot(absolutePath, baseDir)) {
|
|
107
|
-
throw new Error(`Skill resource is outside its skill directory: ${inputPath}`);
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
absolutePath,
|
|
111
|
-
skill,
|
|
112
|
-
isSkillFile: false,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
export function markSkillActivated(activatedSkillDirs, skill) {
|
|
116
|
-
markAdvertisedFileSourceActivated(activatedSkillDirs, skill);
|
|
117
|
-
}
|
|
118
|
-
export function formatPathForPrompt(path) {
|
|
119
|
-
const home = resolve(homedir());
|
|
120
|
-
const resolvedPath = resolve(path);
|
|
121
|
-
if (resolvedPath === home)
|
|
122
|
-
return "~";
|
|
123
|
-
if (resolvedPath.startsWith(`${home}${sep}`)) {
|
|
124
|
-
return `~/${resolvedPath.slice(home.length + 1).split(sep).join("/")}`;
|
|
125
|
-
}
|
|
126
|
-
return resolvedPath.split(sep).join("/");
|
|
127
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as e,c as t,i as n,l as r,o as i,s as a,t as o,u as s}from"./workspace-app-CxwJuZyS.js";function c(e){let t=m(e._meta),n=m(e.structuredContent),r=m(t?.[`forgerelay/activityPanelWorkspace`])??m(n?.[`forgerelay/activityPanelWorkspace`]);if(r&&!(typeof r.workspaceId!=`string`||r.workspaceId.length===0)&&!(typeof r.root!=`string`||r.root.length===0)&&!(r.mode!==void 0&&r.mode!==`checkout`&&r.mode!==`worktree`))return r}var l=class{root;card=null;openInstructionKey=null;showAvailableInstructions=!1;constructor(e){this.root=e}get active(){return this.card!==null}get workspaceId(){return this.card?.workspaceId}accept(e){let t=c(e);return t?(this.card?.workspaceId!==t.workspaceId&&(this.openInstructionKey=null,this.showAvailableInstructions=!1),this.card=t,!0):!1}clear(){this.card=null,this.openInstructionKey=null,this.showAvailableInstructions=!1,this.root.replaceChildren()}render(){return this.card?(this.root.replaceChildren(this.renderPanel(this.card)),!0):!1}renderPanel(t){let n=h(`section`,`workspace-panel`),r=h(`div`,`workspace-panel-header`),a=h(`span`,`workspace-panel-icon`);a.setAttribute(`aria-hidden`,`true`),a.append(e(i.folderOpen,`workspace-panel-icon-svg`));let o=h(`span`,`workspace-panel-title-group`),s=h(`span`,`workspace-panel-title`,`Workspace`),c=h(`span`,`workspace-panel-subtitle`,p(t.root));c.title=t.root,o.append(s,c);let l=h(`span`,`workspace-panel-mode`,t.mode??`workspace`);r.append(a,o,l);let d=h(`div`,`workspace-details`),f=h(`div`,`workspace-rows`);return u(f,`Root`,t.root,i.folderOpen,!0),u(f,`Mode`,t.mode??`workspace`,i.folderTree),t.worktree&&this.appendWorktreeRows(f,t),t.sourceRoot&&t.sourceRoot!==t.root&&u(f,`Source checkout`,t.sourceRoot,i.sourceCheckout,!0),this.appendInstructions(f,t.agentsFiles??[],t.availableAgentsFiles??[]),this.appendSkills(f,t.skills??[]),this.appendAgents(f,t),d.append(f),n.append(r,d),n}appendWorktreeRows(t,n){let r=n.worktree;if(!r)return;let a=[r.baseRef,r.baseSha?.slice(0,8)].filter(e=>!!e);if(a.length>0){let n=h(`span`,`workspace-base-value`),o=h(`span`,`workspace-value`,a.join(` · `));if(o.title=a.join(` · `),n.append(o),r.dirtySource){let t=h(`span`,`workspace-base-warning`);t.title=`The source checkout had uncommitted changes when this worktree was created.`,t.setAttribute(`role`,`img`),t.setAttribute(`aria-label`,`Source checkout changes are not included in this worktree`),t.append(e(i.warning,`workspace-base-warning-svg`)),n.append(t)}d(t,`Base`,n,i.base)}r.branch&&u(t,`Worktree branch`,r.branch,i.gitBranch),r.targetBranch&&u(t,`Merge target`,r.targetBranch,i.gitBranch)}appendInstructions(e,t,n){let r=t.map((e,t)=>({key:`loaded:${t}`,path:e.path,label:e.path??`Loaded instructions`,content:e.content,status:`loaded`})),a=new Set(r.map(e=>e.path).filter(Boolean)),o=n.flatMap((e,t)=>e.path&&a.has(e.path)?[]:[{key:`available:${t}`,path:e.path,label:e.path??`Nested instructions`,status:`available`}]);if(r.length===0&&o.length===0)return;let s=this.showAvailableInstructions?[...r,...o]:r,c=h(`span`,`workspace-instruction-list`);for(let e of s)c.append(this.renderInstruction(e,c));if(o.length>0){let e=h(`button`,`workspace-instructions-toggle`,this.showAvailableInstructions?`Show less`:`View all`);e.type=`button`,e.setAttribute(`aria-expanded`,String(this.showAvailableInstructions)),e.addEventListener(`click`,()=>{this.showAvailableInstructions=!this.showAvailableInstructions,this.showAvailableInstructions||(this.openInstructionKey=null),this.render()}),c.append(e)}let l=h(`div`,`workspace-instructions-content`);l.append(c),d(e,`Instructions`,l,i.instructions,`workspace-instructions-row`)}renderInstruction(t,n){let r=h(`span`,`workspace-instruction-item`);r.dataset.instructionKey=t.key;let a=t.status===`loaded`&&t.content!==void 0,o=h(a?`button`:`span`,`workspace-instruction-header${a?` interactive`:``}`);o instanceof HTMLButtonElement&&(o.type=`button`,o.setAttribute(`aria-expanded`,String(this.openInstructionKey===t.key)));let s=h(`span`,`workspace-instruction-status ${t.status}`);s.setAttribute(`role`,`img`),s.setAttribute(`aria-label`,t.status===`loaded`?`Loaded into the current workspace context`:`Available for a nested directory`),s.append(e(t.status===`loaded`?i.instructionLoaded:i.instructionAvailable,`workspace-instruction-status-svg`));let c=h(`span`,`workspace-instruction-text`),l=p(t.label);if(c.append(h(`span`,`workspace-instruction-name`,l)),t.path&&t.path!==l){let e=h(`span`,`workspace-instruction-path`,t.path);e.title=t.path,c.append(e)}if(o.append(s,c),!a)return r.append(o),r;let u=h(`span`,`workspace-instruction-chevron`);u.setAttribute(`aria-hidden`,`true`),u.append(e(i.chevronDown,`workspace-instruction-chevron-svg`)),o.append(u);let d=h(`pre`,`workspace-instruction-preview`,t.content);return o.addEventListener(`click`,()=>{this.openInstructionKey=this.openInstructionKey===t.key?null:t.key;for(let e of n.querySelectorAll(`.workspace-instruction-item`)){let t=e.querySelector(`.workspace-instruction-header.interactive`),n=e.querySelector(`.workspace-instruction-preview`),r=e.dataset.instructionKey===this.openInstructionKey;e.classList.toggle(`expanded`,r),t?.setAttribute(`aria-expanded`,String(r)),n&&(n.hidden=!r)}}),r.append(o,d),(()=>{let e=this.openInstructionKey===t.key;r.classList.toggle(`expanded`,e),o.setAttribute(`aria-expanded`,String(e)),d.hidden=!e})(),r}appendSkills(e,t){if(t.length===0)return;let n=f(t.map(e=>({label:e.name??`Unnamed skill`,title:e.description||void 0})));n.classList.add(`workspace-skills-list`),d(e,`Skills`,n,i.skills,`workspace-skills-row`)}appendAgents(e,t){let r=t.agentProviders??[],a=(t.agents??[]).map(e=>{let t=e.provider?.trim(),r=e.providerAvailable===!1;return{label:e.name??`Unnamed agent`,logo:t?n(t):void 0,profile:!0,tone:r?`muted`:void 0,title:[e.description,t?`Provider: ${t}`:void 0,e.model?`Model: ${e.model}`:void 0,e.thinking?`Thinking: ${e.thinking}`:void 0,r?e.providerUnavailableReason??`Provider unavailable`:void 0].filter(Boolean).join(`
|
|
2
|
-
`)||void 0}}),o=r.map(e=>{let t=e.name?.trim()||`Unknown provider`,r=n(t);return{label:t,logo:r,bareLogo:!!r,ariaLabel:t,tone:e.available===!1?`muted`:void 0,title:e.available===!1?e.reason??`Provider unavailable`:t}});if(a.length>0){let t=f([...a,...o]);t.classList.add(`workspace-agents-list`),d(e,`Agents`,t,i.agents,`workspace-agents-row`)}else o.length>0&&d(e,`Providers`,f(o),i.providers)}};function u(e,t,n,r,i=!1){let a=h(`span`,`workspace-value${i?` mono`:``}`,n);a.title=n,d(e,t,a,r)}function d(t,n,r,i,a){let o=h(`div`,[`workspace-row`,a].filter(Boolean).join(` `)),s=h(`span`,`workspace-row-icon`);s.setAttribute(`aria-hidden`,`true`),s.append(e(i,`workspace-row-icon-svg`)),o.append(s,h(`span`,`workspace-key`,n),r),t.append(o)}function f(e){let t=h(`span`,`workspace-chip-list`);for(let n of e){let e=!!(n.bareLogo&&n.logo),r=h(`span`,[e?`workspace-provider-logo`:n.profile?`workspace-agent-profile`:`workspace-chip`,n.tone].filter(Boolean).join(` `));if(n.title&&(r.title=n.title),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(h(`span`,`workspace-chip-label`,n.label)),t.append(r)}return t}function p(e){return e.replaceAll(`\\`,`/`).split(`/`).filter(Boolean).at(-1)??e}function m(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:void 0}function h(e,t,n){let r=document.createElement(e);return r.className=t,n!==void 0&&(r.textContent=n),r}document.documentElement.dataset.forgerelayApp=`panel`;var g=document.querySelector(`#app`);if(!g)throw Error(`Missing #app root element.`);var _=g,v=I(`section`,`forgerelay-panel`),y=I(`div`,`workspace-panel-slot`),b=I(`div`,`activity-panel-slot`);v.append(y,b),_.replaceChildren(v);var x=new l(y),S=new o(b,{embedded:!0}),C=null,w,T=!1,E=null,D,O=!1,k;A();async function A(){P(),C=new r({name:`forgerelay-panel`,version:`0.1.0`},{}),C.ontoolinput=e=>{let t=e.arguments?.workspaceId;typeof t==`string`&&t.length>0&&(D=t,T&&!S.active&&M()),P()},C.ontoolresult=e=>{j(e)},C.onhostcontextchanged=e=>{w={...w,...e},N()},C.onteardown=async()=>(T=!1,S.detach(),x.clear(),{});try{await C.connect();let e=C.getHostContext();e&&(w=e),N(),T=!0,S.attach(C),D&&!S.active&&M()}catch(e){E=e instanceof Error?e.message:String(e)}P()}function j(e){x.accept(e)&&(D=x.workspaceId),S.accept(e),P()}async function M(){let e=D;if(!(!C||!T||!e||S.active||O||k===e)&&C.getHostCapabilities()?.serverTools){k=e,O=!0,P();try{let t=await C.callServerTool({name:`activity_snapshot`,arguments:{workspaceId:e}});t.isError||j(t)}finally{O=!1,P()}}}function N(){w?.theme&&s(w.theme),w?.styles?.variables&&t(w.styles.variables),w?.styles?.css?.fonts&&a(w.styles.css.fonts);let e=w?.safeAreaInsets;e&&(document.body.style.padding=`${e.top}px ${e.right}px ${e.bottom}px ${e.left}px`)}function P(){x.render()||F(),S.render()||b.replaceChildren()}function F(){let e=I(`section`,`workspace-panel pending`),t=I(`div`,`workspace-panel-header`),n=I(`span`,`workspace-panel-title-group`);n.append(I(`span`,`workspace-panel-title`,`Workspace`),I(`span`,`workspace-panel-subtitle`,E||(D?O?`Loading workspace…`:D:T?`Waiting for workspace…`:`Connecting to host…`))),t.append(n),e.append(t),y.replaceChildren(e)}function I(e,t,n){let r=document.createElement(e);return r.className=t,n!==void 0&&(r.textContent=n),r}
|