@akira-tl/forgerelay 1.2.4 → 1.3.0

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +19 -11
  3. package/dist/cli/config/domains/context-cli.js +86 -0
  4. package/dist/cli/config/domains/domain-cli.js +468 -0
  5. package/dist/cli/config/general.js +174 -0
  6. package/dist/cli/config/inspect.js +29 -26
  7. package/dist/cli/config/migrate.js +11 -24
  8. package/dist/cli/config/scope.js +35 -0
  9. package/dist/cli/connect/relay.js +284 -0
  10. package/dist/cli/core/command-tree.js +68 -0
  11. package/dist/cli/core/serve-options.js +71 -0
  12. package/dist/cli/init/setup-config.js +26 -2
  13. package/dist/cli/init.js +37 -8
  14. package/dist/cli/maintenance-prune.js +1 -1
  15. package/dist/cli/maintenance.js +6 -6
  16. package/dist/cli/mcp/external-mcp.js +29 -17
  17. package/dist/cli/mcp/status.js +2 -2
  18. package/dist/cli/setup-support.js +3 -2
  19. package/dist/cli/system/status.js +35 -0
  20. package/dist/cli.js +132 -272
  21. package/dist/mcp/operations/external-mcp/external-mcp-oauth.js +2 -2
  22. package/dist/mcp/server/core/schemas.js +2 -10
  23. package/dist/mcp/server/operations/runtime/operation-runtime.js +11 -7
  24. package/dist/runtime/config/config.js +30 -29
  25. package/dist/runtime/config/definition/general-config.js +22 -6
  26. package/dist/runtime/config/external-mcp-config.js +4 -3
  27. package/dist/runtime/config/resolution/resolver.js +7 -4
  28. package/dist/runtime/config/user-config.js +9 -6
  29. package/dist/runtime/config/validation/paths.js +12 -0
  30. package/dist/runtime/config/validation/ports.js +9 -0
  31. package/dist/subagents/profiles.js +37 -0
  32. package/dist/workspaces/bootstrap.js +31 -14
  33. package/dist/workspaces/context.js +159 -7
  34. package/dist/workspaces/relay/auth/cli-test-support.js +22 -0
  35. package/dist/workspaces/resources/context-sources.js +29 -0
  36. package/dist/workspaces/resources/resource-monitor.js +29 -6
  37. package/dist/workspaces/resources/skills.js +15 -10
  38. package/dist/workspaces/sessions.js +4 -2
  39. package/dist/workspaces/state/project-context.js +34 -8
  40. package/dist/workspaces.js +5 -2
  41. package/docs/chatgpt-coding-workflow.md +23 -20
  42. package/docs/configuration.md +40 -27
  43. package/docs/gotchas.md +8 -6
  44. package/docs/roadmap.md +1 -1
  45. package/package.json +2 -2
  46. package/schemas/v1/config.project-local.schema.json +74 -0
  47. package/schemas/v1/config.project.schema.json +74 -0
  48. package/schemas/v1/config.user.schema.json +59 -4
  49. package/scripts/ci/config-v2-product-acceptance.mjs +17 -12
  50. package/scripts/debug/runtime.mjs +19 -3
  51. package/scripts/debug/runtime.test.mjs +3 -0
  52. package/scripts/debug/serve.mjs +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,38 @@ All notable ForgeRelay changes are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.3.0] - 2026-09-18
