golutra-mcp 0.1.1 → 0.1.3

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/.env.example CHANGED
@@ -2,7 +2,11 @@
2
2
  # install paths for `golutra-cli`, then falls back to the platform command
3
3
  # name from PATH (`golutra-cli.exe` on Windows, `golutra-cli` elsewhere).
4
4
  GOLUTRA_CLI_PATH=golutra-cli
5
- GOLUTRA_PROFILE=stable
5
+ # Optional. If profile/host are unset, runtime search order is:
6
+ # stable:desktop, stable:server(web), dev:desktop, dev:server(web).
7
+ GOLUTRA_PROFILE=
8
+ GOLUTRA_CLI_HOST_KIND=
9
+ GOLUTRA_TARGET_ORDER=
6
10
  GOLUTRA_WORKSPACE_PATH=/absolute/path/to/workspace
7
11
  GOLUTRA_COMMAND_TIMEOUT_MS=30000
8
12
 
package/CHANGELOG.md CHANGED
@@ -6,6 +6,30 @@ The format follows Keep a Changelog, and this project uses Semantic Versioning.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.3] - 2026-05-01
10
+
11
+ ### Added
12
+
13
+ - Added focused MCP tools for Golutra team creation, member configuration, conversations, prompt settings, automation, skills, agents, templates, store commands, and generic structured command fallback.
14
+ - Added `golutra-read-cli-guide` so MCP hosts can read the current Golutra CLI team, collaboration, prompt, role, asset, and roadmap guidance directly.
15
+ - Added runtime target fallback for app-backed tools. MCP now tries `stable:desktop`, `stable:server`, `dev:desktop`, then `dev:server` by default, while still allowing explicit `profile`, `hostKind`, or `targetOrder` overrides.
16
+ - Added explicit confirmation fields for destructive or restart operations such as member deletion, conversation deletion, and member restart.
17
+
18
+ ### Changed
19
+
20
+ - Kept `golutra-cli` as the stable integration boundary and injects `GOLUTRA_CLI_HOST_KIND` only for each spawned CLI child process.
21
+ - Updated documentation, environment examples, generated `dist/`, and tests for the expanded tool surface and runtime selection behavior.
22
+
23
+ ## [0.1.2] - 2026-03-22
24
+
25
+ ### Changed
26
+
27
+ - Clarified the runtime `workspacePath` contract so per-tool `workspacePath` input is treated as a one-time override, while `golutra-set-context` remains the only way to persist a new default workspace
28
+ - Added regression tests to lock the workspace context behavior at both `ContextStore` level and workspace-aware tool level
29
+ - Expanded README and `STARTUP_PROCESS.md` so AI hosts can understand the recommended runtime flow for diagnostics, workspace switching, and normal tool execution
30
+ - Added `docs/WORKSPACE_CONTEXT_EXAMPLES.md` with concrete examples for stored defaults, one-off workspace overrides, recommended tool order, and a real chat flow
31
+ - Updated the diagnostic examples to explain that a `workspacePath` passed to `golutra-diagnose` affects only that call and does not mutate the stored default context
32
+
9
33
  ## [0.1.1] - 2026-03-21
10
34
 
11
35
  ### Changed
package/README.md CHANGED
@@ -1,8 +1,44 @@
1
- # golutra-mcp
2
-
3
- `golutra-mcp` is an MCP bridge project for Golutra.
4
-
5
- `golutra-mcp` 是一个面向 Golutra 的 MCP 桥接项目。
1
+ <p align="center">
2
+ <a href="https://www.golutra.com/" target="_blank" rel="noopener noreferrer">
3
+ <img
4
+ width="96"
5
+ src="./assets/readme/golutra-logo.png"
6
+ alt="golutra logo"
7
+ />
8
+ </a>
9
+ </p>
10
+
11
+ <h1 align="center">golutra-mcp</h1>
12
+
13
+ <p align="center">
14
+ MCP bridge for Golutra via golutra-cli. <br />
15
+ 通过 golutra-cli 暴露 Golutra 能力的 MCP 桥接层。
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="https://www.npmjs.com/package/golutra-mcp"><img src="https://img.shields.io/npm/v/golutra-mcp?label=npm" alt="npm version"></a>
20
+ <a href="https://www.npmjs.com/package/golutra-mcp"><img src="https://img.shields.io/npm/dm/golutra-mcp?label=downloads" alt="npm downloads"></a>
21
+ <a href="https://github.com/golutra/golutra-mcp/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/golutra/golutra-mcp/ci.yml?branch=main&label=ci" alt="ci"></a>
22
+ <a href="https://www.npmjs.com/package/golutra-mcp"><img src="https://img.shields.io/badge/node-%3E%3D20.11-2f7af8" alt="node version"></a>
23
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-ff9f1a" alt="license"></a>
24
+ </p>
25
+
26
+ <p align="center">
27
+ <a href="#english">English</a> ·
28
+ <a href="#中文">中文</a> ·
29
+ <a href="https://www.npmjs.com/package/golutra-mcp">npm</a> ·
30
+ <a href="https://github.com/golutra/golutra-mcp">GitHub</a> ·
31
+ <a href="./STARTUP_PROCESS.md">Startup Process</a> ·
32
+ <a href="./docs/WORKSPACE_CONTEXT_EXAMPLES.md">Workspace Examples</a> ·
33
+ <a href="./docs/GOLUTRA_DIAGNOSE_EXAMPLES.md">Diagnose Examples</a>
34
+ </p>
35
+
36
+ <p align="center">
37
+ Keep Golutra as the app runtime. Use golutra-cli as the stable boundary. Expose the workflow through MCP. <br />
38
+ 保留 Golutra 作为桌面运行时,以 golutra-cli 作为稳定边界,再通过 MCP 暴露给外部 AI 宿主。
39
+ </p>
40
+
41
+ ---
6
42
 
