run402 3.5.2 → 3.5.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/cli.mjs +20 -0
- package/lib/archives.mjs +55 -0
- package/lib/cache.mjs +10 -9
- package/lib/cloud.mjs +254 -0
- package/lib/config.mjs +6 -0
- package/lib/core.mjs +122 -0
- package/lib/deploy-v2.mjs +65 -60
- package/lib/doctor-source-scan.mjs +1 -1
- package/lib/functions.mjs +2 -1
- package/lib/init.mjs +8 -6
- package/lib/next-actions.mjs +61 -0
- package/lib/projects.mjs +16 -3
- package/lib/secrets.mjs +3 -2
- package/lib/subdomains.mjs +2 -1
- package/lib/tier.mjs +16 -5
- package/package.json +1 -1
- package/sdk/dist/credentials.d.ts +1 -1
- package/sdk/dist/credentials.d.ts.map +1 -1
- package/sdk/dist/errors.d.ts +25 -2
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +31 -2
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/index.d.ts +5 -1
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +4 -0
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/namespaces/archives.d.ts +12 -0
- package/sdk/dist/namespaces/archives.d.ts.map +1 -0
- package/sdk/dist/namespaces/archives.js +202 -0
- package/sdk/dist/namespaces/archives.js.map +1 -0
- package/sdk/dist/namespaces/archives.types.d.ts +163 -0
- package/sdk/dist/namespaces/archives.types.d.ts.map +1 -0
- package/sdk/dist/namespaces/archives.types.js +2 -0
- package/sdk/dist/namespaces/archives.types.js.map +1 -0
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +3 -0
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts +9 -0
- package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/tier.d.ts +12 -1
- package/sdk/dist/namespaces/tier.d.ts.map +1 -1
- package/sdk/dist/namespaces/tier.js +4 -1
- package/sdk/dist/namespaces/tier.js.map +1 -1
- package/sdk/dist/node/archives-node.d.ts +14 -0
- package/sdk/dist/node/archives-node.d.ts.map +1 -0
- package/sdk/dist/node/archives-node.js +715 -0
- package/sdk/dist/node/archives-node.js.map +1 -0
- package/sdk/dist/node/index.d.ts +4 -1
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +3 -0
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/scoped.d.ts +12 -0
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +25 -0
- package/sdk/dist/scoped.js.map +1 -1
package/lib/deploy-v2.mjs
CHANGED
|
@@ -36,6 +36,7 @@ import { API, allowanceAuthHeaders, getActiveProjectId, resolveProjectId } from
|
|
|
36
36
|
import { normalizeArgv } from "./argparse.mjs";
|
|
37
37
|
import { loadLiveControlPlaneSession } from "../core-dist/control-plane-session.js";
|
|
38
38
|
import { withAutoApprove } from "./operator.mjs";
|
|
39
|
+
import { editRequestAction, nextAction, retryAction } from "./next-actions.mjs";
|
|
39
40
|
|
|
40
41
|
const APPLY_HELP = `run402 deploy apply — Unified deploy primitive (v1.34+)
|
|
41
42
|
|
|
@@ -939,66 +940,66 @@ const CI_DEPLOY_ERROR_GUIDANCE = {
|
|
|
939
940
|
invalid_token: {
|
|
940
941
|
hint: "Ensure the workflow has permissions: id-token: write and is running in the repository/branch linked with run402 ci link github.",
|
|
941
942
|
next_actions: [
|
|
942
|
-
"Check the workflow permissions block includes id-token: write.",
|
|
943
|
-
"
|
|
943
|
+
nextAction("edit_request", { why: "Check the workflow permissions block includes id-token: write." }),
|
|
944
|
+
editRequestAction("run402 ci link github", "Re-link if the repository, branch, or environment changed."),
|
|
944
945
|
],
|
|
945
946
|
},
|
|
946
947
|
access_denied: {
|
|
947
948
|
hint: "The OIDC token was valid, but no active Run402 CI binding allowed this workflow.",
|
|
948
949
|
next_actions: [
|
|
949
|
-
"
|
|
950
|
-
"
|
|
950
|
+
editRequestAction("run402 ci list --project <prj_...>", "Inspect active CI bindings for this project."),
|
|
951
|
+
editRequestAction("run402 ci link github", "Link this repository, branch, and environment to Run402."),
|
|
951
952
|
],
|
|
952
953
|
},
|
|
953
954
|
binding_revoked: {
|
|
954
955
|
hint: "A matching CI binding existed but was revoked — most often because the project was transferred or handed to a new owner, which suspends the prior org's CI bindings.",
|
|
955
956
|
next_actions: [
|
|
956
|
-
"
|
|
957
|
-
"Do not run run402 ci set-asset-scopes
|
|
958
|
-
"
|
|
957
|
+
editRequestAction("run402 ci link github", "Re-create the CI binding from this repository."),
|
|
958
|
+
nextAction("edit_request", { why: "Do not run run402 ci set-asset-scopes; it returns 409 on a revoked binding." }),
|
|
959
|
+
editRequestAction("run402 ci list --project <prj_...>", "Confirm the binding state locally."),
|
|
959
960
|
],
|
|
960
961
|
},
|
|
961
962
|
event_not_allowed: {
|
|
962
963
|
hint: "This binding only allows push and workflow_dispatch events in v1.",
|
|
963
964
|
next_actions: [
|
|
964
|
-
"Trigger the workflow with push or workflow_dispatch.",
|
|
965
|
-
"Create a separate follow-up design before enabling PR deploy events.",
|
|
965
|
+
nextAction("retry", { why: "Trigger the workflow with push or workflow_dispatch." }),
|
|
966
|
+
nextAction("edit_request", { why: "Create a separate follow-up design before enabling PR deploy events." }),
|
|
966
967
|
],
|
|
967
968
|
},
|
|
968
969
|
repository_id_mismatch: {
|
|
969
970
|
hint: "The GitHub repository id in the OIDC token does not match the linked binding.",
|
|
970
971
|
next_actions: [
|
|
971
|
-
"
|
|
972
|
-
"
|
|
972
|
+
editRequestAction("run402 ci link github", "Re-link from the current repository."),
|
|
973
|
+
editRequestAction("run402 ci link github --repository-id <id>", "Pass the numeric GitHub repository id if automatic lookup fails."),
|
|
973
974
|
],
|
|
974
975
|
},
|
|
975
976
|
forbidden_spec_field: {
|
|
976
977
|
hint: "CI deploys can deploy site/functions/database content and route declarations only when the binding includes covering route scopes.",
|
|
977
978
|
next_actions: [
|
|
978
|
-
"Remove forbidden fields such as secrets, subdomains, or checks from the CI manifest.",
|
|
979
|
-
"Keep the normalized manifest small enough to avoid manifest_ref.",
|
|
979
|
+
nextAction("edit_request", { why: "Remove forbidden fields such as secrets, subdomains, or checks from the CI manifest." }),
|
|
980
|
+
nextAction("edit_request", { why: "Keep the normalized manifest small enough to avoid manifest_ref." }),
|
|
980
981
|
],
|
|
981
982
|
},
|
|
982
983
|
CI_ROUTE_SCOPE_DENIED: {
|
|
983
984
|
hint: "This CI binding does not cover one or more route declarations in the deploy manifest.",
|
|
984
985
|
next_actions: [
|
|
985
|
-
"
|
|
986
|
-
"Use exact scopes such as --route-scope /admin or prefix scopes such as --route-scope /api/*.",
|
|
987
|
-
"
|
|
986
|
+
editRequestAction("run402 ci link github --route-scope <pattern>", "Add a CI route scope for every exact or prefix path the workflow may deploy."),
|
|
987
|
+
nextAction("edit_request", { why: "Use exact scopes such as --route-scope /admin or prefix scopes such as --route-scope /api/*." }),
|
|
988
|
+
editRequestAction("run402 deploy apply", "Run route changes locally when they are outside the CI delegation."),
|
|
988
989
|
],
|
|
989
990
|
},
|
|
990
991
|
forbidden_plan: {
|
|
991
992
|
hint: "The gateway rejected this deploy plan for CI. Keep CI deploys to the allowed resources and re-link if policy changed.",
|
|
992
993
|
next_actions: [
|
|
993
|
-
"Inspect the gateway error details for the rejected resource.",
|
|
994
|
-
"
|
|
994
|
+
nextAction("edit_request", { why: "Inspect the gateway error details for the rejected resource." }),
|
|
995
|
+
editRequestAction("run402 deploy apply", "Run operations outside the CI allowlist locally."),
|
|
995
996
|
],
|
|
996
997
|
},
|
|
997
998
|
payment_required: {
|
|
998
999
|
hint: "The project tier or payment state does not allow this CI deploy.",
|
|
999
1000
|
next_actions: [
|
|
1000
|
-
"
|
|
1001
|
-
"Renew or upgrade the project tier, then re-run the workflow.",
|
|
1001
|
+
editRequestAction("run402 tier status --project <prj_...>", "Inspect project tier and payment state locally."),
|
|
1002
|
+
editRequestAction("run402 tier set <tier>", "Renew or upgrade the project tier, then re-run the workflow."),
|
|
1002
1003
|
],
|
|
1003
1004
|
},
|
|
1004
1005
|
};
|
|
@@ -1032,18 +1033,22 @@ function enhanceDeployWarningError(err) {
|
|
|
1032
1033
|
: code
|
|
1033
1034
|
? [code]
|
|
1034
1035
|
: [];
|
|
1035
|
-
const
|
|
1036
|
-
? `
|
|
1037
|
-
:
|
|
1038
|
-
|
|
1039
|
-
|
|
1036
|
+
const allowWarningCommand = unacknowledgedCodes.length > 0
|
|
1037
|
+
? `run402 deploy apply ${unacknowledgedCodes.map((warningCode) => `--allow-warning ${warningCode}`).join(" ")}`
|
|
1038
|
+
: "run402 deploy apply --allow-warning <code>";
|
|
1039
|
+
const allowWarningAction = editRequestAction(
|
|
1040
|
+
allowWarningCommand,
|
|
1041
|
+
unacknowledgedCodes.length > 0
|
|
1042
|
+
? `Retry only after reviewing warning code${unacknowledgedCodes.length === 1 ? "" : "s"}: ${unacknowledgedCodes.join(", ")}.`
|
|
1043
|
+
: "Retry only after reviewing the warning code.",
|
|
1044
|
+
);
|
|
1040
1045
|
const defaultNextActions = [
|
|
1041
1046
|
...(affected.length > 0
|
|
1042
|
-
? [`Set or inspect affected secrets: ${Array.from(new Set(affected)).join(", ")}`]
|
|
1047
|
+
? [editRequestAction("run402 secrets set <project> <KEY> --stdin", `Set or inspect affected secrets: ${Array.from(new Set(affected)).join(", ")}`)]
|
|
1043
1048
|
: []),
|
|
1044
|
-
"
|
|
1049
|
+
retryAction("run402 deploy apply", "Retry after resolving warnings."),
|
|
1045
1050
|
allowWarningAction,
|
|
1046
|
-
"
|
|
1051
|
+
editRequestAction("run402 deploy apply --allow-warnings", "Use only when every warning was explicitly reviewed."),
|
|
1047
1052
|
];
|
|
1048
1053
|
enhanced.body = {
|
|
1049
1054
|
...existingBody,
|
|
@@ -1066,98 +1071,98 @@ const ROUTE_WARNING_GUIDANCE = {
|
|
|
1066
1071
|
PUBLIC_ROUTED_FUNCTION: {
|
|
1067
1072
|
hint: "A deploy route makes a function public same-origin browser ingress; direct /functions/v1/:name remains API-key protected.",
|
|
1068
1073
|
next_actions: [
|
|
1069
|
-
"Review application auth and authorization in the routed function.",
|
|
1070
|
-
"Add CSRF protection for cookie-authenticated POST/PUT/PATCH/DELETE routes.",
|
|
1071
|
-
"Implement CORS and OPTIONS explicitly when cross-origin callers are intended.",
|
|
1072
|
-
"
|
|
1074
|
+
nextAction("edit_request", { why: "Review application auth and authorization in the routed function." }),
|
|
1075
|
+
nextAction("edit_request", { why: "Add CSRF protection for cookie-authenticated POST/PUT/PATCH/DELETE routes." }),
|
|
1076
|
+
nextAction("edit_request", { why: "Implement CORS and OPTIONS explicitly when cross-origin callers are intended." }),
|
|
1077
|
+
retryAction("run402 deploy apply --allow-warnings", "Retry only after the public ingress review is intentional."),
|
|
1073
1078
|
],
|
|
1074
1079
|
},
|
|
1075
1080
|
ROUTE_TARGET_CARRIED_FORWARD: {
|
|
1076
1081
|
hint: "A carried-forward route still points at a base-release function target.",
|
|
1077
1082
|
next_actions: [
|
|
1078
|
-
"
|
|
1079
|
-
"Deploy routes.replace if the target should change in this release.",
|
|
1083
|
+
editRequestAction("run402 deploy release active", "Inspect the active release."),
|
|
1084
|
+
editRequestAction("run402 deploy apply --manifest <path>", "Deploy routes.replace if the target should change in this release."),
|
|
1080
1085
|
],
|
|
1081
1086
|
},
|
|
1082
1087
|
ROUTE_SHADOWS_STATIC_PATH: {
|
|
1083
1088
|
hint: "A dynamic route shadows a static site path.",
|
|
1084
1089
|
next_actions: [
|
|
1085
|
-
"Inspect the warning details for affected static paths.",
|
|
1086
|
-
"
|
|
1087
|
-
"
|
|
1090
|
+
nextAction("edit_request", { why: "Inspect the warning details for affected static paths." }),
|
|
1091
|
+
editRequestAction("run402 deploy release active", "Inspect live routes."),
|
|
1092
|
+
retryAction("run402 deploy apply --allow-warnings", "Retry only when dynamic shadowing is intentional."),
|
|
1088
1093
|
],
|
|
1089
1094
|
},
|
|
1090
1095
|
WILDCARD_ROUTE_SHADOWS_STATIC_PATHS: {
|
|
1091
1096
|
hint: "A prefix wildcard route shadows one or more static site paths.",
|
|
1092
1097
|
next_actions: [
|
|
1093
|
-
"Review affected route/static path details.",
|
|
1094
|
-
"Split exact routes or move static paths if the shadowing is accidental.",
|
|
1095
|
-
"
|
|
1098
|
+
nextAction("edit_request", { why: "Review affected route/static path details." }),
|
|
1099
|
+
nextAction("edit_request", { why: "Split exact routes or move static paths if the shadowing is accidental." }),
|
|
1100
|
+
retryAction("run402 deploy apply --allow-warnings", "Retry only when the wildcard shadowing is intentional."),
|
|
1096
1101
|
],
|
|
1097
1102
|
},
|
|
1098
1103
|
METHOD_SPECIFIC_ROUTE_ALLOWS_GET_STATIC_FALLBACK: {
|
|
1099
1104
|
hint: "A method-specific route allows static fallback for unmatched methods such as GET.",
|
|
1100
1105
|
next_actions: [
|
|
1101
|
-
"Confirm that static fallback for GET/HEAD is intended.",
|
|
1102
|
-
"Add method coverage or static files deliberately.",
|
|
1106
|
+
nextAction("edit_request", { why: "Confirm that static fallback for GET/HEAD is intended." }),
|
|
1107
|
+
nextAction("edit_request", { why: "Add method coverage or static files deliberately." }),
|
|
1103
1108
|
],
|
|
1104
1109
|
},
|
|
1105
1110
|
WILDCARD_ROUTE_EXCLUDES_MUTATION_METHODS: {
|
|
1106
1111
|
hint: "A wildcard function route only allows GET/HEAD, so POST/PUT/PATCH/DELETE paths under that prefix will be rejected before the function runs.",
|
|
1107
1112
|
next_actions: [
|
|
1108
|
-
"Add the mutation methods the routed function supports, such as POST.",
|
|
1109
|
-
"Omit methods to allow every supported method when the route is an API surface.",
|
|
1110
|
-
"Set acknowledge_readonly: true on an intentionally read-only GET/HEAD wildcard function route.",
|
|
1111
|
-
"
|
|
1113
|
+
nextAction("edit_request", { why: "Add the mutation methods the routed function supports, such as POST." }),
|
|
1114
|
+
nextAction("edit_request", { why: "Omit methods to allow every supported method when the route is an API surface." }),
|
|
1115
|
+
nextAction("edit_request", { why: "Set acknowledge_readonly: true on an intentionally read-only GET/HEAD wildcard function route." }),
|
|
1116
|
+
retryAction("run402 deploy apply --allow-warning WILDCARD_ROUTE_EXCLUDES_MUTATION_METHODS", "Use only as a reviewed CLI escape hatch."),
|
|
1112
1117
|
],
|
|
1113
1118
|
},
|
|
1114
1119
|
ROUTE_TABLE_NEAR_LIMIT: {
|
|
1115
1120
|
hint: "The route table is near the gateway/project limit.",
|
|
1116
1121
|
next_actions: [
|
|
1117
|
-
"Consolidate prefix routes where possible.",
|
|
1118
|
-
"Remove stale route entries before adding more.",
|
|
1122
|
+
nextAction("edit_request", { why: "Consolidate prefix routes where possible." }),
|
|
1123
|
+
nextAction("edit_request", { why: "Remove stale route entries before adding more." }),
|
|
1119
1124
|
],
|
|
1120
1125
|
},
|
|
1121
1126
|
ROUTES_NOT_ENABLED: {
|
|
1122
1127
|
hint: "Deploy-v2 web routes are not enabled for this project or environment; direct /functions/v1/:name remains protected and is not a browser-route substitute.",
|
|
1123
1128
|
next_actions: [
|
|
1124
|
-
"Deploy without the routes resource, or request route enablement for this project/environment.",
|
|
1125
|
-
"Keep direct function invocation API-key protected; do not substitute it for same-origin browser routes.",
|
|
1129
|
+
nextAction("edit_request", { why: "Deploy without the routes resource, or request route enablement for this project/environment." }),
|
|
1130
|
+
nextAction("edit_request", { why: "Keep direct function invocation API-key protected; do not substitute it for same-origin browser routes." }),
|
|
1126
1131
|
],
|
|
1127
1132
|
},
|
|
1128
1133
|
STATIC_ALIAS_SHADOWS_STATIC_PATH: {
|
|
1129
1134
|
hint: "A static route target shadows a direct static path at the same public URL.",
|
|
1130
1135
|
next_actions: [
|
|
1131
|
-
"Inspect the route pattern, target.file, and direct static path.",
|
|
1132
|
-
"Confirm only when the static route target is intentional.",
|
|
1136
|
+
nextAction("edit_request", { why: "Inspect the route pattern, target.file, and direct static path." }),
|
|
1137
|
+
nextAction("edit_request", { why: "Confirm only when the static route target is intentional." }),
|
|
1133
1138
|
],
|
|
1134
1139
|
},
|
|
1135
1140
|
STATIC_ALIAS_RELATIVE_ASSET_RISK: {
|
|
1136
1141
|
hint: "Relative asset URLs inside the target HTML may resolve differently at the static route target URL.",
|
|
1137
1142
|
next_actions: [
|
|
1138
|
-
"Inspect the target HTML for relative asset references.",
|
|
1139
|
-
"Use absolute asset URLs or confirm only when the alternate URL is intentional.",
|
|
1143
|
+
nextAction("edit_request", { why: "Inspect the target HTML for relative asset references." }),
|
|
1144
|
+
nextAction("edit_request", { why: "Use absolute asset URLs or confirm only when the alternate URL is intentional." }),
|
|
1140
1145
|
],
|
|
1141
1146
|
},
|
|
1142
1147
|
STATIC_ALIAS_DUPLICATE_CANONICAL_URL: {
|
|
1143
1148
|
hint: "Both the static route target URL and the target file URL may be publicly reachable.",
|
|
1144
1149
|
next_actions: [
|
|
1145
|
-
"Decide which URL should be canonical.",
|
|
1146
|
-
"Update links/canonical tags or accept the duplicate public URL intentionally.",
|
|
1150
|
+
nextAction("edit_request", { why: "Decide which URL should be canonical." }),
|
|
1151
|
+
nextAction("edit_request", { why: "Update links/canonical tags or accept the duplicate public URL intentionally." }),
|
|
1147
1152
|
],
|
|
1148
1153
|
},
|
|
1149
1154
|
STATIC_ALIAS_EXTENSIONLESS_NON_HTML: {
|
|
1150
1155
|
hint: "An extensionless static route target points at a non-HTML file.",
|
|
1151
1156
|
next_actions: [
|
|
1152
|
-
"Check that the extensionless route is meant to serve that content type.",
|
|
1153
|
-
"Prefer extensionless static route targets for HTML pages.",
|
|
1157
|
+
nextAction("edit_request", { why: "Check that the extensionless route is meant to serve that content type." }),
|
|
1158
|
+
nextAction("edit_request", { why: "Prefer extensionless static route targets for HTML pages." }),
|
|
1154
1159
|
],
|
|
1155
1160
|
},
|
|
1156
1161
|
STATIC_ALIAS_TABLE_NEAR_LIMIT: {
|
|
1157
1162
|
hint: "Static route targets count toward the route table limit.",
|
|
1158
1163
|
next_actions: [
|
|
1159
|
-
"Consolidate manual static route targets where possible.",
|
|
1160
|
-
"Avoid one route entry per page for large sites until framework-scale Web Output support exists.",
|
|
1164
|
+
nextAction("edit_request", { why: "Consolidate manual static route targets where possible." }),
|
|
1165
|
+
nextAction("edit_request", { why: "Avoid one route entry per page for large sites until framework-scale Web Output support exists." }),
|
|
1161
1166
|
],
|
|
1162
1167
|
},
|
|
1163
1168
|
};
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* summary. Wired into `run402 deploy` pre-flight as a deploy-failing
|
|
30
30
|
* gate for the error severities; non-blocking for warning severities.
|
|
31
31
|
*
|
|
32
|
-
* @see
|
|
32
|
+
* @see the auth-aware-ssr OpenSpec change (functions-sdk-auth-model)
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
package/lib/functions.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { findProject, API } from "./config.mjs";
|
|
|
3
3
|
import { getSdk } from "./sdk.mjs";
|
|
4
4
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
5
5
|
import { assertKnownFlags, hasHelp, normalizeArgv, parseIntegerFlag, validateRegularFile } from "./argparse.mjs";
|
|
6
|
+
import { cliCommandAction } from "./next-actions.mjs";
|
|
6
7
|
|
|
7
8
|
const FUNCTION_LOG_REQUEST_ID_RE = /^req_[A-Za-z0-9_-]{4,128}$/;
|
|
8
9
|
const ISO_DATE_TIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
@@ -541,7 +542,7 @@ async function rebuild(projectId, args = []) {
|
|
|
541
542
|
code: "CANNOT_REBUILD_UNLOCKED_DEPS",
|
|
542
543
|
message: err.body.error || err.body.message || "Function was deployed before dependency locking.",
|
|
543
544
|
hint: UNLOCKED_DEPS_HINT,
|
|
544
|
-
next_actions: [`run402 functions deploy ${projectId} ${name} --file <file
|
|
545
|
+
next_actions: [cliCommandAction("deploy", `run402 functions deploy ${projectId} ${name} --file <file>`, "Redeploy the function once so Run402 can lock dependencies for future rebuilds.")],
|
|
545
546
|
retryable: false,
|
|
546
547
|
safe_to_retry: false,
|
|
547
548
|
});
|
package/lib/init.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readAllowance, saveAllowance, loadKeyStore, configDir } from "./config.mjs";
|
|
2
2
|
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { fail } from "./sdk-errors.mjs";
|
|
4
|
+
import { setTierAction, deployAction } from "./next-actions.mjs";
|
|
4
5
|
import { getActiveProfile } from "../core-dist/config.js";
|
|
5
6
|
import { readMeta } from "../core-dist/profiles.js";
|
|
6
7
|
import { mkdirSync } from "fs";
|
|
@@ -95,6 +96,7 @@ export async function run(args = []) {
|
|
|
95
96
|
balances: null,
|
|
96
97
|
tier: null,
|
|
97
98
|
projects_saved: 0,
|
|
99
|
+
next_actions: [],
|
|
98
100
|
next_step: null,
|
|
99
101
|
};
|
|
100
102
|
|
|
@@ -268,18 +270,18 @@ export async function run(args = []) {
|
|
|
268
270
|
summary.projects_saved = Object.keys(store.projects).length;
|
|
269
271
|
line("Projects", `${summary.projects_saved} saved`);
|
|
270
272
|
|
|
271
|
-
// 6. Next step
|
|
273
|
+
// 6. Next step — canonical typed action(s); `next_step` is the back-compat
|
|
274
|
+
// string mirror of the first action's command (one spelling, surface-wide).
|
|
272
275
|
write("");
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (
|
|
276
|
+
const tierMissing = !tierInfo || !tierInfo.tier || !tierInfo.active;
|
|
277
|
+
summary.next_actions = [tierMissing ? setTierAction("prototype") : deployAction()];
|
|
278
|
+
summary.next_step = summary.next_actions[0].command;
|
|
279
|
+
if (tierMissing) {
|
|
277
280
|
write(" Next: run402 tier set prototype");
|
|
278
281
|
} else {
|
|
279
282
|
write(" Ready to deploy. Run: run402 deploy apply --manifest app.json");
|
|
280
283
|
}
|
|
281
284
|
write("");
|
|
282
|
-
summary.next_step = nextStep;
|
|
283
285
|
|
|
284
286
|
console.log(JSON.stringify(summary, null, 2));
|
|
285
287
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical bootstrap `next_actions` entries
|
|
3
|
+
* (change: keep-agent-in-loop-on-cold-start).
|
|
4
|
+
*
|
|
5
|
+
* One spelling, surface-wide: each cold-start wall hands the agent a typed
|
|
6
|
+
* action whose `command` is the literal CLI invocation. The shape mirrors the
|
|
7
|
+
* gateway's `next_actions` (`{ type, why, ... }`) extended with `command` for
|
|
8
|
+
* CLI-resolvable, client-side actions — the same shape already used by
|
|
9
|
+
* `cli/lib/email.mjs`. The `type` values match the SDK `NextActionType` union.
|
|
10
|
+
*
|
|
11
|
+
* Use these helpers whenever the CLI itself authors next actions. Gateway/SDK
|
|
12
|
+
* `next_actions` remain the lower-layer source of truth and should pass through.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export function nextAction(type, fields = {}) {
|
|
16
|
+
const action = { type };
|
|
17
|
+
for (const key of ["command", "method", "path", "auth", "why"]) {
|
|
18
|
+
if (fields[key] !== undefined) action[key] = fields[key];
|
|
19
|
+
}
|
|
20
|
+
return action;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function cliCommandAction(type, command, why) {
|
|
24
|
+
return nextAction(type, { command, why });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function editRequestAction(command, why) {
|
|
28
|
+
return cliCommandAction("edit_request", command, why);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function retryAction(command, why) {
|
|
32
|
+
return cliCommandAction("retry", command, why);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function initializeWalletAction() {
|
|
36
|
+
return nextAction("initialize_wallet", {
|
|
37
|
+
command: "run402 init",
|
|
38
|
+
why: "Create and fund an agent allowance, then retry.",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function createProjectAction() {
|
|
43
|
+
return nextAction("create_project", {
|
|
44
|
+
command: "run402 projects provision",
|
|
45
|
+
why: "Provision a project to act on, then retry.",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function setTierAction(tier = "prototype") {
|
|
50
|
+
return nextAction("renew_tier", {
|
|
51
|
+
command: `run402 tier set ${tier}`,
|
|
52
|
+
why: "Subscribe the account to a tier (free on testnet), then retry.",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function deployAction() {
|
|
57
|
+
return nextAction("deploy", {
|
|
58
|
+
command: "run402 deploy apply --manifest app.json",
|
|
59
|
+
why: "Apply your release manifest to deploy.",
|
|
60
|
+
});
|
|
61
|
+
}
|
package/lib/projects.mjs
CHANGED
|
@@ -149,13 +149,16 @@ Examples:
|
|
|
149
149
|
provision: `run402 projects provision — Provision a new Postgres project
|
|
150
150
|
|
|
151
151
|
Usage:
|
|
152
|
-
run402 projects provision [--tier <tier>] [--name <name>] [--org <id>]
|
|
152
|
+
run402 projects provision [--tier <tier>] [--name <name>] [--org <id>] [--idempotency-key <key>]
|
|
153
153
|
|
|
154
154
|
Options:
|
|
155
155
|
--tier <tier> Tier for the new project (default: prototype)
|
|
156
156
|
--name <name> Human-readable name for the project
|
|
157
157
|
--org <id> Provision into an EXISTING org (needs developer+ on it).
|
|
158
158
|
Omit for the cold-start path. Tier is org-governed.
|
|
159
|
+
--idempotency-key <key> Retry-safe key: re-running with the same key returns
|
|
160
|
+
the existing project instead of duplicating it. Auto-derived
|
|
161
|
+
from --name when omitted; an unnamed provision stays un-keyed.
|
|
159
162
|
|
|
160
163
|
Notes:
|
|
161
164
|
- Payment is automatic via x402; requires a funded allowance
|
|
@@ -257,7 +260,7 @@ async function quote() {
|
|
|
257
260
|
}
|
|
258
261
|
|
|
259
262
|
async function provision(args) {
|
|
260
|
-
const opts = { tier: "prototype", name: undefined, orgId: undefined };
|
|
263
|
+
const opts = { tier: "prototype", name: undefined, orgId: undefined, idempotencyKey: undefined };
|
|
261
264
|
for (let i = 0; i < args.length; i++) {
|
|
262
265
|
if (args[i] === "--tier" && args[i + 1]) opts.tier = args[++i];
|
|
263
266
|
// Use !== undefined so an empty-string value is captured (and rejected
|
|
@@ -267,6 +270,16 @@ async function provision(args) {
|
|
|
267
270
|
// Omitted = cold-start. Tier is org-governed, so --tier is irrelevant here
|
|
268
271
|
// (the gateway ignores a client-supplied tier in all cases).
|
|
269
272
|
if (args[i] === "--org" && args[i + 1] !== undefined) opts.orgId = args[++i];
|
|
273
|
+
// Retry-safety: re-running provision (the agent's natural mode after a
|
|
274
|
+
// crash/fresh session) must not duplicate-bill a project.
|
|
275
|
+
if (args[i] === "--idempotency-key" && args[i + 1] !== undefined) opts.idempotencyKey = args[++i];
|
|
276
|
+
}
|
|
277
|
+
// Auto-derive a stable key from --name when none was supplied: a named
|
|
278
|
+
// project is a stable intent, so re-running `provision --name X` collapses
|
|
279
|
+
// onto the same project. An explicit --idempotency-key always wins; an
|
|
280
|
+
// unnamed provision stays un-keyed (each call is a new project on purpose).
|
|
281
|
+
if (opts.idempotencyKey === undefined && opts.name) {
|
|
282
|
+
opts.idempotencyKey = `provision:${opts.name}`;
|
|
270
283
|
}
|
|
271
284
|
if (opts.orgId === "") {
|
|
272
285
|
fail({
|
|
@@ -312,7 +325,7 @@ async function provision(args) {
|
|
|
312
325
|
const activeBefore = getActiveProjectId();
|
|
313
326
|
try {
|
|
314
327
|
const data = await withAutoApprove(() =>
|
|
315
|
-
getSdk().projects.provision({ tier: opts.tier, name: opts.name, orgId: opts.orgId }),
|
|
328
|
+
getSdk().projects.provision({ tier: opts.tier, name: opts.name, orgId: opts.orgId, idempotencyKey: opts.idempotencyKey }),
|
|
316
329
|
);
|
|
317
330
|
const activeAfter = getActiveProjectId();
|
|
318
331
|
const out = { ...data };
|
package/lib/secrets.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { readFileSync } from "fs";
|
|
|
2
2
|
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
4
4
|
import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs, validateRegularFile } from "./argparse.mjs";
|
|
5
|
+
import { editRequestAction } from "./next-actions.mjs";
|
|
5
6
|
|
|
6
7
|
const HELP = `run402 secrets — Manage project secrets
|
|
7
8
|
|
|
@@ -163,8 +164,8 @@ function failMissingStdin() {
|
|
|
163
164
|
message: "Missing secret value on stdin.",
|
|
164
165
|
hint: "Pipe a value, use --file <path>, or provide an inline value only when shell history exposure is acceptable.",
|
|
165
166
|
next_actions: [
|
|
166
|
-
"printf %s \"$VALUE\" | run402 secrets set <project> <KEY> --stdin",
|
|
167
|
-
"run402 secrets set <project> <KEY> --file <path>",
|
|
167
|
+
editRequestAction("printf %s \"$VALUE\" | run402 secrets set <project> <KEY> --stdin", "Pipe the secret value through stdin."),
|
|
168
|
+
editRequestAction("run402 secrets set <project> <KEY> --file <path>", "Read the secret value from a local file."),
|
|
168
169
|
],
|
|
169
170
|
});
|
|
170
171
|
}
|
package/lib/subdomains.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { resolveProject, resolveProjectId } from "./config.mjs";
|
|
|
2
2
|
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
4
4
|
import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs } from "./argparse.mjs";
|
|
5
|
+
import { cliCommandAction } from "./next-actions.mjs";
|
|
5
6
|
|
|
6
7
|
const HELP = `run402 subdomains — Manage custom subdomains
|
|
7
8
|
|
|
@@ -115,7 +116,7 @@ async function claim(args) {
|
|
|
115
116
|
message: "no deployment_id specified and no recent deployment found.",
|
|
116
117
|
hint: "Deploy a site first or pass --deployment <id>.",
|
|
117
118
|
details: { project_id: projectId },
|
|
118
|
-
next_actions: [
|
|
119
|
+
next_actions: [cliCommandAction("deploy", "run402 deploy apply", "Deploy a site first, then retry claiming the subdomain.")],
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
122
|
try {
|
package/lib/tier.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getSdk } from "./sdk.mjs";
|
|
2
2
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
3
|
-
import { assertKnownFlags, normalizeArgv, positionalArgs } from "./argparse.mjs";
|
|
3
|
+
import { assertKnownFlags, normalizeArgv, positionalArgs, flagValue } from "./argparse.mjs";
|
|
4
|
+
import { setTierAction } from "./next-actions.mjs";
|
|
4
5
|
|
|
5
6
|
const HELP = `run402 tier — Manage your Run402 tier subscription
|
|
6
7
|
|
|
@@ -50,11 +51,16 @@ Examples:
|
|
|
50
51
|
set: `run402 tier set — Subscribe, renew, or upgrade your tier
|
|
51
52
|
|
|
52
53
|
Usage:
|
|
53
|
-
run402 tier set <tier>
|
|
54
|
+
run402 tier set <tier> [--idempotency-key <key>]
|
|
54
55
|
|
|
55
56
|
Arguments:
|
|
56
57
|
<tier> One of: prototype, hobby, team
|
|
57
58
|
|
|
59
|
+
Options:
|
|
60
|
+
--idempotency-key <key> Retry-safe key: re-running the same subscribe/renew
|
|
61
|
+
intent with this key does not double-charge. Use a
|
|
62
|
+
fresh key for a deliberate second renewal.
|
|
63
|
+
|
|
58
64
|
Tiers:
|
|
59
65
|
prototype $0.10/7d (free with testnet faucet)
|
|
60
66
|
hobby $5/30d
|
|
@@ -101,8 +107,8 @@ async function status(args = []) {
|
|
|
101
107
|
|
|
102
108
|
async function set(args = []) {
|
|
103
109
|
const parsedArgs = normalizeArgv(args);
|
|
104
|
-
assertKnownFlags(parsedArgs, ["--help", "-h"]);
|
|
105
|
-
const positionals = positionalArgs(parsedArgs);
|
|
110
|
+
assertKnownFlags(parsedArgs, ["--help", "-h"], ["--idempotency-key"]);
|
|
111
|
+
const positionals = positionalArgs(parsedArgs, ["--idempotency-key"]);
|
|
106
112
|
if (positionals.length > 1) {
|
|
107
113
|
fail({
|
|
108
114
|
code: "BAD_USAGE",
|
|
@@ -116,11 +122,16 @@ async function set(args = []) {
|
|
|
116
122
|
code: "BAD_USAGE",
|
|
117
123
|
message: "Missing <tier>.",
|
|
118
124
|
hint: "run402 tier set <prototype|hobby|team>",
|
|
125
|
+
next_actions: [setTierAction()],
|
|
119
126
|
});
|
|
120
127
|
}
|
|
128
|
+
// Caller-supplied idempotency key makes a retried subscribe/renew safe from
|
|
129
|
+
// double-charge. Not auto-derived: the SDK cannot tell a retry from a new
|
|
130
|
+
// renewal intent (that boundary is the caller's).
|
|
131
|
+
const idempotencyKey = flagValue(parsedArgs, "--idempotency-key") ?? undefined;
|
|
121
132
|
try {
|
|
122
133
|
const sdk = getSdk();
|
|
123
|
-
const data = await sdk.tier.set(tierName);
|
|
134
|
+
const data = await sdk.tier.set(tierName, idempotencyKey ? { idempotencyKey } : {});
|
|
124
135
|
let statusAfter = null;
|
|
125
136
|
try {
|
|
126
137
|
statusAfter = await sdk.tier.status();
|
package/package.json
CHANGED
|
@@ -46,7 +46,7 @@ export interface WalletIdentity {
|
|
|
46
46
|
* Gateway write-auth (operator-approval) capabilities. Each is scoped to a
|
|
47
47
|
* target: `org.project.create` → an org, the others → a project.
|
|
48
48
|
*/
|
|
49
|
-
export type WriteAuthCapability = "org.project.create" | "project.deploy" | "project.secret.write";
|
|
49
|
+
export type WriteAuthCapability = "org.project.create" | "project.deploy" | "project.secret.write" | "project.archives.export";
|
|
50
50
|
/** A write-capability target: an org (for `org.project.create`) or a project. */
|
|
51
51
|
export interface WriteAuthTarget {
|
|
52
52
|
org_id?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B,oBAAoB,GACpB,gBAAgB,GAChB,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B,oBAAoB,GACpB,gBAAgB,GAChB,sBAAsB,GACtB,yBAAyB,CAAC;AAE9B,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,+BAA+B;IAC/B,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;OASG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAE1F;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAEpD;;;OAGG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D,kGAAkG;IAClG,aAAa,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvE,oEAAoE;IACpE,aAAa,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1C,kEAAkE;IAClE,gBAAgB,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C,oEAAoE;IACpE,gBAAgB,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE5C,gFAAgF;IAChF,aAAa,CAAC,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAEhD,6CAA6C;IAC7C,aAAa,CAAC,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnD,oHAAoH;IACpH,eAAe,CAAC,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAE3C,6FAA6F;IAC7F,gBAAgB,CAAC,IAAI,MAAM,CAAC;IAE5B;;;;OAIG;IACH,iBAAiB,CAAC,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CACtD"}
|
package/sdk/dist/errors.d.ts
CHANGED
|
@@ -58,8 +58,8 @@ export declare abstract class Run402Error extends Error {
|
|
|
58
58
|
readonly traceId?: string;
|
|
59
59
|
/** Canonical structured context. Preserved by reference from the response body. */
|
|
60
60
|
readonly details?: unknown;
|
|
61
|
-
/** Advisory next actions
|
|
62
|
-
readonly nextActions?:
|
|
61
|
+
/** Advisory next actions (gateway-authored or SDK-synthesized). Rendering them must not execute them. */
|
|
62
|
+
readonly nextActions?: NextAction[];
|
|
63
63
|
/**
|
|
64
64
|
* Quota-denial scope (v1.46+). `"organization"` for pooled organization
|
|
65
65
|
* denials; `"project"` for the orphan fallback (project whose organization
|
|
@@ -252,6 +252,29 @@ export declare class TransferFreezeError extends Run402Error {
|
|
|
252
252
|
readonly projectId: string | null;
|
|
253
253
|
constructor(message: string, status: number, body: unknown, context: string);
|
|
254
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Known `type` values for a {@link NextAction}. The gateway set (style.md
|
|
257
|
+
* §Errors) extended with the client-side bootstrap verbs `create_project` and
|
|
258
|
+
* `initialize_wallet`, plus `operator_approve` (synthesized for WRITE_AUTH).
|
|
259
|
+
* Tolerates unknown future gateway types via the `(string & {})` fallback.
|
|
260
|
+
*/
|
|
261
|
+
export type NextActionType = "retry" | "authenticate" | "submit_payment" | "renew_tier" | "check_usage" | "resume_deploy" | "edit_request" | "edit_migration" | "create_project" | "initialize_wallet" | "deploy" | "operator_approve" | "contact_support";
|
|
262
|
+
/**
|
|
263
|
+
* A single advisory "what to do next" entry. Mirrors the gateway's
|
|
264
|
+
* `next_actions[]` shape (`{ type, method?, path?, auth?, why? }`) extended with
|
|
265
|
+
* `command` — the literal CLI invocation — for client-side, CLI-resolvable
|
|
266
|
+
* actions. Rendering an action must never execute it.
|
|
267
|
+
*/
|
|
268
|
+
export interface NextAction {
|
|
269
|
+
type: NextActionType | (string & {});
|
|
270
|
+
/** Literal CLI invocation for client-side, CLI-resolvable actions. */
|
|
271
|
+
command?: string;
|
|
272
|
+
method?: string;
|
|
273
|
+
path?: string;
|
|
274
|
+
auth?: string;
|
|
275
|
+
why?: string;
|
|
276
|
+
[key: string]: unknown;
|
|
277
|
+
}
|
|
255
278
|
/**
|
|
256
279
|
* HTTP 403 `STEP_UP_REQUIRED` — the gateway requires a fresh, same-client
|
|
257
280
|
* step-up (a recent `passkey` AMR) before this high-stakes control-plane
|