8
+
9
+ ### Added
10
+
11
+ - 新的 domain-first CLI 将运行职责收敛到 `serve`、`init`、`config`、`connect` 与 `system`:`config` 统一 General / Agent Context / External MCP / Hooks / Language Servers / Subagent Profiles 的读取、修改与 provenance 操作,`connect relay` / `connect mcp` 负责运行时连接关系,`system` 负责诊断与维护。
12
+ - `serve` 支持仅当前进程生效的 host、port、allowed root 与 public URL override,不会把调试或多实例参数写回持久配置。
13
+ - Agent context source 现在是 Config System v2 的正式配置:`systemInstructionsPath`、`instructionNames` 与 `skillPaths` 支持 runtime / Project Local / Project / User / built-in precedence、source inspection 和 live refresh。
14
+
15
+ ### Changed
16
+
17
+ - 裸 `forgerelay` 现在只显示帮助,不再隐式启动服务器;脚本和服务启动应显式使用 `forgerelay serve`。旧 `start`、`hooks`、`auth`、`mcp`、`maintenance` 与 `agents` 路由继续作为不出现在公共 help 中的兼容入口。
18
+ - Agent context built-in defaults 收敛为 `~/.agents/AGENTS.md`、Project instruction basename `AGENTS.md`,以及 `~/.agents/skills` / `./.agents/skills`;ForgeRelay 不再隐式维护 `~/.forgerelay/skills` 或 Project `.forgerelay/skills` 作为 Agent Skill 默认来源。
19
+ - ForgeRelay-owned legacy Config source 继续兼容读取,但 1.3 的 deprecation diagnostic 现在明确显示计划在 `1.4.0` 移除,并继续提供 canonical replacement 路径。
20
+
21
+ ### Fixed
22
+
23
+ - `forgerelay serve --help` 现在返回专用 serve usage/options,而不是把 `--help` 当成未知启动参数;`system status` 在尚未运行 `init`、没有 Owner credential 的全新环境里也保持只读并报告 `Instance: not initialized` / `Runtime: not running`。
24
+ - `config` domain adapters 现在统一使用 stable resource names、原子 schema-valid mutation 与一致的 Project / global scope 语义;Subagent Profile 名称与各 domain `get` / `set` / `unset` / `remove` contract 保持一致。
25
+ - 带 public base path 的交互式 debug server 现在打印真实 mounted health URL,例如 `/forgerelay/debug/healthz`,而不是错误的 root `/healthz`。
26
+
27
+ ## [1.2.5] - 2026-09-16
28
+
29
+ ### Changed
30
+
31
+ - Config v2 现在把监听端口的支持范围统一为 `1`–`65535`,并让 JSON Schema、环境变量、`init --advanced`、持久化写入、迁移与运行时使用同一验证约束;External MCP OAuth `callbackPort` 继续保持独立的 `1024`–`65535` 范围。
32
+ - 初始化只持久化用户真正选择的网络 override;HTTPS proxy 场景不再重复写入可从 loopback bind 与 public URL 安全推导的 `trustedProxies: ["loopback"]`。
33
+
34
+ ### Fixed
35
+
36
+ - `config.json` 写入与 legacy migration 现在会在持久化前通过 Config v2 schema 校验,避免 CLI 或迁移先写出运行时随后拒绝的非法配置;迁移遇到非法 General Config 时会在创建 canonical target 或 backup 之前失败。
37
+ - 明确 `host` / `port`、`publicBaseUrl`、`allowedHosts` 与 `trustedProxies` 的配置权威关系:显式用户值始终优先,派生值只在对应 override 缺失时生效。
38
+
7
39
  ## [1.2.4] - 2026-09-15
8
40
 
9
41
  ### Added
package/README.md CHANGED
@@ -64,7 +64,7 @@ forgerelay config sources
64
64
  forgerelay config explain <logical-path>
