siluzan-tso-cli 1.0.0-beta.22 → 1.0.0-beta.24
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 +1 -1
- package/dist/index.js +553 -128
- package/dist/skill/_meta.json +2 -2
- package/package.json +2 -1
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.0.0-beta.
|
|
23
|
+
> **注意**:当前为测试版(1.0.0-beta.24),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
24
24
|
|
|
25
25
|
| 助手 | 建议 `--ai` |
|
|
26
26
|
|------|-------------|
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import * as path from "path";
|
|
|
17
17
|
import * as os from "os";
|
|
18
18
|
import * as https from "https";
|
|
19
19
|
import * as http from "http";
|
|
20
|
+
import * as os2 from "os";
|
|
20
21
|
import * as fs2 from "fs";
|
|
21
22
|
import * as path2 from "path";
|
|
22
23
|
import { fileURLToPath } from "url";
|
|
@@ -130,6 +131,412 @@ function rawRequest(url, options) {
|
|
|
130
131
|
req.end();
|
|
131
132
|
});
|
|
132
133
|
}
|
|
134
|
+
var DEFAULT_SENTRY_DSN = "https://bafcf42aab6fe7b485310619ae041b5e@o4510436169285632.ingest.us.sentry.io/4511103054708736";
|
|
135
|
+
function isSentryDisabled() {
|
|
136
|
+
return process.env.SILUZAN_SENTRY_DISABLED === "1" || process.env.SILUZAN_SENTRY_DISABLED === "true";
|
|
137
|
+
}
|
|
138
|
+
function getDsn() {
|
|
139
|
+
return process.env.SILUZAN_SENTRY_DSN?.trim() || DEFAULT_SENTRY_DSN;
|
|
140
|
+
}
|
|
141
|
+
var cliMeta = { name: "siluzan-cli", version: "unknown" };
|
|
142
|
+
var cliInvocation = "";
|
|
143
|
+
function setSiluzanCliInvocation(redactedCommandLine) {
|
|
144
|
+
cliInvocation = redactedCommandLine;
|
|
145
|
+
}
|
|
146
|
+
function redactCliArgvForSentry(argv) {
|
|
147
|
+
const args = argv.slice(2);
|
|
148
|
+
const redactNext = /* @__PURE__ */ new Set(["-t", "--token", "--api-key", "--password"]);
|
|
149
|
+
const out = [];
|
|
150
|
+
for (let i = 0; i < args.length; i++) {
|
|
151
|
+
const a = args[i];
|
|
152
|
+
if (redactNext.has(a)) {
|
|
153
|
+
out.push(a, "***");
|
|
154
|
+
i++;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const eq = a.indexOf("=");
|
|
158
|
+
if (eq > 0) {
|
|
159
|
+
const key = a.slice(0, eq).toLowerCase();
|
|
160
|
+
if (key === "--token" || key === "--api-key" || key === "--password") {
|
|
161
|
+
out.push(`${a.slice(0, eq)}=***`);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (/^-t[^-]/.test(a) && a.length > 3) {
|
|
166
|
+
out.push("-t***");
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
out.push(a);
|
|
170
|
+
}
|
|
171
|
+
return out.join(" ");
|
|
172
|
+
}
|
|
173
|
+
function isApiTrackingEnabled() {
|
|
174
|
+
const v = process.env.SILUZAN_SENTRY_TRACKING?.trim().toLowerCase();
|
|
175
|
+
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
function setSiluzanCliMeta(name, version) {
|
|
179
|
+
cliMeta = { name, version };
|
|
180
|
+
if (sentryReady) {
|
|
181
|
+
void import("@sentry/node").then((Sentry) => {
|
|
182
|
+
Sentry.setTag("cli", name);
|
|
183
|
+
Sentry.setTag("cli_version", version);
|
|
184
|
+
}).catch(() => {
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
var sentryReady = false;
|
|
189
|
+
var sentryInitPromise = null;
|
|
190
|
+
var flushHookRegistered = false;
|
|
191
|
+
function buildRuntimeContext() {
|
|
192
|
+
const platform = process.platform;
|
|
193
|
+
const osName = platform === "win32" ? "Windows" : platform === "darwin" ? "macOS" : "Linux";
|
|
194
|
+
return {
|
|
195
|
+
os: {
|
|
196
|
+
name: osName,
|
|
197
|
+
version: os2.release(),
|
|
198
|
+
// 内核版本,如 10.0.26100(Win11)、24.3.0(macOS)
|
|
199
|
+
arch: process.arch
|
|
200
|
+
// x64 / arm64
|
|
201
|
+
},
|
|
202
|
+
runtime: {
|
|
203
|
+
node_version: process.version,
|
|
204
|
+
// v18.x.x / v20.x.x
|
|
205
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async function ensureSentryInitialized(requestUrl) {
|
|
210
|
+
const dsn = getDsn();
|
|
211
|
+
if (!dsn || isSentryDisabled()) return false;
|
|
212
|
+
if (sentryReady) return true;
|
|
213
|
+
if (!sentryInitPromise) {
|
|
214
|
+
sentryInitPromise = (async () => {
|
|
215
|
+
const Sentry = await import("@sentry/node");
|
|
216
|
+
const envOverride = process.env.SILUZAN_SENTRY_ENV?.trim();
|
|
217
|
+
const environment = envOverride || (inferSiluzanRuntimeEnvironment(requestUrl) === "test" ? "test" : "production");
|
|
218
|
+
Sentry.init({
|
|
219
|
+
dsn,
|
|
220
|
+
environment,
|
|
221
|
+
sendDefaultPii: true,
|
|
222
|
+
tracesSampleRate: 0
|
|
223
|
+
});
|
|
224
|
+
const ctx = buildRuntimeContext();
|
|
225
|
+
Sentry.setContext("os", ctx.os);
|
|
226
|
+
Sentry.setContext("runtime", ctx.runtime);
|
|
227
|
+
Sentry.setTag("cli", cliMeta.name);
|
|
228
|
+
Sentry.setTag("cli_version", cliMeta.version);
|
|
229
|
+
if (!flushHookRegistered) {
|
|
230
|
+
flushHookRegistered = true;
|
|
231
|
+
process.once("beforeExit", () => {
|
|
232
|
+
void Sentry.flush(2e3);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
sentryReady = true;
|
|
236
|
+
})();
|
|
237
|
+
}
|
|
238
|
+
await sentryInitPromise;
|
|
239
|
+
return sentryReady;
|
|
240
|
+
}
|
|
241
|
+
function deriveMainApiOriginFromRequestUrl(requestUrl) {
|
|
242
|
+
try {
|
|
243
|
+
const u = new URL(requestUrl);
|
|
244
|
+
const host = u.hostname.toLowerCase();
|
|
245
|
+
if (!host.endsWith("siluzan.com")) return null;
|
|
246
|
+
if (host.startsWith("tso-api")) {
|
|
247
|
+
return `${u.protocol}//${host.replace(/^tso-api/, "api")}`;
|
|
248
|
+
}
|
|
249
|
+
if (host === "api.siluzan.com" || host === "api-ci.siluzan.com") {
|
|
250
|
+
return `${u.protocol}//${host}`;
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
} catch {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function inferSiluzanRuntimeEnvironment(requestUrl) {
|
|
258
|
+
try {
|
|
259
|
+
const host = new URL(requestUrl).hostname.toLowerCase();
|
|
260
|
+
return host.includes("-ci") ? "test" : "production";
|
|
261
|
+
} catch {
|
|
262
|
+
return "production";
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function breadcrumbUrl(requestUrl) {
|
|
266
|
+
try {
|
|
267
|
+
const u = new URL(requestUrl);
|
|
268
|
+
return `${u.origin}${u.pathname}`;
|
|
269
|
+
} catch {
|
|
270
|
+
return requestUrl.slice(0, 120);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function trackingPathParts(requestUrl) {
|
|
274
|
+
try {
|
|
275
|
+
const u = new URL(requestUrl);
|
|
276
|
+
return { host: u.hostname.toLowerCase(), pathname: u.pathname || "/" };
|
|
277
|
+
} catch {
|
|
278
|
+
return { host: "unknown", pathname: "/" };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
var SENSITIVE_QUERY_KEYS = /* @__PURE__ */ new Set([
|
|
282
|
+
"token",
|
|
283
|
+
"password",
|
|
284
|
+
"api_key",
|
|
285
|
+
"apikey",
|
|
286
|
+
"key",
|
|
287
|
+
"secret",
|
|
288
|
+
"authorization",
|
|
289
|
+
"auth"
|
|
290
|
+
]);
|
|
291
|
+
function redactUrlForTracking(url) {
|
|
292
|
+
try {
|
|
293
|
+
const u = new URL(url);
|
|
294
|
+
const q = new URLSearchParams(u.search);
|
|
295
|
+
const out = new URLSearchParams();
|
|
296
|
+
for (const [k, v] of q.entries()) {
|
|
297
|
+
out.set(k, SENSITIVE_QUERY_KEYS.has(k.toLowerCase()) ? "[REDACTED]" : v);
|
|
298
|
+
}
|
|
299
|
+
const qs = out.toString();
|
|
300
|
+
return `${u.origin}${u.pathname}${qs ? `?${qs}` : ""}`;
|
|
301
|
+
} catch {
|
|
302
|
+
return url.slice(0, 800);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function redactTrackingHeaders(h) {
|
|
306
|
+
const out = {};
|
|
307
|
+
for (const [k, v] of Object.entries(h)) {
|
|
308
|
+
const low = k.toLowerCase();
|
|
309
|
+
if (low === "authorization" || low === "x-api-key") {
|
|
310
|
+
out[k] = "[REDACTED]";
|
|
311
|
+
} else if (v.length > 2e3) {
|
|
312
|
+
out[k] = `${v.slice(0, 500)}\u2026[truncated ${v.length} chars]`;
|
|
313
|
+
} else {
|
|
314
|
+
out[k] = v;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
var SENSITIVE_JSON_KEY = /* @__PURE__ */ new Set([
|
|
320
|
+
"password",
|
|
321
|
+
"token",
|
|
322
|
+
"apikey",
|
|
323
|
+
"api_key",
|
|
324
|
+
"authorization",
|
|
325
|
+
"authtoken",
|
|
326
|
+
"accesstoken",
|
|
327
|
+
"refreshtoken",
|
|
328
|
+
"secret",
|
|
329
|
+
"client_secret",
|
|
330
|
+
"privatekey",
|
|
331
|
+
"private_key"
|
|
332
|
+
]);
|
|
333
|
+
function deepRedactJson(value, depth) {
|
|
334
|
+
if (depth > 14) return "[MAX_DEPTH]";
|
|
335
|
+
if (value === null || typeof value !== "object") return value;
|
|
336
|
+
if (Array.isArray(value)) {
|
|
337
|
+
return value.map((v) => deepRedactJson(v, depth + 1));
|
|
338
|
+
}
|
|
339
|
+
const o = value;
|
|
340
|
+
const out = {};
|
|
341
|
+
for (const [k, v] of Object.entries(o)) {
|
|
342
|
+
const low = k.toLowerCase().replace(/_/g, "");
|
|
343
|
+
if (SENSITIVE_JSON_KEY.has(low) || low.includes("password") || low.includes("secret")) {
|
|
344
|
+
out[k] = "[REDACTED]";
|
|
345
|
+
} else {
|
|
346
|
+
out[k] = deepRedactJson(v, depth + 1);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return out;
|
|
350
|
+
}
|
|
351
|
+
function redactPlainTextSnippet(input) {
|
|
352
|
+
let s = input;
|
|
353
|
+
s = s.replace(/(Bearer\s+)[^\s'",}]+/gi, "$1***");
|
|
354
|
+
s = s.replace(
|
|
355
|
+
/("?(?:apiKey|authToken|accessToken|refreshToken|token|password)"?\s*[:=]\s*"?)([^"\s,}]{4,})/gi,
|
|
356
|
+
"$1***"
|
|
357
|
+
);
|
|
358
|
+
return s;
|
|
359
|
+
}
|
|
360
|
+
function trackingBodyMaxChars() {
|
|
361
|
+
const n = Number(process.env.SILUZAN_SENTRY_BODY_MAX);
|
|
362
|
+
if (Number.isFinite(n) && n > 256) return Math.min(n, 5e5);
|
|
363
|
+
return 12e3;
|
|
364
|
+
}
|
|
365
|
+
function trackingOmitBodies() {
|
|
366
|
+
const v = process.env.SILUZAN_SENTRY_NO_API_BODY?.trim().toLowerCase();
|
|
367
|
+
return v === "1" || v === "true" || v === "yes";
|
|
368
|
+
}
|
|
369
|
+
function formatTrackingBody(raw) {
|
|
370
|
+
if (trackingOmitBodies()) return "[omitted: SILUZAN_SENTRY_NO_API_BODY]";
|
|
371
|
+
if (raw === void 0 || raw === "") return "";
|
|
372
|
+
const max = trackingBodyMaxChars();
|
|
373
|
+
let text;
|
|
374
|
+
try {
|
|
375
|
+
const parsed = JSON.parse(raw);
|
|
376
|
+
text = JSON.stringify(deepRedactJson(parsed, 0));
|
|
377
|
+
} catch {
|
|
378
|
+
text = redactPlainTextSnippet(raw);
|
|
379
|
+
}
|
|
380
|
+
if (text.length > max) {
|
|
381
|
+
return `${text.slice(0, max)}\u2026[truncated ${text.length} chars]`;
|
|
382
|
+
}
|
|
383
|
+
return text;
|
|
384
|
+
}
|
|
385
|
+
async function reportSiluzanApiCall(payload) {
|
|
386
|
+
if (isSentryDisabled() || !getDsn() || !isApiTrackingEnabled()) return;
|
|
387
|
+
try {
|
|
388
|
+
const okInit = await ensureSentryInitialized(payload.url);
|
|
389
|
+
if (!okInit) return;
|
|
390
|
+
const Sentry = await import("@sentry/node");
|
|
391
|
+
const { host, pathname } = trackingPathParts(payload.url);
|
|
392
|
+
const siluzanEnv = inferSiluzanRuntimeEnvironment(payload.url);
|
|
393
|
+
const authType = payload.config.apiKey ? "apiKey" : "token";
|
|
394
|
+
const statusOk = payload.status >= 200 && payload.status < 300;
|
|
395
|
+
Sentry.captureMessage(
|
|
396
|
+
`siluzan.cli.api ${payload.method} ${host}${pathname} \u2192 ${payload.status}`,
|
|
397
|
+
{
|
|
398
|
+
level: statusOk ? "info" : "warning",
|
|
399
|
+
tags: {
|
|
400
|
+
siluzan_tracking: "api",
|
|
401
|
+
cli: cliMeta.name,
|
|
402
|
+
cli_version: cliMeta.version,
|
|
403
|
+
http_method: payload.method,
|
|
404
|
+
http_host: host,
|
|
405
|
+
http_status: String(payload.status),
|
|
406
|
+
siluzan_env: siluzanEnv,
|
|
407
|
+
auth_type: authType
|
|
408
|
+
},
|
|
409
|
+
fingerprint: [
|
|
410
|
+
"siluzan-cli-api",
|
|
411
|
+
cliMeta.name,
|
|
412
|
+
payload.method,
|
|
413
|
+
host,
|
|
414
|
+
pathname,
|
|
415
|
+
statusOk ? "2xx" : payload.status >= 500 ? "5xx" : "4xx"
|
|
416
|
+
],
|
|
417
|
+
contexts: {
|
|
418
|
+
siluzan_cli: {
|
|
419
|
+
command: cliInvocation || "(pre-action \u672A\u6CE8\u518C\u6216\u5148\u4E8E parse \u53D1\u8D77\u8BF7\u6C42)"
|
|
420
|
+
},
|
|
421
|
+
siluzan_request: {
|
|
422
|
+
url: redactUrlForTracking(payload.url),
|
|
423
|
+
method: payload.method,
|
|
424
|
+
headers: redactTrackingHeaders(payload.reqHeaders),
|
|
425
|
+
body: formatTrackingBody(payload.requestBody)
|
|
426
|
+
},
|
|
427
|
+
siluzan_response: {
|
|
428
|
+
status: payload.status,
|
|
429
|
+
body: formatTrackingBody(payload.responseText)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
} catch {
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function cacheKeyForUser(mainOrigin, config) {
|
|
438
|
+
const cred = config.apiKey ? `k:${config.apiKey}` : `t:${config.authToken}`;
|
|
439
|
+
return `${mainOrigin}\0${cred}`;
|
|
440
|
+
}
|
|
441
|
+
var userContextDone = /* @__PURE__ */ new Set();
|
|
442
|
+
var userContextInflight = /* @__PURE__ */ new Map();
|
|
443
|
+
function parseMeResponse(text) {
|
|
444
|
+
try {
|
|
445
|
+
const json = JSON.parse(text);
|
|
446
|
+
const data = json?.data && typeof json.data === "object" ? json.data : json;
|
|
447
|
+
const id = pickStr(data, "id") ?? pickStr(data, "userId") ?? pickStr(data, "accountId");
|
|
448
|
+
const email = pickStr(data, "email");
|
|
449
|
+
const username = pickStr(data, "userName") ?? pickStr(data, "username") ?? pickStr(data, "name") ?? pickStr(data, "phone");
|
|
450
|
+
if (!id && !email && !username) return null;
|
|
451
|
+
return { id, email, username };
|
|
452
|
+
} catch {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function pickStr(obj, key) {
|
|
457
|
+
const v = obj[key];
|
|
458
|
+
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
459
|
+
}
|
|
460
|
+
async function fetchAndSetUser(mainOrigin, config) {
|
|
461
|
+
const meUrl = `${mainOrigin.replace(/\/$/, "")}/query/account/me`;
|
|
462
|
+
const authHeaders = config.apiKey ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` };
|
|
463
|
+
const res = await rawRequest(meUrl, {
|
|
464
|
+
method: "GET",
|
|
465
|
+
headers: {
|
|
466
|
+
"Content-Type": "application/json",
|
|
467
|
+
"Accept-Language": "zh-CN",
|
|
468
|
+
...authHeaders
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
if (res.status < 200 || res.status >= 300) return;
|
|
472
|
+
const parsed = parseMeResponse(res.text);
|
|
473
|
+
if (!parsed) return;
|
|
474
|
+
const Sentry = await import("@sentry/node");
|
|
475
|
+
const user = {};
|
|
476
|
+
if (parsed.id) user.id = parsed.id;
|
|
477
|
+
if (parsed.email) user.email = parsed.email;
|
|
478
|
+
if (parsed.username) user.username = parsed.username;
|
|
479
|
+
Sentry.setUser(user);
|
|
480
|
+
}
|
|
481
|
+
function scheduleUserContext(mainOrigin, config) {
|
|
482
|
+
const key = cacheKeyForUser(mainOrigin, config);
|
|
483
|
+
if (userContextDone.has(key)) return;
|
|
484
|
+
let p = userContextInflight.get(key);
|
|
485
|
+
if (!p) {
|
|
486
|
+
p = (async () => {
|
|
487
|
+
try {
|
|
488
|
+
await fetchAndSetUser(mainOrigin, config);
|
|
489
|
+
} catch {
|
|
490
|
+
} finally {
|
|
491
|
+
userContextInflight.delete(key);
|
|
492
|
+
userContextDone.add(key);
|
|
493
|
+
}
|
|
494
|
+
})();
|
|
495
|
+
userContextInflight.set(key, p);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function refreshSiluzanUser(apiBase, config) {
|
|
499
|
+
if (isSentryDisabled()) return;
|
|
500
|
+
void (async () => {
|
|
501
|
+
try {
|
|
502
|
+
const ok = await ensureSentryInitialized(apiBase);
|
|
503
|
+
if (!ok) return;
|
|
504
|
+
const mainOrigin = deriveMainApiOriginFromRequestUrl(apiBase) ?? (apiBase.includes("-ci") ? "https://api-ci.siluzan.com" : "https://api.siluzan.com");
|
|
505
|
+
const key = cacheKeyForUser(mainOrigin, config);
|
|
506
|
+
userContextDone.delete(key);
|
|
507
|
+
userContextInflight.delete(key);
|
|
508
|
+
scheduleUserContext(mainOrigin, config);
|
|
509
|
+
} catch {
|
|
510
|
+
}
|
|
511
|
+
})();
|
|
512
|
+
}
|
|
513
|
+
async function prepareSiluzanSentryForApiFetch(url, config, options) {
|
|
514
|
+
if (isSentryDisabled()) return;
|
|
515
|
+
if (!getDsn()) return;
|
|
516
|
+
try {
|
|
517
|
+
const ok = await ensureSentryInitialized(url);
|
|
518
|
+
if (!ok) return;
|
|
519
|
+
const Sentry = await import("@sentry/node");
|
|
520
|
+
const method = options.method ?? "GET";
|
|
521
|
+
const siluzanEnv = inferSiluzanRuntimeEnvironment(url);
|
|
522
|
+
const authType = config.apiKey ? "apiKey" : "token";
|
|
523
|
+
Sentry.addBreadcrumb({
|
|
524
|
+
category: "siluzan.api",
|
|
525
|
+
type: "http",
|
|
526
|
+
level: "info",
|
|
527
|
+
message: `${method} ${breadcrumbUrl(url)}`,
|
|
528
|
+
data: {
|
|
529
|
+
siluzan_env: siluzanEnv,
|
|
530
|
+
auth_type: authType
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
const mainOrigin = deriveMainApiOriginFromRequestUrl(url);
|
|
534
|
+
if (mainOrigin) {
|
|
535
|
+
scheduleUserContext(mainOrigin, config);
|
|
536
|
+
}
|
|
537
|
+
} catch {
|
|
538
|
+
}
|
|
539
|
+
}
|
|
133
540
|
function redactSensitive(input) {
|
|
134
541
|
let output = input;
|
|
135
542
|
output = output.replace(/(Bearer\s+)[^\s",]+/gi, "$1***");
|
|
@@ -140,6 +547,8 @@ function redactSensitive(input) {
|
|
|
140
547
|
return output;
|
|
141
548
|
}
|
|
142
549
|
async function apiFetch(url, config, options = {}, verbose = false) {
|
|
550
|
+
await prepareSiluzanSentryForApiFetch(url, config, options);
|
|
551
|
+
const method = options.method ?? "GET";
|
|
143
552
|
const authHeaders = config.apiKey ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` };
|
|
144
553
|
const reqHeaders = {
|
|
145
554
|
"Content-Type": "application/json",
|
|
@@ -151,11 +560,20 @@ async function apiFetch(url, config, options = {}, verbose = false) {
|
|
|
151
560
|
};
|
|
152
561
|
const body = typeof options.body === "string" ? options.body : void 0;
|
|
153
562
|
const res = await rawRequest(url, {
|
|
154
|
-
method
|
|
563
|
+
method,
|
|
155
564
|
headers: reqHeaders,
|
|
156
565
|
body
|
|
157
566
|
});
|
|
158
567
|
const text = res.text;
|
|
568
|
+
await reportSiluzanApiCall({
|
|
569
|
+
url,
|
|
570
|
+
config,
|
|
571
|
+
method,
|
|
572
|
+
reqHeaders,
|
|
573
|
+
requestBody: body,
|
|
574
|
+
status: res.status,
|
|
575
|
+
responseText: text
|
|
576
|
+
});
|
|
159
577
|
if (res.status < 200 || res.status >= 300) {
|
|
160
578
|
const detail = verbose ? `\uFF1A${redactSensitive(text).slice(0, 300)}` : "";
|
|
161
579
|
throw new Error(`HTTP ${res.status}${detail}`);
|
|
@@ -183,9 +601,11 @@ function getCurrentVersion(importMetaUrl) {
|
|
|
183
601
|
return "0.0.0";
|
|
184
602
|
}
|
|
185
603
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
604
|
+
function npmDistTagForBuildEnv(buildEnv) {
|
|
605
|
+
return buildEnv === "test" ? "beta" : "latest";
|
|
606
|
+
}
|
|
607
|
+
function npmMinRequiredTagForBuildEnv(buildEnv) {
|
|
608
|
+
return buildEnv === "test" ? "min-required-beta" : "min-required";
|
|
189
609
|
}
|
|
190
610
|
function isNewer(a, b) {
|
|
191
611
|
const parse = (v) => {
|
|
@@ -218,6 +638,93 @@ async function fetchNpmVersion(pkgName, tag, timeoutMs = 4e3) {
|
|
|
218
638
|
}
|
|
219
639
|
}
|
|
220
640
|
|
|
641
|
+
// src/utils/version.ts
|
|
642
|
+
import * as fs3 from "fs";
|
|
643
|
+
import * as path3 from "path";
|
|
644
|
+
import * as os3 from "os";
|
|
645
|
+
var PKG_NAME = "siluzan-tso-cli";
|
|
646
|
+
var CONFIG_FILE2 = path3.join(os3.homedir(), ".siluzan", "config.json");
|
|
647
|
+
function getCurrentVersion2() {
|
|
648
|
+
return getCurrentVersion(import.meta.url);
|
|
649
|
+
}
|
|
650
|
+
function readConfigRaw() {
|
|
651
|
+
try {
|
|
652
|
+
return JSON.parse(fs3.readFileSync(CONFIG_FILE2, "utf8"));
|
|
653
|
+
} catch {
|
|
654
|
+
return {};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
function writeConfigRaw(data) {
|
|
658
|
+
try {
|
|
659
|
+
fs3.mkdirSync(path3.dirname(CONFIG_FILE2), { recursive: true });
|
|
660
|
+
fs3.writeFileSync(CONFIG_FILE2, JSON.stringify(data, null, 2), "utf8");
|
|
661
|
+
if (process.platform !== "win32") {
|
|
662
|
+
fs3.chmodSync(CONFIG_FILE2, 384);
|
|
663
|
+
}
|
|
664
|
+
} catch {
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
async function fetchVersionByTag(tag, cacheKey, cfg) {
|
|
668
|
+
const hours24 = 24 * 60 * 60 * 1e3;
|
|
669
|
+
if (cfg._tsoLastVersionCheck && cfg[cacheKey]) {
|
|
670
|
+
const lastCheck = new Date(cfg._tsoLastVersionCheck).getTime();
|
|
671
|
+
if (Date.now() - lastCheck < hours24) {
|
|
672
|
+
return cfg[cacheKey];
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return fetchNpmVersion(PKG_NAME, tag);
|
|
676
|
+
}
|
|
677
|
+
async function notifyIfOutdated() {
|
|
678
|
+
try {
|
|
679
|
+
const current = getCurrentVersion2();
|
|
680
|
+
const tag = npmDistTagForBuildEnv(BUILD_ENV);
|
|
681
|
+
const isBeta = tag === "beta";
|
|
682
|
+
const latestCacheKey = isBeta ? "_tsoLatestBeta" : "_tsoLatestStable";
|
|
683
|
+
const minCacheKey = isBeta ? "_tsoMinRequiredBeta" : "_tsoMinRequiredStable";
|
|
684
|
+
const minTag = npmMinRequiredTagForBuildEnv(BUILD_ENV);
|
|
685
|
+
const cfg = readConfigRaw();
|
|
686
|
+
const [latest, minRequired] = await Promise.all([
|
|
687
|
+
fetchVersionByTag(tag, latestCacheKey, cfg),
|
|
688
|
+
fetchVersionByTag(minTag, minCacheKey, cfg)
|
|
689
|
+
]);
|
|
690
|
+
writeConfigRaw({
|
|
691
|
+
...cfg,
|
|
692
|
+
_tsoLastVersionCheck: (/* @__PURE__ */ new Date()).toISOString(),
|
|
693
|
+
...latest ? { [latestCacheKey]: latest } : {},
|
|
694
|
+
...minRequired ? { [minCacheKey]: minRequired } : {}
|
|
695
|
+
});
|
|
696
|
+
const lastNotified = cfg._tsoLastNotified ? new Date(cfg._tsoLastNotified).getTime() : 0;
|
|
697
|
+
if (Date.now() - lastNotified < 24 * 60 * 60 * 1e3) return;
|
|
698
|
+
const tagLabel = isBeta ? "\uFF08\u6D4B\u8BD5\u7248\uFF09" : "\uFF08\u6B63\u5F0F\u7248\uFF09";
|
|
699
|
+
if (minRequired && isNewer(current, minRequired)) {
|
|
700
|
+
writeConfigRaw({ ...cfg, _tsoLastNotified: (/* @__PURE__ */ new Date()).toISOString() });
|
|
701
|
+
console.log(
|
|
702
|
+
`
|
|
703
|
+
[SILUZAN_FORCE_UPDATE] \u5F53\u524D\u7248\u672C ${current} \u5DF2\u8FC7\u671F\uFF0C\u6700\u4F4E\u8981\u6C42\u7248\u672C ${minRequired}${tagLabel}
|
|
704
|
+
\u26D4 \u5FC5\u987B\u5148\u5347\u7EA7\u624D\u80FD\u7EE7\u7EED\u4F7F\u7528\uFF0CAgent \u8BF7\u7ACB\u5373\u6267\u884C\uFF1Asiluzan-tso update
|
|
705
|
+
\u6216\u624B\u52A8\uFF1Anpm install -g ${PKG_NAME}@${tag}
|
|
706
|
+
`
|
|
707
|
+
);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (latest && isNewer(current, latest)) {
|
|
711
|
+
writeConfigRaw({ ...cfg, _tsoLastNotified: (/* @__PURE__ */ new Date()).toISOString() });
|
|
712
|
+
console.log(
|
|
713
|
+
`
|
|
714
|
+
[SILUZAN_UPDATE_AVAILABLE] \u53D1\u73B0\u65B0\u7248\u672C ${latest}${tagLabel}\uFF08\u5F53\u524D ${current}\uFF09
|
|
715
|
+
\u{1F4A1} Agent \u8BF7\u7ACB\u5373\u6267\u884C\uFF1Asiluzan-tso update
|
|
716
|
+
\u6216\u624B\u52A8\uFF1Anpm install -g ${PKG_NAME}@${tag}
|
|
717
|
+
`
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
async function fetchLatestVersion() {
|
|
724
|
+
const tag = npmDistTagForBuildEnv(BUILD_ENV);
|
|
725
|
+
return fetchNpmVersion(PKG_NAME, tag);
|
|
726
|
+
}
|
|
727
|
+
|
|
221
728
|
// src/utils/auth.ts
|
|
222
729
|
function deriveMainApiUrl(tsoApiBaseUrl) {
|
|
223
730
|
try {
|
|
@@ -286,6 +793,7 @@ function loadConfig(tokenArg) {
|
|
|
286
793
|
\u274C googleApiUrl \u4E0D\u5408\u6CD5\uFF1A${googleApiErr}`);
|
|
287
794
|
process.exit(1);
|
|
288
795
|
}
|
|
796
|
+
setSiluzanCliMeta("siluzan-tso", getCurrentVersion2());
|
|
289
797
|
return {
|
|
290
798
|
apiBaseUrl,
|
|
291
799
|
authToken,
|
|
@@ -390,26 +898,26 @@ function cmdConfigClear() {
|
|
|
390
898
|
}
|
|
391
899
|
|
|
392
900
|
// src/commands/init.ts
|
|
393
|
-
import * as
|
|
901
|
+
import * as fs5 from "fs/promises";
|
|
394
902
|
import * as fsSync from "fs";
|
|
395
|
-
import * as
|
|
396
|
-
import * as
|
|
903
|
+
import * as os4 from "os";
|
|
904
|
+
import * as path5 from "path";
|
|
397
905
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
398
906
|
|
|
399
907
|
// src/templates/load-templates.ts
|
|
400
|
-
import * as
|
|
401
|
-
import * as
|
|
908
|
+
import * as fs4 from "fs/promises";
|
|
909
|
+
import * as path4 from "path";
|
|
402
910
|
async function getSkillFiles(skillDir) {
|
|
403
911
|
const out = {};
|
|
404
912
|
async function walk(dir, prefix) {
|
|
405
|
-
const entries = await
|
|
913
|
+
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
406
914
|
for (const ent of entries) {
|
|
407
915
|
const rel = prefix ? `${prefix}/${ent.name}` : ent.name;
|
|
408
|
-
const full =
|
|
916
|
+
const full = path4.join(dir, ent.name);
|
|
409
917
|
if (ent.isDirectory()) {
|
|
410
918
|
await walk(full, rel);
|
|
411
919
|
} else {
|
|
412
|
-
out[rel] = await
|
|
920
|
+
out[rel] = await fs4.readFile(full, "utf8");
|
|
413
921
|
}
|
|
414
922
|
}
|
|
415
923
|
}
|
|
@@ -418,14 +926,14 @@ async function getSkillFiles(skillDir) {
|
|
|
418
926
|
}
|
|
419
927
|
|
|
420
928
|
// src/commands/init.ts
|
|
421
|
-
var __dirname =
|
|
929
|
+
var __dirname = path5.dirname(fileURLToPath2(import.meta.url));
|
|
422
930
|
var TARGET_DIRS = {
|
|
423
|
-
cursor: (cwd) =>
|
|
424
|
-
claude: (cwd) =>
|
|
425
|
-
"openclaw-workspace": (cwd) =>
|
|
426
|
-
"openclaw-global": (_cwd, home) =>
|
|
427
|
-
"workbuddy-workspace": (cwd) =>
|
|
428
|
-
"workbuddy-global": (_cwd, home) =>
|
|
931
|
+
cursor: (cwd) => path5.join(cwd, ".cursor", "skills", "siluzan-tso"),
|
|
932
|
+
claude: (cwd) => path5.join(cwd, ".claude", "skills", "siluzan-tso"),
|
|
933
|
+
"openclaw-workspace": (cwd) => path5.join(cwd, "skills", "siluzan-tso"),
|
|
934
|
+
"openclaw-global": (_cwd, home) => path5.join(home, ".openclaw", "skills", "siluzan-tso"),
|
|
935
|
+
"workbuddy-workspace": (cwd) => path5.join(cwd, ".workbuddy", "skills", "siluzan-tso"),
|
|
936
|
+
"workbuddy-global": (_cwd, home) => path5.join(home, ".workbuddy", "skills", "siluzan-tso")
|
|
429
937
|
};
|
|
430
938
|
function parseTargets(raw) {
|
|
431
939
|
const normalized = raw.trim().toLowerCase();
|
|
@@ -466,32 +974,32 @@ function parseTargets(raw) {
|
|
|
466
974
|
return [...new Set(result)];
|
|
467
975
|
}
|
|
468
976
|
function skillRoot() {
|
|
469
|
-
return
|
|
977
|
+
return path5.join(__dirname, "..", "skill");
|
|
470
978
|
}
|
|
471
979
|
async function writeSkillFilesToDir(destDir, skillFiles, force) {
|
|
472
|
-
await
|
|
980
|
+
await fs5.mkdir(destDir, { recursive: true });
|
|
473
981
|
let anyWritten = false;
|
|
474
982
|
for (const [relativePath, content] of Object.entries(skillFiles)) {
|
|
475
|
-
const fullPath =
|
|
476
|
-
await
|
|
983
|
+
const fullPath = path5.join(destDir, relativePath);
|
|
984
|
+
await fs5.mkdir(path5.dirname(fullPath), { recursive: true });
|
|
477
985
|
try {
|
|
478
|
-
await
|
|
986
|
+
await fs5.access(fullPath);
|
|
479
987
|
if (!force) {
|
|
480
988
|
console.warn(`\u8DF3\u8FC7\uFF08\u5DF2\u5B58\u5728\uFF0C\u4F7F\u7528 --force \u8986\u76D6\uFF09: ${fullPath}`);
|
|
481
989
|
continue;
|
|
482
990
|
}
|
|
483
991
|
} catch {
|
|
484
992
|
}
|
|
485
|
-
await
|
|
993
|
+
await fs5.writeFile(fullPath, content, "utf8");
|
|
486
994
|
console.log(`\u5DF2\u5199\u5165: ${fullPath}`);
|
|
487
995
|
anyWritten = true;
|
|
488
996
|
}
|
|
489
997
|
return anyWritten;
|
|
490
998
|
}
|
|
491
999
|
function saveInstalledTargets(entries) {
|
|
492
|
-
const CONFIG_FILE4 =
|
|
1000
|
+
const CONFIG_FILE4 = path5.join(os4.homedir(), ".siluzan", "config.json");
|
|
493
1001
|
try {
|
|
494
|
-
fsSync.mkdirSync(
|
|
1002
|
+
fsSync.mkdirSync(path5.dirname(CONFIG_FILE4), { recursive: true });
|
|
495
1003
|
let existing = {};
|
|
496
1004
|
if (fsSync.existsSync(CONFIG_FILE4)) {
|
|
497
1005
|
existing = JSON.parse(fsSync.readFileSync(CONFIG_FILE4, "utf8"));
|
|
@@ -513,11 +1021,11 @@ function saveInstalledTargets(entries) {
|
|
|
513
1021
|
}
|
|
514
1022
|
}
|
|
515
1023
|
async function runInit(options) {
|
|
516
|
-
const home =
|
|
1024
|
+
const home = os4.homedir();
|
|
517
1025
|
const skillFiles = await getSkillFiles(skillRoot());
|
|
518
1026
|
const installedEntries = [];
|
|
519
1027
|
if (options.dir) {
|
|
520
|
-
const destDir =
|
|
1028
|
+
const destDir = path5.resolve(options.cwd, options.dir);
|
|
521
1029
|
console.log(`\u5B89\u88C5\u76EE\u6807\u76EE\u5F55\uFF1A${destDir}`);
|
|
522
1030
|
const anyWritten = await writeSkillFilesToDir(destDir, skillFiles, options.force);
|
|
523
1031
|
if (anyWritten) installedEntries.push({ target: "custom", cwd: "", dir: destDir });
|
|
@@ -547,98 +1055,9 @@ async function runInit(options) {
|
|
|
547
1055
|
// src/commands/update.ts
|
|
548
1056
|
import * as fs6 from "fs";
|
|
549
1057
|
import * as path6 from "path";
|
|
550
|
-
import * as
|
|
1058
|
+
import * as os5 from "os";
|
|
551
1059
|
import { spawnSync } from "child_process";
|
|
552
|
-
|
|
553
|
-
// src/utils/version.ts
|
|
554
|
-
import * as fs5 from "fs";
|
|
555
|
-
import * as path5 from "path";
|
|
556
|
-
import * as os3 from "os";
|
|
557
|
-
var PKG_NAME = "siluzan-tso-cli";
|
|
558
|
-
var CONFIG_FILE2 = path5.join(os3.homedir(), ".siluzan", "config.json");
|
|
559
|
-
function getCurrentVersion2() {
|
|
560
|
-
return getCurrentVersion(import.meta.url);
|
|
561
|
-
}
|
|
562
|
-
function readConfigRaw() {
|
|
563
|
-
try {
|
|
564
|
-
return JSON.parse(fs5.readFileSync(CONFIG_FILE2, "utf8"));
|
|
565
|
-
} catch {
|
|
566
|
-
return {};
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
function writeConfigRaw(data) {
|
|
570
|
-
try {
|
|
571
|
-
fs5.mkdirSync(path5.dirname(CONFIG_FILE2), { recursive: true });
|
|
572
|
-
fs5.writeFileSync(CONFIG_FILE2, JSON.stringify(data, null, 2), "utf8");
|
|
573
|
-
if (process.platform !== "win32") {
|
|
574
|
-
fs5.chmodSync(CONFIG_FILE2, 384);
|
|
575
|
-
}
|
|
576
|
-
} catch {
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
async function fetchVersionByTag(tag, cacheKey, cfg) {
|
|
580
|
-
const hours24 = 24 * 60 * 60 * 1e3;
|
|
581
|
-
if (cfg._tsoLastVersionCheck && cfg[cacheKey]) {
|
|
582
|
-
const lastCheck = new Date(cfg._tsoLastVersionCheck).getTime();
|
|
583
|
-
if (Date.now() - lastCheck < hours24) {
|
|
584
|
-
return cfg[cacheKey];
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
return fetchNpmVersion(PKG_NAME, tag);
|
|
588
|
-
}
|
|
589
|
-
async function notifyIfOutdated() {
|
|
590
|
-
try {
|
|
591
|
-
const current = getCurrentVersion2();
|
|
592
|
-
const tag = npmDistTagForCurrentVersion(current);
|
|
593
|
-
const isBeta = tag === "beta";
|
|
594
|
-
const latestCacheKey = isBeta ? "_tsoLatestBeta" : "_tsoLatestStable";
|
|
595
|
-
const minCacheKey = isBeta ? "_tsoMinRequiredBeta" : "_tsoMinRequiredStable";
|
|
596
|
-
const minTag = isBeta ? "min-required-beta" : "min-required";
|
|
597
|
-
const cfg = readConfigRaw();
|
|
598
|
-
const [latest, minRequired] = await Promise.all([
|
|
599
|
-
fetchVersionByTag(tag, latestCacheKey, cfg),
|
|
600
|
-
fetchVersionByTag(minTag, minCacheKey, cfg)
|
|
601
|
-
]);
|
|
602
|
-
writeConfigRaw({
|
|
603
|
-
...cfg,
|
|
604
|
-
_tsoLastVersionCheck: (/* @__PURE__ */ new Date()).toISOString(),
|
|
605
|
-
...latest ? { [latestCacheKey]: latest } : {},
|
|
606
|
-
...minRequired ? { [minCacheKey]: minRequired } : {}
|
|
607
|
-
});
|
|
608
|
-
const lastNotified = cfg._tsoLastNotified ? new Date(cfg._tsoLastNotified).getTime() : 0;
|
|
609
|
-
if (Date.now() - lastNotified < 24 * 60 * 60 * 1e3) return;
|
|
610
|
-
const tagLabel = isBeta ? "\uFF08\u6D4B\u8BD5\u7248\uFF09" : "\uFF08\u6B63\u5F0F\u7248\uFF09";
|
|
611
|
-
if (minRequired && isNewer(current, minRequired)) {
|
|
612
|
-
writeConfigRaw({ ...cfg, _tsoLastNotified: (/* @__PURE__ */ new Date()).toISOString() });
|
|
613
|
-
console.log(
|
|
614
|
-
`
|
|
615
|
-
[SILUZAN_FORCE_UPDATE] \u5F53\u524D\u7248\u672C ${current} \u5DF2\u8FC7\u671F\uFF0C\u6700\u4F4E\u8981\u6C42\u7248\u672C ${minRequired}${tagLabel}
|
|
616
|
-
\u26D4 \u5FC5\u987B\u5148\u5347\u7EA7\u624D\u80FD\u7EE7\u7EED\u4F7F\u7528\uFF0CAgent \u8BF7\u7ACB\u5373\u6267\u884C\uFF1Asiluzan-tso update
|
|
617
|
-
\u6216\u624B\u52A8\uFF1Anpm install -g ${PKG_NAME}@${tag}
|
|
618
|
-
`
|
|
619
|
-
);
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
if (latest && isNewer(current, latest)) {
|
|
623
|
-
writeConfigRaw({ ...cfg, _tsoLastNotified: (/* @__PURE__ */ new Date()).toISOString() });
|
|
624
|
-
console.log(
|
|
625
|
-
`
|
|
626
|
-
[SILUZAN_UPDATE_AVAILABLE] \u53D1\u73B0\u65B0\u7248\u672C ${latest}${tagLabel}\uFF08\u5F53\u524D ${current}\uFF09
|
|
627
|
-
\u{1F4A1} Agent \u8BF7\u7ACB\u5373\u6267\u884C\uFF1Asiluzan-tso update
|
|
628
|
-
\u6216\u624B\u52A8\uFF1Anpm install -g ${PKG_NAME}@${tag}
|
|
629
|
-
`
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
|
-
} catch {
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
async function fetchLatestVersion() {
|
|
636
|
-
const tag = npmDistTagForCurrentVersion(getCurrentVersion2());
|
|
637
|
-
return fetchNpmVersion(PKG_NAME, tag);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// src/commands/update.ts
|
|
641
|
-
var CONFIG_FILE3 = path6.join(os4.homedir(), ".siluzan", "config.json");
|
|
1060
|
+
var CONFIG_FILE3 = path6.join(os5.homedir(), ".siluzan", "config.json");
|
|
642
1061
|
var PKG_NAME2 = "siluzan-tso-cli";
|
|
643
1062
|
function readInstalledTargets() {
|
|
644
1063
|
try {
|
|
@@ -663,7 +1082,8 @@ async function runUpdate(options) {
|
|
|
663
1082
|
const current = getCurrentVersion2();
|
|
664
1083
|
console.log(`
|
|
665
1084
|
\u5F53\u524D\u7248\u672C\uFF1A${current}`);
|
|
666
|
-
|
|
1085
|
+
const npmTag = npmDistTagForBuildEnv(BUILD_ENV);
|
|
1086
|
+
console.log(`\u6B63\u5728\u67E5\u8BE2 npm registry\uFF08\u6784\u5EFA\uFF1A${BUILD_ENV}\uFF0Cdist-tag\uFF1A${npmTag}\uFF09\u2026`);
|
|
667
1087
|
const latest = await fetchLatestVersion();
|
|
668
1088
|
if (!latest) {
|
|
669
1089
|
console.warn("\u26A0\uFE0F \u65E0\u6CD5\u8BBF\u95EE npm registry\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\u3002");
|
|
@@ -722,13 +1142,13 @@ async function runUpdate(options) {
|
|
|
722
1142
|
\u{1F504} \u6B63\u5728\u66F4\u65B0 ${targets.length} \u5904 Skill \u5B89\u88C5\u4F4D\u7F6E \u2026`);
|
|
723
1143
|
for (const entry of targets) {
|
|
724
1144
|
const label = entry.target === "custom" ? entry.dir ?? "unknown" : entry.target;
|
|
725
|
-
const cwd = entry.cwd ||
|
|
1145
|
+
const cwd = entry.cwd || os5.homedir();
|
|
726
1146
|
console.log(`
|
|
727
1147
|
[${label}]`);
|
|
728
1148
|
try {
|
|
729
1149
|
if (entry.target === "custom" && entry.dir) {
|
|
730
1150
|
await runInit({
|
|
731
|
-
cwd:
|
|
1151
|
+
cwd: os5.homedir(),
|
|
732
1152
|
aiTargets: "",
|
|
733
1153
|
dir: entry.dir,
|
|
734
1154
|
force: true
|
|
@@ -1657,13 +2077,13 @@ async function runTransferList(opts) {
|
|
|
1657
2077
|
// src/commands/invoice.ts
|
|
1658
2078
|
import * as fs7 from "fs";
|
|
1659
2079
|
import * as path7 from "path";
|
|
1660
|
-
import * as
|
|
2080
|
+
import * as os6 from "os";
|
|
1661
2081
|
async function ensureDataPermission(config) {
|
|
1662
2082
|
if (config.dataPermission) return config;
|
|
1663
2083
|
if (!config.mainApiUrl) return config;
|
|
1664
2084
|
const dp = await fetchDataPermission(config.mainApiUrl, config.authToken);
|
|
1665
2085
|
if (!dp) return config;
|
|
1666
|
-
const configPath = path7.join(
|
|
2086
|
+
const configPath = path7.join(os6.homedir(), ".siluzan", "config.json");
|
|
1667
2087
|
try {
|
|
1668
2088
|
const raw = fs7.existsSync(configPath) ? JSON.parse(fs7.readFileSync(configPath, "utf8")) : {};
|
|
1669
2089
|
raw.dataPermission = dp;
|
|
@@ -3374,7 +3794,7 @@ async function runOptimizeChildren(opts) {
|
|
|
3374
3794
|
}
|
|
3375
3795
|
|
|
3376
3796
|
// src/commands/forewarning.ts
|
|
3377
|
-
import
|
|
3797
|
+
import os7 from "os";
|
|
3378
3798
|
import path8 from "path";
|
|
3379
3799
|
import QRCode from "qrcode";
|
|
3380
3800
|
import open from "open";
|
|
@@ -3582,7 +4002,7 @@ async function runForewarningNotifyAccounts(opts) {
|
|
|
3582
4002
|
console.log(" \u901A\u77E5\u6E20\u9053\uFF1A\u4E1D\u8DEF\u8D5E\u5E73\u53F0\u5FAE\u4FE1\u670D\u52A1\u53F7\uFF08\u9700\u626B\u7801\u5173\u6CE8\u540E\u624D\u80FD\u6536\u5230\u9884\u8B66\u901A\u77E5\uFF09\n");
|
|
3583
4003
|
if (qrLink) {
|
|
3584
4004
|
try {
|
|
3585
|
-
const imgPath = path8.join(
|
|
4005
|
+
const imgPath = path8.join(os7.tmpdir(), "siluzan-wechat-qr.png");
|
|
3586
4006
|
await QRCode.toFile(imgPath, qrLink, { width: 300 });
|
|
3587
4007
|
await open(imgPath);
|
|
3588
4008
|
console.log(` \u{1F4F7} \u4E8C\u7EF4\u7801\u5DF2\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\uFF0C\u8BF7\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u7801\u5173\u6CE8"\u4E1D\u8DEF\u8D5E\u5E73\u53F0"\u670D\u52A1\u53F7
|
|
@@ -7201,6 +7621,7 @@ async function runLogin(opts = {}) {
|
|
|
7201
7621
|
process.exit(1);
|
|
7202
7622
|
}
|
|
7203
7623
|
writeSharedConfig({ apiKey: key });
|
|
7624
|
+
refreshSiluzanUser(DEFAULT_API_BASE, { authToken: "", apiKey: key });
|
|
7204
7625
|
console.log(`
|
|
7205
7626
|
\u2705 API Key \u5DF2\u4FDD\u5B58\uFF08${maskSecret(key)}\uFF09`);
|
|
7206
7627
|
console.log(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CONFIG_FILE}`);
|
|
@@ -7253,6 +7674,7 @@ async function runLogin(opts = {}) {
|
|
|
7253
7674
|
process.exit(1);
|
|
7254
7675
|
}
|
|
7255
7676
|
writeSharedConfig({ apiKey });
|
|
7677
|
+
refreshSiluzanUser(DEFAULT_API_BASE, { authToken: "", apiKey });
|
|
7256
7678
|
console.log(`
|
|
7257
7679
|
\u2705 API Key \u5DF2\u4FDD\u5B58\uFF08${maskSecret(apiKey)}\uFF09`);
|
|
7258
7680
|
console.log(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CONFIG_FILE}`);
|
|
@@ -7276,6 +7698,9 @@ var program = new Command();
|
|
|
7276
7698
|
program.name("siluzan-tso").description(
|
|
7277
7699
|
"Siluzan \u5E7F\u544A\u8D26\u6237\u7BA1\u7406\uFF1A\u8D26\u6237\u67E5\u8BE2\u3001\u4F59\u989D\u3001\u6295\u653E\u6570\u636E\u3001\u5F00\u6237\u7533\u8BF7\uFF08Google/TikTok/Yandex/Bing/Kwai\uFF09\u3001\n\u8D26\u53F7\u5206\u4EAB/\u89E3\u7ED1\u3001\u4F18\u5316\u62A5\u544A\u3001\u5145\u503C\u8F6C\u8D26\u3001\u5F00\u7968\u3001AI\u667A\u6295\u3001\u667A\u80FD\u9884\u8B66\u3001Google \u5E7F\u544A\u7BA1\u7406\u3002"
|
|
7278
7700
|
).version(getVersion());
|
|
7701
|
+
program.hook("preAction", () => {
|
|
7702
|
+
setSiluzanCliInvocation(redactCliArgvForSentry(process.argv));
|
|
7703
|
+
});
|
|
7279
7704
|
program.command("login").description("\u4FDD\u5B58\u8BA4\u8BC1\u51ED\u636E\u5230 ~/.siluzan/config.json\uFF08Token \u6216 API Key \u4E8C\u9009\u4E00\uFF09").option("--api-key <key>", "\u76F4\u63A5\u4FDD\u5B58 API Key\uFF08\u5728\u4E1D\u8DEF\u8D5E\u300C\u8BBE\u7F6E \u2192 API Key \u7BA1\u7406\u300D\u521B\u5EFA\uFF09\uFF0C\u63A8\u8350\u65B9\u5F0F").option("--manual", "\u4EA4\u4E92\u5F0F\u7C98\u8D34 JWT Token\uFF08\u65E0 API Key \u65F6\u4F7F\u7528\uFF09").action(async (opts) => {
|
|
7280
7705
|
await runLogin({ apiKey: opts.apiKey });
|
|
7281
7706
|
});
|
|
@@ -7311,7 +7736,7 @@ program.command("init").description("\u5C06 siluzan-tso Skill \u6587\u4EF6\u5199
|
|
|
7311
7736
|
});
|
|
7312
7737
|
program.command("list-accounts").description("\u67E5\u8BE2\u5E7F\u544A\u8D26\u6237\u5217\u8868\uFF08\u652F\u6301\u6309\u5A92\u4F53\u7C7B\u578B\u3001\u72B6\u6001\u3001\u5173\u952E\u5B57\u7B5B\u9009\uFF09").option(
|
|
7313
7738
|
"-m, --media <type>",
|
|
7314
|
-
`\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | MetaAd | BingV2 | Kwai\uFF08\
|
|
7739
|
+
`\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | MetaAd | BingV2 | Kwai\uFF08\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u6CA1\u6709\u4E00\u6B21\u6027\u67E5\u5168\u90E8\u7684\u65B9\u5F0F\uFF09`
|
|
7315
7740
|
).option("-k, --keyword <text>", "\u6309\u8D26\u6237\u540D\u79F0\u6216\u8D26\u6237 ID \u641C\u7D22").option(
|
|
7316
7741
|
"-s, --status <status>",
|
|
7317
7742
|
"\u8D26\u6237\u72B6\u6001\uFF1Anormal\uFF08\u6B63\u5E38\uFF09| invalid\uFF08\u5931\u6548\uFF09| all\uFF08\u5168\u90E8\uFF0C\u9ED8\u8BA4\uFF09",
|
package/dist/skill/_meta.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "siluzan-tso-cli",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.24",
|
|
4
4
|
"description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@sentry/node": "9",
|
|
29
30
|
"commander": "^12.1.0",
|
|
30
31
|
"open": "^10.1.0",
|
|
31
32
|
"qrcode": "^1.5.4"
|