7
43
  ## English
8
44
 
@@ -49,7 +85,7 @@ golutra-mcp
49
85
 
50
86
  `golutra-mcp` is a Model Context Protocol server that exposes Golutra collaboration capabilities to MCP-compatible hosts through `golutra-cli`.
51
87
 
52
- It is not a replacement for Golutra itself, and it does not re-implement Golutra's local IPC protocol. Instead, it acts as a thin integration layer that lets external MCP clients reuse Golutra's existing chat, roadmap, and skill flows through a smaller and more stable boundary.
88
+ It is not a replacement for Golutra itself, and it does not re-implement Golutra's local IPC protocol. Instead, it acts as a thin integration layer that lets external MCP clients reuse Golutra's existing team-building, chat, roadmap, prompt, automation, and skill flows through a smaller and more stable boundary.
53
89
 
54
90
  ### What It Is Today
55
91
 
@@ -59,7 +95,7 @@ Today it already provides:
59
95
 
60
96
  - a local `stdio` MCP server
61
97
  - a command bridge based on `golutra-cli`
62
- - tool coverage for chat, roadmap, context, diagnostics, and skill discovery
98
+ - tool coverage for CLI guides, team config, terminal creation, member deletion, conversation topology, chat, roadmap, prompt settings, member bindings, automation, agents, templates, store commands, context, diagnostics, and skill discovery
63
99
  - skill validation and direct project `SKILL.md` reading for local Golutra skill workflows
64
100
  - an open-source project skeleton with contribution, security, CI, and release-facing metadata
65
101
 
@@ -83,7 +119,7 @@ To work correctly, the server still depends on the existing Golutra desktop runt
83
119
  - a locally installed Golutra desktop application
84
120
  - a reachable `golutra-cli` binary
85
121
  - a valid Golutra workspace path
86
- - the target Golutra desktop app profile running locally, usually `stable`
122
+ - the target Golutra app runtime running locally. By default MCP tries `stable` desktop, `stable` server/web, `dev` desktop, then `dev` server/web.
87
123
 
88
124
  Default CLI discovery by platform:
89
125
 
@@ -97,12 +133,32 @@ Use `golutra-diagnose` when you need to separate:
97
133
 
98
134
  - CLI path problems
99
135
  - missing or invalid workspace paths
100
- - missing user identity for app-backed probes
136
+ - workspace team config and app-backed command failures
101
137
  - app-not-running or profile-mismatch failures
102
138
  - generic app command failures
103
139
 
104
140
  Detailed diagnostic output fields and common `reasonCode` values are documented in `docs/GOLUTRA_DIAGNOSE_EXAMPLES.md`.
105
141
 
