@volcengine/tls-observer-claude-code-install 0.0.1 → 0.0.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/CHANGELOG.md +6 -0
- package/README.md +4 -2
- package/bin/tls-observer-claude-code-install.mjs +0 -0
- package/dist/index.js +92 -20
- package/package.json +9 -9
- package/scripts/sync-plugin.mjs +1 -1
- package/src/installer/installer-runner.ts +102 -28
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -48,12 +48,12 @@ npm exec -y \
|
|
|
48
48
|
tls-observer-claude-code-install \
|
|
49
49
|
--non-interactive \
|
|
50
50
|
--force \
|
|
51
|
-
--region <region> \
|
|
52
|
-
--otel-endpoint https://tls-<region>.volces.com:4318/v1/traces \
|
|
53
51
|
--trace-topic-id <trace-topic-id> \
|
|
54
52
|
--api-key <api-key>
|
|
55
53
|
```
|
|
56
54
|
|
|
55
|
+
TLS Region 默认是 `cn-beijing`,TLS Producer endpoint 会自动生成为 `tls-cn-beijing.volces.com`。如需切换 Region,使用 `--region <region>`;如需单独覆盖 endpoint,使用 `--tls-endpoint <host>`。旧的 `--otel-endpoint` 仅保留兼容。
|
|
56
|
+
|
|
57
57
|
AK/SK 模式将 `--api-key` 替换为 `--ak`、`--sk`,并可提供 `--project-name`、`--app-name` 自动准备 TLS LogApp。
|
|
58
58
|
|
|
59
59
|
## 本地开发
|
|
@@ -67,4 +67,6 @@ CLAUDE_CODE_PLUGIN_SOURCE="$PWD/packages/claude-code" \
|
|
|
67
67
|
node packages/claude-code-install/bin/tls-observer-claude-code-install.mjs --force
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
安装器会读取源码包的 `publishConfig.directory`,因此本地开发与正式发布都会使用构建生成的 `dist-package`。
|
|
71
|
+
|
|
70
72
|
`sync-plugin` 仅用于生成安装器内的开发快照;正式安装路径以 registry 下载插件为准。
|
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFile, spawn } from "node:child_process";
|
|
1
|
+
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
2
2
|
import { homedir, tmpdir } from "node:os";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { promisify } from "node:util";
|
|
@@ -15,10 +15,10 @@ const DEFAULT_SETTINGS_PATH = __rspack_external_node_path_c5b9b54f.join(homedir(
|
|
|
15
15
|
const DEFAULT_SKILLS_ROOT = __rspack_external_node_path_c5b9b54f.join(homedir(), '.claude', 'skills');
|
|
16
16
|
const DEFAULT_ENV_TARGET = __rspack_external_node_path_c5b9b54f.join(homedir(), '.claude', 'tls-observer-claude-code.env');
|
|
17
17
|
const DEFAULT_DATA_ROOT = __rspack_external_node_path_c5b9b54f.join(homedir(), '.claude', 'plugins', 'data', PLUGIN_NAME);
|
|
18
|
-
const DEFAULT_REGION = 'cn-
|
|
18
|
+
const DEFAULT_REGION = 'cn-beijing';
|
|
19
19
|
const DEFAULT_PROJECT_NAME = 'claude_code_observability';
|
|
20
20
|
const DEFAULT_APP_NAME_PREFIX = 'claude-code-observability';
|
|
21
|
-
const DEFAULT_TLS_ENDPOINT = 'tls-cn-
|
|
21
|
+
const DEFAULT_TLS_ENDPOINT = 'tls-cn-beijing.volces.com';
|
|
22
22
|
const DEFAULT_LOG_APP_TYPE = 'Claude';
|
|
23
23
|
const DEFAULT_TEMPLATE_NAME = 'Claude';
|
|
24
24
|
function readEnvString(key) {
|
|
@@ -37,12 +37,6 @@ function takeArg(argv, index, flag) {
|
|
|
37
37
|
function defaultTlsEndpointForRegion(region) {
|
|
38
38
|
return region ? `tls-${region}.volces.com` : DEFAULT_TLS_ENDPOINT;
|
|
39
39
|
}
|
|
40
|
-
function normalizeEndpointHost(endpoint) {
|
|
41
|
-
return String(endpoint || '').trim().replace(/^https?:\/\//i, '').replace(/\/.*$/, '').replace(/:\d+$/, '');
|
|
42
|
-
}
|
|
43
|
-
function defaultOtelEndpointForRegion(region, tlsEndpoint) {
|
|
44
|
-
return `https://${normalizeEndpointHost(tlsEndpoint) || defaultTlsEndpointForRegion(region)}:4318/v1/traces`;
|
|
45
|
-
}
|
|
46
40
|
function randomSuffix() {
|
|
47
41
|
const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
|
|
48
42
|
let value = '';
|
|
@@ -54,8 +48,7 @@ function defaultAppName() {
|
|
|
54
48
|
}
|
|
55
49
|
function applyDerivedDefaults(options) {
|
|
56
50
|
if (options.region) {
|
|
57
|
-
if (!options.tlsEndpoint) options.tlsEndpoint = defaultTlsEndpointForRegion(options.region);
|
|
58
|
-
if (!options.otelEndpoint) options.otelEndpoint = defaultOtelEndpointForRegion(options.region, options.tlsEndpoint);
|
|
51
|
+
if (!options.tlsEndpoint && !options.otelEndpoint) options.tlsEndpoint = defaultTlsEndpointForRegion(options.region);
|
|
59
52
|
}
|
|
60
53
|
if (!options.projectName && !options.projectId) options.projectName = DEFAULT_PROJECT_NAME;
|
|
61
54
|
if (!options.logAppName) options.logAppName = defaultAppName();
|
|
@@ -77,7 +70,7 @@ function parseCliOptions(argv = process.argv.slice(2)) {
|
|
|
77
70
|
pluginTarget: readEnvString('CLAUDE_CODE_PLUGIN_TARGET') || __rspack_external_node_path_c5b9b54f.join(skillsRoot, PLUGIN_NAME),
|
|
78
71
|
envTarget: readEnvString('CLAUDE_CODE_TLS_ENV_TARGET') || DEFAULT_ENV_TARGET,
|
|
79
72
|
dataRoot: readEnvString('CLAUDE_CODE_TLS_DATA_ROOT') || DEFAULT_DATA_ROOT,
|
|
80
|
-
region: readEnvString('CLAUDE_CODE_TLS_REGION'),
|
|
73
|
+
region: readEnvString('CLAUDE_CODE_TLS_REGION') || DEFAULT_REGION,
|
|
81
74
|
otelEndpoint: readEnvString('CLAUDE_CODE_TLS_OTEL_ENDPOINT'),
|
|
82
75
|
traceTopicId: readEnvString('CLAUDE_CODE_TLS_TRACE_TOPIC_ID'),
|
|
83
76
|
apiKey: readEnvString('CLAUDE_CODE_TLS_API_KEY'),
|
|
@@ -151,8 +144,9 @@ Options:
|
|
|
151
144
|
--advanced-paths Prompt for Claude path overrides
|
|
152
145
|
--registry <url> NPM registry for plugin package. Default: https://registry.npmjs.org
|
|
153
146
|
--plugin-version <version> Exact plugin version or dist-tag. Overrides --beta
|
|
154
|
-
--region <region> TLS region
|
|
155
|
-
--
|
|
147
|
+
--region <region> TLS region. Default: cn-beijing
|
|
148
|
+
--tls-endpoint <host> TLS Producer endpoint. Derived from region by default
|
|
149
|
+
--otel-endpoint <url> Deprecated legacy endpoint alias
|
|
156
150
|
--trace-topic-id <id> TLS trace topic ID
|
|
157
151
|
--api-key <value> TLS API Key
|
|
158
152
|
--ak <value> TLS Access Key
|
|
@@ -184,8 +178,81 @@ async function promptRequired(rl, message, defaultValue = '') {
|
|
|
184
178
|
console.log('This value is required.');
|
|
185
179
|
}
|
|
186
180
|
}
|
|
181
|
+
function setTerminalEcho(enabled) {
|
|
182
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
183
|
+
try {
|
|
184
|
+
execFileSync('stty', [
|
|
185
|
+
enabled ? 'echo' : '-echo'
|
|
186
|
+
], {
|
|
187
|
+
stdio: [
|
|
188
|
+
'inherit',
|
|
189
|
+
'ignore',
|
|
190
|
+
'ignore'
|
|
191
|
+
]
|
|
192
|
+
});
|
|
193
|
+
return true;
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
187
198
|
async function promptSecret(rl, message, defaultValue = '') {
|
|
188
|
-
return promptRequired(rl, message, defaultValue);
|
|
199
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return promptRequired(rl, message, defaultValue);
|
|
200
|
+
rl.pause();
|
|
201
|
+
return new Promise((resolve, reject)=>{
|
|
202
|
+
const input = process.stdin;
|
|
203
|
+
let value = '';
|
|
204
|
+
let finished = false;
|
|
205
|
+
const previousRawMode = input.isRaw;
|
|
206
|
+
let echoDisabled = false;
|
|
207
|
+
const suffix = defaultValue ? '\n default: 已设置,回车保留\n> ' : '\n> ';
|
|
208
|
+
function cleanup() {
|
|
209
|
+
input.off('data', onData);
|
|
210
|
+
input.setRawMode(Boolean(previousRawMode));
|
|
211
|
+
if (echoDisabled) setTerminalEcho(true);
|
|
212
|
+
input.pause();
|
|
213
|
+
rl.resume();
|
|
214
|
+
}
|
|
215
|
+
function finish() {
|
|
216
|
+
if (finished) return;
|
|
217
|
+
finished = true;
|
|
218
|
+
cleanup();
|
|
219
|
+
process.stdout.write('\n');
|
|
220
|
+
const resolvedValue = value.trim() || defaultValue;
|
|
221
|
+
if (resolvedValue) resolve(resolvedValue);
|
|
222
|
+
else {
|
|
223
|
+
console.log('This value is required.');
|
|
224
|
+
resolve(promptSecret(rl, message, defaultValue));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function onData(buffer) {
|
|
228
|
+
for (const char of buffer.toString('utf8')){
|
|
229
|
+
if (finished) return;
|
|
230
|
+
if ('\u0003' === char) {
|
|
231
|
+
finished = true;
|
|
232
|
+
cleanup();
|
|
233
|
+
reject(new Error('Interrupted'));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if ('\r' === char || '\n' === char) return void finish();
|
|
237
|
+
if ('\u007f' === char || '\b' === char) {
|
|
238
|
+
if (value.length > 0) {
|
|
239
|
+
value = [
|
|
240
|
+
...value
|
|
241
|
+
].slice(0, -1).join('');
|
|
242
|
+
process.stdout.write('\b \b');
|
|
243
|
+
}
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
value += char;
|
|
247
|
+
process.stdout.write('*');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
input.resume();
|
|
251
|
+
input.setRawMode(true);
|
|
252
|
+
input.on('data', onData);
|
|
253
|
+
echoDisabled = setTerminalEcho(false);
|
|
254
|
+
process.stdout.write(`${message}${suffix}`);
|
|
255
|
+
});
|
|
189
256
|
}
|
|
190
257
|
function detectAuthType(options) {
|
|
191
258
|
if (options.apiKey) return 'apiKey';
|
|
@@ -203,7 +270,7 @@ function validateBaseTlsConfig(options) {
|
|
|
203
270
|
function validateTlsConfig(options) {
|
|
204
271
|
validateBaseTlsConfig(options);
|
|
205
272
|
const missing = [];
|
|
206
|
-
if (!options.otelEndpoint) missing.push('--
|
|
273
|
+
if (!options.tlsEndpoint && !options.otelEndpoint) missing.push('--tls-endpoint, --region, or legacy --otel-endpoint');
|
|
207
274
|
if (!options.traceTopicId) missing.push('--trace-topic-id or CLAUDE_CODE_TLS_TRACE_TOPIC_ID');
|
|
208
275
|
if (missing.length > 0) throw new Error(`Missing required TLS config: ${missing.join(', ')}`);
|
|
209
276
|
}
|
|
@@ -230,7 +297,6 @@ async function collectInteractiveOptions(options) {
|
|
|
230
297
|
applyDerivedDefaults(options);
|
|
231
298
|
const authType = await promptRequired(rl, 'Auth type, apiKey or aksk', detectAuthType(options) || 'apiKey');
|
|
232
299
|
if ('apiKey' === authType) {
|
|
233
|
-
options.otelEndpoint = await promptRequired(rl, 'OTLP trace endpoint', options.otelEndpoint);
|
|
234
300
|
options.traceTopicId = await promptRequired(rl, 'Trace topic ID', options.traceTopicId || '');
|
|
235
301
|
options.apiKey = await promptSecret(rl, 'TLS API Key', options.apiKey || '');
|
|
236
302
|
options.ak = void 0;
|
|
@@ -443,14 +509,18 @@ async function installPluginPackageToTemp(options) {
|
|
|
443
509
|
async function findLocalPluginSource() {
|
|
444
510
|
const explicit = readEnvString('CLAUDE_CODE_PLUGIN_SOURCE');
|
|
445
511
|
if (!explicit) return;
|
|
446
|
-
const
|
|
512
|
+
const manifest = await readJsonFile(__rspack_external_node_path_c5b9b54f.join(explicit, 'package.json'), {});
|
|
513
|
+
const publishDirectory = manifest.publishConfig?.directory;
|
|
514
|
+
const candidates = publishDirectory ? [
|
|
515
|
+
__rspack_external_node_path_c5b9b54f.join(explicit, publishDirectory)
|
|
516
|
+
] : [
|
|
447
517
|
explicit
|
|
448
|
-
]
|
|
518
|
+
];
|
|
449
519
|
for (const candidate of candidates)try {
|
|
450
520
|
await assertPluginRuntime(candidate);
|
|
451
521
|
return candidate;
|
|
452
522
|
} catch {}
|
|
453
|
-
throw new Error(`CLAUDE_CODE_PLUGIN_SOURCE does not point to a built
|
|
523
|
+
throw new Error(`CLAUDE_CODE_PLUGIN_SOURCE does not point to a built runtime package: ${explicit}. Run the plugin build first.`);
|
|
454
524
|
}
|
|
455
525
|
async function readPluginVersion(pluginSource) {
|
|
456
526
|
const manifest = await readJsonFile(__rspack_external_node_path_c5b9b54f.join(pluginSource, 'package.json'), {});
|
|
@@ -489,6 +559,7 @@ async function installPlugin(options) {
|
|
|
489
559
|
const child = spawn(npm, [
|
|
490
560
|
'install',
|
|
491
561
|
'--omit=dev',
|
|
562
|
+
"--ignore-scripts",
|
|
492
563
|
'--no-audit',
|
|
493
564
|
'--no-fund'
|
|
494
565
|
], {
|
|
@@ -557,6 +628,7 @@ function buildTlsEnv(options) {
|
|
|
557
628
|
'# Generated by tls-observer-claude-code installer.',
|
|
558
629
|
'# Keep this file local because it can contain credentials.',
|
|
559
630
|
envLine('CLAUDE_CODE_TLS_REGION', options.region),
|
|
631
|
+
envLine('CLAUDE_CODE_TLS_ENDPOINT', options.tlsEndpoint),
|
|
560
632
|
envLine('CLAUDE_CODE_TLS_OTEL_ENDPOINT', options.otelEndpoint),
|
|
561
633
|
envLine('CLAUDE_CODE_TLS_TRACE_TOPIC_ID', options.traceTopicId),
|
|
562
634
|
envLine('CLAUDE_CODE_TLS_API_KEY', options.apiKey),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volcengine/tls-observer-claude-code-install",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "One-step installer for the TLS Observer Claude Code plugin.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,13 +15,6 @@
|
|
|
15
15
|
"LICENSE",
|
|
16
16
|
"CHANGELOG.md"
|
|
17
17
|
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "rslib build",
|
|
20
|
-
"check": "tsc --noEmit",
|
|
21
|
-
"format": "prettier --write \"bin/**/*.mjs\" \"scripts/**/*.mjs\" \"src/**/*.ts\" \"*.json\"",
|
|
22
|
-
"format:check": "prettier --check \"bin/**/*.mjs\" \"scripts/**/*.mjs\" \"src/**/*.ts\" \"*.json\"",
|
|
23
|
-
"sync-plugin": "node scripts/sync-plugin.mjs"
|
|
24
|
-
},
|
|
25
18
|
"engines": {
|
|
26
19
|
"node": ">=18"
|
|
27
20
|
},
|
|
@@ -37,5 +30,12 @@
|
|
|
37
30
|
"publishConfig": {
|
|
38
31
|
"registry": "https://registry.npmjs.org/",
|
|
39
32
|
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "rslib build",
|
|
36
|
+
"check": "tsc --noEmit",
|
|
37
|
+
"format": "prettier --write \"bin/**/*.mjs\" \"scripts/**/*.mjs\" \"src/**/*.ts\" \"*.json\"",
|
|
38
|
+
"format:check": "prettier --check \"bin/**/*.mjs\" \"scripts/**/*.mjs\" \"src/**/*.ts\" \"*.json\"",
|
|
39
|
+
"sync-plugin": "node scripts/sync-plugin.mjs"
|
|
40
40
|
}
|
|
41
|
-
}
|
|
41
|
+
}
|
package/scripts/sync-plugin.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import * as path from 'node:path';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
|
|
6
6
|
const installRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
-
const pluginSource = path.resolve(installRoot, '..', 'claude-code');
|
|
7
|
+
const pluginSource = path.resolve(installRoot, '..', 'claude-code', 'dist-package');
|
|
8
8
|
const pluginTarget = path.join(installRoot, 'plugins', 'tls-observer-claude-code');
|
|
9
9
|
|
|
10
10
|
async function assertFile(filePath) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
import { execFile, spawn } from 'node:child_process';
|
|
3
|
+
import { execFile, execFileSync, spawn } from 'node:child_process';
|
|
4
4
|
import { homedir, tmpdir } from 'node:os';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
@@ -76,10 +76,10 @@ const DEFAULT_SETTINGS_PATH = path.join(homedir(), '.claude', 'settings.json');
|
|
|
76
76
|
const DEFAULT_SKILLS_ROOT = path.join(homedir(), '.claude', 'skills');
|
|
77
77
|
const DEFAULT_ENV_TARGET = path.join(homedir(), '.claude', 'tls-observer-claude-code.env');
|
|
78
78
|
const DEFAULT_DATA_ROOT = path.join(homedir(), '.claude', 'plugins', 'data', PLUGIN_NAME);
|
|
79
|
-
const DEFAULT_REGION = 'cn-
|
|
79
|
+
const DEFAULT_REGION = 'cn-beijing';
|
|
80
80
|
const DEFAULT_PROJECT_NAME = 'claude_code_observability';
|
|
81
81
|
const DEFAULT_APP_NAME_PREFIX = 'claude-code-observability';
|
|
82
|
-
const DEFAULT_TLS_ENDPOINT = 'tls-cn-
|
|
82
|
+
const DEFAULT_TLS_ENDPOINT = 'tls-cn-beijing.volces.com';
|
|
83
83
|
const DEFAULT_LOG_APP_TYPE = 'Claude';
|
|
84
84
|
const DEFAULT_TEMPLATE_NAME = 'Claude';
|
|
85
85
|
|
|
@@ -105,18 +105,6 @@ function defaultTlsEndpointForRegion(region?: string): string {
|
|
|
105
105
|
return region ? `tls-${region}.volces.com` : DEFAULT_TLS_ENDPOINT;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function normalizeEndpointHost(endpoint?: string): string {
|
|
109
|
-
return String(endpoint || '')
|
|
110
|
-
.trim()
|
|
111
|
-
.replace(/^https?:\/\//i, '')
|
|
112
|
-
.replace(/\/.*$/, '')
|
|
113
|
-
.replace(/:\d+$/, '');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function defaultOtelEndpointForRegion(region?: string, tlsEndpoint?: string): string {
|
|
117
|
-
return `https://${normalizeEndpointHost(tlsEndpoint) || defaultTlsEndpointForRegion(region)}:4318/v1/traces`;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
108
|
function randomSuffix(): string {
|
|
121
109
|
const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
|
|
122
110
|
let value = '';
|
|
@@ -130,9 +118,9 @@ function defaultAppName(): string {
|
|
|
130
118
|
|
|
131
119
|
function applyDerivedDefaults(options: InstallOptions): void {
|
|
132
120
|
if (options.region) {
|
|
133
|
-
if (!options.tlsEndpoint
|
|
134
|
-
|
|
135
|
-
|
|
121
|
+
if (!options.tlsEndpoint && !options.otelEndpoint) {
|
|
122
|
+
options.tlsEndpoint = defaultTlsEndpointForRegion(options.region);
|
|
123
|
+
}
|
|
136
124
|
}
|
|
137
125
|
if (!options.projectName && !options.projectId) options.projectName = DEFAULT_PROJECT_NAME;
|
|
138
126
|
if (!options.logAppName) options.logAppName = defaultAppName();
|
|
@@ -159,7 +147,7 @@ function parseCliOptions(argv: string[] = process.argv.slice(2)): InstallOptions
|
|
|
159
147
|
pluginTarget: readEnvString('CLAUDE_CODE_PLUGIN_TARGET') || path.join(skillsRoot, PLUGIN_NAME),
|
|
160
148
|
envTarget: readEnvString('CLAUDE_CODE_TLS_ENV_TARGET') || DEFAULT_ENV_TARGET,
|
|
161
149
|
dataRoot: readEnvString('CLAUDE_CODE_TLS_DATA_ROOT') || DEFAULT_DATA_ROOT,
|
|
162
|
-
region: readEnvString('CLAUDE_CODE_TLS_REGION'),
|
|
150
|
+
region: readEnvString('CLAUDE_CODE_TLS_REGION') || DEFAULT_REGION,
|
|
163
151
|
otelEndpoint: readEnvString('CLAUDE_CODE_TLS_OTEL_ENDPOINT'),
|
|
164
152
|
traceTopicId: readEnvString('CLAUDE_CODE_TLS_TRACE_TOPIC_ID'),
|
|
165
153
|
apiKey: readEnvString('CLAUDE_CODE_TLS_API_KEY'),
|
|
@@ -238,8 +226,9 @@ Options:
|
|
|
238
226
|
--advanced-paths Prompt for Claude path overrides
|
|
239
227
|
--registry <url> NPM registry for plugin package. Default: https://registry.npmjs.org
|
|
240
228
|
--plugin-version <version> Exact plugin version or dist-tag. Overrides --beta
|
|
241
|
-
--region <region> TLS region
|
|
242
|
-
--
|
|
229
|
+
--region <region> TLS region. Default: cn-beijing
|
|
230
|
+
--tls-endpoint <host> TLS Producer endpoint. Derived from region by default
|
|
231
|
+
--otel-endpoint <url> Deprecated legacy endpoint alias
|
|
243
232
|
--trace-topic-id <id> TLS trace topic ID
|
|
244
233
|
--api-key <value> TLS API Key
|
|
245
234
|
--ak <value> TLS Access Key
|
|
@@ -271,8 +260,85 @@ async function promptRequired(rl: readline.Interface, message: string, defaultVa
|
|
|
271
260
|
}
|
|
272
261
|
}
|
|
273
262
|
|
|
263
|
+
function setTerminalEcho(enabled: boolean): boolean {
|
|
264
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
265
|
+
try {
|
|
266
|
+
execFileSync('stty', [enabled ? 'echo' : '-echo'], {
|
|
267
|
+
stdio: ['inherit', 'ignore', 'ignore'],
|
|
268
|
+
});
|
|
269
|
+
return true;
|
|
270
|
+
} catch {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
274
275
|
async function promptSecret(rl: readline.Interface, message: string, defaultValue = ''): Promise<string> {
|
|
275
|
-
|
|
276
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
277
|
+
return promptRequired(rl, message, defaultValue);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
rl.pause();
|
|
281
|
+
return new Promise<string>((resolve, reject) => {
|
|
282
|
+
const input = process.stdin;
|
|
283
|
+
let value = '';
|
|
284
|
+
let finished = false;
|
|
285
|
+
const previousRawMode = input.isRaw;
|
|
286
|
+
let echoDisabled = false;
|
|
287
|
+
const suffix = defaultValue ? '\n default: 已设置,回车保留\n> ' : '\n> ';
|
|
288
|
+
|
|
289
|
+
function cleanup(): void {
|
|
290
|
+
input.off('data', onData);
|
|
291
|
+
input.setRawMode(Boolean(previousRawMode));
|
|
292
|
+
if (echoDisabled) setTerminalEcho(true);
|
|
293
|
+
input.pause();
|
|
294
|
+
rl.resume();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function finish(): void {
|
|
298
|
+
if (finished) return;
|
|
299
|
+
finished = true;
|
|
300
|
+
cleanup();
|
|
301
|
+
process.stdout.write('\n');
|
|
302
|
+
const resolvedValue = value.trim() || defaultValue;
|
|
303
|
+
if (resolvedValue) {
|
|
304
|
+
resolve(resolvedValue);
|
|
305
|
+
} else {
|
|
306
|
+
console.log('This value is required.');
|
|
307
|
+
resolve(promptSecret(rl, message, defaultValue));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function onData(buffer: Buffer): void {
|
|
312
|
+
for (const char of buffer.toString('utf8')) {
|
|
313
|
+
if (finished) return;
|
|
314
|
+
if (char === '\u0003') {
|
|
315
|
+
finished = true;
|
|
316
|
+
cleanup();
|
|
317
|
+
reject(new Error('Interrupted'));
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (char === '\r' || char === '\n') {
|
|
321
|
+
finish();
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (char === '\u007f' || char === '\b') {
|
|
325
|
+
if (value.length > 0) {
|
|
326
|
+
value = [...value].slice(0, -1).join('');
|
|
327
|
+
process.stdout.write('\b \b');
|
|
328
|
+
}
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
value += char;
|
|
332
|
+
process.stdout.write('*');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
input.resume();
|
|
337
|
+
input.setRawMode(true);
|
|
338
|
+
input.on('data', onData);
|
|
339
|
+
echoDisabled = setTerminalEcho(false);
|
|
340
|
+
process.stdout.write(`${message}${suffix}`);
|
|
341
|
+
});
|
|
276
342
|
}
|
|
277
343
|
|
|
278
344
|
function detectAuthType(options: InstallOptions): 'apiKey' | 'aksk' | undefined {
|
|
@@ -296,7 +362,9 @@ function validateBaseTlsConfig(options: InstallOptions): void {
|
|
|
296
362
|
function validateTlsConfig(options: InstallOptions): void {
|
|
297
363
|
validateBaseTlsConfig(options);
|
|
298
364
|
const missing: string[] = [];
|
|
299
|
-
if (!options.otelEndpoint)
|
|
365
|
+
if (!options.tlsEndpoint && !options.otelEndpoint) {
|
|
366
|
+
missing.push('--tls-endpoint, --region, or legacy --otel-endpoint');
|
|
367
|
+
}
|
|
300
368
|
if (!options.traceTopicId) missing.push('--trace-topic-id or CLAUDE_CODE_TLS_TRACE_TOPIC_ID');
|
|
301
369
|
if (missing.length > 0) throw new Error(`Missing required TLS config: ${missing.join(', ')}`);
|
|
302
370
|
}
|
|
@@ -329,7 +397,6 @@ async function collectInteractiveOptions(options: InstallOptions): Promise<Insta
|
|
|
329
397
|
detectAuthType(options) || 'apiKey',
|
|
330
398
|
);
|
|
331
399
|
if (authType === 'apiKey') {
|
|
332
|
-
options.otelEndpoint = await promptRequired(rl, 'OTLP trace endpoint', options.otelEndpoint);
|
|
333
400
|
options.traceTopicId = await promptRequired(rl, 'Trace topic ID', options.traceTopicId || '');
|
|
334
401
|
options.apiKey = await promptSecret(rl, 'TLS API Key', options.apiKey || '');
|
|
335
402
|
options.ak = undefined;
|
|
@@ -577,7 +644,12 @@ async function installPluginPackageToTemp(options: InstallOptions): Promise<{
|
|
|
577
644
|
async function findLocalPluginSource(): Promise<string | undefined> {
|
|
578
645
|
const explicit = readEnvString('CLAUDE_CODE_PLUGIN_SOURCE');
|
|
579
646
|
if (!explicit) return undefined;
|
|
580
|
-
const
|
|
647
|
+
const manifest = await readJsonFile<{ publishConfig?: { directory?: string } }>(
|
|
648
|
+
path.join(explicit, 'package.json'),
|
|
649
|
+
{},
|
|
650
|
+
);
|
|
651
|
+
const publishDirectory = manifest.publishConfig?.directory;
|
|
652
|
+
const candidates = publishDirectory ? [path.join(explicit, publishDirectory)] : [explicit];
|
|
581
653
|
for (const candidate of candidates) {
|
|
582
654
|
try {
|
|
583
655
|
await assertPluginRuntime(candidate);
|
|
@@ -586,7 +658,9 @@ async function findLocalPluginSource(): Promise<string | undefined> {
|
|
|
586
658
|
// Continue so the final error includes the env var value.
|
|
587
659
|
}
|
|
588
660
|
}
|
|
589
|
-
throw new Error(
|
|
661
|
+
throw new Error(
|
|
662
|
+
`CLAUDE_CODE_PLUGIN_SOURCE does not point to a built runtime package: ${explicit}. Run the plugin build first.`,
|
|
663
|
+
);
|
|
590
664
|
}
|
|
591
665
|
|
|
592
666
|
async function readPluginVersion(pluginSource: string): Promise<string | undefined> {
|
|
@@ -611,12 +685,11 @@ async function installPlugin(options: InstallOptions): Promise<PluginInstallResu
|
|
|
611
685
|
for (const entry of runtimeEntries) {
|
|
612
686
|
await fs.cp(path.join(source, entry), path.join(options.pluginTarget, entry), { recursive: true });
|
|
613
687
|
}
|
|
614
|
-
|
|
615
688
|
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
616
689
|
console.log('插件依赖安装中,请稍后...');
|
|
617
690
|
try {
|
|
618
691
|
await new Promise<void>((resolve, reject) => {
|
|
619
|
-
const child = spawn(npm, ['install', '--omit=dev', '--no-audit', '--no-fund'], {
|
|
692
|
+
const child = spawn(npm, ['install', '--omit=dev', '--ignore-scripts', '--no-audit', '--no-fund'], {
|
|
620
693
|
cwd: options.pluginTarget,
|
|
621
694
|
stdio: 'inherit',
|
|
622
695
|
});
|
|
@@ -696,6 +769,7 @@ function buildTlsEnv(options: InstallOptions): string {
|
|
|
696
769
|
'# Generated by tls-observer-claude-code installer.',
|
|
697
770
|
'# Keep this file local because it can contain credentials.',
|
|
698
771
|
envLine('CLAUDE_CODE_TLS_REGION', options.region),
|
|
772
|
+
envLine('CLAUDE_CODE_TLS_ENDPOINT', options.tlsEndpoint),
|
|
699
773
|
envLine('CLAUDE_CODE_TLS_OTEL_ENDPOINT', options.otelEndpoint),
|
|
700
774
|
envLine('CLAUDE_CODE_TLS_TRACE_TOPIC_ID', options.traceTopicId),
|
|
701
775
|
envLine('CLAUDE_CODE_TLS_API_KEY', options.apiKey),
|