siluzan-cso-cli 1.1.16-beta.1 → 1.1.17-beta.1

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
@@ -54,7 +54,7 @@ siluzan-cso init -d /path/to/skills # 写入自定义目录
54
54
  siluzan-cso init --force # 强制覆盖已存在文件
55
55
  ```
56
56
 
57
- > **注意**:当前为测试版(1.1.16-beta.1),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
57
+ > **注意**:当前为测试版(1.1.17-beta.1),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
58
58
 
59
59
  | 助手 | 建议 `--ai` |
60
60
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -2151,12 +2151,35 @@ import * as path3 from "path";
2151
2151
  import * as os2 from "os";
2152
2152
  import * as https from "https";
2153
2153
  import * as http from "http";
2154
+ import * as zlib from "zlib";
2155
+ import { performance } from "perf_hooks";
2154
2156
  import { randomUUID as _randomUUID } from "crypto";
2155
2157
  import * as fs22 from "fs";
2156
2158
  import * as path22 from "path";
2157
2159
  import { fileURLToPath as fileURLToPath2 } from "url";
2158
2160
  var SILUZAN_DIR = path3.join(os2.homedir(), ".siluzan");
2159
2161
  var CONFIG_FILE = path3.join(SILUZAN_DIR, "config.json");
2162
+ function atomicWriteFileSync(targetPath, content, encoding = "utf8") {
2163
+ const dir = path3.dirname(targetPath);
2164
+ const tmp = path3.join(
2165
+ dir,
2166
+ `.${path3.basename(targetPath)}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`
2167
+ );
2168
+ try {
2169
+ fs3.writeFileSync(tmp, content, encoding);
2170
+ fs3.renameSync(tmp, targetPath);
2171
+ } catch (err) {
2172
+ try {
2173
+ fs3.unlinkSync(tmp);
2174
+ } catch {
2175
+ }
2176
+ try {
2177
+ fs3.writeFileSync(targetPath, content, encoding);
2178
+ } catch {
2179
+ throw err;
2180
+ }
2181
+ }
2182
+ }
2160
2183
  function readStr(raw, key) {
2161
2184
  const v = raw[key];
2162
2185
  return typeof v === "string" && v ? v : void 0;
@@ -2189,7 +2212,7 @@ function writeSharedConfig(partial) {
2189
2212
  for (const k of keys) {
2190
2213
  if (partial[k] !== void 0) existing[k] = partial[k];
2191
2214
  }
2192
- fs3.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2), "utf8");
2215
+ atomicWriteFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
2193
2216
  if (process.platform !== "win32") {
2194
2217
  try {
2195
2218
  fs3.chmodSync(CONFIG_FILE, 384);
@@ -2233,28 +2256,96 @@ function validateBaseUrl(raw) {
2233
2256
  }
2234
2257
  var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
2235
2258
  var MAX_RESPONSE_BYTES = 50 * 1024 * 1024;
2259
+ var httpsAgent = new https.Agent({
2260
+ keepAlive: true,
2261
+ keepAliveMsecs: 3e4,
2262
+ maxSockets: 16,
2263
+ maxFreeSockets: 8,
2264
+ scheduling: "lifo"
2265
+ });
2266
+ var httpAgent = new http.Agent({
2267
+ keepAlive: true,
2268
+ keepAliveMsecs: 3e4,
2269
+ maxSockets: 16,
2270
+ maxFreeSockets: 8,
2271
+ scheduling: "lifo"
2272
+ });
2273
+ var PERF_PREFIX = "[SILUZAN_HTTP_PERF]";
2274
+ function isPerfEnabled() {
2275
+ const v = process.env.SILUZAN_HTTP_PERF;
2276
+ return v === "1" || v === "true";
2277
+ }
2278
+ function emitPerf(record) {
2279
+ try {
2280
+ process.stderr.write(`${PERF_PREFIX} ${JSON.stringify(record)}
2281
+ `);
2282
+ } catch {
2283
+ }
2284
+ }
2236
2285
  function rawRequest(url, options) {
2286
+ const perfOn = isPerfEnabled();
2287
+ const t0 = perfOn ? performance.now() : 0;
2288
+ const method = options.method ?? "GET";
2237
2289
  return new Promise((resolve22, reject) => {
2238
2290
  const parsed = new URL(url);
2239
2291
  const transport = parsed.protocol === "https:" ? https : http;
2240
2292
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2241
2293
  const maxBytes = options.maxResponseBytes ?? MAX_RESPONSE_BYTES;
2294
+ const isHttps = parsed.protocol === "https:";
2242
2295
  const reqOpts = {
2243
2296
  hostname: parsed.hostname,
2244
- port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
2297
+ port: parsed.port || (isHttps ? 443 : 80),
2245
2298
  path: parsed.pathname + parsed.search,
2246
- method: options.method ?? "GET",
2299
+ method,
2247
2300
  headers: options.headers,
2248
- timeout: timeoutMs || void 0
2301
+ timeout: timeoutMs || void 0,
2302
+ agent: isHttps ? httpsAgent : httpAgent
2249
2303
  };
2250
2304
  const req = transport.request(reqOpts, (res) => {
2305
+ const encoding = (res.headers["content-encoding"] ?? "").toString().toLowerCase().trim();
2306
+ let stream = res;
2307
+ if (encoding === "gzip" || encoding === "x-gzip") {
2308
+ stream = res.pipe(zlib.createGunzip());
2309
+ } else if (encoding === "deflate") {
2310
+ stream = res.pipe(zlib.createInflate());
2311
+ } else if (encoding === "br") {
2312
+ stream = res.pipe(zlib.createBrotliDecompress());
2313
+ }
2314
+ const handleStreamError = (err) => {
2315
+ res.destroy();
2316
+ if (perfOn) {
2317
+ emitPerf({
2318
+ method,
2319
+ url,
2320
+ status: res.statusCode ?? 0,
2321
+ elapsedMs: performance.now() - t0,
2322
+ bytes: 0,
2323
+ ok: false,
2324
+ error: `decompress error: ${err.message}`
2325
+ });
2326
+ }
2327
+ reject(new Error(`\u54CD\u5E94\u89E3\u538B\u5931\u8D25\uFF08${encoding || "identity"}\uFF09\uFF1A${err.message}`));
2328
+ };
2329
+ if (stream !== res) stream.on("error", handleStreamError);
2330
+ res.on("error", handleStreamError);
2251
2331
  let data = "";
2252
2332
  let byteLen = 0;
2253
- res.setEncoding("utf8");
2254
- res.on("data", (chunk) => {
2333
+ stream.setEncoding("utf8");
2334
+ stream.on("data", (chunk) => {
2255
2335
  byteLen += Buffer.byteLength(chunk, "utf8");
2256
2336
  if (maxBytes && byteLen > maxBytes) {
2257
2337
  res.destroy();
2338
+ if (perfOn) {
2339
+ emitPerf({
2340
+ method,
2341
+ url,
2342
+ status: res.statusCode ?? 0,
2343
+ elapsedMs: performance.now() - t0,
2344
+ bytes: byteLen,
2345
+ ok: false,
2346
+ error: "response body exceeded limit"
2347
+ });
2348
+ }
2258
2349
  reject(
2259
2350
  new Error(`\u54CD\u5E94\u4F53\u8D85\u8FC7\u4E0A\u9650\uFF08${(maxBytes / 1024 / 1024).toFixed(0)} MB\uFF09\uFF0C\u5DF2\u4E2D\u65AD\u8FDE\u63A5`)
2260
2351
  );
@@ -2262,25 +2353,60 @@ function rawRequest(url, options) {
2262
2353
  }
2263
2354
  data += chunk;
2264
2355
  });
2265
- res.on("end", () => {
2356
+ stream.on("end", () => {
2266
2357
  const headers = {};
2267
2358
  for (const [key, val] of Object.entries(res.headers)) {
2268
2359
  if (val !== void 0) {
2269
2360
  headers[key.toLowerCase()] = Array.isArray(val) ? val[0] : val;
2270
2361
  }
2271
2362
  }
2272
- resolve22({ status: res.statusCode ?? 0, text: data, headers });
2363
+ const status = res.statusCode ?? 0;
2364
+ if (perfOn) {
2365
+ emitPerf({
2366
+ method,
2367
+ url,
2368
+ status,
2369
+ elapsedMs: performance.now() - t0,
2370
+ bytes: byteLen,
2371
+ ok: status >= 200 && status < 300
2372
+ });
2373
+ }
2374
+ resolve22({ status, text: data, headers });
2273
2375
  });
2274
2376
  });
2275
2377
  req.on("timeout", () => {
2276
2378
  req.destroy();
2379
+ if (perfOn) {
2380
+ emitPerf({
2381
+ method,
2382
+ url,
2383
+ status: 0,
2384
+ elapsedMs: performance.now() - t0,
2385
+ bytes: 0,
2386
+ ok: false,
2387
+ error: "timeout"
2388
+ });
2389
+ }
2277
2390
  reject(
2278
2391
  new Error(
2279
- `\u8BF7\u6C42\u8D85\u65F6\uFF08${(timeoutMs / 1e3).toFixed(0)} \u79D2\uFF09\uFF1A${options.method ?? "GET"} ${url}`
2392
+ `\u8BF7\u6C42\u8D85\u65F6\uFF08${(timeoutMs / 1e3).toFixed(0)} \u79D2\uFF09\uFF1A${method} ${url}`
2280
2393
  )
2281
2394
  );
2282
2395
  });
2283
- req.on("error", reject);
2396
+ req.on("error", (err) => {
2397
+ if (perfOn) {
2398
+ emitPerf({
2399
+ method,
2400
+ url,
2401
+ status: 0,
2402
+ elapsedMs: performance.now() - t0,
2403
+ bytes: 0,
2404
+ ok: false,
2405
+ error: err.message
2406
+ });
2407
+ }
2408
+ reject(err);
2409
+ });
2284
2410
  if (options.body) req.write(options.body);
2285
2411
  req.end();
2286
2412
  });
@@ -2300,6 +2426,8 @@ async function apiFetch(url, config, options = {}, verbose = false) {
2300
2426
  const reqHeaders = {
2301
2427
  "Content-Type": "application/json",
2302
2428
  "Accept-Language": "zh-CN",
2429
+ // 声明支持 gzip/deflate/br;服务端不支持则按 identity 返回,rawRequest 会原样收取
2430
+ "Accept-Encoding": "gzip, deflate, br",
2303
2431
  ...authHeaders,
2304
2432
  // dataPermission 仅 TSO 使用;CSO 未设置时为空字符串,服务端忽略该头
2305
2433
  Datapermission: config.dataPermission ?? "",
@@ -19,6 +19,8 @@ compatibility: Requires siluzan-cso-cli installed and authenticated via `siluzan
19
19
  irm https://unpkg.com/siluzan-cso-cli@latest/dist/skill/scripts/install.ps1 | iex
