siluzan-cso-cli 1.1.8-beta.6 → 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+**
@@ -20,7 +50,7 @@ siluzan-cso init -d /path/to/skills # 写入自定义目录
20
50
  siluzan-cso init --force # 强制覆盖已存在文件
21
51
  ```
22
52
 
23
- > **注意**:当前为测试版(1.1.8-beta.6),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
53
+ > **注意**:当前为测试版(1.1.9-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
24
54
 
25
55
  | 助手 | 建议 `--ai` |
26
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
  }
@@ -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 错误处理
@@ -1,5 +1,21 @@
1
1
  {
2
2
  "slug": "siluzan-cso",
3
- "version": "1.1.8-beta.6",
4
- "publishedAt": 1775717101837
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
  }
@@ -6,7 +6,7 @@
6
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-cso-cli",
3
- "version": "1.1.8-beta.6",
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",