@actionway/cli 0.18.4 → 0.18.6

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
@@ -25,14 +25,28 @@ actionway init
25
25
  `doctor` 只输出脱敏后的 Node/npm、registry、global PATH、配置目录、loopback
26
26
  和 OAuth discovery 检查;它不会修改 npm、代理、CA、PATH 或 PowerShell policy。
27
27
 
28
+ ### Development 通道
29
+
30
+ 成功合并并部署 `dev` 后,workflow 从同一 bundle 生成独立公共包 `@actionway/cli-dev`。它只暴露 `actionway-dev`,固定连接 `https://dev.actionway.ai`,使用 `${HOME}/.actionway-dev` 和 `actionway-dev` Skill;不会安装或覆盖正式 `actionway`:
31
+
32
+ ```bash
33
+ npm install --global @actionway/cli-dev@latest
34
+ actionway-dev doctor
35
+ actionway-dev init
36
+ actionway-dev status
37
+ ```
38
+
39
+ `actionway-dev update` 只读取和更新 `@actionway/cli-dev@latest`。每个 dev npm 版本包含不可变 source SHA 与 Actions run metadata;同一 tarball 也保存在私有 GitHub prerelease,后者仅用于内部审计和精确回滚,普通安装不需要 GitHub 账号。
40
+
28
41
  - **auth 唯一途径:Clerk OAuth(PKCE)**。issuer / client_id 运行时从
29
42
  `{public origin}/api/auth/cli-config` 发现;浏览器授权走随机 `127.0.0.1` 回调端口;
30
43
  凭据存 `~/.actionway/credentials.json`(POSIX 上为 0600 owner-only 文件;
31
44
  Windows 上 chmod 无效,保密性依赖 `%USERPROFILE%` 的继承 ACL——不要把
32
45
  `ACTIONWAY_CONFIG_DIR` 指到共享目录)。CLI 从不打印或存储 OAuth client secret。
33
- - **命令面 = 明码写出的注册表**(无 profile flag、无运行期分叉):
46
+ - **命令面 = 明码写出的注册表**(无 capability profile flag;production/development 只切换包、命令、状态、Skill、更新源与 Gateway):
34
47
  - `tools search / inspect / call` 是长尾 capability 的规范发现与执行面;
35
48
  - 高频 typed command 继续提供参数与文件 UX,但投影到同一个 Tool Call;
49
+ - `account invitation` 获取或首次创建当前账户的永久邀请码、链接和服务端本地化分享文案;
36
50
  - `account usage / wallet / transactions` 只读 Actionway Business 数据;
37
51
  - 本壳另有 `init` / `doctor` / `update` / `login` / `logout` / `whoami`,cli-core
38
52
  继续提供 wait/poll 与已冻结的共享 typed commands。
@@ -63,6 +77,7 @@ pnpm cli logout # 删除本机凭据;重复执行也是成功的幂
63
77
  pnpm cli update --check # 查新版(24h 缓存)
64
78
  pnpm cli -- tools search "generate a product image"
65
79
  pnpm cli -- tools inspect media.image.generate
80
+ pnpm cli -- account invitation --locale zh-cn
66
81
  pnpm cli -- account usage
67
82
  pnpm cli -- account usage --from 2026-08-01 --to 2026-09-01 --service generate-image
