@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.
@@ -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.17",
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.js"
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
- });