siluzan-tso-cli 1.1.8-beta.5 → 1.1.8-beta.6

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 CHANGED
@@ -20,7 +20,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
20
20
  siluzan-tso init --force # 强制覆盖已存在文件
21
21
  ```
22
22
 
23
- > **注意**:当前为测试版(1.1.8-beta.5),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
23
+ > **注意**:当前为测试版(1.1.8-beta.6),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
24
24
 
25
25
  | 助手 | 建议 `--ai` |
26
26
  |------|-------------|
package/dist/index.js CHANGED
@@ -1970,11 +1970,11 @@ import * as path from "path";
1970
1970
  import * as os from "os";
1971
1971
  import * as https from "https";
1972
1972
  import * as http from "http";
1973
- import * as os2 from "os";
1974
1973
  var import_semver = __toESM(require_semver2(), 1);
1975
1974
  import * as fs2 from "fs";
1976
1975
  import * as path2 from "path";
1977
1976
  import { fileURLToPath } from "url";
1977
+ import * as os2 from "os";
1978
1978
  var SILUZAN_DIR = path.join(os.homedir(), ".siluzan");
1979
1979
  var CONFIG_FILE = path.join(SILUZAN_DIR, "config.json");
1980
1980
  function readStr(raw, key) {
@@ -2087,6 +2087,110 @@ function rawRequest(url, options) {
2087
2087
  req.end();
2088
2088
  });
2089
2089
  }
2090
+ function redactSensitive(input) {
2091
+ let output = input;
2092
+ output = output.replace(/(Bearer\s+)[^\s",]+/gi, "$1***");
2093
+ output = output.replace(
2094
+ /("?(?:apiKey|authToken|accessToken|refreshToken|token|authorization)"?\s*[:=]\s*"?)([^"\s,}]+)/gi,
2095
+ "$1***"
2096
+ );
2097
+ return output;
2098
+ }
2099
+ async function apiFetch(url, config, options = {}, verbose = false) {
2100
+ const method = options.method ?? "GET";
2101
+ const authHeaders = config.apiKey ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` };
2102
+ const reqHeaders = {
2103
+ "Content-Type": "application/json",
2104
+ "Accept-Language": "zh-CN",
2105
+ ...authHeaders,
2106
+ // dataPermission 仅 TSO 使用;CSO 未设置时为空字符串,服务端忽略该头
2107
+ Datapermission: config.dataPermission ?? "",
2108
+ ...options.headers ?? {}
2109
+ };
2110
+ const body = typeof options.body === "string" ? options.body : void 0;
2111
+ const res = await rawRequest(url, {
2112
+ method,
2113
+ headers: reqHeaders,
2114
+ body
2115
+ });
2116
+ const text = res.text;
2117
+ if (res.status < 200 || res.status >= 300) {
2118
+ const detail = verbose ? `\uFF1A${redactSensitive(text).slice(0, 300)}` : "";
2119
+ throw new Error(`HTTP ${res.status}${detail}`);
2120
+ }
2121
+ if (!text.trim()) return null;
2122
+ try {
2123
+ return JSON.parse(text);
2124
+ } catch {
2125
+ if (verbose) {
2126
+ console.error(` \u26A0\uFE0F \u54CD\u5E94\u975E JSON\uFF0C\u539F\u59CB\u5185\u5BB9\uFF1A${redactSensitive(text).slice(0, 200)}`);
2127
+ }
2128
+ return text;
2129
+ }
2130
+ }
2131
+ async function apiFetchWithHeaders(url, config, options = {}, verbose = false) {
2132
+ const method = options.method ?? "GET";
2133
+ const authHeaders = config.apiKey ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` };
2134
+ const reqHeaders = {
2135
+ "Content-Type": "application/json",
2136
+ "Accept-Language": "zh-CN",
2137
+ ...authHeaders,
2138
+ Datapermission: config.dataPermission ?? "",
2139
+ ...options.headers ?? {}
2140
+ };
2141
+ const body = typeof options.body === "string" ? options.body : void 0;
2142
+ const res = await rawRequest(url, { method, headers: reqHeaders, body });
2143
+ const text = res.text;
2144
+ if (res.status < 200 || res.status >= 300) {
2145
+ const detail = verbose ? `\uFF1A${redactSensitive(text).slice(0, 300)}` : "";
2146
+ throw new Error(`HTTP ${res.status}${detail}`);
2147
+ }
2148
+ let data;
2149
+ if (!text.trim()) {
2150
+ data = null;
2151
+ } else {
2152
+ try {
2153
+ data = JSON.parse(text);
2154
+ } catch {
2155
+ data = text;
2156
+ }
2157
+ }
2158
+ return { data, headers: res.headers };
2159
+ }
2160
+ function getCurrentVersion(importMetaUrl) {
2161
+ try {
2162
+ const __dirname3 = path2.dirname(fileURLToPath(importMetaUrl));
2163
+ const pkgPath = path2.join(__dirname3, "..", "package.json");
2164
+ const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
2165
+ return pkg.version ?? "0.0.0";
2166
+ } catch {
2167
+ return "0.0.0";
2168
+ }
2169
+ }
2170
+ function npmDistTagForCurrentVersion(version) {
2171
+ const v = version.trim().replace(/^v/i, "");
2172
+ return /^\d+\.\d+\.\d+$/.test(v) ? "latest" : "beta";
2173
+ }
2174
+ function npmMinRequiredTagForBuildEnv(buildEnv) {
2175
+ return buildEnv === "test" ? "min-required-beta" : "min-required";
2176
+ }
2177
+ function isNewer(a, b) {
2178
+ return import_semver.default.gt(b, a) === true;
2179
+ }
2180
+ async function fetchNpmVersion(pkgName, tag, timeoutMs = 4e3) {
2181
+ try {
2182
+ const url = `https://registry.npmjs.org/${pkgName}/${tag}`;
2183
+ const controller = new AbortController();
2184
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2185
+ const res = await fetch(url, { signal: controller.signal });
2186
+ clearTimeout(timer);
2187
+ if (!res.ok) return null;
2188
+ const data = await res.json();
2189
+ return data.version ?? null;
2190
+ } catch {
2191
+ return null;
2192
+ }
2193
+ }
2090
2194
  var DEFAULT_SENTRY_DSN = "https://bafcf42aab6fe7b485310619ae041b5e@o4510436169285632.ingest.us.sentry.io/4511103054708736";
