siluzan-tso-cli 1.0.0-beta.13 → 1.0.0-beta.15

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
@@ -1,6 +1,6 @@
1
1
  # Siluzan TSO Skill — 给大模型的安装说明
2
2
 
3
- 当用户发来本页面的 **URL** 时,请按顺序协助用户完成安装(不要编造不存在的 npm 包名;若公司尚未发布,请改用用户提供的 Git 地址或本地 `tso-cli` 路径)。
3
+ 当用户发来本页面的 **URL** 时,请按顺序协助用户完成安装。
4
4
 
5
5
  ## 1. 环境
6
6
 
@@ -13,10 +13,14 @@
13
13
 
14
14
  ```bash
15
15
  npm install -g siluzan-tso-cli@beta
16
- siluzan-tso init --ai cursor
16
+ siluzan-tso init --ai cursor # 写入 Cursor(默认)
17
+ siluzan-tso init --ai cursor,claude # 同时写入多个平台
18
+ siluzan-tso init --ai all # 写入所有支持的平台
19
+ siluzan-tso init -d /path/to/skills # 写入自定义目录
20
+ siluzan-tso init --force # 强制覆盖已存在文件
17
21
  ```
18
22
 
19
- > **注意**:当前为测试版(1.0.0-beta.13),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
23
+ > **注意**:当前为测试版(1.0.0-beta.15),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
20
24
 
21
25
  | 助手 | 建议 `--ai` |
22
26
  |------|-------------|
@@ -28,39 +32,78 @@ siluzan-tso init --ai cursor
28
32
  | WorkBuddy(全局技能) | `workbuddy-global` |
29
33
  | 一次写入多处 | `all` |
30
34
 
31
- CLI 未发布:指导用户在克隆的仓库根目录执行 `npm install && npm run build`,再 `node tso-cli/dist/index.js init --ai cursor`。
35
+ ## 3. 登录 / 鉴权
32
36
 
33
- ## 3. 登录 / Token
37
+ `siluzan-tso` `siluzan-cso` **共用同一个凭据**(存储在 `~/.siluzan/config.json`)。
34
38
 
35
- `siluzan-tso` `siluzan-cso` **共用同一个 Token**(存储在 `~/.siluzan/config.json`)。
39
+ **若用户已安装 `siluzan-cso` 并完成登录,无需重复操作,直接跳到第 4 步。**
36
40
 
37
- **若用户已安装 siluzan-cso 并完成登录,无需重复操作,直接跳到第 4 步。**
41
+ ### 方式 A:交互式登录(推荐)
38
42
 
39
- 若尚未登录,请先安装 `siluzan-cso-cli` 并完成登录:
43
+ ```bash
44
+ siluzan-tso login
45
+ ```
46
+
47
+ 按提示访问丝路赞控制台「个人设置 → API Key 管理」页面创建 API Key 后粘贴到终端,自动保存。也可跳过交互直接传入:
48
+
49
+ ```bash
50
+ siluzan-tso login --api-key <YOUR_API_KEY>
51
+ ```
52
+
53
+ ### 方式 B:直接设置(适合自动化场景)
54
+
55
+ ```bash
56
+ siluzan-tso config set --api-key <你的ApiKey>
57
+ ```
58
+
59
+ ### 方式 C:Token 登录(已有 siluzan-cso 账号)
40
60
 
41
61
  ```bash
42
62
  npm install -g siluzan-cso-cli
43
63
  siluzan-cso login
44
64
  ```
45
65
 