20
20
  ```
21
21
 
22
+ Windows 注意:部分 Agent 客户端通过 PowerShell / cmd 代执行命令时存在兼容性问题。若上述命令异常失败,请先安装 [Git for Windows](https://git-scm.com/download/win),然后在 Git Bash 中执行 macOS / Linux / WSL 的 Bash 安装命令。
23
+
22
24
  脚本会自动完成 Node.js 检测/安装、CLI 安装、Skill 全局注册,并引导用户配置 API Key。无需选择,本脚本专为 siluzan-cso-cli 定制。
23
25
 
24
26
  ---
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "slug": "siluzan-cso",
3
- "version": "1.1.16-beta.1",
4
- "publishedAt": 1777543530152,
3
+ "version": "1.1.17-beta.1",
4
+ "publishedAt": 1778229754485,
5
5
  "homepage": "https://www.siluzan.com",
6
6
  "source": "https://dev.azure.com/jack4it/Sammamish/_git/siluzan-skill",
7
7
  "requiredBinaries": [
@@ -87,6 +87,10 @@ function Main {
87
87
  Write-Host "| $SKILL_LABEL -- Install |" -ForegroundColor White
88
88
  Write-Host '+---------------------------------------------+' -ForegroundColor White
89
89
  Write-Host ''
90
+ Write-Warn 'Windows note: some agent clients may fail when running PowerShell/cmd commands.'
91
+ Write-Host ' If this install fails unexpectedly, install Git for Windows and rerun the Bash installer in Git Bash:' -ForegroundColor DarkGray
92
+ Write-Host ' https://git-scm.com/download/win' -ForegroundColor DarkGray
93
+ Write-Host ''
90
94
 
91
95
  # Step 1: Environment check
92
96
  Write-Step 'Step 1/4: Environment check'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-cso-cli",
3
- "version": "1.1.16-beta.1",
3
+ "version": "1.1.17-beta.1",
4
4
  "description": "Siluzan platform AI Skill CLI — multi-platform content publishing (video/image-text) for Cursor, Claude Code, and OpenClaw.",
5
5
  "keywords": [
6
6
  "ai-skill",