2091
2195
  function isSentryDisabled() {
2092
2196
  return process.env.SILUZAN_SENTRY_DISABLED === "1" || process.env.SILUZAN_SENTRY_DISABLED === "true";
@@ -2126,11 +2230,6 @@ function redactCliArgvForSentry(argv) {
2126
2230
  }
2127
2231
  return out.join(" ");
2128
2232
  }
2129
- function isApiTrackingEnabled() {
2130
- const v = process.env.SILUZAN_SENTRY_TRACKING?.trim().toLowerCase();
2131
- if (v === "0" || v === "false" || v === "off" || v === "no") return false;
2132
- return true;
2133
- }
2134
2233
  function setSiluzanCliMeta(name, version) {
2135
2234
  cliMeta = { name, version };
2136
2235
  if (sentryReady) {
@@ -2219,178 +2318,6 @@ function inferSiluzanRuntimeEnvironment(requestUrl) {
2219
2318
  return "production";
2220
2319
  }
2221
2320
  }
2222
- function breadcrumbUrl(requestUrl) {
2223
- try {
2224
- const u = new URL(requestUrl);
2225
- return `${u.origin}${u.pathname}`;
2226
- } catch {
2227
- return requestUrl.slice(0, 120);
2228
- }
2229
- }
2230
- function trackingPathParts(requestUrl) {
2231
- try {
2232
- const u = new URL(requestUrl);
2233
- return { host: u.hostname.toLowerCase(), pathname: u.pathname || "/" };
2234
- } catch {
2235
- return { host: "unknown", pathname: "/" };
2236
- }
2237
- }
2238
- var SENSITIVE_QUERY_KEYS = /* @__PURE__ */ new Set([
2239
- "token",
2240
- "password",
2241
- "api_key",
2242
- "apikey",
2243
- "key",
2244
- "secret",
2245
- "authorization",
2246
- "auth"
2247
- ]);
2248
- function redactUrlForTracking(url) {
2249
- try {
2250
- const u = new URL(url);
2251
- const q = new URLSearchParams(u.search);
2252
- const out = new URLSearchParams();
2253
- for (const [k, v] of q.entries()) {
2254
- out.set(k, SENSITIVE_QUERY_KEYS.has(k.toLowerCase()) ? "[REDACTED]" : v);
2255
- }
2256
- const qs = out.toString();
2257
- return `${u.origin}${u.pathname}${qs ? `?${qs}` : ""}`;
2258
- } catch {
2259
- return url.slice(0, 800);
2260
- }
2261
- }
2262
- function redactTrackingHeaders(h) {
2263
- const out = {};
2264
- for (const [k, v] of Object.entries(h)) {
2265
- const low = k.toLowerCase();
2266
- if (low === "authorization" || low === "x-api-key") {
2267
- out[k] = "[REDACTED]";
2268
- } else if (v.length > 2e3) {
2269
- out[k] = `${v.slice(0, 500)}\u2026[truncated ${v.length} chars]`;
2270
- } else {
2271
- out[k] = v;
2272
- }
2273
- }
2274
- return out;
2275
- }
2276
- var SENSITIVE_JSON_KEY = /* @__PURE__ */ new Set([
2277
- "password",
2278
- "token",
2279
- "apikey",
2280
- "api_key",
2281
- "authorization",
2282
- "authtoken",
2283
- "accesstoken",
2284
- "refreshtoken",
2285
- "secret",
2286
- "client_secret",
2287
- "privatekey",
2288
- "private_key"
2289
- ]);
2290
- function deepRedactJson(value, depth) {
2291
- if (depth > 14) return "[MAX_DEPTH]";
2292
- if (value === null || typeof value !== "object") return value;
2293
- if (Array.isArray(value)) {
2294
- return value.map((v) => deepRedactJson(v, depth + 1));
2295
- }
2296
- const o = value;
2297
- const out = {};
2298
- for (const [k, v] of Object.entries(o)) {
2299
- const low = k.toLowerCase().replace(/_/g, "");
2300
- if (SENSITIVE_JSON_KEY.has(low) || low.includes("password") || low.includes("secret")) {
2301
- out[k] = "[REDACTED]";
2302
- } else {
2303
- out[k] = deepRedactJson(v, depth + 1);
2304
- }
2305
- }
2306
- return out;
2307
- }
2308
- function redactPlainTextSnippet(input) {
2309
- let s = input;
2310
- s = s.replace(/(Bearer\s+)[^\s'",}]+/gi, "$1***");
2311
- s = s.replace(
2312
- /("?(?:apiKey|authToken|accessToken|refreshToken|token|password)"?\s*[:=]\s*"?)([^"\s,}]{4,})/gi,
2313
- "$1***"
2314
- );
2315
- return s;
2316
- }
2317
- function trackingBodyMaxChars() {
2318
- const n = Number(process.env.SILUZAN_SENTRY_BODY_MAX);
2319
- if (Number.isFinite(n) && n > 256) return Math.min(n, 5e5);
2320
- return 12e3;
2321
- }
2322
- function trackingOmitBodies() {
2323
- const v = process.env.SILUZAN_SENTRY_NO_API_BODY?.trim().toLowerCase();
2324
- return v === "1" || v === "true" || v === "yes";
2325
- }
2326
- function formatTrackingBody(raw) {
2327
- if (trackingOmitBodies()) return "[omitted: SILUZAN_SENTRY_NO_API_BODY]";
2328
- if (raw === void 0 || raw === "") return "";
2329
- const max = trackingBodyMaxChars();
2330
- let text;
2331
- try {
2332
- const parsed = JSON.parse(raw);
2333
- text = JSON.stringify(deepRedactJson(parsed, 0));
2334
- } catch {
2335
- text = redactPlainTextSnippet(raw);
2336
- }
2337
- if (text.length > max) {
2338
- return `${text.slice(0, max)}\u2026[truncated ${text.length} chars]`;
2339
- }
2340
- return text;
2341
- }
2342
- async function reportSiluzanApiCall(payload) {
2343
- if (isSentryDisabled() || !getDsn() || !isApiTrackingEnabled()) return;
2344
- try {
2345
- const okInit = await ensureSentryInitialized(payload.url);
2346
- if (!okInit) return;
2347
- const Sentry = await import("@sentry/node");
2348
- const { host, pathname } = trackingPathParts(payload.url);
2349
- const siluzanEnv = inferSiluzanRuntimeEnvironment(payload.url);
2350
- const authType = payload.config.apiKey ? "apiKey" : "token";
2351
- const statusOk = payload.status >= 200 && payload.status < 300;
2352
- Sentry.captureMessage(
2353
- `siluzan.cli.api ${payload.method} ${host}${pathname} \u2192 ${payload.status}`,
2354
- {
2355
- level: statusOk ? "info" : "warning",
2356
- tags: {
2357
- siluzan_tracking: "api",
2358
- cli: cliMeta.name,
2359
- cli_version: cliMeta.version,
2360
- http_method: payload.method,
2361
- http_host: host,
2362
- http_status: String(payload.status),
2363
- siluzan_env: siluzanEnv,
2364
- auth_type: authType
2365
- },
2366
- fingerprint: [
2367
- "siluzan-cli-api",
2368
- cliMeta.name,
2369
- payload.method,
2370
- host,
2371
- pathname,
2372
- statusOk ? "2xx" : payload.status >= 500 ? "5xx" : "4xx"
2373
- ],
2374
- contexts: {
2375
- siluzan_cli: {
2376
- command: cliInvocation || "(pre-action \u672A\u6CE8\u518C\u6216\u5148\u4E8E parse \u53D1\u8D77\u8BF7\u6C42)"
2377
- },
2378
- siluzan_request: {
2379
- url: redactUrlForTracking(payload.url),
2380
- method: payload.method,
2381
- headers: redactTrackingHeaders(payload.reqHeaders),
2382
- body: formatTrackingBody(payload.requestBody)
2383
- },
2384
- siluzan_response: {
2385
- status: payload.status,
2386
- body: formatTrackingBody(payload.responseText)
2387
- }
2388
- }
2389
- }
2390
- );
2391
- } catch {
2392
- }
2393
- }
2394
2321
  function cacheKeyForUser(mainOrigin, config) {
2395
2322
  const cred = config.apiKey ? `k:${config.apiKey}` : `t:${config.authToken}`;
2396
2323
  return `${mainOrigin}\0${cred}`;
@@ -2478,147 +2405,6 @@ function refreshSiluzanUser(apiBase, config) {
2478
2405
  }
2479
2406
  })();