65
65
  ```
66
66
 
67
- v1.2 的 Config System 统一解析 User、Project 和机器私有 Project Local 配置,统一优先级是 `runtime > project-local > project > user > built-in`。External MCP、Language Servers、Hooks 和 Subagent Profiles 支持按需热刷新与 last-known-good;需要重启的 General 配置会区分 configured / applied,不会自动重启 Server。Canonical JSON 可以引用 npm package 中的 `schemas/v1/*` `$schema`,受支持的敏感字段可用 `${ENV_NAME}`,诊断不会输出解析后的 secretForgeRelay 启动不会自动重写旧配置,需要迁移时使用 `forgerelay config migrate --dry-run ...` 先预览。
67
+ ForgeRelay 的 Config System 统一解析 User、Project 和机器私有 Project Local 配置,优先级是 `runtime > project-local > project > user > built-in`。External MCP、Language Servers、Hooks 和 Subagent Profiles 支持按需刷新与 last-known-good;启动不会自动重写旧配置。需要深入配置、迁移或排查来源时,直接看 [配置指南](https://github.com/Akira-TL/forgerelay/wiki/Configuration)
68
68
 
69
69
  ### ChatGPT 访问不到 localhost?
70
70
 
@@ -100,9 +100,9 @@ bash(command="npm test")
100
100
  bash(command="git status --short")
101
101
  ```
102
102
 
103
- ForgeRelay 不会默认为每个任务创建 worktree。只有你明确要求隔离或并行开发时,才使用 managed worktree。
103
+ ForgeRelay 不会默认为每个任务创建 worktree。只有你明确要求隔离或并行开发时,才使用 managed worktree。Worktree 可以从本地 branch、tag 或历史 commit SHA 启动,同时把最终集成的本地 `targetBranch` 单独记录;不需要为了历史基线先造一个临时 branch。
104
104
 
105
- 长命令也不会要求 Agent 高频轮询。命令超过当前等待窗口时会返回稳定的 `processId`,后续继续等待或中断同一个进程即可。
105
+ 长命令也不会要求 Agent 高频轮询。命令超过当前等待窗口时会返回稳定的 `processId`,后续继续等待、交互或中断同一个进程即可。
106
106
 
107
107
  ## 主要能力
108
108
 
@@ -110,8 +110,9 @@ ForgeRelay 不会默认为每个任务创建 worktree。只有你明确要求隔
110
110
  - Linux / macOS 可以用 Bash、zsh、POSIX sh;Windows 原生支持 PowerShell 7、Windows PowerShell 5.1 和 `cmd.exe`。
111
111
  - 同一个 checkout 会保留自己的 Workspace 身份。换一次对话,不需要重新创建工作区。
112
112
  - `code.intelligence` 可以查 definition、hover、references、symbols 和 diagnostics。
113
- - 项目里的 `AGENTS.md`、`CLAUDE.md` Agent Skills 按需加载,不会每次都把整套说明重新塞进上下文。
114
- - 需要并行开发时可以创建真实 Git worktree;集成回主分支时只接受安全的 fast-forward,不自动制造 merge conflict。
113
+ - Agent 上下文来源走 Config v2:默认 system instructions 是 `~/.agents/AGENTS.md`,项目指令默认只匹配 `AGENTS.md`,Skills 默认按顺序扫描 `~/.agents/skills` 与项目 `./.agents/skills`。项目/User/Project Local/runtime 都可以用 replace semantics 选择其他通用 Agent 目录;ForgeRelay 不再隐式维护 `.forgerelay/skills` 来源。
114
+ - 需要并行开发时可以创建真实 Git worktree;起始 commit 与最终 `targetBranch` 独立,集成仍只接受安全的 fast-forward,不自动制造 merge conflict。
115
+ - 支持 MCP Apps 的 Host 会显示 Workspace Summary / Activity Panel;Workspace 身份色在 Panel 与 pretty log 中保持一致,长路径会在详情区正常换行。
115
116
  - Workspace Relay 可以把执行放到另一台 ForgeRelay;Composite Workspace 可以同时协调几个独立环境。
116
117
  - `read` 可以直接把 PNG、JPEG、WebP 和 GIF 作为临时 MCP Media content 返回;外部 MCP 通过独立的 global/Project `mcp.json` 热加载到 `mcp.external` Capability,并支持人工 OAuth 认证,而不会自动打开它返回的路径或 URL。
117
118
 
@@ -125,7 +126,7 @@ ForgeRelay 给 Agent 的是真实本机执行权限,不是模拟环境。
125
126
 
126
127
  文件工具受 Workspace 和 allowed roots 限制;Shell 命令使用启动 ForgeRelay 的本地用户权限执行,**Shell 不是 OS sandbox**。因此只连接你信任的 MCP Host,只开放确实需要的项目目录,并保护好 Owner password。
127
128
 
128
- Allowed roots 是文件系统 / Workspace authority,不等于 Project Trust approval。v1.2.0 已把项目中的可执行配置统一接入 Project execution trust seam,但首个稳定策略是 compatibility-allow;完整的交互式 Project Trust approval UI 不是 v1.2.0 已实现功能。
129
+ Allowed roots 是文件系统 / Workspace authority,不等于 Project Trust approval。项目中的可执行配置已经统一接入 Project execution trust seam;当前稳定策略仍是 compatibility-allow,完整的交互式 Project Trust approval UI 尚未实现。
129
130
 
130
131
  ForgeRelay 默认拒绝 elevated / administrator 启动。只有你显式选择高权限运行时才会继续,并会提示系统级修改可能不可逆。
131
132
 
@@ -135,6 +136,8 @@ ForgeRelay 默认拒绝 elevated / administrator 启动。只有你显式选择
135
136
 
136
137
  - [快速开始](https://github.com/Akira-TL/forgerelay/wiki/Getting-Started)
137
138
  - [配置](https://github.com/Akira-TL/forgerelay/wiki/Configuration)
139
+ - [Managed Worktree](https://github.com/Akira-TL/forgerelay/wiki/Managed-Worktrees)
140
+ - [ChatGPT 与 MCP 工作流](https://github.com/Akira-TL/forgerelay/wiki/ChatGPT-and-MCP-Workflow)
138
141
  - [External MCP](https://github.com/Akira-TL/forgerelay/wiki/External-MCP)
139
142
  - [安全模型](https://github.com/Akira-TL/forgerelay/wiki/Security)
140
143
  - [故障排查](https://github.com/Akira-TL/forgerelay/wiki/Troubleshooting)
@@ -185,7 +188,7 @@ forgerelay config sources
185
188
  forgerelay config explain <logical-path>
186
189
  ```
187
190
 
188
- Config System v2 in v1.2 resolves User, Project, and machine-private Project Local sources using `runtime > project-local > project > user > built-in`. External MCP, Language Servers, Hooks, and Subagent Profiles refresh on demand with last-known-good protection; restart-required General settings keep configured and applied values distinct and never auto-restart the server. Canonical JSON can point at the packaged `schemas/v1/*` `$schema` files, supported sensitive fields may use `${ENV_NAME}`, and diagnostics do not expose resolved secrets. Startup never rewrites legacy configuration automatically; preview an explicit migration with `forgerelay config migrate --dry-run ...` first.
191
+ ForgeRelay's Config System resolves User, Project, and machine-private Project Local sources using `runtime > project-local > project > user > built-in`. External MCP, Language Servers, Hooks, and Subagent Profiles refresh on demand with last-known-good protection, and startup never rewrites legacy configuration automatically. See [Configuration](https://github.com/Akira-TL/forgerelay/wiki/Configuration) for scopes, migration, schemas, and diagnostics.
189
192
 
190
193
  ### Host cannot reach localhost?
191
194
 
@@ -221,7 +224,7 @@ bash(command="npm test")
221
224
  bash(command="git status --short")
222
225
  ```
223
226
 
224
- ForgeRelay does not create a worktree for every task. Managed worktrees are for cases where you explicitly want isolation or parallel development.
227
+ ForgeRelay does not create a worktree for every task. Managed worktrees are for cases where you explicitly want isolation or parallel development. A worktree may start from a local branch, tag, or historical commit SHA while keeping the eventual local `targetBranch` separate, so a temporary baseline branch is unnecessary.
225
228
 
226
229
  Long commands do not require tight polling either. Once the current wait window expires, ForgeRelay returns a stable `processId`; later calls wait on, interact with, or interrupt that same process.
227
230
 
@@ -231,8 +234,9 @@ Long commands do not require tight polling either. Once the current wait window
231
234
  - Linux/macOS can use Bash, zsh, or POSIX sh. Windows has native PowerShell 7, Windows PowerShell 5.1, and `cmd.exe` support.
232
235
  - Reopening the same checkout reuses the same Workspace identity instead of creating another one for every conversation.
233
236
  - `code.intelligence` provides definition, hover, references, symbols, and diagnostics.
234
- - `AGENTS.md`, `CLAUDE.md`, and Agent Skills are loaded as needed instead of being resent in full on every open.
235
- - Managed worktrees provide real Git isolation when you ask for parallel work, with fast-forward-only finalization.
237
+ - Agent context sources use Config v2: the default system instruction is `~/.agents/AGENTS.md`, project instructions match only `AGENTS.md` by default, and Skills scan `~/.agents/skills` then project `./.agents/skills`. Project/User/Project Local/runtime config can replace those generic sources; ForgeRelay no longer implicitly owns `.forgerelay/skills` sources.
238
+ - Managed worktrees provide real Git isolation when you ask for parallel work. Starting commit and local `targetBranch` are tracked independently, while finalization remains fast-forward-only.
239
+ - MCP Apps-capable hosts can render the Workspace Summary / Activity Panel. Workspace identity colors stay consistent between the panel and pretty logs, and long paths wrap in detail rows instead of hiding their tail.
236
240
  - Workspace Relay runs work on another ForgeRelay instance; Composite Workspaces coordinate several independent environments from one Host.
237
241
  - `read` can return PNG, JPEG, WebP, and GIF directly as transient MCP Media content; external MCP servers hot-reload from standalone global/Project `mcp.json` files into the `mcp.external` Capability, with explicit human OAuth when needed and no automatic dereferencing of returned paths or URLs.
238
242
 
@@ -246,7 +250,7 @@ ForgeRelay gives an Agent real local execution capability.
246
250
 
247
251
  Filesystem tools are constrained by the opened Workspace and configured allowed roots. Shell commands run with the authority of the local user running ForgeRelay; **the shell is not an OS sandbox**. Connect only MCP hosts you trust, expose only project roots you want an Agent to access, and keep the Owner password private.
248
252
 
249
- Allowed roots grant filesystem/Workspace authority; they are not a Project Trust approval. v1.2.0 routes executable project configuration through a shared Project execution trust seam, but its initial policy is compatibility-allow. A full interactive Project Trust approval UI is not claimed for v1.2.0.
253
+ Allowed roots grant filesystem/Workspace authority; they are not a Project Trust approval. Executable project configuration is routed through a shared Project execution trust seam; the current stable policy remains compatibility-allow, and a full interactive Project Trust approval UI is not implemented yet.
250
254
 
251
255
  Elevated / administrator startup is rejected by default. It only proceeds after explicit opt-in, with a warning that system-level AI-driven changes may be irreversible.
252
256
 
@@ -256,6 +260,8 @@ See the [Security model](https://github.com/Akira-TL/forgerelay/wiki/Security) f
256
260
 
257
261
  - [Getting Started](https://github.com/Akira-TL/forgerelay/wiki/Getting-Started)
258
262
  - [Configuration](https://github.com/Akira-TL/forgerelay/wiki/Configuration)
263
+ - [Managed Worktrees](https://github.com/Akira-TL/forgerelay/wiki/Managed-Worktrees)
264
+ - [ChatGPT and MCP Workflow](https://github.com/Akira-TL/forgerelay/wiki/ChatGPT-and-MCP-Workflow)
259
265
  - [External MCP](https://github.com/Akira-TL/forgerelay/wiki/External-MCP)
260
266
  - [Security model](https://github.com/Akira-TL/forgerelay/wiki/Security)
261
267
  - [Troubleshooting](https://github.com/Akira-TL/forgerelay/wiki/Troubleshooting)
@@ -276,3 +282,5 @@ npm run build
276
282
  ```
277
283
 
278
284
  `npm run dev` uses the 7677 debug runtime. Product port 7676 is kept separate from development acceptance.
285
+
286
+ Release tags are created only after a developer has manually exercised and explicitly accepted the exact release-ready state. Automated verification and the Linux/macOS/Windows release matrix remain additional gates; they do not replace manual acceptance.
@@ -0,0 +1,86 @@
1
+ import { assertConfigResolutionValid } from "../../../runtime/config/resolution/resolver.js";
2
+ import { resolveGeneralConfigForScope, runGeneralConfigSet, runGeneralConfigUnset, } from "../general.js";
3
+ import { runConfigInspection } from "../inspect.js";
4
+ import { parseConfigScopeArgs } from "../scope.js";
5
+ const CONTEXT_FIELDS = [
6
+ "systemInstructionsPath",
7
+ "instructionNames",
8
+ "skillPaths",
9
+ ];
10
+ const CONTEXT_LOGICAL_PATHS = new Set(CONTEXT_FIELDS.map((field) => `config.${field}`));
11
+ export async function runConfigContextCommand(args) {
12
+ const [command, ...rest] = args;
13
+ if (!command || command === "help" || command === "--help" || command === "-h") {
14
+ console.log(renderConfigContextHelp());
15
+ return;
16
+ }
17
+ if (command === "get") {
18
+ const parsed = parseConfigScopeArgs(rest);
19
+ if (parsed.rest.length > 0)
20
+ throw new Error(`Unknown config context get option: ${parsed.rest[0]}`);
21
+ const resolution = await resolveGeneralConfigForScope(parsed.scope);
22
+ assertConfigResolutionValid(resolution);
23
+ const output = Object.fromEntries(CONTEXT_FIELDS.map((field) => {
24
+ const entry = resolution.entries[field];
25
+ if (!entry)
26
+ throw new Error(`Missing General Config context field: config.${field}.`);
27
+ return [field, entry.effective.effectiveValue];
28
+ }));
29
+ console.log(JSON.stringify(output, null, 2));
30
+ return;
31
+ }
32
+ if (command === "set") {
33
+ const parsed = parseConfigScopeArgs(rest);
34
+ if (parsed.rest.length < 2) {
35
+ throw new Error("Usage: forgerelay config context set <field> <value> [--project <path>|--global]");
36
+ }
37
+ const field = normalizeContextField(parsed.rest[0]);
38
+ await runGeneralConfigSet([field, ...parsed.rest.slice(1), ...scopeArgs(parsed.scope)]);
39
+ return;
40
+ }
41
+ if (command === "unset") {
42
+ const parsed = parseConfigScopeArgs(rest);
43
+ if (parsed.rest.length !== 1) {
44
+ throw new Error("Usage: forgerelay config context unset <field> [--project <path>|--global]");
45
+ }
46
+ const field = normalizeContextField(parsed.rest[0]);
47
+ await runGeneralConfigUnset([field, ...scopeArgs(parsed.scope)]);
48
+ return;
49
+ }
50
+ if (command === "check" || command === "sources") {
51
+ process.exitCode = await runConfigInspection([command, ...rest], "config", CONTEXT_LOGICAL_PATHS);
52
+ return;
53
+ }
54
+ if (command === "explain") {
55
+ const parsed = parseConfigScopeArgs(rest);
56
+ if (parsed.rest.length < 1) {
57
+ throw new Error("Usage: forgerelay config context explain <field> [--project <path>|--global] [--json]");
58
+ }
59
+ const field = normalizeContextField(parsed.rest[0]);
60
+ process.exitCode = await runConfigInspection(["explain", `config.${field}`, ...parsed.rest.slice(1), ...scopeArgs(parsed.scope)], "config", CONTEXT_LOGICAL_PATHS);
61
+ return;
62
+ }
63
+ throw new Error(`Unknown config context command: ${command}`);
64
+ }
65
+ function normalizeContextField(value) {
66
+ const field = value.startsWith("config.") ? value.slice("config.".length) : value;
67
+ if (CONTEXT_FIELDS.includes(field))
68
+ return field;
69
+ throw new Error(`Unknown config context field: ${value}. Expected ${CONTEXT_FIELDS.join(", ")}.`);
70
+ }
71
+ function scopeArgs(scope) {
72
+ return scope.mode === "global" ? ["--global"] : ["--project", scope.projectRoot];
73
+ }
74
+ function renderConfigContextHelp() {
75
+ return [
76
+ "ForgeRelay config context",
77
+ "",
78
+ "Usage:",
79
+ " forgerelay config context get [--project <path>|--global]",
80
+ " forgerelay config context set <systemInstructionsPath|instructionNames|skillPaths> <value> [--project <path>|--global]",
81
+ " forgerelay config context unset <systemInstructionsPath|instructionNames|skillPaths> [--project <path>|--global]",
82
+ " forgerelay config context check [--project <path>|--global] [--json]",
83
+ " forgerelay config context sources [--project <path>|--global] [--json]",
84
+ " forgerelay config context explain <systemInstructionsPath|instructionNames|skillPaths> [--project <path>|--global] [--json]",
85
+ ].join("\n");
86
+ }