siluzan-cso-cli 1.1.8 → 1.1.9-beta.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/README.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  当用户发来本页面的 **URL** 时,请按顺序协助用户完成安装。
4
4
 
5
+ ## 0. 安全与透明性说明
6
+
7
+ 本 Skill 是 **instruction-only**(纯指令型),不包含可执行代码文件。它通过指导 AI 助手调用 `siluzan-cso` CLI 来操作丝路赞内容发布平台。
8
+
9
+ ### 运行时依赖
10
+ | 项目 | 说明 |
11
+ |------|------|
12
+ | **Node.js 18+** | CLI 运行时 |
13
+ | **npm 8+** 或 **pnpm** | 安装 CLI 全局包 |
14
+ | **siluzan-cso** CLI | 由 `npm install -g siluzan-cso-cli` 安装 |
15
+ | **ffmpeg**(可选) | `extract-cover` 命令截取视频封面帧 |
16
+
17
+ ### 凭据与网络
18
+ | 项目 | 说明 |
19
+ |------|------|
20
+ | API Key / Token | 存储在 `~/.siluzan/config.json`,用于所有 API 调用鉴权 |
21
+ | CSO API | `api.siluzan.com`(生产)/ `api-ci.siluzan.com`(测试) |
22
+ | 前端页面 | `cso.siluzan.com`(授权回调等浏览器操作) |
23
+ | 版本检查 | `registry.npmjs.org` |
24
+
25
+ ### 可选环境变量
26
+ | 变量 | 说明 |
27
+ |------|------|
28
+ | `SILUZAN_AUTH_TOKEN` | 从环境变量读取 Token(优先级高于 config.json,CI/CD 推荐) |
29
+
30
+ ### 可执行的操作范围
31
+ - **只读**:查询媒体账号、运营报表、发布任务状态、人设列表、RAG 知识库检索等
32
+ - **写入**(需用户确认):上传素材、提交发布任务、创建/更新账号分组、生成 AI 内容规划
33
+ - **本地文件操作**:`extract-cover` 截取视频帧输出图片;`init` 将 Skill 文件写入 AI 助手目录
34
+
5
35
  ## 1. 环境
6
36
 
7
37
  - Node.js **18+**
@@ -12,7 +42,7 @@
12
42
  在**用户的目标项目根目录**执行(根据用户使用的助手选择 `--ai`):
13
43
 
14
44
  ```bash
15
- npm install -g siluzan-cso-cli
45
+ npm install -g siluzan-cso-cli@beta
16
46
  siluzan-cso init --ai cursor # 写入 Cursor(默认)
17
47
  siluzan-cso init --ai cursor,claude # 同时写入多个平台
18
48
  siluzan-cso init --ai all # 写入所有支持的平台
@@ -20,6 +50,7 @@ siluzan-cso init -d /path/to/skills # 写入自定义目录
20
50
  siluzan-cso init --force # 强制覆盖已存在文件
21
51
  ```
22
52
 