142
+ ### What An AI Host Should Do
143
+
144
+ If an AI system or MCP-compatible host sees this repository, the intended usage is:
145
+
146
+ 1. Launch `golutra-mcp` as a local `stdio` MCP server, not as an HTTP service.
147
+ 2. Provide `GOLUTRA_CLI_PATH` and usually `GOLUTRA_WORKSPACE_PATH`. Leave profile/host unset to use the default target order, or set `GOLUTRA_PROFILE`, `GOLUTRA_CLI_HOST_KIND`, or `GOLUTRA_TARGET_ORDER` when you need a specific runtime.
148
+ 3. Call `golutra-get-context` or `golutra-diagnose` first to confirm runtime readiness.
149
+ 4. Read the relevant guide before acting, for example `golutra-read-cli-guide` with `guide: "team"` before team creation.
150
+ 5. Use the structured team-building flow instead of inventing a custom transport:
151
+ `golutra-read-team-config` -> `golutra-list-terminal-defaults` / `golutra-create-terminals` / conversation tools / prompt tools / automation tools.
152
+ 6. For runtime collaboration after the team exists, use:
153
+ `golutra-list-conversations` -> `golutra-list-messages` / `golutra-send-message` / `golutra-read-roadmap` / `golutra-update-roadmap`.
154
+ 7. Treat `golutra-cli` as the stable boundary. Do not bypass this project by directly calling Golutra local socket IPC unless you intentionally want to maintain a separate integration layer.
155
+ 8. For destructive or restart tools, pass the explicit confirmation fields required by the tool schema, such as `confirmedMemberId` or `confirmedConversationId`.
156
+ 9. When switching workspaces for a single request, pass `workspacePath` on that tool call. Use `golutra-set-context` only when you want to persist a new default for later calls.
157
+
158
+ In short: start with diagnostics, read `golutra-read-team-config` for the workspace shape, keep workspace/profile/host explicit when needed, and use the provided MCP tools as the integration contract.
159
+
160
+ For concrete workspace switching and chat flow examples, see `docs/WORKSPACE_CONTEXT_EXAMPLES.md`.
161
+
106
162
  ### Future Direction
107
163
 
108
164
  The next stages should move the project from "basic bridge" to "usable integration product."
@@ -175,7 +231,7 @@ golutra-mcp
175
231
 
176
232
  `golutra-mcp` 是一个把 Golutra 能力暴露给 MCP 客户端的桥接层,底层通过 `golutra-cli` 与 Golutra 桌面端联通。
177
233
 
178
- 它不是 Golutra 本体,也不是对 Golutra 本地 IPC 协议的重写。这个仓库的职责,是把外部 MCP Host 和 Golutra 之间的集成边界收敛成一层更小、更稳定、更容易复用的适配层。
234
+ 它不是 Golutra 本体,也不是对 Golutra 本地 IPC 协议的重写。这个仓库的职责,是把外部 MCP Host 和 Golutra 之间的集成边界收敛成一层更小、更稳定、更容易复用的适配层,覆盖团队创建、聊天、路线图、提示词、自动化和技能等主要工作流。
179
235
 
180
236
  ### 当前阶段是什么
181
237
 
@@ -185,7 +241,7 @@ golutra-mcp
185
241
 
186
242
  - 基于 `stdio` 的 MCP Server 形态
187
243
  - 基于 `golutra-cli` 的命令桥接
188
- - chat、roadmap、context、diagnostics、skills 这些基础工具面
244
+ - CLI 指南、team config、terminal 创建、成员删除、频道/私聊拓扑、chat、roadmap、prompt settings、member bindings、automation、agents、templates、store commands、context、diagnostics、skills 等工具面
189
245
  - 技能校验与项目 `SKILL.md` 直接读取能力,能覆盖本地技能开发链路
190
246
  - 基本完整的开源项目骨架,包括贡献规范、安全策略、CI 和发布元数据
191
247
 
@@ -209,7 +265,7 @@ golutra-mcp
209
265
  - 本机已安装 Golutra 桌面应用
210
266
  - 能访问到 `golutra-cli`
211
267
  - 已知的有效工作区路径
212
- - 本地已启动目标 profile 的 Golutra 桌面应用,通常使用 `stable`
268
+ - 本地已启动目标 Golutra 运行实例。默认查找顺序是 `stable` 桌面端、`stable` server/web、`dev` 桌面端、`dev` server/web。
213
269
 
214
270
  按平台的默认 CLI 自动发现顺序:
215
271
 
@@ -223,12 +279,32 @@ golutra-mcp
223
279
 
224
280
  - CLI 路径问题
225
281
  - workspacePath 缺失或非法
226
- - userId 缺失导致 app 探针跳过
282
+ - 工作区团队配置读取与 app 侧命令失败
227
283
  - 桌面应用未运行或 profile 不匹配
228
284
  - 普通 app 命令失败
229
285
 
230
286
  更完整的诊断输出字段和常见 `reasonCode` 说明见 `docs/GOLUTRA_DIAGNOSE_EXAMPLES.md`。
231
287
 
