@bazilio-san/glm-cc 1.0.17 → 1.0.20
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/bin/glm-config.js +118 -0
- package/bin/glm.cmd +45 -0
- package/bin/glm.sh +41 -0
- package/package.json +9 -3
- package/bin/glm.js +0 -153
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Config UI + env file generator for @bazilio-san/glm-cc.
|
|
3
|
+
// Invoked only when the user runs:
|
|
4
|
+
// glm --glm-config | -c → interactive reconfiguration
|
|
5
|
+
// glm --glm-show → print current config
|
|
6
|
+
// glm --glm-version → print glm version
|
|
7
|
+
// Or internally by glm.cmd / glm.sh on first launch (--setup) to populate
|
|
8
|
+
// the KEY=VALUE env sidecar file from the JSON config.
|
|
9
|
+
//
|
|
10
|
+
// CRITICAL: this node process must NOT stay alive during `claude` execution.
|
|
11
|
+
// The whole point of 1.0.20 is that glm's entry point is a shell script that
|
|
12
|
+
// sets env vars and tail-calls claude directly, without a Node intermediate
|
|
13
|
+
// process that would break the parent PTY's ConPty session propagation.
|
|
14
|
+
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import os from 'node:os';
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import readline from 'node:readline';
|
|
19
|
+
import { createRequire } from 'node:module';
|
|
20
|
+
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const pkg = require('../package.json');
|
|
23
|
+
|
|
24
|
+
const JSON_PATH = path.join(os.homedir(), '.glm-claude-code.json');
|
|
25
|
+
const ENV_PATH = path.join(os.homedir(), '.glm-claude-code.env');
|
|
26
|
+
|
|
27
|
+
const FIELDS = [
|
|
28
|
+
{ key: 'ANTHROPIC_BASE_URL', label: 'Anthropic Base URL', default: 'https://api.z.ai/api/anthropic' },
|
|
29
|
+
{ key: 'ANTHROPIC_AUTH_TOKEN', label: 'Anthropic Auth Token', default: '' },
|
|
30
|
+
{ key: 'ANTHROPIC_DEFAULT_HAIKU_MODEL', label: 'Default Haiku model', default: 'glm-4.5-air' },
|
|
31
|
+
{ key: 'ANTHROPIC_DEFAULT_SONNET_MODEL', label: 'Default Sonnet model', default: 'glm-5.1' },
|
|
32
|
+
{ key: 'ANTHROPIC_DEFAULT_OPUS_MODEL', label: 'Default Opus model', default: 'glm-5.1' },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function loadJson () {
|
|
36
|
+
try { return JSON.parse(fs.readFileSync(JSON_PATH, 'utf-8')); } catch { return {}; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function saveJson (config) {
|
|
40
|
+
fs.writeFileSync(JSON_PATH, JSON.stringify(config, null, 2), 'utf-8');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function saveEnv (config) {
|
|
44
|
+
const header = '# Generated by @bazilio-san/glm-cc. Edit via `glm --glm-config`.';
|
|
45
|
+
const body = FIELDS.map(f => `${f.key}=${config[f.key] ?? ''}`).join('\n');
|
|
46
|
+
fs.writeFileSync(ENV_PATH, `${header}\n${body}\n`, 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function saveBoth (config) {
|
|
50
|
+
saveJson(config);
|
|
51
|
+
saveEnv(config);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ask (rl, q) {
|
|
55
|
+
return new Promise(resolve => rl.question(q, resolve));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function runInteractive (fieldsToAsk) {
|
|
59
|
+
const config = loadJson();
|
|
60
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
61
|
+
console.log('\nConfiguration (press Enter to keep current value):\n');
|
|
62
|
+
for (const field of fieldsToAsk) {
|
|
63
|
+
const current = config[field.key] || field.default;
|
|
64
|
+
const hint = current ? ` [\x1b[32m${current}\x1b[0m]` : '';
|
|
65
|
+
const answer = await ask(rl, `${field.label}${hint}: `);
|
|
66
|
+
config[field.key] = answer.trim() !== '' ? answer.trim() : current;
|
|
67
|
+
}
|
|
68
|
+
rl.close();
|
|
69
|
+
saveBoth(config);
|
|
70
|
+
console.log(`\nSaved:\n ${JSON_PATH}\n ${ENV_PATH}\n`);
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function show (config) {
|
|
75
|
+
console.log(`\nConfig: ${JSON_PATH}`);
|
|
76
|
+
console.log(`Env: ${ENV_PATH}\n`);
|
|
77
|
+
for (const f of FIELDS) {
|
|
78
|
+
const v = config[f.key];
|
|
79
|
+
console.log(` ${f.key} = ${v != null && v !== '' ? v : '(not set)'}`);
|
|
80
|
+
}
|
|
81
|
+
console.log('');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function main () {
|
|
85
|
+
const args = process.argv.slice(2);
|
|
86
|
+
|
|
87
|
+
if (args.includes('--glm-version')) {
|
|
88
|
+
console.log(`glm v${pkg.version}`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (args.includes('--glm-show')) {
|
|
92
|
+
show(loadJson());
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (args.includes('--glm-config') || args.includes('-c')) {
|
|
96
|
+
await runInteractive(FIELDS);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (args.includes('--setup')) {
|
|
100
|
+
// Invoked by glm.cmd / glm.sh when ~/.glm-claude-code.env is missing.
|
|
101
|
+
// If JSON exists and is complete → just regenerate .env, no prompts.
|
|
102
|
+
// Otherwise prompt only for missing fields.
|
|
103
|
+
const config = loadJson();
|
|
104
|
+
const missing = FIELDS.filter(f => !config[f.key]);
|
|
105
|
+
if (missing.length === 0) {
|
|
106
|
+
saveEnv(config);
|
|
107
|
+
} else {
|
|
108
|
+
console.log('Some glm settings are missing. Please fill them in:\n');
|
|
109
|
+
await runInteractive(missing);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Fallback: invoked directly → treat as full reconfiguration.
|
|
115
|
+
await runInteractive(FIELDS);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
main().catch(err => { console.error(err.message); process.exit(1); });
|
package/bin/glm.cmd
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
:; SCRIPT="$(readlink -f "$0" 2>/dev/null || echo "$0")"; exec "$(dirname "$SCRIPT")/glm.sh" "$@"
|
|
2
|
+
@echo off
|
|
3
|
+
rem -------------------------------------------------------------------------
|
|
4
|
+
rem glm — launch claude with GLM env vars. Runs as a pure cmd.exe script so
|
|
5
|
+
rem that no intermediate node process sits between the caller (PTY / terminal)
|
|
6
|
+
rem and claude.exe. Critical for parent-PTY scenarios where the ConPty session
|
|
7
|
+
rem must reach claude directly for Ink-based raw-keyboard input to work.
|
|
8
|
+
rem
|
|
9
|
+
rem First line is a bash/batch polyglot:
|
|
10
|
+
rem bash : `:` = no-op, `;` separator, `exec` replaces shell with glm.sh
|
|
11
|
+
rem cmd : `:;` = malformed label, skipped silently
|
|
12
|
+
rem -------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
set "GLM_ARGS=%*"
|
|
15
|
+
|
|
16
|
+
rem --- glm-specific flags are delegated to the node helper ----------------
|
|
17
|
+
set "GLM_PAD= %GLM_ARGS% "
|
|
18
|
+
echo.%GLM_PAD%| findstr /I /C:" --glm-config " /C:" --glm-show " /C:" --glm-version " /C:" -c " >nul
|
|
19
|
+
if not errorlevel 1 (
|
|
20
|
+
node "%~dp0glm-config.js" %*
|
|
21
|
+
exit /b %errorlevel%
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
rem --- First-time setup if env file is missing ----------------------------
|
|
25
|
+
set "GLM_ENV_FILE=%USERPROFILE%\.glm-claude-code.env"
|
|
26
|
+
if not exist "%GLM_ENV_FILE%" (
|
|
27
|
+
node "%~dp0glm-config.js" --setup
|
|
28
|
+
if errorlevel 1 exit /b 1
|
|
29
|
+
if not exist "%GLM_ENV_FILE%" (
|
|
30
|
+
echo glm: setup did not produce %GLM_ENV_FILE% 1>&2
|
|
31
|
+
exit /b 1
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
rem --- Load env vars from KEY=VALUE file ----------------------------------
|
|
36
|
+
rem Note: with `tokens=1,*` the second token is %%L (letter after %%K),
|
|
37
|
+
rem not %%V — `for /f` names extra tokens sequentially by letter.
|
|
38
|
+
for /f "usebackq eol=# tokens=1,* delims==" %%K in ("%GLM_ENV_FILE%") do (
|
|
39
|
+
set "%%K=%%L"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
rem --- Tail-call claude directly. `claude.exe` becomes a child of the
|
|
43
|
+
rem outer (PTY-attached) cmd.exe process, not of any node intermediate.
|
|
44
|
+
claude %*
|
|
45
|
+
exit /b %errorlevel%
|
package/bin/glm.sh
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# glm — launch claude with GLM env vars (POSIX/Unix side).
|
|
3
|
+
# Kept as a pure shell script (no node intermediate) so the parent terminal's
|
|
4
|
+
# TTY reaches claude directly. Counterpart of bin/glm.cmd on Windows.
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# Resolve real script dir (follow symlinks — npm links bin -> bin/glm.cmd → .sh via polyglot)
|
|
9
|
+
_glm_src="${BASH_SOURCE[0]:-$0}"
|
|
10
|
+
if command -v readlink >/dev/null 2>&1; then
|
|
11
|
+
_glm_real="$(readlink -f "$_glm_src" 2>/dev/null || echo "$_glm_src")"
|
|
12
|
+
else
|
|
13
|
+
_glm_real="$_glm_src"
|
|
14
|
+
fi
|
|
15
|
+
GLM_BIN_DIR="$(cd "$(dirname "$_glm_real")" && pwd)"
|
|
16
|
+
|
|
17
|
+
GLM_ENV_FILE="${HOME}/.glm-claude-code.env"
|
|
18
|
+
|
|
19
|
+
# --- glm-specific flags delegate to the node helper -----------------------
|
|
20
|
+
case " $* " in
|
|
21
|
+
*" --glm-config "*|*" -c "*|*" --glm-show "*|*" --glm-version "*)
|
|
22
|
+
exec node "${GLM_BIN_DIR}/glm-config.js" "$@"
|
|
23
|
+
;;
|
|
24
|
+
esac
|
|
25
|
+
|
|
26
|
+
# --- First-time setup -----------------------------------------------------
|
|
27
|
+
if [ ! -f "$GLM_ENV_FILE" ]; then
|
|
28
|
+
node "${GLM_BIN_DIR}/glm-config.js" --setup
|
|
29
|
+
if [ ! -f "$GLM_ENV_FILE" ]; then
|
|
30
|
+
echo "glm: setup did not produce $GLM_ENV_FILE" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# --- Load env vars and tail-call claude -----------------------------------
|
|
36
|
+
set -a
|
|
37
|
+
# shellcheck disable=SC1090
|
|
38
|
+
. "$GLM_ENV_FILE"
|
|
39
|
+
set +a
|
|
40
|
+
|
|
41
|
+
exec claude "$@"
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bazilio-san/glm-cc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "CLI tool for running claude with custom GLM configuration",
|
|
5
|
-
"main": "bin/glm.js",
|
|
6
5
|
"bin": {
|
|
7
|
-
"glm": "bin/glm.
|
|
6
|
+
"glm": "bin/glm.cmd"
|
|
8
7
|
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/glm.cmd",
|
|
10
|
+
"bin/glm.sh",
|
|
11
|
+
"bin/glm-config.js",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
9
15
|
"type": "module",
|
|
10
16
|
"scripts": {
|
|
11
17
|
"test": "echo \"Error: no test specified\" && exit 1"
|
package/bin/glm.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import { createRequire } from 'node:module';
|
|
7
|
-
|
|
8
|
-
const require = createRequire(import.meta.url);
|
|
9
|
-
const pkg = require('../package.json');
|
|
10
|
-
import readline from 'node:readline';
|
|
11
|
-
import { spawn } from 'node:child_process';
|
|
12
|
-
|
|
13
|
-
const CONFIG_FILE = path.join(os.homedir(), '.glm-claude-code.json');
|
|
14
|
-
|
|
15
|
-
const CONFIG_FIELDS = [
|
|
16
|
-
{
|
|
17
|
-
key: 'ANTHROPIC_BASE_URL',
|
|
18
|
-
label: 'Anthropic Base URL',
|
|
19
|
-
defaultValue: 'https://api.z.ai/api/anthropic',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
key: 'ANTHROPIC_AUTH_TOKEN',
|
|
23
|
-
label: 'Anthropic Auth Token',
|
|
24
|
-
defaultValue: '',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
key: 'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
|
28
|
-
label: 'Default Haiku model',
|
|
29
|
-
defaultValue: 'glm-4.5-air',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
key: 'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
33
|
-
label: 'Default Sonnet model',
|
|
34
|
-
defaultValue: 'glm-5.1',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
key: 'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
38
|
-
label: 'Default Opus model',
|
|
39
|
-
defaultValue: 'glm-5.1',
|
|
40
|
-
},
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
function loadConfig() {
|
|
44
|
-
if (!fs.existsSync(CONFIG_FILE)) return {};
|
|
45
|
-
try {
|
|
46
|
-
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
47
|
-
} catch {
|
|
48
|
-
return {};
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function saveConfig(config) {
|
|
53
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function ask(rl, question) {
|
|
57
|
-
return new Promise(resolve => rl.question(question, resolve));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function runConfig(fields) {
|
|
61
|
-
const config = loadConfig();
|
|
62
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
63
|
-
|
|
64
|
-
console.log('\nConfiguration (press Enter to keep current value):\n');
|
|
65
|
-
|
|
66
|
-
for (const field of fields) {
|
|
67
|
-
const current = config[field.key] !== undefined && config[field.key] !== ''
|
|
68
|
-
? config[field.key]
|
|
69
|
-
: field.defaultValue;
|
|
70
|
-
const hint = current ? ` [\x1b[32m${current}\x1b[0m]` : '';
|
|
71
|
-
const answer = await ask(rl, `${field.label}${hint}: `);
|
|
72
|
-
config[field.key] = answer.trim() !== '' ? answer.trim() : current;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
rl.close();
|
|
76
|
-
saveConfig(config);
|
|
77
|
-
console.log(`\nConfig saved: ${CONFIG_FILE}\n`);
|
|
78
|
-
return config;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function showConfig(config) {
|
|
82
|
-
const lines = [
|
|
83
|
-
'',
|
|
84
|
-
`Config file: ${CONFIG_FILE}`,
|
|
85
|
-
'',
|
|
86
|
-
'Current settings:',
|
|
87
|
-
];
|
|
88
|
-
for (const field of CONFIG_FIELDS) {
|
|
89
|
-
const val = config[field.key] !== undefined && config[field.key] !== ''
|
|
90
|
-
? config[field.key]
|
|
91
|
-
: '(not set)';
|
|
92
|
-
lines.push(` ${field.key} = ${val}`);
|
|
93
|
-
}
|
|
94
|
-
lines.push('');
|
|
95
|
-
console.log(lines.join('\n'));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function launchClaude(config, args) {
|
|
99
|
-
const env = { ...process.env };
|
|
100
|
-
for (const field of CONFIG_FIELDS) {
|
|
101
|
-
if (config[field.key]) {
|
|
102
|
-
env[field.key] = config[field.key];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
const proc = spawn('claude', args, {
|
|
106
|
-
env,
|
|
107
|
-
stdio: 'inherit',
|
|
108
|
-
shell: process.platform === 'win32',
|
|
109
|
-
});
|
|
110
|
-
proc.on('exit', code => process.exit(code ?? 0));
|
|
111
|
-
proc.on('error', err => {
|
|
112
|
-
console.error(`Failed to launch claude: ${err.message}`);
|
|
113
|
-
process.exit(1);
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async function main() {
|
|
118
|
-
const args = process.argv.slice(2);
|
|
119
|
-
|
|
120
|
-
// Only glm-specific flags: --config, --show-config, --glm-version
|
|
121
|
-
if (args.includes('--glm-config') || args.includes('-c')) {
|
|
122
|
-
await runConfig(CONFIG_FIELDS);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (args.includes('--glm-show')) {
|
|
127
|
-
showConfig(loadConfig());
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (args.includes('--glm-version')) {
|
|
132
|
-
console.log(`glm v${pkg.version}`);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
let config = loadConfig();
|
|
137
|
-
|
|
138
|
-
const missingFields = CONFIG_FIELDS.filter(f => !config[f.key]);
|
|
139
|
-
|
|
140
|
-
if (missingFields.length > 0) {
|
|
141
|
-
console.log('Some settings are missing. Please fill them in:\n');
|
|
142
|
-
config = await runConfig(missingFields);
|
|
143
|
-
config = loadConfig();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// All arguments are passed through to claude
|
|
147
|
-
launchClaude(config, args);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
main().catch(err => {
|
|
151
|
-
console.error(err.message);
|
|
152
|
-
process.exit(1);
|
|
153
|
-
});
|