turnzero 0.16.1 → 0.17.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/dist/turnzero-cloud.mjs +125 -34
- package/package.json +2 -2
package/dist/turnzero-cloud.mjs
CHANGED
|
@@ -201,6 +201,11 @@ var LOCAL_RUN_SETTING = Object.freeze({
|
|
|
201
201
|
var LOCAL_RUN_ENVIRONMENT = "local";
|
|
202
202
|
var LOCAL_RUN_CREDENTIAL_SETTINGS = Object.freeze([LOCAL_RUN_SETTING.platformCredential, LOCAL_RUN_SETTING.databaseConnection]);
|
|
203
203
|
var ENVIRONMENT_FILE_NAME = ".env";
|
|
204
|
+
var provisionBareLineOf = (call) => commandLineOf(call.origin, PROVISION_SUBCOMMAND, {
|
|
205
|
+
options: [[PROVISION_OPTION.application, call.application]],
|
|
206
|
+
...call.origin !== DEFAULT_COMMAND_ORIGIN ? { origin: call.origin } : {},
|
|
207
|
+
...call.envFile !== void 0 ? { quoted: [[PROVISION_OPTION.envFile, call.envFile]] } : {}
|
|
208
|
+
});
|
|
204
209
|
var DEPLOY_ROTATE_FLAG = "--rotate-database-credential";
|
|
205
210
|
var DEPLOY_PREPARED_LINE_HEAD = "prepared: ";
|
|
206
211
|
var CREDENTIAL_LOOKUP_BUSY = "credential_lookup_busy";
|
|
@@ -368,7 +373,7 @@ function compareWireVersions(a, b) {
|
|
|
368
373
|
|
|
369
374
|
// src/turnzero_cloud_command/plane_wire.ts
|
|
370
375
|
var PLANE_WIRE_NEEDED = Object.freeze({
|
|
371
|
-
action_payloads: "2026-10-
|
|
376
|
+
action_payloads: "2026-10-14.2",
|
|
372
377
|
management_api: "2026-10-10.5",
|
|
373
378
|
wire_errors: "2026-10-10.6"
|
|
374
379
|
});
|
|
@@ -777,6 +782,11 @@ var pipeWholeWayOn = (aWhat, call, nothingPiped = false) => `Pipe ${aWhat} whole
|
|
|
777
782
|
// src/turnzero_cloud_command/line_form.ts
|
|
778
783
|
var onWindows = (io2) => io2.windows ?? process.platform === "win32";
|
|
779
784
|
var ranThroughNpx = (io2) => io2.ranThroughNpx ?? false;
|
|
785
|
+
var printedLineOf = (io2, line, origin) => {
|
|
786
|
+
const run = ranThroughNpx(io2) ? npxFormOf(line, origin) : line;
|
|
787
|
+
return onWindows(io2) ? windowsLineOf(run) : run;
|
|
788
|
+
};
|
|
789
|
+
var provisionLineFor = (io2, call) => printedLineOf(io2, provisionBareLineOf(call), call.origin);
|
|
780
790
|
var loginLineFor = (io2, origin) => {
|
|
781
791
|
const line = loginLineOf(origin);
|
|
782
792
|
return onWindows(io2) ? windowsLineOf(line) : line;
|
|
@@ -801,6 +811,7 @@ var unendedReadRefusal = (declared, reading, nothingArrived = false) => unendedR
|
|
|
801
811
|
declared.computerToken === true ? grantWaysOn(declared) : pipeWholeWayOn(declared.aGrant, declared.call, declared.mintedToken && nothingArrived)
|
|
802
812
|
);
|
|
803
813
|
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.`;
|
|
814
|
+
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();
|
|
804
815
|
var expired = (expiresAt, now) => expiresAt !== null && Date.parse(expiresAt) <= now.getTime();
|
|
805
816
|
async function readCredential(io2, declared, again, line = { named: void 0, closing: "" }) {
|
|
806
817
|
if (declared.piped !== false) {
|
|
@@ -841,7 +852,7 @@ async function readCredential(io2, declared, again, line = { named: void 0, clos
|
|
|
841
852
|
if (!("state" in read2)) return { refusal: readRefusalOf(read2, place.path) };
|
|
842
853
|
const entry = read2.state.origins.get(origin.origin);
|
|
843
854
|
const unauthorized = (expiredAt) => ({
|
|
844
|
-
refusal:
|
|
855
|
+
refusal: noCredentialRefusal(declared, origin.origin, expiredAt, again),
|
|
845
856
|
unauthorized: { origin: origin.origin, expiredAt }
|
|
846
857
|
});
|
|
847
858
|
if (entry === void 0) return unauthorized(null);
|
|
@@ -1048,8 +1059,9 @@ function keepPendingRequest(io2, origin, say) {
|
|
|
1048
1059
|
dropCredentialWriting(io2, place, tag);
|
|
1049
1060
|
}
|
|
1050
1061
|
opening = standing !== void 0 ? replacedRequestSentence(origin, standing) : "";
|
|
1062
|
+
return { pending, opening, standing: standing !== void 0 ? { kind: "replaced", expired: standing } : { kind: "new" } };
|
|
1051
1063
|
}
|
|
1052
|
-
return { pending, opening };
|
|
1064
|
+
return { pending, opening, standing: { kind: "reused" } };
|
|
1053
1065
|
}
|
|
1054
1066
|
var TOKEN_CALL_BOUND_SECONDS = 60;
|
|
1055
1067
|
var EXCHANGE_ACT = "exchange";
|
|
@@ -2848,6 +2860,7 @@ var headCommitOf = (folder, reads = FILE_SYSTEM_READS) => {
|
|
|
2848
2860
|
};
|
|
2849
2861
|
|
|
2850
2862
|
// src/turnzero_cloud_command/options.ts
|
|
2863
|
+
var afterOwnErrorOf = (row, call, io2) => typeof row.afterOwnError === "function" ? row.afterOwnError(call, io2) : row.afterOwnError;
|
|
2851
2864
|
var PATH_OPTION_NAMES = Object.freeze([DEPLOY_OPTION.path, SECRET_OPTION.valueFile, PROVISION_OPTION.envFile]);
|
|
2852
2865
|
var DEPLOY_OPTIONS = Object.freeze([
|
|
2853
2866
|
{
|
|
@@ -3252,7 +3265,6 @@ async function prepareDeploy(io2, options, zipSha256Value, bearer, userAgent, re
|
|
|
3252
3265
|
io2.print(PREPARE_AGAIN);
|
|
3253
3266
|
return nothingStarted;
|
|
3254
3267
|
}
|
|
3255
|
-
io2.print(preparedLineOf(prepared));
|
|
3256
3268
|
return prepared;
|
|
3257
3269
|
}
|
|
3258
3270
|
var NOTHING_STARTED = "Nothing was started.";
|
|
@@ -3372,6 +3384,7 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3372
3384
|
for (const line of unserved) io2.print(line);
|
|
3373
3385
|
return PROGRESS_EXIT_STATUS.nothing_started;
|
|
3374
3386
|
}
|
|
3387
|
+
io2.print(preparedLineOf(prepared));
|
|
3375
3388
|
const { upload, grant, environment } = prepared;
|
|
3376
3389
|
const headers = { authorization: `Bearer ${grant}`, "user-agent": agent.userAgent };
|
|
3377
3390
|
const stop = commandStopSentence(options.application, environment);
|
|
@@ -3529,7 +3542,7 @@ var DEPLOY_ROW = Object.freeze({
|
|
|
3529
3542
|
`A progress read answered 502, 503, or 504, which is often temporary, is made again, at most three more times, and an upload answered 503 credential_lookup_busy, which wrote nothing, is made again after the wait it names, at most twice.`,
|
|
3530
3543
|
`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, and ${HASH_SUBCOMMAND} ${HASH_OPTION.list} names every file; no such line prints for a .zip file.`,
|
|
3531
3544
|
`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, and a warning for a health path that no file names, package files and documentation set aside, each on a line of its own and only where there is something to name. The health warning refuses nothing.`,
|
|
3532
|
-
`After the
|
|
3545
|
+
`After the preparing call, where the platform's answer names the recorded manifest's health path and says the environment serves no version, and 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, and 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.`
|
|
3533
3546
|
],
|
|
3534
3547
|
afterOwnError: DEPLOY_AFTER_OWN_ERROR,
|
|
3535
3548
|
run: (call, io2) => deploy(call.named, io2, call.flags)
|
|
@@ -3970,7 +3983,7 @@ var freshLineSteps = (act) => `${SECRET_ACTION[act]} from your tool with the nam
|
|
|
3970
3983
|
var secretAgain = (act) => `Call ${freshLineSteps(act)}`;
|
|
3971
3984
|
var RESTART_UNDER_STORE = `${SECRET_OPTION.restart} goes with ${SECRET_SUBCOMMAND} rotate alone: a store restarts nothing. ${NOTHING_SENT2}`;
|
|
3972
3985
|
var RESTART_UNDER_GRANT = `${SECRET_OPTION.restart} asks for a restart, and a write under a secret grant writes the value alone. Run the line without ${SECRET_OPTION.restart} and then make the restart_application call the answer's next names, or, where you wrote the line yourself, run it with no grant piped, under this computer's token. ${NOTHING_SENT2}`;
|
|
3973
|
-
var
|
|
3986
|
+
var platformMintedUnderToken = (line) => `${SECRET_OPTION.name} names a credential the platform minted for that application, which a rotation under this computer's token would re-mint and answer a value the command never prints. ${line}, run in the folder the application runs in, re-mints it and writes it into the environment file. ${NOTHING_SENT2}`;
|
|
3974
3987
|
var underTokenLine = (origin) => `No secret grant was piped, so this line runs under this computer's token for ${origin}.`;
|
|
3975
3988
|
var sentLine = (act, underGrant) => `The ${SECRET_ACTION[act]} write is being sent; wait for its answer. Before any second run, call list_secrets from your tool, whose row for the name shows when it was last written, so whether this write landed.${underGrant ? " The secret grant serves once: where this write landed, a second run of this line is normally refused secret_grant_spent." : ""}`;
|
|
3976
3989
|
var secretOptionsOf = (call, cwd) => {
|
|
@@ -4056,7 +4069,7 @@ async function secret(call, io2) {
|
|
|
4056
4069
|
return COMMAND_STATUS.nothing_started;
|
|
4057
4070
|
}
|
|
4058
4071
|
if (credential.source === "credential_file" && options.act === "rotate" && options.application !== null && (options.name === databaseCredentialNameOf(options.application) || options.name === platformCredentialNameOf(options.application))) {
|
|
4059
|
-
io2.print(
|
|
4072
|
+
io2.print(platformMintedUnderToken(provisionLineFor(io2, { origin: credential.origin, application: options.application })));
|
|
4060
4073
|
return COMMAND_STATUS.nothing_started;
|
|
4061
4074
|
}
|
|
4062
4075
|
const sent = credentialOriginOf(io2.env, credential, line);
|
|
@@ -5645,14 +5658,17 @@ var PROVISION_CREDENTIAL = Object.freeze({
|
|
|
5645
5658
|
grant: "secret grant",
|
|
5646
5659
|
aGrant: "a secret grant",
|
|
5647
5660
|
call: "the submit_manifest call naming local_run",
|
|
5648
|
-
mintedToken: true
|
|
5661
|
+
mintedToken: true,
|
|
5662
|
+
bare: `with nothing piped and neither token, it keeps a verifier and prints the challenge and the submit_manifest call naming local_run and ${CODE_CHALLENGE_MEMBER}, and a grant piped is presented with that verifier`
|
|
5649
5663
|
});
|
|
5650
5664
|
var NOTHING_REMINTED = "Nothing was re-minted, and the environment file is as it stood.";
|
|
5651
|
-
var
|
|
5652
|
-
var
|
|
5653
|
-
var
|
|
5654
|
-
var
|
|
5655
|
-
|
|
5665
|
+
var PROVISION_BARE_LINE_WORDS = commandLineOf(DEFAULT_COMMAND_ORIGIN, PROVISION_SUBCOMMAND, { options: [[PROVISION_OPTION.application, "<application id>"]] });
|
|
5666
|
+
var freshLineOf = (io2, application, origin, envFile) => provisionLineFor(io2, { origin, application, ...envFile !== void 0 ? { envFile } : {} });
|
|
5667
|
+
var freshLineSteps2 = (line) => `${line} with nothing piped, and where it prints a submit_manifest call, make that call from your tool and run the line it answers as given`;
|
|
5668
|
+
var provisionAgainOf = (line) => `Run ${freshLineSteps2(line)}.`;
|
|
5669
|
+
var againSteps = (kind, line) => kind === "grant" ? `run ${freshLineSteps2(line)}` : "run the line again";
|
|
5670
|
+
var remintAgainSentence = (kind, line) => {
|
|
5671
|
+
const steps = againSteps(kind, line);
|
|
5656
5672
|
return `${steps[0].toUpperCase()}${steps.slice(1)}: it re-mints both credentials and writes the file.`;
|
|
5657
5673
|
};
|
|
5658
5674
|
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}`;
|
|
@@ -5704,13 +5720,13 @@ var unreachedLine = (origin, error) => {
|
|
|
5704
5720
|
}
|
|
5705
5721
|
return `${PROVISION_ACTION}: the platform could not be reached: ${said}`;
|
|
5706
5722
|
};
|
|
5707
|
-
async function remint(io2, origin, credential, userAgent, application, name) {
|
|
5723
|
+
async function remint(io2, origin, credential, userAgent, application, name, verifier = null) {
|
|
5708
5724
|
let response;
|
|
5709
5725
|
try {
|
|
5710
5726
|
response = await io2.fetch(`${origin}${actionPathOf(PROVISION_ACTION)}`, {
|
|
5711
5727
|
method: "POST",
|
|
5712
5728
|
headers: { authorization: `Bearer ${credential}`, "user-agent": userAgent, "content-type": "application/json" },
|
|
5713
|
-
body: JSON.stringify({ name, application, environment: PROVISION_ENVIRONMENT }),
|
|
5729
|
+
body: JSON.stringify({ name, application, environment: PROVISION_ENVIRONMENT, ...verifier !== null ? { [CODE_VERIFIER_MEMBER]: verifier } : {} }),
|
|
5714
5730
|
redirect: "manual"
|
|
5715
5731
|
});
|
|
5716
5732
|
} catch (error) {
|
|
@@ -5753,7 +5769,20 @@ var factsOf = (member) => {
|
|
|
5753
5769
|
};
|
|
5754
5770
|
var connectionSettingOf = (facts, password) => `postgresql://${encodeURIComponent(facts.roleName)}:${encodeURIComponent(password)}@${facts.host}:5432/${encodeURIComponent(facts.dbName)}?sslmode=verify-full`;
|
|
5755
5771
|
var LIMIT_SHOWN_MAX = 80;
|
|
5756
|
-
var
|
|
5772
|
+
var UNATTENDED_JOB_VARIABLE = "CI";
|
|
5773
|
+
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`;
|
|
5774
|
+
var PROVISION_AFTER_OWN_ERROR = afterOwnErrorBy(`${PROVISION_BARE_LINE_WORDS}, with the ${PROVISION_OPTION.origin} and ${PROVISION_OPTION.envFile} the line named,`);
|
|
5775
|
+
var provisionAfterOwnError = (call, io2) => {
|
|
5776
|
+
try {
|
|
5777
|
+
const application = call.named[PROVISION_OPTION.application];
|
|
5778
|
+
if (application === void 0 || !UPLOAD_ID.test(application)) return PROVISION_AFTER_OWN_ERROR;
|
|
5779
|
+
const origin = commandOriginOf(call.named[PROVISION_OPTION.origin] ?? DEFAULT_COMMAND_ORIGIN);
|
|
5780
|
+
if ("refusal" in origin) return PROVISION_AFTER_OWN_ERROR;
|
|
5781
|
+
return afterOwnErrorBy(`${freshLineOf(io2, application, origin.origin, call.named[PROVISION_OPTION.envFile])},`);
|
|
5782
|
+
} catch {
|
|
5783
|
+
return PROVISION_AFTER_OWN_ERROR;
|
|
5784
|
+
}
|
|
5785
|
+
};
|
|
5757
5786
|
var remintUnknownSentence = (which) => `Whether the development ${which} credential was re-minted is unknown, and no value was written.`;
|
|
5758
5787
|
var DATABASE_VALUE_LOST = "The development database credential was re-minted and its value was not written, so a connection setting an earlier run wrote no longer connects.";
|
|
5759
5788
|
var PLATFORM_VALUE_LOST = `The development platform credential was re-minted and its value was not written, so a ${LOCAL_RUN_SETTING.platformCredential} an earlier run wrote is revoked.`;
|
|
@@ -5781,7 +5810,18 @@ async function provision(call, io2) {
|
|
|
5781
5810
|
);
|
|
5782
5811
|
return COMMAND_STATUS.nothing_started;
|
|
5783
5812
|
}
|
|
5784
|
-
const
|
|
5813
|
+
const namedEnvFile = call.named[PROVISION_OPTION.envFile];
|
|
5814
|
+
const read2 = await readCredential(io2, PROVISION_CREDENTIAL, provisionAgainOf(freshLineOf(io2, options.application, options.origin, namedEnvFile)), {
|
|
5815
|
+
named: call.named[PROVISION_OPTION.origin],
|
|
5816
|
+
closing: NOTHING_REMINTED
|
|
5817
|
+
});
|
|
5818
|
+
if ("unauthorized" in read2) {
|
|
5819
|
+
if (io2.env[UNATTENDED_JOB_VARIABLE] !== void 0) {
|
|
5820
|
+
io2.print(noCredentialRefusal(PROVISION_CREDENTIAL, read2.unauthorized.origin, read2.unauthorized.expiredAt, ""));
|
|
5821
|
+
return COMMAND_STATUS.nothing_started;
|
|
5822
|
+
}
|
|
5823
|
+
return firstProvisionAct(io2, call, options.application, namedEnvFile, read2.unauthorized);
|
|
5824
|
+
}
|
|
5785
5825
|
if ("refusal" in read2) {
|
|
5786
5826
|
io2.print(read2.refusal);
|
|
5787
5827
|
return COMMAND_STATUS.nothing_started;
|
|
@@ -5793,6 +5833,8 @@ async function provision(call, io2) {
|
|
|
5793
5833
|
return COMMAND_STATUS.nothing_started;
|
|
5794
5834
|
}
|
|
5795
5835
|
const sent = { ...options, origin: where.origin };
|
|
5836
|
+
const again = freshLineOf(io2, options.application, sent.origin, namedEnvFile);
|
|
5837
|
+
const verifier = read2.kind === "grant" ? pendingVerifierOf(io2, sent.origin) : null;
|
|
5796
5838
|
const tried = tryEnvironmentFile(io2, options.envFile);
|
|
5797
5839
|
if ("unwritten" in tried) {
|
|
5798
5840
|
io2.print(
|
|
@@ -5802,7 +5844,7 @@ async function provision(call, io2) {
|
|
|
5802
5844
|
}
|
|
5803
5845
|
const reminted = { database: "no", platform: "no" };
|
|
5804
5846
|
const release = io2.whenEnded(() => {
|
|
5805
|
-
for (const line of signalEndedLines(reminted, read2.kind)) io2.print(line);
|
|
5847
|
+
for (const line of signalEndedLines(reminted, read2.kind, again)) io2.print(line);
|
|
5806
5848
|
dropWritingFile(io2, options.envFile);
|
|
5807
5849
|
});
|
|
5808
5850
|
try {
|
|
@@ -5811,29 +5853,72 @@ async function provision(call, io2) {
|
|
|
5811
5853
|
io2.print(older);
|
|
5812
5854
|
return COMMAND_STATUS.nothing_started;
|
|
5813
5855
|
}
|
|
5814
|
-
return await remintAndWrite(io2, sent, agent.userAgent, read2, standing.text, reminted);
|
|
5856
|
+
return await remintAndWrite(io2, sent, agent.userAgent, { ...read2, verifier, again }, standing.text, reminted);
|
|
5815
5857
|
} finally {
|
|
5816
5858
|
release();
|
|
5817
5859
|
dropWritingFile(io2, options.envFile);
|
|
5818
5860
|
}
|
|
5819
5861
|
}
|
|
5820
|
-
var signalEndedLines = (reminted, kind) => {
|
|
5862
|
+
var signalEndedLines = (reminted, kind, line) => {
|
|
5821
5863
|
const lost = [
|
|
5822
5864
|
...reminted.database === "answered" ? [DATABASE_VALUE_LOST] : reminted.database === "unknown" ? [remintUnknownSentence("database")] : [],
|
|
5823
5865
|
...reminted.platform === "answered" ? [PLATFORM_VALUE_LOST] : reminted.platform === "unknown" ? [remintUnknownSentence("platform")] : []
|
|
5824
5866
|
];
|
|
5825
|
-
return lost.length === 0 ? [NOTHING_REMINTED] : [...lost, remintAgainSentence(kind)];
|
|
5867
|
+
return lost.length === 0 ? [NOTHING_REMINTED] : [...lost, remintAgainSentence(kind, line)];
|
|
5826
5868
|
};
|
|
5869
|
+
var pendingVerifierOf = (io2, origin) => {
|
|
5870
|
+
const read2 = readMovingCredentialFile(io2, credentialFileOf(io2), io2.print);
|
|
5871
|
+
if (!("state" in read2)) return null;
|
|
5872
|
+
hideStandingTokens(io2, read2.state);
|
|
5873
|
+
const pending = read2.state.pending.get(origin);
|
|
5874
|
+
if (pending === void 0) return null;
|
|
5875
|
+
io2.hide(pending.verifier, REDACTED_VERIFIER);
|
|
5876
|
+
return pending.verifier;
|
|
5877
|
+
};
|
|
5878
|
+
var PROVISION_CHALLENGE_HEAD = "challenge: ";
|
|
5879
|
+
var PROVISION_CALL_HEAD = "call submit_manifest with:";
|
|
5880
|
+
var MANIFEST_MEMBER_WORDS = "the application's manifest, as your tool submits it";
|
|
5881
|
+
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.`;
|
|
5882
|
+
var provisionFirstActLines = (origin, application, challenge, expiresAt, line) => [
|
|
5883
|
+
`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.`,
|
|
5884
|
+
`${PROVISION_CHALLENGE_HEAD}${challenge}`,
|
|
5885
|
+
PROVISION_CALL_HEAD,
|
|
5886
|
+
` application: ${JSON.stringify(application)}`,
|
|
5887
|
+
` manifest: ${MANIFEST_MEMBER_WORDS}`,
|
|
5888
|
+
" local_run: true",
|
|
5889
|
+
` ${CODE_CHALLENGE_MEMBER}: ${JSON.stringify(challenge)}`,
|
|
5890
|
+
`That line presents its grant with this computer's verifier, so a copy of it re-mints nothing elsewhere; it re-mints the development credentials and writes them into the environment file. The request is pending until ${expiresAt}: ${line} run again before then prints this challenge again.`
|
|
5891
|
+
];
|
|
5892
|
+
async function firstProvisionAct(io2, call, application, envFile, unauthorized) {
|
|
5893
|
+
io2.print(noTokenSentence(unauthorized.origin, unauthorized.expiredAt));
|
|
5894
|
+
const origin = authorizationOriginOf(io2.env, call.named[PROVISION_OPTION.origin], NOTHING_REMINTED);
|
|
5895
|
+
if ("refusal" in origin) {
|
|
5896
|
+
io2.print(origin.refusal);
|
|
5897
|
+
return COMMAND_STATUS.nothing_started;
|
|
5898
|
+
}
|
|
5899
|
+
const kept2 = keepPendingRequest(io2, origin.origin, io2.print);
|
|
5900
|
+
if ("status" in kept2) return kept2.status;
|
|
5901
|
+
const { pending, standing } = kept2;
|
|
5902
|
+
const opening = standing.kind === "reused" ? kept2.opening : standing.kind === "replaced" ? replacedProvisionRequestSentence(origin.origin, standing.expired.expires_at) : null;
|
|
5903
|
+
const lines = [...opening !== null ? [opening] : [], ...provisionFirstActLines(origin.origin, application, pending.challenge, pending.expires_at, freshLineOf(io2, application, origin.origin, envFile))];
|
|
5904
|
+
let last = -1;
|
|
5905
|
+
for (const [index, line] of lines.entries()) if (line.includes(pending.challenge)) last = index;
|
|
5906
|
+
for (const [index, line] of lines.entries()) {
|
|
5907
|
+
io2.print(line);
|
|
5908
|
+
if (index === last) io2.hide(pending.challenge, REDACTED_CHALLENGE);
|
|
5909
|
+
}
|
|
5910
|
+
return COMMAND_STATUS.done;
|
|
5911
|
+
}
|
|
5827
5912
|
async function remintAndWrite(io2, { application, envFile, origin }, userAgent, read2, standing, reminted = { database: "no", platform: "no" }) {
|
|
5828
5913
|
const untouched = (lines2) => {
|
|
5829
5914
|
for (const line of [...lines2, NOTHING_REMINTED]) io2.print(line);
|
|
5830
5915
|
return COMMAND_STATUS.nothing_started;
|
|
5831
5916
|
};
|
|
5832
5917
|
const lost = (lines2) => {
|
|
5833
|
-
for (const line of [...lines2, remintAgainSentence(read2.kind)]) io2.print(line);
|
|
5918
|
+
for (const line of [...lines2, remintAgainSentence(read2.kind, read2.again)]) io2.print(line);
|
|
5834
5919
|
return COMMAND_STATUS.unknown;
|
|
5835
5920
|
};
|
|
5836
|
-
const database = await remint(io2, origin, read2.credential, userAgent, application, databaseCredentialNameOf(application));
|
|
5921
|
+
const database = await remint(io2, origin, read2.credential, userAgent, application, databaseCredentialNameOf(application), read2.verifier);
|
|
5837
5922
|
reminted.database = database.kind === "answered" || database.kind === "unknown" ? database.kind : "no";
|
|
5838
5923
|
if (database.kind === "unsent") return untouched(database.lines);
|
|
5839
5924
|
if (database.kind === "unknown") return lost([...database.lines, remintUnknownSentence("database")]);
|
|
@@ -5850,7 +5935,7 @@ async function remintAndWrite(io2, { application, envFile, origin }, userAgent,
|
|
|
5850
5935
|
io2.hide(password, settingMark(facts.setting));
|
|
5851
5936
|
io2.hide(encodeURIComponent(password), settingMark(facts.setting));
|
|
5852
5937
|
}
|
|
5853
|
-
const platform = await remint(io2, origin, read2.credential, userAgent, application, platformCredentialNameOf(application));
|
|
5938
|
+
const platform = await remint(io2, origin, read2.credential, userAgent, application, platformCredentialNameOf(application), read2.verifier);
|
|
5854
5939
|
reminted.platform = platform.kind === "answered" || platform.kind === "unknown" ? platform.kind : "no";
|
|
5855
5940
|
const databaseLost = url !== null ? [DATABASE_VALUE_LOST] : [];
|
|
5856
5941
|
if (platform.kind === "unsent" || platform.kind === "refused") return url !== null ? lost([...platform.lines, ...databaseLost]) : untouched(platform.lines);
|
|
@@ -5888,7 +5973,7 @@ async function remintAndWrite(io2, { application, envFile, origin }, userAgent,
|
|
|
5888
5973
|
if (facts !== null && limitCarried !== null) {
|
|
5889
5974
|
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";
|
|
5890
5975
|
io2.print(
|
|
5891
|
-
`${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}, where 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)}.`
|
|
5976
|
+
`${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}, where 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)}.`
|
|
5892
5977
|
);
|
|
5893
5978
|
return COMMAND_STATUS.failed;
|
|
5894
5979
|
}
|
|
@@ -5907,13 +5992,16 @@ var PROVISION_ROW = Object.freeze({
|
|
|
5907
5992
|
options: PROVISION_OPTIONS,
|
|
5908
5993
|
credential: PROVISION_CREDENTIAL,
|
|
5909
5994
|
statuses: {
|
|
5910
|
-
done: "the settings written",
|
|
5995
|
+
done: "the settings written, or the first act printed its challenge and call",
|
|
5911
5996
|
failed: "the settings written without a connection limit, which the platform's answer did not carry",
|
|
5912
5997
|
unknown: "a credential was re-minted, or may have been, and its value was not written: the printed sentence names the run that re-mints again",
|
|
5913
5998
|
nothing_started: "nothing was re-minted"
|
|
5914
5999
|
},
|
|
5915
|
-
note: [
|
|
5916
|
-
|
|
6000
|
+
note: [
|
|
6001
|
+
`Run ${PROVISION_BARE_LINE_WORDS} from the folder the application runs in. With nothing piped and 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.`,
|
|
6002
|
+
`Each run revokes the development credentials the application had before it. A deployed development environment holds the revoked values until it restarts, so call restart_application on development afterwards. Its redeploy takes both re-minted credentials from custody, the connection string and the platform credential, and spends no deploy of the day. ${PROVISION_SUBCOMMAND} writes no ${PORT_SETTING}: ${RUN_SUBCOMMAND} sets it from ${RUN_OPTION.port}.`
|
|
6003
|
+
],
|
|
6004
|
+
afterOwnError: provisionAfterOwnError,
|
|
5917
6005
|
run: provision
|
|
5918
6006
|
});
|
|
5919
6007
|
|
|
@@ -7198,6 +7286,8 @@ var SHAPE_STEP = `Shape the service to your own before the first deploy: the tab
|
|
|
7198
7286
|
var WEB_SHAPE_STEP = `Shape the app to your own before the first deploy: the page in ${APPLICATION_FOLDER}/client, the table and its migration in ${APPLICATION_FOLDER}/migrations, the routes in ${APPLICATION_FOLDER}/src, and the tests, which cover those routes as written.`;
|
|
7199
7287
|
var WEB_OPEN_STEP = "Open the address the deploy answers in a browser and sign in there: the page lists the signed-in user's own rows, adds one, and answers a question under the plan's AI allowance.";
|
|
7200
7288
|
var SUBMIT_NO_SETTING_STEP = `Call submit_manifest from your tool with the application and its manifest, ${APPLICATION_FOLDER}/${APPLICATION_MANIFEST}, naming no local_run, since the service reads no setting.`;
|
|
7289
|
+
var provisionStepLineOf = (origin, windows) => machineLineOf(provisionBareLineOf({ origin, application: "<application id>" }), windows, origin);
|
|
7290
|
+
var provisionStepOf = (origin, windows) => `Call submit_manifest from your tool with the application and its manifest, ${APPLICATION_FOLDER}/${APPLICATION_MANIFEST}, naming no local_run. Then run ${freshLineSteps2("the provision line below")}, all in ${APPLICATION_FOLDER}/. The provision line: ${provisionStepLineOf(origin, windows)}`;
|
|
7201
7291
|
var RUN_STEP_BACKGROUND = "The line runs until the application ends, so start it as a background process and read its lines";
|
|
7202
7292
|
var SCHEDULED_JOB_SCHEDULE = "heartbeat";
|
|
7203
7293
|
var ENTRY_PAGES = Object.freeze({
|
|
@@ -7237,13 +7327,14 @@ var TEMPLATE_FACTS = Object.freeze({
|
|
|
7237
7327
|
routes: Object.freeze(["health"]),
|
|
7238
7328
|
columns: SCAFFOLD_FIELD_RESERVED
|
|
7239
7329
|
}),
|
|
7240
|
-
// submit_manifest naming local_run,
|
|
7241
|
-
//
|
|
7242
|
-
//
|
|
7330
|
+
// submit_manifest naming no local_run, then the bare provision line, the
|
|
7331
|
+
// call its first act prints and the line that call answers, in the
|
|
7332
|
+
// words `provision` prints for a fresh line (provisionStepOf); the run
|
|
7333
|
+
// line; the shaping step, so the first deploy ships the service
|
|
7243
7334
|
// its author means and not the template (cloud:PLD-L0-103); and deploy.
|
|
7244
7335
|
steps: (origin, windows) => [
|
|
7245
7336
|
CREATE_STEP,
|
|
7246
|
-
|
|
7337
|
+
provisionStepOf(origin, windows),
|
|
7247
7338
|
`In ${APPLICATION_FOLDER}/, start the application on this machine. ${RUN_STEP_BACKGROUND}: ${runLineOf(origin, windows)}`,
|
|
7248
7339
|
SHAPE_STEP,
|
|
7249
7340
|
DEPLOY_STEP
|
|
@@ -7279,7 +7370,7 @@ var TEMPLATE_FACTS = Object.freeze({
|
|
|
7279
7370
|
// page first, and a sixth: the page opened on the deployed address.
|
|
7280
7371
|
steps: (origin, windows) => [
|
|
7281
7372
|
CREATE_STEP,
|
|
7282
|
-
|
|
7373
|
+
provisionStepOf(origin, windows),
|
|
7283
7374
|
`In ${APPLICATION_FOLDER}/, start the application on this machine, where its page then answers at / and its API routes take a session as a Bearer; sign-in is on the deployed application's own address. ${RUN_STEP_BACKGROUND}: ${runLineOf(origin, windows)}`,
|
|
7284
7375
|
WEB_SHAPE_STEP,
|
|
7285
7376
|
DEPLOY_STEP,
|
|
@@ -8743,7 +8834,7 @@ var NOTHING_REACHED = "no subcommand was reached, so nothing was started";
|
|
|
8743
8834
|
var ownErrorSentence = (error, afterOwnError = NOTHING_REACHED) => `The command stopped on an error of its own (${error instanceof Error ? error.message : String(error)}); ${afterOwnError}.`;
|
|
8744
8835
|
var ownErrorLineOf = (argv2, io2, error) => {
|
|
8745
8836
|
const parsed = parseArguments(argv2, io2.windows ?? ON_WINDOWS);
|
|
8746
|
-
return ownErrorSentence(error, parsed.kind === "run" ? parsed.subcommand.
|
|
8837
|
+
return ownErrorSentence(error, parsed.kind === "run" ? afterOwnErrorOf(parsed.subcommand, parsed.call, io2) : NOTHING_REACHED);
|
|
8747
8838
|
};
|
|
8748
8839
|
async function runProcess(argv2, unguarded2, io2 = guarded(unguarded2)) {
|
|
8749
8840
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turnzero",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Turn Zero Cloud command, which an AI coding tool runs on your machine to deploy an application, store a secret, upload a file, download an export, take a library entry, write a local run's settings, start the application locally, and scaffold a new service from a template.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"node": ">=24"
|
|
9
9
|
},
|
|
10
10
|
"plane_wire": {
|
|
11
|
-
"action_payloads": "2026-10-
|
|
11
|
+
"action_payloads": "2026-10-14.2",
|
|
12
12
|
"management_api": "2026-10-10.5",
|
|
13
13
|
"wire_errors": "2026-10-10.6"
|
|
14
14
|
},
|