turnzero 0.18.2-next.1 → 0.18.3-next.1
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 +291 -89
- package/package.json +1 -1
package/dist/turnzero-cloud.mjs
CHANGED
|
@@ -200,6 +200,7 @@ var LOCAL_RUN_SETTING = Object.freeze({
|
|
|
200
200
|
});
|
|
201
201
|
var RETIRED_PLATFORM_CREDENTIAL_SETTING = "TURNZERO_CLOUD_TOKEN";
|
|
202
202
|
var LOCAL_RUN_ENVIRONMENT = "local";
|
|
203
|
+
var REMINT_FILE_INSTRUCTION_TAIL = " from it in the development environment file now.";
|
|
203
204
|
var LOCAL_RUN_CREDENTIAL_SETTINGS = Object.freeze([LOCAL_RUN_SETTING.platformCredential, LOCAL_RUN_SETTING.databaseConnection]);
|
|
204
205
|
var ENVIRONMENT_FILE_NAME = ".env";
|
|
205
206
|
var provisionBareLineOf = (call) => commandLineOf(call.origin, PROVISION_SUBCOMMAND, {
|
|
@@ -504,15 +505,21 @@ var planeObjectOf = (text) => {
|
|
|
504
505
|
return null;
|
|
505
506
|
}
|
|
506
507
|
};
|
|
508
|
+
var platformMarked = (text) => {
|
|
509
|
+
const version = planeObjectOf(text)?.contract_version;
|
|
510
|
+
return typeof version === "number" && Number.isInteger(version) && version > 0;
|
|
511
|
+
};
|
|
507
512
|
var redirected = (answer) => answer.type === "opaqueredirect" || answer.status >= 300 && answer.status <= 399;
|
|
508
513
|
var answeredHow = (status) => status >= 100 ? `HTTP ${status}` : "with a redirect";
|
|
509
514
|
var ANSWER_KINDS = Object.freeze(["refused", "failed"]);
|
|
510
515
|
var answerLineOf = (act, kind, status, error) => `${act} ${kind} (HTTP ${status}): ${error}`;
|
|
516
|
+
var PLATFORM_FAILURE_STATUS = 500;
|
|
517
|
+
var notThePlatformLine = (act, status) => `The ${act} was answered HTTP ${status}, not by the platform.`;
|
|
518
|
+
var markedRefusalOf = (status, text) => status < PLATFORM_FAILURE_STATUS && !platformMarked(text) ? null : planeRefusalOf(text);
|
|
511
519
|
var refusalLines = (act, status, refusal) => [
|
|
512
520
|
answerLineOf(act, "refused", status, refusal.error),
|
|
513
521
|
...refusal.detail !== null ? [oneLine(refusal.detail)] : []
|
|
514
522
|
];
|
|
515
|
-
var PLATFORM_FAILURE_STATUS = 500;
|
|
516
523
|
var failureLines = (act, status, said) => said === null ? [`The ${act} was answered HTTP ${status}, a failure of the platform's or of a server between.`] : [answerLineOf(act, "failed", status, said.error), ...said.detail !== null ? [oneLine(said.detail)] : []];
|
|
517
524
|
var HEALTH_READ_PATH = "/healthz";
|
|
518
525
|
var PLANE_CHECK_BOUND_SECONDS = 10;
|
|
@@ -812,7 +819,8 @@ var CREDENTIAL_SOURCES = Object.freeze(["standard_input", "minted_token_variable
|
|
|
812
819
|
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 ? `, and otherwise 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 ? `, and otherwise this computer's token for the platform's address from its credential file, which login and authorize write, and never a minted token from ${MINTED_TOKEN_VARIABLE}` : ""}.`;
|
|
813
820
|
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}.`;
|
|
814
821
|
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.`;
|
|
815
|
-
var emptyGrantRefusal = (declared) =>
|
|
822
|
+
var emptyGrantRefusal = (declared) => emptyPipeRefusal(declared, grantWaysOn(declared));
|
|
823
|
+
var emptyPipeRefusal = (declared, wayOn) => `The ${declared.grant} piped to the command was empty, so nothing was sent and nothing was started. ${wayOn}`;
|
|
816
824
|
var unendedReadRefusal = (declared, reading, nothingArrived = false) => unendedReadSentence(
|
|
817
825
|
declared.grant,
|
|
818
826
|
reading,
|
|
@@ -828,7 +836,9 @@ async function readCredential(io2, declared, again, line = { named: void 0, clos
|
|
|
828
836
|
const unended = unendedReadingOf(read3);
|
|
829
837
|
if (unended !== null) return { refusal: unendedReadRefusal(declared, unended, read3.bytes === 0) };
|
|
830
838
|
const grant = read3.text.trim();
|
|
831
|
-
if (declared.
|
|
839
|
+
if (declared.emptyPipeRefused === true && grant === "" && read3.bytes > 0) {
|
|
840
|
+
return { refusal: declared.computerToken === true ? emptyGrantRefusal(declared) : emptyPipeRefusal(declared, again) };
|
|
841
|
+
}
|
|
832
842
|
if (grant !== "") {
|
|
833
843
|
if (!UPLOAD_GRANT_FORM.test(grant)) {
|
|
834
844
|
return { refusal: `What the command read as its ${declared.grant} is not ${declared.aGrant}, so nothing was sent and nothing was started. ${again}` };
|
|
@@ -1107,7 +1117,7 @@ async function revokeUnder(io2, origin, token2, id, userAgent) {
|
|
|
1107
1117
|
if (answered.status >= 200 && answered.status <= 299) {
|
|
1108
1118
|
return revokedRowOf(planeObjectOf(answered.text), id) ? { revoked: true } : { lines: [`${REVOKE_TOKEN_ACTION} was answered HTTP ${answered.status} with no revocation of token ${id} the command can read.`], known: false };
|
|
1109
1119
|
}
|
|
1110
|
-
const refusal =
|
|
1120
|
+
const refusal = markedRefusalOf(answered.status, answered.text);
|
|
1111
1121
|
if (refusal === null) return { lines: [`${REVOKE_TOKEN_ACTION} was answered HTTP ${answered.status}, not by the platform.`], known: false };
|
|
1112
1122
|
if (NO_LONGER_SERVES.includes(answered.status)) return { ended: true };
|
|
1113
1123
|
return { lines: refusalLines(REVOKE_TOKEN_ACTION, answered.status, refusal), known: true };
|
|
@@ -1155,8 +1165,8 @@ async function readAccountUnder(io2, origin, token2, userAgent) {
|
|
|
1155
1165
|
const account = accountOf(planeObjectOf(answered.text));
|
|
1156
1166
|
return account !== null ? { account } : { lines: [`The ${ACCOUNT_READ_ACT} was answered HTTP ${answered.status} with no account the command can read.`], refused: false };
|
|
1157
1167
|
}
|
|
1158
|
-
const refusal =
|
|
1159
|
-
if (refusal === null) return { lines: [
|
|
1168
|
+
const refusal = markedRefusalOf(answered.status, answered.text);
|
|
1169
|
+
if (refusal === null) return { lines: [notThePlatformLine(ACCOUNT_READ_ACT, answered.status)], refused: false };
|
|
1160
1170
|
return { lines: refusalLines(ACCOUNT_READ_ACT, answered.status, refusal), refused: true };
|
|
1161
1171
|
}
|
|
1162
1172
|
var whoamiLineFor = (io2, origin) => printedLineOf(io2, commandLineOf(origin, WHOAMI_SUBCOMMAND, origin !== DEFAULT_COMMAND_ORIGIN ? { origin } : {}), origin);
|
|
@@ -1225,9 +1235,9 @@ async function completeAct(io2, origin, kind, code, userAgent, say, deliver = ()
|
|
|
1225
1235
|
const answer = planeObjectOf(answered.text);
|
|
1226
1236
|
if (answer !== null && typeof answer.value === "string") io2.hide(answer.value, REDACTED_TOKEN);
|
|
1227
1237
|
if (answered.status < 200 || answered.status > 299) {
|
|
1228
|
-
const refusal =
|
|
1238
|
+
const refusal = markedRefusalOf(answered.status, answered.text);
|
|
1229
1239
|
if (refusal === null) {
|
|
1230
|
-
say(
|
|
1240
|
+
say(notThePlatformLine(EXCHANGE_ACT, answered.status));
|
|
1231
1241
|
say(unknown);
|
|
1232
1242
|
return COMMAND_STATUS.unknown;
|
|
1233
1243
|
}
|
|
@@ -1573,6 +1583,79 @@ function strFromU8(dat, latin1) {
|
|
|
1573
1583
|
}
|
|
1574
1584
|
}
|
|
1575
1585
|
|
|
1586
|
+
// src/session_free_paths.ts
|
|
1587
|
+
var SESSION_FREE_SIGN_IN_PREFIX = "/__account/";
|
|
1588
|
+
var SESSION_FREE_ROUTER_PREFIX = "/__router/";
|
|
1589
|
+
|
|
1590
|
+
// src/client_files.ts
|
|
1591
|
+
var MiB = 1024 * 1024;
|
|
1592
|
+
var CLIENT_FOLDER = "client/";
|
|
1593
|
+
var CLIENT_FILES_MAX = 2e3;
|
|
1594
|
+
var CLIENT_FILE_MAX_BYTES = 25 * MiB;
|
|
1595
|
+
var CLIENT_FILES_TOTAL_MAX_BYTES = 50 * MiB;
|
|
1596
|
+
var CLIENT_NAME_MAX_CHARS = 512;
|
|
1597
|
+
var CLIENT_NAME_MAX_SEGMENTS = 32;
|
|
1598
|
+
var PRUNE_FILE_DELETE_BUDGET_MS = 20 * 6e4;
|
|
1599
|
+
var INDEX_CACHE_BYTES = 32 * MiB;
|
|
1600
|
+
var CONTENT_CACHE_BYTES = 128 * MiB;
|
|
1601
|
+
var CONTENT_CACHE_FILE_MAX_BYTES = 1 * MiB;
|
|
1602
|
+
var SHELL_FILES = Object.freeze([
|
|
1603
|
+
{ name: "index.html", paths: ["/", "/index.html"] },
|
|
1604
|
+
{ name: "app.js", paths: ["/app.js"] },
|
|
1605
|
+
{ name: "styles.css", paths: ["/styles.css"] }
|
|
1606
|
+
]);
|
|
1607
|
+
var SHELL_NAMES = new Set(SHELL_FILES.map((file) => file.name));
|
|
1608
|
+
var SHELL_PATHS = new Set(SHELL_FILES.flatMap((file) => file.paths));
|
|
1609
|
+
var CLIENT_CONTENT_TYPES = Object.freeze({
|
|
1610
|
+
".html": "text/html",
|
|
1611
|
+
".css": "text/css",
|
|
1612
|
+
".js": "text/javascript",
|
|
1613
|
+
".mjs": "text/javascript",
|
|
1614
|
+
".json": "application/json",
|
|
1615
|
+
".map": "application/json",
|
|
1616
|
+
".svg": "image/svg+xml",
|
|
1617
|
+
".png": "image/png",
|
|
1618
|
+
".jpg": "image/jpeg",
|
|
1619
|
+
".jpeg": "image/jpeg",
|
|
1620
|
+
".gif": "image/gif",
|
|
1621
|
+
".webp": "image/webp",
|
|
1622
|
+
".avif": "image/avif",
|
|
1623
|
+
".ico": "image/x-icon",
|
|
1624
|
+
".woff": "font/woff",
|
|
1625
|
+
".woff2": "font/woff2",
|
|
1626
|
+
".ttf": "font/ttf",
|
|
1627
|
+
".otf": "font/otf",
|
|
1628
|
+
".wasm": "application/wasm",
|
|
1629
|
+
".txt": "text/plain",
|
|
1630
|
+
".xml": "application/xml",
|
|
1631
|
+
".webmanifest": "application/manifest+json",
|
|
1632
|
+
".pdf": "application/pdf",
|
|
1633
|
+
".mp3": "audio/mpeg",
|
|
1634
|
+
".ogg": "audio/ogg",
|
|
1635
|
+
".oga": "audio/ogg",
|
|
1636
|
+
".wav": "audio/wav",
|
|
1637
|
+
".m4a": "audio/mp4",
|
|
1638
|
+
".flac": "audio/flac",
|
|
1639
|
+
".mp4": "video/mp4",
|
|
1640
|
+
".webm": "video/webm",
|
|
1641
|
+
".ogv": "video/ogg"
|
|
1642
|
+
});
|
|
1643
|
+
var RESERVED_TOP_FOLDERS = Object.freeze([SESSION_FREE_SIGN_IN_PREFIX, SESSION_FREE_ROUTER_PREFIX].map((prefix) => prefix.replace(/^\/|\/$/g, "").toLowerCase()));
|
|
1644
|
+
var ADMITTED_SEGMENT = /^[A-Za-z0-9\-_.~]+$/;
|
|
1645
|
+
function admittedClientName(path) {
|
|
1646
|
+
if (!isClientPath(path)) return null;
|
|
1647
|
+
const name = path.slice(CLIENT_FOLDER.length);
|
|
1648
|
+
if (name.length > CLIENT_NAME_MAX_CHARS) return null;
|
|
1649
|
+
const segments = name.split("/");
|
|
1650
|
+
if (segments.length > CLIENT_NAME_MAX_SEGMENTS) return null;
|
|
1651
|
+
for (const segment of segments) {
|
|
1652
|
+
if (!ADMITTED_SEGMENT.test(segment) || segment.startsWith(".") || segment.endsWith(".")) return null;
|
|
1653
|
+
}
|
|
1654
|
+
if (RESERVED_TOP_FOLDERS.includes(segments[0].toLowerCase())) return null;
|
|
1655
|
+
return name;
|
|
1656
|
+
}
|
|
1657
|
+
var isClientPath = (path) => path.startsWith(CLIENT_FOLDER) && path.length > CLIENT_FOLDER.length;
|
|
1658
|
+
|
|
1576
1659
|
// src/turnzero_cloud_command/export_download.ts
|
|
1577
1660
|
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve2 } from "node:path";
|
|
1578
1661
|
|
|
@@ -2218,7 +2301,7 @@ var readHealthPath = (path, entries) => {
|
|
|
2218
2301
|
return answer("absent");
|
|
2219
2302
|
};
|
|
2220
2303
|
var shownHealthPath = (path) => shownNoteName(bareHealthPath(path));
|
|
2221
|
-
var healthLineOf = (path) => `${WARNING_LINE_HEAD}${MANIFEST_FILE} names the health path ${shownHealthPath(path)}, and no other file of the zip holds its last segment, package files and documentation set aside. A deploy waits for that path to answer 200, so check that the application serves it. A route built in another form is not seen here.`;
|
|
2304
|
+
var healthLineOf = (path) => `${WARNING_LINE_HEAD}${MANIFEST_FILE} names the health path ${shownHealthPath(path)}, and no other file of the zip holds its last segment, package files and documentation set aside. A deploy waits for that path to answer 200 three times in a row, so check that the application serves it. A route built in another form is not seen here.`;
|
|
2222
2305
|
var CREDENTIAL_FILE_NAMES = Object.freeze(["id_rsa", "id_dsa", "id_ecdsa", "id_ed25519", ".netrc", ".pgpass", ".htpasswd", "credentials.json"]);
|
|
2223
2306
|
var CLIENT_SECRET_OPENING = "client_secret";
|
|
2224
2307
|
var CLIENT_SECRET_ENDING = ".json";
|
|
@@ -2292,11 +2375,14 @@ var DeployRefusal = class extends Error {
|
|
|
2292
2375
|
* row's outcome, a failed gate's `gate`, its evidence (PLD-L0-59;
|
|
2293
2376
|
* PLD-L0-63); onto the wire, an `upload_hash_mismatch`'s
|
|
2294
2377
|
* `received_sha256` and `expected_sha256` (deploy_upload.ts's readUpload;
|
|
2295
|
-
* PLD-L0-86), which the upload form's read throws before any row
|
|
2296
|
-
*
|
|
2297
|
-
*
|
|
2298
|
-
*
|
|
2299
|
-
*
|
|
2378
|
+
* PLD-L0-86), which the upload form's read throws before any row, and a
|
|
2379
|
+
* `deploy_files_too_large`'s `limit`, the cap it met (deploy_files.ts's
|
|
2380
|
+
* tooLarge; #4418; MAPI-26); empty for every other refusal. Never
|
|
2381
|
+
* `error` or `detail` themselves — failRun keeps those two first. The
|
|
2382
|
+
* wire mapping (main.ts's actRefusal) reads status, code, and detail, the
|
|
2383
|
+
* two hashes for `upload_hash_mismatch` alone, and `limit` for
|
|
2384
|
+
* `deploy_files_too_large` alone; a refusal thrown after the 202 reaches
|
|
2385
|
+
* the row, not the wire. */
|
|
2300
2386
|
members;
|
|
2301
2387
|
/** The fields the control plane's `version failed` line carries beside
|
|
2302
2388
|
* the refusal for the operator alone (MAPI-10), never the row's or the
|
|
@@ -2584,7 +2670,7 @@ var isRecord = (value) => typeof value === "object" && value !== null && !Array.
|
|
|
2584
2670
|
var ROOT_FILE_MAX_BYTES = 1024 * 1024;
|
|
2585
2671
|
var MEMBER_FILE_MAX_BYTES = 64 * 1024;
|
|
2586
2672
|
var MEMBER_FILES_READ_MAX = 50;
|
|
2587
|
-
var WINDOWS_ZIP_COMMAND = "tar.exe -a -c -f app.zip --exclude node_modules --exclude .env --exclude '.env.*' --exclude .git -C app .";
|
|
2673
|
+
var WINDOWS_ZIP_COMMAND = "tar.exe -a -c -f app.zip --exclude node_modules --exclude .env --exclude '.env.*' --exclude .git --exclude .claude --exclude .mcp.json --exclude .codex --exclude .cursor --exclude .gemini --exclude .continue --exclude .roo --exclude .kiro --exclude .amazonq --exclude .aider.conf.yml -C app .";
|
|
2588
2674
|
function readArtifact(bytes) {
|
|
2589
2675
|
const stored = [];
|
|
2590
2676
|
const names = [];
|
|
@@ -2766,11 +2852,48 @@ function manifestCopyDepthRefusal(reading) {
|
|
|
2766
2852
|
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 (PLD-L0-63).`);
|
|
2767
2853
|
}
|
|
2768
2854
|
}
|
|
2855
|
+
function clientReadingOf(records) {
|
|
2856
|
+
const admitted = /* @__PURE__ */ new Map();
|
|
2857
|
+
const skipped = [];
|
|
2858
|
+
const skippedSeen = /* @__PURE__ */ new Set();
|
|
2859
|
+
for (const record of records) {
|
|
2860
|
+
const { path, folder } = entryNameOf(record.name);
|
|
2861
|
+
if (folder || !isClientPath(path)) continue;
|
|
2862
|
+
const name = admittedClientName(path);
|
|
2863
|
+
if (name !== null) admitted.set(name, record);
|
|
2864
|
+
else if (!skippedSeen.has(path)) {
|
|
2865
|
+
skippedSeen.add(path);
|
|
2866
|
+
skipped.push(path);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
return { admitted, skipped };
|
|
2870
|
+
}
|
|
2871
|
+
function clientBoundRefusal(reading) {
|
|
2872
|
+
const cite = " (SVC-L0-26).";
|
|
2873
|
+
if (reading.admitted.size > CLIENT_FILES_MAX) {
|
|
2874
|
+
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}`;
|
|
2875
|
+
}
|
|
2876
|
+
let total = 0;
|
|
2877
|
+
for (const [name, record] of reading.admitted) {
|
|
2878
|
+
const declared = declaredBytesOf(record);
|
|
2879
|
+
if (declared > CLIENT_FILE_MAX_BYTES) {
|
|
2880
|
+
return `The zip's client file ${shownEntry(`${CLIENT_FOLDER}${name}`)} declares ${declared} bytes, more than the 25 MiB (${CLIENT_FILE_MAX_BYTES} bytes) one client file holds: keep long media in a storage area, and deploy again${cite}`;
|
|
2881
|
+
}
|
|
2882
|
+
total += declared;
|
|
2883
|
+
}
|
|
2884
|
+
if (total > CLIENT_FILES_TOTAL_MAX_BYTES) {
|
|
2885
|
+
return `The zip's client files declare ${total} bytes in all, more than the 50 MiB (${CLIENT_FILES_TOTAL_MAX_BYTES} bytes) one version's client files hold: keep large media in a storage area, and deploy again${cite}`;
|
|
2886
|
+
}
|
|
2887
|
+
return null;
|
|
2888
|
+
}
|
|
2769
2889
|
function checkArtifact(bytes) {
|
|
2770
2890
|
const reading = readArtifact(bytes);
|
|
2771
2891
|
const packageJson = artifactLayoutOf(reading);
|
|
2772
2892
|
manifestCopyDepthRefusal(reading);
|
|
2773
|
-
|
|
2893
|
+
const client = clientReadingOf(reading.records);
|
|
2894
|
+
const bounded = clientBoundRefusal(client);
|
|
2895
|
+
if (bounded !== null) throw new DeployRefusal(400, "artifact_layout_invalid", bounded);
|
|
2896
|
+
return { reading, packageJson, client };
|
|
2774
2897
|
}
|
|
2775
2898
|
function healthPathReadingOf(bytes, reading, recorded) {
|
|
2776
2899
|
const path = isRecord(recorded) && typeof recorded.health === "string" ? recorded.health : null;
|
|
@@ -3292,7 +3415,7 @@ async function prepareDeploy(io2, options, zipSha256Value, bearer, userAgent, re
|
|
|
3292
3415
|
return nothingStarted;
|
|
3293
3416
|
}
|
|
3294
3417
|
if (answered.status < 200 || answered.status > 299) {
|
|
3295
|
-
const refusal =
|
|
3418
|
+
const refusal = markedRefusalOf(answered.status, answered.text);
|
|
3296
3419
|
if (refusal === null) {
|
|
3297
3420
|
io2.print(prepareNotThePlatform(answered.status));
|
|
3298
3421
|
io2.print(PREPARE_AGAIN);
|
|
@@ -3496,7 +3619,7 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3496
3619
|
return PROGRESS_EXIT_STATUS.refused;
|
|
3497
3620
|
}
|
|
3498
3621
|
const answer = planeObjectOf(started.text);
|
|
3499
|
-
if (answer === null) {
|
|
3622
|
+
if (answer === null || (started.status < 200 || started.status > 299) && !platformMarked(started.text)) {
|
|
3500
3623
|
io2.print(`The start was answered HTTP ${started.status}, not by the platform, so the deploy may have started.`);
|
|
3501
3624
|
io2.print(stop);
|
|
3502
3625
|
return PROGRESS_EXIT_STATUS.refused;
|
|
@@ -3543,8 +3666,8 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3543
3666
|
return PROGRESS_EXIT_STATUS.refused;
|
|
3544
3667
|
}
|
|
3545
3668
|
if (read2.status !== 200 || !read2.type.startsWith("text/plain")) {
|
|
3546
|
-
const refusal =
|
|
3547
|
-
const said = refusal !== null ? refusalLines(PROGRESS_READ_ACT, read2.status, refusal) : [
|
|
3669
|
+
const refusal = markedRefusalOf(read2.status, read2.text);
|
|
3670
|
+
const said = refusal !== null ? refusalLines(PROGRESS_READ_ACT, read2.status, refusal) : [notThePlatformLine(PROGRESS_READ_ACT, read2.status)];
|
|
3548
3671
|
for (const line of said) io2.print(line);
|
|
3549
3672
|
io2.print(stop);
|
|
3550
3673
|
return PROGRESS_EXIT_STATUS.refused;
|
|
@@ -3713,7 +3836,7 @@ async function callCheck(io2, options, bearer, userAgent, refused = () => null)
|
|
|
3713
3836
|
return nothingChecked;
|
|
3714
3837
|
}
|
|
3715
3838
|
if (answered.status < 200 || answered.status > 299) {
|
|
3716
|
-
const refusal =
|
|
3839
|
+
const refusal = markedRefusalOf(answered.status, answered.text);
|
|
3717
3840
|
if (refusal === null) {
|
|
3718
3841
|
io2.print(checkNotThePlatform(answered.status));
|
|
3719
3842
|
io2.print(CHECK_AGAIN);
|
|
@@ -3828,7 +3951,7 @@ var CHECK_ROW = Object.freeze({
|
|
|
3828
3951
|
note: [
|
|
3829
3952
|
`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.`,
|
|
3830
3953
|
`After its zip line, ${CHECK_SUBCOMMAND} prints the further lines ${DEPLOY_SUBCOMMAND} prints, leaving 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 naming submit_manifest where the zip's manifest.json names another health path, and a last checked: line counting the refusals.`,
|
|
3831
|
-
`A check
|
|
3954
|
+
`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.`
|
|
3832
3955
|
],
|
|
3833
3956
|
afterOwnError: CHECK_AFTER_OWN_ERROR,
|
|
3834
3957
|
run: (call, io2) => check(call.named, io2)
|
|
@@ -4026,7 +4149,8 @@ var SECRET_CREDENTIAL = Object.freeze({
|
|
|
4026
4149
|
aGrant: "a secret grant",
|
|
4027
4150
|
call: "the store_secret or rotate_secret call",
|
|
4028
4151
|
mintedToken: false,
|
|
4029
|
-
computerToken: true
|
|
4152
|
+
computerToken: true,
|
|
4153
|
+
emptyPipeRefused: true
|
|
4030
4154
|
});
|
|
4031
4155
|
var freshLineSteps = (act) => `${SECRET_ACTION[act]} from your tool with the name and its scope, naming no value, and run the line it answers as given.`;
|
|
4032
4156
|
var secretAgain = (act) => `Call ${freshLineSteps(act)}`;
|
|
@@ -4176,7 +4300,7 @@ async function secret(call, io2) {
|
|
|
4176
4300
|
}
|
|
4177
4301
|
const answer = planeObjectOf(answered.text);
|
|
4178
4302
|
if (answered.status < 200 || answered.status > 299) {
|
|
4179
|
-
const refusal =
|
|
4303
|
+
const refusal = markedRefusalOf(answered.status, answered.text);
|
|
4180
4304
|
if (refusal === null) {
|
|
4181
4305
|
io2.print(`${action} was answered HTTP ${answered.status}, not by the platform.`);
|
|
4182
4306
|
io2.print(unknown);
|
|
@@ -4394,10 +4518,12 @@ var RUN_OPTIONS = Object.freeze([
|
|
|
4394
4518
|
}
|
|
4395
4519
|
]);
|
|
4396
4520
|
var NO_APPLICATION_STARTED = "No application was started.";
|
|
4521
|
+
var RUN_STEP_BACKGROUND = "The line runs until the application ends, so start it as a background process and read its lines";
|
|
4397
4522
|
var PORT_FORM = /^[1-9][0-9]{0,4}$/;
|
|
4398
4523
|
var isPortNumber = (value) => PORT_FORM.test(value) && Number(value) <= 65535;
|
|
4399
4524
|
var RUN_BACKGROUND_SENTENCE = "The command runs until the application ends, so a tool starts it as a background process and reads its lines.";
|
|
4400
|
-
var
|
|
4525
|
+
var stopSentenceOf = (processId, onWindows2 = NPM_NEEDS_SHELL) => `To stop it, send it an interrupt or run ${onWindows2 ? `taskkill /T /F /PID ${processId}` : `kill ${processId}`}, which ends the application with the command.`;
|
|
4526
|
+
var startingLineOf = (folder, processId, onWindows2 = NPM_NEEDS_SHELL) => `Starting npm start in ${folder}; the command's process id is ${processId}. ${RUN_BACKGROUND_SENTENCE} ${stopSentenceOf(processId, onWindows2)}`;
|
|
4401
4527
|
var PORT_SOURCE_WORDS = Object.freeze({
|
|
4402
4528
|
option: RUN_OPTION.port,
|
|
4403
4529
|
file: "the environment file",
|
|
@@ -4557,7 +4683,7 @@ async function localRun(call, io2) {
|
|
|
4557
4683
|
}
|
|
4558
4684
|
const { env, overridden } = applicationEnvironmentOf(io2.env, read2.settings, options.port);
|
|
4559
4685
|
for (const [text, shown2] of hiddenTextsOf(Object.entries(env))) io2.hide(text, shown2);
|
|
4560
|
-
io2.print(startingLineOf(options.folder));
|
|
4686
|
+
io2.print(startingLineOf(options.folder, io2.processId ?? process.pid, io2.windows ?? NPM_NEEDS_SHELL));
|
|
4561
4687
|
if (file.text === null) io2.print(`No environment file stands at ${options.envFile}, so the application receives the shell's environment alone.`);
|
|
4562
4688
|
else if (read2.settings.length === 0) io2.print(`${options.envFile} sets no setting.`);
|
|
4563
4689
|
else io2.print(`${options.envFile} sets ${read2.settings.length} setting(s): ${read2.settings.map(([name]) => name).join(", ")}.`);
|
|
@@ -4796,7 +4922,7 @@ var readOf = async (io2, origin, userAgent, body, what) => {
|
|
|
4796
4922
|
}
|
|
4797
4923
|
const answer = planeObjectOf(text);
|
|
4798
4924
|
if (status >= 200 && status <= 299 && answer !== null) return { answer };
|
|
4799
|
-
const refusal =
|
|
4925
|
+
const refusal = markedRefusalOf(status, text);
|
|
4800
4926
|
if (refusal === null) return { lines: [`${LIBRARY_READ_ACTION} was answered HTTP ${status}, not by the platform. ${NOTHING_WRITTEN2}`] };
|
|
4801
4927
|
return { lines: [...refusalLines(LIBRARY_READ_ACTION, status, refusal), NOTHING_WRITTEN2] };
|
|
4802
4928
|
};
|
|
@@ -5800,6 +5926,7 @@ var PROVISION_CREDENTIAL = Object.freeze({
|
|
|
5800
5926
|
aGrant: "a secret grant",
|
|
5801
5927
|
call: "the submit_manifest call naming local_run",
|
|
5802
5928
|
mintedToken: true,
|
|
5929
|
+
emptyPipeRefused: true,
|
|
5803
5930
|
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`
|
|
5804
5931
|
});
|
|
5805
5932
|
var NOTHING_REMINTED = "Nothing was re-minted, and the environment file is as it stood.";
|
|
@@ -5835,7 +5962,19 @@ var outsideApplicationFolder = (io2, envFile) => APPLICATION_FOLDER_MARKS.every(
|
|
|
5835
5962
|
return false;
|
|
5836
5963
|
}
|
|
5837
5964
|
});
|
|
5965
|
+
var PROVISION_NEXT_HEAD = "next: ";
|
|
5966
|
+
var nextRunLineOf = (io2, origin, envFile) => machineLineOf(
|
|
5967
|
+
commandLineOf(origin, RUN_SUBCOMMAND, {
|
|
5968
|
+
...origin !== DEFAULT_COMMAND_ORIGIN ? { origin } : {},
|
|
5969
|
+
...envFile !== void 0 ? { quoted: [[RUN_OPTION.envFile, envFile]] } : {}
|
|
5970
|
+
}),
|
|
5971
|
+
{ windows: onWindows(io2), npx: ranThroughNpx(io2) },
|
|
5972
|
+
origin
|
|
5973
|
+
);
|
|
5974
|
+
var provisionNextLineOf = (line) => `${PROVISION_NEXT_HEAD}start the application on this machine. ${RUN_STEP_BACKGROUND}: ${line}`;
|
|
5838
5975
|
var charactersOf = (value) => `<${value.length} characters>`;
|
|
5976
|
+
var REMINT_FILE_INSTRUCTION = new RegExp(`: set [A-Z][A-Z0-9_]*${REMINT_FILE_INSTRUCTION_TAIL.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`);
|
|
5977
|
+
var writtenDetailOf = (detail, setting, envFile) => detail.replace(REMINT_FILE_INSTRUCTION, () => `, and this run wrote it into ${setting} in ${envFile}.`);
|
|
5839
5978
|
var transportText = (error) => {
|
|
5840
5979
|
const oneLine2 = (text) => text.replace(/\s+/g, " ").trim();
|
|
5841
5980
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -5899,7 +6038,7 @@ async function remint(io2, origin, credential, userAgent, application, name, ver
|
|
|
5899
6038
|
}
|
|
5900
6039
|
const answer = planeObjectOf(text);
|
|
5901
6040
|
if (status < 200 || status > 299) {
|
|
5902
|
-
const refusal =
|
|
6041
|
+
const refusal = markedRefusalOf(status, text);
|
|
5903
6042
|
if (refusal === null) return { kind: "unknown", lines: [`${PROVISION_ACTION} was answered HTTP ${status}, not by the platform.`] };
|
|
5904
6043
|
return { kind: "refused", error: refusal.error, action: answer?.action, lines: refusalLines(PROVISION_ACTION, status, refusal) };
|
|
5905
6044
|
}
|
|
@@ -6003,7 +6142,7 @@ async function provision(call, io2) {
|
|
|
6003
6142
|
io2.print(older);
|
|
6004
6143
|
return COMMAND_STATUS.nothing_started;
|
|
6005
6144
|
}
|
|
6006
|
-
return await remintAndWrite(io2, sent, agent.userAgent, { ...read2, verifier, again }, standing.text, reminted);
|
|
6145
|
+
return await remintAndWrite(io2, sent, agent.userAgent, { ...read2, verifier, again, next: nextRunLineOf(io2, sent.origin, namedEnvFile) }, standing.text, reminted);
|
|
6007
6146
|
} finally {
|
|
6008
6147
|
release();
|
|
6009
6148
|
dropWritingFile(io2, options.envFile);
|
|
@@ -6113,22 +6252,24 @@ async function remintAndWrite(io2, { application, envFile, origin }, userAgent,
|
|
|
6113
6252
|
if (facts !== null && url !== null) {
|
|
6114
6253
|
io2.print(`${envFile}: ${facts.setting}=${charactersOf(url)}, ${limit !== null ? `${LOCAL_RUN_SETTING.databaseConnectionLimit}=${limit}, ` : ""}${plain}`);
|
|
6115
6254
|
io2.print(`development database: ${oneLine(facts.roleName)} on ${facts.host}, database ${oneLine(facts.dbName)}${limit === null ? "" : `, a client pool of at most ${limit} ${limit === 1 ? "connection" : "connections"} per process`}`);
|
|
6116
|
-
if (database.kind === "answered" && typeof database.answer.detail === "string") io2.print(oneLine(database.answer.detail));
|
|
6255
|
+
if (database.kind === "answered" && typeof database.answer.detail === "string") io2.print(oneLine(writtenDetailOf(database.answer.detail, facts.setting, envFile)));
|
|
6117
6256
|
} else {
|
|
6118
6257
|
io2.print(`${envFile}: ${plain}`);
|
|
6119
6258
|
io2.print(
|
|
6120
6259
|
`no development database stands for ${application}: ${LOCAL_RUN_SETTING.databaseConnection} and ${LOCAL_RUN_SETTING.databaseConnectionLimit} were not written; submit_manifest with the database kind provisions one.`
|
|
6121
6260
|
);
|
|
6122
6261
|
}
|
|
6123
|
-
if (typeof platform.answer.detail === "string") io2.print(oneLine(platform.answer.detail));
|
|
6262
|
+
if (typeof platform.answer.detail === "string") io2.print(oneLine(writtenDetailOf(platform.answer.detail, LOCAL_RUN_SETTING.platformCredential, envFile)));
|
|
6124
6263
|
if (outsideApplicationFolder(io2, envFile)) io2.print(outsideApplicationWarningOf(dirname5(envFile)));
|
|
6125
6264
|
if (facts !== null && limitCarried !== null) {
|
|
6126
6265
|
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";
|
|
6127
6266
|
io2.print(
|
|
6128
6267
|
`${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)}.`
|
|
6129
6268
|
);
|
|
6269
|
+
io2.print(provisionNextLineOf(read2.next));
|
|
6130
6270
|
return COMMAND_STATUS.failed;
|
|
6131
6271
|
}
|
|
6272
|
+
io2.print(provisionNextLineOf(read2.next));
|
|
6132
6273
|
return COMMAND_STATUS.done;
|
|
6133
6274
|
}
|
|
6134
6275
|
var PROVISION_ROW = Object.freeze({
|
|
@@ -6151,7 +6292,7 @@ var PROVISION_ROW = Object.freeze({
|
|
|
6151
6292
|
},
|
|
6152
6293
|
note: [
|
|
6153
6294
|
`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.`,
|
|
6154
|
-
`Each run revokes the development credentials the application had before it. A deployed development environment holds the revoked values until it restarts, so
|
|
6295
|
+
`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}.`
|
|
6155
6296
|
],
|
|
6156
6297
|
afterOwnError: provisionAfterOwnError,
|
|
6157
6298
|
run: provision
|
|
@@ -6178,7 +6319,7 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
6178
6319
|
"",
|
|
6179
6320
|
"Turn Zero Cloud serves its rules to a coding tool through its MCP server, `turnzero-cloud`, and they change with the platform, so none is repeated here.",
|
|
6180
6321
|
"",
|
|
6181
|
-
`- Where the server is not connected, connect it first. The platform's page "Connect
|
|
6322
|
+
`- Where the server is not connected, connect it first. The platform's page "Connect Claude Code or Codex" says how.`,
|
|
6182
6323
|
"- Before you change or deploy this service, read the server's `deploy` skill.",
|
|
6183
6324
|
"",
|
|
6184
6325
|
"## What you run here",
|
|
@@ -6341,9 +6482,9 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
6341
6482
|
" }",
|
|
6342
6483
|
" send(404, { error: 'not_found' });",
|
|
6343
6484
|
" } catch (error) {",
|
|
6344
|
-
" const refused = error instanceof SyntaxError
|
|
6345
|
-
" if (
|
|
6346
|
-
" send(refused ?
|
|
6485
|
+
" const refused = error instanceof SyntaxError ? 'invalid_json' : error?.code === '23514' ? 'invalid_note' : null;",
|
|
6486
|
+
" if (refused === null) console.error(error);",
|
|
6487
|
+
" send(refused === null ? 500 : 400, { error: refused ?? 'internal' });",
|
|
6347
6488
|
" }",
|
|
6348
6489
|
" });",
|
|
6349
6490
|
"}",
|
|
@@ -6423,6 +6564,17 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
6423
6564
|
" assert.deepEqual(await response.json(), { error: 'invalid_note' });",
|
|
6424
6565
|
" assert.deepEqual(await (await fetch(`${base}/notes`)).json(), []);",
|
|
6425
6566
|
"});",
|
|
6567
|
+
"",
|
|
6568
|
+
"test('POST /notes answers 400 invalid_json for a body that is not JSON, and GET /notes returns no row', async () => {",
|
|
6569
|
+
" const response = await fetch(`${base}/notes`, {",
|
|
6570
|
+
" method: 'POST',",
|
|
6571
|
+
" headers: { 'content-type': 'application/json' },",
|
|
6572
|
+
" body: '{',",
|
|
6573
|
+
" });",
|
|
6574
|
+
" assert.equal(response.status, 400);",
|
|
6575
|
+
" assert.deepEqual(await response.json(), { error: 'invalid_json' });",
|
|
6576
|
+
" assert.deepEqual(await (await fetch(`${base}/notes`)).json(), []);",
|
|
6577
|
+
"});",
|
|
6426
6578
|
""
|
|
6427
6579
|
].join("\n")]
|
|
6428
6580
|
]),
|
|
@@ -6444,7 +6596,7 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
6444
6596
|
"",
|
|
6445
6597
|
"Turn Zero Cloud serves its rules to a coding tool through its MCP server, `turnzero-cloud`, and they change with the platform, so none is repeated here.",
|
|
6446
6598
|
"",
|
|
6447
|
-
`- Where the server is not connected, connect it first. The platform's page "Connect
|
|
6599
|
+
`- Where the server is not connected, connect it first. The platform's page "Connect Claude Code or Codex" says how.`,
|
|
6448
6600
|
"- Before you change or deploy this service, read the server's `deploy` skill.",
|
|
6449
6601
|
"",
|
|
6450
6602
|
"## What you run here",
|
|
@@ -6563,7 +6715,7 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
6563
6715
|
"",
|
|
6564
6716
|
"Turn Zero Cloud serves its rules to a coding tool through its MCP server, `turnzero-cloud`, and they change with the platform, so none is repeated here.",
|
|
6565
6717
|
"",
|
|
6566
|
-
`- Where the server is not connected, connect it first. The platform's page "Connect
|
|
6718
|
+
`- Where the server is not connected, connect it first. The platform's page "Connect Claude Code or Codex" says how.`,
|
|
6567
6719
|
"- Before you change or deploy this service, read the server's `deploy` skill, and before you change its schedule, the server's `schedule` skill.",
|
|
6568
6720
|
"",
|
|
6569
6721
|
"## What you run here",
|
|
@@ -6769,7 +6921,7 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
6769
6921
|
"",
|
|
6770
6922
|
"Turn Zero Cloud serves its rules to a coding tool through its MCP server, `turnzero-cloud`, and they change with the platform, so none is repeated here.",
|
|
6771
6923
|
"",
|
|
6772
|
-
`- Where the server is not connected, connect it first. The platform's page "Connect
|
|
6924
|
+
`- Where the server is not connected, connect it first. The platform's page "Connect Claude Code or Codex" says how.`,
|
|
6773
6925
|
"- 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.",
|
|
6774
6926
|
"",
|
|
6775
6927
|
"## What you run here",
|
|
@@ -7235,9 +7387,9 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
7235
7387
|
" }",
|
|
7236
7388
|
" send(404, { error: 'not_found' });",
|
|
7237
7389
|
" } catch (error) {",
|
|
7238
|
-
" const refused = error instanceof SyntaxError
|
|
7239
|
-
" if (
|
|
7240
|
-
" send(refused ?
|
|
7390
|
+
" const refused = error instanceof SyntaxError ? 'invalid_json' : error?.code === '23514' ? 'invalid_note' : null;",
|
|
7391
|
+
" if (refused === null) console.error(error);",
|
|
7392
|
+
" send(refused === null ? 500 : 400, { error: refused ?? 'internal' });",
|
|
7241
7393
|
" }",
|
|
7242
7394
|
" });",
|
|
7243
7395
|
"}",
|
|
@@ -7406,6 +7558,17 @@ var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
|
7406
7558
|
" assert.deepEqual(await (await fetch(`${base}/api/notes`, { headers: signedIn(sam) })).json(), []);",
|
|
7407
7559
|
"});",
|
|
7408
7560
|
"",
|
|
7561
|
+
"test('POST /api/notes answers 400 invalid_json for a body that is not JSON, and GET /api/notes returns no row', async () => {",
|
|
7562
|
+
" const response = await fetch(`${base}/api/notes`, {",
|
|
7563
|
+
" method: 'POST',",
|
|
7564
|
+
" headers: { 'content-type': 'application/json', ...signedIn(sam) },",
|
|
7565
|
+
" body: '{',",
|
|
7566
|
+
" });",
|
|
7567
|
+
" assert.equal(response.status, 400);",
|
|
7568
|
+
" assert.deepEqual(await response.json(), { error: 'invalid_json' });",
|
|
7569
|
+
" assert.deepEqual(await (await fetch(`${base}/api/notes`, { headers: signedIn(sam) })).json(), []);",
|
|
7570
|
+
"});",
|
|
7571
|
+
"",
|
|
7409
7572
|
"test('POST /api/ask answers under the AI allowance, and answers 503 naming why the model gave no answer: the allowance drawn, no key in place, or the provider out of capacity', async () => {",
|
|
7410
7573
|
" ai.prime({ text: 'Lunch is at noon.', usage: { input: 40, output: 12 } });",
|
|
7411
7574
|
" const ask = (question) =>",
|
|
@@ -7446,7 +7609,6 @@ var WEB_OPEN_STEP = "Open the address the deploy answers in a browser and sign i
|
|
|
7446
7609
|
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.`;
|
|
7447
7610
|
var provisionStepLineOf = (origin, windows) => machineLineOf(provisionBareLineOf({ origin, application: "<application id>" }), windows, origin);
|
|
7448
7611
|
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)}`;
|
|
7449
|
-
var RUN_STEP_BACKGROUND = "The line runs until the application ends, so start it as a background process and read its lines";
|
|
7450
7612
|
var SCHEDULED_JOB_SCHEDULE = "heartbeat";
|
|
7451
7613
|
var ENTRY_PAGES = Object.freeze({
|
|
7452
7614
|
database: Object.freeze({ follow: "the guide Add a database", page: "/cloud/guides/add-a-database/" }),
|
|
@@ -8080,16 +8242,20 @@ var standsAt = (io2, folder, name) => {
|
|
|
8080
8242
|
var noFolderSentence = (name, where) => `Something that is no folder stands at ${name} in the project's root, where ${where}: move it, then run the line again. ${NOTHING_WRITTEN2}`;
|
|
8081
8243
|
var scaffoldLinkSentence = (what) => `${what} stands on a link that leads outside the project's root, and the scaffold never writes through one. Put a folder in the link's place, or remove it, then run the line again. ${NOTHING_WRITTEN2}`;
|
|
8082
8244
|
var listOf = (value) => value === null ? [] : Array.isArray(value) ? value : [value];
|
|
8245
|
+
var BRING_PAGE = "/cloud/guides/bring-an-existing-application/";
|
|
8246
|
+
var BRING_FOLLOW = "the guide Bring an existing application";
|
|
8247
|
+
var STANDS_DEPLOY_LINE = `Where ${APPLICATION_FOLDER}/ holds an application, deploy it: call deploy from your tool and run the line it answers in ${APPLICATION_FOLDER}/, as ${BRING_FOLLOW}, read_documentation's page ${BRING_PAGE}, says.`;
|
|
8083
8248
|
var applicationStandsLines = (template, takeLines) => {
|
|
8084
8249
|
const { takes } = TEMPLATE_FACTS[template];
|
|
8085
8250
|
const lines = listOf(takeLines);
|
|
8086
8251
|
const first = `The project's root holds ${APPLICATION_FOLDER}/ with files in it, so an application stands there, and ${SCAFFOLD_SUBCOMMAND} starts a new application and writes over none. ${NOTHING_WRITTEN2}`;
|
|
8087
|
-
if (takes.length === 0 || lines.length !== takes.length) return [first];
|
|
8252
|
+
if (takes.length === 0 || lines.length !== takes.length) return [first, STANDS_DEPLOY_LINE];
|
|
8088
8253
|
return [
|
|
8089
8254
|
first,
|
|
8090
8255
|
...takes.map(
|
|
8091
8256
|
(take, index) => `To add the ${take.entry} package to the application that stands, run this line at the project's root, then follow ${take.follow}, read_documentation's page ${take.page}: ${lines[index]}`
|
|
8092
|
-
)
|
|
8257
|
+
),
|
|
8258
|
+
STANDS_DEPLOY_LINE
|
|
8093
8259
|
];
|
|
8094
8260
|
};
|
|
8095
8261
|
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 among it: move or remove ${STAGE_FOLDER}, then run the line again. ${NOTHING_WRITTEN2}`;
|
|
@@ -8170,8 +8336,9 @@ var applicationStateOf = (io2, app) => {
|
|
|
8170
8336
|
}
|
|
8171
8337
|
};
|
|
8172
8338
|
var standsWayOn = (template, takeLines) => {
|
|
8173
|
-
const
|
|
8174
|
-
return
|
|
8339
|
+
const takes = applicationStandsLines(template, takeLines).slice(1, -1);
|
|
8340
|
+
return `${takes.length > 0 ? `. ${takes.join("\n")}` : `: ${SCAFFOLD_SUBCOMMAND} starts a new application and writes over none.`}
|
|
8341
|
+
${STANDS_DEPLOY_LINE}`;
|
|
8175
8342
|
};
|
|
8176
8343
|
var stoppedCloseOf = (state, template, takeLines) => {
|
|
8177
8344
|
const before = `before ${APPLICATION_FOLDER}/ was written`;
|
|
@@ -8836,44 +9003,79 @@ var usageLineOf = (subcommand) => {
|
|
|
8836
9003
|
return `${pipedHead(subcommand)}${SERVED} ${[namedWithActs(subcommand), ...required, ...optional].join(" ")}`;
|
|
8837
9004
|
};
|
|
8838
9005
|
var usageHeadOf = (version) => `${TURNZERO_CLOUD_PACKAGE} ${version}: the Turn Zero Cloud command, installed from the public npm registry by your platform's tag, latest on production and next on development.`;
|
|
8839
|
-
var
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
const
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
}
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
}
|
|
8869
|
-
|
|
9006
|
+
var unreadNames = () => SUBCOMMANDS.filter((subcommand) => subcommand.credential === null && subcommand.reads === void 0).map((subcommand) => subcommand.name);
|
|
9007
|
+
var unattendedNames = () => SUBCOMMANDS.filter((subcommand) => subcommand.credential?.mintedToken === true).map((subcommand) => subcommand.name);
|
|
9008
|
+
var computerOnlyNames = () => SUBCOMMANDS.filter((subcommand) => subcommand.credential?.computerToken === true).map((subcommand) => subcommand.name);
|
|
9009
|
+
var sectionLinesOf = (subcommand) => [
|
|
9010
|
+
`${namedWithActs(subcommand)}: ${subcommand.summary}`,
|
|
9011
|
+
"",
|
|
9012
|
+
"Options:",
|
|
9013
|
+
...subcommand.options.flatMap((option) => [` ${shown(option)}${option.required ? " (required)" : ""}`, ` ${option.description}`]),
|
|
9014
|
+
""
|
|
9015
|
+
];
|
|
9016
|
+
var pipedLinesOf = (subcommand) => {
|
|
9017
|
+
const credential = subcommand.credential;
|
|
9018
|
+
if (credential === null || credential.piped === false) return [];
|
|
9019
|
+
return [
|
|
9020
|
+
` ${subcommand.name} reads ${credential.aGrant} from standard input, as the line ${credential.call} answers pipes it in${credential.mintedToken ? `; and otherwise a minted token from ${MINTED_TOKEN_VARIABLE} where it is set, or this computer's token from its credential file` : credential.computerToken === true ? `; and otherwise this computer's token from its credential file, never a minted token from ${MINTED_TOKEN_VARIABLE}, whose being set refuses the run` : ""}${credential.bare !== void 0 ? `; ${credential.bare}` : ""}.`
|
|
9021
|
+
];
|
|
9022
|
+
};
|
|
9023
|
+
var readsLinesOf = (subcommand) => subcommand.reads !== void 0 ? [` ${subcommand.name} reads ${subcommand.reads}.`] : [];
|
|
9024
|
+
var noCredentialLine = () => {
|
|
9025
|
+
const unread = unreadNames();
|
|
9026
|
+
return ` The command takes no credential in its arguments, and ${listed(unread)} ${unread.length === 1 ? "reads" : "read"} none.`;
|
|
9027
|
+
};
|
|
9028
|
+
var mintedTokenVariableLine = () => ` ${MINTED_TOKEN_VARIABLE} under ${listed(unattendedNames())}, a minted token for a tool connected by a minted token or an unattended run, set for the one line from where the tool keeps it or from your CI's secret store. Where it is set, it wins over this computer's credential file.`;
|
|
9029
|
+
var originVariableLine = () => {
|
|
9030
|
+
const computerOnly = computerOnlyNames();
|
|
9031
|
+
return ` ${ORIGIN_VARIABLE} under ${listed(unattendedNames())}, in an unattended run, the platform's address the minted token is sent to where the line names no ${DEPLOY_OPTION.origin}. Under the token, a ${DEPLOY_OPTION.origin} the line names must be that address, or ${DEFAULT_COMMAND_ORIGIN} where this variable is unset. Where ${MINTED_TOKEN_VARIABLE} is not set and the line names no ${DEPLOY_OPTION.origin}, this variable names the address whose token the command reads from this computer's credential file${computerOnly.length > 0 ? `, under ${listed(computerOnly)} too where no grant is piped` : ""}. A run under a grant does not read it.`;
|
|
9032
|
+
};
|
|
9033
|
+
var USER_AGENT_LINE = ` ${USER_AGENT_VARIABLE} text appended to the command's user agent on each request.`;
|
|
9034
|
+
var ownVariableLinesOf = (subcommand) => (subcommand.variables ?? []).map(([name, does]) => ` ${name} under ${subcommand.name}, ${does}`);
|
|
9035
|
+
var statusLineOf = (status, meaning, clauses, also) => ` ${COMMAND_STATUS[status]} ${meaning}${clauses.length > 0 ? `: ${clauses.join("; ")}` : ""}.${also !== void 0 ? ` ${also}` : ""}`;
|
|
9036
|
+
var noteLinesOf2 = (subcommand) => (subcommand.note ?? []).flatMap((paragraph) => ["", paragraph]);
|
|
9037
|
+
var usageBlockOf = (subcommandHelp) => [
|
|
9038
|
+
"Usage:",
|
|
9039
|
+
...SUBCOMMANDS.map((subcommand) => ` ${usageLineOf(subcommand)}`),
|
|
9040
|
+
` ${SERVED} --version`,
|
|
9041
|
+
` ${SERVED} --help`,
|
|
9042
|
+
...subcommandHelp ? [` ${SERVED} <subcommand> --help`] : []
|
|
9043
|
+
];
|
|
9044
|
+
var sharedSentenceLines = () => [WINDOWS_SENTENCE, "", NPX_FORM_SENTENCE, "", QUOTED_PATH_SENTENCE];
|
|
9045
|
+
var SUBCOMMAND_HELP_SENTENCE = `Run ${SERVED} <subcommand> --help for that subcommand's options, credential, variables, statuses, and notes.`;
|
|
9046
|
+
var SHARED_HELP_SENTENCE = `Run ${SERVED} --help for every subcommand and the sentences they share: a line's Windows form, its npx form, and a path between double quotes.`;
|
|
9047
|
+
var shortUsageText = (version = commandVersion()) => [
|
|
9048
|
+
usageHeadOf(version),
|
|
9049
|
+
"",
|
|
9050
|
+
...usageBlockOf(true),
|
|
9051
|
+
"",
|
|
9052
|
+
...sharedSentenceLines(),
|
|
9053
|
+
"",
|
|
9054
|
+
"Statuses:",
|
|
9055
|
+
...STATUS_MEANINGS.map(([status, meaning, also]) => statusLineOf(status, meaning, [], also)),
|
|
9056
|
+
"",
|
|
9057
|
+
"Subcommands:",
|
|
9058
|
+
...SUBCOMMANDS.map((subcommand) => ` ${namedWithActs(subcommand)}: ${subcommand.summary}`),
|
|
9059
|
+
"",
|
|
9060
|
+
SUBCOMMAND_HELP_SENTENCE
|
|
9061
|
+
].join("\n");
|
|
9062
|
+
var subcommandHelpOf = (subcommand, version = commandVersion()) => {
|
|
9063
|
+
const lines = [usageHeadOf(version), "", "Usage:", ` ${usageLineOf(subcommand)}`, "", ...sectionLinesOf(subcommand)];
|
|
9064
|
+
lines.push("Input:", ...pipedLinesOf(subcommand), ...readsLinesOf(subcommand), noCredentialLine(), "", "Environment variables:", ...ownVariableLinesOf(subcommand));
|
|
9065
|
+
if (unattendedNames().includes(subcommand.name)) lines.push(mintedTokenVariableLine());
|
|
9066
|
+
if (unattendedNames().length > 0 && (unattendedNames().includes(subcommand.name) || computerOnlyNames().includes(subcommand.name))) lines.push(originVariableLine());
|
|
9067
|
+
lines.push(USER_AGENT_LINE, "", "Statuses:");
|
|
8870
9068
|
for (const [status, meaning, also] of STATUS_MEANINGS) {
|
|
8871
|
-
const
|
|
8872
|
-
|
|
9069
|
+
const clause = subcommand.statuses[status];
|
|
9070
|
+
if (clause !== void 0 || also !== void 0) lines.push(statusLineOf(status, meaning, clause !== void 0 ? [`under ${subcommand.name}, ${clause}`] : [], also));
|
|
8873
9071
|
}
|
|
8874
|
-
|
|
9072
|
+
lines.push(...noteLinesOf2(subcommand), "", SHARED_HELP_SENTENCE);
|
|
8875
9073
|
return lines.join("\n");
|
|
8876
9074
|
};
|
|
9075
|
+
var subcommandHelpText = (name, version = commandVersion()) => {
|
|
9076
|
+
const subcommand = SUBCOMMANDS.find((entry) => entry.name === name);
|
|
9077
|
+
return subcommand === void 0 ? null : subcommandHelpOf(subcommand, version);
|
|
9078
|
+
};
|
|
8877
9079
|
var grantArgumentSentence = () => "An argument holds a grant, and the command never takes a grant from its arguments: it reads the grant from standard input, as the line your tool's call answers pipes it in. Run the line the call answered as given. Nothing was sent and nothing was started.";
|
|
8878
9080
|
var tokenArgumentSentence = () => `An argument holds a minted token, and the command never takes a credential from its arguments: it reads a minted token from ${MINTED_TOKEN_VARIABLE} alone, which a tool connected by a minted token, or an unattended run, sets 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.`;
|
|
8879
9081
|
var NOTHING_STARTED2 = "Nothing was started.";
|
|
@@ -8894,21 +9096,21 @@ var valuePlacesOf = (argv2) => {
|
|
|
8894
9096
|
}
|
|
8895
9097
|
return places;
|
|
8896
9098
|
};
|
|
9099
|
+
var noSubcommandSentence = (first) => `${first === void 0 ? "No subcommand was named" : `The first argument is not a subcommand of ${TURNZERO_CLOUD_PACKAGE}`}: the subcommands are ${SUBCOMMANDS.map((entry) => entry.name).join(", ")}. ${NOTHING_STARTED2}`;
|
|
8897
9100
|
var parseArguments = (argv2, onWindows2 = ON_WINDOWS) => {
|
|
8898
9101
|
if (argv2.some((argument) => argument.includes(UPLOAD_GRANT_PREFIX))) return { kind: "refused", sentence: grantArgumentSentence() };
|
|
8899
9102
|
if (argv2.some((argument) => argument.includes(MINTED_TOKEN_PREFIX))) return { kind: "refused", sentence: tokenArgumentSentence() };
|
|
8900
9103
|
const values = valuePlacesOf(argv2);
|
|
8901
9104
|
const standsAlone = (words) => argv2.some((argument, index) => words.includes(argument) && !values.has(index));
|
|
8902
|
-
if (standsAlone(["--help", "-h"])) return { kind: "help" };
|
|
8903
|
-
if (standsAlone(["--version"])) return { kind: "version" };
|
|
8904
9105
|
const [first, ...afterName] = argv2;
|
|
8905
9106
|
const subcommand = SUBCOMMANDS.find((entry) => entry.name === first);
|
|
8906
|
-
if (
|
|
8907
|
-
return {
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
};
|
|
9107
|
+
if (standsAlone(["--help", "-h"])) {
|
|
9108
|
+
if (subcommand !== void 0) return { kind: "help", subcommand };
|
|
9109
|
+
if (first.startsWith("-")) return { kind: "help" };
|
|
9110
|
+
return { kind: "refused", sentence: noSubcommandSentence(first) };
|
|
8911
9111
|
}
|
|
9112
|
+
if (standsAlone(["--version"])) return { kind: "version" };
|
|
9113
|
+
if (subcommand === void 0) return { kind: "refused", sentence: noSubcommandSentence(first) };
|
|
8912
9114
|
let act = null;
|
|
8913
9115
|
let rest = afterName;
|
|
8914
9116
|
if (subcommand.acts !== void 0) {
|
|
@@ -8968,7 +9170,7 @@ async function runGuarded(argv2, io2) {
|
|
|
8968
9170
|
}
|
|
8969
9171
|
const parsed = parseArguments(argv2, io2.windows ?? ON_WINDOWS);
|
|
8970
9172
|
if (parsed.kind === "help") {
|
|
8971
|
-
io2.print(
|
|
9173
|
+
io2.print((parsed.subcommand === void 0 ? null : subcommandHelpText(parsed.subcommand.name, version)) ?? shortUsageText(version));
|
|
8972
9174
|
return COMMAND_STATUS.done;
|
|
8973
9175
|
}
|
|
8974
9176
|
if (parsed.kind === "version") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turnzero",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.3-next.1",
|
|
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",
|