288
+ ### AI 看到这个项目后应该怎么做
289
+
290
+ 如果一个 AI 系统或支持 MCP 的宿主看到这个仓库,推荐按下面方式接入:
291
+
292
+ 1. 把 `golutra-mcp` 当作本地 `stdio` MCP Server 启动,不要把它当成 HTTP 服务。
293
+ 2. 提供 `GOLUTRA_CLI_PATH`,通常还需要提供 `GOLUTRA_WORKSPACE_PATH`。profile/host 不填时使用默认查找顺序;需要固定运行实例时再设置 `GOLUTRA_PROFILE`、`GOLUTRA_CLI_HOST_KIND` 或 `GOLUTRA_TARGET_ORDER`。
294
+ 3. 先调用 `golutra-get-context` 或 `golutra-diagnose`,确认运行时上下文和联通状态。
295
+ 4. 执行前先读对应指南,例如创建团队前用 `golutra-read-cli-guide` 读取 `guide: "team"`。
296
+ 5. 创建团队时按标准工具链路走,不要自造传输协议:
297
+ `golutra-read-team-config` -> `golutra-list-terminal-defaults` / `golutra-create-terminals` / 会话拓扑工具 / 提示词工具 / 自动化工具。
298
+ 6. 团队创建完成后的运行期协作使用:
299
+ `golutra-list-conversations` -> `golutra-list-messages` / `golutra-send-message` / `golutra-read-roadmap` / `golutra-update-roadmap`。
300
+ 7. 把 `golutra-cli` 视为稳定边界。除非你明确准备长期维护另一套集成层,否则不要绕过这个项目去直连 Golutra 本地 socket IPC。
301
+ 8. 删除或重启类工具必须按 tool schema 传入显式确认字段,例如 `confirmedMemberId` 或 `confirmedConversationId`。
302
+ 9. 如果只是某一次调用切换工作区,就在该次 tool 调用里显式传 `workspacePath`。只有在你希望后续调用默认都改到新工作区时,才使用 `golutra-set-context`。
303
+
304
+ 一句话概括:先诊断,再用 `golutra-read-team-config` 读取工作区形态,必要时显式指定 workspace/profile/host,然后把 README 里提供的 MCP tools 当作正式集成契约来用。
305
+
306
+ 如果你想看“工作区切换”和“真实聊天链路”的具体调用示例,直接看 `docs/WORKSPACE_CONTEXT_EXAMPLES.md`。
307
+
232
308
  ### 后续规划
233
309
 
234
310
  后续项目应该从“能桥接”继续推进到“更好用、可发布、可维护”。
@@ -260,6 +336,8 @@ npm run dev
260
336
 
261
337
  - Startup, installation, validation, and MCP client wiring:
262
338
  [STARTUP_PROCESS.md](./STARTUP_PROCESS.md)
339
+ - Workspace override and stored default examples:
340
+ [docs/WORKSPACE_CONTEXT_EXAMPLES.md](./docs/WORKSPACE_CONTEXT_EXAMPLES.md)
263
341
  - Diagnostic output examples and reason codes:
264
342
  [docs/GOLUTRA_DIAGNOSE_EXAMPLES.md](./docs/GOLUTRA_DIAGNOSE_EXAMPLES.md)
265
343
  - Contribution guide:
@@ -47,6 +47,15 @@ This repository focuses on protocol translation, context management, diagnostics
47
47
  - `golutra-diagnose` also returns `summary.status` (`ok`, `partial`, `error`), `reasonCodes`, and `nextSteps`
48
48
  - Detailed output examples and common `reasonCode` values live in `docs/GOLUTRA_DIAGNOSE_EXAMPLES.md`
49
49
 
50
+ ### Workspace Override Contract
51
+
52
+ - Passing `workspacePath` in a tool call is a one-time override for that call only
53
+ - `golutra-set-context` is the explicit operation that persists a new default `workspacePath` for later calls
54
+ - If an AI host switches workspaces frequently, prefer passing `workspacePath` per call instead of mutating shared defaults
55
+ - 如果只是某一次调用临时切换工作区,直接在该次 tool 输入里传 `workspacePath`
56
+ - 如果希望后续调用默认都切到新工作区,再使用 `golutra-set-context`
57
+ - Concrete request sequences are documented in `docs/WORKSPACE_CONTEXT_EXAMPLES.md`
58
+
50
59
  ### Current Limitations
51
60
 
52
61
  - `workspacePath` must be provided either in the tool call or through `golutra-set-context`
@@ -82,6 +91,10 @@ It still needs more real-world validation across MCP hosts, operating systems, a
82
91
  Default: auto-discover a platform-specific `golutra-cli` install when available, otherwise fall back to the platform command name from `PATH`