68
83
  pnpm cli -- account transactions --from 2026-08-01 --kind top_up
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ process.env.ACTIONWAY_CLI_CHANNEL = "development";
9
+ process.env.ACTIONWAY_CONFIG_DIR ||= join(homedir(), ".actionway-dev");
10
+ process.env.ACTIONWAY_GATEWAY_URL ||= "https://dev.actionway.ai";
11
+
12
+ const executable = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "dist", "index.js");
13
+ const result = spawnSync(process.execPath, [executable, ...process.argv.slice(2)], {
14
+ stdio: "inherit",
15
+ env: process.env,
16
+ windowsHide: true,
17
+ });
18
+ if (result.error) throw result.error;
19
+ process.exit(result.status || 0);
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { createHash } from "node:crypto";
5
+ import {
6
+ cpSync,
7
+ existsSync,
8
+ mkdirSync,
9
+ mkdtempSync,
10
+ readFileSync,
11
+ rmSync,
12
+ writeFileSync,
13
+ } from "node:fs";
14
+ import { homedir, tmpdir } from "node:os";
15
+ import { dirname, join, resolve } from "node:path";
16
+
17
+ const repository = (process.env.ACTIONWAY_DEV_REPOSITORY || "PawLogic/actionway").trim();
18
+ const packageName = "@actionway/cli-dev";
19
+ const devHome = resolve(process.env.ACTIONWAY_DEV_HOME || join(homedir(), ".actionway-dev"));
20
+ const metadataPath = join(devHome, "current-cli.json");
21
+ const updateIntervalMs = Number(process.env.ACTIONWAY_DEV_UPDATE_INTERVAL_MS || 300_000);
22
+ const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
23
+
24
+ export function commandInvocation(command, args, platform = process.platform, env = process.env) {
25
+ if (platform === "win32" && /\.(?:cmd|bat)$/i.test(command)) {
26
+ const commandProcessor = (env.ComSpec ?? env.COMSPEC ?? "cmd.exe").trim().replace(/^"|"$/g, "") || "cmd.exe";
27
+ return {
28
+ command: commandProcessor,
29
+ args: ["/d", "/s", "/c", command, ...args],
30
+ };
31
+ }
32
+ return { command, args: [...args] };
33
+ }
34
+
35
+ function run(command, args, { capture = false, env = process.env } = {}) {
36
+ const invocation = commandInvocation(command, args, process.platform, env);
37
+ const result = spawnSync(invocation.command, invocation.args, {
38
+ encoding: "utf8",
39
+ env,
40
+ stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
41
+ windowsHide: true,
42
+ });
43
+ if (result.error) throw result.error;
44
+ if (result.status !== 0) {
45
+ if (capture && result.stderr) process.stderr.write(result.stderr);
46
+ const error = new Error(`${command} exited with status ${result.status}`);
47
+ error.exitCode = result.status || 1;
48
+ throw error;
49
+ }
50
+ return capture ? result.stdout.trim() : "";
51
+ }
52
+
53
+ function readMetadata() {
54
+ if (!existsSync(metadataPath)) return null;
55
+ return JSON.parse(readFileSync(metadataPath, "utf8"));
56
+ }
57
+
58
+ function writeMetadata(metadata) {
59
+ mkdirSync(devHome, { recursive: true, mode: 0o700 });
60
+ writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, { mode: 0o600 });
61
+ }
62
+
63
+ export function validateReleaseManifest(manifest, expectedRepository = repository) {
64
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
65
+ throw new Error("Development release manifest must be a JSON object.");
66
+ }
67
+ if (
68
+ manifest.schema_version !== 1 ||
69
+ manifest.channel !== "development" ||
70
+ manifest.distribution !== "github_release"
71
+ ) {
72
+ throw new Error("Development release manifest has an unsupported schema, channel, or distribution.");
73
+ }
74
+ if (manifest.repository !== expectedRepository) {
75
+ throw new Error("Development release manifest uses an unexpected repository.");
76
+ }
77
+ if (typeof manifest.source_sha !== "string" || !/^[0-9a-f]{40}$/.test(manifest.source_sha)) {
78
+ throw new Error("Development release manifest has an invalid source_sha.");
79
+ }
80
+ if (manifest.release_tag !== `actionway-dev-${manifest.source_sha}`) {
81
+ throw new Error("Development release manifest tag does not match source_sha.");
82
+ }
83
+ if (manifest.gateway_url !== "https://dev.actionway.ai") {
84
+ throw new Error("Development release manifest does not target the development Gateway.");
85
+ }
86
+ if (!manifest.archive || typeof manifest.archive !== "object" || Array.isArray(manifest.archive)) {
87
+ throw new Error("Development release manifest is missing archive metadata.");
88
+ }
89
+ if (typeof manifest.archive.sha256 !== "string" || !/^[0-9a-f]{64}$/.test(manifest.archive.sha256)) {
90
+ throw new Error("Development release manifest has an invalid archive sha256.");
91
+ }
92
+ if (
93
+ manifest.archive.asset_name !==
94
+ `actionway-cli-${manifest.source_sha}-${manifest.archive.sha256}.tgz`
95
+ ) {
96
+ throw new Error("Development release archive asset does not match source_sha and sha256.");
97
+ }
98
+ if (!Number.isSafeInteger(manifest.archive.size_bytes) || manifest.archive.size_bytes <= 0) {
99
+ throw new Error("Development release manifest has an invalid archive size.");
100
+ }
101
+ if (!Number.isSafeInteger(manifest.run_id) || manifest.run_id <= 0) {
102
+ throw new Error("Development release manifest has an invalid run_id.");
103
+ }
104
+ if (
105
+ manifest.run_url !==
106
+ `https://github.com/${manifest.repository}/actions/runs/${manifest.run_id}`
107
+ ) {
108
+ throw new Error("Development release manifest has an invalid run_url.");
109
+ }
110
+ if (manifest.artifact_name !== `actionway-cli-dev-${manifest.source_sha}`) {
111
+ throw new Error("Development release manifest has an invalid artifact_name.");
112
+ }
113
+ if (typeof manifest.published_at !== "string" || !Number.isFinite(Date.parse(manifest.published_at))) {
114
+ throw new Error("Development release manifest has an invalid published_at timestamp.");
115
+ }
116
+
117
+ return {
118
+ headSha: manifest.source_sha,
119
+ runId: manifest.run_id,
120
+ runUrl: manifest.run_url,
121
+ artifact: manifest.artifact_name,
122
+ releaseTag: manifest.release_tag,
123
+ archiveAsset: manifest.archive.asset_name,
124
+ archiveSha256: manifest.archive.sha256,
125
+ archiveSize: manifest.archive.size_bytes,
126
+ archivePath: null,
127
+ };
128
+ }
129
+
130
+ function resolveLatestRelease() {
131
+ const localArchive = process.env.ACTIONWAY_DEV_ARCHIVE?.trim();
132
+ if (localArchive) {
133
+ return {
134
+ headSha: (process.env.ACTIONWAY_DEV_SOURCE_SHA || "local-development").trim(),
135
+ runId: null,
136
+ runUrl: null,
137
+ artifact: null,
138
+ releaseTag: null,
139
+ archiveAsset: null,
140
+ archiveSha256: null,
141
+ archiveSize: null,
142
+ archivePath: resolve(localArchive),
143
+ };
144
+ }
145
+
146
+ const manifest = JSON.parse(run(npmCommand, ["view", `${packageName}@latest`, "--json"], { capture: true }));
147
+ if (manifest?.name !== packageName || typeof manifest.version !== "string") {
148
+ throw new Error(`npm did not return a valid ${packageName}@latest manifest.`);
149
+ }
150
+ const metadata = manifest.actionway;
151
+ if (
152
+ metadata?.channel !== "development" ||
153
+ !/^[0-9a-f]{40}$/.test(metadata.source_sha || "") ||
154
+ !Number.isSafeInteger(metadata.run_id) ||
155
+ metadata.run_id <= 0 ||
156
+ metadata.run_url !== `https://github.com/${repository}/actions/runs/${metadata.run_id}` ||
157
+ metadata.gateway_url !== "https://dev.actionway.ai"
158
+ ) {
159
+ throw new Error(`The published ${packageName}@${manifest.version} metadata is invalid.`);
160
+ }
161
+ if (typeof manifest.dist?.integrity !== "string" || !manifest.dist.integrity.startsWith("sha512-")) {
162
+ throw new Error(`The published ${packageName}@${manifest.version} integrity is missing.`);
163
+ }
164
+ return {
165
+ headSha: metadata.source_sha,
166
+ runId: metadata.run_id,
167
+ runUrl: metadata.run_url,
168
+ artifact: `actionway-cli-dev-${metadata.source_sha}`,
169
+ releaseTag: `actionway-dev-${metadata.source_sha}`,
170
+ archiveAsset: null,
171
+ archiveSha256: null,
172
+ archiveSize: null,
173
+ archivePath: null,
174
+ packageVersion: manifest.version,
175
+ npmIntegrity: manifest.dist.integrity,
176
+ };
177
+ }
178
+
179
+ function installSkill(packageRoot) {
180
+ const source = join(packageRoot, "assets", "skill", "actionway-dev");
181
+ if (!existsSync(source)) throw new Error("The development CLI artifact is missing its actionway-dev Skill.");
182
+ const explicitTarget = process.env.ACTIONWAY_DEV_SKILL_DIR?.trim();
183
+ const officialRoot = join(homedir(), ".agents", "skills");
184
+ const legacyRoot = join(homedir(), ".codex", "skills");
185
+ const skillRoot = process.env.CODEX_HOME?.trim()
186
+ ? join(resolve(process.env.CODEX_HOME), "skills")
187
+ : existsSync(legacyRoot) && !existsSync(officialRoot)
188
+ ? legacyRoot
189
+ : officialRoot;
190
+ const target = explicitTarget ? resolve(explicitTarget) : join(skillRoot, "actionway-dev");
191
+ mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
192
+ rmSync(target, { recursive: true, force: true });
193
+ cpSync(source, target, { recursive: true });
194
+ return target;
195
+ }
196
+
197
+ function refreshInstalledAssets(metadata) {
198
+ const packageRoot = join(metadata.runtime_dir, "node_modules", "@actionway", "cli-dev");
199
+ if (!existsSync(packageRoot)) throw new Error("The installed Actionway development CLI package is missing.");
200
+ const skillDir = installSkill(packageRoot);
201
+ const packagedLauncher = join(packageRoot, "assets", "dev", "actionway-dev-launcher.mjs");
202
+ const installedLauncher = join(devHome, "bin", "actionway-dev-launcher.mjs");
203
+ if (existsSync(packagedLauncher)) {
204
+ mkdirSync(dirname(installedLauncher), { recursive: true, mode: 0o700 });
205
+ cpSync(packagedLauncher, installedLauncher, { force: true });
206
+ }
207
+ return { ...metadata, skill_dir: skillDir };
208
+ }
209
+
210
+ export function verifyArchiveBytes(bytes, expectedSize, expectedSha256) {
211
+ const archive = Buffer.from(bytes);
212
+ if (archive.byteLength !== expectedSize) {
213
+ throw new Error(
214
+ `Development archive size mismatch: expected ${expectedSize}, received ${archive.byteLength}.`,
215
+ );
216
+ }
217
+ const actualSha256 = createHash("sha256").update(archive).digest("hex");
218
+ if (actualSha256 !== expectedSha256) throw new Error("Development archive checksum mismatch.");
219
+ return archive;
220
+ }
221
+
222
+ function installRelease(release) {
223
+ const downloadDir = mkdtempSync(join(tmpdir(), "actionway-dev-cli-"));
224
+ try {
225
+ let archivePath = release.archivePath;
226
+ if (!archivePath) {
227
+ const packed = JSON.parse(
228
+ run(npmCommand, ["pack", `${packageName}@${release.packageVersion}`, "--pack-destination", downloadDir, "--json"], {
229
+ capture: true,
230
+ }),
231
+ );
232
+ const filename = packed?.[0]?.filename;
233
+ if (typeof filename !== "string") throw new Error(`npm pack did not return ${packageName}@${release.packageVersion}.`);
234
+ archivePath = join(downloadDir, filename);
235
+ const actualIntegrity = `sha512-${createHash("sha512").update(readFileSync(archivePath)).digest("base64")}`;
236
+ if (actualIntegrity !== release.npmIntegrity) throw new Error("Development npm archive integrity mismatch.");
237
+ }
238
+ if (!existsSync(archivePath)) throw new Error(`Actionway development CLI archive not found: ${archivePath}`);
239
+
240
+ const runtimeDir = join(devHome, "runtimes", release.headSha);
241
+ mkdirSync(runtimeDir, { recursive: true, mode: 0o700 });
242
+ run(npmCommand, [
243
+ "install",
244
+ "--ignore-scripts",
245
+ "--no-audit",
246
+ "--no-fund",
247
+ "--prefix",
248
+ runtimeDir,
249
+ archivePath,
250
+ ]);
251
+
252
+ const packageRoot = join(runtimeDir, "node_modules", "@actionway", "cli-dev");
253
+ const executable = join(runtimeDir, "node_modules", ".bin", process.platform === "win32" ? "actionway-dev.cmd" : "actionway-dev");
254
+ if (!existsSync(executable)) throw new Error("The development CLI artifact did not install an actionway-dev executable.");
255
+ const skillDir = installSkill(packageRoot);
256
+
257
+ const packagedLauncher = join(packageRoot, "assets", "dev", "actionway-dev-launcher.mjs");
258
+ const installedLauncher = join(devHome, "bin", "actionway-dev-launcher.mjs");
259
+ if (existsSync(packagedLauncher)) {
260
+ mkdirSync(dirname(installedLauncher), { recursive: true, mode: 0o700 });
261
+ cpSync(packagedLauncher, installedLauncher, { force: true });
262
+ }
263
+
264
+ const now = new Date().toISOString();
265
+ const metadata = {
266
+ version: 1,
267
+ source_sha: release.headSha,
268
+ run_id: release.runId,
269
+ run_url: release.runUrl,
270
+ artifact: release.artifact,
271
+ release_tag: release.releaseTag,
272
+ archive_asset: release.archiveAsset,
273
+ archive_sha256: release.archiveSha256,
274
+ package: packageName,
275
+ package_version: release.packageVersion ?? null,
276
+ npm_integrity: release.npmIntegrity ?? null,
277
+ runtime_dir: runtimeDir,
278
+ skill_dir: skillDir,
279
+ gateway_url: "https://dev.actionway.ai",
280
+ installed_at: now,
281
+ last_checked_at: now,
282
+ };
283
+ writeMetadata(metadata);
284
+ return metadata;
285
+ } finally {
286
+ rmSync(downloadDir, { recursive: true, force: true });
287
+ }
288
+ }
289
+
290
+ function checkForUpdate({ install, skipInterval = false } = {}) {
291
+ const current = readMetadata();
292
+ if (!skipInterval && current?.last_checked_at) {
293
+ const age = Date.now() - Date.parse(current.last_checked_at);
294
+ if (Number.isFinite(age) && age >= 0 && age < updateIntervalMs) return current;
295
+ }
296
+
297
+ const latest = resolveLatestRelease();
298
+ const updateAvailable = current?.source_sha !== latest.headSha;
299
+ if (!install) {
300
+ process.stdout.write(
301
+ `${JSON.stringify({
302
+ ok: true,
303
+ update_available: updateAvailable,
304
+ installed_source_sha: current?.source_sha || null,
305
+ latest_source_sha: latest.headSha,
306
+ })}\n`,
307
+ );
308
+ return current;
309
+ }
310
+ if (!updateAvailable) {
311
+ const refreshed = refreshInstalledAssets(current);
312
+ refreshed.last_checked_at = new Date().toISOString();
313
+ writeMetadata(refreshed);
314
+ return refreshed;
315
+ }
316
+ return installRelease(latest);
317
+ }
318
+
319
+ function executableFor(metadata) {
320
+ return join(
321
+ metadata.runtime_dir,
322
+ "node_modules",
323
+ ".bin",
324
+ process.platform === "win32" ? "actionway-dev.cmd" : "actionway-dev",
325
+ );
326
+ }
327
+
328
+ function main(args = process.argv.slice(2)) {
329
+ if (args[0] === "status") {
330
+ process.stdout.write(`${JSON.stringify({ ok: true, channel: "development", ...readMetadata() }, null, 2)}\n`);
331
+ return 0;
332
+ }
333
+ if (args[0] === "update") {
334
+ const checkOnly = args.includes("--check");
335
+ const metadata = checkForUpdate({ install: !checkOnly, skipInterval: true });
336
+ if (!checkOnly && metadata) {
337
+ process.stdout.write(`Actionway development CLI is current at ${metadata.source_sha}.\n`);
338
+ }
339
+ return 0;
340
+ }
341
+
342
+ let metadata = readMetadata();
343
+ try {
344
+ metadata = checkForUpdate({ install: true }) || metadata;
345
+ } catch (error) {
346
+ if (!metadata) throw error;
347
+ process.stderr.write(`Actionway development update check failed; using ${metadata.source_sha}: ${error.message}\n`);
348
+ }
349
+ if (!metadata) throw new Error("Actionway development CLI is not installed. Run the installer first.");
350
+
351
+ const executable = executableFor(metadata);
352
+ if (!existsSync(executable)) throw new Error("The installed Actionway development CLI runtime is missing.");
353
+ const childEnv = {
354
+ ...process.env,
355
+ ACTIONWAY_CONFIG_DIR: devHome,
356
+ ACTIONWAY_GATEWAY_URL: "https://dev.actionway.ai",
357
+ };
358
+ const invocation = commandInvocation(executable, args.length > 0 ? args : ["--help"], process.platform, childEnv);
359
+ const result = spawnSync(invocation.command, invocation.args, {
360
+ stdio: "inherit",
361
+ env: childEnv,
362
+ windowsHide: true,
363
+ });
364
+ if (result.error) throw result.error;
365
+ return result.status || 0;
366
+ }
367
+
368
+ if (process.env.ACTIONWAY_DEV_ASSET_IMPORT_ONLY !== "1") {
369
+ process.exit(main());
370
+ }
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import {
5
+ accessSync,
6
+ chmodSync,
7
+ constants,
8
+ copyFileSync,
9
+ existsSync,
10
+ mkdirSync,
11
+ readFileSync,
12
+ writeFileSync,
13
+ } from "node:fs";
14
+ import { homedir } from "node:os";
15
+ import { delimiter, dirname, join, resolve } from "node:path";
16
+ import { fileURLToPath, pathToFileURL } from "node:url";
17
+
18
+ const repository = (process.env.ACTIONWAY_DEV_REPOSITORY || "PawLogic/actionway").trim();
19
+ const bootstrapRef = (process.env.ACTIONWAY_DEV_BOOTSTRAP_REF || "dev").trim();
20
+ const devHome = resolve(process.env.ACTIONWAY_DEV_HOME || join(homedir(), ".actionway-dev"));
21
+ const installedLauncher = join(devHome, "bin", "actionway-dev-launcher.mjs");
22
+ const marker = "Actionway managed development launcher";
23
+
24
+ export function commandInvocation(command, args, platform = process.platform, env = process.env) {
25
+ if (platform === "win32" && /\.(?:cmd|bat)$/i.test(command)) {
26
+ const commandProcessor = (env.ComSpec ?? env.COMSPEC ?? "cmd.exe").trim().replace(/^"|"$/g, "") || "cmd.exe";
27
+ return {
28
+ command: commandProcessor,
29
+ args: ["/d", "/s", "/c", command, ...args],
30
+ };
31
+ }
32
+ return { command, args: [...args] };
33
+ }
34
+
35
+ function run(command, args, { capture = false } = {}) {
36
+ const invocation = commandInvocation(command, args);
37
+ const result = spawnSync(invocation.command, invocation.args, {
38
+ encoding: "utf8",
39
+ env: process.env,
40
+ stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
41
+ windowsHide: true,
42
+ });
43
+ if (result.error) throw result.error;
44
+ if (result.status !== 0) {
45
+ if (capture && result.stderr) process.stderr.write(result.stderr);
46
+ throw new Error(`${command} exited with status ${result.status || 1}`);
47
+ }
48
+ return capture ? result.stdout : "";
49
+ }
50
+
51
+ function requireNode20() {
52
+ const major = Number(process.versions.node.split(".")[0]);
53
+ if (!Number.isInteger(major) || major < 20) {
54
+ throw new Error(`Actionway development requires Node.js 20 or newer; found ${process.version}.`);
55
+ }
56
+ }
57
+
58
+ function pathEntries() {
59
+ return (process.env.PATH || "")
60
+ .split(delimiter)
61
+ .map((entry) => entry.trim())
62
+ .filter(Boolean)
63
+ .map((entry) => resolve(entry));
64
+ }
65
+
66
+ function samePath(left, right) {
67
+ return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
68
+ }
69
+
70
+ function isOnPath(directory) {
71
+ const target = resolve(directory);
72
+ return pathEntries().some((entry) => samePath(entry, target));
73
+ }
74
+
75
+ function ensureWritable(directory) {
76
+ try {
77
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
78
+ accessSync(directory, constants.W_OK);
79
+ return true;
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ function existingActionwayBin() {
86
+ const executableNames = process.platform === "win32" ? ["actionway.cmd", "actionway.exe", "actionway"] : ["actionway"];
87
+ for (const directory of pathEntries()) {
88
+ if (executableNames.some((name) => existsSync(join(directory, name))) && ensureWritable(directory)) return directory;
89
+ }
90
+ return null;
91
+ }
92
+
93
+ function npmGlobalBin() {
94
+ try {
95
+ const prefix = run(process.platform === "win32" ? "npm.cmd" : "npm", ["prefix", "--global"], {
96
+ capture: true,
97
+ }).trim();
98
+ if (!prefix) return null;
99
+ const directory = process.platform === "win32" ? resolve(prefix) : resolve(prefix, "bin");
100
+ return isOnPath(directory) && ensureWritable(directory) ? directory : null;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+
106
+ function resolveBinDir() {
107
+ if (process.env.ACTIONWAY_DEV_BIN_DIR?.trim()) {
108
+ const explicit = resolve(process.env.ACTIONWAY_DEV_BIN_DIR.trim());
109
+ if (!ensureWritable(explicit)) throw new Error(`Development CLI bin directory is not writable: ${explicit}`);
110
+ return explicit;
111
+ }
112
+ return existingActionwayBin() || npmGlobalBin() || join(homedir(), ".local", "bin");
113
+ }
114
+
115
+ function localLauncherSource() {
116
+ const candidates = [
117
+ process.env.ACTIONWAY_DEV_LAUNCHER?.trim(),
118
+ join(dirname(fileURLToPath(import.meta.url)), "actionway-dev-launcher.mjs"),
119
+ join(process.cwd(), "packages", "cli", "assets", "dev", "actionway-dev-launcher.mjs"),
120
+ ].filter(Boolean);
121
+ return candidates.find((candidate) => existsSync(resolve(candidate))) || null;
122
+ }
123
+
124
+ function fetchLauncher() {
125
+ const endpoint = `repos/${repository}/contents/packages/cli/assets/dev/actionway-dev-launcher.mjs?ref=${encodeURIComponent(bootstrapRef)}`;
126
+ const source = run(
127
+ "gh",
128
+ ["api", "-H", "Accept: application/vnd.github.raw+json", endpoint],
129
+ { capture: true },
130
+ );
131
+ if (!source.startsWith("#!/usr/bin/env node")) {
132
+ throw new Error(`GitHub did not return the Actionway development launcher from ${repository}@${bootstrapRef}.`);
133
+ }
134
+ return source;
135
+ }
136
+
137
+ function installLauncher() {
138
+ mkdirSync(dirname(installedLauncher), { recursive: true, mode: 0o700 });
139
+ const localSource = localLauncherSource();
140
+ if (localSource) copyFileSync(resolve(localSource), installedLauncher);
141
+ else writeFileSync(installedLauncher, fetchLauncher(), { mode: 0o600 });
142
+ run(process.execPath, ["--check", installedLauncher], { capture: true });
143
+ }
144
+
145
+ function installShim(binDir) {
146
+ if (!ensureWritable(binDir)) throw new Error(`Development CLI bin directory is not writable: ${binDir}`);
147
+ const shimPath = join(binDir, process.platform === "win32" ? "actionway-dev.cmd" : "actionway-dev");
148
+ if (existsSync(shimPath) && !readFileSync(shimPath, "utf8").includes(marker)) {
149
+ throw new Error(`Refusing to overwrite unmanaged executable: ${shimPath}`);
150
+ }
151
+ const shim =
152
+ process.platform === "win32"
153
+ ? `@echo off\r\nrem ${marker}\r\nnode "${installedLauncher}" %*\r\n`
154
+ : `#!/usr/bin/env node\n// ${marker}\nimport(${JSON.stringify(pathToFileURL(installedLauncher).href)});\n`;
155
+ writeFileSync(shimPath, shim, { mode: 0o755 });
156
+ if (process.platform !== "win32") chmodSync(shimPath, 0o755);
157
+ return shimPath;
158
+ }
159
+
160
+ function main() {
161
+ requireNode20();
162
+ run(process.platform === "win32" ? "npm.cmd" : "npm", ["--version"], { capture: true });
163
+ if (!process.env.ACTIONWAY_DEV_ARCHIVE?.trim()) {
164
+ run("gh", ["auth", "status", "--hostname", "github.com"], { capture: true });
165
+ }
166
+ installLauncher();
167
+ const binDir = resolveBinDir();
168
+ const shimPath = installShim(binDir);
169
+ run(process.execPath, [installedLauncher, "update"]);
170
+
171
+ process.stdout.write(`Installed ${shimPath}.\n`);
172
+ if (!isOnPath(binDir)) {
173
+ process.stdout.write(`Add ${binDir} to PATH, then open a new terminal.\n`);
174
+ }
175
+ process.stdout.write("Stable command: actionway\nDevelopment command: actionway-dev\n");
176
+ }
177
+
178
+ if (process.env.ACTIONWAY_DEV_ASSET_IMPORT_ONLY !== "1") {
179
+ try {
180
+ main();
181
+ } catch (error) {
182
+ process.stderr.write(`Actionway development installation failed: ${error instanceof Error ? error.message : String(error)}\n`);
183
+ process.exit(1);
184
+ }
185
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: actionway
3
- description: Use the local Actionway CLI to discover, inspect, price, and call Actionway capabilities; continue asynchronous jobs or confirmed calls; read Actionway account usage, wallet, and transactions; authenticate; or update the CLI. Trigger when a local Agent can complete a task through `actionway`, when a `job_ref` or `call_ref` must be continued, or when the user asks about Actionway tools or account data.
3
+ description: Use the local Actionway CLI to discover, inspect, price, and call Actionway capabilities; continue asynchronous jobs or confirmed calls; get the signed-in user's Actionway invitation, account usage, wallet, and transactions; authenticate; or update the CLI. Trigger when a local Agent can complete a task through `actionway`, when a `job_ref` or `call_ref` must be continued, or when the user asks about Actionway tools or account data.
4
4
  metadata:
5
5
  version: "0.0.0-dev"
6
6
  ---
@@ -23,7 +23,8 @@ Use the installed `actionway` CLI and its server-backed Capability Registry as t
23
23
  - If Search returns `clarification_required`, ask its bounded question. If it returns candidates, choose only a supported `tool_ref` and run `actionway tools inspect <tool_ref>` before the generic call. Inspect is the authority for the current input Schema, variants, effects, guidance, and Actionway USD price.
24
24
  - `actionway search` is the Web Search business capability. Capability discovery is always `actionway tools search`.
25
25
  - Use a high-frequency typed command only when `actionway --help` exposes an exact fit. Read `actionway <command> --help` and, when available, `--print-schema`; typed commands are parameter/file conveniences over the same Tool Call, confirmation, billing, and Job path.
26
- - For account questions, use only the requested read command: `actionway account usage`, `actionway account wallet`, or `actionway account transactions`.
26
+ - When the signed-in user asks to generate, show, copy, or share their invitation, run `actionway account invitation --locale <zh-cn|zh-tw|en|ja>` using the conversation language. Return the server-provided `code`, `url`, and `message`; do not invent reward amounts or rewrite promotion terms. The first request creates one permanent code and later requests return that same code.
27
+ - For other account questions, use only the requested read command: `actionway account usage`, `actionway account wallet`, or `actionway account transactions`.
27
28
 
28
29
  ## Inspect and call
29
30
 
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: actionway-dev
3
+ description: Use the installed Actionway development channel to discover, inspect, price, and call capabilities against dev.actionway.ai; test confirmed calls and async jobs; or inspect the deployed dev CLI source SHA without touching the stable Actionway installation. Trigger for Actionway dev environment, staging verification, a newly merged dev deployment, or pre-production Codex testing.
4
+ ---
5
+
6
+ # Actionway Dev
7
+
8
+ Use only the `actionway-dev` executable. Never substitute the stable `actionway` command.
9
+
10
+ ## Prepare
11
+
12
+ 1. Run `actionway-dev update` once per Agent session. It follows public `@actionway/cli-dev@latest` from the latest successful `dev` deployment and never updates the stable `@actionway/cli` package.
13
+ 2. Run `actionway-dev status` when the user asks which source SHA is installed.
14
+ 3. Run `actionway-dev whoami`. On `E_NOT_AUTHENTICATED`, run `actionway-dev init` and ask the user only to approve browser login. Never request or expose credentials.
15
+
16
+ ## Discover and call
17
+
18
+ 1. For long-tail capabilities run `actionway-dev tools search "<intent>"`, then inspect exactly one supported result with `actionway-dev tools inspect <tool_ref>`.
19
+ 2. Match the inspected Schema exactly and call with `actionway-dev tools call <tool_ref> --input-json '<object>'`.
20
+ 3. On `confirmation_required`, present the server plan and quote. Resume only after approval with `actionway-dev tools call --resume <call_ref>`.
21
+ 4. On `accepted`, immediately run `actionway-dev wait --job-ref=<uuid>`. Reuse the same reference until terminal state; never resubmit a pending call.
22
+
23
+ Use typed commands only when `actionway-dev --help` exposes an exact fit. Read command help and `--print-schema`, then run `--dry-run` before a real typed call.
24
+
25
+ ## Boundaries
26
+
27
+ - Treat the development Gateway as the authority for Schema, price, confirmation, and results.
28
+ - Development calls can consume development credits and real upstream vendor cost.
29
+ - Do not run the stable CLI's updater, publish npm packages, or change production configuration.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Actionway Dev"
3
+ short_description: "Use the isolated Actionway development channel"
4
+ default_prompt: "Use $actionway-dev to test a capability against the latest Actionway development deployment."
package/dist/index.js CHANGED
@@ -12152,13 +12152,36 @@ import { chmodSync, existsSync, mkdirSync, readFileSync as readFileSync2, writeF
12152
12152
  import { homedir } from "node:os";
12153
12153
  import { join } from "node:path";
12154
12154
 
12155
+ // src/distribution.ts
12156
+ var PRODUCTION_DISTRIBUTION = {
12157
+ channel: "production",
12158
+ packageName: "@actionway/cli",
12159
+ distTag: "latest",
12160
+ commandName: "actionway",
12161
+ configDirectoryName: ".actionway",
12162
+ skillName: "actionway",
12163
+ gatewayUrl: "https://actionway.ai"
12164
+ };
12165
+ var DEVELOPMENT_DISTRIBUTION = {
12166
+ channel: "development",
12167
+ packageName: "@actionway/cli-dev",
12168
+ distTag: "latest",
12169
+ commandName: "actionway-dev",
12170
+ configDirectoryName: ".actionway-dev",
12171
+ skillName: "actionway-dev",
12172
+ gatewayUrl: "https://dev.actionway.ai"
12173
+ };
12174
+ function cliDistribution(env = process.env) {
12175
+ return env.ACTIONWAY_CLI_CHANNEL === "development" ? DEVELOPMENT_DISTRIBUTION : PRODUCTION_DISTRIBUTION;
12176
+ }
12177
+
12155
12178
  // src/version.ts
12156
12179
  import { readFileSync } from "node:fs";
12157
12180
  import { dirname as dirname2, resolve } from "node:path";
12158
12181
  import { fileURLToPath as fileURLToPath2 } from "node:url";
12159
- var PACKAGE_NAME = "@actionway/cli";
12160
- function readOwnVersion(moduleUrl = import.meta.url) {
12182
+ function readOwnPackage(moduleUrl = import.meta.url, env = process.env) {
12161
12183
  const here = dirname2(fileURLToPath2(moduleUrl));
12184
+ const expectedPackage = cliDistribution(env).packageName;
12162
12185
  for (const path of [
12163
12186
  resolve(here, "package.json"),
12164
12187
  resolve(here, "../package.json"),
@@ -12168,13 +12191,27 @@ function readOwnVersion(moduleUrl = import.meta.url) {
12168
12191
  const parsed = JSON.parse(readFileSync(path, "utf8"));
12169
12192
  if (typeof parsed !== "object" || parsed === null) continue;
12170
12193
  const manifest = parsed;
12171
- if (manifest.name === PACKAGE_NAME && typeof manifest.version === "string" && manifest.version.trim()) {
12172
- return manifest.version.trim();
12194
+ if (manifest.name === expectedPackage && typeof manifest.version === "string" && manifest.version.trim()) {
12195
+ const result = {
12196
+ name: expectedPackage,
12197
+ version: manifest.version.trim(),
12198
+ ...typeof manifest.actionway === "object" && manifest.actionway !== null ? { actionway: manifest.actionway } : {}
12199
+ };
12200
+ if (expectedPackage === "@actionway/cli-dev") {
12201
+ const metadata = result.actionway;
12202
+ if (metadata?.channel !== "development" || !/^[0-9a-f]{40}$/.test(metadata.source_sha ?? "") || !Number.isSafeInteger(metadata.run_id) || (metadata.run_id ?? 0) <= 0 || metadata.run_url !== `https://github.com/PawLogic/actionway/actions/runs/${metadata.run_id}` || metadata.gateway_url !== "https://dev.actionway.ai") {
12203
+ throw new Error("the installed @actionway/cli-dev package metadata is invalid");
12204
+ }
12205
+ }
12206
+ return result;
12173
12207
  }
12174
12208
  } catch {
12175
12209
  }
12176
12210
  }
12177
- throw new Error(`cannot determine the installed ${PACKAGE_NAME} version`);
12211
+ throw new Error(`cannot determine the installed ${expectedPackage} version`);
12212
+ }
12213
+ function readOwnVersion(moduleUrl = import.meta.url, env = process.env) {
12214
+ return readOwnPackage(moduleUrl, env).version;
12178
12215
  }
12179
12216
 
12180
12217
  // src/lib/npm-runner.ts
@@ -12275,14 +12312,14 @@ function runNpmText(args, operation, options = {}) {
12275
12312
  }
12276
12313
 
12277
12314
  // src/lib/update-state.ts
12278
- var PACKAGE_NAME2 = "@actionway/cli";
12279
12315
  var UPDATE_STATE_BASENAME = "update.json";
12280
12316
  var UPDATE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
12317
+ var DEVELOPMENT_VERSION_PATTERN = /^0\.0\.0-dev\.[1-9]\d*\.[0-9a-f]{12}$/;
12281
12318
  function actionwayConfigDir(env = process.env) {
12282
12319
  const override = (env.ACTIONWAY_CONFIG_DIR ?? "").trim();
12283
12320
  if (override) return override;
12284
12321
  const home = (env.HOME ?? "").trim() || homedir();
12285
- return join(home, ".actionway");
12322
+ return join(home, cliDistribution(env).configDirectoryName);
12286
12323
  }
12287
12324
  function updateStatePath(env = process.env) {
12288
12325
  return join(actionwayConfigDir(env), UPDATE_STATE_BASENAME);
@@ -12331,7 +12368,15 @@ function loadState(env) {
12331
12368
  if (existsSync(path)) {
12332
12369
  try {
12333
12370
  const parsed = parseState(readFileSync2(path, "utf8"));
12334
- if (parsed) return parsed;
12371
+ if (parsed) {
12372
+ if (cliDistribution(env).channel === "development") {
12373
+ if (parsed.latest_version && !DEVELOPMENT_VERSION_PATTERN.test(parsed.latest_version)) {
12374
+ return emptyState();
12375
+ }
12376
+ return { ...parsed, min_version: null };
12377
+ }
12378
+ return parsed;
12379
+ }
12335
12380
  } catch {
12336
12381
  }
12337
12382
  }
@@ -12353,6 +12398,7 @@ function readVersionState(env = process.env) {
12353
12398
  return { latest_version: state.latest_version, min_version: state.min_version, checked_at: state.checked_at };
12354
12399
  }
12355
12400
  function recordVersionSignal(signal, env = process.env, now = /* @__PURE__ */ new Date()) {
12401
+ if (cliDistribution(env).channel === "development") return;
12356
12402
  const latest = (signal.latest ?? "").trim();
12357
12403
  const min = (signal.min ?? "").trim();
12358
12404
  const validLatest = latest && isValidVersion(latest) ? latest : null;
@@ -12375,19 +12421,28 @@ function recordVersionSignal(signal, env = process.env, now = /* @__PURE__ */ ne
12375
12421
  );
12376
12422
  }
12377
12423
  function fetchLatestVersionFromNpm(env) {
12424
+ const distribution = cliDistribution(env);
12378
12425
  const output = runNpmText(
12379
- ["view", `${PACKAGE_NAME2}@latest`, "version"],
12380
- `npm registry check for ${PACKAGE_NAME2}@latest`,
12426
+ ["view", `${distribution.packageName}@${distribution.distTag}`, "version"],
12427
+ `npm registry check for ${distribution.packageName}@${distribution.distTag}`,
12381
12428
  { env, timeoutMs: 15e3 }
12382
12429
  );
12383
12430
  const version = output.split(/\r?\n/).at(-1)?.trim() ?? "";
12384
- if (!version) throw new Error(`npm returned no version for ${PACKAGE_NAME2}@latest`);
12431
+ if (!version) throw new Error(`npm returned no version for ${distribution.packageName}@${distribution.distTag}`);
12385
12432
  return version;
12386
12433
  }
12387
12434
  function installFromNpm(env) {
12435
+ const distribution = cliDistribution(env);
12388
12436
  runNpmText(
12389
- ["install", "--global", "--no-audit", "--no-fund", "--no-update-notifier", `${PACKAGE_NAME2}@latest`],
12390
- `npm global update for ${PACKAGE_NAME2}`,
12437
+ [
12438
+ "install",
12439
+ "--global",
12440
+ "--no-audit",
12441
+ "--no-fund",
12442
+ "--no-update-notifier",
12443
+ `${distribution.packageName}@${distribution.distTag}`
12444
+ ],
12445
+ `npm global update for ${distribution.packageName}`,
12391
12446
  { env, timeoutMs: 12e4 }
12392
12447
  );
12393
12448
  }
@@ -12396,12 +12451,13 @@ function isFresh(state, now) {
12396
12451
  const checkedAt = Date.parse(state.checked_at);
12397
12452
  return Number.isFinite(checkedAt) && now.getTime() - checkedAt >= 0 && now.getTime() - checkedAt < UPDATE_CACHE_TTL_MS;
12398
12453
  }
12399
- function statusFrom(currentVersion, latestVersion, checkedAt, fromCache) {
12454
+ function statusFrom(currentVersion, latestVersion, checkedAt, fromCache, env) {
12455
+ const distribution = cliDistribution(env);
12400
12456
  return {
12401
- package: PACKAGE_NAME2,
12457
+ package: distribution.packageName,
12402
12458
  current_version: currentVersion,
12403
12459
  latest_version: latestVersion,
12404
- update_available: isValidVersion(latestVersion) && compareVersions(latestVersion, currentVersion) > 0,
12460
+ update_available: isValidVersion(latestVersion) && (distribution.channel === "development" ? latestVersion !== currentVersion : compareVersions(latestVersion, currentVersion) > 0),
12405
12461
  checked_at: checkedAt,
12406
12462
  from_cache: fromCache
12407
12463
  };
@@ -12412,12 +12468,12 @@ function getUpdateStatus(options = {}) {
12412
12468
  const currentVersion = options.currentVersion ?? readOwnVersion();
12413
12469
  const state = loadState(env);
12414
12470
  if (!options.refresh && isFresh(state, now) && state.latest_version) {
12415
- return statusFrom(currentVersion, state.latest_version, state.checked_at ?? now.toISOString(), true);
12471
+ return statusFrom(currentVersion, state.latest_version, state.checked_at ?? now.toISOString(), true, env);
12416
12472
  }
12417
12473
  const latestVersion = (options.fetchLatestVersion ?? fetchLatestVersionFromNpm)(env);
12418
12474
  const checkedAt = now.toISOString();
12419
12475
  saveState({ version: 2, checked_at: checkedAt, latest_version: latestVersion, min_version: state.min_version }, env);
12420
- return statusFrom(currentVersion, latestVersion, checkedAt, false);
12476
+ return statusFrom(currentVersion, latestVersion, checkedAt, false, env);
12421
12477
  }
12422
12478
  function runUpdate(options = {}) {
12423
12479
  const env = options.env ?? process.env;
@@ -13015,6 +13071,10 @@ function failAccount(error) {
13015
13071
  }
13016
13072
  fail({ code: "E_BACKEND", message: error instanceof Error ? error.message : String(error) });
13017
13073
  }
13074
+ function invitationEndpoint(locale = "en") {
13075
+ const parameters = new URLSearchParams({ locale });
13076
+ return `v1/account/invitation?${parameters.toString()}`;
13077
+ }
13018
13078
  function accountEndpoint(path, options) {
13019
13079
  const parameters = new URLSearchParams();
13020
13080
  for (const [name, value] of [
@@ -13040,7 +13100,8 @@ async function read(endpoint, getTransport3) {
13040
13100
  }
13041
13101
  }
13042
13102
  function registerAccountCommands(program2, getTransport3) {
13043
- const account = program2.command("account").description("Read your Actionway USD wallet, service usage, and wallet transactions.");
13103
+ const account = program2.command("account").description("Access your Actionway invitation, USD wallet, service usage, and wallet transactions.");
13104
+ account.command("invitation").description("Get or create your permanent invitation code, link, and localized share message.").option("--locale <locale>", "Share-message locale: zh-cn, zh-tw, en, or ja.", "en").action((options) => read(invitationEndpoint(options.locale), getTransport3));
13044
13105
  account.command("usage").description("Query held, charged, released, and free capability calls.").option("--limit <count>", "Maximum records in this page (1-100).").option("--cursor <cursor>", "Opaque nextCursor returned by the previous page.").option("--from <date>", "Inclusive ISO date or timestamp (UTC for date-only values).").option("--to <date>", "Exclusive ISO date or timestamp (UTC for date-only values).").option("--status <status>", "held, settled, or expired.").option("--service <service-id>", "Exact public serviceId.").option("--pricing-kind <kind>", "priced or free.").action((options) => read(accountEndpoint("usage", options), getTransport3));
13045
13106
  account.command("wallet").description("Show the current available and held Actionway USD wallet balance.").action(() => read("v1/account/wallet", getTransport3));
13046
13107
  account.command("transactions").description("Query wallet top-ups, charges, promotions, refunds, and other fund movements.").option("--limit <count>", "Maximum records in this page (1-50).").option("--cursor <cursor>", "Opaque nextCursor returned by the previous page.").option("--from <date>", "Inclusive ISO date or timestamp (UTC for date-only values).").option("--to <date>", "Exclusive ISO date or timestamp (UTC for date-only values).").option("--status <status>", "pending, succeeded, or failed.").option("--kind <kind>", "top_up, charge, refund, dispute, or promotion kind.").action((options) => read(accountEndpoint("transactions", options), getTransport3));
@@ -13052,7 +13113,6 @@ import { existsSync as existsSync2, rmSync as rmSync2, statSync, writeFileSync a
13052
13113
  import { createServer as createServer2 } from "node:http";
13053
13114
  import { randomBytes as randomBytes2 } from "node:crypto";
13054
13115
  import { dirname as dirname3, join as join3, posix, win32 } from "node:path";
13055
- var PACKAGE_NAME3 = "@actionway/cli";
13056
13116
  function pass(id, message) {
13057
13117
  return { id, status: "pass", message };
13058
13118
  }
@@ -13141,6 +13201,7 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
13141
13201
  const readPolicy = dependencies.readPowerShellPolicy ?? powerShellPolicy;
13142
13202
  const resolveCommand = dependencies.resolveCommand ?? defaultResolveCommand;
13143
13203
  const checks = [];
13204
+ const distribution = cliDistribution(env);
13144
13205
  const configDir = actionwayConfigDir(env);
13145
13206
  const nodeMajor = Number.parseInt(nodeVersion.split(".")[0] ?? "", 10);
13146
13207
  checks.push(
@@ -13172,7 +13233,7 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
13172
13233
  safe.startsWith("https://registry.npmjs.org/") ? pass("scope_registry", `@actionway packages use ${safe}.`) : warn(
13173
13234
  "scope_registry",
13174
13235
  `@actionway packages are mapped to ${safe}.`,
13175
- "make sure that registry mirrors public @actionway/cli, or install from the public npm registry when policy permits"
13236
+ `make sure that registry mirrors public ${distribution.packageName}, or install from the public npm registry when policy permits`
13176
13237
  )
13177
13238
  );
13178
13239
  } else if (scopeResult.error || scopeResult.status !== 0) {
@@ -13188,14 +13249,21 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
13188
13249
  } else {
13189
13250
  const api = pathApi(platform);
13190
13251
  const binDir = platform === "win32" ? prefix : api.join(prefix, "bin");
13191
- const binName = platform === "win32" ? "actionway.cmd" : "actionway";
13252
+ const binName = platform === "win32" ? `${distribution.commandName}.cmd` : distribution.commandName;
13192
13253
  let resolvedCache;
13193
- const resolved = () => resolvedCache !== void 0 ? resolvedCache : resolvedCache = resolveCommand("actionway");
13254
+ const resolved = () => resolvedCache !== void 0 ? resolvedCache : resolvedCache = resolveCommand(distribution.commandName);
13194
13255
  checks.push(
13195
- pathContains(binDir, env, platform) ? pass("global_path", `npm global executable directory is in PATH: ${binDir}`) : resolved() ? pass("global_path", `actionway resolves from PATH at ${resolved()} (outside the npm global prefix ${binDir}).`) : fail2("global_path", `npm global executable directory is not in PATH: ${binDir}`, "add it to the user PATH and open a new terminal")
13256
+ pathContains(binDir, env, platform) ? pass("global_path", `npm global executable directory is in PATH: ${binDir}`) : resolved() ? pass(
13257
+ "global_path",
13258
+ `${distribution.commandName} resolves from PATH at ${resolved()} (outside the npm global prefix ${binDir}).`
13259
+ ) : fail2("global_path", `npm global executable directory is not in PATH: ${binDir}`, "add it to the user PATH and open a new terminal")
13196
13260
  );
13197
13261
  checks.push(
13198
- pathExists(api.join(binDir, binName)) ? pass("command", `${binName} is installed in the npm global prefix.`) : resolved() ? pass("command", `actionway command resolves at ${resolved()}.`) : fail2("command", `${binName} was not found in the npm global prefix.`, "reinstall @actionway/cli@latest in the same Node/npm environment")
13262
+ pathExists(api.join(binDir, binName)) ? pass("command", `${binName} is installed in the npm global prefix.`) : resolved() ? pass("command", `${distribution.commandName} command resolves at ${resolved()}.`) : fail2(
13263
+ "command",
13264
+ `${binName} was not found in the npm global prefix.`,
13265
+ `reinstall ${distribution.packageName}@${distribution.distTag} in the same Node/npm environment`
13266
+ )
13199
13267
  );
13200
13268
  }
13201
13269
  if (platform === "win32") {
@@ -13203,9 +13271,12 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
13203
13271
  checks.push(
13204
13272
  policy && ["Restricted", "AllSigned"].includes(policy) ? warn(
13205
13273
  "powershell_policy",
13206
- `PowerShell execution policy is ${policy}; npm's actionway.ps1 shim may be blocked.`,
13207
- "use actionway.cmd; do not weaken organization policy without approval"
13208
- ) : pass("powershell_policy", `PowerShell execution policy is ${policy || "not available"}; actionway.cmd remains the stable entrypoint.`)
13274
+ `PowerShell execution policy is ${policy}; npm's ${distribution.commandName}.ps1 shim may be blocked.`,
13275
+ `use ${distribution.commandName}.cmd; do not weaken organization policy without approval`
13276
+ ) : pass(
13277
+ "powershell_policy",
13278
+ `PowerShell execution policy is ${policy || "not available"}; ${distribution.commandName}.cmd remains the entrypoint.`
13279
+ )
13209
13280
  );
13210
13281
  }
13211
13282
  try {
@@ -13224,13 +13295,19 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
13224
13295
  checks.push({ id: "npm_registry", status: "skip", message: "npm registry check was skipped." });
13225
13296
  checks.push({ id: "actionway_oauth", status: "skip", message: "Actionway OAuth discovery check was skipped." });
13226
13297
  } else {
13227
- const packageResult = npm(["view", `${PACKAGE_NAME3}@latest`, "version"], { env, platform, timeoutMs: 15e3 });
13298
+ const packageResult = npm(
13299
+ ["view", `${distribution.packageName}@${distribution.distTag}`, "version"],
13300
+ { env, platform, timeoutMs: 15e3 }
13301
+ );
13228
13302
  checks.push(
13229
13303
  npmCheck(
13230
13304
  "npm_registry",
13231
13305
  packageResult,
13232
13306
  "Actionway package lookup",
13233
- (version) => pass("npm_registry", `${PACKAGE_NAME3}@latest is reachable (${version.split(/\r?\n/).at(-1)}).`)
13307
+ (version) => pass(
13308
+ "npm_registry",
13309
+ `${distribution.packageName}@${distribution.distTag} is reachable (${version.split(/\r?\n/).at(-1)}).`
13310
+ )
13234
13311
  )
13235
13312
  );
13236
13313
  try {
@@ -13256,9 +13333,10 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
13256
13333
  };
13257
13334
  }
13258
13335
  function registerDoctorCommand(program2) {
13336
+ const distribution = cliDistribution();
13259
13337
  program2.command("doctor").description("Diagnose Node, npm, PATH, registry, configuration, and local OAuth callback readiness.").option("--no-network", "skip npm registry and Actionway OAuth discovery checks").action(async (options) => {
13260
13338
  const report = await diagnoseActionway({ network: options.network !== false });
13261
- const command = process.platform === "win32" ? "actionway.cmd" : "actionway";
13339
+ const command = process.platform === "win32" ? `${distribution.commandName}.cmd` : distribution.commandName;
13262
13340
  ok({
13263
13341
  ...report,
13264
13342
  hint: report.ready ? `Actionway CLI prerequisites are ready. Run \`${command} init\`.` : `Resolve the failed checks, then run \`${command} doctor\` again.`
@@ -13282,15 +13360,21 @@ var InitError = class extends Error {
13282
13360
  }
13283
13361
  };
13284
13362
  var ACTIONWAY_SKILL_INSTRUCTION = "Install this skill from `source` into your agent's skill directory and make sure it loads in future sessions. If a skill named `actionway` already exists there, replace it only when it is an earlier Actionway skill (check its frontmatter metadata.version); otherwise ask the user first.";
13285
- function resolveBundledSkill(moduleUrl = import.meta.url) {
13363
+ var ACTIONWAY_DEV_SKILL_INSTRUCTION = "Install this skill from `source` into your agent's skill directory and make sure it loads in future sessions. If a skill named `actionway-dev` already exists there, replace it only when it is an earlier Actionway development skill (check its frontmatter metadata.version); otherwise ask the user first. Never replace the stable `actionway` Skill.";
13364
+ function resolveBundledSkill(moduleUrl = import.meta.url, env = process.env) {
13286
13365
  const here = dirname4(fileURLToPath3(moduleUrl));
13287
- for (const candidate of [resolve2(here, "../assets/skill/actionway"), resolve2(here, "../../assets/skill/actionway")]) {
13366
+ const skillName = cliDistribution(env).skillName;
13367
+ for (const candidate of [resolve2(here, `../assets/skill/${skillName}`), resolve2(here, `../../assets/skill/${skillName}`)]) {
13288
13368
  if (existsSync3(join4(candidate, "SKILL.md"))) return realpathSync2(candidate);
13289
13369
  }
13290
- throw new InitError("the bundled Actionway Skill is missing", "reinstall @actionway/cli, then run `actionway init` again");
13370
+ const distribution = cliDistribution(env);
13371
+ throw new InitError(
13372
+ "the bundled Actionway Skill is missing",
13373
+ `reinstall ${distribution.packageName}@${distribution.distTag}, then run \`${distribution.commandName} init\` again`
13374
+ );
13291
13375
  }
13292
13376
  function skillSourcePath(env = process.env) {
13293
- return join4(actionwayConfigDir(env), "skill", "actionway");
13377
+ return join4(actionwayConfigDir(env), "skill", cliDistribution(env).skillName);
13294
13378
  }
13295
13379
  function stampSkillVersion(skillMarkdown, version) {
13296
13380
  const newline = skillMarkdown.includes("\r\n") ? "\r\n" : "\n";
@@ -13329,18 +13413,26 @@ function stampSkillVersion(skillMarkdown, version) {
13329
13413
  }
13330
13414
  function materializeSkill(options = {}) {
13331
13415
  const env = options.env ?? process.env;
13332
- const source = options.source ? realpathSync2(options.source) : resolveBundledSkill();
13416
+ const distribution = cliDistribution(env);
13417
+ const source = options.source ? realpathSync2(options.source) : resolveBundledSkill(import.meta.url, env);
13333
13418
  if (!existsSync3(join4(source, "SKILL.md"))) {
13334
- throw new InitError("the Actionway Skill source is invalid", "reinstall @actionway/cli and retry init");
13419
+ throw new InitError(
13420
+ "the Actionway Skill source is invalid",
13421
+ `reinstall ${distribution.packageName}@${distribution.distTag} and retry init`
13422
+ );
13335
13423
  }
13336
- const version = options.version ?? readOwnVersion();
13424
+ const version = options.version ?? readOwnVersion(import.meta.url, env);
13337
13425
  const target = skillSourcePath(env);
13338
13426
  mkdirSync3(dirname4(target), { recursive: true, mode: 448 });
13339
13427
  rmSync3(target, { recursive: true, force: true });
13340
13428
  cpSync(source, target, { recursive: true });
13341
13429
  const skillPath = join4(target, "SKILL.md");
13342
13430
  writeFileSync4(skillPath, stampSkillVersion(readFileSync4(skillPath, "utf8"), version));
13343
- return { source: target, version, instruction: ACTIONWAY_SKILL_INSTRUCTION };
13431
+ return {
13432
+ source: target,
13433
+ version,
13434
+ instruction: distribution.channel === "development" ? ACTIONWAY_DEV_SKILL_INSTRUCTION : ACTIONWAY_SKILL_INSTRUCTION
13435
+ };
13344
13436
  }
13345
13437
 
13346
13438
  // src/lib/install-session.ts
@@ -13461,6 +13553,7 @@ function failFromError3(err) {
13461
13553
  fail({ code: "E_BACKEND", message: err instanceof Error ? err.message : String(err) });
13462
13554
  }
13463
13555
  function registerInitCommand(program2) {
13556
+ const distribution = cliDistribution();
13464
13557
  program2.command("init").description(
13465
13558
  "Complete browser authentication and stage the Actionway Skill for your agent to install. The CLI never writes into an agent's own configuration directory."
13466
13559
  ).option("--gateway <url>", "Actionway public origin").option("--client-id <id>", "Actionway OAuth client id (must be paired with --issuer)").option("--issuer <url>", "Actionway OAuth issuer (must be paired with --client-id)").option("--timeout-seconds <n>", "how long to wait for browser authentication").option("--install-session-id <uuid>", "onboarding install session id").option("--no-browser", "print the authorize URL instead of opening a browser").addOption(new Option("--skill-only").hideHelp()).action(async (opts) => {
@@ -13502,7 +13595,7 @@ function registerInitCommand(program2) {
13502
13595
  skill,
13503
13596
  workspace_id: account.workspaceId,
13504
13597
  ...snapshot?.user ? { user: snapshot.user } : {},
13505
- hint: "Actionway is ready. Install the skill from `skill.source` into your agent's skill directory, then run `actionway --help` to discover supported capabilities."
13598
+ hint: `Actionway is ready. Install the skill from \`skill.source\` into your agent's skill directory, then run \`${distribution.commandName} --help\` to discover supported capabilities.`
13506
13599
  });
13507
13600
  } catch (error) {
13508
13601
  failFromError3(error);
@@ -13510,6 +13603,26 @@ function registerInitCommand(program2) {
13510
13603
  });
13511
13604
  }
13512
13605
 
13606
+ // src/commands/status.ts
13607
+ function registerDevelopmentStatusCommand(program2) {
13608
+ const distribution = cliDistribution();
13609
+ if (distribution.channel !== "development") return;
13610
+ program2.command("status").description("Show the installed Actionway development channel and immutable source revision.").action(() => {
13611
+ const manifest = readOwnPackage();
13612
+ const metadata = manifest.actionway;
13613
+ ok({
13614
+ channel: "development",
13615
+ package: manifest.name,
13616
+ version: manifest.version,
13617
+ source_sha: metadata?.source_sha ?? null,
13618
+ run_id: metadata?.run_id ?? null,
13619
+ run_url: metadata?.run_url ?? null,
13620
+ gateway_url: metadata?.gateway_url ?? distribution.gatewayUrl,
13621
+ config_dir: process.env.ACTIONWAY_CONFIG_DIR ?? null
13622
+ });
13623
+ });
13624
+ }
13625
+
13513
13626
  // src/commands/update.ts
13514
13627
  import { spawnSync as spawnSync3 } from "node:child_process";
13515
13628
  function refreshSkillWithNewBinary() {
@@ -13530,7 +13643,8 @@ function refreshSkillWithNewBinary() {
13530
13643
  }
13531
13644
  }
13532
13645
  function registerUpdateCommand(program2) {
13533
- program2.command("update").description("Check for or install the latest @actionway/cli version from npm.").option("--check", "check for a newer version without installing").option("--refresh", "ignore the 24-hour update-check cache").action((opts) => {
13646
+ const distribution = cliDistribution();
13647
+ program2.command("update").description(`Check for or install the latest ${distribution.packageName} version from npm.`).option("--check", "check for a newer version without installing").option("--refresh", "ignore the 24-hour update-check cache").action((opts) => {
13534
13648
  try {
13535
13649
  if (opts.check) {
13536
13650
  ok({ ...getUpdateStatus({ ...opts.refresh ? { refresh: true } : {} }) });
@@ -13553,7 +13667,7 @@ function registerUpdateCommand(program2) {
13553
13667
  fail({
13554
13668
  code: "E_UPDATE_FAILED",
13555
13669
  message: error instanceof Error ? error.message : String(error),
13556
- hint: "retry later or reinstall @actionway/cli@latest"
13670
+ hint: `retry later or reinstall ${distribution.packageName}@${distribution.distTag}`
13557
13671
  });
13558
13672
  }
13559
13673
  });
@@ -13563,6 +13677,7 @@ function registerUpdateCommand(program2) {
13563
13677
  function withVersionMetadata(payload, deps = {}) {
13564
13678
  const env = deps.env ?? process.env;
13565
13679
  const cliVersion2 = deps.currentVersion ?? getCliVersion();
13680
+ const distribution = cliDistribution(env);
13566
13681
  const skillSource = skillSourcePath(env);
13567
13682
  const isUpdateCommandOutput = "update_available" in payload;
13568
13683
  const notes = [];
@@ -13570,12 +13685,12 @@ function withVersionMetadata(payload, deps = {}) {
13570
13685
  const state = readVersionState(env);
13571
13686
  if (!isUpdateCommandOutput && state.latest_version && isValidVersion(state.latest_version) && compareVersions(state.latest_version, cliVersion2) > 0) {
13572
13687
  notes.push(
13573
- `Update available: ${cliVersion2} -> ${state.latest_version}. Run \`actionway update\` at the next session boundary, then refresh the installed skill from ${skillSource}.`
13688
+ `Update available: ${cliVersion2} -> ${state.latest_version}. Run \`${distribution.commandName} update\` at the next session boundary, then refresh the installed skill from ${skillSource}.`
13574
13689
  );
13575
13690
  }
13576
13691
  if (state.min_version && isValidVersion(state.min_version) && compareVersions(cliVersion2, state.min_version) < 0) {
13577
13692
  notes.push(
13578
- `This CLI version (${cliVersion2}) is below the minimum supported version (${state.min_version}). Run \`actionway update\` now, then refresh the installed skill from ${skillSource}.`
13693
+ `This CLI version (${cliVersion2}) is below the minimum supported version (${state.min_version}). Run \`${distribution.commandName} update\` now, then refresh the installed skill from ${skillSource}.`
13579
13694
  );
13580
13695
  }
13581
13696
  } catch {
@@ -13794,14 +13909,16 @@ function getTransport2() {
13794
13909
  }
13795
13910
  var actionDeps = { getTransport: getTransport2, projectToolCall: projectTypedToolCall };
13796
13911
  function buildProgram() {
13912
+ const distribution = cliDistribution();
13797
13913
  const version = readOwnVersion();
13798
13914
  setCliVersion(version);
13799
- const program2 = new Command("actionway");
13915
+ const program2 = new Command(distribution.commandName);
13800
13916
  program2.version(version);
13801
13917
  program2.description("Actionway CLI: run Actionway media and research capabilities from your local agent.");
13802
13918
  registerInitCommand(program2);
13803
13919
  registerDoctorCommand(program2);
13804
13920
  registerUpdateCommand(program2);
13921
+ registerDevelopmentStatusCommand(program2);
13805
13922
  registerAuthCommands(program2);
13806
13923
  registerAccountCommands(program2, getTransport2);
13807
13924
  registerToolsCommands(program2, getTransport2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actionway/cli",
3
- "version": "0.18.4",
3
+ "version": "0.18.6",
4
4
  "description": "actionway CLI 的本地端壳:Clerk OAuth 鉴权 + cli-core 共享命令面 + init / update / skill 物化 / 版本三链路(终端用户本地 Codex / Claude Code 使用)",
5
5
  "type": "module",
6
6
  "bin": {