turnzero 0.27.0 → 0.30.0
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 +15 -5
- package/dist/turnzero-cloud.mjs +238 -155
- package/package.json +18 -3
package/dist/turnzero-cloud.mjs
CHANGED
|
@@ -37,7 +37,7 @@ var shownHealthPathOf = (path) => {
|
|
|
37
37
|
}
|
|
38
38
|
return JSON.stringify("\u2026");
|
|
39
39
|
};
|
|
40
|
-
var healthPathUnservedDetail = (path) => `No source text in the zip names the health path ${shownHealthPathOf(path)}. Serve it from your code, or set the manifest's health to a served path and call submit_manifest
|
|
40
|
+
var healthPathUnservedDetail = (path) => `No source text in the zip names the health path ${shownHealthPathOf(path)}. Serve it from your code, or set the manifest's health to a served path and call submit_manifest.`;
|
|
41
41
|
var deployAreaName = (application) => `${DEPLOY_AREA_PREFIX}${application}`;
|
|
42
42
|
var UPLOAD_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
43
43
|
var uploadFileName = (id) => `${id}.zip`;
|
|
@@ -275,6 +275,11 @@ var MINT_TOKEN_ACTION = "mint_token";
|
|
|
275
275
|
var REVOKE_TOKEN_ACTION = "revoke_token";
|
|
276
276
|
var AUTHORIZED_LINE_HEAD = "authorized: ";
|
|
277
277
|
var LOGGED_OUT_LINE_HEAD = "logged out: ";
|
|
278
|
+
var COMPLETION_MEMBER = "completion";
|
|
279
|
+
var COMPLETION_MINT = "mint";
|
|
280
|
+
var MINT_MEMBER = "mint";
|
|
281
|
+
var MINT_OPTION = "--mint";
|
|
282
|
+
var MINT_ID_FORM = UPLOAD_ID;
|
|
278
283
|
|
|
279
284
|
// src/turnzero_cloud_command/credential_file.ts
|
|
280
285
|
import { randomBytes } from "node:crypto";
|
|
@@ -393,14 +398,14 @@ var isWireVersion = (value) => parseWireVersion(value) !== null;
|
|
|
393
398
|
function compareWireVersions(a, b) {
|
|
394
399
|
const x2 = parseWireVersion(a);
|
|
395
400
|
const y = parseWireVersion(b);
|
|
396
|
-
if (!x2 || !y) throw new Error("a wire version is a date, a dot, and a counter
|
|
401
|
+
if (!x2 || !y) throw new Error("a wire version is a date, a dot, and a counter.");
|
|
397
402
|
if (x2.date !== y.date) return x2.date < y.date ? -1 : 1;
|
|
398
403
|
return x2.counter === y.counter ? 0 : x2.counter < y.counter ? -1 : 1;
|
|
399
404
|
}
|
|
400
405
|
|
|
401
406
|
// src/turnzero_cloud_command/plane_wire.ts
|
|
402
407
|
var PLANE_WIRE_NEEDED = Object.freeze({
|
|
403
|
-
action_payloads: "2026-10-
|
|
408
|
+
action_payloads: "2026-10-24.10",
|
|
404
409
|
management_api: "2026-10-10.5",
|
|
405
410
|
wire_errors: "2026-10-10.6"
|
|
406
411
|
});
|
|
@@ -440,19 +445,20 @@ var originReadOf = (value) => {
|
|
|
440
445
|
const held = (value ?? "").trim();
|
|
441
446
|
return held === "" ? DEFAULT_COMMAND_ORIGIN : originOf(held) ?? held;
|
|
442
447
|
};
|
|
448
|
+
var ORIGIN_FORM_SENTENCES = `It is an https address with no path, ${ORIGIN_HOST_WORDS}. For a local platform, it may be an http address of this machine: 127.0.0.1, localhost, or [::1].`;
|
|
443
449
|
var commandOriginOf = (text) => {
|
|
444
450
|
const origin = originOf(text);
|
|
445
451
|
if (origin !== null) return { origin };
|
|
446
452
|
return {
|
|
447
|
-
refusal: `${DEPLOY_OPTION.origin} names the platform's address, such as ${DEFAULT_COMMAND_ORIGIN}
|
|
453
|
+
refusal: `${DEPLOY_OPTION.origin} names the platform's address, such as ${DEFAULT_COMMAND_ORIGIN}, since the command sends its credential to it. ${ORIGIN_FORM_SENTENCES} Nothing was started.`
|
|
448
454
|
};
|
|
449
455
|
};
|
|
450
456
|
var MINTED_TOKEN_OUTSIDE_OPENING = `${DEPLOY_OPTION.origin} names an address this run does not send its minted token to.`;
|
|
451
457
|
var firstPartyOriginWayOn = (origin) => `Set ${ORIGIN_VARIABLE} to ${origin} beside ${MINTED_TOKEN_VARIABLE} and run the line again, since that variable, never the line, states where the minted token goes.`;
|
|
452
|
-
var MINTED_TOKEN_HELD_ALONE = `${MINTED_TOKEN_OUTSIDE_OPENING} Where ${ORIGIN_VARIABLE} is set, a line run by a tool connected by a minted token, or by a job, sends the token in ${MINTED_TOKEN_VARIABLE} only to the address that variable holds
|
|
458
|
+
var MINTED_TOKEN_HELD_ALONE = `${MINTED_TOKEN_OUTSIDE_OPENING} Where ${ORIGIN_VARIABLE} is set, a line run by a tool connected by a minted token, or by a job, sends the token in ${MINTED_TOKEN_VARIABLE} only to the address that variable holds. The line's ${DEPLOY_OPTION.origin} may then name that address alone. Where the platform is at another address, set ${ORIGIN_VARIABLE} to it beside the token, or unset the variable for the command's default address, and run the line again.`;
|
|
453
459
|
var mintedTokenOriginOf = (env, named, closing) => {
|
|
454
460
|
const held = heldOriginOf(env, named, closing, {
|
|
455
|
-
variable: "
|
|
461
|
+
variable: "A line run by a tool connected by a minted token, or by a job, sends the minted token to the address the variable names",
|
|
456
462
|
outside: `${MINTED_TOKEN_OUTSIDE_OPENING} A line run by a tool connected by a minted token, or by a job, sends the token in ${MINTED_TOKEN_VARIABLE} only to ${DEFAULT_COMMAND_ORIGIN} or to the address ${ORIGIN_VARIABLE} holds in the same environment. Where the platform is at another address, set ${ORIGIN_VARIABLE} to it beside the token, and run the line again.`,
|
|
457
463
|
heldAlone: MINTED_TOKEN_HELD_ALONE
|
|
458
464
|
});
|
|
@@ -466,7 +472,7 @@ var heldOriginOf = (env, named, closing, words) => {
|
|
|
466
472
|
const fromEnvironment = held === "" ? null : originOf(held);
|
|
467
473
|
if (held !== "" && fromEnvironment === null) {
|
|
468
474
|
return {
|
|
469
|
-
refusal: `What ${ORIGIN_VARIABLE} holds is not the platform's address
|
|
475
|
+
refusal: `What ${ORIGIN_VARIABLE} holds is not the platform's address. ${words.variable}, such as ${DEFAULT_COMMAND_ORIGIN}. ${ORIGIN_FORM_SENTENCES} Set the variable to that address, or unset it. ${closing}`
|
|
470
476
|
};
|
|
471
477
|
}
|
|
472
478
|
if (named === void 0) return { origin: fromEnvironment ?? DEFAULT_COMMAND_ORIGIN };
|
|
@@ -481,7 +487,7 @@ var heldOriginOf = (env, named, closing, words) => {
|
|
|
481
487
|
var AUTHORIZATION_ACTS_WORDS = `${AUTHORIZE_SUBCOMMAND}, ${TOKEN_SUBCOMMAND} ${TOKEN_REQUEST_ACT}, and ${TOKEN_SUBCOMMAND} ${TOKEN_CLAIM_ACT}`;
|
|
482
488
|
var authorizationOriginOf = (env, named, closing) => heldOriginOf(env, named, closing, {
|
|
483
489
|
variable: `${AUTHORIZATION_ACTS_WORDS} reach only the address the variable names`,
|
|
484
|
-
outside: `${DEPLOY_OPTION.origin} names an address this computer's authorization does not reach
|
|
490
|
+
outside: `${DEPLOY_OPTION.origin} names an address this computer's authorization does not reach. ${AUTHORIZATION_ACTS_WORDS} reach only ${DEFAULT_COMMAND_ORIGIN}, ${DEVELOPMENT_COMMAND_ORIGIN}, or the address ${ORIGIN_VARIABLE} holds. A line chooses only between the platform's own two addresses, and the environment names any other. Where the platform is at another address, set ${ORIGIN_VARIABLE} to it, and run the line again.`,
|
|
485
491
|
admitted: FIRST_PARTY_COMMAND_ORIGINS
|
|
486
492
|
});
|
|
487
493
|
var entryOriginOf = (env, named, closing) => {
|
|
@@ -491,7 +497,7 @@ var entryOriginOf = (env, named, closing) => {
|
|
|
491
497
|
const origin = originOf(held);
|
|
492
498
|
if (origin !== null) return { origin };
|
|
493
499
|
return {
|
|
494
|
-
refusal: `What ${ORIGIN_VARIABLE} holds is not the platform's address
|
|
500
|
+
refusal: `What ${ORIGIN_VARIABLE} holds is not the platform's address. Where the line names no ${DEPLOY_OPTION.origin}, the command reads this computer's entry for the address the variable names, such as ${DEFAULT_COMMAND_ORIGIN}. ${ORIGIN_FORM_SENTENCES} Set the variable to that address, or unset it. ${closing}`
|
|
495
501
|
};
|
|
496
502
|
};
|
|
497
503
|
var HEADER_VALUE = /^[\x20-\x7e]*$/;
|
|
@@ -551,7 +557,7 @@ var WIRE_FILE_NOUNS = Object.freeze({
|
|
|
551
557
|
});
|
|
552
558
|
var PLANE_OLDER_WAY_ON = "Nothing was started: run this command again once the origin serves a newer release.";
|
|
553
559
|
var PLANE_OLDER_FIRST_ACT_WAY_ON = "Nothing was started: once the origin serves a newer release, start again with the first act.";
|
|
554
|
-
var planeOlderSentence = (version, origin, key, needs, served, tokenCodeUnread) => `${TURNZERO_CLOUD_PACKAGE} ${version} needs ${WIRE_FILE_NOUNS[key]} of ${needs} or later, and ${origin} serves ${served}, an older Turn Zero Cloud. ${tokenCodeUnread === void 0 ? PLANE_OLDER_WAY_ON : `${PLANE_OLDER_FIRST_ACT_WAY_ON} ${tokenCodeUnread}`}`;
|
|
560
|
+
var planeOlderSentence = (version, origin, key, needs, served, tokenCodeUnread) => `${TURNZERO_CLOUD_PACKAGE} ${version} needs ${WIRE_FILE_NOUNS[key]} of ${needs} or later, and ${origin} serves ${served}, an older Turn Zero Cloud. ${tokenCodeUnread === void 0 ? PLANE_OLDER_WAY_ON : tokenCodeUnread === "" ? PLANE_OLDER_FIRST_ACT_WAY_ON : `${PLANE_OLDER_FIRST_ACT_WAY_ON} ${tokenCodeUnread}`}`;
|
|
555
561
|
var COMMAND_LATEST_FORM = /^(?:0|[1-9]\d{0,8})\.(?:0|[1-9]\d{0,8})\.(?:0|[1-9]\d{0,8})$/;
|
|
556
562
|
var servedHealthOf = async (io2, origin) => {
|
|
557
563
|
const controller = new AbortController();
|
|
@@ -845,7 +851,8 @@ var loginLinesFor = (io2, origin) => [
|
|
|
845
851
|
var REDACTED_TOKEN = "<minted token>";
|
|
846
852
|
var MINTED_TOKEN_PREFIX_WORDS = `${MINTED_TOKEN_PREFIX.slice(0, -1)} and an underscore`;
|
|
847
853
|
var CREDENTIAL_SOURCES = Object.freeze(["standard_input", "minted_token_variable", "credential_file"]);
|
|
848
|
-
var
|
|
854
|
+
var NO_INPUT_PLACES = `The command reads no standard input. It reads a minted token from ${MINTED_TOKEN_VARIABLE} where it is set. A tool connected by a minted token sets it from where its token is kept, and a job with no tool from its secret store. Otherwise it reads this computer's token for the platform's address from its credential file, which login and authorize write.`;
|
|
855
|
+
var credentialPlaces = (declared) => declared.places ?? `The command reads the grant from its standard input, as the line ${declared.call} answers pipes it in.${declared.mintedToken ? ` Otherwise it reads a minted token from ${MINTED_TOKEN_VARIABLE} where it is set, or this computer's token for the platform's address from its credential file, which login and authorize write.` : declared.computerToken === true ? ` Otherwise it reads this computer's token for the platform's address from its credential file, which login and authorize write. It never reads a minted token from ${MINTED_TOKEN_VARIABLE}.` : ""}`;
|
|
849
856
|
var unsetVariableWayOn = (declared) => `To run a line you wrote yourself under this computer's token, unset ${MINTED_TOKEN_VARIABLE} and run it again; or pipe ${declared.aGrant}.`;
|
|
850
857
|
var grantWaysOn = (declared) => `Pipe ${declared.aGrant}, as the line ${declared.call} answers pipes it, or, where you wrote the line yourself, pipe nothing to run it under this computer's token.`;
|
|
851
858
|
var emptyGrantRefusal = (declared) => `The ${declared.grant} piped to the command was empty, so nothing was sent and nothing was started. ${grantWaysOn(declared)}`;
|
|
@@ -856,7 +863,7 @@ var unendedReadRefusal = (declared, reading) => unendedReadSentence(
|
|
|
856
863
|
declared.computerToken === true ? grantWaysOn(declared) : pipeWholeWayOn(declared.aGrant, declared.call)
|
|
857
864
|
);
|
|
858
865
|
var emptyTokenVariableRefusal = () => `${MINTED_TOKEN_VARIABLE} is set but empty, so nothing was sent and nothing was started: the read that set it for this line found no token. Set it to your minted token from where your tool keeps it, or unset it to use this computer's token.`;
|
|
859
|
-
var noCredentialRefusal = (declared, origin, expiredAt, again) => `No ${declared.grant} was read, so nothing was started
|
|
866
|
+
var noCredentialRefusal = (declared, origin, expiredAt, again) => `No ${declared.grant} was read, so nothing was started. ${credentialPlaces(declared)} ${expiredAt === null ? `This computer's credential file holds no token for ${origin}.` : `This computer's token for ${origin} in its credential file expired at ${expiredAt}.`} ${again}`.trimEnd();
|
|
860
867
|
var expired = (expiresAt, now) => expiresAt !== null && Date.parse(expiresAt) <= now.getTime();
|
|
861
868
|
async function readCredential(io2, declared, again, line = { named: void 0, closing: "" }) {
|
|
862
869
|
if (declared.piped !== false) {
|
|
@@ -880,14 +887,14 @@ async function readCredential(io2, declared, again, line = { named: void 0, clos
|
|
|
880
887
|
if (held !== void 0 && token2 === "" && declared.mintedToken) return { refusal: emptyTokenVariableRefusal() };
|
|
881
888
|
if (token2 !== "" && !declared.mintedToken) {
|
|
882
889
|
return {
|
|
883
|
-
refusal: `No ${declared.grant} was read, so nothing was started. ${MINTED_TOKEN_VARIABLE} is set, and this subcommand does not run under a minted token
|
|
890
|
+
refusal: `No ${declared.grant} was read, so nothing was started. ${MINTED_TOKEN_VARIABLE} is set, and this subcommand does not run under a minted token. ${credentialPlaces(declared)} ${computerToken ? `${unsetVariableWayOn(declared)} ` : ""}${again}`
|
|
884
891
|
};
|
|
885
892
|
}
|
|
886
|
-
if (!declared.mintedToken && !computerToken) return { refusal: `No ${declared.grant} was read, so nothing was started
|
|
893
|
+
if (!declared.mintedToken && !computerToken) return { refusal: `No ${declared.grant} was read, so nothing was started. ${credentialPlaces(declared)} ${again}` };
|
|
887
894
|
if (token2 !== "") {
|
|
888
895
|
if (!MINTED_TOKEN_FORM.test(token2)) {
|
|
889
896
|
return {
|
|
890
|
-
refusal:
|
|
897
|
+
refusal: `${MINTED_TOKEN_VARIABLE} does not hold a minted token, so nothing was sent and nothing was started. A token that mint_token answers opens with ${MINTED_TOKEN_PREFIX_WORDS}. Set the variable to that token, from where your tool keeps it or from your CI's secret store.`
|
|
891
898
|
};
|
|
892
899
|
}
|
|
893
900
|
return { credential: token2, kind: "token", source: "minted_token_variable" };
|
|
@@ -922,18 +929,19 @@ var variableReadSentence = (origin, standing) => {
|
|
|
922
929
|
const opening = `The credential this run read was the minted token in ${MINTED_TOKEN_VARIABLE}, which wins over this computer's credential file`;
|
|
923
930
|
switch (standing) {
|
|
924
931
|
case "live":
|
|
925
|
-
return `${opening}
|
|
932
|
+
return `${opening}. That file holds a token for ${origin}. Where the variable is left from another run, unset it and run the line again to run under this computer's token.`;
|
|
926
933
|
case "expired":
|
|
927
|
-
return `${opening}
|
|
934
|
+
return `${opening}. That file holds a token for ${origin} that has expired.`;
|
|
928
935
|
case "none":
|
|
929
|
-
return `${opening}
|
|
936
|
+
return `${opening}. That file holds no token for ${origin}.`;
|
|
930
937
|
case "unread":
|
|
931
|
-
return `${opening}
|
|
938
|
+
return `${opening}. That file could not be read.`;
|
|
932
939
|
}
|
|
933
940
|
};
|
|
934
941
|
var fileReadSentence = (origin) => `The credential this run read was this computer's token for ${origin}, from its credential file: where the platform no longer accepts it, run ${LOGIN_SUBCOMMAND}${originWordsOf(origin)} to authorize this computer again.`;
|
|
935
942
|
var noTokenSentence = (origin, expiredAt) => expiredAt === null ? `No credential was read, so nothing was sent: ${MINTED_TOKEN_VARIABLE} is not set, and this computer's credential file holds no token for ${origin}.` : `No credential was read, so nothing was sent: ${MINTED_TOKEN_VARIABLE} is not set, and this computer's token for ${origin} in its credential file expired at ${expiredAt}.`;
|
|
936
|
-
var VARIABLE_ROUTE_SENTENCES = `A tool connected by a minted token, or a job, sets ${MINTED_TOKEN_VARIABLE} from where its token is kept, never by writing the value in a line
|
|
943
|
+
var VARIABLE_ROUTE_SENTENCES = `A tool connected by a minted token, or a job, sets ${MINTED_TOKEN_VARIABLE} from where its token is kept, never by writing the value in a line. It sets ${ORIGIN_VARIABLE} to the address that minted the token where that is not ${DEFAULT_COMMAND_ORIGIN}, then runs the line again. Such a tool cannot use ${MINT_TOKEN_ACTION}, which takes a sign-in alone. A tool sets ${MINTED_TOKEN_VARIABLE} for one line at a time and unsets it once that line ends.`;
|
|
944
|
+
var authorizeRouteSentence = (origin) => `${AUTHORIZE_SUBCOMMAND}${originWordsOf(origin)} run with nothing piped prints the ${MINT_TOKEN_ACTION} call a signed-in tool makes to authorize this computer.`;
|
|
937
945
|
var LOGIN_ROUTE_OPENER = "A person logs this computer in once, in a terminal of their own, and its token then stays on this computer for 30 days:";
|
|
938
946
|
var DEPLOY_FILES_ROUTE_SENTENCE = "A folder within the caps of your tool's deploy_files call deploys now through deploy_files, with no login.";
|
|
939
947
|
var loginRouteLines = (io2, origin, deployFiles = true) => [
|
|
@@ -1010,7 +1018,9 @@ var TOKEN_CODE_RUN_AGAIN = "Run the line again once the cause above is put right
|
|
|
1010
1018
|
var TOKEN_CODE_UNREAD = `${TOKEN_CODE_UNREAD_FIRST} ${TOKEN_CODE_RUN_AGAIN}`;
|
|
1011
1019
|
var tokenCodeFloorSentence = (version, found) => `${TURNZERO_CLOUD_PACKAGE} ${version} needs Node.js ${COMMAND_NODE_FLOOR} or later, and this machine runs Node.js ${found}, so nothing was read and nothing was started. A token code the line carried was not read: it is live until it expires, and it mints nothing without this computer's verifier. Install Node.js ${COMMAND_NODE_FLOOR} or later, then run the line again.`;
|
|
1012
1020
|
var NOTHING_SENT = "Nothing was sent and nothing was started.";
|
|
1013
|
-
var
|
|
1021
|
+
var MINT_UNPRESENTED = `Nothing was presented. ${TOKEN_CODE_RUN_AGAIN}`;
|
|
1022
|
+
var namesMint = (argv2) => argv2[0] === AUTHORIZE_SUBCOMMAND && argv2.slice(1).some((argument) => argument === MINT_OPTION || argument.startsWith(`${MINT_OPTION}=`));
|
|
1023
|
+
var carriesTokenCode = (argv2) => argv2[0] === AUTHORIZE_SUBCOMMAND && !namesMint(argv2) || argv2[0] === TOKEN_SUBCOMMAND && argv2[1] === TOKEN_CLAIM_ACT;
|
|
1014
1024
|
var LABEL_FALLBACK = "authorized computer";
|
|
1015
1025
|
var computerLabelOf = (host) => {
|
|
1016
1026
|
let label = "";
|
|
@@ -1035,24 +1045,25 @@ var mintCallMembers = (kind, challenge, label) => kind === "authorized computer"
|
|
|
1035
1045
|
[CODE_CHALLENGE_MEMBER, challenge],
|
|
1036
1046
|
["scope_kind", "account"],
|
|
1037
1047
|
[AUTHORIZED_COMPUTER_MEMBER, true],
|
|
1038
|
-
["label", label]
|
|
1048
|
+
["label", label],
|
|
1049
|
+
[COMPLETION_MEMBER, COMPLETION_MINT]
|
|
1039
1050
|
] : [[CODE_CHALLENGE_MEMBER, challenge]];
|
|
1040
1051
|
var firstActNamed = (kind, origin = DEFAULT_COMMAND_ORIGIN) => `${kind === "authorized computer" ? AUTHORIZE_SUBCOMMAND : `${TOKEN_SUBCOMMAND} ${TOKEN_REQUEST_ACT}`}${originWordsOf(origin)}`;
|
|
1041
1052
|
var noConnectionLine = (origin) => `Where your tool holds no connection signed in to ${origin}, ${MINT_TOKEN_ACTION} is not among its tools: a person connects the tool and signs in, in a browser, before the call.`;
|
|
1042
1053
|
var loginWaySentence = (loginLines) => `A person can instead log this computer in themselves, in a terminal of their own, by running ${loginLines.join(", then ")}, and approving the login in the browser.`;
|
|
1043
1054
|
var firstActLines = (origin, kind, pending, label, loginLines = []) => [
|
|
1044
|
-
kind === "authorized computer" ? `To authorize this computer at ${origin}, call ${MINT_TOKEN_ACTION} from your tool, through the connection you signed in with, naming the members below and no others
|
|
1055
|
+
kind === "authorized computer" ? `To authorize this computer at ${origin}, call ${MINT_TOKEN_ACTION} from your tool, through the connection you signed in with, naming the members below and no others. Then run the line it answers once, as given, on this computer.` : `To mint a token whose value never passes through the chat, call ${MINT_TOKEN_ACTION} at ${origin} from your tool, through the connection you signed in with. Name the member below beside the scope_kind, application, grants, label, and expires_in_days the task needs.`,
|
|
1045
1056
|
// The case this command cannot see (#4273): a tool with no signed-in
|
|
1046
1057
|
// connection holds no mint_token, and only a person's sign-in mends it.
|
|
1047
1058
|
noConnectionLine(origin),
|
|
1048
1059
|
`${CHALLENGE_LINE_HEAD}${pending.challenge}`,
|
|
1049
1060
|
CALL_LINE_HEAD,
|
|
1050
1061
|
...mintCallMembers(kind, pending.challenge, label).map(([name, value]) => ` ${name}: ${JSON.stringify(value)}`),
|
|
1051
|
-
kind === "authorized computer" ? `The line keeps the token it mints in this computer's credential file and revokes the one this computer held there before. The token is account-scoped and works from any computer until it expires or revoke_token ends it, and list_tokens shows it by its label. The request is pending until ${pending.expires_at}: ${firstActNamed(kind, origin)} run again before then prints this challenge again.` : `It answers a ${TOKEN_SUBCOMMAND} ${TOKEN_CLAIM_ACT} line, which is yours to run in a terminal outside the AI coding tool, on this computer as this user, and never through the tool
|
|
1062
|
+
kind === "authorized computer" ? `The line it answers holds no code. It keeps the token it mints in this computer's credential file and revokes the one this computer held there before. The token is account-scoped and works from any computer until it expires or revoke_token ends it, and list_tokens shows it by its label. The request is pending until ${pending.expires_at}: ${firstActNamed(kind, origin)} run again before then prints this challenge again.` : `It answers a ${TOKEN_SUBCOMMAND} ${TOKEN_CLAIM_ACT} line, which is yours to run in a terminal outside the AI coding tool, on this computer as this user, and never through the tool. That line prints the token's value once to standard output, so pipe it into your secret store's command where the token is a job's. The request is pending until ${pending.expires_at}: ${firstActNamed(kind, origin)} run again before then prints this challenge again.`,
|
|
1052
1063
|
...kind === "authorized computer" && loginLines.length > 0 ? [loginWaySentence(loginLines)] : []
|
|
1053
1064
|
];
|
|
1054
1065
|
var reusedRequestSentence = (origin, pending) => `A request for ${origin} is pending until ${pending.expires_at}, so its challenge is printed again and no new verifier was made.`;
|
|
1055
|
-
var replacedRequestSentence = (origin, expired2) => `The request pending for ${origin} expired at ${expired2.expires_at}, so a new one replaces it
|
|
1066
|
+
var replacedRequestSentence = (origin, expired2) => `The request pending for ${origin} expired at ${expired2.expires_at}, so a new one replaces it. A line minted against the old challenge is refused at its exchange, and a ${MINT_TOKEN_ACTION} call naming the new challenge below answers a fresh one.`;
|
|
1056
1067
|
async function firstAct(io2, origin, kind, say, opener = null) {
|
|
1057
1068
|
const kept2 = keepPendingRequest(io2, origin, say);
|
|
1058
1069
|
if ("status" in kept2) return kept2.status;
|
|
@@ -1115,6 +1126,7 @@ function keepPendingRequest(io2, origin, say) {
|
|
|
1115
1126
|
var TOKEN_CALL_BOUND_SECONDS = 60;
|
|
1116
1127
|
var EXCHANGE_ACT = "exchange";
|
|
1117
1128
|
var exchangeBodyOf = (verifier) => ({ [CODE_VERIFIER_MEMBER]: verifier });
|
|
1129
|
+
var heldExchangeBodyOf = (mint) => ({ [MINT_MEMBER]: mint });
|
|
1118
1130
|
var revocationBodyOf = (id) => ({ id });
|
|
1119
1131
|
var postAction = async (io2, origin, action, bearer, userAgent, body) => {
|
|
1120
1132
|
const controller = new AbortController();
|
|
@@ -1171,7 +1183,7 @@ var mintedTokenOf = (answer) => {
|
|
|
1171
1183
|
};
|
|
1172
1184
|
var mintAgainSentence = (kind, origin = DEFAULT_COMMAND_ORIGIN, login2 = null) => login2 !== null ? `Run ${login2} again to log this computer in.` : `Call ${MINT_TOKEN_ACTION} from your tool again with the challenge ${firstActNamed(kind, origin)} prints, and run the line it answers.`;
|
|
1173
1185
|
var exchangeUnknownSentence = (kind, origin = DEFAULT_COMMAND_ORIGIN, login2 = null) => `Whether a token was minted is unknown: list_tokens from your tool shows each token by its label, and revoke_token ends one nothing holds. ${mintAgainSentence(kind, origin, login2)}`;
|
|
1174
|
-
var noPendingSentence = (origin, kind) => `No request is pending for ${origin} on this computer, so the token code was not presented and nothing was sent: the verifier it needs is kept only on the computer whose ${firstActNamed(kind)} printed its challenge. Run ${firstActNamed(kind, origin)} on this computer, call ${MINT_TOKEN_ACTION} from your tool with the challenge it prints, and run the line it answers.`;
|
|
1186
|
+
var noPendingSentence = (origin, kind, presented = "code") => `No request is pending for ${origin} on this computer, so ${presented === "mint" ? "nothing was sent: the verifier the mint needs" : "the token code was not presented and nothing was sent: the verifier it needs"} is kept only on the computer whose ${firstActNamed(kind)} printed its challenge. Run ${firstActNamed(kind, origin)} on this computer, call ${MINT_TOKEN_ACTION} from your tool with the challenge it prints, and run the line it answers.`;
|
|
1175
1187
|
var markerDisagreesSentence = (kind, origin = DEFAULT_COMMAND_ORIGIN) => kind === "authorized computer" ? `The exchange minted a token that is not an authorized computer's, and ${AUTHORIZE_SUBCOMMAND} keeps only an authorized computer's, so nothing of its value was printed or kept. Call ${MINT_TOKEN_ACTION} from your tool again naming ${AUTHORIZED_COMPUTER_MEMBER}: true, as ${AUTHORIZE_SUBCOMMAND} prints it, and run the line it answers.` : `The exchange minted an authorized computer's token, and ${TOKEN_SUBCOMMAND} ${TOKEN_CLAIM_ACT} prints only a token that is not one, so nothing of its value was printed or kept. Call ${MINT_TOKEN_ACTION} from your tool again without ${AUTHORIZED_COMPUTER_MEMBER}, as ${TOKEN_SUBCOMMAND} ${TOKEN_REQUEST_ACT} prints it, and run the line it answers; to authorize this computer, run ${AUTHORIZE_SUBCOMMAND}${originWordsOf(origin)}.`;
|
|
1176
1188
|
var mintedRevocationLines = (revocation, minted) => "lines" in revocation ? [...revocation.lines, standsSentence(`The token it minted, ${minted.id},`, minted.id, minted.expiresAt)] : [`The token it minted, ${minted.id}, is revoked.`];
|
|
1177
1189
|
var authorizedLineOf = (origin, entry, account = null) => `${AUTHORIZED_LINE_HEAD}${origin}${account !== null ? ` as ${accountWordsOf(account)}` : ""}, label ${entry.label !== null ? JSON.stringify(entry.label) : "none"}, its token expiring ${entry.expires_at !== null ? `at ${entry.expires_at}` : "never"}`;
|
|
@@ -1202,17 +1214,19 @@ async function readAccountUnder(io2, origin, token2, userAgent) {
|
|
|
1202
1214
|
var whoamiLineFor = (io2, origin) => printedLineOf(io2, commandLineOf(origin, WHOAMI_SUBCOMMAND, origin !== DEFAULT_COMMAND_ORIGIN ? { origin } : {}), origin);
|
|
1203
1215
|
var accountUnreadSentence = (whoami2) => `The account this computer's token acts as was not read, and the token is kept: run ${whoami2} to read it.`;
|
|
1204
1216
|
var CLAIMED_LINE_HEAD = "claimed: ";
|
|
1205
|
-
async function completeAct(io2, origin, kind,
|
|
1217
|
+
async function completeAct(io2, origin, kind, presentation, userAgent, say, deliver = () => void 0, login2 = null) {
|
|
1218
|
+
const held = "mint" in presentation;
|
|
1219
|
+
const unread = held ? MINT_UNPRESENTED : TOKEN_CODE_UNREAD;
|
|
1206
1220
|
const place = credentialFileOf(io2);
|
|
1207
1221
|
const read2 = readMovingCredentialFile(io2, place, say);
|
|
1208
1222
|
if (!("state" in read2)) {
|
|
1209
|
-
say(`${readRefusalOf(read2, place.path)} ${
|
|
1223
|
+
say(`${readRefusalOf(read2, place.path)} ${unread}`);
|
|
1210
1224
|
return COMMAND_STATUS.nothing_started;
|
|
1211
1225
|
}
|
|
1212
1226
|
hideStandingTokens(io2, read2.state);
|
|
1213
1227
|
const pending = read2.state.pending.get(origin);
|
|
1214
1228
|
if (pending === void 0) {
|
|
1215
|
-
say(noPendingSentence(origin, kind));
|
|
1229
|
+
say(noPendingSentence(origin, kind, held ? "mint" : "code"));
|
|
1216
1230
|
return COMMAND_STATUS.nothing_started;
|
|
1217
1231
|
}
|
|
1218
1232
|
io2.hide(pending.verifier, REDACTED_VERIFIER);
|
|
@@ -1222,25 +1236,25 @@ async function completeAct(io2, origin, kind, code, userAgent, say, deliver = ()
|
|
|
1222
1236
|
const release = io2.whenEnded(() => {
|
|
1223
1237
|
stage.ended = true;
|
|
1224
1238
|
say(
|
|
1225
|
-
stage.now === "unsent" ? `The run was ended before its exchange. ${
|
|
1239
|
+
stage.now === "unsent" ? `The run was ended before its exchange. ${unread}` : stage.now === "sent" ? `The run was ended during its exchange. ${exchangeUnknownSentence(kind, origin, login2)}` : `The run was ended after its exchange minted a token, which may not have been ${kind === "authorized computer" ? "kept" : "printed"}: list_tokens from your tool shows it by its label, and revoke_token ends it.`
|
|
1226
1240
|
);
|
|
1227
1241
|
dropCredentialWriting(io2, place, tag);
|
|
1228
1242
|
});
|
|
1229
1243
|
try {
|
|
1230
1244
|
const prepared = prepareCredentialFile(io2, place, tag);
|
|
1231
1245
|
if ("unwritten" in prepared) {
|
|
1232
|
-
say(`${unwritableFileSentence(place.path, prepared.unwritten)} Nothing was sent. ${
|
|
1246
|
+
say(`${unwritableFileSentence(place.path, prepared.unwritten)} Nothing was sent. ${unread}`);
|
|
1233
1247
|
return COMMAND_STATUS.nothing_started;
|
|
1234
1248
|
}
|
|
1235
1249
|
if (stage.ended) return COMMAND_STATUS.nothing_started;
|
|
1236
|
-
const older = await planeWireRefusal(io2, origin, TOKEN_CODE_UNREAD_FIRST);
|
|
1250
|
+
const older = await planeWireRefusal(io2, origin, held ? "" : TOKEN_CODE_UNREAD_FIRST);
|
|
1237
1251
|
if (stage.ended) return COMMAND_STATUS.nothing_started;
|
|
1238
1252
|
if (older !== null) {
|
|
1239
1253
|
say(older);
|
|
1240
1254
|
return COMMAND_STATUS.nothing_started;
|
|
1241
1255
|
}
|
|
1242
1256
|
stage.now = "sent";
|
|
1243
|
-
const answered = await postAction(io2, origin, MINT_TOKEN_ACTION, code, userAgent, exchangeBodyOf(pending.verifier));
|
|
1257
|
+
const answered = "mint" in presentation ? await postAction(io2, origin, MINT_TOKEN_ACTION, pending.verifier, userAgent, heldExchangeBodyOf(presentation.mint)) : await postAction(io2, origin, MINT_TOKEN_ACTION, presentation.code, userAgent, exchangeBodyOf(pending.verifier));
|
|
1244
1258
|
if (stage.ended) {
|
|
1245
1259
|
const late = answered === null ? null : planeObjectOf(answered.text);
|
|
1246
1260
|
if (late !== null && typeof late.value === "string") io2.hide(late.value, REDACTED_TOKEN);
|
|
@@ -1253,7 +1267,7 @@ async function completeAct(io2, origin, kind, code, userAgent, say, deliver = ()
|
|
|
1253
1267
|
return COMMAND_STATUS.unknown;
|
|
1254
1268
|
}
|
|
1255
1269
|
if (answered.redirected) {
|
|
1256
|
-
say(`The exchange was answered ${answeredHow(answered.status)}, which the command never follows, so the code and the verifier were sent to no second address.`);
|
|
1270
|
+
say(`The exchange was answered ${answeredHow(answered.status)}, which the command never follows, so ${held ? "the verifier was" : "the code and the verifier were"} sent to no second address.`);
|
|
1257
1271
|
say(unknown);
|
|
1258
1272
|
return COMMAND_STATUS.unknown;
|
|
1259
1273
|
}
|
|
@@ -1272,7 +1286,9 @@ async function completeAct(io2, origin, kind, code, userAgent, say, deliver = ()
|
|
|
1272
1286
|
return COMMAND_STATUS.unknown;
|
|
1273
1287
|
}
|
|
1274
1288
|
for (const line of refusalLines(EXCHANGE_ACT, answered.status, refusal)) say(line);
|
|
1275
|
-
say(
|
|
1289
|
+
say(
|
|
1290
|
+
answered.status === 429 ? `Nothing was minted, and ${held ? "nothing was spent" : "the token code was not spent"}: run the line again after the wait the answer names.` : `Nothing was minted. ${mintAgainSentence(kind, origin, login2)}`
|
|
1291
|
+
);
|
|
1276
1292
|
return COMMAND_STATUS.nothing_started;
|
|
1277
1293
|
}
|
|
1278
1294
|
const minted = answer === null ? null : mintedTokenOf(answer);
|
|
@@ -1344,29 +1360,45 @@ var AUTHORIZE_OPTIONS = Object.freeze([
|
|
|
1344
1360
|
value: "<origin>",
|
|
1345
1361
|
required: false,
|
|
1346
1362
|
description: `The platform's address this computer is authorized at. Where absent, the address ${ORIGIN_VARIABLE} holds, or ${DEFAULT_COMMAND_ORIGIN} where it is unset. A ${DEPLOY_OPTION.origin} the line names must be ${DEFAULT_COMMAND_ORIGIN}, ${DEVELOPMENT_COMMAND_ORIGIN}, or the address ${ORIGIN_VARIABLE} holds, an address of this machine only where ${ORIGIN_VARIABLE} names it.`
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
name: MINT_OPTION,
|
|
1366
|
+
value: "<id>",
|
|
1367
|
+
required: false,
|
|
1368
|
+
description: `The mint the line names, by its id, a lowercase UUID, as the line a ${MINT_TOKEN_ACTION} call answers names it. The id is no credential. Under it, ${AUTHORIZE_SUBCOMMAND} reads nothing on standard input and presents this computer's verifier for that mint.`
|
|
1347
1369
|
}
|
|
1348
1370
|
]);
|
|
1371
|
+
var MINT_ID_REFUSAL = `${MINT_OPTION} names the mint by its id, a lowercase UUID, as the line the ${MINT_TOKEN_ACTION} call answered names it: run that line as given. ${NOTHING_SENT}`;
|
|
1349
1372
|
var AUTHORIZE_CREDENTIAL = Object.freeze({
|
|
1350
1373
|
grant: "token code",
|
|
1351
1374
|
aGrant: "a token code",
|
|
1352
1375
|
call: `the ${MINT_TOKEN_ACTION} call`,
|
|
1353
1376
|
mintedToken: false,
|
|
1354
|
-
|
|
1377
|
+
pipes: `where the line pipes one in, as the line of a ${MINT_TOKEN_ACTION} call that sets no completion does`,
|
|
1378
|
+
bare: `with nothing piped and no ${MINT_OPTION}, it keeps a verifier and prints the challenge and the ${MINT_TOKEN_ACTION} call that authorize this computer. That call answers ${AUTHORIZE_SUBCOMMAND} ${MINT_OPTION} <id>, a line that holds no code: under it, ${AUTHORIZE_SUBCOMMAND} reads nothing on standard input and presents the verifier for that mint`
|
|
1355
1379
|
});
|
|
1356
|
-
var AUTHORIZATION_ORIGIN_VARIABLE_LINE = `the platform's address the authorization reaches where the line names no ${DEPLOY_OPTION.origin}
|
|
1380
|
+
var AUTHORIZATION_ORIGIN_VARIABLE_LINE = `the platform's address the authorization reaches where the line names no ${DEPLOY_OPTION.origin}. It is the one address beside ${DEFAULT_COMMAND_ORIGIN} and ${DEVELOPMENT_COMMAND_ORIGIN} a ${DEPLOY_OPTION.origin} may name, an address of this machine only where it names it.`;
|
|
1357
1381
|
var CREDENTIAL_FILE_WORDS = `${CREDENTIAL_FILE_NAME} in a folder ${CREDENTIAL_FOLDER_NAME} under ${CONFIG_HOME_VARIABLE}, or under .config in your home folder where that is unset, readable by you alone. On Windows it is in a folder ${WINDOWS_CREDENTIAL_FOLDER_NAME} under your profile folder, ${PROFILE_VARIABLE}. A file under ${LOCAL_APP_DATA_VARIABLE} from an older version is moved there once`;
|
|
1358
|
-
var AUTHORIZE_AFTER_OWN_ERROR = "a token code the line carried may not have been presented
|
|
1382
|
+
var AUTHORIZE_AFTER_OWN_ERROR = "a token code the line carried may not have been presented. That code is live until it expires, and mints nothing without this computer's verifier. Where a token was minted, list_tokens from your tool shows it by its label, and revoke_token ends it";
|
|
1383
|
+
var MINT_AFTER_OWN_ERROR = "the verifier may not have been presented. Where a token was minted, list_tokens from your tool shows it by its label, and revoke_token ends it";
|
|
1359
1384
|
async function authorize(call, io2) {
|
|
1385
|
+
const mint = call.named[MINT_OPTION];
|
|
1386
|
+
if (mint !== void 0 && !MINT_ID_FORM.test(mint)) {
|
|
1387
|
+
io2.print(MINT_ID_REFUSAL);
|
|
1388
|
+
return COMMAND_STATUS.nothing_started;
|
|
1389
|
+
}
|
|
1390
|
+
const unread = mint !== void 0 ? MINT_UNPRESENTED : TOKEN_CODE_UNREAD;
|
|
1360
1391
|
const origin = authorizationOriginOf(io2.env, call.named[DEPLOY_OPTION.origin], NOTHING_SENT);
|
|
1361
1392
|
if ("refusal" in origin) {
|
|
1362
|
-
io2.print(`${origin.refusal} ${
|
|
1393
|
+
io2.print(`${origin.refusal} ${unread}`);
|
|
1363
1394
|
return COMMAND_STATUS.nothing_started;
|
|
1364
1395
|
}
|
|
1365
1396
|
const agent = userAgentOf(io2.env);
|
|
1366
1397
|
if ("refusal" in agent) {
|
|
1367
|
-
io2.print(`${agent.refusal} ${
|
|
1398
|
+
io2.print(`${agent.refusal} ${unread}`);
|
|
1368
1399
|
return COMMAND_STATUS.nothing_started;
|
|
1369
1400
|
}
|
|
1401
|
+
if (mint !== void 0) return completeAct(io2, origin.origin, "authorized computer", { mint }, agent.userAgent, io2.print);
|
|
1370
1402
|
const read2 = await readTokenCode(io2, "authorized computer", origin.origin);
|
|
1371
1403
|
if ("refusal" in read2) {
|
|
1372
1404
|
io2.print(read2.refusal);
|
|
@@ -1379,11 +1411,11 @@ async function authorize(call, io2) {
|
|
|
1379
1411
|
return COMMAND_STATUS.nothing_started;
|
|
1380
1412
|
}
|
|
1381
1413
|
io2.hide(code, REDACTED_TOKEN_CODE);
|
|
1382
|
-
return completeAct(io2, origin.origin, "authorized computer", code, agent.userAgent, io2.print);
|
|
1414
|
+
return completeAct(io2, origin.origin, "authorized computer", { code }, agent.userAgent, io2.print);
|
|
1383
1415
|
}
|
|
1384
1416
|
var AUTHORIZE_ROW = Object.freeze({
|
|
1385
1417
|
name: AUTHORIZE_SUBCOMMAND,
|
|
1386
|
-
summary: `Authorizes this computer at the platform's address, in two acts around one ${MINT_TOKEN_ACTION} call your tool makes. Run with nothing piped, it keeps a verifier in this computer's credential file, and prints the challenge and the call. Run
|
|
1418
|
+
summary: `Authorizes this computer at the platform's address, in two acts around one ${MINT_TOKEN_ACTION} call your tool makes. Run with nothing piped, it keeps a verifier in this computer's credential file, and prints the challenge and the call. Run as the line the call answers, with ${MINT_OPTION} and no code, or with a piped token code, it presents the verifier. It then keeps the minted token in that file, and prints authorized:. It prints no token, code, or verifier.`,
|
|
1387
1419
|
example: AUTHORIZE_SUBCOMMAND,
|
|
1388
1420
|
options: AUTHORIZE_OPTIONS,
|
|
1389
1421
|
credential: AUTHORIZE_CREDENTIAL,
|
|
@@ -1391,14 +1423,14 @@ var AUTHORIZE_ROW = Object.freeze({
|
|
|
1391
1423
|
done: "the challenge printed, or this computer authorized",
|
|
1392
1424
|
failed: "a token was minted and not kept, or the token this computer held before was not revoked: the printed lines name revoke_token",
|
|
1393
1425
|
unknown: "the exchange drew no answer, or an answer that is not a refusal: list_tokens shows whether a token was minted",
|
|
1394
|
-
nothing_started:
|
|
1426
|
+
nothing_started: `nothing was minted. An option, the mint's id, the credential file, or the token code was refused. Or no request was pending, the platform was too old, or the exchange was refused`
|
|
1395
1427
|
},
|
|
1396
1428
|
variables: [[ORIGIN_VARIABLE, AUTHORIZATION_ORIGIN_VARIABLE_LINE]],
|
|
1397
1429
|
note: [
|
|
1398
|
-
`Authorizing this computer takes three acts once for each platform address, which your signed-in tool makes itself and then reports to you; login, run in a terminal of your own, is your own way. ${AUTHORIZE_SUBCOMMAND} run with nothing piped prints a challenge and the ${MINT_TOKEN_ACTION} call to make
|
|
1430
|
+
`Authorizing this computer takes three acts once for each platform address, which your signed-in tool makes itself and then reports to you; login, run in a terminal of your own, is your own way. ${AUTHORIZE_SUBCOMMAND} run with nothing piped prints a challenge and the ${MINT_TOKEN_ACTION} call to make. Your tool makes that call through the connection you signed in with, and runs the line it answers once, as given, on this computer. That line holds no code, and it keeps the token.`,
|
|
1399
1431
|
`The token is account-scoped and works from any computer until it expires or revoke_token ends it: keep it as you would a password, list_tokens shows it by this computer's host name, and logout ends it. The credential file is ${CREDENTIAL_FILE_WORDS}.`
|
|
1400
1432
|
],
|
|
1401
|
-
afterOwnError: AUTHORIZE_AFTER_OWN_ERROR,
|
|
1433
|
+
afterOwnError: (call) => call.named[MINT_OPTION] !== void 0 ? MINT_AFTER_OWN_ERROR : AUTHORIZE_AFTER_OWN_ERROR,
|
|
1402
1434
|
run: authorize
|
|
1403
1435
|
});
|
|
1404
1436
|
|
|
@@ -1865,11 +1897,11 @@ var codeOf = (error) => {
|
|
|
1865
1897
|
return typeof code === "string" && /^[A-Z0-9_]+$/.test(code) ? code : "unwritable";
|
|
1866
1898
|
};
|
|
1867
1899
|
var addressable = (entry) => ![entry.area, ...entry.name.split(/[\\/]/)].some((segment) => segment === "." || segment === "..");
|
|
1868
|
-
var applicationFolderSentence = () => `The folder the line runs in holds package.json, so it is an application's folder, and the line names no ${EXPORT_OPTION.path}
|
|
1900
|
+
var applicationFolderSentence = () => `The folder the line runs in holds package.json, so it is an application's folder, and the line names no ${EXPORT_OPTION.path}. The export would be written inside it, where a deploy from that folder would carry every file of the export. Run the line again with ${EXPORT_OPTION.path} naming a folder outside this one, which the same grant serves until it expires. ${NOTHING_WRITTEN}`;
|
|
1869
1901
|
var ABSENT_FILE_STATUS = 404;
|
|
1870
1902
|
var ABSENT_FILE_ERROR = "no_such_file";
|
|
1871
1903
|
var freshExportLine = (absent) => `${absent === 1 ? "A file the manifest names was" : `${absent} files the manifest names were`} answered as an absent name and ${absent === 1 ? "was" : "were"} not read. A fresh export has the application's files as they now stand: call request_export from your tool, then ${DOWNLOAD_GRANT_ACTION} once read_export answers it completed.`;
|
|
1872
|
-
var runAgainLine = (again) => `Run the line again before its grant expires, or take a fresh one
|
|
1904
|
+
var runAgainLine = (again) => `Run the line again before its grant expires, or take a fresh one. ${again} It skips the files on disk and reads again each file whose read failed.`;
|
|
1873
1905
|
var MANIFEST_READ = "The manifest read";
|
|
1874
1906
|
var fileReadOf = (natural) => `The read of ${shownWhole(natural)}`;
|
|
1875
1907
|
var APPLICATION_MARK = "package.json";
|
|
@@ -1917,7 +1949,7 @@ async function exportDownload(call, io2) {
|
|
|
1917
1949
|
}
|
|
1918
1950
|
if (earlier === null) {
|
|
1919
1951
|
io2.print(
|
|
1920
|
-
`${called} holds files and no index of this export, so the command writes nothing into it
|
|
1952
|
+
`${called} holds files and no index of this export, so the command writes nothing into it. Name a new or empty folder with ${EXPORT_OPTION.path}, or the folder an earlier run of this export's line wrote. ${NOTHING_WRITTEN}`
|
|
1921
1953
|
);
|
|
1922
1954
|
return COMMAND_STATUS.nothing_started;
|
|
1923
1955
|
}
|
|
@@ -1986,7 +2018,7 @@ async function exportDownload(call, io2) {
|
|
|
1986
2018
|
if (!(manifestRead instanceof Response)) {
|
|
1987
2019
|
if (manifestRead.kind === "ended") for (const line of manifestRead.lines) io2.print(line);
|
|
1988
2020
|
else io2.print(`The export's manifest could not be read: ${manifestRead.kind === "failed" ? manifestRead.reason : "the read was capped"}.`);
|
|
1989
|
-
io2.print(`${manifestRead.kind === "ended" ? again : `Call read_export from your tool with application ${options.application} and export ${options.export} to see whether the export completed
|
|
2021
|
+
io2.print(`${manifestRead.kind === "ended" ? again : `Call read_export from your tool with application ${options.application} and export ${options.export} to see whether the export completed. Then ${again.charAt(0).toLowerCase()}${again.slice(1)}`} ${NOTHING_WRITTEN}`);
|
|
1990
2022
|
return COMMAND_STATUS.nothing_started;
|
|
1991
2023
|
}
|
|
1992
2024
|
let manifestBytes;
|
|
@@ -2866,9 +2898,9 @@ function* readArtifactSteps(bytes) {
|
|
|
2866
2898
|
const unreadable = () => new DeployRefusal(
|
|
2867
2899
|
400,
|
|
2868
2900
|
"artifact_unreadable",
|
|
2869
|
-
"The artifact's bytes do not read as a zip archive. The platform deploys a zip holding the application's package.json at its root: zip the project folder's contents and deploy again
|
|
2901
|
+
"The artifact's bytes do not read as a zip archive. The platform deploys a zip holding the application's package.json at its root: zip the project folder's contents and deploy again."
|
|
2870
2902
|
);
|
|
2871
|
-
const layoutRefusal = (detail) => new DeployRefusal(400, "artifact_layout_invalid", `${detail}
|
|
2903
|
+
const layoutRefusal = (detail) => new DeployRefusal(400, "artifact_layout_invalid", `${detail}.`);
|
|
2872
2904
|
let declaredBytes = 0;
|
|
2873
2905
|
let listing;
|
|
2874
2906
|
try {
|
|
@@ -2909,7 +2941,7 @@ function* readArtifactSteps(bytes) {
|
|
|
2909
2941
|
texts.set(index, inflateZipRecord(bytes, records[index]).toString("utf8"));
|
|
2910
2942
|
} catch (error) {
|
|
2911
2943
|
if (!(error instanceof ZipUnreadable)) throw error;
|
|
2912
|
-
throw new DeployRefusal(400, "artifact_unreadable", `The zip's ${error.message}: zip the application's folder again, and deploy again
|
|
2944
|
+
throw new DeployRefusal(400, "artifact_unreadable", `The zip's ${error.message}: zip the application's folder again, and deploy again.`);
|
|
2913
2945
|
}
|
|
2914
2946
|
yield;
|
|
2915
2947
|
}
|
|
@@ -2991,14 +3023,14 @@ function unbuiltBinding(reading) {
|
|
|
2991
3023
|
}
|
|
2992
3024
|
function artifactLayoutOf(reading) {
|
|
2993
3025
|
const refuse = (detail) => {
|
|
2994
|
-
throw new DeployRefusal(400, "artifact_layout_invalid", `${detail}
|
|
3026
|
+
throw new DeployRefusal(400, "artifact_layout_invalid", `${detail}.`);
|
|
2995
3027
|
};
|
|
2996
3028
|
const unbuilt = unbuiltBinding(reading);
|
|
2997
3029
|
if (unbuilt !== null) {
|
|
2998
3030
|
const where = unbuilt.root ? "is the root binding.gyp, and the zip holds no .node file outside node_modules and every workspace member's folder" : "is a binding.gyp of the artifact's own, beside a workspace member's package.json, and that member's folder, less its nested members' folders, holds no .node file";
|
|
2999
3031
|
const ship = unbuilt.root ? "ship its .node file in the zip" : "ship its .node file inside the member's folder";
|
|
3000
3032
|
refuse(
|
|
3001
|
-
`The zip's entry ${shownEntry(unbuilt.entry)} ${where}. A native module of the artifact's own must ship prebuilt, because the platform never builds the customer's code
|
|
3033
|
+
`The zip's entry ${shownEntry(unbuilt.entry)} ${where}. A native module of the artifact's own must ship prebuilt, because the platform never builds the customer's code: build it for Linux on your machine, ${ship}, and deploy again`
|
|
3002
3034
|
);
|
|
3003
3035
|
}
|
|
3004
3036
|
if (reading.oversizedRoot !== null) {
|
|
@@ -3037,7 +3069,7 @@ var nestingDeeperThan = (value, max) => {
|
|
|
3037
3069
|
function manifestCopyDepthRefusal(reading) {
|
|
3038
3070
|
if (reading.manifestJson === null) return;
|
|
3039
3071
|
if (nestingDeeperThan(parsePackageJson(reading.manifestJson), MANIFEST_COPY_DEPTH_MAX)) {
|
|
3040
|
-
throw new DeployRefusal(400, "artifact_layout_invalid", `The zip's manifest.json nests deeper than the ${MANIFEST_COPY_DEPTH_MAX} levels the deploy reads: the platform reads the manifest submit_manifest recorded, so flatten the zip's copy or leave it out, and deploy again
|
|
3072
|
+
throw new DeployRefusal(400, "artifact_layout_invalid", `The zip's manifest.json nests deeper than the ${MANIFEST_COPY_DEPTH_MAX} levels the deploy reads: the platform reads the manifest submit_manifest recorded, so flatten the zip's copy or leave it out, and deploy again.`);
|
|
3041
3073
|
}
|
|
3042
3074
|
}
|
|
3043
3075
|
function clientReadingOf(records) {
|
|
@@ -3057,7 +3089,7 @@ function clientReadingOf(records) {
|
|
|
3057
3089
|
return { admitted, skipped };
|
|
3058
3090
|
}
|
|
3059
3091
|
function clientBoundRefusal(reading) {
|
|
3060
|
-
const cite = "
|
|
3092
|
+
const cite = ".";
|
|
3061
3093
|
if (reading.admitted.size > CLIENT_FILES_MAX) {
|
|
3062
3094
|
return `The zip's client/ folder holds ${reading.admitted.size} files the publish admits, more than the ${CLIENT_FILES_MAX} files one version publishes: ship fewer client files, and deploy again${cite}`;
|
|
3063
3095
|
}
|
|
@@ -3160,6 +3192,8 @@ var folderBindingOf = (io2, path) => {
|
|
|
3160
3192
|
const application = members[APPLICATION_MEMBER];
|
|
3161
3193
|
return typeof application === "string" && UPLOAD_ID.test(application) ? { kind: "bound", application } : { kind: "off_form" };
|
|
3162
3194
|
};
|
|
3195
|
+
var BINDING_LINE_HEAD = "binding: ";
|
|
3196
|
+
var bindingLineOf = (application) => `${BINDING_LINE_HEAD}${FOLDER_MANIFEST} gets application ${application}, so later deploy and check lines naming another application are refused`;
|
|
3163
3197
|
var BOUND_LINE_HEAD = "bound: ";
|
|
3164
3198
|
var boundLineOf = (application) => `${BOUND_LINE_HEAD}${FOLDER_MANIFEST} now names application ${application}; a deploy or check line naming another application is refused`;
|
|
3165
3199
|
var BIND_WRITE_FAILED_LINE = `${FOLDER_MANIFEST} could not be written to bind this folder to its application, so the deploy goes on unbound, and the next deploy writes it again.`;
|
|
@@ -3566,6 +3600,7 @@ function zipFolder(root, seams = {}) {
|
|
|
3566
3600
|
// src/turnzero_cloud_command/deploy.ts
|
|
3567
3601
|
var ZIP_LINE_HEAD = "zip: ";
|
|
3568
3602
|
var PREPARED_LINE_HEAD = DEPLOY_PREPARED_LINE_HEAD;
|
|
3603
|
+
var UPLOADING_LINE_HEAD = "uploading: ";
|
|
3569
3604
|
var UPLOADED_LINE_HEAD = "uploaded: ";
|
|
3570
3605
|
var STARTED_LINE_HEAD = "started: ";
|
|
3571
3606
|
var WAITING_LINE_HEAD = "waiting: ";
|
|
@@ -3584,7 +3619,7 @@ var deployOptionsOf = (named, cwd, flags = /* @__PURE__ */ new Set()) => {
|
|
|
3584
3619
|
const found = COMMAND_ENVIRONMENTS.find((name) => name === env);
|
|
3585
3620
|
if (found === void 0) {
|
|
3586
3621
|
return {
|
|
3587
|
-
refusal: `${DEPLOY_OPTION.env} names the environment to deploy to, ${COMMAND_ENVIRONMENTS.join(" or ")}
|
|
3622
|
+
refusal: `${DEPLOY_OPTION.env} names the environment to deploy to, ${COMMAND_ENVIRONMENTS.join(" or ")}. A line that names none deploys to the environment the platform chooses. Nothing was started.`
|
|
3588
3623
|
};
|
|
3589
3624
|
}
|
|
3590
3625
|
environment = found;
|
|
@@ -3609,9 +3644,9 @@ var DEPLOY_CREDENTIAL = Object.freeze({
|
|
|
3609
3644
|
call: "the deploy call",
|
|
3610
3645
|
mintedToken: true,
|
|
3611
3646
|
piped: false,
|
|
3612
|
-
places:
|
|
3647
|
+
places: NO_INPUT_PLACES
|
|
3613
3648
|
});
|
|
3614
|
-
var DEPLOY_READS = `no standard input
|
|
3649
|
+
var DEPLOY_READS = `no standard input. It reads a minted token from ${MINTED_TOKEN_VARIABLE} where that is set. Otherwise it reads this computer's token for the platform's address from its credential file, which ${LOGIN_SUBCOMMAND} and ${AUTHORIZE_SUBCOMMAND} write, and sends it only to that address. With neither, it prints the variable route first, then the login route where the address is admitted`;
|
|
3615
3650
|
var DEPLOY_AFTER_LOGIN = `Once login has printed authorized:, run this deploy line again. ${NOTHING_SENT_OR_STARTED}`;
|
|
3616
3651
|
var PREPARE_ACT = "prepare";
|
|
3617
3652
|
var PREPARE_AGAIN = "Nothing was started. Run the line again: it prepares a fresh upload, which replaces any upload this run prepared.";
|
|
@@ -3848,6 +3883,7 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3848
3883
|
}
|
|
3849
3884
|
io2.print(preparedLineOf(prepared));
|
|
3850
3885
|
if (binding?.kind === "unbound" && binding.manifest !== null) {
|
|
3886
|
+
io2.print(bindingLineOf(options.application));
|
|
3851
3887
|
io2.print(writeManifestMember(io2, binding.manifest, APPLICATION_MEMBER, options.application) ? boundLineOf(options.application) : BIND_WRITE_FAILED_LINE);
|
|
3852
3888
|
}
|
|
3853
3889
|
const { upload, grant, environment } = prepared;
|
|
@@ -3855,6 +3891,7 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3855
3891
|
const stop = commandStopSentence(options.application, environment);
|
|
3856
3892
|
const uploadNoAnswer = uploadNoAnswerSentence(options.application, environment);
|
|
3857
3893
|
const address = storageFileAddress(origin, deployAreaName(options.application), uploadFileName(upload));
|
|
3894
|
+
io2.print(`${UPLOADING_LINE_HEAD}${zipped.zip.length} bytes as upload ${upload}`);
|
|
3858
3895
|
let uploaded;
|
|
3859
3896
|
try {
|
|
3860
3897
|
uploaded = await writeAgain(
|
|
@@ -3984,7 +4021,7 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3984
4021
|
io2.print(stop);
|
|
3985
4022
|
return PROGRESS_EXIT_STATUS.refused;
|
|
3986
4023
|
}
|
|
3987
|
-
var DEPLOY_AFTER_OWN_ERROR = "the deploy may or may not have started
|
|
4024
|
+
var DEPLOY_AFTER_OWN_ERROR = "the deploy may or may not have started. Call read_status from your tool first, make the start-only retry a pending upload it answers carries, and run the line again only where it shows neither";
|
|
3988
4025
|
var DEPLOY_ROW = Object.freeze({
|
|
3989
4026
|
name: DEPLOY_SUBCOMMAND,
|
|
3990
4027
|
summary: "Deploys the application folder, or a .zip file. It zips the folder, makes the preparing deploy call, uploads the zip under the upload grant that call answers, starts the deploy, and waits for it, printing each step and the outcome. It runs under this computer's token. A tool connected by a minted token, or a job with no tool, runs it under a minted token. With no credential, it sends nothing and prints the two routes to one: the minted token's variables first, then, where the address is admitted, the login route.",
|
|
@@ -3996,21 +4033,21 @@ var DEPLOY_ROW = Object.freeze({
|
|
|
3996
4033
|
done: `the version deployed, or the deploy started where ${DEPLOY_NO_WAIT_VARIABLE} is set`,
|
|
3997
4034
|
failed: "the deploy failed",
|
|
3998
4035
|
unknown: "the wait stopped, or the start drew no answer: the deploy may go on, and read_status reads it",
|
|
3999
|
-
nothing_started: "an option, credential, folder, or the folder's binding was refused, no credential was read
|
|
4036
|
+
nothing_started: "an option, credential, folder, or the folder's binding was refused, or no credential was read. Or the platform was too old, or no usable upload was answered. Or no zipped file names the health path. Or the upload or start was refused, or the upload drew no answer"
|
|
4000
4037
|
},
|
|
4001
4038
|
variables: [[DEPLOY_NO_WAIT_VARIABLE, "any value returns the command once the deploy has started, without waiting for it."]],
|
|
4002
4039
|
note: [
|
|
4003
|
-
`A deploy is one line, which carries no credential: run the line your tool's deploy call answers, or ${DEPLOY_SUBCOMMAND} ${DEPLOY_OPTION.application} <application id>, from the application's folder. It reads no standard input. On an authorized computer it runs under this computer's token, which ${LOGIN_SUBCOMMAND} or ${AUTHORIZE_SUBCOMMAND} keeps in the credential file for each platform address
|
|
4004
|
-
`On a computer with no token for that address, or one that has expired, it sends nothing, ends 3, and prints two routes, in this order. A tool connected by a minted token, or a job with no tool, such as a CI job's, sets ${MINTED_TOKEN_VARIABLE} from where its token is kept,
|
|
4005
|
-
`Otherwise a person logs this computer in once with ${LOGIN_SUBCOMMAND}, in a terminal of their own, then runs the deploy line again
|
|
4040
|
+
`A deploy is one line, which carries no credential: run the line your tool's deploy call answers, or ${DEPLOY_SUBCOMMAND} ${DEPLOY_OPTION.application} <application id>, from the application's folder. It reads no standard input. On an authorized computer it runs under this computer's token, which ${LOGIN_SUBCOMMAND} or ${AUTHORIZE_SUBCOMMAND} keeps in the credential file for each platform address. It sends that token only to the address it is kept under: ${DEPLOY_OPTION.origin} where the line names one, else the address ${ORIGIN_VARIABLE} holds, else ${DEFAULT_COMMAND_ORIGIN}.`,
|
|
4041
|
+
`On a computer with no token for that address, or one that has expired, it sends nothing, ends 3, and prints two routes, in this order. A tool connected by a minted token, or a job with no tool, such as a CI job's, sets ${MINTED_TOKEN_VARIABLE} from where its token is kept, never by writing the value in a line. It sets ${ORIGIN_VARIABLE} to the address that minted the token where that is not ${DEFAULT_COMMAND_ORIGIN}, then runs the deploy line again. Such a tool cannot use ${MINT_TOKEN_ACTION}, which takes a sign-in alone.`,
|
|
4042
|
+
`Otherwise a person logs this computer in once with ${LOGIN_SUBCOMMAND}, in a terminal of their own, then runs the deploy line again. A folder within the caps of the tool's deploy_files call deploys now through deploy_files, with no login. The credential file is ${CREDENTIAL_FILE_WORDS}.`,
|
|
4006
4043
|
`A tool sets ${MINTED_TOKEN_VARIABLE} for one line at a time and unsets it once that line ends, since the command cannot unset a variable of the shell that started it. A value a job's environment already supplies stays as it stands. A job's token is minted for the application, with an expiry. The variable's token wins over the credential file: the command makes the preparing call under that token, and sends the token in no other request.`,
|
|
4007
|
-
`Under the variable, the command sends its token only to the address ${ORIGIN_VARIABLE} holds, or to ${DEFAULT_COMMAND_ORIGIN} where that variable is unset
|
|
4008
|
-
`A progress read answered 502, 503, or 504, which is often temporary, is made again, at most three more times
|
|
4009
|
-
`After its zip line, ${DEPLOY_SUBCOMMAND} prints the folder's top-level entries the zip holds on one line, at most ten and then the count of the rest
|
|
4010
|
-
`Then it prints the paths the zip left out, a warning for a .zip file at the folder's root, a warning for a file that looks like a key or a credential
|
|
4011
|
-
`After the preparing call,
|
|
4044
|
+
`Under the variable, the command sends its token only to the address ${ORIGIN_VARIABLE} holds, or to ${DEFAULT_COMMAND_ORIGIN} where that variable is unset. Under the token, ${DEPLOY_OPTION.origin} may name that address alone, and where the line names none the command addresses it. ${DEPLOY_OPTION.env} names the environment where a line names one, ${COMMAND_ENVIRONMENTS.join(" or ")}. Where none is named, the platform chooses, and the command deploys to the environment the platform answers.`,
|
|
4045
|
+
`A progress read answered 502, 503, or 504, which is often temporary, is made again, at most three more times. An upload answered 503 credential_lookup_busy, which wrote nothing, is made again after the wait it names, at most twice.`,
|
|
4046
|
+
`After its zip line, ${DEPLOY_SUBCOMMAND} prints the folder's top-level entries the zip holds on one line, at most ten and then the count of the rest. ${HASH_SUBCOMMAND} ${HASH_OPTION.list} names every file. No such line prints for a .zip file.`,
|
|
4047
|
+
`Then it prints the paths the zip left out, a warning for a .zip file at the folder's root, and a warning for a file that looks like a key or a credential. It warns of a health path that no file names, package files and documentation set aside. Each prints on a line of its own, only where there is something to name. The health warning refuses nothing.`,
|
|
4048
|
+
`After the preparing call, the platform's answer may name the recorded manifest's health path and say the environment serves no version. Where no file of the folder's zip names that path, ${DEPLOY_SUBCOMMAND} prints the platform's own refusal, then that nothing was uploaded and nothing was started. It ends 3 with no ${PREPARED_LINE_HEAD.trimEnd()} line, as the start would refuse it. A .zip file the line names is not read for it, and the start's own refusal stands.`,
|
|
4012
4049
|
`Before its zip, ${DEPLOY_SUBCOMMAND} reads the folder's manifest.json. Where its application member names another application than ${DEPLOY_OPTION.application}, or is not an application id, the deploy ends 3 before anything is zipped or sent. To deploy the folder to another application on purpose, the account's owner changes or removes that member.`,
|
|
4013
|
-
`Where the manifest holds no application member, ${DEPLOY_SUBCOMMAND}
|
|
4050
|
+
`Where the manifest holds no application member, ${DEPLOY_SUBCOMMAND} prints a ${BINDING_LINE_HEAD.trimEnd()} line after the ${PREPARED_LINE_HEAD.trimEnd()} line, then writes ${DEPLOY_OPTION.application}'s id into it and prints a ${BOUND_LINE_HEAD.trimEnd()} line. Later deploy and check lines naming another application are then refused. A write that fails prints one line and stops nothing. A .zip file the line names is not read for it.`
|
|
4014
4051
|
],
|
|
4015
4052
|
afterOwnError: DEPLOY_AFTER_OWN_ERROR,
|
|
4016
4053
|
run: (call, io2) => deploy(call.named, io2, call.flags)
|
|
@@ -4023,6 +4060,8 @@ var BINDING_NOTHING_CHECKED = "Nothing further was read, and nothing was sent or
|
|
|
4023
4060
|
var REFUSED_LINE_HEAD = "refused: ";
|
|
4024
4061
|
var MANIFEST_LINE_HEAD = "manifest: ";
|
|
4025
4062
|
var CHECKED_LINE_HEAD = "checked: ";
|
|
4063
|
+
var CHECKING_LINE_HEAD = "checking: ";
|
|
4064
|
+
var checkingLineOf = (application) => `${CHECKING_LINE_HEAD}application ${application} on the platform, then the zip.`;
|
|
4026
4065
|
var CHECK_ACT = "check";
|
|
4027
4066
|
var CHECK_CALL_BOUND_SECONDS = 60;
|
|
4028
4067
|
var CHECK_NO_ANSWER = "The check call drew no answer.";
|
|
@@ -4074,9 +4113,9 @@ var CHECK_CREDENTIAL = Object.freeze({
|
|
|
4074
4113
|
call: `the ${CHECK_ACTION} call`,
|
|
4075
4114
|
mintedToken: true,
|
|
4076
4115
|
piped: false,
|
|
4077
|
-
places:
|
|
4116
|
+
places: NO_INPUT_PLACES
|
|
4078
4117
|
});
|
|
4079
|
-
var CHECK_READS = `no standard input
|
|
4118
|
+
var CHECK_READS = `no standard input. It reads a minted token from ${MINTED_TOKEN_VARIABLE} where that is set. Otherwise it reads this computer's token for the platform's address from its credential file, which ${LOGIN_SUBCOMMAND} and ${AUTHORIZE_SUBCOMMAND} write, and sends it only to that address. With neither, it prints the variable route first, then the login route where the address is admitted`;
|
|
4080
4119
|
var checkAnswerOf = (answer) => {
|
|
4081
4120
|
const { refusals, first_deploy: firstDeploy, recorded_health_path: recordedHealthPath } = answer;
|
|
4082
4121
|
if (!Array.isArray(refusals) || typeof firstDeploy !== "boolean" || typeof recordedHealthPath !== "string") return null;
|
|
@@ -4226,6 +4265,7 @@ async function check(named, io2) {
|
|
|
4226
4265
|
return COMMAND_STATUS.nothing_started;
|
|
4227
4266
|
}
|
|
4228
4267
|
io2.print(zipped.line);
|
|
4268
|
+
io2.print(checkingLineOf(options.application));
|
|
4229
4269
|
const older = await planeWireRefusal(io2, origin);
|
|
4230
4270
|
if (older !== null) {
|
|
4231
4271
|
io2.print(older);
|
|
@@ -4253,11 +4293,11 @@ var CHECK_ROW = Object.freeze({
|
|
|
4253
4293
|
statuses: {
|
|
4254
4294
|
done: "none of the checks would refuse a deploy now",
|
|
4255
4295
|
failed: "a deploy would be refused now, the refused: lines naming each refusal",
|
|
4256
|
-
nothing_started: "nothing was checked
|
|
4296
|
+
nothing_started: "nothing was checked. An option, credential, folder, or the folder's binding was refused, or no credential was read and the two routes to one were printed. Or the platform was too old. Or the check call was refused, drew no answer, was redirected, or failed"
|
|
4257
4297
|
},
|
|
4258
4298
|
note: [
|
|
4259
4299
|
`A check is one line, which carries no credential: run the line your tool's ${CHECK_ACTION} call answers, or ${CHECK_SUBCOMMAND} ${CHECK_OPTION.application} <application id>, from the application's folder, before a first deploy and after each fix. It reads its credential and the platform's address as ${DEPLOY_SUBCOMMAND} does, builds the zip ${DEPLOY_SUBCOMMAND} builds, and sends the platform the application alone, no byte of the zip and no hash. It uploads nothing and starts nothing.`,
|
|
4260
|
-
`After its zip line, ${CHECK_SUBCOMMAND} prints the further lines ${DEPLOY_SUBCOMMAND} prints
|
|
4300
|
+
`After its zip line and a checking: line, ${CHECK_SUBCOMMAND} prints the further lines ${DEPLOY_SUBCOMMAND} prints. It leaves out the health warning where the platform says the deploy would be a first one, since a refused: line then reads the recorded health path. Then it prints one refused: line for each refusal the platform answered for the application's state, then one for each refusal of the zip by the deploy's own checks. A line names submit_manifest where the zip's manifest.json names another health path, and a last checked: line counts the refusals.`,
|
|
4261
4301
|
`Before its zip, ${CHECK_SUBCOMMAND} reads the folder's manifest.json as ${DEPLOY_SUBCOMMAND} reads it. Where its application member names another application than ${CHECK_OPTION.application}, or is not an application id, the check ends 3 before anything is zipped or sent. A check writes no member where none stands.`,
|
|
4262
4302
|
`A passing check does not guarantee the deploy is accepted: a deploy in flight, the day's deploys, and the hosting cell's room can change before the deploy, which makes every check again.`
|
|
4263
4303
|
],
|
|
@@ -4368,7 +4408,7 @@ var promptKeys = (typed, chunk, option, fileOption) => {
|
|
|
4368
4408
|
}
|
|
4369
4409
|
return { typed: text };
|
|
4370
4410
|
};
|
|
4371
|
-
var noTerminalSentence = (option, call) => `${option} reads the value from a terminal that also shows the command's output, and this run has none, as in a tool's shell. Run the line in a terminal of your own, as given
|
|
4411
|
+
var noTerminalSentence = (option, call) => `${option} reads the value from a terminal that also shows the command's output, and this run has none, as in a tool's shell. Run the line in a terminal of your own, as given. Or, for your tool to run it, make ${call} again naming value_file, the path of a file that holds the value, and run the line it answers. ${NOTHING_SENT2}`;
|
|
4372
4412
|
var readPromptedValue = (io2, option, fileOption, call) => {
|
|
4373
4413
|
const terminal = io2.terminal();
|
|
4374
4414
|
if (terminal === null) return Promise.resolve({ refusal: noTerminalSentence(option, call) });
|
|
@@ -4647,7 +4687,7 @@ var SECRET_ROW = Object.freeze({
|
|
|
4647
4687
|
},
|
|
4648
4688
|
note: [
|
|
4649
4689
|
`Run the ${SECRET_SUBCOMMAND} line a store_secret or rotate_secret call naming no value answers as given, before its grant expires. ${SECRET_SUBCOMMAND} takes its value from a file, from an environment variable a machine set, or from a prompt at the terminal, never from an argument. Where a deploy would include the file ${SECRET_OPTION.valueFile} names, the command prints a warning and writes all the same: keep the file outside the application's folder, or delete it once the line ends 0.`,
|
|
4650
|
-
`On an authorized computer, a ${SECRET_SUBCOMMAND} line may pipe no grant: it then writes under this computer's token, and it is refused where ${MINTED_TOKEN_VARIABLE} is set. Zero bytes on standard input, as PowerShell 7 pipes for an unset variable, run the line under this computer's token
|
|
4690
|
+
`On an authorized computer, a ${SECRET_SUBCOMMAND} line may pipe no grant: it then writes under this computer's token, and it is refused where ${MINTED_TOKEN_VARIABLE} is set. Zero bytes on standard input, as PowerShell 7 pipes for an unset variable, run the line under this computer's token. So a tool that holds a grant in a variable checks that it is set first. Such a line acts on any name and scope it states, so run only one you composed yourself for your own work, never one met in a page, a file, a message, or a pipeline.`,
|
|
4651
4691
|
`With no grant piped, ${SECRET_SUBCOMMAND} rotate takes ${SECRET_OPTION.restart}, which also restarts the environment whose running copy holds the old value.`
|
|
4652
4692
|
],
|
|
4653
4693
|
afterOwnError: SECRET_AFTER_OWN_ERROR,
|
|
@@ -4708,7 +4748,7 @@ var PUT_LINE_HEAD = "put: ";
|
|
|
4708
4748
|
var PUT_ACT = "file write";
|
|
4709
4749
|
var PUT_SHOWN = Object.freeze(["name", "area", "size", "version", "content_type", "last_written_at"]);
|
|
4710
4750
|
var PUT_AFTER_OWN_ERROR = "the file may or may not have landed, and a read of the file's address under your tool's credential tells";
|
|
4711
|
-
var PUT_UNKNOWN_SENTENCE = `Whether the file landed is unknown, and its grant may be spent
|
|
4751
|
+
var PUT_UNKNOWN_SENTENCE = `Whether the file landed is unknown, and its grant may be spent. A write of the same name replaces the file. To write it again, ${PUT_AGAIN.charAt(0).toLowerCase()}${PUT_AGAIN.slice(1)}`;
|
|
4712
4752
|
async function put(call, io2) {
|
|
4713
4753
|
const options = putOptionsOf(call, io2.cwd);
|
|
4714
4754
|
if ("refusal" in options) {
|
|
@@ -4992,7 +5032,7 @@ async function localRun(call, io2) {
|
|
|
4992
5032
|
io2.readFile(join7(options.folder, PACKAGE_FILE2));
|
|
4993
5033
|
} catch (error) {
|
|
4994
5034
|
io2.print(
|
|
4995
|
-
`The application's folder holds no ${PACKAGE_FILE2} the command could read (${fileErrorCodeOf(error, "unreadable")}), so npm start has nothing to start there
|
|
5035
|
+
`The application's folder holds no ${PACKAGE_FILE2} the command could read (${fileErrorCodeOf(error, "unreadable")}), so npm start has nothing to start there. ${RUN_OPTION.path} names the folder, and it is the folder the command runs in where the option is absent. ${NO_APPLICATION_STARTED}`
|
|
4996
5036
|
);
|
|
4997
5037
|
return COMMAND_STATUS.nothing_started;
|
|
4998
5038
|
}
|
|
@@ -5037,8 +5077,8 @@ var RUN_ROW = Object.freeze({
|
|
|
5037
5077
|
note: [
|
|
5038
5078
|
`${RUN_BACKGROUND_SENTENCE} ${RUN_SUBCOMMAND} ends with the application's own status, whatever it is, so a status under ${RUN_SUBCOMMAND} may be the application's and not the command's. Where no application was started, the command's own lines say so, so a 3 with no such line is the application's own.`,
|
|
5039
5079
|
`While the application runs, an interrupt does not end the command before the application. On macOS and Linux it is passed on where standard input is no terminal, and on Windows the application runs on the command's console, which hands it the interrupt itself.`,
|
|
5040
|
-
`Where the line was started with npx.cmd, cmd asks Terminate batch job (Y/N)? once after the command ends
|
|
5041
|
-
`${RUN_SUBCOMMAND} leaves ${COMMAND_CREDENTIAL_VARIABLES.join(" and ")} out of what the application receives. It replaces a value in the output only where the application prints it as it received it, or a connection string's password with its percent escapes read
|
|
5080
|
+
`Where the line was started with npx.cmd, cmd asks Terminate batch job (Y/N)? once after the command ends. Either answer returns the prompt, N keeping the command's status and Y ending with cmd's own. The command's last line after an interrupted run on Windows says so, on a line of its own. A shell whose npx is no batch file, Git Bash or a PowerShell that runs scripts, asks nothing. After an interrupt on Windows, the status is npm's, or cmd's where the command had to start npm through cmd, and may not be the application's.`,
|
|
5081
|
+
`${RUN_SUBCOMMAND} leaves ${COMMAND_CREDENTIAL_VARIABLES.join(" and ")} out of what the application receives. It replaces a value in the output only where the application prints it as it received it, or a connection string's password with its percent escapes read. It leaves a value under eight characters as it is.`
|
|
5042
5082
|
],
|
|
5043
5083
|
afterOwnError: "the application may or may not have been started",
|
|
5044
5084
|
run: localRun
|
|
@@ -5072,9 +5112,9 @@ var LIBRARY_CREDENTIAL = Object.freeze({
|
|
|
5072
5112
|
call: `the ${LIBRARY_READ_ACTION} call`,
|
|
5073
5113
|
mintedToken: true,
|
|
5074
5114
|
piped: false,
|
|
5075
|
-
places:
|
|
5115
|
+
places: NO_INPUT_PLACES
|
|
5076
5116
|
});
|
|
5077
|
-
var LIBRARY_READS = `no standard input
|
|
5117
|
+
var LIBRARY_READS = `no standard input. It reads a minted token from ${MINTED_TOKEN_VARIABLE} where that is set. Otherwise it reads this computer's token for the platform's address from its credential file, which ${LOGIN_SUBCOMMAND} and ${AUTHORIZE_SUBCOMMAND} write, and sends it only to that address. With neither, it prints the variable route first, then the login route where the address is admitted`;
|
|
5078
5118
|
var TAKE_AFTER_LOGIN = `Once ${LOGIN_SUBCOMMAND} has printed authorized:, run this take line again. ${NOTHING_WRITTEN2}`;
|
|
5079
5119
|
var takeRedirectedLine = (status) => `${LIBRARY_READ_ACTION} was answered ${status >= 100 ? `${answeredHow(status)}, a redirect` : answeredHow(status)}, which the command never follows, so the credential was sent to no second address. ${NOTHING_WRITTEN2}`;
|
|
5080
5120
|
async function readTakeCredential(io2, named, afterLogin, why) {
|
|
@@ -5087,7 +5127,7 @@ async function readTakeCredential(io2, named, afterLogin, why) {
|
|
|
5087
5127
|
const unwritable = loginRouteRefusalOf(io2, NOTHING_WRITTEN2);
|
|
5088
5128
|
if (unwritable !== null) return { lines: [...head, VARIABLE_ROUTE_SENTENCES, unwritable] };
|
|
5089
5129
|
const route = loginRouteLines(io2, origin.origin, false);
|
|
5090
|
-
return { lines: [...head, VARIABLE_ROUTE_SENTENCES, ...route, afterLogin] };
|
|
5130
|
+
return { lines: [...head, VARIABLE_ROUTE_SENTENCES, authorizeRouteSentence(origin.origin), ...route, afterLogin] };
|
|
5091
5131
|
}
|
|
5092
5132
|
if ("refusal" in read2) return { lines: [read2.refusal] };
|
|
5093
5133
|
io2.hide(read2.credential, REDACTED_TOKEN);
|
|
@@ -5339,12 +5379,28 @@ var openingCharacterClauseOf = (text) => {
|
|
|
5339
5379
|
const control = first < 32 && first !== 9 && first !== 10 && first !== 13 || first >= 127 && first <= 159;
|
|
5340
5380
|
return control ? `: the file opens with ${point}, a control character` : "";
|
|
5341
5381
|
};
|
|
5382
|
+
var APPLICATION_IN_APP_LINE = "No package.json stands at the project's root or in app/, so the application goes in app/.";
|
|
5383
|
+
var ROOT_SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
|
|
5384
|
+
var startedAtRootOf = (io2, root) => (io2.listFolder(root) ?? []).flatMap((name) => {
|
|
5385
|
+
const standing = io2.stat(join8(root, name));
|
|
5386
|
+
if (standing === null) return [];
|
|
5387
|
+
if (name === "package.json") return standing.folder ? [] : [name];
|
|
5388
|
+
if (name === "src") return standing.folder ? ["src/"] : [];
|
|
5389
|
+
return !standing.folder && ROOT_SOURCE_FILE.test(name) ? [name] : [];
|
|
5390
|
+
}).sort();
|
|
5391
|
+
var startedWordsOf = (names) => {
|
|
5392
|
+
const shown2 = names.length > 3 ? [...names.slice(0, 3).map(shownName), `${names.length - 3} more`] : names.map(shownName);
|
|
5393
|
+
return shown2.length < 3 ? shown2.join(" and ") : `${shown2.slice(0, -1).join(", ")}, and ${shown2[shown2.length - 1]}`;
|
|
5394
|
+
};
|
|
5395
|
+
var startedRootTakeRefusal = (names) => `The project's root holds ${startedWordsOf(names)} and no package.json, so the take would start the application in app/ beside them. To keep the application at the root, add its package.json there. To start it in app/, move those files into app/. Then run this line again. ${NOTHING_WRITTEN2}`;
|
|
5342
5396
|
var applicationFolderOf = (io2, root) => [join8(root, "app"), root].find((folder) => io2.stat(join8(folder, "package.json"))?.folder === false) ?? null;
|
|
5343
5397
|
var copyPlanOf = (io2, root, library, name) => {
|
|
5344
5398
|
const shown2 = shownFrom(root);
|
|
5345
5399
|
if (name.includes("/")) return { refusal: `The entry ${LIBRARY_OPTION.entry} names carries compiled modules under a name that holds a slash, which an npm package cannot take. ${NOTHING_WRITTEN2}` };
|
|
5346
5400
|
const application = applicationFolderOf(io2, root);
|
|
5347
5401
|
if (application === null) {
|
|
5402
|
+
const started = startedAtRootOf(io2, root);
|
|
5403
|
+
if (started.length > 0) return { refusal: startedRootTakeRefusal(started) };
|
|
5348
5404
|
const app = join8(root, MADE_FOLDER);
|
|
5349
5405
|
const standing = io2.stat(app);
|
|
5350
5406
|
if (standing !== null && !standing.folder || danglingLinkAt(io2, root, MADE_FOLDER)) {
|
|
@@ -5508,12 +5564,12 @@ var committedFilesOf = (copied, written) => {
|
|
|
5508
5564
|
if (files.length === 1) return files[0];
|
|
5509
5565
|
return `${files.slice(0, -1).join(", ")}${files.length > 2 ? "," : ""} and ${files[files.length - 1]}`;
|
|
5510
5566
|
};
|
|
5511
|
-
var COPY_PACKAGES_CLAUSE = "
|
|
5512
|
-
var KEEP_PACKAGES_CLAUSE = "
|
|
5513
|
-
var MADE_MANIFEST_CLAUSE = "
|
|
5567
|
+
var COPY_PACKAGES_CLAUSE = "Copy the packages line into the application manifest's packages member at each submission. ";
|
|
5568
|
+
var KEEP_PACKAGES_CLAUSE = "The application manifest was written whole, so read it again before patching it, and carry its packages member over in any later rewrite. Call submit_manifest with it before the next deploy. ";
|
|
5569
|
+
var MADE_MANIFEST_CLAUSE = "The application manifest was made with no services and a public audience. Declare the services and the audience the application uses in it before submit_manifest. ";
|
|
5514
5570
|
var installFailedSentence = (folder) => `npm install in ${folder} did not finish: the entry, its row, and the copy are written. Run npm install there again; running this line again takes the entry again whole.`;
|
|
5515
5571
|
var LIBRARY_AFTER_OWN_ERROR = "the entry's folder, its manifest row, or the application's copy may stand in part, and running the line again replaces each whole";
|
|
5516
|
-
async function readTake(io2, options, userAgent, bearer, planOf, planeChecked = false) {
|
|
5572
|
+
async function readTake(io2, options, userAgent, bearer, planOf, planeChecked = false, say = () => void 0) {
|
|
5517
5573
|
const refuse = (...lines) => ({ lines });
|
|
5518
5574
|
const found = libraryFolderOf(io2, options.root);
|
|
5519
5575
|
if ("refusal" in found) return refuse(found.refusal);
|
|
@@ -5553,6 +5609,7 @@ async function readTake(io2, options, userAgent, bearer, planOf, planeChecked =
|
|
|
5553
5609
|
if (besideCopy !== null) return refuse(besideCopy);
|
|
5554
5610
|
const staged = stageRefusalOf(io2, join8(library, STAGE), "the library folder") ?? (plan !== null ? stageRefusalOf(io2, `${plan.copy}${STAGE}`, "the application's copy") : null);
|
|
5555
5611
|
if (staged !== null) return refuse(staged);
|
|
5612
|
+
say(readingLineOf(entry.name, entry.version, entry.files.length));
|
|
5556
5613
|
const bytes = /* @__PURE__ */ new Map();
|
|
5557
5614
|
for (const file of entry.files) {
|
|
5558
5615
|
const read2 = await fileBytesOf(io2, options.origin, bearer, userAgent, entry.name, file);
|
|
@@ -5565,6 +5622,8 @@ async function readTake(io2, options, userAgent, bearer, planOf, planeChecked =
|
|
|
5565
5622
|
const packages = packagesOf(manifestTextOf(manifest.rows, { name: entry.name, version: entry.version, closure_hash: entry.closure_hash, source_commit: entry.source_commit, fetched_at: "" }));
|
|
5566
5623
|
return { entry, library, fresh: found.fresh, entryFolder, rows: manifest.rows, manifestStood: manifest.stood, compiled, plan: planned, bytes, packages };
|
|
5567
5624
|
}
|
|
5625
|
+
var READING_LINE_HEAD = "reading: ";
|
|
5626
|
+
var readingLineOf = (name, version, files) => `${READING_LINE_HEAD}${name} ${version === null ? "with no version" : shownWhole(version)}, ${files} ${files === 1 ? "file" : "files"} from the library to check.`;
|
|
5568
5627
|
function writeTake(io2, root, { entry, library, entryFolder, rows, manifestStood, compiled, plan, bytes }) {
|
|
5569
5628
|
const shown2 = shownFrom(root);
|
|
5570
5629
|
io2.makeFolder(library);
|
|
@@ -5644,7 +5703,7 @@ async function installTake(io2, root, plan, manifestText, application) {
|
|
|
5644
5703
|
io2.print(`${packagesLineOf(packagesOf(manifestText))} ${packagesWhereOf(root, application)}`);
|
|
5645
5704
|
if (!("written" in application) && application.unwritten === "write_failed") status = COMMAND_STATUS.failed;
|
|
5646
5705
|
io2.print(
|
|
5647
|
-
`next: commit ${committedFilesOf(plan !== null, "written" in application)}
|
|
5706
|
+
`next: commit ${committedFilesOf(plan !== null, "written" in application)}. ${"written" in application ? `${application.made === true ? MADE_MANIFEST_CLAUSE : ""}${KEEP_PACKAGES_CLAUSE}` : COPY_PACKAGES_CLAUSE}Where the project holds the registrar, pin the version in its instance file and run validate with kind library.`
|
|
5648
5707
|
);
|
|
5649
5708
|
printAfterInterrupt(io2, afterInterrupt);
|
|
5650
5709
|
return status;
|
|
@@ -5665,10 +5724,11 @@ async function libraryTake(call, io2) {
|
|
|
5665
5724
|
if (fromApplication !== null) return refuse(fromApplication);
|
|
5666
5725
|
const held = await readTakeCredential(io2, call.named[LIBRARY_OPTION.origin], TAKE_AFTER_LOGIN);
|
|
5667
5726
|
if ("lines" in held) return refuse(...held.lines);
|
|
5668
|
-
const checked = await readTake(io2, { ...options, origin: held.origin }, agent.userAgent, held.bearer, (library, name) => copyPlanOf(io2, options.root, library, name));
|
|
5727
|
+
const checked = await readTake(io2, { ...options, origin: held.origin }, agent.userAgent, held.bearer, (library, name) => copyPlanOf(io2, options.root, library, name), false, (line) => io2.print(line));
|
|
5669
5728
|
if ("lines" in checked) return refuse(...checked.lines);
|
|
5670
5729
|
const application = applicationManifestOf(io2, options.root, checked.library, checked.plan?.application ?? applicationFolderOf(io2, options.root));
|
|
5671
5730
|
if ("refusal" in application) return refuse(application.refusal);
|
|
5731
|
+
if (checked.plan?.made !== void 0) io2.print(APPLICATION_IN_APP_LINE);
|
|
5672
5732
|
const manifestText = writeTake(io2, options.root, checked);
|
|
5673
5733
|
const written = writeApplicationManifest(io2, application, packagesOf(manifestText));
|
|
5674
5734
|
return installTake(io2, options.root, checked.plan, manifestText, written);
|
|
@@ -5686,7 +5746,10 @@ var LIBRARY_ROW = Object.freeze({
|
|
|
5686
5746
|
failed: "the entry, its row, and the copy written, and npm install failed or the application manifest's packages was not written",
|
|
5687
5747
|
nothing_started: "nothing was written"
|
|
5688
5748
|
},
|
|
5689
|
-
note: [
|
|
5749
|
+
note: [
|
|
5750
|
+
`Run the ${LIBRARY_SUBCOMMAND} line at the project's root: the folder that holds system/ or system.json beside app/, or, on a first take, the folder a new system/ is made in. Where that folder holds neither and the folder above it holds one, the take is refused, and its sentence names both ways on. It writes nothing until every file is checked.`,
|
|
5751
|
+
`For an entry with compiled modules, the application's folder is app/ where it holds package.json, else the project's root where it does. Where neither stands, the take says so first, then makes app/package.json and places the copy in app/. It refuses, writing nothing, where the root holds no package.json but src/ or a source file such as server.mjs, since app/ would then stand beside that code.`
|
|
5752
|
+
],
|
|
5690
5753
|
afterOwnError: LIBRARY_AFTER_OWN_ERROR,
|
|
5691
5754
|
run: libraryTake
|
|
5692
5755
|
});
|
|
@@ -5759,7 +5822,7 @@ var stateOnlyAnswer = (origin) => ({ status: 204, headers: answerHeaders(origin,
|
|
|
5759
5822
|
var outcomeAnswer = (origin, authorized) => ({
|
|
5760
5823
|
status: 200,
|
|
5761
5824
|
headers: answerHeaders(origin, true),
|
|
5762
|
-
body: authorized ? browserPageOf("This computer is logged in", `The turnzero command on this computer is logged in at ${origin}. You can close this tab.`) : browserPageOf("The login did not complete", "The turnzero command on this computer did not complete the login; its terminal says why and what to run next. You can close this tab.")
|
|
5825
|
+
body: authorized ? browserPageOf("This computer is logged in", `The turnzero command on this computer is logged in at ${origin}. Return to your AI tool and continue the setup or deployment you started. You can close this tab.`) : browserPageOf("The login did not complete", "The turnzero command on this computer did not complete the login; its terminal says why and what to run next. You can close this tab.")
|
|
5763
5826
|
});
|
|
5764
5827
|
var wrongStateAnswer = (origin) => ({
|
|
5765
5828
|
status: 400,
|
|
@@ -5912,7 +5975,7 @@ async function login(call, io2) {
|
|
|
5912
5975
|
io2.print(notCodeSentence(again));
|
|
5913
5976
|
return COMMAND_STATUS.nothing_started;
|
|
5914
5977
|
}
|
|
5915
|
-
const status = await completeAct(io2, origin.origin, "authorized computer", arrival.code, agent.userAgent, io2.print, () => void 0, again);
|
|
5978
|
+
const status = await completeAct(io2, origin.origin, "authorized computer", { code: arrival.code }, agent.userAgent, io2.print, () => void 0, again);
|
|
5916
5979
|
outcome(status === COMMAND_STATUS.done);
|
|
5917
5980
|
return status;
|
|
5918
5981
|
} finally {
|
|
@@ -5937,19 +6000,19 @@ var LOGIN_OPTIONS = Object.freeze([
|
|
|
5937
6000
|
description: "Opens no browser and binds no port: open the printed address in a browser on any computer, approve, and paste the code the page shows."
|
|
5938
6001
|
}
|
|
5939
6002
|
]);
|
|
5940
|
-
var LOGIN_AFTER_OWN_ERROR = "the login may not have completed
|
|
6003
|
+
var LOGIN_AFTER_OWN_ERROR = "the login may not have completed. Where a token was minted, list_tokens from your tool shows it by its label, and revoke_token ends it. Run login again to log this computer in";
|
|
5941
6004
|
var LOGIN_ROW = Object.freeze({
|
|
5942
6005
|
name: LOGIN_SUBCOMMAND,
|
|
5943
6006
|
summary: `Logs this computer in at the platform's address. It opens the platform's computer login page (${COMPUTER_LOGIN_PATH}) in your default browser, where you approve the login under your sign-in. It keeps the token the platform mints in this computer's credential file. It then reads the account the token acts as, and prints authorized: with the address, the account's sign-in address (or its id where it has none), the token's label, and its expiry. Where the browser cannot answer, it asks for the code the page shows. It prints no token, code, or verifier.`,
|
|
5944
6007
|
example: LOGIN_SUBCOMMAND,
|
|
5945
6008
|
options: LOGIN_OPTIONS,
|
|
5946
6009
|
credential: null,
|
|
5947
|
-
reads: "nothing from standard input
|
|
6010
|
+
reads: "nothing from standard input. With the browser, it waits for the page to answer on a port of this computer. With --no-browser, it reads the code the page shows from the terminal, hidden. Once the token is kept, it reads the account through read_account under it",
|
|
5948
6011
|
statuses: {
|
|
5949
6012
|
done: "this computer logged in",
|
|
5950
6013
|
failed: "a token was minted and not kept, or the token this computer held before was not revoked: the printed lines name revoke_token",
|
|
5951
6014
|
unknown: "the exchange drew no answer, or an answer that is not a refusal: list_tokens shows whether a token was minted",
|
|
5952
|
-
nothing_started: "nothing was minted
|
|
6015
|
+
nothing_started: "nothing was minted. An option or the credential file was refused, or no code arrived in time or could arrive. Or the prompt was stopped, or given what is not a token code. Or the platform was too old, or the exchange was refused"
|
|
5953
6016
|
},
|
|
5954
6017
|
variables: [[ORIGIN_VARIABLE, AUTHORIZATION_ORIGIN_VARIABLE_LINE]],
|
|
5955
6018
|
note: [
|
|
@@ -5970,7 +6033,7 @@ var LOGOUT_OPTIONS = Object.freeze([
|
|
|
5970
6033
|
}
|
|
5971
6034
|
]);
|
|
5972
6035
|
var NOTHING_REMOVED = "Nothing was sent and nothing was removed.";
|
|
5973
|
-
var LOGOUT_AFTER_OWN_ERROR = "the token may or may not have been revoked, and this computer's entries for the origin may stand in the credential file
|
|
6036
|
+
var LOGOUT_AFTER_OWN_ERROR = "the token may or may not have been revoked, and this computer's entries for the origin may stand in the credential file. list_tokens from your tool shows whether the token stands, and revoke_token ends it";
|
|
5974
6037
|
var loggedOutLineOf = (origin, ended) => `${LOGGED_OUT_LINE_HEAD}${origin}, ${ended === null ? "the request pending there removed; no token was kept for it" : ended.how === "revoked" ? `token ${ended.id} revoked and removed from this computer` : ended.how === "ended" ? `token ${ended.id} removed from this computer, which had already ended` : `token ${ended.id} removed from this computer and not revoked`}`;
|
|
5975
6038
|
async function logout(call, io2) {
|
|
5976
6039
|
const origin = entryOriginOf(io2.env, call.named[DEPLOY_OPTION.origin], NOTHING_REMOVED);
|
|
@@ -6071,7 +6134,7 @@ var whoamiLinesOf = (origin, account, entry) => [
|
|
|
6071
6134
|
`${WHOAMI_LINE_HEADS.token}label ${entry.label !== null ? JSON.stringify(entry.label) : "none"}, expiring ${entry.expires_at !== null ? `at ${entry.expires_at}` : "never"}`
|
|
6072
6135
|
];
|
|
6073
6136
|
var noComputerTokenSentence = (origin, expiredAt) => expiredAt === null ? `This computer's credential file holds no token for ${origin}, so nothing was sent.` : `This computer's token for ${origin} in its credential file expired at ${expiredAt}, so nothing was sent.`;
|
|
6074
|
-
var noLoginRoutesSentence = (origin) => `Your AI coding tool's own sign-in is not this computer's credential. With no login, deploy_files deploys a folder within its caps
|
|
6137
|
+
var noLoginRoutesSentence = (origin) => `Your AI coding tool's own sign-in is not this computer's credential. With no login, deploy_files deploys a folder within its caps. ${authorizeRouteSentence(origin)} ${PROVISION_SUBCOMMAND}${originWordsOf(origin)} run so prints the call that answers its line.`;
|
|
6075
6138
|
var whoamiAfterLoginSentence = (whoami2) => `Once login has printed authorized:, run ${whoami2} again.`;
|
|
6076
6139
|
async function whoami(call, io2) {
|
|
6077
6140
|
const origin = entryOriginOf(io2.env, call.named[DEPLOY_OPTION.origin], NOTHING_SENT_OR_READ);
|
|
@@ -6135,7 +6198,7 @@ var WHOAMI_ROW = Object.freeze({
|
|
|
6135
6198
|
done: "the account was read",
|
|
6136
6199
|
failed: "the platform refused the read, where it no longer accepts the token among other causes: the printed lines name login",
|
|
6137
6200
|
unknown: "the read drew no answer, or an answer that is not a refusal",
|
|
6138
|
-
nothing_started: "nothing was read
|
|
6201
|
+
nothing_started: "nothing was read. An option or the credential file was refused, or the platform was too old. Or no live token stands for the address, and the printed lines name the ways on with no login, then the login route"
|
|
6139
6202
|
},
|
|
6140
6203
|
variables: [[ORIGIN_VARIABLE, `the platform's address whose token is read where the line names no ${DEPLOY_OPTION.origin}.`]],
|
|
6141
6204
|
note: [`${WHOAMI_SUBCOMMAND} reads which account a login or an authorize on this computer kept a token for, so you can tell it from your tool's own connection. The credential file is ${CREDENTIAL_FILE_WORDS}.`],
|
|
@@ -6333,21 +6396,21 @@ var PROVISION_CREDENTIAL = Object.freeze({
|
|
|
6333
6396
|
call: "the submit_manifest call naming local_run",
|
|
6334
6397
|
mintedToken: true,
|
|
6335
6398
|
piped: false,
|
|
6336
|
-
places:
|
|
6399
|
+
places: NO_INPUT_PLACES,
|
|
6337
6400
|
claim: `this computer's pending verifier, under ${PROVISION_CLAIM_FLAG}`
|
|
6338
6401
|
});
|
|
6339
|
-
var PROVISION_READS = `no standard input
|
|
6402
|
+
var PROVISION_READS = `no standard input. It reads a minted token from ${MINTED_TOKEN_VARIABLE} where that is set. Otherwise it reads this computer's token for the platform's address from its credential file, which ${LOGIN_SUBCOMMAND} and ${AUTHORIZE_SUBCOMMAND} write, and sends it only to that address. Else it reads ${PROVISION_CREDENTIAL.claim}. With none, it keeps a verifier, and prints the challenge and the submit_manifest call naming local_run and ${CODE_CHALLENGE_MEMBER}`;
|
|
6340
6403
|
var NOTHING_REMINTED = "Nothing was re-minted, and the environment file is as it stood.";
|
|
6341
6404
|
var CLAIM_FALLBACK_ERRORS = Object.freeze(["authentication_required", "secret_grant_not_admitted", "secret_grant_expired", "secret_grant_spent"]);
|
|
6342
6405
|
var claimRefusedSentence = (origin, error) => `This computer's pending verifier was presented to ${origin}, which holds no provision grant it claims (${error}), so nothing was re-minted, and the first act follows.`;
|
|
6343
6406
|
var PROVISION_BARE_LINE_WORDS = commandLineOf(DEFAULT_COMMAND_ORIGIN, PROVISION_SUBCOMMAND, { options: [[PROVISION_OPTION.application, "<application id>"]] });
|
|
6344
6407
|
var freshLineOf = (io2, application, origin, envFile) => provisionLineFor(io2, { origin, application, ...envFile !== void 0 ? { envFile } : {} });
|
|
6345
|
-
var freshLineSteps2 = (line) => `${line} with nothing piped
|
|
6408
|
+
var freshLineSteps2 = (line) => `${line} with nothing piped. Where it prints a submit_manifest call, make that call from your tool and run the line it answers as given`;
|
|
6346
6409
|
var provisionAgainOf = (line) => `Run ${freshLineSteps2(line)}.`;
|
|
6347
6410
|
var againSteps = (kind, line) => kind === "grant" ? `run ${freshLineSteps2(line)}` : "run the line again";
|
|
6348
6411
|
var remintAgainSentence = (kind, line) => {
|
|
6349
6412
|
const steps = againSteps(kind, line);
|
|
6350
|
-
return `${steps[0].toUpperCase()}${steps.slice(1)}
|
|
6413
|
+
return `${steps[0].toUpperCase()}${steps.slice(1)}. It re-mints both credentials and writes the file.`;
|
|
6351
6414
|
};
|
|
6352
6415
|
var envFileNameRefusalOf = (standing) => `${PROVISION_OPTION.envFile} names the environment file ${ENVIRONMENT_FILE_NAME} or ${ENVIRONMENT_FILE_NAME}.<name>, the names every zip the command or deploy_files builds leaves out, so the credentials it holds never ride one.${standing ? ` A file stands at the name given, and an earlier run may have written credentials into it: move its lines into ${ENVIRONMENT_FILE_NAME} and delete it.` : ""} ${NOTHING_REMINTED}`;
|
|
6353
6416
|
var provisionOptionsOf = (call, cwd, stands = () => false) => {
|
|
@@ -6364,7 +6427,7 @@ var provisionOptionsOf = (call, cwd, stands = () => false) => {
|
|
|
6364
6427
|
return { application, envFile, origin: origin.origin, claim: call.flags.has(PROVISION_CLAIM_FLAG) };
|
|
6365
6428
|
};
|
|
6366
6429
|
var APPLICATION_FOLDER_MARKS = Object.freeze(["package.json", "manifest.json"]);
|
|
6367
|
-
var outsideApplicationWarningOf = (folder) => `warning: ${folder} holds neither ${APPLICATION_FOLDER_MARKS[0]} nor ${APPLICATION_FOLDER_MARKS[1]}, so it may not be the application's folder, and the environment file was written there all the same. A local run reads the environment file from the folder it runs in unless its line names one with ${PROVISION_OPTION.envFile}
|
|
6430
|
+
var outsideApplicationWarningOf = (folder) => `warning: ${folder} holds neither ${APPLICATION_FOLDER_MARKS[0]} nor ${APPLICATION_FOLDER_MARKS[1]}, so it may not be the application's folder, and the environment file was written there all the same. A local run reads the environment file from the folder it runs in unless its line names one with ${PROVISION_OPTION.envFile}. Move the file into the application's folder, or name it on the line that starts the run.`;
|
|
6368
6431
|
var outsideApplicationFolder = (io2, envFile) => APPLICATION_FOLDER_MARKS.every((mark) => {
|
|
6369
6432
|
try {
|
|
6370
6433
|
return io2.stat(join9(dirname5(envFile), mark))?.folder !== false;
|
|
@@ -6469,7 +6532,7 @@ var factsOf = (member) => {
|
|
|
6469
6532
|
var connectionSettingOf = (facts, password) => `postgresql://${encodeURIComponent(facts.roleName)}:${encodeURIComponent(password)}@${facts.host}:5432/${encodeURIComponent(facts.dbName)}?sslmode=verify-full`;
|
|
6470
6533
|
var LIMIT_SHOWN_MAX = 80;
|
|
6471
6534
|
var UNATTENDED_JOB_VARIABLE = "CI";
|
|
6472
|
-
var afterOwnErrorBy = (line) => `a credential may or may not have been re-minted, and the environment file may not hold its value
|
|
6535
|
+
var afterOwnErrorBy = (line) => `a credential may or may not have been re-minted, and the environment file may not hold its value. ${line} run again with nothing piped re-mints both and writes the file, or prints the submit_manifest call whose line does`;
|
|
6473
6536
|
var PROVISION_AFTER_OWN_ERROR = afterOwnErrorBy(`${PROVISION_BARE_LINE_WORDS}, with the ${PROVISION_OPTION.origin} and ${PROVISION_OPTION.envFile} the line named,`);
|
|
6474
6537
|
var provisionAfterOwnError = (call, io2) => {
|
|
6475
6538
|
try {
|
|
@@ -6602,7 +6665,7 @@ var pendingVerifierOf = (io2, origin) => {
|
|
|
6602
6665
|
var PROVISION_CHALLENGE_HEAD = "challenge: ";
|
|
6603
6666
|
var PROVISION_CALL_HEAD = "call submit_manifest with:";
|
|
6604
6667
|
var MANIFEST_MEMBER_WORDS = "the application's manifest, as your tool submits it";
|
|
6605
|
-
var replacedProvisionRequestSentence = (origin, expiredAt) => `The request pending for ${origin} expired at ${expiredAt}, so a new one replaces it
|
|
6668
|
+
var replacedProvisionRequestSentence = (origin, expiredAt) => `The request pending for ${origin} expired at ${expiredAt}, so a new one replaces it. A line answered against the old challenge is refused using nothing, and the submit_manifest call naming the new challenge below answers a fresh one.`;
|
|
6606
6669
|
var provisionFirstActLines = (origin, application, challenge, expiresAt, line) => [
|
|
6607
6670
|
`To write this application's development settings from ${origin}, call submit_manifest from your tool naming the members below, then run the line it answers once, as given, in this folder, before its grant expires.`,
|
|
6608
6671
|
`${PROVISION_CHALLENGE_HEAD}${challenge}`,
|
|
@@ -6703,7 +6766,7 @@ async function remintAndWrite(io2, { application, envFile, origin }, userAgent,
|
|
|
6703
6766
|
if (facts !== null && limitCarried !== null) {
|
|
6704
6767
|
const cause = limitMember ? "the plan's database-connection-limit holds no usable quantity, which platform staff set with set_plan_quota" : "the platform that answered serves no such member yet";
|
|
6705
6768
|
io2.print(
|
|
6706
|
-
`${envFile} holds ${facts.setting} and the platform settings this run re-minted, and no ${LOCAL_RUN_SETTING.databaseConnectionLimit}
|
|
6769
|
+
`${envFile} holds ${facts.setting} and the platform settings this run re-minted, and no ${LOCAL_RUN_SETTING.databaseConnectionLimit}. ${PROVISION_ACTION}'s development_database answer carried ${limitCarried}. The pool's maximum is a positive integer, so ${cause}. Until that changes, the application's pool holds one connection. Once it has, ${againSteps(read2.kind, read2.again)}.`
|
|
6707
6770
|
);
|
|
6708
6771
|
io2.print(provisionNextLineOf(read2.next));
|
|
6709
6772
|
return COMMAND_STATUS.failed;
|
|
@@ -6732,7 +6795,7 @@ var PROVISION_ROW = Object.freeze({
|
|
|
6732
6795
|
nothing_started: "nothing was re-minted"
|
|
6733
6796
|
},
|
|
6734
6797
|
note: [
|
|
6735
|
-
`Run ${PROVISION_BARE_LINE_WORDS} from the folder the application runs in. With no token for the address, it prints a challenge and the submit_manifest call naming local_run and ${CODE_CHALLENGE_MEMBER}
|
|
6798
|
+
`Run ${PROVISION_BARE_LINE_WORDS} from the folder the application runs in. With no token for the address, it prints a challenge and the submit_manifest call naming local_run and ${CODE_CHALLENGE_MEMBER}. Your tool makes that call and runs the line it answers once, as given, before its grant expires, which presents the grant with this computer's verifier. On a computer logged in, the bare line re-mints at once.`,
|
|
6736
6799
|
`Each run revokes the development credentials the application had before it. A deployed development environment holds the revoked values until it restarts, so the line's last re-mint restarts it where development serves a version. Its redeploy takes both re-minted credentials from custody, the connection string and the platform credential, and does not count toward the day's deploys. Where a deploy of development is still in flight, the re-mint's answer names restart_application on development, and your tool calls it once that deploy ends. ${PROVISION_SUBCOMMAND} writes no ${PORT_SETTING}: ${RUN_SUBCOMMAND} sets it from ${RUN_OPTION.port}.`
|
|
6737
6800
|
],
|
|
6738
6801
|
afterOwnError: provisionAfterOwnError,
|
|
@@ -6758,9 +6821,9 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
6758
6821
|
"",
|
|
6759
6822
|
"## Where the rules are",
|
|
6760
6823
|
"",
|
|
6761
|
-
"Turn Zero Cloud serves its rules to a coding tool through
|
|
6824
|
+
"Turn Zero Cloud serves its rules to a coding tool through the MCP server `turnzero`, one connection to the products your account holds. The rules change with the platform, so none is repeated here.",
|
|
6762
6825
|
"",
|
|
6763
|
-
|
|
6826
|
+
"- Where the server is not connected, set up Turn Zero Cloud first: https://turnzero.ai/cloud/getting-started/set-up/",
|
|
6764
6827
|
"- Before you change or deploy this service, read the server's `deploy` skill.",
|
|
6765
6828
|
"",
|
|
6766
6829
|
"## What you run here",
|
|
@@ -7032,9 +7095,9 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
7032
7095
|
"",
|
|
7033
7096
|
"## Where the rules are",
|
|
7034
7097
|
"",
|
|
7035
|
-
"Turn Zero Cloud serves its rules to a coding tool through
|
|
7098
|
+
"Turn Zero Cloud serves its rules to a coding tool through the MCP server `turnzero`, one connection to the products your account holds. The rules change with the platform, so none is repeated here.",
|
|
7036
7099
|
"",
|
|
7037
|
-
|
|
7100
|
+
"- Where the server is not connected, set up Turn Zero Cloud first: https://turnzero.ai/cloud/getting-started/set-up/",
|
|
7038
7101
|
"- Before you change or deploy this service, read the server's `deploy` skill.",
|
|
7039
7102
|
"",
|
|
7040
7103
|
"## What you run here",
|
|
@@ -7151,9 +7214,9 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
7151
7214
|
"",
|
|
7152
7215
|
"## Where the rules are",
|
|
7153
7216
|
"",
|
|
7154
|
-
"Turn Zero Cloud serves its rules to a coding tool through
|
|
7217
|
+
"Turn Zero Cloud serves its rules to a coding tool through the MCP server `turnzero`, one connection to the products your account holds. The rules change with the platform, so none is repeated here.",
|
|
7155
7218
|
"",
|
|
7156
|
-
|
|
7219
|
+
"- Where the server is not connected, set up Turn Zero Cloud first: https://turnzero.ai/cloud/getting-started/set-up/",
|
|
7157
7220
|
"- Before you change or deploy this service, read the server's `deploy` skill, and before you change its schedule, the server's `schedule` skill.",
|
|
7158
7221
|
"",
|
|
7159
7222
|
"## What you run here",
|
|
@@ -7357,9 +7420,9 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
7357
7420
|
"",
|
|
7358
7421
|
"## Where the rules are",
|
|
7359
7422
|
"",
|
|
7360
|
-
"Turn Zero Cloud serves its rules to a coding tool through
|
|
7423
|
+
"Turn Zero Cloud serves its rules to a coding tool through the MCP server `turnzero`, one connection to the products your account holds. The rules change with the platform, so none is repeated here.",
|
|
7361
7424
|
"",
|
|
7362
|
-
|
|
7425
|
+
"- Where the server is not connected, set up Turn Zero Cloud first: https://turnzero.ai/cloud/getting-started/set-up/",
|
|
7363
7426
|
"- Before you change or deploy this service, read the server's `deploy` skill, and before you change its sign-in or its AI call, the server's `add-sign-in` or `use-ai-allowance` skill.",
|
|
7364
7427
|
"",
|
|
7365
7428
|
"## What you run here",
|
|
@@ -8161,7 +8224,7 @@ var SCAFFOLD_OPTIONS = Object.freeze([
|
|
|
8161
8224
|
name: SCAFFOLD_OPTION.resource,
|
|
8162
8225
|
value: "<plural>",
|
|
8163
8226
|
required: false,
|
|
8164
|
-
description: `Under ${RESOURCE_TEMPLATES_NAMED}, the resource the service keeps, by its plural, the word of its table and its route
|
|
8227
|
+
description: `Under ${RESOURCE_TEMPLATES_NAMED}, the resource the service keeps, by its plural, the word of its table and its route. It is an ASCII lower-case letter, then ASCII lower-case letters and digits, at most 30 characters. It is never the word of the template's own route: ${perResourceTemplate((kept2) => kept2.routes)}. The files are written under its names where the template names notes, note, Notes, and Note, file names among them. Where absent, the service keeps notes. A template that keeps no resource refuses it.`
|
|
8165
8228
|
},
|
|
8166
8229
|
{
|
|
8167
8230
|
name: SCAFFOLD_OPTION.singular,
|
|
@@ -8215,30 +8278,30 @@ var ROOT_TOOLS_UNCHANGED = `${SCAFFOLD_LINE_HEAD.unchanged}${TOOLS_FILE} at the
|
|
|
8215
8278
|
var ROOT_TOOLS_FAILED = `${SCAFFOLD_LINE_HEAD.failed}${TOOLS_FILE} at the project's root, where a part of the file may stand. Add this sentence to that file, so a coding tool started at the root reads ${APPLICATION_FOLDER}/${TOOLS_FILE}: ${ROOT_SENTENCE}`;
|
|
8216
8279
|
var ROOT_CLAUDE_UNCHANGED = `${SCAFFOLD_LINE_HEAD.unchanged}${CLAUDE_FILE} at the project's root. Claude Code reads that file and then reads no ${TOOLS_FILE} by itself, so add this sentence to it: ${ROOT_SENTENCE}`;
|
|
8217
8280
|
var NEITHER_LEFT = Object.freeze({ tools: "written", claude: false });
|
|
8218
|
-
var ROOT_TOOLS_STEP = `Add
|
|
8219
|
-
var ROOT_TOOLS_FAILED_STEP = `Add
|
|
8220
|
-
var ROOT_CLAUDE_STEP = `Add
|
|
8281
|
+
var ROOT_TOOLS_STEP = `Add a sentence to ${TOOLS_FILE} at the project's root, which the scaffold did not change, so a coding tool started at the root reads ${APPLICATION_FOLDER}/${TOOLS_FILE}. The sentence to add: ${ROOT_SENTENCE}`;
|
|
8282
|
+
var ROOT_TOOLS_FAILED_STEP = `Add a sentence to ${TOOLS_FILE} at the project's root, which the scaffold could not write, so a coding tool started at the root reads ${APPLICATION_FOLDER}/${TOOLS_FILE}. The sentence to add: ${ROOT_SENTENCE}`;
|
|
8283
|
+
var ROOT_CLAUDE_STEP = `Add a sentence to ${CLAUDE_FILE} at the project's root, since Claude Code reads that file and then reads no ${TOOLS_FILE} by itself. The sentence to add: ${ROOT_SENTENCE}`;
|
|
8221
8284
|
var APPLICATION_FORM_REFUSAL = `${SCAFFOLD_OPTION.application} names the application the service deploys to, by its id, a lowercase UUID, as create_application answers it. ${NOTHING_WRITTEN2}`;
|
|
8222
8285
|
var RESOURCE_REFUSAL = Object.freeze({
|
|
8223
8286
|
/** A plural outside SCAFFOLD_NAME_FORM. */
|
|
8224
8287
|
resourceForm: `${SCAFFOLD_OPTION.resource} takes the plural of the resource the service keeps, the word of its table and its route: an ASCII lower-case letter, then ASCII lower-case letters and digits, at most 30 characters, such as orders. ${NOTHING_WRITTEN2}`,
|
|
8225
8288
|
/** A singular outside SCAFFOLD_NAME_FORM. */
|
|
8226
|
-
singularForm: `${SCAFFOLD_OPTION.singular} takes the singular of that resource, the word for one of its rows, in the form ${SCAFFOLD_OPTION.resource} takes
|
|
8289
|
+
singularForm: `${SCAFFOLD_OPTION.singular} takes the singular of that resource, the word for one of its rows, in the form ${SCAFFOLD_OPTION.resource} takes. That is an ASCII lower-case letter, then ASCII lower-case letters and digits, at most 30 characters, such as order. ${NOTHING_WRITTEN2}`,
|
|
8227
8290
|
/** `--singular` with no `--resource`. */
|
|
8228
|
-
noResource: `${SCAFFOLD_OPTION.singular} takes the singular of the resource ${SCAFFOLD_OPTION.resource} names, and the line names no ${SCAFFOLD_OPTION.resource}
|
|
8291
|
+
noResource: `${SCAFFOLD_OPTION.singular} takes the singular of the resource ${SCAFFOLD_OPTION.resource} names, and the line names no ${SCAFFOLD_OPTION.resource}. Name the plural with ${SCAFFOLD_OPTION.resource}, or leave ${SCAFFOLD_OPTION.singular} out. ${NOTHING_WRITTEN2}`,
|
|
8229
8292
|
/** A plural that does not end in s, with no `--singular`. */
|
|
8230
|
-
noClosingS: `${SCAFFOLD_OPTION.resource} takes a plural that ends in s where the line names no ${SCAFFOLD_OPTION.singular}, since the singular is then the plural without its closing s
|
|
8293
|
+
noClosingS: `${SCAFFOLD_OPTION.resource} takes a plural that ends in s where the line names no ${SCAFFOLD_OPTION.singular}, since the singular is then the plural without its closing s. Name the singular with ${SCAFFOLD_OPTION.singular}, such as ${SCAFFOLD_OPTION.resource} people ${SCAFFOLD_OPTION.singular} person. ${NOTHING_WRITTEN2}`,
|
|
8231
8294
|
/** The plural `s` alone, with no `--singular`: its singular would be empty. */
|
|
8232
|
-
emptySingular: `${SCAFFOLD_OPTION.resource} takes a plural longer than s alone where the line names no ${SCAFFOLD_OPTION.singular}, since the singular, the plural without its closing s, is never empty
|
|
8295
|
+
emptySingular: `${SCAFFOLD_OPTION.resource} takes a plural longer than s alone where the line names no ${SCAFFOLD_OPTION.singular}, since the singular, the plural without its closing s, is never empty. Name the singular with ${SCAFFOLD_OPTION.singular}, or a longer plural. ${NOTHING_WRITTEN2}`,
|
|
8233
8296
|
/** A singular, given or derived, of `json` (#5225): the resource's
|
|
8234
8297
|
* invalid error, invalid_<singular>, would then be invalid_json, the
|
|
8235
8298
|
* error of a body that does not parse, and a caller could no longer
|
|
8236
8299
|
* tell the two apart (PLD-L0-106). */
|
|
8237
|
-
jsonSingular: `${SCAFFOLD_OPTION.singular} takes a singular other than json, since the service answers invalid_json for a body that does not parse and invalid_ with the singular for a row it refuses
|
|
8300
|
+
jsonSingular: `${SCAFFOLD_OPTION.singular} takes a singular other than json, since the service answers invalid_json for a body that does not parse and invalid_ with the singular for a row it refuses. Name another singular with ${SCAFFOLD_OPTION.singular}, such as ${SCAFFOLD_OPTION.resource} jsons ${SCAFFOLD_OPTION.singular} document. ${NOTHING_WRITTEN2}`
|
|
8238
8301
|
});
|
|
8239
8302
|
var RESOURCE_NONE_KEPT = `${SCAFFOLD_OPTION.resource} and ${SCAFFOLD_OPTION.singular} name the resource ${RESOURCE_TEMPLATES_NAMED} keep, and the template this line writes keeps none: leave them out, or name ${RESOURCE_TEMPLATE_CHOICES}. ${NOTHING_WRITTEN2}`;
|
|
8240
8303
|
var UNKNOWN_TEMPLATE = `${SCAFFOLD_OPTION.template} names the template to write, and the command has no template of that name: it has ${listedAnd(SCAFFOLD_TEMPLATES)}, and it writes ${SCAFFOLD_DEFAULT_TEMPLATE} where the line names none. ${NOTHING_WRITTEN2}`;
|
|
8241
|
-
var ownRouteRefusal = (word) => `${SCAFFOLD_OPTION.resource} takes a plural other than ${word}, the word of a route the template serves itself
|
|
8304
|
+
var ownRouteRefusal = (word) => `${SCAFFOLD_OPTION.resource} takes a plural other than ${word}, the word of a route the template serves itself. Name another plural. ${NOTHING_WRITTEN2}`;
|
|
8242
8305
|
var JSON_SINGULAR = "json";
|
|
8243
8306
|
var resourceNamesOf = (call, template) => {
|
|
8244
8307
|
const plural = call.named[SCAFFOLD_OPTION.resource];
|
|
@@ -8259,7 +8322,7 @@ var resourceNamesOf = (call, template) => {
|
|
|
8259
8322
|
var CARRIED_FIELDS = Object.freeze([Object.freeze({ name: "title", kind: "text" })]);
|
|
8260
8323
|
var FIELD_KINDS_CLAUSE = `the kind ${orListed(SCAFFOLD_FIELD_KINDS)}`;
|
|
8261
8324
|
var COLUMN_MEANING = Object.freeze({ id: "the key column", owner: "the column of the row's owner" });
|
|
8262
|
-
var reservedFieldRefusal = (columns) => `${SCAFFOLD_OPTION.fields} takes a field name other than ${columns.join(" or ")}, ${listedAnd(columns.map((column) => COLUMN_MEANING[column] ?? `the column ${column}`))} the migration writes itself, and ${FIELD_KINDS_CLAUSE}
|
|
8325
|
+
var reservedFieldRefusal = (columns) => `${SCAFFOLD_OPTION.fields} takes a field name other than ${columns.join(" or ")}, ${listedAnd(columns.map((column) => COLUMN_MEANING[column] ?? `the column ${column}`))} the migration writes itself, and ${FIELD_KINDS_CLAUSE}. Name the field another way. ${NOTHING_WRITTEN2}`;
|
|
8263
8326
|
var FIELD_REFUSAL = Object.freeze({
|
|
8264
8327
|
/** A field with no colon or more than one, an empty field among them. */
|
|
8265
8328
|
listForm: `${SCAFFOLD_OPTION.fields} takes fields separated by commas, each a name, a colon, and a kind, such as name:text,message:text, ${FIELD_KINDS_CLAUSE}. ${NOTHING_WRITTEN2}`,
|
|
@@ -8733,7 +8796,7 @@ var applicationStandsLines = (template, takeLines) => {
|
|
|
8733
8796
|
return [
|
|
8734
8797
|
first,
|
|
8735
8798
|
...takes.map(
|
|
8736
|
-
(take, index) => `To add the ${take.entry} package to the application that stands,
|
|
8799
|
+
(take, index) => `To add the ${take.entry} package to the application that stands, follow ${take.follow}, read_documentation's page ${take.page}. Run this line at the project's root first: ${lines[index]}`
|
|
8737
8800
|
),
|
|
8738
8801
|
STANDS_DEPLOY_LINE
|
|
8739
8802
|
];
|
|
@@ -8801,14 +8864,16 @@ var takeMadeLines = (template, entries, lineOf) => {
|
|
|
8801
8864
|
return [first, `${opening}, which takes ${taken} itself.`];
|
|
8802
8865
|
}
|
|
8803
8866
|
const lines = again.map(lineOf);
|
|
8804
|
-
if (again.length === 1) return [first, `${opening}
|
|
8867
|
+
if (again.length === 1) return [first, `${opening}. Then run this line at the project's root, which takes the ${again[0]} entry again into the new ${APPLICATION_FOLDER}/: ${lines[0]}`];
|
|
8805
8868
|
return [
|
|
8806
8869
|
first,
|
|
8807
|
-
`${opening}
|
|
8870
|
+
`${opening}. Then run each of these ${LINE_COUNT_WORDS[lines.length] ?? lines.length} lines at the project's root, in order, which take the ${listedAnd(again)} entries again into the new ${APPLICATION_FOLDER}/:`,
|
|
8808
8871
|
...lines
|
|
8809
8872
|
];
|
|
8810
8873
|
};
|
|
8811
|
-
var STAGE_NOT_OWN = `Something that is not this run's own stands at ${STAGE_FOLDER} in the project's root, the name the scaffold writes under
|
|
8874
|
+
var STAGE_NOT_OWN = `Something that is not this run's own stands at ${STAGE_FOLDER} in the project's root, the name the scaffold writes under. An earlier run's files under another template or another resource name may be among it. Move or remove ${STAGE_FOLDER}, then run the line again. ${NOTHING_WRITTEN2}`;
|
|
8875
|
+
var startedRootScaffoldRefusal = (names) => `The project's root holds ${startedWordsOf(names)} and no package.json, so an application may be started there, and the scaffold would write app/ beside it. To add a library package to that application, add its package.json at the root, then run ${LIBRARY_SUBCOMMAND} take there. To start a new service, run this line in a new folder, or name one with ${SCAFFOLD_OPTION.path}. ${NOTHING_WRITTEN2}`;
|
|
8876
|
+
var ROOT_PACKAGE_LINE = "A package.json stands at the project's root, so the new application goes in app/ beside it.";
|
|
8812
8877
|
var folderRefusalOf = (io2, options, files, takeLines, lineOf) => {
|
|
8813
8878
|
const { root } = options;
|
|
8814
8879
|
for (let at = root; ; at = dirname6(at)) {
|
|
@@ -8828,6 +8893,8 @@ var folderRefusalOf = (io2, options, files, takeLines, lineOf) => {
|
|
|
8828
8893
|
const taken = takeMadeEntriesOf(io2, app);
|
|
8829
8894
|
return taken !== null ? takeMadeLines(options.template, taken, lineOf) : applicationStandsLines(options.template, takeLines);
|
|
8830
8895
|
}
|
|
8896
|
+
const started = startedAtRootOf(io2, root);
|
|
8897
|
+
if (started.length > 0 && !started.includes("package.json")) return [startedRootScaffoldRefusal(started)];
|
|
8831
8898
|
if (!heldInside(io2, root, app)) return [scaffoldLinkSentence(`The application's folder, ${APPLICATION_FOLDER}/,`)];
|
|
8832
8899
|
const staged = io2.stat(stage);
|
|
8833
8900
|
if (staged === null && danglingAt(io2, root, STAGE_FOLDER)) return [notOwn];
|
|
@@ -8916,7 +8983,7 @@ var ApplicationArrived = class extends Error {
|
|
|
8916
8983
|
};
|
|
8917
8984
|
var arrivedLine = (template, takeLines) => `${APPLICATION_FOLDER}/ took a file or a folder while the scaffold ran, so an application stands there and the template was not written${standsWayOn(template, takeLines)}`;
|
|
8918
8985
|
var ARRIVED_NO_FOLDER_LINE = `Something that is no folder came to stand at ${APPLICATION_FOLDER} while the scaffold ran, and the template was not written: move it, then run the same line again.`;
|
|
8919
|
-
var SCAFFOLD_AFTER_OWN_ERROR = `where the project's root holds no ${APPLICATION_FOLDER}/, or an empty one, the application was not written, and the same line run again writes it
|
|
8986
|
+
var SCAFFOLD_AFTER_OWN_ERROR = `where the project's root holds no ${APPLICATION_FOLDER}/, or an empty one, the application was not written, and the same line run again writes it. Where ${APPLICATION_FOLDER}/ holds the service's files, the ${LIBRARY_SUBCOMMAND} take line for each entry the template takes, run at the project's root, then npm test in ${APPLICATION_FOLDER}/, finish it where the template takes entries. Where it takes none, npm install and then npm test in ${APPLICATION_FOLDER}/ finish it`;
|
|
8920
8987
|
var printEach = (io2, text) => {
|
|
8921
8988
|
for (const line of text.split("\n")) io2.print(line);
|
|
8922
8989
|
};
|
|
@@ -9055,12 +9122,14 @@ async function scaffold(call, io2) {
|
|
|
9055
9122
|
if (arrived !== null) return refuse(...arrived);
|
|
9056
9123
|
const toolsStands = standsAt(io2, root, TOOLS_FILE);
|
|
9057
9124
|
const claudeStands = standsAt(io2, root, CLAUDE_FILE);
|
|
9125
|
+
const rootPackage = startedAtRootOf(io2, root).includes("package.json");
|
|
9058
9126
|
let ended = false;
|
|
9059
9127
|
release = io2.whenEnded(() => {
|
|
9060
9128
|
ended = true;
|
|
9061
9129
|
printEach(io2, signalEndedLine(stateNow(), template, takeLines));
|
|
9062
9130
|
});
|
|
9063
9131
|
reached = "writes";
|
|
9132
|
+
io2.print(rootPackage ? ROOT_PACKAGE_LINE : APPLICATION_IN_APP_LINE);
|
|
9064
9133
|
if (io2.stat(root) === null) io2.makeFolder(root);
|
|
9065
9134
|
if (held.fresh) io2.makeFolder(held.library);
|
|
9066
9135
|
writeTemplate(io2, stage, app, composedFilesOf(files, held.packages, minute, options.application));
|
|
@@ -9126,9 +9195,9 @@ async function installAndTest(io2, app, template, taken, origin, left) {
|
|
|
9126
9195
|
return COMMAND_STATUS.done;
|
|
9127
9196
|
}
|
|
9128
9197
|
var SCAFFOLD_CREDENTIAL = LIBRARY_CREDENTIAL;
|
|
9129
|
-
var SCAFFOLD_READS = `under a template that takes library entries
|
|
9198
|
+
var SCAFFOLD_READS = `what library take reads under a template that takes library entries: ${LIBRARY_READS}. Under a template that takes none, it reads no credential`;
|
|
9130
9199
|
var SCAFFOLD_AFTER_LOGIN = `Once ${LOGIN_SUBCOMMAND} has printed authorized:, run this ${SCAFFOLD_SUBCOMMAND} line again. ${NOTHING_WRITTEN2}`;
|
|
9131
|
-
var SCAFFOLD_SIGN_IN_WHY = `A template that takes library entries needs a sign-in, since the library's reads are under a credential
|
|
9200
|
+
var SCAFFOLD_SIGN_IN_WHY = `A template that takes library entries needs a sign-in, since the library's reads are under a credential. The ${SCAFFOLD_DEFAULT_TEMPLATE} template, which takes none, runs without one.`;
|
|
9132
9201
|
var SCAFFOLD_ROW = Object.freeze({
|
|
9133
9202
|
name: SCAFFOLD_SUBCOMMAND,
|
|
9134
9203
|
summary: `Writes a new service from a template into a project, ${SCAFFOLD_DEFAULT_TEMPLATE} where the line names no ${SCAFFOLD_OPTION.template}. It writes the template's files under app/, runs npm install and npm test there, and prints the next steps. It starts a new application, and writes over no file. Some templates take library entries: database-service takes the Database entry, and web-app takes three. Such a template takes each entry as library take does, under the same credential and with the same checks, before it writes anything. It fills the manifest's packages member from the library folder's rows. With no credential, it writes nothing, and prints the login route and this line to run again once you are signed in. A template that takes no entry reads no credential.`,
|
|
@@ -9139,16 +9208,17 @@ var SCAFFOLD_ROW = Object.freeze({
|
|
|
9139
9208
|
statuses: {
|
|
9140
9209
|
done: "the files written and installed, the entries taken where the template takes any, and the tests passed",
|
|
9141
9210
|
failed: "the files written, and npm install or the tests failed",
|
|
9142
|
-
unknown: "the run stopped before its end
|
|
9211
|
+
unknown: "the run stopped before its end. An interrupt or another signal ended npm, or the command met an error of its own, or something came to stand at app while it wrote. The printed sentence names the way on",
|
|
9143
9212
|
nothing_started: "nothing was written"
|
|
9144
9213
|
},
|
|
9145
9214
|
note: [
|
|
9146
9215
|
`Run the ${SCAFFOLD_SUBCOMMAND} line at the root of a new project, or name the root with ${SCAFFOLD_OPTION.path}, which the command makes where it does not stand. A line that names no ${SCAFFOLD_OPTION.template} writes ${SCAFFOLD_DEFAULT_TEMPLATE}, which takes no library entry and makes no request, and says so in a line before the files it wrote. Under scheduled-job, the minute of the schedule's cron in the manifest is the minute of the hour, in UTC, at which the run started.`,
|
|
9216
|
+
`${SCAFFOLD_SUBCOMMAND} writes the service in ${APPLICATION_FOLDER}/, and before the files it lists, says so and why. Where a package.json stands at the root, it writes ${APPLICATION_FOLDER}/ beside it all the same. It refuses, writing nothing, where the root holds src/ or a source file such as server.mjs and no package.json.`,
|
|
9147
9217
|
`With ${SCAFFOLD_OPTION.application}, the manifest names that application in its application member, on the line after manifest_version, which binds the folder to it before any deploy: a deploy or check line naming another application is refused. Where absent, the first deploy writes the member.`,
|
|
9148
|
-
`Under ${listedAnd(RESOURCE_TEMPLATES)}, with ${SCAFFOLD_OPTION.resource},
|
|
9149
|
-
`A root that holds neither ${LIBRARY_FOLDER}/ nor ${POINTER_FILE} takes a ${LIBRARY_FOLDER}/ of its own. Where the root holds no ${TOOLS_FILE}, it writes one of one sentence, which sends a coding tool to ${APPLICATION_FOLDER}/${TOOLS_FILE}
|
|
9150
|
-
`${SCAFFOLD_SUBCOMMAND} leaves ${COMMAND_CREDENTIAL_VARIABLES.join(" and ")} out of what npm install and npm test receive. An interrupt reaches npm, the command ends after it, and after an interrupted install the tests are not started. A signal that arrives after the command's first write and before npm starts is acted on once the writes are made
|
|
9151
|
-
`Where the tests fail, they failed on the files as written, and the printed line says to report it
|
|
9218
|
+
`Under ${listedAnd(RESOURCE_TEMPLATES)}, with ${SCAFFOLD_OPTION.resource}, it writes the same service under the resource's names, its table, its route, its functions, and its file names among them. Name ${SCAFFOLD_OPTION.singular} too where the singular is not the plural without its closing s. It runs the tests on the files as written. It refuses, writing nothing, where the root's ${APPLICATION_FOLDER}/ holds any file or folder, and it takes no option that overwrites. Where the template takes entries, its sentence names the ${LIBRARY_SUBCOMMAND} take line for each, which adds the package to an application that stands.`,
|
|
9219
|
+
`A root that holds neither ${LIBRARY_FOLDER}/ nor ${POINTER_FILE} takes a ${LIBRARY_FOLDER}/ of its own. Where the root holds no ${TOOLS_FILE}, it writes one of one sentence, which sends a coding tool to ${APPLICATION_FOLDER}/${TOOLS_FILE}. It changes no file that stands at the root, and where an ${TOOLS_FILE} or a ${CLAUDE_FILE} stands there it prints the sentence to add.`,
|
|
9220
|
+
`${SCAFFOLD_SUBCOMMAND} leaves ${COMMAND_CREDENTIAL_VARIABLES.join(" and ")} out of what npm install and npm test receive. An interrupt reaches npm, the command ends after it, and after an interrupted install the tests are not started. A signal that arrives after the command's first write and before npm starts is acted on once the writes are made. The command then prints the way on, starts no npm, and ends with 128 and the signal's number.`,
|
|
9221
|
+
`Where the tests fail, they failed on the files as written, and the printed line says to report it. It names the versions the library served where the template takes entries, and the template and the command's version where it takes none.`
|
|
9152
9222
|
],
|
|
9153
9223
|
afterOwnError: SCAFFOLD_AFTER_OWN_ERROR,
|
|
9154
9224
|
run: scaffold
|
|
@@ -9187,13 +9257,13 @@ var SESSION_OPTIONS = Object.freeze([
|
|
|
9187
9257
|
name: SESSION_OPTION.endUser,
|
|
9188
9258
|
value: "<end user id or label>",
|
|
9189
9259
|
required: true,
|
|
9190
|
-
description: "The test end user, by the id or the label
|
|
9260
|
+
description: "The test end user, by the id or the label test_end_users answered with act create."
|
|
9191
9261
|
},
|
|
9192
9262
|
{
|
|
9193
9263
|
name: SESSION_OPTION.out,
|
|
9194
9264
|
value: "<path>",
|
|
9195
9265
|
required: true,
|
|
9196
|
-
description: `The cookie jar file the session's cookie is written into, which curl -b reads. Its folder must stand. A file that stands there is refused unless the line names ${SESSION_OPTION.force}
|
|
9266
|
+
description: `The cookie jar file the session's cookie is written into, which curl -b reads. Its folder must stand. A file that stands there is refused unless the line names ${SESSION_OPTION.force}. A path inside the folder the command runs in is refused where that folder holds package.json, since a deploy from it would upload the jar.`
|
|
9197
9267
|
},
|
|
9198
9268
|
{
|
|
9199
9269
|
name: SESSION_OPTION.origin,
|
|
@@ -9215,9 +9285,9 @@ var SESSION_CREDENTIAL = Object.freeze({
|
|
|
9215
9285
|
call: `the ${SIGN_IN_TEST_END_USER_ACTION} call`,
|
|
9216
9286
|
mintedToken: true,
|
|
9217
9287
|
piped: false,
|
|
9218
|
-
places:
|
|
9288
|
+
places: NO_INPUT_PLACES
|
|
9219
9289
|
});
|
|
9220
|
-
var SESSION_READS = `no standard input
|
|
9290
|
+
var SESSION_READS = `no standard input. It reads a minted token from ${MINTED_TOKEN_VARIABLE} where that is set. Otherwise it reads this computer's token for the platform's address from its credential file, which ${LOGIN_SUBCOMMAND} and ${AUTHORIZE_SUBCOMMAND} write, and sends it only to that address. With neither, it prints the variable route first, then the login route where the address is admitted`;
|
|
9221
9291
|
var missingOptionSentence = (option) => `${SESSION_SUBCOMMAND} needs ${option.name} ${option.value}, as the line a ${SIGN_IN_TEST_END_USER_ACTION} call answers names it. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9222
9292
|
var ENVIRONMENT_REFUSAL = `${SESSION_OPTION.environment} names the environment the end user signs in to, ${COMMAND_ENVIRONMENTS.join(" or ")}. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9223
9293
|
var OUT_STANDS_REFUSAL = `A file stands where ${SESSION_OPTION.out} names the cookie jar, and ${SESSION_SUBCOMMAND} replaces one only where the line names ${SESSION_OPTION.force}. Name a new file, or add ${SESSION_OPTION.force} to replace it. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
@@ -9463,12 +9533,12 @@ var SESSION_ROW = Object.freeze({
|
|
|
9463
9533
|
reads: SESSION_READS,
|
|
9464
9534
|
statuses: {
|
|
9465
9535
|
done: `the end user was signed in and the session's cookie written to the file ${SESSION_OPTION.out} names`,
|
|
9466
|
-
failed: "the platform refused the sign-in, the printed lines
|
|
9536
|
+
failed: "the platform refused the sign-in, and the printed lines name the platform's error and detail. Or the end user was signed in, and the cookie jar could not be written, so the session's cookie was not kept",
|
|
9467
9537
|
unknown: "the sign-in call drew no answer, was redirected, failed, or drew an answer that is neither a refusal nor a session, and nothing was written",
|
|
9468
|
-
nothing_started: `nothing was sent, or nothing was written
|
|
9538
|
+
nothing_started: `nothing was sent, or nothing was written. An option, the file ${SESSION_OPTION.out} names, or the credential was refused. Or no credential was read, and the two routes to one were printed. Or the platform was too old, or the jar's folder admitted no write`
|
|
9469
9539
|
},
|
|
9470
9540
|
note: [
|
|
9471
|
-
`A session line is the one your tool's ${SIGN_IN_TEST_END_USER_ACTION} call answers where the platform serves that action, and it carries no credential. Run it once: it signs the test end user in and writes the session's cookie into the file ${SESSION_OPTION.out} names
|
|
9541
|
+
`A session line is the one your tool's ${SIGN_IN_TEST_END_USER_ACTION} call answers where the platform serves that action, and it carries no credential. Run it once: it signs the test end user in and writes the session's cookie into the file ${SESSION_OPTION.out} names. The file is in the cookie file form curl -b reads, so the session's value never passes through your tool. Requests then carry the cookie with curl -b and that path.`,
|
|
9472
9542
|
`${SESSION_SUBCOMMAND} writes the cookie jar under another name beside it, owner-only, and renames it into place, and it removes that file where the run ends before the rename. It writes over a file that stands only where the line names ${SESSION_OPTION.force}. It refuses a path inside the folder the command runs in where that folder holds package.json, since a deploy from it would upload the jar.`
|
|
9473
9543
|
],
|
|
9474
9544
|
afterOwnError: `whether the end user was signed in is unknown, and a file ${SESSION_OPTION.out} names may now stand, so run the line again, adding ${SESSION_OPTION.force} where it does`,
|
|
@@ -9491,7 +9561,7 @@ var TOKEN_CREDENTIAL = Object.freeze({
|
|
|
9491
9561
|
mintedToken: false,
|
|
9492
9562
|
bare: `under ${TOKEN_ACTS[0]}, it reads none, and prints the challenge for a ${MINT_TOKEN_ACTION} call`
|
|
9493
9563
|
});
|
|
9494
|
-
var TERMINAL_RULE_SENTENCE = `${TOKEN_SUBCOMMAND} ${TOKEN_CLAIM_ACT} prints a token's value, so it runs only where its standard error is a terminal, as in a terminal of your own outside the AI coding tool, its standard output piped or not
|
|
9564
|
+
var TERMINAL_RULE_SENTENCE = `${TOKEN_SUBCOMMAND} ${TOKEN_CLAIM_ACT} prints a token's value, so it runs only where its standard error is a terminal, as in a terminal of your own outside the AI coding tool, its standard output piped or not. Here it is not, as under an AI coding tool's shell, so nothing was read and nothing was sent. Run the line yourself in a terminal outside the AI coding tool, piping it into your secret store's command where the token is a job's.`;
|
|
9495
9565
|
async function token(call, io2) {
|
|
9496
9566
|
const claim = call.act === TOKEN_CLAIM_ACT;
|
|
9497
9567
|
const say = claim ? io2.printError ?? io2.print : io2.print;
|
|
@@ -9526,7 +9596,7 @@ async function token(call, io2) {
|
|
|
9526
9596
|
return COMMAND_STATUS.nothing_started;
|
|
9527
9597
|
}
|
|
9528
9598
|
io2.hide(code, REDACTED_TOKEN_CODE);
|
|
9529
|
-
return completeAct(io2, origin.origin, "general", code, agent.userAgent, say, (minted) => io2.printValue(minted.value));
|
|
9599
|
+
return completeAct(io2, origin.origin, "general", { code }, agent.userAgent, say, (minted) => io2.printValue(minted.value));
|
|
9530
9600
|
}
|
|
9531
9601
|
var TOKEN_ROW = Object.freeze({
|
|
9532
9602
|
name: TOKEN_SUBCOMMAND,
|
|
@@ -9539,10 +9609,10 @@ var TOKEN_ROW = Object.freeze({
|
|
|
9539
9609
|
done: "the challenge printed, or the value printed once",
|
|
9540
9610
|
failed: "the token minted was an authorized computer's, and is revoked where it could be: the printed lines name revoke_token",
|
|
9541
9611
|
unknown: "the exchange drew no answer, or an answer that is not a refusal: list_tokens shows whether a token was minted",
|
|
9542
|
-
nothing_started: "nothing was minted
|
|
9612
|
+
nothing_started: "nothing was minted. An option, the credential file, the token code, or a standard error that is no terminal was refused. Or no request was pending, the platform was too old, or the exchange was refused"
|
|
9543
9613
|
},
|
|
9544
9614
|
variables: [[ORIGIN_VARIABLE, AUTHORIZATION_ORIGIN_VARIABLE_LINE]],
|
|
9545
|
-
note: [`${TOKEN_SUBCOMMAND} ${TOKEN_ACTS[0]} may run in a tool, since it prints nothing secret. ${TOKEN_SUBCOMMAND} ${TOKEN_ACTS[1]} is yours, run in a terminal outside the AI coding tool on the same computer as the same user, and never by a tool
|
|
9615
|
+
note: [`${TOKEN_SUBCOMMAND} ${TOKEN_ACTS[0]} may run in a tool, since it prints nothing secret. ${TOKEN_SUBCOMMAND} ${TOKEN_ACTS[1]} is yours, run in a terminal outside the AI coding tool on the same computer as the same user, and never by a tool. It runs only where its standard error is a terminal, which yours stays when you pipe its standard output into your secret store's command, such as gh secret set ${MINTED_TOKEN_VARIABLE}. Read the line's origin before you run it.`],
|
|
9546
9616
|
afterOwnError: AUTHORIZE_AFTER_OWN_ERROR,
|
|
9547
9617
|
run: token
|
|
9548
9618
|
});
|
|
@@ -9787,6 +9857,7 @@ var processIo = () => ({
|
|
|
9787
9857
|
`);
|
|
9788
9858
|
},
|
|
9789
9859
|
errorIsTerminal: process.stderr.isTTY === true,
|
|
9860
|
+
outputIsTerminal: process.stdout.isTTY === true,
|
|
9790
9861
|
// The one value line (PLD-L0-94): a minted token's value alone, whole, on
|
|
9791
9862
|
// standard output; any other text is written as print writes it.
|
|
9792
9863
|
printValue: (value) => {
|
|
@@ -9896,7 +9967,7 @@ var STATUS_MEANINGS = Object.freeze([
|
|
|
9896
9967
|
["nothing_started", "nothing was started"]
|
|
9897
9968
|
]);
|
|
9898
9969
|
var WINDOWS_SENTENCE = `On Windows, write ${WINDOWS_LINE_PROGRAM} for ${LINE_PROGRAM}, because Windows PowerShell refuses ${LINE_PROGRAM} where running scripts is disabled.`;
|
|
9899
|
-
var QUOTED_PATH_SENTENCE = "On Windows, a path written between double quotes
|
|
9970
|
+
var QUOTED_PATH_SENTENCE = "On Windows, end a path written between double quotes with no backslash. cmd and Windows PowerShell 5.1 read a backslash before the closing quote as keeping the quote. The command there refuses a path that holds a double quote.";
|
|
9900
9971
|
var ON_WINDOWS = process.platform === "win32";
|
|
9901
9972
|
var pipedHead = (subcommand) => subcommand.credential === null || subcommand.credential.piped === false ? "" : subcommand.credential.bare !== void 0 || subcommand.credential.computerToken === true ? `[echo <${subcommand.credential.grant}> |] ` : `echo <${subcommand.credential.grant}> | `;
|
|
9902
9973
|
var usageLineOf = (subcommand) => {
|
|
@@ -9927,7 +9998,7 @@ var pipedLinesOf = (subcommand) => {
|
|
|
9927
9998
|
const credential = subcommand.credential;
|
|
9928
9999
|
if (credential === null || credential.piped === false) return [];
|
|
9929
10000
|
return [
|
|
9930
|
-
` ${subcommand.name} reads ${credential.aGrant} from standard input
|
|
10001
|
+
` ${subcommand.name} reads ${credential.aGrant} from standard input${credential.pipes !== void 0 ? ` ${credential.pipes}` : `, as the line ${credential.call} answers pipes it in`}.${credential.mintedToken ? ` Otherwise it reads a minted token from ${MINTED_TOKEN_VARIABLE} where that is set, or this computer's token from its credential file${credential.claim !== void 0 ? `, else ${credential.claim}` : ""}.` : credential.computerToken === true ? ` Otherwise it reads this computer's token from its credential file. It never reads a minted token from ${MINTED_TOKEN_VARIABLE}, and refuses the run where that variable is set.` : ""}${credential.bare !== void 0 ? ` ${credential.bare.charAt(0).toUpperCase()}${credential.bare.slice(1)}.` : ""}`
|
|
9931
10002
|
];
|
|
9932
10003
|
};
|
|
9933
10004
|
var readsLinesOf = (subcommand) => subcommand.reads !== void 0 ? [` ${subcommand.name} reads ${subcommand.reads}.`] : [];
|
|
@@ -9942,7 +10013,8 @@ var originVariableLine = () => {
|
|
|
9942
10013
|
};
|
|
9943
10014
|
var USER_AGENT_LINE = ` ${USER_AGENT_VARIABLE} text appended to the command's user agent on each request.`;
|
|
9944
10015
|
var ownVariableLinesOf = (subcommand) => (subcommand.variables ?? []).map(([name, does]) => ` ${name} under ${subcommand.name}, ${does}`);
|
|
9945
|
-
var statusLineOf = (status, meaning, clauses, also) => ` ${COMMAND_STATUS[status]} ${meaning}
|
|
10016
|
+
var statusLineOf = (status, meaning, clauses, also) => ` ${COMMAND_STATUS[status]} ${meaning}.${clauses.map((clause) => ` ${clause}.`).join("")}${also !== void 0 ? ` ${also}` : ""}`;
|
|
10017
|
+
var statusClauseOf = (name, clause) => `Under ${name}, ${clause}`;
|
|
9946
10018
|
var noteLinesOf2 = (subcommand) => (subcommand.note ?? []).flatMap((paragraph) => ["", paragraph]);
|
|
9947
10019
|
var usageBlockOf = (subcommandHelp) => [
|
|
9948
10020
|
"Usage:",
|
|
@@ -9977,7 +10049,7 @@ var subcommandHelpOf = (subcommand, version = commandVersion()) => {
|
|
|
9977
10049
|
lines.push(USER_AGENT_LINE, "", "Statuses:");
|
|
9978
10050
|
for (const [status, meaning, also] of STATUS_MEANINGS) {
|
|
9979
10051
|
const clause = subcommand.statuses[status];
|
|
9980
|
-
if (clause !== void 0 || also !== void 0) lines.push(statusLineOf(status, meaning, clause !== void 0 ? [
|
|
10052
|
+
if (clause !== void 0 || also !== void 0) lines.push(statusLineOf(status, meaning, clause !== void 0 ? [statusClauseOf(subcommand.name, clause)] : [], also));
|
|
9981
10053
|
}
|
|
9982
10054
|
lines.push(...noteLinesOf2(subcommand), "", SHARED_HELP_SENTENCE);
|
|
9983
10055
|
return lines.join("\n");
|
|
@@ -9990,7 +10062,7 @@ var grantArgumentSentence = () => "An argument holds a grant. The command never
|
|
|
9990
10062
|
var tokenArgumentSentence = () => `An argument holds a minted token. The command never takes a credential from its arguments: it reads a minted token from ${MINTED_TOKEN_VARIABLE} alone. A tool connected by a minted token, or an unattended run, sets that variable for the one line, from where the tool keeps the token or from your CI's secret store. Nothing was sent and nothing was started.`;
|
|
9991
10063
|
var NOTHING_STARTED2 = "Nothing was started.";
|
|
9992
10064
|
var unquoted = (value) => value.length >= 2 && value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
|
|
9993
|
-
var quoteInPathSentence = (name) => `${name} names a path, and its value holds a double quote, which no path on Windows holds. The usual cause is a
|
|
10065
|
+
var quoteInPathSentence = (name) => `${name} names a path, and its value holds a double quote, which no path on Windows holds. The usual cause is a backslash before the path's closing quote. cmd and Windows PowerShell 5.1 read it as keeping the quote, and join the arguments that follow into the value. Write the path with no backslash at its end. ${NOTHING_STARTED2}`;
|
|
9994
10066
|
var emptyValueSentence = (name) => `${name} was given an empty value, as an unset shell variable gives. Name its value, or leave ${name} out where the line means its default. ${NOTHING_STARTED2}`;
|
|
9995
10067
|
var valuePlacesOf = (argv2) => {
|
|
9996
10068
|
const places = /* @__PURE__ */ new Set();
|
|
@@ -10072,6 +10144,9 @@ var nodeFloorRefusal = (found, version = commandVersion()) => {
|
|
|
10072
10144
|
return major !== null && major < COMMAND_NODE_FLOOR ? nodeFloorSentence(version, found) : null;
|
|
10073
10145
|
};
|
|
10074
10146
|
var versionLineOf = (version, found) => `${TURNZERO_CLOUD_PACKAGE} ${version} (Node.js ${found})`;
|
|
10147
|
+
var SETUP_GUIDE = "https://turnzero.ai/cloud/getting-started/set-up/";
|
|
10148
|
+
var SETUP_PROMPT = "Set me up with Turn Zero Cloud and a small demo app. Read https://turnzero.ai/cloud/getting-started/set-up/index.md in full and follow the steps.";
|
|
10149
|
+
var showOnboarding = (io2) => io2.outputIsTerminal === true && !io2.env.CI;
|
|
10075
10150
|
async function runGuarded(argv2, io2) {
|
|
10076
10151
|
const found = io2.nodeVersion ?? process.versions.node;
|
|
10077
10152
|
const version = commandVersion();
|
|
@@ -10083,6 +10158,8 @@ async function runGuarded(argv2, io2) {
|
|
|
10083
10158
|
const parsed = parseArguments(argv2, io2.windows ?? ON_WINDOWS);
|
|
10084
10159
|
if (parsed.kind === "help") {
|
|
10085
10160
|
io2.print((parsed.subcommand === void 0 ? null : subcommandHelpText(parsed.subcommand.name, version)) ?? shortUsageText(version));
|
|
10161
|
+
if (parsed.subcommand === void 0 && showOnboarding(io2)) io2.print(`
|
|
10162
|
+
New to Turn Zero Cloud? Start here: ${SETUP_GUIDE}`);
|
|
10086
10163
|
return COMMAND_STATUS.done;
|
|
10087
10164
|
}
|
|
10088
10165
|
if (parsed.kind === "version") {
|
|
@@ -10096,6 +10173,12 @@ async function runGuarded(argv2, io2) {
|
|
|
10096
10173
|
const npx = "ranThroughNpx" in io2 && io2.ranThroughNpx === true;
|
|
10097
10174
|
const help = npx ? npxFormOf(`${SERVED} --help`, originReadOf(io2.env[ORIGIN_VARIABLE])) : `${SERVED} --help`;
|
|
10098
10175
|
say(`${io2.windows ?? ON_WINDOWS ? windowsLineOf(help) : help} prints the usage.`);
|
|
10176
|
+
if (argv2.length === 0 && showOnboarding(io2)) {
|
|
10177
|
+
say(`
|
|
10178
|
+
Get started: ${SETUP_GUIDE}`);
|
|
10179
|
+
say(`Paste this into Claude, ChatGPT, or Codex:
|
|
10180
|
+
${SETUP_PROMPT}`);
|
|
10181
|
+
}
|
|
10099
10182
|
return COMMAND_STATUS.nothing_started;
|
|
10100
10183
|
}
|
|
10101
10184
|
const upgrade = startUpgradeCheck(io2, parsed.subcommand.name, parsed.call.named, version);
|
|
@@ -10106,7 +10189,7 @@ async function runGuarded(argv2, io2) {
|
|
|
10106
10189
|
}
|
|
10107
10190
|
var outputOf = (argv2, io2) => argv2[0] === TOKEN_SUBCOMMAND && argv2[1] === TOKEN_CLAIM_ACT ? io2.printError ?? io2.print : io2.print;
|
|
10108
10191
|
var NOTHING_REACHED = "no subcommand was reached, so nothing was started";
|
|
10109
|
-
var ownErrorSentence = (error, afterOwnError = NOTHING_REACHED) => `The command stopped on an error of its own (${error instanceof Error ? error.message : String(error)})
|
|
10192
|
+
var ownErrorSentence = (error, afterOwnError = NOTHING_REACHED) => `The command stopped on an error of its own (${error instanceof Error ? error.message : String(error)}). ${afterOwnError.charAt(0).toUpperCase()}${afterOwnError.slice(1)}.`;
|
|
10110
10193
|
var ownErrorLineOf = (argv2, io2, error) => {
|
|
10111
10194
|
const parsed = parseArguments(argv2, io2.windows ?? ON_WINDOWS);
|
|
10112
10195
|
return ownErrorSentence(error, parsed.kind === "run" ? afterOwnErrorOf(parsed.subcommand, parsed.call, io2) : NOTHING_REACHED);
|