@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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @volcengine/tls-observer-claude-code-install
2
2
 
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 安装包更新
8
+
3
9
  ## 0.0.1
4
10
 
5
11
  ### Patch Changes
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-guilin-boe';
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-guilin-boe.volces.com';
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
- --otel-endpoint <url> OTLP traces endpoint
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('--otel-endpoint or CLAUDE_CODE_TLS_OTEL_ENDPOINT');
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 candidates = [
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
- ].filter((candidate)=>Boolean(candidate));
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 plugin: ${explicit}`);
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.1",
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
+ }
@@ -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-guilin-boe';
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-guilin-boe.volces.com';
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) options.tlsEndpoint = defaultTlsEndpointForRegion(options.region);
134
- if (!options.otelEndpoint)
135
- options.otelEndpoint = defaultOtelEndpointForRegion(options.region, options.tlsEndpoint);
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
- --otel-endpoint <url> OTLP traces endpoint
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
- return promptRequired(rl, message, defaultValue);
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) missing.push('--otel-endpoint or CLAUDE_CODE_TLS_OTEL_ENDPOINT');
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 candidates = [explicit].filter((candidate): candidate is string => Boolean(candidate));
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(`CLAUDE_CODE_PLUGIN_SOURCE does not point to a built plugin: ${explicit}`);
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),