46
- 向导会在终端中提示用户访问 [https://www.siluzan.com](https://www.siluzan.com) 注册登录,然后在控制台的「个人设置 → API Token」页面复制 Token,粘贴到命令行提示处,自动保存到 `~/.siluzan/config.json`,后续 `siluzan-tso` 命令自动读取。
66
+ 向导会提示用户访问 [https://www.siluzan.com](https://www.siluzan.com) 注册登录,在「个人设置 → API Token」页面复制 Token 粘贴到命令行,自动保存后 `siluzan-tso` 会读取该 Token。
47
67
 
48
- 提醒用户:**不要将 Token 提交到 Git。**
68
+ 提醒用户:**不要将 Token / API Key 提交到 Git。**
49
69
 
50
70
  ## 4. 验证
51
71
 
52
- 确认以下路径已存在且含 `SKILL.md`:
72
+ 确认以下路径已存在且含 `SKILL.md` 和 `_meta.json`:
53
73
 
54
74
  | 助手 | Skill 文件路径 |
55
75
  |------|----------------|
56
76
  | Cursor | `.cursor/skills/siluzan-tso/SKILL.md` |
57
77
  | Claude Code | `.claude/skills/siluzan-tso/SKILL.md` |
58
78
  | OpenClaw 工作区 | `skills/siluzan-tso/SKILL.md` |
79
+ | OpenClaw 全局 | `~/.openclaw/skills/siluzan-tso/SKILL.md` |
59
80
  | WorkBuddy 项目级 | `.workbuddy/skills/siluzan-tso/SKILL.md` |
60
81
  | WorkBuddy 全局 | `~/.workbuddy/skills/siluzan-tso/SKILL.md` |
61
82
 
62
- 完成后,用户可在对话中描述广告账户管理需求(查账户、查余额、查消耗、充值、绑定 MCC/BC/BM 等),由已加载的 Skill 指导 `siluzan-tso` CLI 使用方式。
83
+ 完成后,用户可在对话中描述广告账户管理需求(查账户、查余额、查消耗、充值、开户、绑定 MCC/BC/BM、管理 Google 广告等),由已加载的 Skill 指导 `siluzan-tso` CLI 的使用方式。
84
+
85
+ ## 5. 更新
86
+
87
+ CLI 在每次命令执行后会静默检查是否有新版本可用,有更新时会提示:
88
+
89
+ ```
90
+ 💡 发现新版本 x.x.x(测试版),运行以下命令升级:
91
+ npm install -g siluzan-tso-cli@beta
92
+ ```
93
+
94
+ **发现提示后,Agent 应主动执行以下两步完成升级:**
95
+
96
+ ```bash
97
+ # 第一步:升级 CLI
98
+ {{INSTALL_CMD}}
99
+
100
+ # 第二步:用新版 CLI 重新写入 Skill 文件(--force 覆盖旧版)
101
+ siluzan-tso init --ai xxx --force
102
+ ```
103
+
104
+ > `--ai` 参数换成实际使用的平台(如 `claude`、`openclaw-global` 等)。
105
+ > 若不确定当前安装在哪个平台,可查看 `~/.siluzan/config.json` 中的 `tsoInstalledTargets` 字段。
63
106
 
64
- ## 5. Token 续期
107
+ ## 6. Token 续期
65
108
 
66
- Token 过期后,重新运行 `siluzan-cso login` 即可更新,`siluzan-tso` 会自动读取新 Token,无需额外操作。
109
+ Token 过期后,重新运行 `siluzan-cso login` 或 `siluzan-tso config set --api-key <新Key>` 即可,`siluzan-tso` 自动读取新凭据,无需额外操作。
@@ -51,9 +51,9 @@ function parseTargets(raw) {
51
51
  }
52
52
  return [...new Set(result)];
53
53
  }
54
- function assetsRoot() {
55
- // dist/commands/init.js → ../../assets
56
- return path.join(__dirname, "..", "..", "assets");
54
+ function skillRoot() {
55
+ // dist/commands/init.js → dist/skill/
56
+ return path.join(__dirname, "..", "skill");
57
57
  }
58
58
  /** 将 skill 文件写入指定目录,返回是否有文件被写入 */
59
59
  async function writeSkillFilesToDir(destDir, skillFiles, force) {
@@ -106,7 +106,7 @@ function saveInstalledTargets(entries) {
106
106
  }
107
107
  export async function runInit(options) {
108
108
  const home = os.homedir();
109
- const skillFiles = await getSkillFiles(assetsRoot());
109
+ const skillFiles = await getSkillFiles(skillRoot());
110
110
  const installedEntries = [];
111
111
  if (options.dir) {
112
112
  // 自定义目录模式
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ import { runKeywordSuggest } from "./commands/keyword.js";
26
26
  import { runAccountDelink, runAccountShare, runAccountUnshare, runAccountShareDetail, runAccountAuth, runAccountMccBind, runAccountMccUnbind, runAccountClose, runAccountBmBind, runAccountWithdrawList, runAccountWithdrawSubmit, runAccountBcBind, runAccountBcUnbind, runAccountEmailAuthList, runAccountEmailAuth, runAccountEmailDeauth, } from "./commands/account-manage.js";
27
27
  import { runListAdvertiserGroups, runOpenAccountYandex, runOpenAccountBing, runOpenAccountKwai, runOpenAccountGoogleTimezones, runOpenAccountGoogleWizard, runOpenAccountGoogle, runOpenAccountTikTok, runOpenAccountTikTokTimezones, runOpenAccountTikTokIndustries, runOpenAccountTikTokAreas, runOpenAccountBingIndustries, } from "./commands/open-account.js";
28
28
  import { runLogin } from "./commands/login.js";
29
+ import { notifyIfOutdated } from "./utils/version.js";
29
30
  /** 从 package.json 读取当前版本号 */
30
31
  function getVersion() {
31
32
  try {
@@ -1970,4 +1971,7 @@ program
1970
1971
  console.log(" 诊断入口(测试):https://www-ci.siluzan.com/diagnostics/\n");
1971
1972
  console.log(" 请在浏览器中打开上述地址完成诊断操作。\n");
1972
1973
  });
1973
- program.parse();
1974
+ // parseAsync 支持异步 action;命令执行完后触发版本检查(不阻塞主流程)
1975
+ program.parseAsync().then(() => {
1976
+ notifyIfOutdated();
1977
+ });
@@ -15,7 +15,7 @@ description: >-
15
15
 
16
16
  通过 `siluzan-tso` CLI 完成广告投放管理,不要手动调接口。
17
17
 
18
- 登录与配置详见 `references/setup.md`。
18
+ 配置查看与更新详见 `references/setup.md`。
19
19
 
20
20
  ---
21
21
 
@@ -0,0 +1,5 @@
1
+ {
2
+ "slug": "siluzan-tso",
3
+ "version": "1.0.0-beta.15",
4
+ "publishedAt": 1774319855442
5
+ }
@@ -7,7 +7,7 @@
7
7
 
8
8
  ## invoice-info — 发票抬头管理
9
9
 
10
- 对应页面:`{{WEB_URL}}/v3/foreign_trade/settings/invoiceInformation`
10
+ 对应页面:`https://www-ci.siluzan.com/v3/foreign_trade/settings/invoiceInformation`
11
11
 
12
12
  发票抬头是开票申请时使用的公司/企业信息模板,支持三种类型:
13
13
  - **PI**:形式发票(境外美金账户,英文信息)
@@ -130,10 +130,10 @@ siluzan-tso config show
130
130
  **示例:**
131
131
 
132
132
  ```
133
- - 现金充值(单笔):{{WEB_URL}}/recharge/pay
134
- - 现金充值(批量):{{WEB_URL}}/recharge/pay_batch
135
- - 月结充值: {{WEB_URL}}/recharge/accountBillingQuota
136
- - 丝路赞钱包: {{WEB_URL}}/recharge/siluzanWallet
133
+ - 现金充值(单笔):https://www-ci.siluzan.com/recharge/pay
134
+ - 现金充值(批量):https://www-ci.siluzan.com/recharge/pay_batch
135
+ - 月结充值: https://www-ci.siluzan.com/recharge/accountBillingQuota
136
+ - 丝路赞钱包: https://www-ci.siluzan.com/recharge/siluzanWallet
137
137
  ```
138
138
 
139
139
  ---
@@ -132,10 +132,10 @@ siluzan-tso report list -m Google --json
132
132
 
133
133
  # 第二步:查看 webUrl
134
134
  siluzan-tso config show
135
- # webUrl: {{WEB_URL}}
135
+ # webUrl: https://www-ci.siluzan.com
136
136
 
137
137
  # 第三步:拼接链接(Google 日报)
138
- # {{WEB_URL}}/media-report/publish/rpt_abc123?culture=zh-CN
138
+ # https://www-ci.siluzan.com/media-report/publish/rpt_abc123?culture=zh-CN
139
139
  ```
140
140
 
141
141
  ---
@@ -0,0 +1,119 @@
1
+ # 安装与配置
2
+
3
+ ## 安装 CLI
4
+
5
+ ```bash
6
+ npm install -g siluzan-tso-cli@beta
7
+ ```
8
+
9
+ 环境要求:Node.js 18+
10
+
11
+ ---
12
+
13
+ ## 初始化 Skill 文件
14
+
15
+ 将 Skill 文件写入 AI 助手目录,使 Agent 能读取到本文档:
16
+
17
+ ```bash
18
+ siluzan-tso init # 写入 Cursor(默认)
19
+ siluzan-tso init --ai cursor,claude,workbuddy,openclaw # 同时写入多个平台
20
+ siluzan-tso init --ai all # 写入所有支持的平台
21
+ siluzan-tso init -d /path/to/skills # 写入自定义目录
22
+ siluzan-tso init --force # 强制覆盖已存在文件
23
+ ```
24
+
25
+ 根据你的平台,使用不同的安装命令,如果没有列出你的平台,则使用 `init -d /path/to/skills`的方式,将skill复制到你的skill目录下
26
+ 支持的 `--ai` 目标:
27
+
28
+ | 值 | 写入路径 |
29
+ |----|---------|
30
+ | `cursor` | `.cursor/skills/siluzan-tso/` |
31
+ | `claude` | `.claude/skills/siluzan-tso/` |
32
+ | `openclaw-workspace` | `skills/siluzan-tso/` |
33
+ | `openclaw-global` | `~/.openclaw/skills/siluzan-tso/` |
34
+ | `workbuddy-workspace` | `.workbuddy/skills/siluzan-tso/` |
35
+ | `workbuddy-global` | `~/.workbuddy/skills/siluzan-tso/` |
36
+ | `all` | 以上全部 |
37
+
38
+ ---
39
+
40
+ ## 首次登录 / 配置凭据
41
+
42
+ `siluzan-tso` 与 `siluzan-cso` **共用同一份凭据**,存储在 `~/.siluzan/config.json`,配置一次两个 CLI 均可使用。
43
+
44
+ ```bash
45
+ siluzan-tso login # 交互式登录,按提示创建 API Key 后粘贴
46
+ siluzan-tso login --api-key <YOUR_API_KEY> # 直接设置 API Key(跳过交互)
47
+ siluzan-tso config set --api-key <Key> # 或通过 config set 直接写入
48
+ siluzan-tso config set --token <Token> # 备用:设置 JWT Token
49
+ ```
50
+
51
+ API Key 获取入口:`https://www-ci.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
52
+
53
+ ---
54
+
55
+ ## 查看当前配置
56
+
57
+ ```bash
58
+ siluzan-tso config show
59
+ ```
60
+
61
+ 输出示例:
62
+ ```
63
+ 构建环境 : production
64
+ apiBaseUrl : https://tso-api-ci.siluzan.com
65
+ googleApiUrl : https://googleapi-ci.mysiluzan.com
66
+ webUrl : https://www-ci.siluzan.com
67
+ apiKey : abcd****1234
68
+ ```
69
+
70
+ `webUrl` 是前端页面基地址,需要引导用户打开网页时用此值拼接路径。
71
+
72
+ ---
73
+
74
+ ## 更新 CLI 与 Skill 文件
75
+
76
+ CLI 在每次命令执行后会静默检查新版本,有更新时提示:
77
+
78
+ ```
79
+ 💡 发现新版本 x.x.x,运行以下命令升级:
80
+ npm install -g siluzan-tso-cli@beta
81
+ ```
82
+
83
+ 发现提示后主动执行:
84
+
85
+ ```bash
86
+ npm install -g siluzan-tso-cli@beta # 升级 CLI
87
+ siluzan-tso init --force # 重新写入 Skill 文件(--ai 与初始安装时一致)
88
+ ```
89
+
90
+ 已安装过的目标平台记录在 `~/.siluzan/config.json` 的 `tsoInstalledTargets` 字段,可用于确认需要更新哪些平台。
91
+
92
+ ---
93
+
94
+ ## 修改其他配置
95
+
96
+ ```bash
97
+ siluzan-tso config set --api-base <url> # 切换 TSO API 地址
98
+ siluzan-tso config set --google-api <url> # 切换 Google 网关地址
99
+ siluzan-tso config clear # 清空所有凭据
100
+ ```
101
+
102
+ ---
103
+
104
+ ## 环境切换
105
+
106
+ | 配置项 | 生产环境(默认) | 测试环境 |
107
+ |--------|----------------|---------|
108
+ | `apiBaseUrl` | `https://tso-api.siluzan.com` | `https://tso-api-ci.siluzan.com` |
109
+ | `googleApiUrl` | `https://googleapi.mysiluzan.com` | `https://googleapi-ci.mysiluzan.com` |
110
+
111
+ ```bash
112
+ # 切换到测试环境
113
+ siluzan-tso config set --api-base https://tso-api-ci.siluzan.com
114
+ siluzan-tso config set --google-api https://googleapi-ci.mysiluzan.com
115
+
116
+ # 切回生产环境
117
+ siluzan-tso config set --api-base https://tso-api.siluzan.com
118
+ siluzan-tso config set --google-api https://googleapi.mysiluzan.com
119
+ ```
@@ -6,7 +6,7 @@
6
6
 
7
7
  用 `siluzan-tso config show` 读取 **`webUrl`**,再拼接路径:
8
8
 
9
- 首页地址:`{{WEB_URL}}/v3/foreign_trade/tso/home`
9
+ 首页地址:`https://www-ci.siluzan.com/v3/foreign_trade/tso/home`
10
10
 
11
11
  > 若用户已登录 TSO,也可从左侧菜单进入「首页」。
12
12
 
@@ -252,7 +252,7 @@ Failed / HasFailed(部分或全部失败)
252
252
  # 前置:确认 Google API 地址已配置
253
253
  siluzan-tso config show
254
254
  # 若 googleApiUrl 为空,先配置:
255
- # siluzan-tso config set --google-api {{GOOGLE_API_URL}}
255
+ # siluzan-tso config set --google-api https://googleapi-ci.mysiluzan.com
256
256
 
257
257
  # 第一步:确认目标账户
258
258
  siluzan-tso list-accounts -m Google --json
@@ -1,5 +1,6 @@
1
1
  /**
2
- * assets/siluzan-ads/ 目录读取所有 skill 文件,返回 { 相对路径: 文件内容 } 映射。
3
- * 读取后自动将环境占位符替换为当前构建环境对应的真实地址。
2
+ * 读取 dist/skill/ 目录下所有文件,返回 { 相对路径: 文件内容 } 映射。
3
+ *
4
+ * 占位符替换已在构建阶段(copy-skill-assets.mjs)完成,此处无需二次处理。
4
5
  */
5
- export declare function getSkillFiles(assetsDir: string): Promise<Record<string, string>>;
6
+ export declare function getSkillFiles(skillDir: string): Promise<Record<string, string>>;
@@ -1,45 +1,11 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
- import { DEFAULT_API_BASE, DEFAULT_GOOGLE_API } from "../config/defaults.js";
4
3
  /**
5
- * 根据 TSO API 地址推导前端 Web 地址。
6
- * tso-api.siluzan.com → www.siluzan.com
7
- * tso-api-ci.siluzan.com → www-ci.siluzan.com
8
- */
9
- function deriveWebBaseUrl(tsoApiBase) {
10
- try {
11
- const u = new URL(tsoApiBase);
12
- u.hostname = u.hostname.replace(/^tso-api/, "www");
13
- return u.origin;
14
- }
15
- catch {
16
- return "https://www.siluzan.com";
17
- }
18
- }
19
- // 构建时确定的环境变量,用于替换 markdown 中的占位符
20
- const WEB_URL = deriveWebBaseUrl(DEFAULT_API_BASE);
21
- const TSO_API_URL = DEFAULT_API_BASE;
22
- const GOOGLE_API_URL = DEFAULT_GOOGLE_API;
23
- /**
24
- * 将 markdown 内容中的环境占位符替换为当前构建环境对应的真实地址。
4
+ * 读取 dist/skill/ 目录下所有文件,返回 { 相对路径: 文件内容 } 映射。
25
5
  *
26
- * 占位符规范:
27
- * {{WEB_URL}} → 前端页面基地址(如 https://www.siluzan.com)
28
- * {{TSO_API_URL}} → TSO 主 API 地址
29
- * {{GOOGLE_API_URL}} → Google 广告网关地址
30
- */
31
- function applyEnvPlaceholders(content) {
32
- return content
33
- .replaceAll("{{WEB_URL}}", WEB_URL)
34
- .replaceAll("{{TSO_API_URL}}", TSO_API_URL)
35
- .replaceAll("{{GOOGLE_API_URL}}", GOOGLE_API_URL);
36
- }
37
- /**
38
- * 从 assets/siluzan-ads/ 目录读取所有 skill 文件,返回 { 相对路径: 文件内容 } 映射。
39
- * 读取后自动将环境占位符替换为当前构建环境对应的真实地址。
6
+ * 占位符替换已在构建阶段(copy-skill-assets.mjs)完成,此处无需二次处理。
40
7
  */
41
- export async function getSkillFiles(assetsDir) {
42
- const skillDir = path.join(assetsDir, "siluzan-ads");
8
+ export async function getSkillFiles(skillDir) {
43
9
  const out = {};
44
10
  async function walk(dir, prefix) {
45
11
  const entries = await fs.readdir(dir, { withFileTypes: true });
@@ -50,8 +16,7 @@ export async function getSkillFiles(assetsDir) {
50
16
  await walk(full, rel);
51
17
  }
52
18
  else {
53
- const raw = await fs.readFile(full, "utf8");
54
- out[rel] = applyEnvPlaceholders(raw);
19
+ out[rel] = await fs.readFile(full, "utf8");
55
20
  }
56
21
  }
57
22
  }
@@ -0,0 +1,11 @@
1
+ /** 读取 CLI 自身的当前版本(来自 package.json) */
2
+ export declare function getCurrentVersion(): string;
3
+ /** 比较语义化版本,支持 beta 后缀,返回 true 表示 b > a */
4
+ export declare function isNewer(a: string, b: string): boolean;
5
+ /**
6
+ * 静默版本检查:命令完成后异步触发。
7
+ * - 当前为 beta 版 → 对比 npm beta tag 的最新版本
8
+ * - 当前为正式版 → 对比 npm latest tag 的最新版本
9
+ * 有更新时打印一行提示,不阻塞主命令,不抛出异常。
10
+ */
11
+ export declare function notifyIfOutdated(): Promise<void>;
@@ -0,0 +1,124 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import * as os from "node:os";
4
+ import { fileURLToPath } from "node:url";
5
+ const CONFIG_FILE = path.join(os.homedir(), ".siluzan", "config.json");
6
+ const PKG_NAME = "siluzan-tso-cli";
7
+ /** 读取 CLI 自身的当前版本(来自 package.json) */
8
+ export function getCurrentVersion() {
9
+ try {
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ // dist/utils/version.js → ../../package.json
12
+ const pkgPath = path.join(__dirname, "..", "..", "package.json");
13
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
14
+ return pkg.version ?? "0.0.0";
15
+ }
16
+ catch {
17
+ return "0.0.0";
18
+ }
19
+ }
20
+ /** 判断当前版本是否为 beta(版本号含 -beta) */
21
+ function isBetaVersion(version) {
22
+ return version.includes("-beta");
23
+ }
24
+ /** 比较语义化版本,支持 beta 后缀,返回 true 表示 b > a */
25
+ export function isNewer(a, b) {
26
+ // 拆出纯数字部分,如 "1.0.0-beta.12" → [1,0,0,12]
27
+ const parse = (v) => {
28
+ const [base, pre] = v.replace(/^v/, "").split("-beta.");
29
+ const nums = base.split(".").map(Number);
30
+ // beta 版附加预发布号;无预发布号的正式版视为无穷大(正式版 > beta)
31
+ nums.push(pre !== undefined ? Number(pre) : Infinity);
32
+ return nums;
33
+ };
34
+ const av = parse(a);
35
+ const bv = parse(b);
36
+ for (let i = 0; i < Math.max(av.length, bv.length); i++) {
37
+ const ai = av[i] ?? 0;
38
+ const bi = bv[i] ?? 0;
39
+ if (bi !== ai)
40
+ return bi > ai;
41
+ }
42
+ return false;
43
+ }
44
+ function readConfigRaw() {
45
+ try {
46
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8"));
47
+ }
48
+ catch {
49
+ return {};
50
+ }
51
+ }
52
+ function writeConfigRaw(data) {
53
+ try {
54
+ fs.mkdirSync(path.dirname(CONFIG_FILE), { recursive: true });
55
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2), "utf8");
56
+ if (process.platform !== "win32") {
57
+ fs.chmodSync(CONFIG_FILE, 0o600);
58
+ }
59
+ }
60
+ catch {
61
+ // 写入失败不影响主流程
62
+ }
63
+ }
64
+ /**
65
+ * 从 npm registry 查询指定 tag 的最新版本。
66
+ * 结果缓存于 config.json,24 小时内不重复请求。
67
+ */
68
+ async function fetchVersionByTag(tag, cfg) {
69
+ const cacheKey = tag === "beta" ? "_tsoLatestBeta" : "_tsoLatestStable";
70
+ const hours24 = 24 * 60 * 60 * 1000;
71
+ // 检查 24 小时内的缓存(两个 tag 共用同一个检查时间戳)
72
+ if (cfg._tsoLastVersionCheck && cfg[cacheKey]) {
73
+ const lastCheck = new Date(cfg._tsoLastVersionCheck).getTime();
74
+ if (Date.now() - lastCheck < hours24) {
75
+ return cfg[cacheKey];
76
+ }
77
+ }
78
+ try {
79
+ const url = `https://registry.npmjs.org/${PKG_NAME}/${tag}`;
80
+ const controller = new AbortController();
81
+ const timer = setTimeout(() => controller.abort(), 4000);
82
+ const res = await fetch(url, { signal: controller.signal });
83
+ clearTimeout(timer);
84
+ if (!res.ok)
85
+ return null;
86
+ const data = await res.json();
87
+ return data.version ?? null;
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ /**
94
+ * 静默版本检查:命令完成后异步触发。
95
+ * - 当前为 beta 版 → 对比 npm beta tag 的最新版本
96
+ * - 当前为正式版 → 对比 npm latest tag 的最新版本
97
+ * 有更新时打印一行提示,不阻塞主命令,不抛出异常。
98
+ */
99
+ export async function notifyIfOutdated() {
100
+ try {
101
+ const current = getCurrentVersion();
102
+ const isBeta = isBetaVersion(current);
103
+ const tag = isBeta ? "beta" : "latest";
104
+ const cfg = readConfigRaw();
105
+ const latest = await fetchVersionByTag(tag, cfg);
106
+ if (!latest)
107
+ return;
108
+ // 更新缓存
109
+ const cacheKey = isBeta ? "_tsoLatestBeta" : "_tsoLatestStable";
110
+ writeConfigRaw({
111
+ ...cfg,
112
+ _tsoLastVersionCheck: new Date().toISOString(),
113
+ [cacheKey]: latest,
114
+ });
115
+ if (isNewer(current, latest)) {
116
+ const tagLabel = isBeta ? "(测试版)" : "(正式版)";
117
+ console.error(`\n💡 发现新版本 ${latest}${tagLabel}(当前 ${current}),运行以下命令升级:\n` +
118
+ ` npm install -g ${PKG_NAME}@${tag}\n`);
119
+ }
120
+ }
121
+ catch {
122
+ // 静默,不干扰用户
123
+ }
124
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.0.0-beta.13",
3
+ "version": "1.0.0-beta.15",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,14 +8,13 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
- "assets",
12
11
  "README.md"
13
12
  ],
14
13
  "scripts": {
15
14
  "prebuild": "node scripts/write-defaults.mjs --env production",
16
- "build": "tsc",
17
- "build:prod": "node scripts/write-defaults.mjs --env production && tsc",
18
- "build:test": "node scripts/write-defaults.mjs --env test && tsc",
15
+ "build": "tsc && node scripts/copy-skill-assets.mjs --env production",
16
+ "build:prod": "node scripts/write-defaults.mjs --env production && tsc && node scripts/copy-skill-assets.mjs --env production",
17
+ "build:test": "node scripts/write-defaults.mjs --env test && tsc && node scripts/copy-skill-assets.mjs --env test",
19
18
  "start": "node dist/index.js",
20
19
  "prepublishOnly": "npm run build:prod",
21
20
  "prepublishOnly:test": "npm run build:test"
@@ -1,80 +0,0 @@
1
- # 安装、登录与配置
2
-
3
- ## 安装 Skill 文件(首次)
4
-
5
- 运行以下命令将 Skill 文件写入 AI 助手目录,使 AI Agent 能读取到本文档:
6
-
7
- ```bash
8
- siluzan-tso init # 写入 Cursor(默认)
9
- siluzan-tso init -a cursor,claude # 写入多个平台
10
- siluzan-tso init -d /path/to/skills # 写入自定义目录
11
- siluzan-tso init --force # 强制覆盖已存在文件
12
- ```
13
-
14
- 支持的目标(`-a`):`cursor` | `claude` | `openclaw-workspace` | `openclaw-global` | `workbuddy-workspace` | `workbuddy-global` | `all`
15
-
16
- ---
17
-
18
- ## 首次登录
19
-
20
- ```bash
21
- siluzan-tso login
22
- ```
23
-
24
- 按提示访问 `{{WEB_URL}}/v3/foreign_trade/settings/apiKeyManagement`,创建 API Key 后粘贴到终端。
25
- API Key 保存在 `~/.siluzan/config.json`,与 `siluzan-cso` 共用,配置一次两个 CLI 均可使用。
26
-
27
- 也可直接通过参数跳过交互:
28
-
29
- ```bash
30
- siluzan-tso login --api-key <YOUR_API_KEY>
31
- ```
32
-
33
- ---
34
-
35
- ## 查看当前配置
36
-
37
- ```bash
38
- siluzan-tso config show
39
- ```
40
-
41
- 输出示例:
42
- ```
43
- apiBaseUrl : {{TSO_API_URL}}
44
- googleApiUrl : {{GOOGLE_API_URL}}
45
- webUrl : {{WEB_URL}}
46
- apiKey : abcd****1234
47
- ```
48
-
49
- `webUrl` 是前端页面基地址,需要引导用户打开网页时用此值拼接路径。
50
-
51
- ---
52
-
53
- ## 修改配置
54
-
55
- ```bash
56
- siluzan-tso config set --api-key <新ApiKey> # 推荐:设置 API Key
57
- siluzan-tso config set --token <新Token> # 备用:设置 JWT Token
58
- siluzan-tso config set --api-base <url> # 切换 TSO API 地址
59
- siluzan-tso config set --google-api <url> # 切换 Google 网关地址
60
- siluzan-tso config clear # 清空凭据
61
- ```
62
-
63
- ---
64
-
65
- ## 环境切换
66
-
67
- | 配置项 | 生产环境(默认) | 测试环境 |
68
- |--------|----------------|---------|
69
- | `apiBaseUrl` | `https://tso-api.siluzan.com` | `https://tso-api-ci.siluzan.com` |
70
- | `googleApiUrl` | `https://googleapi.mysiluzan.com` | `https://googleapi-ci.mysiluzan.com` |
71
-
72
- ```bash
73
- # 切换到测试环境
74
- siluzan-tso config set --api-base https://tso-api-ci.siluzan.com
75
- siluzan-tso config set --google-api https://googleapi-ci.mysiluzan.com
76
-
77
- # 切回生产环境
78
- siluzan-tso config set --api-base https://tso-api.siluzan.com
79
- siluzan-tso config set --google-api https://googleapi.mysiluzan.com
80
- ```