glm-coding-router 1.1.0 → 1.1.2
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/README.md +8 -1
- package/dist/bin/glm-review.js +18 -1
- package/dist/bin/glm-worker.js +7 -0
- package/dist/commands/benchmark.js +4 -0
- package/dist/commands/status.js +3 -1
- package/dist/commands/usage.js +1 -1
- package/dist/core/agent-args.js +20 -0
- package/dist/core/user-env.js +5 -3
- package/dist/mcp/server.js +2 -2
- package/package.json +48 -48
package/README.md
CHANGED
|
@@ -135,7 +135,14 @@ dependency inspection, and preliminary review:
|
|
|
135
135
|
glm-review "Inspect this repository"
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
Runs with `--tools Read,Glob,Grep` — it cannot edit files or run
|
|
138
|
+
Runs with `--tools Read,Glob,Grep --strict-mcp-config` — it cannot edit files or run
|
|
139
|
+
commands.
|
|
140
|
+
|
|
141
|
+
The second flag is part of the guarantee, not a detail: `--tools` restricts only Claude
|
|
142
|
+
Code's **built-in** tools, so without it a review session would also inherit whatever MCP
|
|
143
|
+
servers you have registered — including this project's own, whose `glm_worker` tool writes
|
|
144
|
+
files. `glm-worker` and `glm-router benchmark` are isolated the same way. Interactive
|
|
145
|
+
sessions (`glm-chat`, `glm-fast`) are not: your servers are yours.
|
|
139
146
|
|
|
140
147
|
## Profiles
|
|
141
148
|
|
package/dist/bin/glm-review.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { STRICT_MCP_ARGS } from "../core/agent-args.js";
|
|
2
3
|
import { loadConfig } from "../core/config.js";
|
|
3
4
|
import { locateClaude } from "../core/claude.js";
|
|
4
5
|
import { createGlmEnv } from "../core/env.js";
|
|
@@ -11,8 +12,24 @@ import { spawnAgent } from "../core/process.js";
|
|
|
11
12
|
import { resolveZaiApiKey } from "../core/zai-key.js";
|
|
12
13
|
/** Read-only review surface (spec §17) — no Edit, Write, or Bash. */
|
|
13
14
|
export const REVIEW_TOOLS = "Read,Glob,Grep";
|
|
15
|
+
/**
|
|
16
|
+
* Build the child arguments (spec §17, specs/review-mcp-isolation.md).
|
|
17
|
+
*
|
|
18
|
+
* `--tools` alone does not make this read-only: it restricts the built-in set,
|
|
19
|
+
* while MCP tools from the user's config are additive — with our own server
|
|
20
|
+
* registered, a review could call `glm_worker` and write files. STRICT_MCP_ARGS
|
|
21
|
+
* is what actually holds the guarantee.
|
|
22
|
+
*/
|
|
14
23
|
export function buildReviewArgs(prompt, config) {
|
|
15
|
-
return [
|
|
24
|
+
return [
|
|
25
|
+
"-p",
|
|
26
|
+
prompt,
|
|
27
|
+
"--max-turns",
|
|
28
|
+
String(config.review.maxTurns),
|
|
29
|
+
"--tools",
|
|
30
|
+
REVIEW_TOOLS,
|
|
31
|
+
...STRICT_MCP_ARGS,
|
|
32
|
+
];
|
|
16
33
|
}
|
|
17
34
|
/**
|
|
18
35
|
* glm-review (spec §17): read-only worker for exploration, call-graph
|
package/dist/bin/glm-worker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { STRICT_MCP_ARGS } from "../core/agent-args.js";
|
|
2
3
|
import { loadConfig } from "../core/config.js";
|
|
3
4
|
import { locateClaude } from "../core/claude.js";
|
|
4
5
|
import { createGlmEnv } from "../core/env.js";
|
|
@@ -21,6 +22,10 @@ export const WORKER_TOOLS_NO_BASH = "Read,Glob,Grep,Edit,Write";
|
|
|
21
22
|
* `--allowedTools` every Bash call comes back "This command requires
|
|
22
23
|
* approval". When the allowlist is empty we drop Bash from `--tools` entirely
|
|
23
24
|
* rather than advertising a tool the worker can never use.
|
|
25
|
+
*
|
|
26
|
+
* STRICT_MCP_ARGS keeps the user's MCP servers out of the child
|
|
27
|
+
* (specs/review-mcp-isolation.md): write is expected here, undeclared
|
|
28
|
+
* recursion into `glm_delegate` and its own turn budget is not.
|
|
24
29
|
*/
|
|
25
30
|
export function buildWorkerArgs(prompt, config) {
|
|
26
31
|
const allowedBash = config.worker.allowedBash;
|
|
@@ -33,6 +38,8 @@ export function buildWorkerArgs(prompt, config) {
|
|
|
33
38
|
"acceptEdits",
|
|
34
39
|
"--tools",
|
|
35
40
|
allowedBash.length > 0 ? WORKER_TOOLS : WORKER_TOOLS_NO_BASH,
|
|
41
|
+
// Before --allowedTools: its values are variadic and must stay last.
|
|
42
|
+
...STRICT_MCP_ARGS,
|
|
36
43
|
];
|
|
37
44
|
if (allowedBash.length > 0) {
|
|
38
45
|
args.push("--allowedTools", ...allowedBash.map((pattern) => `Bash(${pattern})`));
|
|
@@ -10,6 +10,7 @@ import { locateClaude } from "../core/claude.js";
|
|
|
10
10
|
import { createGlmEnv } from "../core/env.js";
|
|
11
11
|
import { Errors } from "../core/errors.js";
|
|
12
12
|
import { configDir } from "../core/paths.js";
|
|
13
|
+
import { STRICT_MCP_ARGS } from "../core/agent-args.js";
|
|
13
14
|
import { spawnAgentCapture } from "../core/process.js";
|
|
14
15
|
import { resolveZaiApiKey } from "../core/zai-key.js";
|
|
15
16
|
import { BENCHMARK_TASKS, benchmarkTaskById } from "../templates/benchmark-tasks.js";
|
|
@@ -126,6 +127,9 @@ async function runTask(task, run, ctx) {
|
|
|
126
127
|
"acceptEdits",
|
|
127
128
|
"--tools",
|
|
128
129
|
WORKER_TOOLS,
|
|
130
|
+
// A benchmark is only comparable if the tool surface is fixed
|
|
131
|
+
// (specs/review-mcp-isolation.md).
|
|
132
|
+
...STRICT_MCP_ARGS,
|
|
129
133
|
"--output-format",
|
|
130
134
|
"json",
|
|
131
135
|
];
|
package/dist/commands/status.js
CHANGED
|
@@ -51,6 +51,8 @@ export function statusCommand(options, deps = {}) {
|
|
|
51
51
|
});
|
|
52
52
|
return 0;
|
|
53
53
|
}
|
|
54
|
+
/** Every status row pads its label to this column (spec §41). */
|
|
55
|
+
const LABEL_WIDTH = 16;
|
|
54
56
|
const lines = [
|
|
55
57
|
`GLM Coding Router v${version}`,
|
|
56
58
|
"",
|
|
@@ -63,7 +65,7 @@ export function statusCommand(options, deps = {}) {
|
|
|
63
65
|
];
|
|
64
66
|
for (const row of skillState) {
|
|
65
67
|
const enabled = row.homeDetected && row.installed;
|
|
66
|
-
lines.push(`${row.agent} skill
|
|
68
|
+
lines.push(`${`${row.agent} skill`.padEnd(LABEL_WIDTH)}${enabled ? "enabled" : "disabled"}`);
|
|
67
69
|
}
|
|
68
70
|
lines.push("", `Main model ${config.models.main}`, `Fast model ${config.models.fast}`);
|
|
69
71
|
process.stdout.write(lines.join("\n") + "\n");
|
package/dist/commands/usage.js
CHANGED
|
@@ -9,7 +9,7 @@ import { emitJson } from "./context.js";
|
|
|
9
9
|
/** Z.ai monitor API used by their own dashboard (specs/usage.md). */
|
|
10
10
|
const ZAI_QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
|
|
11
11
|
/** Window labels for the observed enum values (specs/usage.md); unknown values stay generic. */
|
|
12
|
-
function describeWindow(limit) {
|
|
12
|
+
export function describeWindow(limit) {
|
|
13
13
|
if (limit.unit === 3 && typeof limit.number === "number") {
|
|
14
14
|
return `${limit.number}-hour window`;
|
|
15
15
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arguments shared by every headless agent we spawn
|
|
3
|
+
* (specs/review-mcp-isolation.md).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Keep the user's MCP servers out of the children we construct.
|
|
7
|
+
*
|
|
8
|
+
* `--tools` only restricts Claude Code's **built-in** set, so MCP tools
|
|
9
|
+
* registered at user/project scope are additive and survive it. With our own
|
|
10
|
+
* server registered (`glm-router mcp install`), that handed every `glm-review`
|
|
11
|
+
* session an `mcp__glm-coding-router__glm_worker` — write access and recursion
|
|
12
|
+
* from a surface documented as read-only.
|
|
13
|
+
*
|
|
14
|
+
* `--strict-mcp-config` uses only the servers given by `--mcp-config`. We pass
|
|
15
|
+
* none, so the set is empty and the child's tool surface is exactly the one we
|
|
16
|
+
* asked for. Security setting: not configurable, not overridable by a profile.
|
|
17
|
+
* Interactive sessions (`glm-chat`, `glm-fast`) are deliberately excluded —
|
|
18
|
+
* the user's own servers are theirs.
|
|
19
|
+
*/
|
|
20
|
+
export const STRICT_MCP_ARGS = ["--strict-mcp-config"];
|
package/dist/core/user-env.js
CHANGED
|
@@ -168,14 +168,16 @@ export function describeShellExport(name, deps = {}) {
|
|
|
168
168
|
const env = deps.env ?? process.env;
|
|
169
169
|
const home = deps.home ?? env.HOME ?? "~";
|
|
170
170
|
const exists = deps.exists ?? ((file) => fs.existsSync(file));
|
|
171
|
-
const shell = path.basename(env.SHELL ?? "bash");
|
|
171
|
+
const shell = path.posix.basename(env.SHELL ?? "bash");
|
|
172
172
|
const candidates = shell === "zsh"
|
|
173
173
|
? [".zshrc", ".zprofile", ".profile"]
|
|
174
174
|
: shell === "fish"
|
|
175
175
|
? [".config/fish/config.fish"]
|
|
176
176
|
: [".bashrc", ".bash_profile", ".profile"];
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
// POSIX paths by definition: this guidance only ever names a shell rc file,
|
|
178
|
+
// so it must not pick up Windows separators when the process runs on win32.
|
|
179
|
+
const found = candidates.find((rel) => exists(path.posix.join(home, rel)));
|
|
180
|
+
const profile = path.posix.join(home, found ?? candidates[0]);
|
|
179
181
|
const line = shell === "fish" ? `set -gx ${name} <your-key>` : `export ${name}="<your-key>"`;
|
|
180
182
|
return { line, profile };
|
|
181
183
|
}
|
package/dist/mcp/server.js
CHANGED
|
@@ -11,7 +11,7 @@ import { applyProfile } from "../core/profile.js";
|
|
|
11
11
|
import { version } from "../core/version.js";
|
|
12
12
|
import { createDelegateWorktree, delegateBranch, removeDelegateWorktree, rollbackDelegateBranch, validateDelegateName, } from "../core/worktree.js";
|
|
13
13
|
import { resolveZaiApiKey } from "../core/zai-key.js";
|
|
14
|
-
import { aggregateLocalUsage, fetchZaiQuota } from "../commands/usage.js";
|
|
14
|
+
import { aggregateLocalUsage, describeWindow, fetchZaiQuota } from "../commands/usage.js";
|
|
15
15
|
const PROMPT_PROPERTY = { type: "string", description: "The task prompt for the GLM agent." };
|
|
16
16
|
export const MCP_TOOLS = [
|
|
17
17
|
{
|
|
@@ -138,7 +138,7 @@ async function usage(deps) {
|
|
|
138
138
|
lines.push(`Z.ai Coding Plan${quota.level ? ` (level: ${quota.level})` : ""}`);
|
|
139
139
|
for (const limit of quota.limits ?? []) {
|
|
140
140
|
const resets = typeof limit.nextResetTime === "number" ? ` — resets ${new Date(limit.nextResetTime).toISOString()}` : "";
|
|
141
|
-
lines.push(` ${
|
|
141
|
+
lines.push(` ${describeWindow(limit).padEnd(15)} ${String(limit.currentValue ?? "?")} / ${String(limit.usage ?? "?")} credits (${String(limit.percentage ?? "?")}%)${resets}`);
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "glm-coding-router",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "GLM Coding Plan workers for Claude Code and Codex",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "hieu9721",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/hieu9721/GLM-coding-router.git"
|
|
11
|
-
},
|
|
12
|
-
"bin": {
|
|
13
|
-
"glm-router": "./dist/cli.js",
|
|
14
|
-
"glm-chat": "./dist/bin/glm-chat.js",
|
|
15
|
-
"glm-worker": "./dist/bin/glm-worker.js",
|
|
16
|
-
"glm-review": "./dist/bin/glm-review.js",
|
|
17
|
-
"glm-fast": "./dist/bin/glm-fast.js",
|
|
18
|
-
"glm-mcp": "./dist/bin/glm-mcp.js"
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist"
|
|
22
|
-
],
|
|
23
|
-
"scripts": {
|
|
24
|
-
"dev": "tsx src/cli.ts",
|
|
25
|
-
"build": "tsc",
|
|
26
|
-
"test": "vitest run",
|
|
27
|
-
"test:watch": "vitest",
|
|
28
|
-
"lint": "eslint src tests",
|
|
29
|
-
"prepublishOnly": "npm run build && npm test"
|
|
30
|
-
},
|
|
31
|
-
"engines": {
|
|
32
|
-
"node": ">=20"
|
|
33
|
-
},
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"commander": "^15.0.0",
|
|
36
|
-
"prompts": "^2.4.2",
|
|
37
|
-
"zod": "^4.6.5"
|
|
38
|
-
},
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"@types/node": "^22.20.3",
|
|
41
|
-
"@types/prompts": "^2.4.9",
|
|
42
|
-
"eslint": "^9.39.5",
|
|
43
|
-
"tsx": "^4.23.13",
|
|
44
|
-
"typescript": "^5.9.3",
|
|
45
|
-
"typescript-eslint": "^8.70.0",
|
|
46
|
-
"vitest": "^5.0.1"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "glm-coding-router",
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "GLM Coding Plan workers for Claude Code and Codex",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "hieu9721",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/hieu9721/GLM-coding-router.git"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"glm-router": "./dist/cli.js",
|
|
14
|
+
"glm-chat": "./dist/bin/glm-chat.js",
|
|
15
|
+
"glm-worker": "./dist/bin/glm-worker.js",
|
|
16
|
+
"glm-review": "./dist/bin/glm-review.js",
|
|
17
|
+
"glm-fast": "./dist/bin/glm-fast.js",
|
|
18
|
+
"glm-mcp": "./dist/bin/glm-mcp.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "tsx src/cli.ts",
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:watch": "vitest",
|
|
28
|
+
"lint": "eslint src tests",
|
|
29
|
+
"prepublishOnly": "npm run build && npm test"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"commander": "^15.0.0",
|
|
36
|
+
"prompts": "^2.4.2",
|
|
37
|
+
"zod": "^4.6.5"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.20.3",
|
|
41
|
+
"@types/prompts": "^2.4.9",
|
|
42
|
+
"eslint": "^9.39.5",
|
|
43
|
+
"tsx": "^4.23.13",
|
|
44
|
+
"typescript": "^5.9.3",
|
|
45
|
+
"typescript-eslint": "^8.70.0",
|
|
46
|
+
"vitest": "^5.0.1"
|
|
47
|
+
}
|
|
48
|
+
}
|