83
92
  - `GOLUTRA_PROFILE`
84
93
  Optional Golutra runtime profile: `dev`, `canary`, or `stable`
94
+ - `GOLUTRA_CLI_HOST_KIND`
95
+ Optional Golutra runtime host: `desktop` or `server`; `web` maps to `server`
96
+ - `GOLUTRA_TARGET_ORDER`
97
+ Optional comma-separated runtime search order, for example `stable:desktop,stable:server,dev:desktop,dev:server`
85
98
  - `GOLUTRA_WORKSPACE_PATH`
86
99
  Optional default workspace path used by tools that require a workspace
87
100
  - `GOLUTRA_COMMAND_TIMEOUT_MS`
@@ -89,6 +102,13 @@ It still needs more real-world validation across MCP hosts, operating systems, a
89
102
 
90
103
  Use [.env.example](./.env.example) as the minimal local template.
91
104
 
105
+ When profile/host are not pinned, MCP tries runtime targets in this order:
106
+
107
+ - `stable:desktop`
108
+ - `stable:server` / web
109
+ - `dev:desktop`
110
+ - `dev:server` / web
111
+
92
112
  Default CLI discovery order:
93
113
 
94
114
  - macOS: `/Applications/Golutra.app/Contents/MacOS/golutra-cli`, `~/Applications/Golutra.app/Contents/MacOS/golutra-cli`, `golutra-cli`
@@ -140,6 +160,46 @@ export GOLUTRA_WORKSPACE_PATH=/absolute/path/to/workspace
140
160
  golutra-mcp
