@actionway/cli 0.18.2 → 0.18.4
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/assets/skill/actionway/SKILL.md +1 -0
- package/dist/index.js +73 -14
- package/package.json +1 -1
|
@@ -36,6 +36,7 @@ Use the installed `actionway` CLI and its server-backed Capability Registry as t
|
|
|
36
36
|
- `accepted`: immediately wait on the returned `job.jobRef`.
|
|
37
37
|
- `completed`: consume the returned result.
|
|
38
38
|
- `confirmation: strong`: never treat chat text alone as approval; follow a server-verifiable approval instruction or report that execution cannot continue.
|
|
39
|
+
6. On `E_INSUFFICIENT_BALANCE`, stop the call. When the failure includes `top_up_url`, the user-facing response must render that exact field value as a Markdown link by substituting it into `[Top up Actionway balance](<top_up_url>)`; never leave it only in terminal JSON, replace it with a terminal command, or synthesize a URL for another environment. Ask the user to retry the original task after the top-up is complete.
|
|
39
40
|
|
|
40
41
|
## Handle asynchronous jobs
|
|
41
42
|
|
package/dist/index.js
CHANGED
|
@@ -12434,7 +12434,7 @@ var REFRESH_SKEW_S = 30;
|
|
|
12434
12434
|
var TOKEN_TIMEOUT_MS = 2e4;
|
|
12435
12435
|
var DISCOVERY_TIMEOUT_MS = 1e4;
|
|
12436
12436
|
var SESSION_TIMEOUT_MS = 2e4;
|
|
12437
|
-
var DEFAULT_LOGIN_TIMEOUT_MS =
|
|
12437
|
+
var DEFAULT_LOGIN_TIMEOUT_MS = 12e5;
|
|
12438
12438
|
function stripSlash(url) {
|
|
12439
12439
|
return url.replace(/\/+$/, "");
|
|
12440
12440
|
}
|
|
@@ -12521,7 +12521,15 @@ async function fetchCliOAuthConfig(gateway, fetchImpl = fetch) {
|
|
|
12521
12521
|
signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
|
|
12522
12522
|
});
|
|
12523
12523
|
if (!response.ok) {
|
|
12524
|
-
|
|
12524
|
+
let detail = "";
|
|
12525
|
+
try {
|
|
12526
|
+
const body2 = await response.json();
|
|
12527
|
+
const code = typeof body2?.error?.code === "string" ? body2.error.code : "";
|
|
12528
|
+
const message = typeof body2?.error?.message === "string" ? body2.error.message : "";
|
|
12529
|
+
detail = [code, message].filter(Boolean).join(": ");
|
|
12530
|
+
} catch {
|
|
12531
|
+
}
|
|
12532
|
+
throw new Error(`Actionway login configuration is unavailable (${response.status}${detail ? `; ${detail}` : ""})`);
|
|
12525
12533
|
}
|
|
12526
12534
|
const body = await response.json();
|
|
12527
12535
|
if (typeof body !== "object" || body === null || !("issuer" in body) || typeof body.issuer !== "string" || !("client_id" in body) || typeof body.client_id !== "string" || !body.issuer.trim() || !body.client_id.trim()) {
|
|
@@ -12694,14 +12702,21 @@ async function waitForCode(state, timeoutMs) {
|
|
|
12694
12702
|
const port = server.address().port;
|
|
12695
12703
|
const timer = setTimeout(() => {
|
|
12696
12704
|
rejectCode(
|
|
12697
|
-
new CliError(
|
|
12705
|
+
new CliError(
|
|
12706
|
+
"E_BACKEND",
|
|
12707
|
+
`login timed out after ${Math.round(timeoutMs / 1e3)} seconds`,
|
|
12708
|
+
"run `actionway login` again and ask the user to finish the browser sign-in promptly; pass --timeout-seconds <n> to allow more time"
|
|
12709
|
+
)
|
|
12698
12710
|
);
|
|
12699
12711
|
server.close();
|
|
12700
12712
|
}, timeoutMs);
|
|
12701
12713
|
return {
|
|
12702
12714
|
redirectUri: `http://127.0.0.1:${port}/callback`,
|
|
12703
12715
|
code: code.finally(() => clearTimeout(timer)),
|
|
12704
|
-
close: () =>
|
|
12716
|
+
close: () => {
|
|
12717
|
+
clearTimeout(timer);
|
|
12718
|
+
return new Promise((resolve3) => server.listening ? server.close(() => resolve3()) : resolve3());
|
|
12719
|
+
}
|
|
12705
12720
|
};
|
|
12706
12721
|
}
|
|
12707
12722
|
async function readJsonObject(response) {
|
|
@@ -12720,8 +12735,14 @@ async function tokenRequest(issuer, body, fetchImpl) {
|
|
|
12720
12735
|
});
|
|
12721
12736
|
const value = await readJsonObject(response);
|
|
12722
12737
|
if (!response.ok || typeof value.access_token !== "string") {
|
|
12723
|
-
const
|
|
12724
|
-
|
|
12738
|
+
const code = typeof value.error === "string" ? value.error : "";
|
|
12739
|
+
const description = typeof value.error_description === "string" ? value.error_description : "";
|
|
12740
|
+
const message = [code, description].filter(Boolean).join(": ") || "token request failed";
|
|
12741
|
+
throw new CliError(
|
|
12742
|
+
"E_BACKEND",
|
|
12743
|
+
message,
|
|
12744
|
+
code === "invalid_client" ? "the client_id/issuer pair does not belong to one Actionway environment: clear ACTIONWAY_OAUTH_CLIENT_ID and ACTIONWAY_OAUTH_ISSUER (or drop --client-id/--issuer) and run `actionway login` again to rediscover both; if no overrides are set, the stored credentials are stale \u2014 run `actionway logout` then `actionway login`" : void 0
|
|
12745
|
+
);
|
|
12725
12746
|
}
|
|
12726
12747
|
return {
|
|
12727
12748
|
accessToken: value.access_token,
|
|
@@ -12796,19 +12817,44 @@ async function login(options = {}) {
|
|
|
12796
12817
|
const onNotice = options.onNotice ?? ((message) => process.stderr.write(`${message}
|
|
12797
12818
|
`));
|
|
12798
12819
|
const gateway = resolveGatewayUrl(options.gateway, env);
|
|
12820
|
+
const gatewaySource = options.gateway?.trim() ? "flag" : (env.ACTIONWAY_GATEWAY_URL ?? "").trim() ? "env" : "default";
|
|
12799
12821
|
let clientId = options.clientId?.trim() || (env.ACTIONWAY_OAUTH_CLIENT_ID ?? "").trim();
|
|
12800
12822
|
let issuer = options.issuer?.trim() || (env.ACTIONWAY_OAUTH_ISSUER ?? "").trim();
|
|
12823
|
+
const clientIdSource = options.clientId?.trim() ? "flag" : (env.ACTIONWAY_OAUTH_CLIENT_ID ?? "").trim() ? "env" : "discovery";
|
|
12824
|
+
const issuerSource = options.issuer?.trim() ? "flag" : (env.ACTIONWAY_OAUTH_ISSUER ?? "").trim() ? "env" : "discovery";
|
|
12825
|
+
const authContext = () => ({
|
|
12826
|
+
gateway_url: gateway,
|
|
12827
|
+
gateway_source: gatewaySource,
|
|
12828
|
+
...issuer ? { issuer } : {},
|
|
12829
|
+
issuer_source: issuerSource,
|
|
12830
|
+
...clientId ? { client_id: clientId } : {},
|
|
12831
|
+
client_id_source: clientIdSource
|
|
12832
|
+
});
|
|
12833
|
+
const withAuthContext = (error) => {
|
|
12834
|
+
if (error instanceof CliError) {
|
|
12835
|
+
throw new CliError(error.code, error.message, error.hint, { ...error.extra ?? {}, auth_context: authContext() });
|
|
12836
|
+
}
|
|
12837
|
+
throw error;
|
|
12838
|
+
};
|
|
12839
|
+
if (!!clientId !== !!issuer) {
|
|
12840
|
+
throw new CliError(
|
|
12841
|
+
"E_SCHEMA",
|
|
12842
|
+
"client_id and issuer overrides must be provided together",
|
|
12843
|
+
"pass both --client-id and --issuer (or set both ACTIONWAY_OAUTH_CLIENT_ID and ACTIONWAY_OAUTH_ISSUER), or neither to use discovery",
|
|
12844
|
+
{ auth_context: authContext() }
|
|
12845
|
+
);
|
|
12846
|
+
}
|
|
12801
12847
|
if (!clientId || !issuer) {
|
|
12802
12848
|
let discovered;
|
|
12803
12849
|
try {
|
|
12804
12850
|
discovered = await fetchCliOAuthConfig(gateway, fetchImpl);
|
|
12805
12851
|
} catch (error) {
|
|
12806
12852
|
const message = error instanceof Error ? error.message : String(error);
|
|
12807
|
-
|
|
12853
|
+
return withAuthContext(new CliError(
|
|
12808
12854
|
"E_BACKEND",
|
|
12809
12855
|
`login failed: ${message}`,
|
|
12810
|
-
"
|
|
12811
|
-
);
|
|
12856
|
+
"check auth_context.gateway_url is the intended Actionway environment (unset ACTIONWAY_GATEWAY_URL or fix --gateway if not) and run `actionway login` again; if the gateway is correct, the environment is misconfigured \u2014 report this error to the user instead of retrying (passing both --client-id and --issuer explicitly remains available as a temporary override)"
|
|
12857
|
+
));
|
|
12812
12858
|
}
|
|
12813
12859
|
clientId ||= discovered.clientId;
|
|
12814
12860
|
issuer ||= discovered.issuer;
|
|
@@ -12865,6 +12911,8 @@ ${authorizeUrl}`);
|
|
|
12865
12911
|
};
|
|
12866
12912
|
saveCredentials(credentials, env);
|
|
12867
12913
|
return { credentials, account };
|
|
12914
|
+
} catch (error) {
|
|
12915
|
+
return withAuthContext(error);
|
|
12868
12916
|
} finally {
|
|
12869
12917
|
await callback.close();
|
|
12870
12918
|
}
|
|
@@ -12917,7 +12965,7 @@ function failFromError2(err, fallbackHint) {
|
|
|
12917
12965
|
});
|
|
12918
12966
|
}
|
|
12919
12967
|
function registerAuthCommands(program2) {
|
|
12920
|
-
program2.command("login").description("Authenticate with Actionway in the browser (Clerk OAuth with PKCE).").option("--gateway <url>", "Actionway public origin").option("--client-id <id>", "Actionway OAuth client id (default: discovered from the public origin)").option("--issuer <url>", "Actionway OAuth issuer (default: discovered from the public origin)").option("--timeout-seconds <n>", "how long to wait for browser authentication").option("--no-browser", "print the authorize URL instead of opening a browser").action(async (opts) => {
|
|
12968
|
+
program2.command("login").description("Authenticate with Actionway in the browser (Clerk OAuth with PKCE).").option("--gateway <url>", "Actionway public origin").option("--client-id <id>", "Actionway OAuth client id (must be paired with --issuer; default: both discovered from the public origin)").option("--issuer <url>", "Actionway OAuth issuer (must be paired with --client-id; default: both discovered from the public origin)").option("--timeout-seconds <n>", "how long to wait for browser authentication").option("--no-browser", "print the authorize URL instead of opening a browser").action(async (opts) => {
|
|
12921
12969
|
try {
|
|
12922
12970
|
const { credentials, account } = await login({
|
|
12923
12971
|
...opts.gateway ? { gateway: opts.gateway } : {},
|
|
@@ -13335,7 +13383,10 @@ async function captureInstallStarted(input) {
|
|
|
13335
13383
|
{
|
|
13336
13384
|
method: "POST",
|
|
13337
13385
|
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
13338
|
-
body: JSON.stringify({
|
|
13386
|
+
body: JSON.stringify({
|
|
13387
|
+
installSessionId: input.installSessionId,
|
|
13388
|
+
installSource: input.installSource
|
|
13389
|
+
})
|
|
13339
13390
|
},
|
|
13340
13391
|
ANALYTICS_TIMEOUT_MS,
|
|
13341
13392
|
input.fetchImpl ?? fetch
|
|
@@ -13348,6 +13399,7 @@ async function captureInstallStarted(input) {
|
|
|
13348
13399
|
async function requestCliInstallSession(input) {
|
|
13349
13400
|
const url = new URL(`${stripSlash2(input.gateway)}/api/auth/cli-session`);
|
|
13350
13401
|
url.searchParams.set("install_session_id", input.installSessionId);
|
|
13402
|
+
url.searchParams.set("install_source", input.installSource);
|
|
13351
13403
|
let response;
|
|
13352
13404
|
try {
|
|
13353
13405
|
response = await fetchWithTimeout(
|
|
@@ -13393,6 +13445,7 @@ async function connectInstallSession(input) {
|
|
|
13393
13445
|
gateway,
|
|
13394
13446
|
accessToken: credentials.access_token,
|
|
13395
13447
|
installSessionId: input.installSessionId,
|
|
13448
|
+
installSource: input.installSource,
|
|
13396
13449
|
fetchImpl
|
|
13397
13450
|
});
|
|
13398
13451
|
}
|
|
@@ -13410,15 +13463,20 @@ function failFromError3(err) {
|
|
|
13410
13463
|
function registerInitCommand(program2) {
|
|
13411
13464
|
program2.command("init").description(
|
|
13412
13465
|
"Complete browser authentication and stage the Actionway Skill for your agent to install. The CLI never writes into an agent's own configuration directory."
|
|
13413
|
-
).option("--gateway <url>", "Actionway public origin").option("--client-id <id>", "Actionway OAuth client id").option("--issuer <url>", "Actionway OAuth issuer").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) => {
|
|
13466
|
+
).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) => {
|
|
13414
13467
|
try {
|
|
13415
13468
|
const skill = materializeSkill();
|
|
13416
13469
|
if (opts.skillOnly) {
|
|
13417
13470
|
ok({ skill });
|
|
13418
13471
|
}
|
|
13472
|
+
const installSource = opts.installSessionId ? "website_prompt" : "manual_init";
|
|
13419
13473
|
const installSessionId = resolveInstallSessionId(opts.installSessionId);
|
|
13420
13474
|
const initGateway = initGatewayUrl(opts.gateway, process.env);
|
|
13421
|
-
await captureInstallStarted({
|
|
13475
|
+
await captureInstallStarted({
|
|
13476
|
+
gateway: initGateway,
|
|
13477
|
+
installSessionId,
|
|
13478
|
+
installSource
|
|
13479
|
+
});
|
|
13422
13480
|
let loginStarted = false;
|
|
13423
13481
|
if (!whoamiSnapshot()) {
|
|
13424
13482
|
loginStarted = true;
|
|
@@ -13433,7 +13491,8 @@ function registerInitCommand(program2) {
|
|
|
13433
13491
|
}
|
|
13434
13492
|
const account = await connectInstallSession({
|
|
13435
13493
|
...opts.gateway ? { explicitGateway: opts.gateway } : {},
|
|
13436
|
-
installSessionId
|
|
13494
|
+
installSessionId,
|
|
13495
|
+
installSource
|
|
13437
13496
|
});
|
|
13438
13497
|
const snapshot = whoamiSnapshot();
|
|
13439
13498
|
ok({
|
package/package.json
CHANGED