aiterm-mcp 0.44.0 → 0.44.1
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 +8 -1
- package/README.ja.md +1 -1
- package/README.md +1 -1
- package/dist/agent-resolver.js +68 -6
- package/dist/harnesses/claude.js +5 -6
- package/dist/harnesses/grok.js +5 -5
- package/dist/tmux-runtime.js +7 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.44.1] - 2026-09-30
|
|
11
|
+
|
|
12
|
+
### 修正
|
|
13
|
+
|
|
14
|
+
- `agent_models`のGrokが、ときどき`MODEL_CATALOG_INVALID: initializeの応答がありません(exit=0 … stdout=0bytes)`で失敗していた。要求を書いてすぐ標準入力を閉じていたため、grok 1.0.41がinitializeの処理中に入力の終わりを受けて、応答せずに終わっていた。ClaudeとGrokは、応答が出力に現れるまで標準入力を開けておき、現れてから閉じる。macOSのAqua外(launchd経由)でも同じ待ち方をする。
|
|
15
|
+
|
|
10
16
|
## [0.44.0] - 2026-09-30
|
|
11
17
|
|
|
12
18
|
### 追加
|
|
@@ -1918,7 +1924,8 @@ prototype (preserved under `prototype/python/` as the porting source and referen
|
|
|
1918
1924
|
`ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
|
|
1919
1925
|
provenance.
|
|
1920
1926
|
|
|
1921
|
-
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.44.
|
|
1927
|
+
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.44.1...HEAD
|
|
1928
|
+
[0.44.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.44.0...v0.44.1
|
|
1922
1929
|
[0.44.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.43.7...v0.44.0
|
|
1923
1930
|
[0.43.7]: https://github.com/kitepon/aiterm-mcp/compare/v0.43.6...v0.43.7
|
|
1924
1931
|
[0.43.6]: https://github.com/kitepon/aiterm-mcp/compare/v0.43.5...v0.43.6
|
package/README.ja.md
CHANGED
|
@@ -203,7 +203,7 @@ runtime-error store は canonical dotagents config の `collection.enabled: true
|
|
|
203
203
|
場合だけ収集し、既定OFF、network送信は行いません。tag起点CIのnpm provenance(OIDC Trusted
|
|
204
204
|
Publishing)で公開し、GitHub Release が Official MCP Registry を再登録します。
|
|
205
205
|
|
|
206
|
-
**状態:** 開発継続中 · 現行公開版 **v0.44.
|
|
206
|
+
**状態:** 開発継続中 · 現行公開版 **v0.44.1** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
|
|
207
207
|
|
|
208
208
|
### 更新と巻き戻し
|
|
209
209
|
|
package/README.md
CHANGED
|
@@ -217,7 +217,7 @@ collection is off by default and performs no network I/O. It ships via
|
|
|
217
217
|
tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
|
|
218
218
|
Release re-registers the Official MCP Registry entry.
|
|
219
219
|
|
|
220
|
-
**Status:** actively maintained · current public release **v0.44.
|
|
220
|
+
**Status:** actively maintained · current public release **v0.44.1** · runs on Linux · WSL2 · macOS · native Windows (tmux on POSIX, the tmux-CLI-compatible [psmux](https://github.com/psmux/psmux) on native Windows — no WSL required) · MIT · see the [CHANGELOG](CHANGELOG.md).
|
|
221
221
|
|
|
222
222
|
### Update and rollback
|
|
223
223
|
|
package/dist/agent-resolver.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// エージェント CLI(claude / codex / grok / cursor-agent)・Throughline・pane shell(Windows は Git Bash)の
|
|
2
2
|
// 実行ファイルをどう見つけ、どう起動するかの所有者(OS 分岐の所有者。tmux とは独立)。
|
|
3
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import * as os from "node:os";
|
|
@@ -75,11 +75,7 @@ export function spawnAgentControlCommand(bin, args, _cwd, options) {
|
|
|
75
75
|
if (/\.(?:cmd|bat)$/i.test(bin)) {
|
|
76
76
|
// Node は CVE-2024-27980 対処以降、.cmd/.bat の直接 spawn を EINVAL で拒否する。
|
|
77
77
|
// 受入が .cmd/.bat を許す以上、control 経路は shell 経由で実行する(args は固定語彙とAiterm所有の一時path)。
|
|
78
|
-
|
|
79
|
-
if (args.some(arg => /["%^&|<>!]/.test(arg))) {
|
|
80
|
-
throw new AitermError(`${path.basename(bin)} のcontrol commandへ渡せない文字を含む引数があります`, 2);
|
|
81
|
-
}
|
|
82
|
-
return spawnSync(bin, args.map(arg => /\s/.test(arg) ? `"${arg}"` : arg), { ...options, shell: true });
|
|
78
|
+
return spawnSync(bin, windowsControlArgs(bin, args), { ...options, shell: true });
|
|
83
79
|
}
|
|
84
80
|
// 受入は pane shell(Git Bash)が shebang で実行できる script も許す。Windows の
|
|
85
81
|
// CreateProcess は shebang を解さないため、control command も同じ Git Bash で実行する。
|
|
@@ -88,6 +84,72 @@ export function spawnAgentControlCommand(bin, args, _cwd, options) {
|
|
|
88
84
|
}
|
|
89
85
|
return spawnInMacGuiWhenOutsideAqua(bin, args, options) ?? spawnSync(bin, args, options);
|
|
90
86
|
}
|
|
87
|
+
function windowsControlArgs(bin, args) {
|
|
88
|
+
// shell経由ではNodeが引数を引用しないので、空白を含むpathだけ二重引用符でくくる。引用符とshell記号は受け付けない。
|
|
89
|
+
if (args.some(arg => /["%^&|<>!]/.test(arg))) {
|
|
90
|
+
throw new AitermError(`${path.basename(bin)} のcontrol commandへ渡せない文字を含む引数があります`, 2);
|
|
91
|
+
}
|
|
92
|
+
return args.map(arg => /\s/.test(arg) ? `"${arg}"` : arg);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 標準入出力で要求と応答をやりとりするcontrol command(`claude -p --input-format stream-json`、`grok agent stdio`)。
|
|
96
|
+
* inputを書いた後、untilの文字列がstdoutに現れるまで標準入力を開けておき、現れたら閉じて終了を待つ。
|
|
97
|
+
* 応答前に標準入力を閉じると、応答せずに正常終了するCLIがある(grok 1.0.41で実測)。
|
|
98
|
+
* 起動方法のOS差はspawnAgentControlCommandと同じ。macOSのAqua外はlaunchdの仕事の中で同じ待ち方をする。
|
|
99
|
+
*/
|
|
100
|
+
export async function runAgentProtocolCommand(bin, args, options) {
|
|
101
|
+
const gui = spawnInMacGuiWhenOutsideAqua(bin, args, options);
|
|
102
|
+
if (gui)
|
|
103
|
+
return { status: gui.status, signal: gui.signal, stdout: gui.stdout, stderr: gui.stderr, ...(gui.error ? { error: gui.error } : {}) };
|
|
104
|
+
let command = bin;
|
|
105
|
+
let argv = args;
|
|
106
|
+
let shell = false;
|
|
107
|
+
if (isWin && !/\.(?:exe|com)$/i.test(bin)) {
|
|
108
|
+
if (/\.(?:cmd|bat)$/i.test(bin)) {
|
|
109
|
+
argv = windowsControlArgs(bin, args);
|
|
110
|
+
shell = true;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
command = resolveWinPaneShell("bash");
|
|
114
|
+
argv = [bin, ...args];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
const child = spawn(command, argv, { cwd: options.cwd, env: options.env, shell, windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
119
|
+
let stdout = "";
|
|
120
|
+
let stderr = "";
|
|
121
|
+
let error;
|
|
122
|
+
let settled = false;
|
|
123
|
+
const finish = (status, signal) => {
|
|
124
|
+
if (settled)
|
|
125
|
+
return;
|
|
126
|
+
settled = true;
|
|
127
|
+
clearTimeout(timer);
|
|
128
|
+
resolve({ status, signal, stdout, stderr, ...(error ? { error } : {}) });
|
|
129
|
+
};
|
|
130
|
+
const stop = (reason) => {
|
|
131
|
+
error ??= reason;
|
|
132
|
+
child.stdin.destroy();
|
|
133
|
+
child.kill();
|
|
134
|
+
};
|
|
135
|
+
const timer = setTimeout(() => stop(Object.assign(new Error(`${path.basename(bin)} ${args.join(" ")} が${options.timeout}ms以内に応答しませんでした`), { code: "ETIMEDOUT" })), options.timeout);
|
|
136
|
+
child.on("error", (reason) => { error ??= reason; finish(null, null); });
|
|
137
|
+
child.on("close", (status, signal) => finish(status, signal));
|
|
138
|
+
child.stdin.on("error", () => { });
|
|
139
|
+
child.stderr.setEncoding("utf8");
|
|
140
|
+
child.stderr.on("data", (chunk) => { if (stderr.length < options.maxBuffer)
|
|
141
|
+
stderr += chunk; });
|
|
142
|
+
child.stdout.setEncoding("utf8");
|
|
143
|
+
child.stdout.on("data", (chunk) => {
|
|
144
|
+
stdout += chunk;
|
|
145
|
+
if (stdout.length > options.maxBuffer)
|
|
146
|
+
return stop(new Error(`${path.basename(bin)} の出力が${options.maxBuffer}bytesを超えました`));
|
|
147
|
+
if (child.stdin.writable && stdout.includes(options.until))
|
|
148
|
+
child.stdin.end();
|
|
149
|
+
});
|
|
150
|
+
child.stdin.write(options.input);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
91
153
|
export function resolveWinPaneShell(shell) {
|
|
92
154
|
if (!isWin)
|
|
93
155
|
return shell;
|
package/dist/harnesses/claude.js
CHANGED
|
@@ -8,7 +8,7 @@ import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { AitermError } from "../errors.js";
|
|
10
10
|
import { modeBitsWorldAccessible } from "../tmux-runtime.js";
|
|
11
|
-
import { spawnAgentControlCommand } from "../agent-resolver.js";
|
|
11
|
+
import { runAgentProtocolCommand, spawnAgentControlCommand } from "../agent-resolver.js";
|
|
12
12
|
import { catalogInvalid, catalogUnavailable, checkedCatalog, findJsonLine, processSummary } from "../model-catalog.js";
|
|
13
13
|
import { currentUid, writeJson0600, shq, subagentInstruction, writeScopeLaunchNote, agentEventPath, createEmpty0600, writeAgentMetadata, agentLineageFields, assertSessionName, agentsDir, LAUNCH_ID_RE, AGENT_EVENT_TAIL_BYTES, readFileRange, } from "../agent-shared.js";
|
|
14
14
|
export const OPERATION_ID_RE = /^sha256:[0-9a-f]{64}$/;
|
|
@@ -146,24 +146,23 @@ const CLAUDE_ULTRACODE_NOTE = "Claude Codeの--effortが受け付けるsession
|
|
|
146
146
|
* (SDKの公開型 SDKControlRequest/SDKControlInitializeResponse)の応答にある models を読む。
|
|
147
147
|
* promptは送らず、sessionを保存せず、利用者のhookとMCP serverを起動しない。
|
|
148
148
|
*/
|
|
149
|
-
export function claudeModelChoices(bin, cwd) {
|
|
149
|
+
export async function claudeModelChoices(bin, cwd) {
|
|
150
150
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "aiterm-claude-models-"));
|
|
151
151
|
try {
|
|
152
152
|
const settings = path.join(dir, "settings.json");
|
|
153
153
|
fs.writeFileSync(settings, JSON.stringify({ disableAllHooks: true }), { mode: 0o600 });
|
|
154
154
|
const requestId = `aiterm-models-${randomUUID()}`;
|
|
155
155
|
const request = { type: "control_request", request_id: requestId, request: { subtype: "initialize" } };
|
|
156
|
-
const result =
|
|
156
|
+
const result = await runAgentProtocolCommand(bin, [
|
|
157
157
|
"-p", "--output-format", "stream-json", "--verbose", "--input-format", "stream-json",
|
|
158
158
|
"--no-session-persistence", "--strict-mcp-config", "--settings", settings,
|
|
159
|
-
],
|
|
159
|
+
], {
|
|
160
160
|
cwd,
|
|
161
|
-
encoding: "utf8",
|
|
162
161
|
env: process.env,
|
|
163
162
|
input: JSON.stringify(request) + "\n",
|
|
163
|
+
until: requestId,
|
|
164
164
|
timeout: CLAUDE_MODELS_TIMEOUT_MS,
|
|
165
165
|
maxBuffer: CLAUDE_MODELS_MAX_BYTES,
|
|
166
|
-
windowsHide: true,
|
|
167
166
|
});
|
|
168
167
|
if (result.error || result.status !== 0) {
|
|
169
168
|
throw catalogUnavailable("Claude Code", result.error?.message || result.stderr?.trim() || `exit=${result.status ?? "unknown"}`);
|
package/dist/harnesses/grok.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as os from "node:os";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
9
9
|
import { AitermError } from "../errors.js";
|
|
10
|
-
import { spawnAgentControlCommand } from "../agent-resolver.js";
|
|
10
|
+
import { runAgentProtocolCommand, spawnAgentControlCommand } from "../agent-resolver.js";
|
|
11
11
|
import { catalogInvalid, catalogUnavailable, checkedCatalog, findJsonLine, processSummary } from "../model-catalog.js";
|
|
12
12
|
import { shq, subagentInstruction, writeScopeLaunchNote, safeStatSize, readFileRange, sleep, agentMetadataPath, writeAgentMetadata, agentEventPath, createEmpty0600, agentLineageFields, AGENT_DONE_POLL_MS, AGENT_EVENT_MAX_BYTES, GROK_TRANSCRIPT_INCREMENT_MAX_BYTES, agentHarness, } from "../agent-shared.js";
|
|
13
13
|
const GROK_MODELS_MAX_BYTES = 1024 * 1024;
|
|
@@ -67,16 +67,16 @@ const GROK_AGENT_CATALOG_TIMEOUT_MS = 30_000;
|
|
|
67
67
|
* `initialize` 応答にある `_meta.modelState` を読む。`session/new` は同じ候補を返すがsessionを保存するので使わない。
|
|
68
68
|
* promptもsessionも作らず、推論は起きない。
|
|
69
69
|
*/
|
|
70
|
-
export function grokModelChoices(bin, cwd) {
|
|
70
|
+
export async function grokModelChoices(bin, cwd) {
|
|
71
71
|
const request = { jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: 1, clientCapabilities: {} } };
|
|
72
|
-
|
|
72
|
+
// 応答前に標準入力を閉じると、grok 1.0.41は応答せずに正常終了することがある。応答を読むまで開けておく。
|
|
73
|
+
const result = await runAgentProtocolCommand(bin, ["agent", "--no-leader", "stdio"], {
|
|
73
74
|
cwd,
|
|
74
|
-
encoding: "utf8",
|
|
75
75
|
env: process.env,
|
|
76
76
|
input: JSON.stringify(request) + "\n",
|
|
77
|
+
until: '"id":1',
|
|
77
78
|
timeout: GROK_AGENT_CATALOG_TIMEOUT_MS,
|
|
78
79
|
maxBuffer: GROK_MODELS_MAX_BYTES,
|
|
79
|
-
windowsHide: true,
|
|
80
80
|
});
|
|
81
81
|
if (result.error || result.status !== 0) {
|
|
82
82
|
throw catalogUnavailable("Grok", result.error?.message || result.stderr?.trim() || `exit=${result.status ?? "unknown"}`);
|
package/dist/tmux-runtime.js
CHANGED
|
@@ -201,9 +201,9 @@ export function spawnInMacGuiWhenOutsideAqua(bin, args, options) {
|
|
|
201
201
|
return null;
|
|
202
202
|
const timeout = options.timeout ?? 30_000;
|
|
203
203
|
const input = options.input === undefined ? undefined : typeof options.input === "string" ? options.input : Buffer.from(options.input.buffer, options.input.byteOffset, options.input.byteLength);
|
|
204
|
-
return runInMacGui([bin, ...args], options.env ?? process.env, String(options.cwd ?? process.cwd()), timeout, `launchd経由の ${path.basename(bin)} ${args.join(" ")} が${timeout}ms以内に終わりませんでした`, input);
|
|
204
|
+
return runInMacGui([bin, ...args], options.env ?? process.env, String(options.cwd ?? process.cwd()), timeout, `launchd経由の ${path.basename(bin)} ${args.join(" ")} が${timeout}ms以内に終わりませんでした`, input, options.until);
|
|
205
205
|
}
|
|
206
|
-
function runInMacGui(argv, env, cwd, timeoutMs, timeoutMessage, input) {
|
|
206
|
+
function runInMacGui(argv, env, cwd, timeoutMs, timeoutMessage, input, until) {
|
|
207
207
|
const uid = process.getuid?.();
|
|
208
208
|
if (uid === undefined)
|
|
209
209
|
return null;
|
|
@@ -219,10 +219,14 @@ function runInMacGui(argv, env, cwd, timeoutMs, timeoutMessage, input) {
|
|
|
219
219
|
const plist = path.join(dir, "job.plist");
|
|
220
220
|
try {
|
|
221
221
|
// launchdの仕事は標準入力を持たない。入力を渡す時はファイルに書き、同じ仕事の中でつなぐ。
|
|
222
|
+
// untilを渡した時は、その文字列が出力に現れるまで標準入力を開けておく(閉じると応答前に終わるCLIがある)。
|
|
222
223
|
if (input !== undefined) {
|
|
223
224
|
const inputFile = path.join(dir, "in");
|
|
224
225
|
fs.writeFileSync(inputFile, input, { mode: 0o600 });
|
|
225
|
-
argv =
|
|
226
|
+
argv = until === undefined
|
|
227
|
+
? ["/bin/sh", "-c", 'f=$1; shift; exec "$@" <"$f"', "aiterm-gui-input", inputFile, ...argv]
|
|
228
|
+
: ["/bin/sh", "-c", 'f=$1; m=$2; o=$3; shift 3; { cat "$f"; until grep -qF -- "$m" "$o" 2>/dev/null; do sleep 0.05; done; } | "$@"',
|
|
229
|
+
"aiterm-gui-input", inputFile, until, path.join(dir, "out"), ...argv];
|
|
226
230
|
}
|
|
227
231
|
fs.writeFileSync(plist, macGuiTmuxPlist(label, dir, argv, env, cwd));
|
|
228
232
|
const boot = spawnSync("launchctl", ["bootstrap", `gui/${uid}`, plist], { encoding: "utf8", timeout: 10000 });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiterm-mcp",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.1",
|
|
4
4
|
"mcpName": "io.github.kitepon/aiterm-mcp",
|
|
5
5
|
"description": "Persistent terminal MCP with one harness-based launcher for Claude Code, Codex CLI, Grok CLI, and Cursor Agent CLI, plus durable PTYs for SSH, containers, and REPLs.",
|
|
6
6
|
"keywords": [
|