2480
2407
  }
2481
- async function prepareSiluzanSentryForApiFetch(url, config, options) {
2482
- if (isSentryDisabled()) return;
2483
- if (!getDsn()) return;
2484
- try {
2485
- const ok = await ensureSentryInitialized(url);
2486
- if (!ok) return;
2487
- const Sentry = await import("@sentry/node");
2488
- const method = options.method ?? "GET";
2489
- const siluzanEnv = inferSiluzanRuntimeEnvironment(url);
2490
- const authType = config.apiKey ? "apiKey" : "token";
2491
- Sentry.addBreadcrumb({
2492
- category: "siluzan.api",
2493
- type: "http",
2494
- level: "info",
2495
- message: `${method} ${breadcrumbUrl(url)}`,
2496
- data: {
2497
- siluzan_env: siluzanEnv,
2498
- auth_type: authType
2499
- }
2500
- });
2501
- const mainOrigin = deriveMainApiOriginFromRequestUrl(url);
2502
- if (mainOrigin) {
2503
- scheduleUserContext(mainOrigin, config);
2504
- }
2505
- } catch {
2506
- }
2507
- }
2508
- function redactSensitive(input) {
2509
- let output = input;
2510
- output = output.replace(/(Bearer\s+)[^\s",]+/gi, "$1***");
2511
- output = output.replace(
2512
- /("?(?:apiKey|authToken|accessToken|refreshToken|token|authorization)"?\s*[:=]\s*"?)([^"\s,}]+)/gi,
2513
- "$1***"
2514
- );
2515
- return output;
2516
- }
2517
- async function apiFetch(url, config, options = {}, verbose = false) {
2518
- const method = options.method ?? "GET";
2519
- const authHeaders = config.apiKey ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` };
2520
- const reqHeaders = {
2521
- "Content-Type": "application/json",
2522
- "Accept-Language": "zh-CN",
2523
- ...authHeaders,
2524
- // dataPermission 仅 TSO 使用;CSO 未设置时为空字符串,服务端忽略该头
2525
- Datapermission: config.dataPermission ?? "",
2526
- ...options.headers ?? {}
2527
- };
2528
- const body = typeof options.body === "string" ? options.body : void 0;
2529
- const res = await rawRequest(url, {
2530
- method,
2531
- headers: reqHeaders,
2532
- body
2533
- });
2534
- const text = res.text;
2535
- if (res.status < 200 || res.status >= 300) {
2536
- const detail = verbose ? `\uFF1A${redactSensitive(text).slice(0, 300)}` : "";
2537
- throw new Error(`HTTP ${res.status}${detail}`);
2538
- }
2539
- if (!text.trim()) return null;
2540
- try {
2541
- return JSON.parse(text);
2542
- } catch {
2543
- if (verbose) {
2544
- console.error(` \u26A0\uFE0F \u54CD\u5E94\u975E JSON\uFF0C\u539F\u59CB\u5185\u5BB9\uFF1A${redactSensitive(text).slice(0, 200)}`);
2545
- }
2546
- return text;
2547
- }
2548
- }
2549
- async function apiFetchWithHeaders(url, config, options = {}, verbose = false) {
2550
- await prepareSiluzanSentryForApiFetch(url, config, options);
2551
- const method = options.method ?? "GET";
2552
- const authHeaders = config.apiKey ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` };
2553
- const reqHeaders = {
2554
- "Content-Type": "application/json",
2555
- "Accept-Language": "zh-CN",
2556
- ...authHeaders,
2557
- Datapermission: config.dataPermission ?? "",
2558
- ...options.headers ?? {}
2559
- };
2560
- const body = typeof options.body === "string" ? options.body : void 0;
2561
- const res = await rawRequest(url, { method, headers: reqHeaders, body });
2562
- const text = res.text;
2563
- await reportSiluzanApiCall({
2564
- url,
2565
- config,
2566
- method,
2567
- reqHeaders,
2568
- requestBody: body,
2569
- status: res.status,
2570
- responseText: text
2571
- });
2572
- if (res.status < 200 || res.status >= 300) {
2573
- const detail = verbose ? `\uFF1A${redactSensitive(text).slice(0, 300)}` : "";
2574
- throw new Error(`HTTP ${res.status}${detail}`);
2575
- }
2576
- let data;
2577
- if (!text.trim()) {
2578
- data = null;
2579
- } else {
2580
- try {
2581
- data = JSON.parse(text);
2582
- } catch {
2583
- data = text;
2584
- }
2585
- }
2586
- return { data, headers: res.headers };
2587
- }
2588
- function getCurrentVersion(importMetaUrl) {
2589
- try {
2590
- const __dirname3 = path2.dirname(fileURLToPath(importMetaUrl));
2591
- const pkgPath = path2.join(__dirname3, "..", "package.json");
2592
- const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
2593
- return pkg.version ?? "0.0.0";
2594
- } catch {
2595
- return "0.0.0";
2596
- }
2597
- }
2598
- function npmDistTagForCurrentVersion(version) {
2599
- const v = version.trim().replace(/^v/i, "");
2600
- return /^\d+\.\d+\.\d+$/.test(v) ? "latest" : "beta";
2601
- }
2602
- function npmMinRequiredTagForBuildEnv(buildEnv) {
2603
- return buildEnv === "test" ? "min-required-beta" : "min-required";
2604
- }
2605
- function isNewer(a, b) {
2606
- return import_semver.default.gt(b, a) === true;
2607
- }
2608
- async function fetchNpmVersion(pkgName, tag, timeoutMs = 4e3) {
2609
- try {
2610
- const url = `https://registry.npmjs.org/${pkgName}/${tag}`;
2611
- const controller = new AbortController();
2612
- const timer = setTimeout(() => controller.abort(), timeoutMs);
2613
- const res = await fetch(url, { signal: controller.signal });
2614
- clearTimeout(timer);
2615
- if (!res.ok) return null;
2616
- const data = await res.json();
2617
- return data.version ?? null;
2618
- } catch {
2619
- return null;
2620
- }
2621
- }
2622
2408
 
2623
2409
  // src/utils/version.ts
2624
2410
  import * as fs3 from "fs";
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.8-beta.5",
4
- "publishedAt": 1775716200188
3
+ "version": "1.1.8-beta.6",
4
+ "publishedAt": 1775717054893
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.8-beta.5",
3
+ "version": "1.1.8-beta.6",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "type": "module",
6
6
  "bin": {