53
+ > **注意**:当前为测试版(1.1.9-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
23
54
 
24
55
  | 助手 | 建议 `--ai` |
25
56
  |------|-------------|
package/dist/index.js CHANGED
@@ -342,8 +342,8 @@ var require_semver = __commonJS({
342
342
  }
343
343
  // preminor will bump the version up to the next minor release, and immediately
344
344
  // down to pre-release. premajor and prepatch work the same way.
345
- inc(release2, identifier, identifierBase) {
346
- if (release2.startsWith("pre")) {
345
+ inc(release, identifier, identifierBase) {
346
+ if (release.startsWith("pre")) {
347
347
  if (!identifier && identifierBase === false) {
348
348
  throw new Error("invalid increment argument: identifier is empty");
349
349
  }
@@ -354,7 +354,7 @@ var require_semver = __commonJS({
354
354
  }
355
355
  }
356
356
  }
357
- switch (release2) {
357
+ switch (release) {
358
358
  case "premajor":
359
359
  this.prerelease.length = 0;
360
360
  this.patch = 0;
@@ -445,7 +445,7 @@ var require_semver = __commonJS({
445
445
  break;
446
446
  }
447
447
  default:
448
- throw new Error(`invalid increment argument: ${release2}`);
448
+ throw new Error(`invalid increment argument: ${release}`);
449
449
  }
450
450
  this.raw = this.format();
451
451
  if (this.build.length) {
@@ -511,7 +511,7 @@ var require_inc = __commonJS({
511
511
  "../node_modules/.pnpm/semver@7.7.4/node_modules/semver/functions/inc.js"(exports, module) {
512
512
  "use strict";
513
513
  var SemVer = require_semver();
514
- var inc = (version, release2, options, identifier, identifierBase) => {
514
+ var inc = (version, release, options, identifier, identifierBase) => {
515
515
  if (typeof options === "string") {
516
516
  identifierBase = identifier;
517
517
  identifier = options;
@@ -521,7 +521,7 @@ var require_inc = __commonJS({
521
521
  return new SemVer(
522
522
  version instanceof SemVer ? version.version : version,
523
523
  options
524
- ).inc(release2, identifier, identifierBase).version;
524
+ ).inc(release, identifier, identifierBase).version;
525
525
  } catch (er) {
526
526
  return null;
527
527
  }
@@ -1939,11 +1939,11 @@ var require_semver2 = __commonJS({
1939
1939
  import { Command, Option } from "commander";
1940
1940
 
1941
1941
  // src/config/defaults.ts
1942
- var BUILD_ENV = "production";
1943
- var DEFAULT_API_BASE = "https://api.siluzan.com";
1944
- var DEFAULT_CSO_BASE = "https://cso.siluzan.com";
1945
- var DEFAULT_WEB_BASE = "https://www.siluzan.com";
1946
- var DEFAULT_AGENT_BASE = "https://agent.mysiluzan.com";
1942
+ var BUILD_ENV = "test";
1943
+ var DEFAULT_API_BASE = "https://api-ci.siluzan.com";
1944
+ var DEFAULT_CSO_BASE = "https://cso-ci.siluzan.com";
1945
+ var DEFAULT_WEB_BASE = "https://www-ci.siluzan.com";
1946
+ var DEFAULT_AGENT_BASE = "https://agent-ci.mysiluzan.com";
1947
1947
 
1948
1948
  // src/commands/init.ts
1949
1949
  import * as fs2 from "fs/promises";
@@ -2129,7 +2129,6 @@ import { randomUUID as _randomUUID } from "crypto";
2129
2129
  import * as fs22 from "fs";
2130
2130
  import * as path22 from "path";
2131
2131
  import { fileURLToPath as fileURLToPath2 } from "url";
2132
- import * as os22 from "os";
2133
2132
  var SILUZAN_DIR = path3.join(os2.homedir(), ".siluzan");
2134
2133
  var CONFIG_FILE = path3.join(SILUZAN_DIR, "config.json");
2135
2134
  function readStr(raw, key) {
@@ -2210,21 +2209,33 @@ function validateBaseUrl(raw) {
2210
2209
  }
2211
2210
  return null;
2212
2211
  }
2212
+ var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
2213
+ var MAX_RESPONSE_BYTES = 50 * 1024 * 1024;
2213
2214
  function rawRequest(url, options) {
2214
2215
  return new Promise((resolve6, reject) => {
2215
2216
  const parsed = new URL(url);
2216
2217
  const transport = parsed.protocol === "https:" ? https : http;
2218
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2219
+ const maxBytes = options.maxResponseBytes ?? MAX_RESPONSE_BYTES;
2217
2220
  const reqOpts = {
2218
2221
  hostname: parsed.hostname,
2219
2222
  port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
2220
2223
  path: parsed.pathname + parsed.search,
2221
2224
  method: options.method ?? "GET",
2222
- headers: options.headers
2225
+ headers: options.headers,
2226
+ timeout: timeoutMs || void 0
2223
2227
  };
2224
2228
  const req = transport.request(reqOpts, (res) => {
2225
2229
  let data = "";
2230
+ let byteLen = 0;
2226
2231
  res.setEncoding("utf8");
2227
2232
  res.on("data", (chunk) => {
2233
+ byteLen += Buffer.byteLength(chunk, "utf8");
2234
+ if (maxBytes && byteLen > maxBytes) {
2235
+ res.destroy();
2236
+ reject(new Error(`\u54CD\u5E94\u4F53\u8D85\u8FC7\u4E0A\u9650\uFF08${(maxBytes / 1024 / 1024).toFixed(0)} MB\uFF09\uFF0C\u5DF2\u4E2D\u65AD\u8FDE\u63A5`));
2237
+ return;
2238
+ }
2228
2239
  data += chunk;
2229
2240
  });
2230
2241
  res.on("end", () => {
@@ -2237,6 +2248,10 @@ function rawRequest(url, options) {
2237
2248
  resolve6({ status: res.statusCode ?? 0, text: data, headers });
2238
2249
  });
2239
2250
  });
2251
+ req.on("timeout", () => {
2252
+ req.destroy();
2253
+ reject(new Error(`\u8BF7\u6C42\u8D85\u65F6\uFF08${(timeoutMs / 1e3).toFixed(0)} \u79D2\uFF09\uFF1A${options.method ?? "GET"} ${url}`));
2254
+ });
2240
2255
  req.on("error", reject);
2241
2256
  if (options.body) req.write(options.body);
2242
2257
  req.end();
@@ -2332,140 +2347,29 @@ async function fetchNpmVersion(pkgName, tag, timeoutMs = 4e3) {
2332
2347
  return null;
2333
2348
  }
2334
2349
  }
2335
- var DEFAULT_SENTRY_DSN = "https://bafcf42aab6fe7b485310619ae041b5e@o4510436169285632.ingest.us.sentry.io/4511103054708736";
2336
- function isSentryDisabled() {
2337
- return process.env.SILUZAN_SENTRY_DISABLED === "1" || process.env.SILUZAN_SENTRY_DISABLED === "true";
2338
- }
2339
- function getDsn() {
2340
- return process.env.SILUZAN_SENTRY_DSN?.trim() || DEFAULT_SENTRY_DSN;
2341
- }
2342
- var cliMeta = { name: "siluzan-cli", version: "unknown" };
2343
- var cliInvocation = "";
2344
- function setSiluzanCliInvocation(redactedCommandLine) {
2345
- cliInvocation = redactedCommandLine;
2346
- }
2347
- function redactCliArgvForSentry(argv) {
2348
- const args = argv.slice(2);
2349
- const redactNext = /* @__PURE__ */ new Set(["-t", "--token", "--api-key", "--password"]);
2350
- const out = [];
2351
- for (let i = 0; i < args.length; i++) {
2352
- const a = args[i];
2353
- if (redactNext.has(a)) {
2354
- out.push(a, "***");
2355
- i++;
2356
- continue;
2357
- }
2358
- const eq = a.indexOf("=");
2359
- if (eq > 0) {
2360
- const key = a.slice(0, eq).toLowerCase();
2361
- if (key === "--token" || key === "--api-key" || key === "--password") {
2362
- out.push(`${a.slice(0, eq)}=***`);
2363
- continue;
2364
- }
2365
- }
2366
- if (/^-t[^-]/.test(a) && a.length > 3) {
2367
- out.push("-t***");
2368
- continue;
2369
- }
2370
- out.push(a);
2371
- }
2372
- return out.join(" ");
2373
- }
2374
- function setSiluzanCliMeta(name, version) {
2375
- cliMeta = { name, version };
2376
- if (sentryReady) {
2377
- void import("@sentry/node").then((Sentry) => {
2378
- Sentry.setTag("cli", name);
2379
- Sentry.setTag("cli_version", version);
2380
- }).catch(() => {
2381
- });
2382
- }
2383
- }
2384
- var sentryReady = false;
2385
- var sentryInitPromise = null;
2386
- var flushHookRegistered = false;
2387
- function buildRuntimeContext() {
2388
- const platform = process.platform;
2389
- const osName = platform === "win32" ? "Windows" : platform === "darwin" ? "macOS" : "Linux";
2390
- return {
2391
- os: {
2392
- name: osName,
2393
- version: os22.release(),
2394
- // 内核版本,如 10.0.26100(Win11)、24.3.0(macOS)
2395
- arch: process.arch
2396
- // x64 / arm64
2397
- },
2398
- runtime: {
2399
- node_version: process.version,
2400
- // v18.x.x / v20.x.x
2401
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
2402
- }
2403
- };
2404
- }
2405
- async function ensureSentryInitialized(requestUrl) {
2406
- const dsn = getDsn();
2407
- if (!dsn || isSentryDisabled()) return false;
2408
- if (sentryReady) return true;
2409
- if (!sentryInitPromise) {
2410
- sentryInitPromise = (async () => {
2411
- const Sentry = await import("@sentry/node");
2412
- const envOverride = process.env.SILUZAN_SENTRY_ENV?.trim();
2413
- const environment = envOverride || (inferSiluzanRuntimeEnvironment(requestUrl) === "test" ? "test" : "production");
2414
- Sentry.init({
2415
- dsn,
2416
- environment,
2417
- sendDefaultPii: true,
2418
- tracesSampleRate: 0
2419
- });
2420
- const ctx = buildRuntimeContext();
2421
- Sentry.setContext("os", ctx.os);
2422
- Sentry.setContext("runtime", ctx.runtime);
2423
- Sentry.setTag("cli", cliMeta.name);
2424
- Sentry.setTag("cli_version", cliMeta.version);
2425
- if (!flushHookRegistered) {
2426
- flushHookRegistered = true;
2427
- process.once("beforeExit", () => {
2428
- void Sentry.flush(2e3).catch(() => {
2429
- });
2430
- });
2431
- }
2432
- sentryReady = true;
2433
- })();
2434
- }
2435
- await sentryInitPromise;
2436
- return sentryReady;
2437
- }
2438
- function deriveMainApiOriginFromRequestUrl(requestUrl) {
2350
+ function deriveMainApiOrigin(apiBase) {
2439
2351
  try {
2440
- const u = new URL(requestUrl);
2352
+ const u = new URL(apiBase);
2441
2353
  const host = u.hostname.toLowerCase();
2442
- if (!host.endsWith("siluzan.com")) return null;
2354
+ if (!host.endsWith("siluzan.com")) {
2355
+ return apiBase.includes("-ci") ? "https://api-ci.siluzan.com" : "https://api.siluzan.com";
2356
+ }
2443
2357
  if (host.startsWith("tso-api")) {
2444
2358
  return `${u.protocol}//${host.replace(/^tso-api/, "api")}`;
2445
2359
  }
2446
2360
  if (host === "api.siluzan.com" || host === "api-ci.siluzan.com") {
2447
2361
  return `${u.protocol}//${host}`;
2448
2362
  }
2449
- return null;
2450
- } catch {
2451
- return null;
2452
- }
2453
- }
2454
- function inferSiluzanRuntimeEnvironment(requestUrl) {
2455
- try {
2456
- const host = new URL(requestUrl).hostname.toLowerCase();
2457
- return host.includes("-ci") ? "test" : "production";
2363
+ return apiBase.includes("-ci") ? "https://api-ci.siluzan.com" : "https://api.siluzan.com";
2458
2364
  } catch {
2459
- return "production";
2365
+ return apiBase.includes("-ci") ? "https://api-ci.siluzan.com" : "https://api.siluzan.com";
2460
2366
  }
2461
2367
  }
2462
- function cacheKeyForUser(mainOrigin, config) {
2463
- const cred = config.apiKey ? `k:${config.apiKey}` : `t:${config.authToken}`;
2464
- return `${mainOrigin}\0${cred}`;
2368
+ function pickStr(obj, key) {
2369
+ const v = obj[key];
2370
+ return typeof v === "string" && v.trim() ? v.trim() : void 0;
2465
2371
  }
2466
- var userContextDone = /* @__PURE__ */ new Set();
2467
- var userContextInflight = /* @__PURE__ */ new Map();
2468
- function pickCompanyIdFromMe(data) {
2372
+ function pickCompanyId(data) {
2469
2373
  const direct = pickStr(data, "companyId") ?? pickStr(data, "companyID") ?? pickStr(data, "CompanyId");
2470
2374
  if (direct) return direct;
2471
2375
  const ci = data["companyInfo"];
@@ -2482,40 +2386,15 @@ function parseMeResponse(text) {
2482
2386
  const id = pickStr(data, "entityId") ?? pickStr(data, "id") ?? pickStr(data, "userId") ?? pickStr(data, "accountId");
2483
2387
  const email = pickStr(data, "email");
2484
2388
  const username = pickStr(data, "userName") ?? pickStr(data, "username") ?? pickStr(data, "name") ?? pickStr(data, "phone");
2485
- const companyId = pickCompanyIdFromMe(data);
2389
+ const companyId = pickCompanyId(data);
2486
2390
  if (!id && !email && !username && !companyId) return null;
2487
2391
  return { id, email, username, companyId };
2488
2392
  } catch {
2489
2393
  return null;
2490
2394
  }
2491
2395
  }
2492
- function pickStr(obj, key) {
2493
- const v = obj[key];
2494
- return typeof v === "string" && v.trim() ? v.trim() : void 0;
2495
- }
2496
- async function fetchAndSetUser(mainOrigin, config) {
2497
- const meUrl = `${mainOrigin.replace(/\/$/, "")}/query/account/me`;
2498
- const authHeaders = config.apiKey ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` };
2499
- const res = await rawRequest(meUrl, {
2500
- method: "GET",
2501
- headers: {
2502
- "Content-Type": "application/json",
2503
- "Accept-Language": "zh-CN",
2504
- ...authHeaders
2505
- }
2506
- });
2507
- if (res.status < 200 || res.status >= 300) return;
2508
- const parsed = parseMeResponse(res.text);
2509
- if (!parsed) return;
2510
- const Sentry = await import("@sentry/node");
2511
- const user = {};
2512
- if (parsed.id) user.id = parsed.id;
2513
- if (parsed.email) user.email = parsed.email;
2514
- if (parsed.username) user.username = parsed.username;
2515
- Sentry.setUser(user);
2516
- }
2517
2396
  async function fetchSiluzanCurrentUser(apiBase, config) {
2518
- const mainOrigin = deriveMainApiOriginFromRequestUrl(apiBase) ?? (apiBase.includes("-ci") ? "https://api-ci.siluzan.com" : "https://api.siluzan.com");
2397
+ const mainOrigin = deriveMainApiOrigin(apiBase);
2519
2398
  const meUrl = `${mainOrigin.replace(/\/$/, "")}/query/account/me`;
2520
2399
  const authHeaders = config.apiKey ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` };
2521
2400
  try {
@@ -2540,38 +2419,6 @@ async function fetchSiluzanCurrentUser(apiBase, config) {
2540
2419
  return null;
2541
2420
  }
2542
2421
  }
2543
- function scheduleUserContext(mainOrigin, config) {
2544
- const key = cacheKeyForUser(mainOrigin, config);
2545
- if (userContextDone.has(key)) return;
2546
- let p = userContextInflight.get(key);
2547
- if (!p) {
2548
- p = (async () => {
2549
- try {
2550
- await fetchAndSetUser(mainOrigin, config);
2551
- } catch {
2552
- } finally {
2553
- userContextInflight.delete(key);
2554
- userContextDone.add(key);
2555
- }
2556
- })();
2557
- userContextInflight.set(key, p);
2558
- }
2559
- }
2560
- function refreshSiluzanUser(apiBase, config) {
2561
- if (isSentryDisabled()) return;
2562
- void (async () => {
2563
- try {
2564
- const ok = await ensureSentryInitialized(apiBase);
2565
- if (!ok) return;
2566
- const mainOrigin = deriveMainApiOriginFromRequestUrl(apiBase) ?? (apiBase.includes("-ci") ? "https://api-ci.siluzan.com" : "https://api.siluzan.com");
2567
- const key = cacheKeyForUser(mainOrigin, config);
2568
- userContextDone.delete(key);
2569
- userContextInflight.delete(key);
2570
- scheduleUserContext(mainOrigin, config);
2571
- } catch {
2572
- }
2573
- })();
2574
- }
2575
2422
 
2576
2423
  // src/commands/login.ts
2577
2424
  async function runLogin(opts = {}) {
@@ -2582,7 +2429,6 @@ async function runLogin(opts = {}) {
2582
2429
  process.exit(1);
2583
2430
  }
2584
2431
  writeSharedConfig({ authToken: token });
2585
- refreshSiluzanUser(DEFAULT_API_BASE, { authToken: token });
2586
2432
  console.log(`
2587
2433
  \u2705 Token \u5DF2\u4FDD\u5B58\uFF08${maskSecret(token)}\uFF09`);
2588
2434
  console.log(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CONFIG_FILE}`);
@@ -2597,7 +2443,6 @@ async function runLogin(opts = {}) {
2597
2443
  process.exit(1);
2598
2444
  }
2599
2445
  writeSharedConfig({ apiKey: key });
2600
- refreshSiluzanUser(DEFAULT_API_BASE, { authToken: "", apiKey: key });
2601
2446
  console.log(`
2602
2447
  \u2705 API Key \u5DF2\u4FDD\u5B58\uFF08${maskSecret(key)}\uFF09`);
2603
2448
  console.log(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CONFIG_FILE}`);
@@ -2638,7 +2483,6 @@ async function runLogin(opts = {}) {
2638
2483
  process.exit(1);
2639
2484
  }
2640
2485
  writeSharedConfig({ apiKey });
2641
- refreshSiluzanUser(DEFAULT_API_BASE, { authToken: "", apiKey });
2642
2486
  console.log(`
2643
2487
  \u2705 API Key \u5DF2\u4FDD\u5B58\uFF08${maskSecret(apiKey)}\uFF09`);
2644
2488
  console.log(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CONFIG_FILE}`);
@@ -2823,6 +2667,7 @@ async function runUpdate(options) {
2823
2667
  }
2824
2668
  console.log(`
2825
2669
  \u{1F504} \u6B63\u5728\u66F4\u65B0 ${targets.length} \u5904 skill \u5B89\u88C5\u4F4D\u7F6E \u2026`);
2670
+ let failCount = 0;
2826
2671
  for (const entry of targets) {
2827
2672
  const label = entry.target === "custom" ? entry.dir ?? "unknown" : entry.target;
2828
2673
  const cwd = entry.cwd || os4.homedir();
@@ -2846,9 +2691,16 @@ async function runUpdate(options) {
2846
2691
  });
2847
2692
  }
2848
2693
  } catch (e) {
2694
+ failCount++;
2849
2695
  console.error(` \u274C \u66F4\u65B0\u5931\u8D25\uFF1A${e.message}`);
2850
2696
  }
2851
2697
  }
2698
+ if (failCount > 0) {
2699
+ console.error(`
2700
+ \u26A0\uFE0F ${targets.length} \u5904\u5B89\u88C5\u4F4D\u7F6E\u4E2D\u6709 ${failCount} \u5904\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u4E0A\u65B9\u9519\u8BEF\u4FE1\u606F\u3002`);
2701
+ console.log(" \u5982\u679C AI \u52A9\u624B\u6B63\u5728\u8FD0\u884C\uFF0C\u5EFA\u8BAE\u91CD\u542F\u4EE5\u4F7F\u5DF2\u6210\u529F\u7684 skill \u6587\u4EF6\u751F\u6548\u3002\n");
2702
+ process.exit(1);
2703
+ }
2852
2704
  console.log("\n\u2705 \u5168\u90E8 skill \u6587\u4EF6\u5DF2\u5237\u65B0\u3002");
2853
2705
  console.log(" \u5982\u679C AI \u52A9\u624B\u6B63\u5728\u8FD0\u884C\uFF0C\u5EFA\u8BAE\u91CD\u542F\u4EE5\u4F7F\u65B0 skill \u6587\u4EF6\u751F\u6548\u3002\n");
2854
2706
  }
@@ -2885,7 +2737,6 @@ function loadConfig(tokenArg) {
2885
2737
  \u274C agentBaseUrl \u4E0D\u5408\u6CD5\uFF1A${agentErr}`);
2886
2738
  process.exit(1);
2887
2739
  }
2888
- setSiluzanCliMeta("siluzan-cso", getCurrentVersion2());
2889
2740
  return { apiBaseUrl, csoBaseUrl, agentBaseUrl, authToken, apiKey, dataPermission: shared.dataPermission };
2890
2741
  }
2891
2742
  function apiFetch2(url, config, options = {}, verbose = false) {
@@ -3045,8 +2896,7 @@ async function runListAccounts(options) {
3045
2896
  mediaCustomerName: a.mediaCustomerName,
3046
2897
  mediaAccountState: a.mediaAccountState ?? "",
3047
2898
  invalidOAuthToken: a.invalidOAuthToken ?? false,
3048
- // 优先使用 expiresOn(OAuth Token 实际到期时间),fallback 到 tokenTime
3049
- expiresOn: a.expiresOn ?? a.tokenTime ?? null,
2899
+ expiresOn: a.expiresOn ?? null,
3050
2900
  lastAuthorizationTime: a.lastAuthorizationTime ?? null,
3051
2901
  totalActiveTaskCount: a.totalActiveTaskCount ?? 0,
3052
2902
  ownerInfo: a.ownerInfo ?? [],
@@ -3091,7 +2941,7 @@ async function runListAccounts(options) {
3091
2941
  { key: "commentCount", header: "\u8BC4\u8BBA\u6570" },
3092
2942
  { key: "diggCount", header: "\u83B7\u8D5E\u6570" },
3093
2943
  { key: "lastAuthTime", header: "\u4E0A\u6B21\u6388\u6743" },
3094
- { key: "tokenExpiry", header: "Token\u5230\u671F" },
2944
+ { key: "expiresOn", header: "Token\u5230\u671F" },
3095
2945
  { key: "owners", header: "\u8D1F\u8D23\u4EBA" }
3096
2946
  ];
3097
2947
  for (const [platform, accounts] of groups) {
@@ -3110,7 +2960,7 @@ async function runListAccounts(options) {
3110
2960
  commentCount: formatCount(ov?.commentCount),
3111
2961
  diggCount: formatCount(ov?.diggCount),
3112
2962
  lastAuthTime: formatDate(a.lastAuthorizationTime),
3113
- tokenExpiry: formatDate(a.expiresOn ?? a.tokenTime),
2963
+ expiresOn: formatDate(a.expiresOn),
3114
2964
  owners: formatOwners(a.ownerInfo)
3115
2965
  };
3116
2966
  });
@@ -6421,11 +6271,21 @@ function cmdConfigClear() {
6421
6271
  }
6422
6272
 
6423
6273
  // src/index.ts
6274
+ process.on("uncaughtException", (err) => {
6275
+ console.error(`
6276
+ \u274C \u672A\u6355\u83B7\u7684\u5F02\u5E38\uFF1A${err.message}`);
6277
+ if (process.argv.includes("--verbose")) console.error(err.stack);
6278
+ process.exit(1);
6279
+ });
6280
+ process.on("unhandledRejection", (reason) => {
6281
+ const msg = reason instanceof Error ? reason.message : String(reason);
6282
+ console.error(`
6283
+ \u274C \u672A\u5904\u7406\u7684\u5F02\u6B65\u9519\u8BEF\uFF1A${msg}`);
6284
+ if (process.argv.includes("--verbose") && reason instanceof Error) console.error(reason.stack);
6285
+ process.exit(1);
6286
+ });
6424
6287
  var program = new Command();
6425
6288
  program.name("siluzan-cso").description("Siluzan \u5E73\u53F0 Skill \u5DE5\u5177\uFF1A\u521D\u59CB\u5316\u3001\u8D26\u53F7\u67E5\u8BE2\u3001\u4E0A\u4F20\u3001\u56FE\u6587/\u89C6\u9891\u53D1\u5E03").version(getCurrentVersion2());
6426
- program.hook("preAction", () => {
6427
- setSiluzanCliInvocation(redactCliArgvForSentry(process.argv));
6428
- });
6429
6289
  var configCmd = program.command("config").description("\u67E5\u770B\u6216\u8BBE\u7F6E Siluzan \u8BA4\u8BC1\u914D\u7F6E\uFF08~/.siluzan/config.json\uFF09");
6430
6290
  configCmd.command("show").description("\u5C55\u793A\u5F53\u524D\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E\uFF08Token \u8131\u654F\u663E\u793A\uFF09").action(() => cmdConfigShow());
6431
6291
  configCmd.command("set").description("\u4FDD\u5B58\u914D\u7F6E\u5230 ~/.siluzan/config.json\uFF0C\u540E\u7EED\u547D\u4EE4\u81EA\u52A8\u8BFB\u53D6").option("--api-key <key>", "API Key\uFF08\u63A8\u8350\uFF0C\u4E0E siluzan-tso \u5171\u7528\uFF0C\u4F18\u5148\u7EA7\u9AD8\u4E8E token\uFF09").option("-t, --token <token>", "\u7528\u6237 Auth Token\uFF08JWT\uFF09").action((opts) => {
@@ -9,6 +9,54 @@ compatibility: Requires siluzan-cso-cli installed and authenticated via `siluzan
9
9
 
10
10
  # siluzan-cso
11
11
 
12
+ ## 前置条件与运行时依赖
13
+
14
+ 使用本 Skill 前,以下组件必须已安装并就绪:
15
+
16
+ ### 必需二进制 / 运行时
17
+ | 依赖 | 最低版本 | 用途 |
18
+ |------|---------|------|
19
+ | **Node.js** | 18+ | CLI 执行运行时以及 `node -e` 数据过滤 |
20
+ | **npm** 或 **pnpm** | npm 8+ / pnpm 8+ | 安装 `siluzan-cso-cli` 全局包 |
21
+ | **siluzan-cso** CLI | 与 Skill 同版本 | 所有业务操作的执行入口(由 `npm install -g siluzan-cso-cli` 安装) |
22
+
23
+ ### 可选二进制
24
+ | 依赖 | 用途 |
25
+ |------|------|
26
+ | **ffmpeg** | `extract-cover` 命令截取视频封面帧(未安装时该命令会报错提示) |
27
+
28
+ ### 凭据与配置文件
29
+ | 项目 | 位置 | 说明 |
30
+ |------|------|------|
31
+ | API Key 或 Token | `~/.siluzan/config.json` 中的 `apiKey` 或 `authToken` 字段 | 用于所有 CSO API 调用的鉴权。API Key 在丝路赞控制台「个人设置 → API Key 管理」创建。**请勿将此文件提交到 Git。** |
32
+ | csoBaseUrl | `~/.siluzan/config.json` 中的 `apiBaseUrl` | CSO 后端 API 地址,默认值由 CLI 构建时写入 |
33
+
34
+ ### 网络端点
35
+ 本 Skill 通过 CLI 访问以下远程服务:
36
+ | 端点 | 用途 |
37
+ |------|------|
38
+ | `api.siluzan.com` / `api-ci.siluzan.com` | CSO 核心 API(账号管理、发布、素材、报表等) |
39
+ | `cso.siluzan.com` / `cso-ci.siluzan.com` | CSO 前端页面(授权回调等浏览器操作) |
40
+ | `www.siluzan.com` / `www-ci.siluzan.com` | 丝路赞主站(注册、API Key 管理) |
41
+ | `registry.npmjs.org` | 版本更新检查 |
42
+
43
+ ### 可选环境变量
44
+ | 变量 | 说明 |
45
+ |------|------|
46
+ | `SILUZAN_AUTH_TOKEN` | 从环境变量读取 Token(优先级高于 config.json,CI/CD 推荐) |
47
+
48
+ 如果上述依赖缺失,请先参照 `references/setup.md` 完成安装与配置。
49
+
50
+ ---
51
+
52
+ ## 可执行的操作范围
53
+
54
+ - **只读**:查询媒体账号列表、账号分组、运营报表、发布任务状态、人设列表、RAG 知识库检索、AI 内容规划详情
55
+ - **写入**(需用户确认):上传素材、提交发布任务、创建/更新账号分组、生成 AI 内容规划、站内信回复
56
+ - **本地文件操作**:`extract-cover` 在本地截取视频帧并输出图片文件;`init` 将 Skill 文件写入 AI 助手目录
57
+
58
+ ---
59
+
12
60
  ## 能力范围
13
61
 
14
62
  | 业务流程 | 手段 | 说明 |
@@ -37,10 +85,12 @@ compatibility: Requires siluzan-cso-cli installed and authenticated via `siluzan
37
85
  | `siluzan-cso task list/detail/item` | 查看任务状态 / 处理失败 / 重试 | `references/task.md` |
38
86
  | `siluzan-cso report fetch --media <平台>` | 运营报表(核心指标 / 视频排行 / 趋势) | `references/report.md` |
39
87
  | `siluzan-cso planning ...` | AI 内容规划:生成、监控、详情、导出 | `references/planning.md` |
40
- | —(网页端) | CSO 后台全部页面 URL(含测试/生产环境) | `references/web-pages.md` |
88
+ | —(网页端) | CSO web端全部页面 URL | `references/web-pages.md` |
41
89
 
42
90
  ---
43
91
 
92
+
93
+
44
94
  ## 常见业务场景 → 阅读哪个文件
45
95
 
46
96
  | 用户在做什么 | 先阅读 |
@@ -95,14 +145,15 @@ account-group ──需要 mediaCustomerId──► list-accounts
95
145
  - **先查账号再操作**:对具体账号做操作前,先用 `list-accounts --name <名称> --media-type <平台>` 确认账号存在且 Token 有效。
96
146
  - **使用 `--json` 处理数据**:需要对返回结果做计算或筛选时,加 `--json` 再用 `node -e` 提取(`node -e "const d=require('fs').readFileSync('/dev/stdin','utf8'); ..."`)。
97
147
  - **不猜账号 ID**:`entityId` ≠ `mediaCustomerId`,两者均须从 `list-accounts --json` 获取,不可假设。
98
- - **严禁让用户执行命令**:除 `references/setup.md` 中的安装/登录命令外,所有 CLI 命令均由 AI 代替用户执行,禁止向用户透露任何命令行、参数或代码逻辑。
148
+ - **命令透明性**:以简洁的方式向用户说明即将执行的操作意图(如「正在上传视频到素材库」「正在为您查询 YouTube 账号列表」),让用户了解操作进度。用户主动要求查看执行细节时,应如实提供完整命令。安装/登录/更新等一次性命令(见 `references/setup.md`)可直接展示给用户自行执行。
99
149
  - **操作后必须验证**:完成发布、上传、分组等写操作后,需通过对应的查询命令确认结果。
100
150
 
101
- ### 必须遵守(违反将导致 skill 不可用)
102
-
103
- - 输出的任何内容(计划、总结、报告)中,**严禁出现 CLI 命令**(setup.md 相关命令除外)。
104
- - 每次执行前主动检查 CLI 版本,若需更新请先执行更新流程(详见 `references/setup.md`)。
151
+ ### 必须遵守
105
152
 
153
+ - 主动更新(详情请读取 `references/setup.md`)。
154
+ - **破坏性操作必须用户确认**:涉及写入/修改/删除的操作(发布、上传、分组变更等),执行前必须明确告知用户操作内容并获得确认。
155
+ - **只读操作可自主执行**:查询类命令(`list-accounts`、`report fetch`、`task list`、`config show` 等)可直接执行,无需额外确认。
156
+ - 禁止提供虚假信息,比如web端连接就必须确认 `references/web-pages.md` 中存在才能提供给用户
106
157
  ---
107
158
 
108
159
  ## 常见 HTTP 错误处理
@@ -123,6 +174,6 @@ account-group ──需要 mediaCustomerId──► list-accounts
123
174
 
124
175
  > 无对应 CLI 命令的模块,或需要引导用户在网页端查看数据时,查阅 `references/web-pages.md` 获取完整页面清单与链接。
125
176
 
126
- URL 格式:`https://www.siluzan.com/v3/foreign_trade/cso/{页面}`
177
+ URL 格式:`https://www-ci.siluzan.com/v3/foreign_trade/cso/{页面}`
127
178
 
128
179
  常用页面:`task`(任务管理)· `postVideo`(发布页)· `ManageAccounts`(账号管理)· `planning`(AI 内容规划)· `table`(绩效报表)· `Workdata`(作品数据)
@@ -1,5 +1,21 @@
1
1
  {
2
2
  "slug": "siluzan-cso",
3
- "version": "1.1.8",
4
- "publishedAt": 1775724298669
3
+ "version": "1.1.9-beta.2",
4
+ "publishedAt": 1776070442759,
5
+ "homepage": "https://www.siluzan.com",
6
+ "source": "https://dev.azure.com/jack4it/Sammamish/_git/siluzan-skill",
7
+ "requiredBinaries": [
8
+ "node",
9
+ "npm"
10
+ ],
11
+ "optionalBinaries": [
12
+ "ffmpeg"
13
+ ],
14
+ "requiredEnv": [],
15
+ "optionalEnv": [
16
+ "SILUZAN_AUTH_TOKEN"
17
+ ],
18
+ "configPaths": [
19
+ "~/.siluzan/config.json"
20
+ ]
5
21
  }
@@ -199,7 +199,7 @@ siluzan-cso publish -c publish-config.json
199
199
 
200
200
  > 查询发布状态、处理失败项 → 参见 `references/task.md`
201
201
 
202
- 前往 CSO 任务管理页面查看进度:`https://www.siluzan.com/v3/foreign_trade/cso/task`
202
+ 前往 CSO 任务管理页面查看进度:`https://www-ci.siluzan.com/v3/foreign_trade/cso/task`
203
203
 
204
204
  ---
205
205
 
@@ -3,10 +3,10 @@
3
3
  ## 安装 CLI
4
4
 
5
5
  ```bash
6
- npm install -g siluzan-cso-cli
6
+ npm install -g siluzan-cso-cli@beta
7
7
  ```
8
8
 
9
- 环境要求:Node.js 24+
9
+ 环境要求:Node.js 18+
10
10
 
11
11
  ---
12
12
 
@@ -44,7 +44,7 @@ siluzan-cso config set --token <Token> # 备用:设置 JWT Token
44
44
 
45
45
  > **⚠️ 不要使用 `config set --token <token>` 的方式。** 该方式会将 Token 明文写入 shell history(`~/.bash_history`、`~/.zsh_history`、PowerShell 历史),存在凭证泄露风险。推荐使用 `siluzan-cso login` 交互式输入。
46
46
 
47
- API Key 获取入口:`https://cso.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
47
+ API Key 获取入口:`https://cso-ci.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
48
48
 
49
49
  **Token 读取优先级(由高到低):**
50
50
  1. 环境变量 `SILUZAN_AUTH_TOKEN`(CI/CD 推荐)
@@ -63,8 +63,8 @@ siluzan-cso config show
63
63
  输出示例:
64
64
  ```
65
65
  构建环境 : production
66
- apiBaseUrl : https://api.siluzan.com
67
- csoBaseUrl : https://cso.siluzan.com
66
+ apiBaseUrl : https://api-ci.siluzan.com
67
+ csoBaseUrl : https://cso-ci.siluzan.com
68
68
  apiKey : abcd****1234
69
69
  ```
70
70
 
@@ -1,7 +1,7 @@
1
1
  # web-pages — CSO 后台页面速查
2
2
 
3
3
  > 当需要引导用户前往网页端查看数据或执行操作时,使用本文件中的页面链接。
4
- > URL 格式:`https://www.siluzan.com/v3/foreign_trade/cso/{页面路径}`
4
+ > URL 格式:`https://www-ci.siluzan.com/v3/foreign_trade/cso/{页面路径}`
5
5
 
6
6
  ---
7
7
 
@@ -11,9 +11,9 @@
11
11
 
12
12
  | 页面 | 完整链接 | 功能说明 |
13
13
  |------|----------|----------|
14
- | 账号管理 | `https://www.siluzan.com/v3/foreign_trade/cso/ManageAccounts` | 绑定/授权/查看媒体账号列表、账号状态、Token 到期时间 |
15
- | 账号分组 | `https://www.siluzan.com/v3/foreign_trade/cso/AccountGroup` | 新建分组、管理分组内账号 |
16
- | 重点账号 | `https://www.siluzan.com/v3/foreign_trade/cso/KeyAccounts` | 配置重点关注账号、数据备份 |
14
+ | 账号管理 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/ManageAccounts` | 绑定/授权/查看媒体账号列表、账号状态、Token 到期时间 |
15
+ | 账号分组 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/AccountGroup` | 新建分组、管理分组内账号 |
16
+ | 重点账号 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/KeyAccounts` | 配置重点关注账号、数据备份 |
17
17
 
18
18
 
19
19
  ---
@@ -22,10 +22,10 @@
22
22
 
23
23
  | 页面 | 完整链接 | 功能说明 |
24
24
  |------|----------|----------|
25
- | 发布作品 | `https://www.siluzan.com/v3/foreign_trade/cso/postVideo?contentType=1` | 矩阵发布视频、图文、草稿管理、话题组 contentType=1 是视频,contentType=2是图文 |
25
+ | 发布作品 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/postVideo?contentType=1` | 矩阵发布视频、图文、草稿管理、话题组 contentType=1 是视频,contentType=2是图文 |
26
26
 
27
- | 发布日历 | `https://www.siluzan.com/v3/foreign_trade/cso/publishCalendar` | 日历视图规划发布任务、创建/编辑发布任务 |
28
- | 营销日历 | `https://www.siluzan.com/v3/foreign_trade/cso/marketingCalendar` | 营销节点日历、跳转创建发布任务 |
27
+ | 发布日历 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/publishCalendar` | 日历视图规划发布任务、创建/编辑发布任务 |
28
+ | 营销日历 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/marketingCalendar` | 营销节点日历、跳转创建发布任务 |
29
29
 
30
30
  ---
31
31
 
@@ -33,10 +33,10 @@
33
33
 
34
34
  | 页面 | 完整链接 | 功能说明 |
35
35
  |------|----------|----------|
36
- | 任务列表 | `https://www.siluzan.com/v3/foreign_trade/cso/task` | 发布任务列表、状态筛选、任务详情抽屉 |
37
- | 视频管理 | `https://www.siluzan.com/v3/foreign_trade/cso/VideoMgr` | 已发布视频/图文列表、删除、重发、评论查看 |
38
- | 视频搬家 | `https://www.siluzan.com/v3/foreign_trade/cso/relocation` | 将视频搬运到其他平台 |
39
- | 搬家记录 | `https://www.siluzan.com/v3/foreign_trade/cso/MovingRecord` | 搬家任务列表与执行状态 |
36
+ | 任务列表 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/task` | 发布任务列表、状态筛选、任务详情抽屉 |
37
+ | 视频管理 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/VideoMgr` | 已发布视频/图文列表、删除、重发、评论查看 |
38
+ | 视频搬家 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/relocation` | 将视频搬运到其他平台 |
39
+ | 搬家记录 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/MovingRecord` | 搬家任务列表与执行状态 |
40
40
 
41
41
  ---
42
42
 
@@ -44,9 +44,9 @@
44
44
 
45
45
  | 页面 | 完整链接 | 功能说明 |
46
46
  |------|----------|----------|
47
- | 私信管理 | `https://www.siluzan.com/v3/foreign_trade/cso/letter` | 按渠道/时间查看与处理私信(多平台 Tab) |
48
- | 评论管理 | `https://www.siluzan.com/v3/foreign_trade/cso/comment` | 收到的评论列表、回复、账号组筛选 |
49
- | 智能互动 | `https://www.siluzan.com/v3/foreign_trade/cso/interaction` | 私信欢迎语、自动回复规则配置 |
47
+ | 私信管理 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/letter` | 按渠道/时间查看与处理私信(多平台 Tab) |
48
+ | 评论管理 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/comment` | 收到的评论列表、回复、账号组筛选 |
49
+ | 智能互动 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/interaction` | 私信欢迎语、自动回复规则配置 |
50
50
 
51
51
  ---
52
52
 
@@ -54,9 +54,9 @@
54
54
 
55
55
  | 页面 | 完整链接 | 功能说明 |
56
56
  |------|----------|----------|
57
- | 作品数据 | `https://www.siluzan.com/v3/foreign_trade/cso/Workdata` | 作品维度统计、图表、明细(对应 CLI `report fetch`) |
58
- | 账户数据 | `https://www.siluzan.com/v3/foreign_trade/cso/accountdata` | 账户维度汇总数据、趋势图表 |
59
- | 绩效报表 | `https://www.siluzan.com/v3/foreign_trade/cso/table` | 多维度绩效报表、PDF 导出(对应 CLI `report fetch/records/download`) |
57
+ | 作品数据 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/Workdata` | 作品维度统计、图表、明细(对应 CLI `report fetch`) |
58
+ | 账户数据 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/accountdata` | 账户维度汇总数据、趋势图表 |
59
+ | 绩效报表 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/table` | 多维度绩效报表、PDF 导出(对应 CLI `report fetch/records/download`) |
60
60
 
61
61
  ---
62
62
 
@@ -64,10 +64,10 @@
64
64
 
65
65
  | 页面 | 完整链接 | 功能说明 |
66
66
  |------|----------|----------|
67
- | 内容规划 | `https://www.siluzan.com/v3/foreign_trade/cso/planning` | AI 内容规划列表、生成规划、企业维度筛选(对应 CLI `planning`) |
68
- | 营销首页 | `https://www.siluzan.com/v3/foreign_trade/cso/ContentHome` | 工作台总览:账号数、视频数、最新评论与视频 |
67
+ | 内容规划 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/planning` | AI 内容规划列表、生成规划、企业维度筛选(对应 CLI `planning`) |
68
+ | 营销首页 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/ContentHome` | 工作台总览:账号数、视频数、最新评论与视频 |
69
69
 
70
- | 话题组 | `https://www.siluzan.com/v3/foreign_trade/cso/TopicGroup` | 话题组维护、话题内容管理 |
70
+ | 话题组 | `https://www-ci.siluzan.com/v3/foreign_trade/cso/TopicGroup` | 话题组维护、话题内容管理 |
71
71
 
72
72
 
73
73
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-cso-cli",
3
- "version": "1.1.8",
3
+ "version": "1.1.9-beta.2",
4
4
  "description": "Siluzan platform AI Skill CLI — multi-platform content publishing (video/image-text) for Cursor, Claude Code, and OpenClaw.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,13 +24,21 @@
24
24
  "content-publishing",
25
25
  "cli"
26
26
  ],
27
+ "homepage": "https://www.siluzan.com",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://dev.azure.com/jack4it/Sammamish/_git/siluzan-skill",
31
+ "directory": "cso-cli"
32
+ },
33
+ "bugs": {
34
+ "url": "https://dev.azure.com/jack4it/Sammamish/_git/siluzan-skill/issues"
35
+ },
27
36
  "license": "UNLICENSED",
28
37
  "publishConfig": {
29
38
  "access": "public"
30
39
  },
31
40
  "dependencies": {
32
41
  "@azure/storage-blob": "^12.31.0",
33
- "@sentry/node": "9",
34
42
  "cli-table3": "^0.6.5",
35
43
  "commander": "^12.1.0",
36
44
  "image-size": "^2.0.2"
@@ -42,7 +50,7 @@
42
50
  "siluzan-cli-common": "1.0.0"
43
51
  },
44
52
  "engines": {
45
- "node": ">=24"
53
+ "node": ">=18"
46
54
  },
47
55
  "scripts": {
48
56
  "postinstall": "node scripts/postinstall.mjs",