@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { deleteWorkspaceActivityHistory } from "../storage/history.js";
|
|
1
2
|
export class ActivityQueryService {
|
|
2
3
|
turns;
|
|
3
4
|
audit;
|
|
@@ -7,6 +8,15 @@ export class ActivityQueryService {
|
|
|
7
8
|
this.audit = audit;
|
|
8
9
|
this.outputs = outputs;
|
|
9
10
|
}
|
|
11
|
+
deleteWorkspaceHistory(stateDir, workspaceId) {
|
|
12
|
+
deleteWorkspaceActivityHistory({
|
|
13
|
+
stateDir,
|
|
14
|
+
workspaceId,
|
|
15
|
+
audit: this.audit,
|
|
16
|
+
outputs: this.outputs,
|
|
17
|
+
turns: this.turns,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
10
20
|
beginTurn(conversationScopeId, workspaceId) {
|
|
11
21
|
const turn = this.turns.begin(conversationScopeId, workspaceId);
|
|
12
22
|
return this.state(turn.turnId);
|
|
@@ -73,7 +83,7 @@ export class ActivityQueryService {
|
|
|
73
83
|
}
|
|
74
84
|
bashOutput(turnId, outputId, cursor = 0) {
|
|
75
85
|
this.requireTurn(turnId);
|
|
76
|
-
const output = this.outputs.
|
|
86
|
+
const output = this.outputs.readSince(outputId, cursor);
|
|
77
87
|
if (!output)
|
|
78
88
|
throw new Error(`Unknown Bash output: ${outputId}.`);
|
|
79
89
|
const activities = this.audit.listActivitiesByTurn(turnId);
|
|
@@ -81,17 +91,13 @@ export class ActivityQueryService {
|
|
|
81
91
|
if (!visible) {
|
|
82
92
|
throw new Error(`Bash output ${outputId} is not part of Host Turn ${turnId}.`);
|
|
83
93
|
}
|
|
84
|
-
const latestCursor = output.chunks.at(-1)?.sequence ?? 0;
|
|
85
94
|
return {
|
|
86
95
|
outputId: output.outputId,
|
|
87
96
|
activityId: output.activityId,
|
|
88
97
|
processId: output.processId,
|
|
89
98
|
command: output.command,
|
|
90
|
-
output: output.
|
|
91
|
-
|
|
92
|
-
.map((chunk) => chunk.data)
|
|
93
|
-
.join(""),
|
|
94
|
-
cursor: latestCursor,
|
|
99
|
+
output: output.output,
|
|
100
|
+
cursor: output.cursor,
|
|
95
101
|
status: output.status,
|
|
96
102
|
...(output.exitCode !== undefined ? { exitCode: output.exitCode } : {}),
|
|
97
103
|
...(output.signal !== undefined ? { signal: output.signal } : {}),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
2
2
|
import * as z from "zod/v4";
|
|
3
|
-
import { logEvent, transportSessionIdPrefix } from "
|
|
4
|
-
import { hostConversationScopeId } from "
|
|
5
|
-
import { ACTIVITY_PANEL_DEFAULT_EXPANDED_META_KEY, ACTIVITY_PANEL_WORKSPACE_META_KEY, } from "
|
|
3
|
+
import { logEvent, transportSessionIdPrefix } from "../../runtime/logging/logger.js";
|
|
4
|
+
import { hostConversationScopeId } from "../../mcp/request-meta.js";
|
|
5
|
+
import { ACTIVITY_PANEL_DEFAULT_EXPANDED_META_KEY, ACTIVITY_PANEL_WORKSPACE_META_KEY, } from "../ui/contract.js";
|
|
6
6
|
const READ_ONLY_ANNOTATIONS = {
|
|
7
7
|
readOnlyHint: true,
|
|
8
8
|
destructiveHint: false,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { removeWorkspaceActivityStorage } from "./paths.js";
|
|
2
|
+
/**
|
|
3
|
+
* User-requested Workspace deletion is the retention boundary for durable
|
|
4
|
+
* Activity history by default. Index rows are removed before the backing files
|
|
5
|
+
* so a crash cannot leave SQLite pointing at already-deleted payload shards.
|
|
6
|
+
*/
|
|
7
|
+
export function deleteWorkspaceActivityHistory(input) {
|
|
8
|
+
input.outputs.deleteWorkspace(input.workspaceId);
|
|
9
|
+
input.audit.deleteWorkspace(input.workspaceId);
|
|
10
|
+
input.turns.deleteWorkspace(input.workspaceId);
|
|
11
|
+
removeWorkspaceActivityStorage(input.stateDir, input.workspaceId);
|
|
12
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
const SAFE_WORKSPACE_ID = /^(?:ws|rws|cws)_[a-z0-9]+$/i;
|
|
5
|
+
export function activityWorkspaceStorageKey(workspace) {
|
|
6
|
+
if (workspace.id && SAFE_WORKSPACE_ID.test(workspace.id))
|
|
7
|
+
return workspace.id;
|
|
8
|
+
return `root_${createHash("sha256").update(`${workspace.mode}\0${resolve(workspace.root)}`).digest("hex").slice(0, 20)}`;
|
|
9
|
+
}
|
|
10
|
+
export function activityWorkspaceDirectory(stateDir, workspace) {
|
|
11
|
+
return join(stateDir, "workspaces", activityWorkspaceStorageKey(workspace), "activity");
|
|
12
|
+
}
|
|
13
|
+
export function activityEventLogPrefix(stateDir, workspace) {
|
|
14
|
+
return join(activityWorkspaceDirectory(stateDir, workspace), "events");
|
|
15
|
+
}
|
|
16
|
+
export function bashOutputDirectory(stateDir, workspace) {
|
|
17
|
+
return join(activityWorkspaceDirectory(stateDir, workspace), "bash");
|
|
18
|
+
}
|
|
19
|
+
export function bashOutputLogPrefix(stateDir, workspace, outputId) {
|
|
20
|
+
return join(bashOutputDirectory(stateDir, workspace), outputId);
|
|
21
|
+
}
|
|
22
|
+
export function bashOutputMetadataPrefix(stateDir, workspace, outputId, field) {
|
|
23
|
+
return join(bashOutputDirectory(stateDir, workspace), `${outputId}-${field}`);
|
|
24
|
+
}
|
|
25
|
+
export function removeWorkspaceActivityStorage(stateDir, workspaceId) {
|
|
26
|
+
if (!SAFE_WORKSPACE_ID.test(workspaceId)) {
|
|
27
|
+
throw new Error(`Invalid Workspace id for Activity storage cleanup: ${workspaceId}`);
|
|
28
|
+
}
|
|
29
|
+
rmSync(join(stateDir, "workspaces", workspaceId, "activity"), {
|
|
30
|
+
recursive: true,
|
|
31
|
+
force: true,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function ensurePrivateDirectory(path) {
|
|
35
|
+
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
36
|
+
}
|
|
37
|
+
export function stateRelativePath(stateDir, path) {
|
|
38
|
+
const relationship = relative(resolve(stateDir), resolve(path));
|
|
39
|
+
if (relationship === "" ||
|
|
40
|
+
relationship === ".." ||
|
|
41
|
+
relationship.startsWith(`..${sep}`)) {
|
|
42
|
+
throw new Error(`Activity log path is outside ForgeRelay state directory: ${path}`);
|
|
43
|
+
}
|
|
44
|
+
return relationship.split(sep).join("/");
|
|
45
|
+
}
|
|
46
|
+
export function resolveStateRelativePath(stateDir, path) {
|
|
47
|
+
const absolute = resolve(stateDir, path);
|
|
48
|
+
const relationship = relative(resolve(stateDir), absolute);
|
|
49
|
+
if (relationship === ".." || relationship.startsWith(`..${sep}`)) {
|
|
50
|
+
throw new Error(`Activity log reference escapes ForgeRelay state directory: ${path}`);
|
|
51
|
+
}
|
|
52
|
+
return absolute;
|
|
53
|
+
}
|
|
54
|
+
export function ensureParentPrivate(path) {
|
|
55
|
+
ensurePrivateDirectory(dirname(path));
|
|
56
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { closeSync, constants as fsConstants, fstatSync, openSync, readSync, readdirSync, statSync, writeSync, } from "node:fs";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import { ensureParentPrivate, resolveStateRelativePath, stateRelativePath } from "./paths.js";
|
|
4
|
+
/**
|
|
5
|
+
* Segment files are deliberately small enough to inspect, copy, prune, and
|
|
6
|
+
* recover independently. The logical stream may be arbitrarily large; no
|
|
7
|
+
* individual on-disk activity/output file should grow without bound.
|
|
8
|
+
*/
|
|
9
|
+
export const ACTIVITY_LOG_SEGMENT_BYTES = 8 * 1024 * 1024;
|
|
10
|
+
export class SegmentedLogStore {
|
|
11
|
+
stateDir;
|
|
12
|
+
segmentBytes;
|
|
13
|
+
appendOffsets = new Map();
|
|
14
|
+
constructor(stateDir, segmentBytes = ACTIVITY_LOG_SEGMENT_BYTES) {
|
|
15
|
+
this.stateDir = stateDir;
|
|
16
|
+
this.segmentBytes = segmentBytes;
|
|
17
|
+
}
|
|
18
|
+
append(prefixPath, data) {
|
|
19
|
+
const prefix = stateRelativePath(this.stateDir, prefixPath);
|
|
20
|
+
const currentOffset = this.appendOffsets.get(prefix) ?? this.discoverLength(prefixPath);
|
|
21
|
+
this.writeAt(prefixPath, currentOffset, data);
|
|
22
|
+
this.appendOffsets.set(prefix, currentOffset + data.byteLength);
|
|
23
|
+
return { prefix, offset: currentOffset, length: data.byteLength };
|
|
24
|
+
}
|
|
25
|
+
appendAt(prefix, logicalOffset, data) {
|
|
26
|
+
const prefixPath = resolveStateRelativePath(this.stateDir, prefix);
|
|
27
|
+
this.writeAt(prefixPath, logicalOffset, data);
|
|
28
|
+
const next = logicalOffset + data.byteLength;
|
|
29
|
+
const known = this.appendOffsets.get(prefix);
|
|
30
|
+
if (known === undefined || next > known)
|
|
31
|
+
this.appendOffsets.set(prefix, next);
|
|
32
|
+
}
|
|
33
|
+
read(reference) {
|
|
34
|
+
return this.readRange(reference.prefix, reference.offset, reference.offset + reference.length);
|
|
35
|
+
}
|
|
36
|
+
readRange(prefix, start, end) {
|
|
37
|
+
if (!Number.isSafeInteger(start) || start < 0 || !Number.isSafeInteger(end) || end < start) {
|
|
38
|
+
throw new Error(`Invalid segmented log range: ${start}..${end}.`);
|
|
39
|
+
}
|
|
40
|
+
if (start === end)
|
|
41
|
+
return Buffer.alloc(0);
|
|
42
|
+
const prefixPath = resolveStateRelativePath(this.stateDir, prefix);
|
|
43
|
+
const result = Buffer.allocUnsafe(end - start);
|
|
44
|
+
let logicalOffset = start;
|
|
45
|
+
let resultOffset = 0;
|
|
46
|
+
while (logicalOffset < end) {
|
|
47
|
+
const segmentIndex = Math.floor(logicalOffset / this.segmentBytes);
|
|
48
|
+
const segmentOffset = logicalOffset % this.segmentBytes;
|
|
49
|
+
const available = Math.min(end - logicalOffset, this.segmentBytes - segmentOffset);
|
|
50
|
+
const path = segmentPath(prefixPath, segmentIndex);
|
|
51
|
+
const fd = openSync(path, fsConstants.O_RDONLY);
|
|
52
|
+
try {
|
|
53
|
+
const bytesRead = readSync(fd, result, resultOffset, available, segmentOffset);
|
|
54
|
+
if (bytesRead !== available) {
|
|
55
|
+
throw new Error(`Segmented log ${path} ended before logical offset ${logicalOffset + available}.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
closeSync(fd);
|
|
60
|
+
}
|
|
61
|
+
logicalOffset += available;
|
|
62
|
+
resultOffset += available;
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
logicalLength(prefix) {
|
|
67
|
+
const known = this.appendOffsets.get(prefix);
|
|
68
|
+
if (known !== undefined)
|
|
69
|
+
return known;
|
|
70
|
+
const prefixPath = resolveStateRelativePath(this.stateDir, prefix);
|
|
71
|
+
const length = this.discoverLength(prefixPath);
|
|
72
|
+
this.appendOffsets.set(prefix, length);
|
|
73
|
+
return length;
|
|
74
|
+
}
|
|
75
|
+
writeAt(prefixPath, logicalOffset, data) {
|
|
76
|
+
if (!Number.isSafeInteger(logicalOffset) || logicalOffset < 0) {
|
|
77
|
+
throw new Error(`Invalid segmented log offset: ${logicalOffset}.`);
|
|
78
|
+
}
|
|
79
|
+
if (data.byteLength === 0)
|
|
80
|
+
return;
|
|
81
|
+
ensureParentPrivate(prefixPath);
|
|
82
|
+
const bytes = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
83
|
+
let sourceOffset = 0;
|
|
84
|
+
let current = logicalOffset;
|
|
85
|
+
while (sourceOffset < bytes.length) {
|
|
86
|
+
const segmentIndex = Math.floor(current / this.segmentBytes);
|
|
87
|
+
const segmentOffset = current % this.segmentBytes;
|
|
88
|
+
const writable = Math.min(bytes.length - sourceOffset, this.segmentBytes - segmentOffset);
|
|
89
|
+
const path = segmentPath(prefixPath, segmentIndex);
|
|
90
|
+
const fd = openSync(path, fsConstants.O_CREAT | fsConstants.O_WRONLY, 0o600);
|
|
91
|
+
try {
|
|
92
|
+
const stats = fstatSync(fd);
|
|
93
|
+
if (stats.size !== segmentOffset) {
|
|
94
|
+
throw new Error(`Segmented log append position mismatch for ${path}: expected ${segmentOffset}, found ${stats.size}.`);
|
|
95
|
+
}
|
|
96
|
+
const written = writeSync(fd, bytes, sourceOffset, writable, segmentOffset);
|
|
97
|
+
if (written !== writable) {
|
|
98
|
+
throw new Error(`Short write while appending segmented log ${path}.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
closeSync(fd);
|
|
103
|
+
}
|
|
104
|
+
sourceOffset += writable;
|
|
105
|
+
current += writable;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
discoverLength(prefixPath) {
|
|
109
|
+
const directory = dirname(prefixPath);
|
|
110
|
+
const stem = basename(prefixPath);
|
|
111
|
+
let files;
|
|
112
|
+
try {
|
|
113
|
+
files = readdirSync(directory);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error.code === "ENOENT")
|
|
117
|
+
return 0;
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
const pattern = new RegExp(`^${escapeRegExp(stem)}\\.(\\d{6})\\.log$`);
|
|
121
|
+
let maxIndex = -1;
|
|
122
|
+
let maxSize = 0;
|
|
123
|
+
for (const file of files) {
|
|
124
|
+
const match = pattern.exec(file);
|
|
125
|
+
if (!match)
|
|
126
|
+
continue;
|
|
127
|
+
const index = Number(match[1]);
|
|
128
|
+
const size = statSync(join(directory, file)).size;
|
|
129
|
+
if (size > this.segmentBytes) {
|
|
130
|
+
throw new Error(`Segmented log shard exceeds ${this.segmentBytes} bytes: ${join(directory, file)}.`);
|
|
131
|
+
}
|
|
132
|
+
if (index > maxIndex) {
|
|
133
|
+
maxIndex = index;
|
|
134
|
+
maxSize = size;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (maxIndex < 0)
|
|
138
|
+
return 0;
|
|
139
|
+
if (maxSize < this.segmentBytes)
|
|
140
|
+
return maxIndex * this.segmentBytes + maxSize;
|
|
141
|
+
return (maxIndex + 1) * this.segmentBytes;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export function segmentPath(prefixPath, index) {
|
|
145
|
+
return `${prefixPath}.${String(index).padStart(6, "0")}.log`;
|
|
146
|
+
}
|
|
147
|
+
function escapeRegExp(value) {
|
|
148
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
149
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import * as prompts from "@clack/prompts";
|
|
3
|
+
import { satisfies } from "semver";
|
|
4
|
+
const SUPPORTED_NODE_RANGE = ">=20.12 <27";
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
export function isNullConfigValue(value) {
|
|
7
|
+
const normalized = value.trim().toLowerCase();
|
|
8
|
+
return !normalized || normalized === "null" || normalized === "none";
|
|
9
|
+
}
|
|
10
|
+
export function normalizeOptionalPublicBaseUrl(value) {
|
|
11
|
+
const trimmed = value.trim();
|
|
12
|
+
if (isNullConfigValue(trimmed))
|
|
13
|
+
return null;
|
|
14
|
+
return compactPublicBaseUrlConfig(normalizePublicBaseUrlsInput(trimmed));
|
|
15
|
+
}
|
|
16
|
+
export function normalizePublicBaseUrlsInput(value) {
|
|
17
|
+
const entries = value
|
|
18
|
+
.split(",")
|
|
19
|
+
.map((entry) => entry.trim())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
if (entries.length === 0)
|
|
22
|
+
throw new Error("Enter at least one public base URL.");
|
|
23
|
+
return Array.from(new Set(entries.map((entry) => normalizePublicBaseUrl(entry))));
|
|
24
|
+
}
|
|
25
|
+
export function compactPublicBaseUrlConfig(baseUrls) {
|
|
26
|
+
return baseUrls.length === 1 ? baseUrls[0] : baseUrls;
|
|
27
|
+
}
|
|
28
|
+
export function normalizePublicBaseUrl(value) {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
const parsed = new URL(trimmed);
|
|
31
|
+
parsed.hash = "";
|
|
32
|
+
parsed.search = "";
|
|
33
|
+
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
34
|
+
return parsed.toString().replace(/\/$/, "");
|
|
35
|
+
}
|
|
36
|
+
export async function textPrompt(options) {
|
|
37
|
+
const result = await prompts.text({
|
|
38
|
+
...options,
|
|
39
|
+
validate: (value) => options.validate?.(value?.trim() ? value : options.defaultValue),
|
|
40
|
+
});
|
|
41
|
+
if (prompts.isCancel(result))
|
|
42
|
+
throw new SetupCancelledError();
|
|
43
|
+
const value = String(result).trim();
|
|
44
|
+
return value || options.defaultValue;
|
|
45
|
+
}
|
|
46
|
+
export function validatePort(value) {
|
|
47
|
+
const port = Number(value);
|
|
48
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535
|
|
49
|
+
? undefined
|
|
50
|
+
: "Enter a port between 1 and 65535.";
|
|
51
|
+
}
|
|
52
|
+
export function validateRequiredPublicBaseUrls(value) {
|
|
53
|
+
const trimmed = value?.trim() ?? "";
|
|
54
|
+
if (!trimmed)
|
|
55
|
+
return "Enter at least one public base URL from your tunnel or reverse proxy.";
|
|
56
|
+
try {
|
|
57
|
+
normalizePublicBaseUrlsInput(trimmed);
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return error instanceof Error ? error.message : String(error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function assertSupportedNode() {
|
|
65
|
+
if (satisfies(process.versions.node, SUPPORTED_NODE_RANGE))
|
|
66
|
+
return;
|
|
67
|
+
throw new Error([
|
|
68
|
+
`ForgeRelay requires Node ${SUPPORTED_NODE_RANGE}.`,
|
|
69
|
+
`Current Node: ${process.version}`,
|
|
70
|
+
"",
|
|
71
|
+
"Install Node 22 LTS or use a version manager such as nvm, fnm, or mise.",
|
|
72
|
+
].join("\n"));
|
|
73
|
+
}
|
|
74
|
+
export function nodeVersionStatus() {
|
|
75
|
+
return satisfies(process.versions.node, SUPPORTED_NODE_RANGE)
|
|
76
|
+
? `supported ${SUPPORTED_NODE_RANGE}`
|
|
77
|
+
: `unsupported, requires ${SUPPORTED_NODE_RANGE}`;
|
|
78
|
+
}
|
|
79
|
+
export class SetupCancelledError extends Error {
|
|
80
|
+
}
|
|
81
|
+
export function checkSqliteNative() {
|
|
82
|
+
try {
|
|
83
|
+
const Database = require("better-sqlite3");
|
|
84
|
+
const db = new Database(":memory:");
|
|
85
|
+
db.close();
|
|
86
|
+
return "ok";
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return error instanceof Error ? error.message : String(error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export function checkGitAvailable() {
|
|
93
|
+
try {
|
|
94
|
+
const { execFileSync } = require("node:child_process");
|
|
95
|
+
return execFileSync("git", ["--version"], { encoding: "utf8" }).trim();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99
|
+
return `unavailable (${message})`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function checkBashShell() {
|
|
103
|
+
try {
|
|
104
|
+
const { execFileSync } = require("node:child_process");
|
|
105
|
+
const command = process.env.BASH?.trim() || "bash";
|
|
106
|
+
const version = execFileSync(command, ["--version"], {
|
|
107
|
+
encoding: "utf8",
|
|
108
|
+
windowsHide: true,
|
|
109
|
+
}).split(/\r?\n/, 1)[0]?.trim();
|
|
110
|
+
return version ? `${command} (${version})` : command;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
114
|
+
return `unavailable (${message})`;
|
|
115
|
+
}
|
|
116
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -8,23 +8,21 @@ import { tmpdir } from "node:os";
|
|
|
8
8
|
import { join, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import * as prompts from "@clack/prompts";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { loadConfig } from "./config.js";
|
|
14
|
-
import { runHooksCommand } from "./hook-cli.js";
|
|
11
|
+
import { loadConfig } from "./runtime/config/config.js";
|
|
12
|
+
import { runHooksCommand } from "./mcp/hooks/hook-cli.js";
|
|
15
13
|
import { executeSubagentSession } from "./subagents/sessions/execution.js";
|
|
16
14
|
import { SubagentDeliveryMailbox } from "./subagents/sessions/delivery-mailbox.js";
|
|
17
15
|
import { SubagentSessionManager } from "./subagents/sessions/manager.js";
|
|
18
16
|
import { formatSubagentProviderAvailabilitySummary } from "./subagents/providers/availability.js";
|
|
19
17
|
import { parseSubagentRunArgs } from "./subagents/cli-target.js";
|
|
20
|
-
import { ensureForgeRelayInstanceId, generateInstanceId, generateOwnerToken, loadForgeRelayFiles, removeForgeRelayRemote, renameForgeRelayRemote, resolveSubagentsFlag, writeForgeRelayAuth, writeForgeRelayConfig, writeForgeRelayRemote, } from "./user-config.js";
|
|
21
|
-
import { expandHomePath } from "./roots.js";
|
|
22
|
-
import { shutdownHttpServer } from "./server-shutdown.js";
|
|
23
|
-
import { publicEndpointUrl } from "./oauth/public-url.js";
|
|
24
|
-
import { authenticateRemote, defaultRemoteAlias, isRemoteMcpUnauthorized, normalizeRemoteServiceTarget, refreshRemoteAuthentication, verifyRemoteMcp, } from "./remote-auth.js";
|
|
25
|
-
import { defaultSshRouteAlias, parseSshRoute, readRemoteOwnerToken, withRemoteServiceEndpoint, } from "./remote-transport.js";
|
|
18
|
+
import { ensureForgeRelayInstanceId, generateInstanceId, generateOwnerToken, loadForgeRelayFiles, removeForgeRelayRemote, renameForgeRelayRemote, resolveSubagentsFlag, writeForgeRelayAuth, writeForgeRelayConfig, writeForgeRelayRemote, } from "./runtime/config/user-config.js";
|
|
19
|
+
import { expandHomePath } from "./mcp/filesystem/roots.js";
|
|
20
|
+
import { shutdownHttpServer } from "./mcp/server/transport/server-shutdown.js";
|
|
21
|
+
import { publicEndpointUrl } from "./mcp/oauth/public-url.js";
|
|
22
|
+
import { authenticateRemote, defaultRemoteAlias, isRemoteMcpUnauthorized, normalizeRemoteServiceTarget, refreshRemoteAuthentication, verifyRemoteMcp, } from "./workspaces/relay/auth/remote-auth.js";
|
|
23
|
+
import { defaultSshRouteAlias, parseSshRoute, readRemoteOwnerToken, withRemoteServiceEndpoint, } from "./workspaces/relay/transport/remote-transport.js";
|
|
24
|
+
import { normalizeOptionalPublicBaseUrl, normalizePublicBaseUrlsInput, compactPublicBaseUrlConfig, textPrompt, validatePort, validateRequiredPublicBaseUrls, assertSupportedNode, nodeVersionStatus, SetupCancelledError, checkSqliteNative, checkGitAvailable, checkBashShell } from "./cli/setup-support.js";
|
|
26
25
|
const require = createRequire(import.meta.url);
|
|
27
|
-
const SUPPORTED_NODE_RANGE = ">=20.12 <27";
|
|
28
26
|
async function main(argv) {
|
|
29
27
|
assertSupportedNode();
|
|
30
28
|
const [rawCommand, ...args] = argv;
|
|
@@ -74,11 +72,11 @@ function normalizeCommand(command) {
|
|
|
74
72
|
async function ensureConfigured() {
|
|
75
73
|
const files = loadForgeRelayFiles();
|
|
76
74
|
if (files.configExists && files.authExists) {
|
|
77
|
-
ensureForgeRelayInstanceId();
|
|
75
|
+
await ensureForgeRelayInstanceId();
|
|
78
76
|
return;
|
|
79
77
|
}
|
|
80
78
|
if (process.env.FORGERELAY_OAUTH_OWNER_TOKEN) {
|
|
81
|
-
ensureForgeRelayInstanceId();
|
|
79
|
+
await ensureForgeRelayInstanceId();
|
|
82
80
|
return;
|
|
83
81
|
}
|
|
84
82
|
if (!input.isTTY || !output.isTTY) {
|
|
@@ -156,7 +154,7 @@ async function runInit({ force }) {
|
|
|
156
154
|
instanceId: files.auth.instanceId ?? generateInstanceId(),
|
|
157
155
|
};
|
|
158
156
|
const configPath = writeForgeRelayConfig(config);
|
|
159
|
-
const authPath = writeForgeRelayAuth(auth);
|
|
157
|
+
const authPath = await writeForgeRelayAuth(auth);
|
|
160
158
|
const lines = [
|
|
161
159
|
`Config: ${configPath}`,
|
|
162
160
|
`Auth: ${authPath}`,
|
|
@@ -316,7 +314,7 @@ async function runAuthCommand(args) {
|
|
|
316
314
|
if (!fromAlias || !toAlias || extra.length > 0) {
|
|
317
315
|
throw new Error("Usage: forgerelay auth rename <old-alias> <new-alias>");
|
|
318
316
|
}
|
|
319
|
-
renameForgeRelayRemote(fromAlias, toAlias);
|
|
317
|
+
await renameForgeRelayRemote(fromAlias, toAlias);
|
|
320
318
|
console.log(`Renamed remote ${fromAlias} to ${toAlias}.`);
|
|
321
319
|
return;
|
|
322
320
|
}
|
|
@@ -324,7 +322,7 @@ async function runAuthCommand(args) {
|
|
|
324
322
|
const [alias, ...extra] = rest;
|
|
325
323
|
if (!alias || extra.length > 0)
|
|
326
324
|
throw new Error("Usage: forgerelay auth remove <alias>");
|
|
327
|
-
removeForgeRelayRemote(alias);
|
|
325
|
+
await removeForgeRelayRemote(alias);
|
|
328
326
|
console.log(`Removed remote ${alias}.`);
|
|
329
327
|
return;
|
|
330
328
|
}
|
|
@@ -341,7 +339,7 @@ async function runAuthCommand(args) {
|
|
|
341
339
|
let refreshed = false;
|
|
342
340
|
if (remote.accessTokenExpiresAt <= Math.floor(Date.now() / 1000)) {
|
|
343
341
|
remote = await refreshRemoteAuthentication(remote, endpoint);
|
|
344
|
-
writeForgeRelayRemote(alias, remote);
|
|
342
|
+
await writeForgeRelayRemote(alias, remote);
|
|
345
343
|
refreshed = true;
|
|
346
344
|
}
|
|
347
345
|
try {
|
|
@@ -351,7 +349,7 @@ async function runAuthCommand(args) {
|
|
|
351
349
|
if (refreshed || !isRemoteMcpUnauthorized(error))
|
|
352
350
|
throw error;
|
|
353
351
|
remote = await refreshRemoteAuthentication(remote, endpoint);
|
|
354
|
-
writeForgeRelayRemote(alias, remote);
|
|
352
|
+
await writeForgeRelayRemote(alias, remote);
|
|
355
353
|
await verifyRemoteMcp(remote, endpoint);
|
|
356
354
|
}
|
|
357
355
|
});
|
|
@@ -378,9 +376,9 @@ async function runAuthCommand(args) {
|
|
|
378
376
|
: defaultRemoteAlias(remote.target);
|
|
379
377
|
const alias = parsed.alias?.trim() || existingAlias || defaultAlias;
|
|
380
378
|
if (!files.auth.instanceId) {
|
|
381
|
-
ensureForgeRelayInstanceId();
|
|
379
|
+
await ensureForgeRelayInstanceId();
|
|
382
380
|
}
|
|
383
|
-
writeForgeRelayRemote(alias, remote);
|
|
381
|
+
await writeForgeRelayRemote(alias, remote);
|
|
384
382
|
console.log(`Authenticated remote ${alias} (${remote.instanceId}).`);
|
|
385
383
|
}
|
|
386
384
|
async function runDoctor() {
|
|
@@ -641,112 +639,6 @@ function printVersion() {
|
|
|
641
639
|
}
|
|
642
640
|
console.log(packageJson.version);
|
|
643
641
|
}
|
|
644
|
-
function isNullConfigValue(value) {
|
|
645
|
-
const normalized = value.trim().toLowerCase();
|
|
646
|
-
return !normalized || normalized === "null" || normalized === "none";
|
|
647
|
-
}
|
|
648
|
-
function normalizeOptionalPublicBaseUrl(value) {
|
|
649
|
-
const trimmed = value.trim();
|
|
650
|
-
if (isNullConfigValue(trimmed))
|
|
651
|
-
return null;
|
|
652
|
-
return compactPublicBaseUrlConfig(normalizePublicBaseUrlsInput(trimmed));
|
|
653
|
-
}
|
|
654
|
-
function normalizePublicBaseUrlsInput(value) {
|
|
655
|
-
const entries = value
|
|
656
|
-
.split(",")
|
|
657
|
-
.map((entry) => entry.trim())
|
|
658
|
-
.filter(Boolean);
|
|
659
|
-
if (entries.length === 0)
|
|
660
|
-
throw new Error("Enter at least one public base URL.");
|
|
661
|
-
return Array.from(new Set(entries.map((entry) => normalizePublicBaseUrl(entry))));
|
|
662
|
-
}
|
|
663
|
-
function compactPublicBaseUrlConfig(baseUrls) {
|
|
664
|
-
return baseUrls.length === 1 ? baseUrls[0] : baseUrls;
|
|
665
|
-
}
|
|
666
|
-
function normalizePublicBaseUrl(value) {
|
|
667
|
-
const trimmed = value.trim();
|
|
668
|
-
const parsed = new URL(trimmed);
|
|
669
|
-
parsed.hash = "";
|
|
670
|
-
parsed.search = "";
|
|
671
|
-
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
672
|
-
return parsed.toString().replace(/\/$/, "");
|
|
673
|
-
}
|
|
674
|
-
async function textPrompt(options) {
|
|
675
|
-
const result = await prompts.text({
|
|
676
|
-
...options,
|
|
677
|
-
validate: (value) => options.validate?.(value?.trim() ? value : options.defaultValue),
|
|
678
|
-
});
|
|
679
|
-
if (prompts.isCancel(result))
|
|
680
|
-
throw new SetupCancelledError();
|
|
681
|
-
const value = String(result).trim();
|
|
682
|
-
return value || options.defaultValue;
|
|
683
|
-
}
|
|
684
|
-
function validatePort(value) {
|
|
685
|
-
const port = Number(value);
|
|
686
|
-
return Number.isInteger(port) && port >= 1 && port <= 65535
|
|
687
|
-
? undefined
|
|
688
|
-
: "Enter a port between 1 and 65535.";
|
|
689
|
-
}
|
|
690
|
-
function validateRequiredPublicBaseUrls(value) {
|
|
691
|
-
const trimmed = value?.trim() ?? "";
|
|
692
|
-
if (!trimmed)
|
|
693
|
-
return "Enter at least one public base URL from your tunnel or reverse proxy.";
|
|
694
|
-
try {
|
|
695
|
-
normalizePublicBaseUrlsInput(trimmed);
|
|
696
|
-
return undefined;
|
|
697
|
-
}
|
|
698
|
-
catch (error) {
|
|
699
|
-
return error instanceof Error ? error.message : String(error);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
function assertSupportedNode() {
|
|
703
|
-
if (satisfies(process.versions.node, SUPPORTED_NODE_RANGE))
|
|
704
|
-
return;
|
|
705
|
-
throw new Error([
|
|
706
|
-
`ForgeRelay requires Node ${SUPPORTED_NODE_RANGE}.`,
|
|
707
|
-
`Current Node: ${process.version}`,
|
|
708
|
-
"",
|
|
709
|
-
"Install Node 22 LTS or use a version manager such as nvm, fnm, or mise.",
|
|
710
|
-
].join("\n"));
|
|
711
|
-
}
|
|
712
|
-
function nodeVersionStatus() {
|
|
713
|
-
return satisfies(process.versions.node, SUPPORTED_NODE_RANGE)
|
|
714
|
-
? `supported ${SUPPORTED_NODE_RANGE}`
|
|
715
|
-
: `unsupported, requires ${SUPPORTED_NODE_RANGE}`;
|
|
716
|
-
}
|
|
717
|
-
class SetupCancelledError extends Error {
|
|
718
|
-
}
|
|
719
|
-
function checkSqliteNative() {
|
|
720
|
-
try {
|
|
721
|
-
const Database = require("better-sqlite3");
|
|
722
|
-
const db = new Database(":memory:");
|
|
723
|
-
db.close();
|
|
724
|
-
return "ok";
|
|
725
|
-
}
|
|
726
|
-
catch (error) {
|
|
727
|
-
return error instanceof Error ? error.message : String(error);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
function checkGitAvailable() {
|
|
731
|
-
try {
|
|
732
|
-
const { execFileSync } = require("node:child_process");
|
|
733
|
-
return execFileSync("git", ["--version"], { encoding: "utf8" }).trim();
|
|
734
|
-
}
|
|
735
|
-
catch (error) {
|
|
736
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
737
|
-
return `unavailable (${message})`;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
function checkBashShell() {
|
|
741
|
-
try {
|
|
742
|
-
const { shell, args } = getShellConfig();
|
|
743
|
-
return `${shell} ${args.join(" ")}`;
|
|
744
|
-
}
|
|
745
|
-
catch (error) {
|
|
746
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
747
|
-
return `unavailable (${message})`;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
642
|
main(process.argv.slice(2)).catch((error) => {
|
|
751
643
|
console.error(error instanceof Error ? error.message : String(error));
|
|
752
644
|
process.exitCode = 1;
|
|
@@ -5,7 +5,7 @@ import { pathToFileURL } from "node:url";
|
|
|
5
5
|
import { createMessageConnection } from "vscode-jsonrpc/node";
|
|
6
6
|
import { DefinitionRequest, DocumentDiagnosticReportKind, DocumentDiagnosticRequest, DocumentSymbolRequest, ExitNotification, HoverRequest, InitializeRequest, ReferencesRequest, InitializedNotification, MarkupKind, PositionEncodingKind, PublishDiagnosticsNotification, ShutdownRequest, WorkspaceSymbolRequest, } from "vscode-languageserver-protocol";
|
|
7
7
|
import { LanguageServerConfigurationError, } from "./language-server-config.js";
|
|
8
|
-
import { terminateProcessTree } from "../process-platform.js";
|
|
8
|
+
import { terminateProcessTree } from "../mcp/process/process-platform.js";
|
|
9
9
|
import { CodeIntelligenceError } from "./code-intelligence-error.js";
|
|
10
10
|
import { DEFAULT_CODE_INTELLIGENCE_RESULT_LIMIT } from "./code-intelligence-types.js";
|
|
11
11
|
import { normalizeHoverContents } from "./normalization/hover.js";
|
|
@@ -4,16 +4,16 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
6
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
7
|
-
import { ActivityAuditStore } from "../../activity/audit-store.js";
|
|
8
|
-
import { BashOutputStore } from "../../activity/bash-output-store.js";
|
|
9
|
-
import { HostTurnStore } from "../../activity/host-turn-store.js";
|
|
10
|
-
import { ActivityLifecycle } from "../../activity/lifecycle.js";
|
|
11
|
-
import { ActivityQueryService } from "../../activity/query-service.js";
|
|
12
|
-
import { loadConfig } from "../../config.js";
|
|
13
|
-
import { createReviewCheckpointManager } from "../../review-checkpoints.js";
|
|
14
|
-
import { ProcessManager } from "../../process-sessions.js";
|
|
7
|
+
import { ActivityAuditStore } from "../../activity/history/audit-store.js";
|
|
8
|
+
import { BashOutputStore } from "../../activity/history/bash-output-store.js";
|
|
9
|
+
import { HostTurnStore } from "../../activity/history/host-turn-store.js";
|
|
10
|
+
import { ActivityLifecycle } from "../../activity/runtime/lifecycle.js";
|
|
11
|
+
import { ActivityQueryService } from "../../activity/history/query-service.js";
|
|
12
|
+
import { loadConfig } from "../../runtime/config/config.js";
|
|
13
|
+
import { createReviewCheckpointManager } from "../../workspaces/review/review-checkpoints.js";
|
|
14
|
+
import { ProcessManager } from "../../mcp/process/process-sessions.js";
|
|
15
15
|
import { createMcpServer } from "../../server.js";
|
|
16
|
-
import { SqliteWorkspaceStore } from "../../workspace-store.js";
|
|
16
|
+
import { SqliteWorkspaceStore } from "../../workspaces/state/workspace-store.js";
|
|
17
17
|
import { WorkspaceRegistry } from "../../workspaces.js";
|
|
18
18
|
import { CodeIntelligenceManager } from "../runtime/manager.js";
|
|
19
19
|
export async function createCodeIntelligenceServerFixture(t, options = {}) {
|