@actionway/cli 0.18.1 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/index.js +79 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,8 +27,9 @@ actionway init
|
|
|
27
27
|
|
|
28
28
|
- **auth 唯一途径:Clerk OAuth(PKCE)**。issuer / client_id 运行时从
|
|
29
29
|
`{public origin}/api/auth/cli-config` 发现;浏览器授权走随机 `127.0.0.1` 回调端口;
|
|
30
|
-
凭据存 `~/.actionway/credentials.json`(0600 owner-only
|
|
31
|
-
|
|
30
|
+
凭据存 `~/.actionway/credentials.json`(POSIX 上为 0600 owner-only 文件;
|
|
31
|
+
Windows 上 chmod 无效,保密性依赖 `%USERPROFILE%` 的继承 ACL——不要把
|
|
32
|
+
`ACTIONWAY_CONFIG_DIR` 指到共享目录)。CLI 从不打印或存储 OAuth client secret。
|
|
32
33
|
- **命令面 = 明码写出的注册表**(无 profile flag、无运行期分叉):
|
|
33
34
|
- `tools search / inspect / call` 是长尾 capability 的规范发现与执行面;
|
|
34
35
|
- 高频 typed command 继续提供参数与文件 UX,但投影到同一个 Tool Call;
|
package/dist/index.js
CHANGED
|
@@ -12209,28 +12209,63 @@ function resultDetail(result) {
|
|
|
12209
12209
|
const error = result.error instanceof Error ? result.error.message : "";
|
|
12210
12210
|
return sanitizeDetail(error || result.stderr || result.stdout || `exit ${result.status ?? "signal"}`);
|
|
12211
12211
|
}
|
|
12212
|
+
var HINTS = {
|
|
12213
|
+
E_NODE_UNSUPPORTED: "install Node.js 20 or newer, open a new terminal, and retry",
|
|
12214
|
+
E_NPM_NOT_FOUND: "install Node.js with npm and make sure npm/npm.cmd is available in PATH",
|
|
12215
|
+
E_GLOBAL_PREFIX: "use a user-writable npm global prefix; do not elevate without reviewing the target path",
|
|
12216
|
+
E_NPM_REGISTRY: "check the npm registry, @actionway scope mapping, proxy, and corporate CA configuration"
|
|
12217
|
+
};
|
|
12218
|
+
var NPM_ERROR_CODE_MAP = {
|
|
12219
|
+
EBADENGINE: "E_NODE_UNSUPPORTED",
|
|
12220
|
+
EACCES: "E_GLOBAL_PREFIX",
|
|
12221
|
+
EPERM: "E_GLOBAL_PREFIX",
|
|
12222
|
+
E401: "E_NPM_REGISTRY",
|
|
12223
|
+
E403: "E_NPM_REGISTRY",
|
|
12224
|
+
E404: "E_NPM_REGISTRY",
|
|
12225
|
+
E407: "E_NPM_REGISTRY",
|
|
12226
|
+
E408: "E_NPM_REGISTRY",
|
|
12227
|
+
E429: "E_NPM_REGISTRY",
|
|
12228
|
+
ETIMEDOUT: "E_NPM_REGISTRY",
|
|
12229
|
+
ECONNRESET: "E_NPM_REGISTRY",
|
|
12230
|
+
ECONNREFUSED: "E_NPM_REGISTRY",
|
|
12231
|
+
ENETUNREACH: "E_NPM_REGISTRY",
|
|
12232
|
+
EHOSTUNREACH: "E_NPM_REGISTRY",
|
|
12233
|
+
EAI_AGAIN: "E_NPM_REGISTRY",
|
|
12234
|
+
ENOTFOUND: "E_NPM_REGISTRY",
|
|
12235
|
+
SELF_SIGNED_CERT_IN_CHAIN: "E_NPM_REGISTRY",
|
|
12236
|
+
UNABLE_TO_GET_ISSUER_CERT_LOCALLY: "E_NPM_REGISTRY",
|
|
12237
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: "E_NPM_REGISTRY",
|
|
12238
|
+
CERT_HAS_EXPIRED: "E_NPM_REGISTRY",
|
|
12239
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: "E_NPM_REGISTRY"
|
|
12240
|
+
// npm 自身的 ENOENT(缓存/rename 竞态等本地文件问题)可重试,
|
|
12241
|
+
// 不属于"npm 未安装"——那只由 spawn 层的 error/9009 判定。
|
|
12242
|
+
};
|
|
12243
|
+
function classifyNpmFailure(result, searchable) {
|
|
12244
|
+
const spawnError = result.error;
|
|
12245
|
+
if (spawnError?.code === "ENOENT") return "E_NPM_NOT_FOUND";
|
|
12246
|
+
if (result.status === 9009) return "E_NPM_NOT_FOUND";
|
|
12247
|
+
const codeToken = /npm (?:ERR!|error) code ([A-Z0-9_]+)/i.exec(searchable)?.[1]?.toUpperCase();
|
|
12248
|
+
if (codeToken) {
|
|
12249
|
+
const mapped = NPM_ERROR_CODE_MAP[codeToken];
|
|
12250
|
+
if (mapped) return mapped;
|
|
12251
|
+
if (/^(?:CERT_|ERR_TLS_|UNABLE_TO_)/.test(codeToken)) return "E_NPM_REGISTRY";
|
|
12252
|
+
return "E_UPDATE_FAILED";
|
|
12253
|
+
}
|
|
12254
|
+
if (/unsupported engine|not compatible with your version of node/i.test(searchable)) return "E_NODE_UNSUPPORTED";
|
|
12255
|
+
if (/not recognized as an internal or external command|npm(?:\.cmd)?: (?:command )?not found/i.test(searchable))
|
|
12256
|
+
return "E_NPM_NOT_FOUND";
|
|
12257
|
+
if (/permission denied|operation not permitted/i.test(searchable)) return "E_GLOBAL_PREFIX";
|
|
12258
|
+
if (/self[- ]signed certificate|unable to (?:get|verify) .*certificate|getaddrinfo|socket hang up|network timeout/i.test(searchable))
|
|
12259
|
+
return "E_NPM_REGISTRY";
|
|
12260
|
+
return "E_UPDATE_FAILED";
|
|
12261
|
+
}
|
|
12212
12262
|
function npmFailure(result, operation) {
|
|
12213
12263
|
const detail = resultDetail(result);
|
|
12214
12264
|
const searchable = `${detail}
|
|
12215
12265
|
${result.stderr}
|
|
12216
12266
|
${result.stdout}`;
|
|
12217
|
-
|
|
12218
|
-
|
|
12219
|
-
if (/EBADENGINE|unsupported engine|not compatible with your version of node/i.test(searchable)) {
|
|
12220
|
-
code = "E_NODE_UNSUPPORTED";
|
|
12221
|
-
hint = "install Node.js 20 or newer, open a new terminal, and retry";
|
|
12222
|
-
} else if (/ENOENT|not recognized as an internal or external command|npm(?:\.cmd)?: not found/i.test(searchable)) {
|
|
12223
|
-
code = "E_NPM_NOT_FOUND";
|
|
12224
|
-
hint = "install Node.js with npm and make sure npm/npm.cmd is available in PATH";
|
|
12225
|
-
} else if (/EACCES|EPERM|permission denied|operation not permitted/i.test(searchable)) {
|
|
12226
|
-
code = "E_GLOBAL_PREFIX";
|
|
12227
|
-
hint = "use a user-writable npm global prefix; do not elevate without reviewing the target path";
|
|
12228
|
-
} else if (/E401|E403|E404|E407|SELF_SIGNED_CERT|CERT_|UNABLE_TO_GET_ISSUER|UNABLE_TO_VERIFY_LEAF_SIGNATURE|ETIMEDOUT|ECONNRESET|ECONNREFUSED|ENETUNREACH|EHOSTUNREACH|EAI_AGAIN|ENOTFOUND|registry/i.test(
|
|
12229
|
-
searchable
|
|
12230
|
-
)) {
|
|
12231
|
-
code = "E_NPM_REGISTRY";
|
|
12232
|
-
hint = "check the npm registry, @actionway scope mapping, proxy, and corporate CA configuration";
|
|
12233
|
-
}
|
|
12267
|
+
const code = classifyNpmFailure(result, searchable);
|
|
12268
|
+
const hint = HINTS[code] ?? "retry from a normal host terminal or reinstall @actionway/cli@latest (use npm.cmd on Windows)";
|
|
12234
12269
|
return new CliError(code, `${operation} failed: ${detail || "npm exited unsuccessfully"}`, hint);
|
|
12235
12270
|
}
|
|
12236
12271
|
function runNpmText(args, operation, options = {}) {
|
|
@@ -12965,9 +13000,10 @@ function registerAccountCommands(program2, getTransport3) {
|
|
|
12965
13000
|
|
|
12966
13001
|
// src/commands/doctor.ts
|
|
12967
13002
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
12968
|
-
import {
|
|
13003
|
+
import { existsSync as existsSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
12969
13004
|
import { createServer as createServer2 } from "node:http";
|
|
12970
|
-
import {
|
|
13005
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
13006
|
+
import { dirname as dirname3, join as join3, posix, win32 } from "node:path";
|
|
12971
13007
|
var PACKAGE_NAME3 = "@actionway/cli";
|
|
12972
13008
|
function pass(id, message) {
|
|
12973
13009
|
return { id, status: "pass", message };
|
|
@@ -13010,7 +13046,9 @@ function defaultWritable(path) {
|
|
|
13010
13046
|
while (!existsSync2(candidate) && dirname3(candidate) !== candidate) candidate = dirname3(candidate);
|
|
13011
13047
|
try {
|
|
13012
13048
|
if (!statSync(candidate).isDirectory()) return false;
|
|
13013
|
-
|
|
13049
|
+
const probe = join3(candidate, `.actionway-doctor-${randomBytes2(6).toString("hex")}`);
|
|
13050
|
+
writeFileSync3(probe, "");
|
|
13051
|
+
rmSync2(probe, { force: true });
|
|
13014
13052
|
return true;
|
|
13015
13053
|
} catch {
|
|
13016
13054
|
return false;
|
|
@@ -13023,6 +13061,11 @@ function checkLoopback() {
|
|
|
13023
13061
|
server.listen(0, "127.0.0.1", () => server.close((error) => error ? reject(error) : resolve3()));
|
|
13024
13062
|
});
|
|
13025
13063
|
}
|
|
13064
|
+
function defaultResolveCommand(name) {
|
|
13065
|
+
const result = process.platform === "win32" ? spawnSync2("where.exe", [name], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5e3, windowsHide: true }) : spawnSync2("/bin/sh", ["-c", `command -v -- ${name}`], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5e3 });
|
|
13066
|
+
if (result.error || result.status !== 0) return null;
|
|
13067
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? null;
|
|
13068
|
+
}
|
|
13026
13069
|
function powerShellPolicy() {
|
|
13027
13070
|
const result = spawnSync2(
|
|
13028
13071
|
"powershell.exe",
|
|
@@ -13048,6 +13091,7 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
|
|
|
13048
13091
|
const fetchOAuth = dependencies.fetchOAuth ?? fetchCliOAuthConfig;
|
|
13049
13092
|
const loopback = dependencies.checkLoopback ?? checkLoopback;
|
|
13050
13093
|
const readPolicy = dependencies.readPowerShellPolicy ?? powerShellPolicy;
|
|
13094
|
+
const resolveCommand = dependencies.resolveCommand ?? defaultResolveCommand;
|
|
13051
13095
|
const checks = [];
|
|
13052
13096
|
const configDir = actionwayConfigDir(env);
|
|
13053
13097
|
const nodeMajor = Number.parseInt(nodeVersion.split(".")[0] ?? "", 10);
|
|
@@ -13068,11 +13112,11 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
|
|
|
13068
13112
|
checks.push(
|
|
13069
13113
|
pathWritable(configDir) ? pass("config_dir", `Configuration directory is writable: ${configDir}`) : fail2("config_dir", `Configuration directory is not writable: ${configDir}`, "set ACTIONWAY_CONFIG_DIR to a user-writable directory")
|
|
13070
13114
|
);
|
|
13071
|
-
const registryResult = npm(["config", "get", "registry"], { env, platform, timeoutMs:
|
|
13115
|
+
const registryResult = npm(["config", "get", "registry"], { env, platform, timeoutMs: 15e3 });
|
|
13072
13116
|
checks.push(
|
|
13073
13117
|
npmCheck("npm", registryResult, "npm discovery", (registry) => pass("npm", `npm is available; registry is ${safeRegistry(registry)}.`))
|
|
13074
13118
|
);
|
|
13075
|
-
const scopeResult = npm(["config", "get", "@actionway:registry"], { env, platform, timeoutMs:
|
|
13119
|
+
const scopeResult = npm(["config", "get", "@actionway:registry"], { env, platform, timeoutMs: 15e3 });
|
|
13076
13120
|
const scopeRegistry = commandOutput(scopeResult);
|
|
13077
13121
|
if (scopeRegistry && !["null", "undefined"].includes(scopeRegistry.toLowerCase())) {
|
|
13078
13122
|
const safe = safeRegistry(scopeRegistry);
|
|
@@ -13088,7 +13132,7 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
|
|
|
13088
13132
|
} else {
|
|
13089
13133
|
checks.push(pass("scope_registry", "No custom @actionway npm registry mapping is configured."));
|
|
13090
13134
|
}
|
|
13091
|
-
const prefixResult = npm(["prefix", "--global"], { env, platform, timeoutMs:
|
|
13135
|
+
const prefixResult = npm(["prefix", "--global"], { env, platform, timeoutMs: 15e3 });
|
|
13092
13136
|
const prefix = commandOutput(prefixResult);
|
|
13093
13137
|
if (!prefix) {
|
|
13094
13138
|
const error = npmFailure(prefixResult, "npm global prefix lookup");
|
|
@@ -13097,11 +13141,13 @@ async function diagnoseActionway(options = {}, dependencies = {}) {
|
|
|
13097
13141
|
const api = pathApi(platform);
|
|
13098
13142
|
const binDir = platform === "win32" ? prefix : api.join(prefix, "bin");
|
|
13099
13143
|
const binName = platform === "win32" ? "actionway.cmd" : "actionway";
|
|
13144
|
+
let resolvedCache;
|
|
13145
|
+
const resolved = () => resolvedCache !== void 0 ? resolvedCache : resolvedCache = resolveCommand("actionway");
|
|
13100
13146
|
checks.push(
|
|
13101
|
-
pathContains(binDir, env, platform) ? pass("global_path", `npm global executable directory is in PATH: ${binDir}`) : fail2("global_path", `npm global executable directory is not in PATH: ${binDir}`, "add it to the user PATH and open a new terminal")
|
|
13147
|
+
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")
|
|
13102
13148
|
);
|
|
13103
13149
|
checks.push(
|
|
13104
|
-
pathExists(api.join(binDir, binName)) ? pass("command", `${binName} is installed in the npm global prefix.`) : fail2("command", `${binName} was not found in the npm global prefix.`, "reinstall @actionway/cli@latest in the same Node/npm environment")
|
|
13150
|
+
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")
|
|
13105
13151
|
);
|
|
13106
13152
|
}
|
|
13107
13153
|
if (platform === "win32") {
|
|
@@ -13176,8 +13222,8 @@ function registerDoctorCommand(program2) {
|
|
|
13176
13222
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
13177
13223
|
|
|
13178
13224
|
// src/lib/skill.ts
|
|
13179
|
-
import { cpSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, rmSync as
|
|
13180
|
-
import { dirname as dirname4, join as
|
|
13225
|
+
import { cpSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
13226
|
+
import { dirname as dirname4, join as join4, resolve as resolve2 } from "node:path";
|
|
13181
13227
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
13182
13228
|
var InitError = class extends Error {
|
|
13183
13229
|
hint;
|
|
@@ -13191,12 +13237,12 @@ var ACTIONWAY_SKILL_INSTRUCTION = "Install this skill from `source` into your ag
|
|
|
13191
13237
|
function resolveBundledSkill(moduleUrl = import.meta.url) {
|
|
13192
13238
|
const here = dirname4(fileURLToPath3(moduleUrl));
|
|
13193
13239
|
for (const candidate of [resolve2(here, "../assets/skill/actionway"), resolve2(here, "../../assets/skill/actionway")]) {
|
|
13194
|
-
if (existsSync3(
|
|
13240
|
+
if (existsSync3(join4(candidate, "SKILL.md"))) return realpathSync2(candidate);
|
|
13195
13241
|
}
|
|
13196
13242
|
throw new InitError("the bundled Actionway Skill is missing", "reinstall @actionway/cli, then run `actionway init` again");
|
|
13197
13243
|
}
|
|
13198
13244
|
function skillSourcePath(env = process.env) {
|
|
13199
|
-
return
|
|
13245
|
+
return join4(actionwayConfigDir(env), "skill", "actionway");
|
|
13200
13246
|
}
|
|
13201
13247
|
function stampSkillVersion(skillMarkdown, version) {
|
|
13202
13248
|
const newline = skillMarkdown.includes("\r\n") ? "\r\n" : "\n";
|
|
@@ -13236,16 +13282,16 @@ function stampSkillVersion(skillMarkdown, version) {
|
|
|
13236
13282
|
function materializeSkill(options = {}) {
|
|
13237
13283
|
const env = options.env ?? process.env;
|
|
13238
13284
|
const source = options.source ? realpathSync2(options.source) : resolveBundledSkill();
|
|
13239
|
-
if (!existsSync3(
|
|
13285
|
+
if (!existsSync3(join4(source, "SKILL.md"))) {
|
|
13240
13286
|
throw new InitError("the Actionway Skill source is invalid", "reinstall @actionway/cli and retry init");
|
|
13241
13287
|
}
|
|
13242
13288
|
const version = options.version ?? readOwnVersion();
|
|
13243
13289
|
const target = skillSourcePath(env);
|
|
13244
13290
|
mkdirSync3(dirname4(target), { recursive: true, mode: 448 });
|
|
13245
|
-
|
|
13291
|
+
rmSync3(target, { recursive: true, force: true });
|
|
13246
13292
|
cpSync(source, target, { recursive: true });
|
|
13247
|
-
const skillPath =
|
|
13248
|
-
|
|
13293
|
+
const skillPath = join4(target, "SKILL.md");
|
|
13294
|
+
writeFileSync4(skillPath, stampSkillVersion(readFileSync4(skillPath, "utf8"), version));
|
|
13249
13295
|
return { source: target, version, instruction: ACTIONWAY_SKILL_INSTRUCTION };
|
|
13250
13296
|
}
|
|
13251
13297
|
|
package/package.json
CHANGED