141
161
  ```
142
162
 
163
+ ## Quick Verification After Install
164
+
165
+ Use this sequence when you want to confirm that the published package and the local Golutra runtime are wired correctly.
166
+
167
+ 1. Confirm the package is visible from npm:
168
+
169
+ ```bash
170
+ npm view golutra-mcp version
171
+ ```
172
+
173
+ 2. Confirm the binary can start with your local Golutra runtime settings:
174
+
175
+ macOS/Linux:
176
+
177
+ ```bash
178
+ export GOLUTRA_CLI_PATH=/absolute/path/to/golutra-cli
179
+ export GOLUTRA_PROFILE=stable
180
+ export GOLUTRA_WORKSPACE_PATH=/absolute/path/to/workspace
181
+ golutra-mcp
182
+ ```
183
+
184
+ Windows PowerShell:
185
+
186
+ ```powershell
187
+ $env:GOLUTRA_CLI_PATH="C:\absolute\path\to\golutra-cli.exe"
188
+ $env:GOLUTRA_PROFILE="stable"
189
+ $env:GOLUTRA_WORKSPACE_PATH="C:\absolute\path\to\workspace"
190
+ golutra-mcp
191
+ ```
192
+
193
+ 3. If your MCP host can call tools immediately, run `golutra-get-context` or `golutra-diagnose`.
194
+
195
+ What to expect:
196
+
197
+ - `golutra-get-context` should return the resolved `cliPath`, `profile`, `workspacePath`, and `timeoutMs`
198
+ - `golutra-diagnose` should report `checks.cliPath.ok = true` and `checks.cliCommand.ok = true`
199
+ - If you also provide a valid workspace `userId`, `golutra-diagnose` should attempt the app-backed `chat.conversations.list` probe
200
+
201
+ If installation succeeds but runtime access still fails, use `golutra-diagnose` first and then compare the output with `docs/GOLUTRA_DIAGNOSE_EXAMPLES.md`.
202
+
143
203
  ## Local Development
144
204
 
145
205
  Start the MCP server directly from source:
@@ -225,6 +285,43 @@ npm pack --dry-run
225
285
 
226
286
  `prepack` is configured to build the project before npm packaging.
227
287
 
288
+ ## Publish To npm
289
+
290
+ Use this flow for a normal public npm release:
291
+
292
+ 1. Confirm you are in the repository root and that checks pass:
293
+
294
+ ```bash
295
+ cd /path/to/golutra-mcp
296
+ npm run check
297
+ npm pack
298
+ ```
299
+
300
+ 2. Confirm your npm CLI session is authenticated:
301
+
302
+ ```bash
303
+ npm whoami
304
+ ```
305
+
306
+ 3. Publish the package:
307
+
308
+ ```bash
309
+ npm publish
310
+ ```
311
+
312
+ 4. Verify the published result:
313
+
314
+ ```bash
315
+ npm view golutra-mcp version
316
+ npm view golutra-mcp
317
+ ```
318
+
319
+ Release notes:
320
+
321
+ - If `npm publish` reports that the version already exists, bump `package.json` to the next version and publish again
322
+ - If npm requires browser or OTP confirmation, complete that flow and wait for the CLI to print `+ golutra-mcp@<version>`
323
+ - `npm pack` creates a local `.tgz` artifact in the repository root; this is useful for pre-publish inspection or private file distribution
324
+
228
325
  ## Self-Hosted E2E CI
229
326
 
230
327
  The repository includes a dedicated GitHub Actions workflow for real MCP smoke tests on a macOS self-hosted runner:
Binary file
@@ -1,7 +1,10 @@
1
1
  import type { CliCommandRequest } from "./types.js";
2
2
  export interface CliJsonRunner {
3
3
  executeJson<T>(request: CliCommandRequest): Promise<T>;
4
+ executeText?(request: CliCommandRequest): Promise<string>;
4
5
  }
5
6
  export declare class NodeCliJsonRunner implements CliJsonRunner {
7
+ private executeRaw;
8
+ executeText(request: CliCommandRequest): Promise<string>;
6
9
  executeJson<T>(request: CliCommandRequest): Promise<T>;
7
10
  }
@@ -20,18 +20,21 @@ function extractErrorMessage(parsed) {
20
20
  return undefined;
21
21
  }
22
22
  export class NodeCliJsonRunner {
23
- async executeJson(request) {
23
+ async executeRaw(request) {
24
24
  const stdoutChunks = [];
25
25
  const stderrChunks = [];
26
26
  const child = spawn(request.cliPath, request.args, {
27
- env: process.env,
27
+ env: {
28
+ ...process.env,
29
+ ...request.env
30
+ },
28
31
  stdio: ["ignore", "pipe", "pipe"]
29
32
  });
30
33
  child.stdout.setEncoding("utf8");
31
34
  child.stderr.setEncoding("utf8");
32
35
  child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
33
36
  child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
34
- const completed = await new Promise((resolve, reject) => {
37
+ return new Promise((resolve, reject) => {
35
38
  const timer = setTimeout(() => {
36
39
  child.kill("SIGTERM");
37
40
  reject(new CliExecutionError({
@@ -63,6 +66,25 @@ export class NodeCliJsonRunner {
63
66
  });
64
67
  });
65
68
  });
69
+ }
70
+ async executeText(request) {
71
+ const completed = await this.executeRaw(request);
72
+ if (completed.exitCode !== 0) {
73
+ throw new CliExecutionError({
74
+ message: completed.stderr.trim() ||
75
+ completed.stdout.trim() ||
76
+ `golutra-cli exited with code ${completed.exitCode}`,
77
+ cliPath: request.cliPath,
78
+ args: request.args,
79
+ exitCode: completed.exitCode,
80
+ stdout: completed.stdout,
81
+ stderr: completed.stderr
82
+ });
83
+ }
84
+ return completed.stdout.trimEnd();
85
+ }
86
+ async executeJson(request) {
87
+ const completed = await this.executeRaw(request);
66
88
  const trimmedStdout = completed.stdout.trim();
67
89
  if (!trimmedStdout) {
68
90
  throw new CliExecutionError({
@@ -1 +1 @@
1
- {"version":3,"file":"cli-runner.js","sourceRoot":"","sources":["../../src/lib/cli-runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli-runner.js","sourceRoot":"","sources":["../../src/lib/cli-runner.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
- import type { CommandContextInput, RuntimeContextSnapshot } from "./types.js";
1
+ import type { CommandContextInput, GolutraRuntimeTarget, RuntimeContextSnapshot } from "./types.js";
2
+ export declare const DEFAULT_TARGET_ORDER: GolutraRuntimeTarget[];
3
+ export declare function resolveRuntimeTargets(context: RuntimeContextSnapshot): GolutraRuntimeTarget[];
2
4
  export declare function resolveDefaultCliPath(env: NodeJS.ProcessEnv, options?: {
3
5
  platform?: NodeJS.Platform;
4
6
  homeDirectory?: string;
@@ -11,6 +13,7 @@ export declare class ContextStore {
11
13
  constructor(initialContext: RuntimeContextSnapshot);
12
14
  getSnapshot(): RuntimeContextSnapshot;
13
15
  reset(): RuntimeContextSnapshot;
16
+ private mergeContext;
14
17
  update(nextValues: CommandContextInput): RuntimeContextSnapshot;
15
18
  resolveCommandContext(nextValues?: CommandContextInput): RuntimeContextSnapshot;
16
19
  requireWorkspacePath(nextValues?: CommandContextInput): string;
@@ -1,9 +1,15 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import path from "node:path";
4
- import { GOLUTRA_PROFILES } from "./types.js";
4
+ import { GOLUTRA_HOST_KINDS, GOLUTRA_PROFILES } from "./types.js";
5
5
  const DEFAULT_TIMEOUT_MS = 30_000;
6
6
  const MAX_TIMEOUT_MS = 300_000;
7
+ export const DEFAULT_TARGET_ORDER = [
8
+ { profile: "stable", hostKind: "desktop" },
9
+ { profile: "stable", hostKind: "server" },
10
+ { profile: "dev", hostKind: "desktop" },
11
+ { profile: "dev", hostKind: "server" }
12
+ ];
7
13
  function normalizeNonEmptyString(value) {
8
14
  const trimmed = value?.trim();
9
15
  return trimmed ? trimmed : undefined;
@@ -43,6 +49,72 @@ function normalizeProfile(value) {
43
49
  }
44
50
  throw new Error(`Unsupported Golutra profile: ${trimmed}`);
45
51
  }
52
+ function normalizeHostKind(value) {
53
+ const trimmed = normalizeNonEmptyString(value)?.toLowerCase();
54
+ if (!trimmed || trimmed === "auto") {
55
+ return undefined;
56
+ }
57
+ const normalized = trimmed === "web" ? "server" : trimmed;
58
+ if (GOLUTRA_HOST_KINDS.includes(normalized)) {
59
+ return normalized;
60
+ }
61
+ throw new Error(`Unsupported Golutra hostKind: ${trimmed}`);
62
+ }
63
+ function normalizeTargetOrder(value) {
64
+ if (Array.isArray(value)) {
65
+ const targets = value.map((target) => ({
66
+ profile: normalizeProfile(target.profile) ?? "stable",
67
+ hostKind: normalizeHostKind(target.hostKind) ?? "desktop"
68
+ }));
69
+ return targets.length > 0 ? dedupeTargets(targets) : undefined;
70
+ }
71
+ const trimmed = normalizeNonEmptyString(value);
72
+ if (!trimmed) {
73
+ return undefined;
74
+ }
75
+ const targets = trimmed.split(",").map((rawTarget) => {
76
+ const [rawProfile, rawHostKind] = rawTarget.split(":");
77
+ const profile = normalizeProfile(rawProfile);
78
+ const hostKind = normalizeHostKind(rawHostKind);
79
+ if (!profile || !hostKind) {
80
+ throw new Error("Unsupported Golutra targetOrder entry. Expected profile:hostKind, for example stable:desktop.");
81
+ }
82
+ return { profile, hostKind };
83
+ });
84
+ return targets.length > 0 ? dedupeTargets(targets) : undefined;
85
+ }
86
+ function dedupeTargets(targets) {
87
+ const seen = new Set();
88
+ const result = [];
89
+ for (const target of targets) {
90
+ const key = `${target.profile}:${target.hostKind}`;
91
+ if (seen.has(key)) {
92
+ continue;
93
+ }
94
+ seen.add(key);
95
+ result.push(target);
96
+ }
97
+ return result;
98
+ }
99
+ export function resolveRuntimeTargets(context) {
100
+ if (context.targetOrder && context.targetOrder.length > 0) {
101
+ return context.targetOrder;
102
+ }
103
+ const profiles = context.profile
104
+ ? [context.profile]
105
+ : DEFAULT_TARGET_ORDER.map((target) => target.profile);
106
+ const hostKinds = context.hostKind
107
+ ? [context.hostKind]
108
+ : DEFAULT_TARGET_ORDER.map((target) => target.hostKind);
109
+ const candidates = [];
110
+ for (const defaultTarget of DEFAULT_TARGET_ORDER) {
111
+ if (profiles.includes(defaultTarget.profile) &&
112
+ hostKinds.includes(defaultTarget.hostKind)) {
113
+ candidates.push(defaultTarget);
114
+ }
115
+ }
116
+ return dedupeTargets(candidates);
117
+ }
46
118
  function getDefaultCliCommand(platform) {
47
119
  return platform === "win32" ? "golutra-cli.exe" : "golutra-cli";
48
120
  }
@@ -101,12 +173,27 @@ export function resolveDefaultCliPath(env, options = {}) {
101
173
  return (candidates.find((candidatePath) => pathModule.isAbsolute(candidatePath) && pathExists(candidatePath)) ?? fallbackCommand);
102
174
  }
103
175
  export function createInitialContext(env) {
104
- return {
176
+ const context = {
105
177
  cliPath: resolveDefaultCliPath(env),
106
- profile: normalizeProfile(env.GOLUTRA_PROFILE),
107
- workspacePath: normalizeNonEmptyString(env.GOLUTRA_WORKSPACE_PATH),
108
178
  timeoutMs: normalizeTimeout(env.GOLUTRA_COMMAND_TIMEOUT_MS)
109
179
  };
180
+ const profile = normalizeProfile(env.GOLUTRA_PROFILE);
181
+ const hostKind = normalizeHostKind(env.GOLUTRA_CLI_HOST_KIND ?? env.GOLUTRA_HOST_KIND);
182
+ const targetOrder = normalizeTargetOrder(env.GOLUTRA_TARGET_ORDER);
183
+ const workspacePath = normalizeNonEmptyString(env.GOLUTRA_WORKSPACE_PATH);
184
+ if (profile) {
185
+ context.profile = profile;
186
+ }
187
+ if (hostKind) {
188
+ context.hostKind = hostKind;
189
+ }
190
+ if (targetOrder) {
191
+ context.targetOrder = targetOrder;
192
+ }
193
+ if (workspacePath) {
194
+ context.workspacePath = workspacePath;
195
+ }
196
+ return context;
110
197
  }
111
198
  export class ContextStore {
112
199
  initialContext;
@@ -122,29 +209,45 @@ export class ContextStore {
122
209
  this.context = { ...this.initialContext };
123
210
  return this.getSnapshot();
124
211
  }
125
- update(nextValues) {
212
+ mergeContext(baseContext, nextValues) {
126
213
  const nextContext = {
127
- cliPath: normalizeNonEmptyString(nextValues.cliPath) ?? this.context.cliPath,
128
- profile: nextValues.profile ?? this.context.profile,
129
- workspacePath: normalizeNonEmptyString(nextValues.workspacePath) ??
130
- this.context.workspacePath,
214
+ cliPath: normalizeNonEmptyString(nextValues.cliPath) ?? baseContext.cliPath,
131
215
  timeoutMs: typeof nextValues.timeoutMs === "number"
132
216
  ? normalizeTimeout(nextValues.timeoutMs)
133
- : this.context.timeoutMs
217
+ : baseContext.timeoutMs
134
218
  };
219
+ const profile = nextValues.profile ?? baseContext.profile;
220
+ const hostKind = nextValues.hostKind ?? baseContext.hostKind;
221
+ const targetOrder = nextValues.targetOrder !== undefined
222
+ ? normalizeTargetOrder(nextValues.targetOrder)
223
+ : nextValues.profile !== undefined || nextValues.hostKind !== undefined
224
+ ? undefined
225
+ : baseContext.targetOrder;
226
+ const workspacePath = normalizeNonEmptyString(nextValues.workspacePath) ??
227
+ baseContext.workspacePath;
228
+ if (profile) {
229
+ nextContext.profile = profile;
230
+ }
231
+ if (hostKind) {
232
+ nextContext.hostKind = hostKind;
233
+ }
234
+ if (targetOrder) {
235
+ nextContext.targetOrder = targetOrder;
236
+ }
237
+ if (workspacePath) {
238
+ nextContext.workspacePath = workspacePath;
239
+ }
240
+ return nextContext;
241
+ }
242
+ update(nextValues) {
243
+ // `golutra-set-context` 走这里,表示显式持久化更新默认上下文。
244
+ const nextContext = this.mergeContext(this.context, nextValues);
135
245
  this.context = nextContext;
136
246
  return this.getSnapshot();
137
247
  }
138
248
  resolveCommandContext(nextValues = {}) {
139
- return {
140
- cliPath: normalizeNonEmptyString(nextValues.cliPath) ?? this.context.cliPath,
141
- profile: nextValues.profile ?? this.context.profile,
142
- workspacePath: normalizeNonEmptyString(nextValues.workspacePath) ??
143
- this.context.workspacePath,
144
- timeoutMs: typeof nextValues.timeoutMs === "number"
145
- ? normalizeTimeout(nextValues.timeoutMs)
146
- : this.context.timeoutMs
147
- };
249
+ // 普通业务 tool 走这里时,`workspacePath` 只是本次调用覆盖,不会写回默认缓存。
250
+ return this.mergeContext(this.context, nextValues);
148
251
  }
149
252
  requireWorkspacePath(nextValues = {}) {
150
253
  const workspacePath = this.resolveCommandContext(nextValues).workspacePath;