turnzero 0.18.3 → 0.20.0-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/README.md +2 -2
- package/dist/turnzero-cloud.mjs +314 -127
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Turn Zero Cloud connects to your AI coding tool through a Model Context Protocol (MCP) server. Once connected, your AI can code, build, and test backends, connect them to your app, and host your code in the cloud.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Your AI coding tool writes the app; Turn Zero Cloud runs it, with hosting, database, sign-in and scaling included. Your tool writes, builds, and tests the application on your computer. The platform builds it, runs it in production at the application's address, and records its logs, counters, and usage. To start, connect your AI coding tool: https://turnzero.ai/cloud/getting-started/connect-your-tool/
|
|
6
6
|
|
|
7
7
|
## What this package is
|
|
8
8
|
|
|
@@ -23,7 +23,7 @@ Install it once with `npm install -g turnzero`, then run `turnzero login` once i
|
|
|
23
23
|
- `run` starts your application on your computer with that file's settings.
|
|
24
24
|
- `scaffold` writes a new service into your project from a template, installs it, and runs its tests.
|
|
25
25
|
- `login` logs this computer in through your browser, `whoami` names the account its token acts as, and `logout` revokes its token.
|
|
26
|
-
- `authorize` gives this computer a token through your tool,
|
|
26
|
+
- `authorize` gives this computer a token through your signed-in tool, which then tells you it did.
|
|
27
27
|
- `token request` and `token claim` get a token for another computer or for an unattended run.
|
|
28
28
|
|
|
29
29
|
Every subcommand's options, statuses, and output are on the reference page: https://turnzero.ai/cloud/reference/turnzero-command/
|
package/dist/turnzero-cloud.mjs
CHANGED
|
@@ -222,7 +222,8 @@ var SCAFFOLD_OPTION = Object.freeze({
|
|
|
222
222
|
singular: "--singular",
|
|
223
223
|
fields: "--fields",
|
|
224
224
|
path: DEPLOY_OPTION.path,
|
|
225
|
-
origin: DEPLOY_OPTION.origin
|
|
225
|
+
origin: DEPLOY_OPTION.origin,
|
|
226
|
+
application: DEPLOY_OPTION.application
|
|
226
227
|
});
|
|
227
228
|
var SCAFFOLD_NAME_FORM = /^[a-z][a-z0-9]{0,29}$/;
|
|
228
229
|
var SCAFFOLD_FIELD_NAME_FORM = /^[a-z][a-z0-9_]{0,29}$/;
|
|
@@ -419,6 +420,10 @@ var originOf = (text) => {
|
|
|
419
420
|
if (url.protocol === "http:" && LOOPBACK_HOSTNAMES.includes(url.hostname)) return url.origin;
|
|
420
421
|
return null;
|
|
421
422
|
};
|
|
423
|
+
var originReadOf = (value) => {
|
|
424
|
+
const held = (value ?? "").trim();
|
|
425
|
+
return held === "" ? DEFAULT_COMMAND_ORIGIN : originOf(held) ?? held;
|
|
426
|
+
};
|
|
422
427
|
var commandOriginOf = (text) => {
|
|
423
428
|
const origin = originOf(text);
|
|
424
429
|
if (origin !== null) return { origin };
|
|
@@ -905,10 +910,14 @@ var variableReadSentence = (origin, standing) => {
|
|
|
905
910
|
};
|
|
906
911
|
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.`;
|
|
907
912
|
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}.`;
|
|
908
|
-
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, and ${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 the deploy or
|
|
913
|
+
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, and ${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 the deploy, check, take, or scaffold line alone and unsets it once that line ends.`;
|
|
909
914
|
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:";
|
|
910
915
|
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.";
|
|
911
|
-
var loginRouteLines = (io2, origin) => [
|
|
916
|
+
var loginRouteLines = (io2, origin, deployFiles = true) => [
|
|
917
|
+
LOGIN_ROUTE_OPENER,
|
|
918
|
+
...loginLinesFor(io2, origin),
|
|
919
|
+
...deployFiles ? [DEPLOY_FILES_ROUTE_SENTENCE] : []
|
|
920
|
+
];
|
|
912
921
|
var loginRouteRefusalOf = (io2, closing) => {
|
|
913
922
|
const place = credentialFileOf(io2);
|
|
914
923
|
const tag = runTagOf();
|
|
@@ -1364,7 +1373,7 @@ var AUTHORIZE_ROW = Object.freeze({
|
|
|
1364
1373
|
},
|
|
1365
1374
|
variables: [[ORIGIN_VARIABLE, AUTHORIZATION_ORIGIN_VARIABLE_LINE]],
|
|
1366
1375
|
note: [
|
|
1367
|
-
`Authorizing this computer takes three acts once for each platform address,
|
|
1376
|
+
`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, which keeps the token.`,
|
|
1368
1377
|
`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}.`
|
|
1369
1378
|
],
|
|
1370
1379
|
afterOwnError: AUTHORIZE_AFTER_OWN_ERROR,
|
|
@@ -2922,6 +2931,76 @@ function healthPathReadingOf(bytes, reading, recorded) {
|
|
|
2922
2931
|
};
|
|
2923
2932
|
}
|
|
2924
2933
|
|
|
2934
|
+
// src/turnzero_cloud_command/application_manifest.ts
|
|
2935
|
+
import { join as join3, relative as relative3 } from "node:path";
|
|
2936
|
+
var FOLDER_MANIFEST = "manifest.json";
|
|
2937
|
+
var APPLICATION_MEMBER = "application";
|
|
2938
|
+
var BYTE_ORDER_MARK3 = "\uFEFF";
|
|
2939
|
+
var jsonTextOf = (bytes) => {
|
|
2940
|
+
const text = bytes.toString("utf8");
|
|
2941
|
+
return text.startsWith(BYTE_ORDER_MARK3) ? text.slice(BYTE_ORDER_MARK3.length) : text;
|
|
2942
|
+
};
|
|
2943
|
+
var applicationManifestPartialOf = (at) => `${at}.partial`;
|
|
2944
|
+
var NOT_BOUND = Object.freeze({ kind: "unbound", manifest: null });
|
|
2945
|
+
var folderBindingOf = (io2, path) => {
|
|
2946
|
+
let text;
|
|
2947
|
+
let at;
|
|
2948
|
+
try {
|
|
2949
|
+
const folder = io2.realPath(path);
|
|
2950
|
+
if (folder === null) return NOT_BOUND;
|
|
2951
|
+
at = join3(folder, FOLDER_MANIFEST);
|
|
2952
|
+
const real = io2.realPath(at);
|
|
2953
|
+
if (real === null || relative3(at, real) !== "") return NOT_BOUND;
|
|
2954
|
+
const standing = io2.stat(at);
|
|
2955
|
+
if (standing === null || standing.folder) return NOT_BOUND;
|
|
2956
|
+
text = jsonTextOf(io2.readFile(at));
|
|
2957
|
+
} catch {
|
|
2958
|
+
return NOT_BOUND;
|
|
2959
|
+
}
|
|
2960
|
+
let parsed;
|
|
2961
|
+
try {
|
|
2962
|
+
parsed = JSON.parse(text);
|
|
2963
|
+
} catch {
|
|
2964
|
+
return NOT_BOUND;
|
|
2965
|
+
}
|
|
2966
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return NOT_BOUND;
|
|
2967
|
+
const members = parsed;
|
|
2968
|
+
if (!Object.prototype.hasOwnProperty.call(members, APPLICATION_MEMBER)) return { kind: "unbound", manifest: { at, parsed: members } };
|
|
2969
|
+
const application = members[APPLICATION_MEMBER];
|
|
2970
|
+
return typeof application === "string" && UPLOAD_ID.test(application) ? { kind: "bound", application } : { kind: "off_form" };
|
|
2971
|
+
};
|
|
2972
|
+
var BOUND_LINE_HEAD = "bound: ";
|
|
2973
|
+
var boundLineOf = (application) => `${BOUND_LINE_HEAD}${FOLDER_MANIFEST} now names application ${application}; a deploy or check line naming another application is refused`;
|
|
2974
|
+
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.`;
|
|
2975
|
+
var boundElsewhereSentence = (bound) => `${FOLDER_MANIFEST} binds this folder to application ${bound}, and this line names another.`;
|
|
2976
|
+
var OFF_FORM_SENTENCE = `${FOLDER_MANIFEST}'s ${APPLICATION_MEMBER} member is not an application id, a lowercase UUID, so it binds this folder to no application a line can name.`;
|
|
2977
|
+
var BINDING_OWNER_ASK = `Ask the account's owner; where they mean this folder for that application, change or remove the manifest's ${APPLICATION_MEMBER} member, then run the line again.`;
|
|
2978
|
+
var HASH_BINDING_OWNER_ASK = `Ask the account's owner which application this folder deploys to; change or remove the manifest's ${APPLICATION_MEMBER} member to match, then run the line again.`;
|
|
2979
|
+
var bindingRefusalOf = (binding, application, closing) => {
|
|
2980
|
+
if (binding.kind === "unbound" || binding.kind === "bound" && binding.application === application) return null;
|
|
2981
|
+
const first = binding.kind === "bound" ? boundElsewhereSentence(binding.application) : OFF_FORM_SENTENCE;
|
|
2982
|
+
return `${first} ${closing} ${BINDING_OWNER_ASK}`;
|
|
2983
|
+
};
|
|
2984
|
+
var manifestTextWithMember = (parsed, member, value) => `${JSON.stringify({ ...parsed, [member]: value }, null, 2)}
|
|
2985
|
+
`;
|
|
2986
|
+
var writeManifestMember = (io2, manifest, member, value) => {
|
|
2987
|
+
const partial = applicationManifestPartialOf(manifest.at);
|
|
2988
|
+
let made = false;
|
|
2989
|
+
try {
|
|
2990
|
+
if (io2.stat(partial)?.folder === false) io2.remove(partial);
|
|
2991
|
+
made = io2.stat(partial) === null;
|
|
2992
|
+
io2.writeNewFile(partial, manifestTextWithMember(manifest.parsed, member, value));
|
|
2993
|
+
io2.rename(partial, manifest.at);
|
|
2994
|
+
} catch {
|
|
2995
|
+
try {
|
|
2996
|
+
if (made) io2.remove(partial);
|
|
2997
|
+
} catch {
|
|
2998
|
+
}
|
|
2999
|
+
return false;
|
|
3000
|
+
}
|
|
3001
|
+
return true;
|
|
3002
|
+
};
|
|
3003
|
+
|
|
2925
3004
|
// src/turnzero_cloud_command/deploy.ts
|
|
2926
3005
|
import { createHash as createHash2 } from "node:crypto";
|
|
2927
3006
|
import { readFileSync as readFileSync4, realpathSync, statSync as statSync3 } from "node:fs";
|
|
@@ -2929,7 +3008,7 @@ import { resolve as resolve4 } from "node:path";
|
|
|
2929
3008
|
|
|
2930
3009
|
// src/turnzero_cloud_command/commit.ts
|
|
2931
3010
|
import { readFileSync as readFileSync2, statSync } from "node:fs";
|
|
2932
|
-
import { dirname as dirname3, join as
|
|
3011
|
+
import { dirname as dirname3, join as join4, resolve as resolve3 } from "node:path";
|
|
2933
3012
|
var START_COMMIT_FORM = /^[0-9a-f]{7,64}$/;
|
|
2934
3013
|
var GIT_ENTRY = ".git";
|
|
2935
3014
|
var SYMBOLIC_DEPTH_MAX = 5;
|
|
@@ -2959,7 +3038,7 @@ var firstLineOf = (text) => (text.split(/\r?\n/, 1)[0] ?? "").trim();
|
|
|
2959
3038
|
var gitFolderOf = (folder, reads = FILE_SYSTEM_READS) => {
|
|
2960
3039
|
let dir = resolve3(folder);
|
|
2961
3040
|
for (; ; ) {
|
|
2962
|
-
const entry =
|
|
3041
|
+
const entry = join4(dir, GIT_ENTRY);
|
|
2963
3042
|
const kind = reads.kind(entry);
|
|
2964
3043
|
if (kind === "folder") return entry;
|
|
2965
3044
|
if (kind === "file") {
|
|
@@ -2977,7 +3056,7 @@ var gitFolderOf = (folder, reads = FILE_SYSTEM_READS) => {
|
|
|
2977
3056
|
}
|
|
2978
3057
|
};
|
|
2979
3058
|
var sharedGitFolderOf = (gitFolder, reads = FILE_SYSTEM_READS) => {
|
|
2980
|
-
const text = reads.text(
|
|
3059
|
+
const text = reads.text(join4(gitFolder, "commondir"));
|
|
2981
3060
|
const named = text === null ? "" : firstLineOf(text);
|
|
2982
3061
|
if (named === "") return gitFolder;
|
|
2983
3062
|
const shared = resolve3(gitFolder, named);
|
|
@@ -2987,9 +3066,9 @@ var followable = (ref) => ref.startsWith("refs/") && ref.split("/").every((segme
|
|
|
2987
3066
|
var refValueOf = (reads, gitFolder, sharedFolder, ref, depth) => {
|
|
2988
3067
|
if (depth > SYMBOLIC_DEPTH_MAX || !followable(ref)) return null;
|
|
2989
3068
|
const home = PER_WORKTREE_REFS.some((prefix) => ref.startsWith(prefix)) ? gitFolder : sharedFolder;
|
|
2990
|
-
const loose = reads.text(
|
|
3069
|
+
const loose = reads.text(join4(home, ...ref.split("/")));
|
|
2991
3070
|
if (loose !== null) return valueOf(reads, gitFolder, sharedFolder, firstLineOf(loose), depth + 1);
|
|
2992
|
-
const packed = reads.text(
|
|
3071
|
+
const packed = reads.text(join4(sharedFolder, "packed-refs"));
|
|
2993
3072
|
if (packed === null) return null;
|
|
2994
3073
|
for (const line of packed.split(/\r?\n/)) {
|
|
2995
3074
|
if (line.startsWith("#") || line.startsWith("^")) continue;
|
|
@@ -3006,7 +3085,7 @@ var headCommitOf = (folder, reads = FILE_SYSTEM_READS) => {
|
|
|
3006
3085
|
try {
|
|
3007
3086
|
const gitFolder = gitFolderOf(folder, reads);
|
|
3008
3087
|
if (gitFolder === null) return null;
|
|
3009
|
-
const head = reads.text(
|
|
3088
|
+
const head = reads.text(join4(gitFolder, "HEAD"));
|
|
3010
3089
|
if (head === null) return null;
|
|
3011
3090
|
const sharedFolder = sharedGitFolderOf(gitFolder, reads);
|
|
3012
3091
|
if (sharedFolder === null) return null;
|
|
@@ -3105,7 +3184,7 @@ async function writeAgain(io2, what, write) {
|
|
|
3105
3184
|
|
|
3106
3185
|
// src/turnzero_cloud_command/zip.ts
|
|
3107
3186
|
import { readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
|
|
3108
|
-
import { join as
|
|
3187
|
+
import { join as join5 } from "node:path";
|
|
3109
3188
|
import * as zlib from "node:zlib";
|
|
3110
3189
|
import { deflateRawSync, inflateRawSync as inflateRawSync2 } from "node:zlib";
|
|
3111
3190
|
var AI_TOOL_NAMES = Object.freeze([
|
|
@@ -3144,7 +3223,7 @@ var walkFolder = (root, limit, leftOut) => {
|
|
|
3144
3223
|
leftOut?.push(entry.isDirectory() ? `${name}/` : name);
|
|
3145
3224
|
continue;
|
|
3146
3225
|
}
|
|
3147
|
-
const path =
|
|
3226
|
+
const path = join5(folder.path, entry.name);
|
|
3148
3227
|
if (entry.isSymbolicLink()) throw new ZipRefusal(linkRefusalSentence(name));
|
|
3149
3228
|
if (entry.isDirectory()) {
|
|
3150
3229
|
pending.push({ path, name });
|
|
@@ -3234,7 +3313,7 @@ var NO_PACKAGE_SENTENCE = "The folder holds no package.json at its root, so it i
|
|
|
3234
3313
|
function zipFolder(root, seams = {}) {
|
|
3235
3314
|
let rootFile = false;
|
|
3236
3315
|
try {
|
|
3237
|
-
rootFile = statSync2(
|
|
3316
|
+
rootFile = statSync2(join5(root, "package.json")).isFile();
|
|
3238
3317
|
} catch {
|
|
3239
3318
|
rootFile = false;
|
|
3240
3319
|
}
|
|
@@ -3440,6 +3519,7 @@ async function prepareDeploy(io2, options, zipSha256Value, bearer, userAgent, re
|
|
|
3440
3519
|
return prepared;
|
|
3441
3520
|
}
|
|
3442
3521
|
var NOTHING_STARTED = "Nothing was started.";
|
|
3522
|
+
var BINDING_NOTHING_SENT = "Nothing further was read and nothing was sent.";
|
|
3443
3523
|
var NOTHING_UPLOADED_OR_STARTED = "Nothing was uploaded and nothing was started.";
|
|
3444
3524
|
var healthPathRefusalOf = (prepared, zip, places) => {
|
|
3445
3525
|
if (prepared.healthPathToRead === void 0 || places === null) return null;
|
|
@@ -3536,6 +3616,12 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3536
3616
|
}
|
|
3537
3617
|
const origin = sent.origin;
|
|
3538
3618
|
const refused = () => credential.source === "credential_file" ? fileReadSentence(origin) : variableReadSentence(origin, fileStandingOf(io2, origin, io2.print));
|
|
3619
|
+
const binding = pathKindOf(options.path) === "folder" ? folderBindingOf(io2, options.path) : null;
|
|
3620
|
+
const unbound = binding === null ? null : bindingRefusalOf(binding, options.application, BINDING_NOTHING_SENT);
|
|
3621
|
+
if (unbound !== null) {
|
|
3622
|
+
io2.print(unbound);
|
|
3623
|
+
return PROGRESS_EXIT_STATUS.nothing_started;
|
|
3624
|
+
}
|
|
3539
3625
|
const zipped = zipOf(options.path, io2, NOTHING_STARTED, named[DEPLOY_OPTION.path] !== void 0);
|
|
3540
3626
|
if ("refusal" in zipped) {
|
|
3541
3627
|
io2.print(zipped.refusal);
|
|
@@ -3557,6 +3643,9 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3557
3643
|
return PROGRESS_EXIT_STATUS.nothing_started;
|
|
3558
3644
|
}
|
|
3559
3645
|
io2.print(preparedLineOf(prepared));
|
|
3646
|
+
if (binding?.kind === "unbound" && binding.manifest !== null) {
|
|
3647
|
+
io2.print(writeManifestMember(io2, binding.manifest, APPLICATION_MEMBER, options.application) ? boundLineOf(options.application) : BIND_WRITE_FAILED_LINE);
|
|
3648
|
+
}
|
|
3560
3649
|
const { upload, grant, environment } = prepared;
|
|
3561
3650
|
const headers = { authorization: `Bearer ${grant}`, "user-agent": agent.userAgent };
|
|
3562
3651
|
const stop = commandStopSentence(options.application, environment);
|
|
@@ -3667,7 +3756,7 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3667
3756
|
}
|
|
3668
3757
|
if (read2.status !== 200 || !read2.type.startsWith("text/plain")) {
|
|
3669
3758
|
const refusal = markedRefusalOf(read2.status, read2.text);
|
|
3670
|
-
const said = refusal
|
|
3759
|
+
const said = refusal === null ? [notThePlatformLine(PROGRESS_READ_ACT, read2.status)] : read2.status >= PLATFORM_FAILURE_STATUS ? failureLines(PROGRESS_READ_ACT, read2.status, refusal) : refusalLines(PROGRESS_READ_ACT, read2.status, refusal);
|
|
3671
3760
|
for (const line of said) io2.print(line);
|
|
3672
3761
|
io2.print(stop);
|
|
3673
3762
|
return PROGRESS_EXIT_STATUS.refused;
|
|
@@ -3702,19 +3791,21 @@ var DEPLOY_ROW = Object.freeze({
|
|
|
3702
3791
|
done: `the version deployed, or the deploy started where ${DEPLOY_NO_WAIT_VARIABLE} is set`,
|
|
3703
3792
|
failed: "the deploy failed",
|
|
3704
3793
|
unknown: "the wait stopped, or the start drew no answer: the deploy may go on, and read_status reads it",
|
|
3705
|
-
nothing_started: "an option,
|
|
3794
|
+
nothing_started: "an option, credential, folder, or the folder's binding was refused, no credential was read, the platform was too old, no usable upload was answered, no zipped file names the health path, the upload or start was refused, or the upload drew no answer"
|
|
3706
3795
|
},
|
|
3707
3796
|
variables: [[DEPLOY_NO_WAIT_VARIABLE, "any value returns the command once the deploy has started, without waiting for it."]],
|
|
3708
3797
|
note: [
|
|
3709
3798
|
`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, and sends it 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}.`,
|
|
3710
3799
|
`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, such as the job's secret store, 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.`,
|
|
3711
3800
|
`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}.`,
|
|
3712
|
-
`A tool sets ${MINTED_TOKEN_VARIABLE} for the deploy or
|
|
3801
|
+
`A tool sets ${MINTED_TOKEN_VARIABLE} for the deploy, check, take, or scaffold line alone 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.`,
|
|
3713
3802
|
`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.`,
|
|
3714
3803
|
`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.`,
|
|
3715
3804
|
`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.`,
|
|
3716
3805
|
`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.`,
|
|
3717
|
-
`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
|
|
3806
|
+
`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.`,
|
|
3807
|
+
`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.`,
|
|
3808
|
+
`Where the manifest holds no application member, ${DEPLOY_SUBCOMMAND} writes ${DEPLOY_OPTION.application}'s id into it after the ${PREPARED_LINE_HEAD.trimEnd()} line and prints a ${BOUND_LINE_HEAD.trimEnd()} line, so later deploy and check lines naming another application are refused. A write that fails prints one line and stops nothing. A .zip file the line names is not read for it.`
|
|
3718
3809
|
],
|
|
3719
3810
|
afterOwnError: DEPLOY_AFTER_OWN_ERROR,
|
|
3720
3811
|
run: (call, io2) => deploy(call.named, io2, call.flags)
|
|
@@ -3723,6 +3814,7 @@ var DEPLOY_ROW = Object.freeze({
|
|
|
3723
3814
|
// src/turnzero_cloud_command/check.ts
|
|
3724
3815
|
var NOTHING_SENT_OR_CHECKED = "Nothing was sent and nothing was checked.";
|
|
3725
3816
|
var NOTHING_CHECKED = "Nothing was checked.";
|
|
3817
|
+
var BINDING_NOTHING_CHECKED = "Nothing further was read, and nothing was sent or checked.";
|
|
3726
3818
|
var REFUSED_LINE_HEAD = "refused: ";
|
|
3727
3819
|
var MANIFEST_LINE_HEAD = "manifest: ";
|
|
3728
3820
|
var CHECKED_LINE_HEAD = "checked: ";
|
|
@@ -3914,6 +4006,12 @@ async function check(named, io2) {
|
|
|
3914
4006
|
}
|
|
3915
4007
|
const origin = sent.origin;
|
|
3916
4008
|
const refused = () => credential.source === "credential_file" ? fileReadSentence(origin) : variableReadSentence(origin, fileStandingOf(io2, origin, io2.print));
|
|
4009
|
+
const binding = pathKindOf(options.path) === "folder" ? folderBindingOf(io2, options.path) : null;
|
|
4010
|
+
const unbound = binding === null ? null : bindingRefusalOf(binding, options.application, BINDING_NOTHING_CHECKED);
|
|
4011
|
+
if (unbound !== null) {
|
|
4012
|
+
io2.print(unbound);
|
|
4013
|
+
return COMMAND_STATUS.nothing_started;
|
|
4014
|
+
}
|
|
3917
4015
|
const zipped = zipOf(options.path, io2, NOTHING_CHECKED, named[CHECK_OPTION.path] !== void 0, null, "checked");
|
|
3918
4016
|
if ("refusal" in zipped) {
|
|
3919
4017
|
io2.print(zipped.refusal);
|
|
@@ -3946,11 +4044,12 @@ var CHECK_ROW = Object.freeze({
|
|
|
3946
4044
|
statuses: {
|
|
3947
4045
|
done: "none of the checks would refuse a deploy now",
|
|
3948
4046
|
failed: "a deploy would be refused now, the refused: lines naming each refusal",
|
|
3949
|
-
nothing_started: "nothing was checked: an option,
|
|
4047
|
+
nothing_started: "nothing was checked: an option, credential, folder, or the folder's binding was refused, no credential was read and the two routes to one were printed, the platform was too old, or the check call was refused, drew no answer, was redirected, or failed"
|
|
3950
4048
|
},
|
|
3951
4049
|
note: [
|
|
3952
4050
|
`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.`,
|
|
3953
4051
|
`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.`,
|
|
4052
|
+
`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.`,
|
|
3954
4053
|
`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.`
|
|
3955
4054
|
],
|
|
3956
4055
|
afterOwnError: CHECK_AFTER_OWN_ERROR,
|
|
@@ -3978,6 +4077,10 @@ async function hash(named, io2, flags = /* @__PURE__ */ new Set()) {
|
|
|
3978
4077
|
io2.print(LIST_ZIP_FILE_SENTENCE);
|
|
3979
4078
|
return PROGRESS_EXIT_STATUS.nothing_started;
|
|
3980
4079
|
}
|
|
4080
|
+
if (pathKindOf(folder) === "folder" && folderBindingOf(io2, folder).kind === "off_form") {
|
|
4081
|
+
io2.print(`${OFF_FORM_SENTENCE} ${NO_HASH_PRINTED} ${HASH_BINDING_OWNER_ASK}`);
|
|
4082
|
+
return PROGRESS_EXIT_STATUS.nothing_started;
|
|
4083
|
+
}
|
|
3981
4084
|
const zipped = zipOf(folder, io2, NO_HASH_PRINTED, path !== void 0);
|
|
3982
4085
|
if ("refusal" in zipped) {
|
|
3983
4086
|
io2.print(zipped.refusal);
|
|
@@ -3999,7 +4102,7 @@ var HASH_ROW = Object.freeze({
|
|
|
3999
4102
|
});
|
|
4000
4103
|
|
|
4001
4104
|
// src/turnzero_cloud_command/secret.ts
|
|
4002
|
-
import { isAbsolute as isAbsolute3, join as
|
|
4105
|
+
import { isAbsolute as isAbsolute3, join as join6, relative as relative4, resolve as resolve7, sep as sep2 } from "node:path";
|
|
4003
4106
|
|
|
4004
4107
|
// src/turnzero_cloud_command/value.ts
|
|
4005
4108
|
var NOTHING_SENT2 = "Nothing was sent.";
|
|
@@ -4204,15 +4307,15 @@ var secretUnknownSentence = (act) => `Whether the value was written is unknown:
|
|
|
4204
4307
|
var PACKAGE_FILE = "package.json";
|
|
4205
4308
|
var VALUE_FILE_WARNING = `warning: the file ${SECRET_OPTION.valueFile} names stands inside an application folder, and a deploy from that folder would include it. Keep the file outside the application's folder, or delete it once this line ends 0.`;
|
|
4206
4309
|
var valueFileWouldDeploy = (io2, cwd, file) => {
|
|
4207
|
-
const between =
|
|
4310
|
+
const between = relative4(cwd, file);
|
|
4208
4311
|
if (between === "" || between === ".." || between.startsWith(`..${sep2}`) || isAbsolute3(between)) return false;
|
|
4209
4312
|
const segments = between.split(sep2);
|
|
4210
4313
|
if (segments.some(leftOutName)) return false;
|
|
4211
4314
|
let folder = cwd;
|
|
4212
4315
|
for (let depth = 0; depth < segments.length; depth += 1) {
|
|
4213
|
-
if (depth > 0) folder =
|
|
4316
|
+
if (depth > 0) folder = join6(folder, segments[depth - 1]);
|
|
4214
4317
|
try {
|
|
4215
|
-
if (io2.stat(
|
|
4318
|
+
if (io2.stat(join6(folder, PACKAGE_FILE))?.folder === false) return true;
|
|
4216
4319
|
} catch {
|
|
4217
4320
|
}
|
|
4218
4321
|
}
|
|
@@ -4486,11 +4589,11 @@ var PUT_ROW = Object.freeze({
|
|
|
4486
4589
|
|
|
4487
4590
|
// src/turnzero_cloud_command/library_take.ts
|
|
4488
4591
|
import { createHash as createHash3 } from "node:crypto";
|
|
4489
|
-
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute4, join as
|
|
4592
|
+
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute4, join as join8, relative as relative5, resolve as resolve10, sep as sep3 } from "node:path";
|
|
4490
4593
|
|
|
4491
4594
|
// src/turnzero_cloud_command/run.ts
|
|
4492
4595
|
import { constants } from "node:os";
|
|
4493
|
-
import { join as
|
|
4596
|
+
import { join as join7, resolve as resolve9, win32 as win323 } from "node:path";
|
|
4494
4597
|
var RUN_OPTIONS = Object.freeze([
|
|
4495
4598
|
{
|
|
4496
4599
|
name: RUN_OPTION.path,
|
|
@@ -4674,7 +4777,7 @@ async function localRun(call, io2) {
|
|
|
4674
4777
|
return COMMAND_STATUS.nothing_started;
|
|
4675
4778
|
}
|
|
4676
4779
|
try {
|
|
4677
|
-
io2.readFile(
|
|
4780
|
+
io2.readFile(join7(options.folder, PACKAGE_FILE2));
|
|
4678
4781
|
} catch (error) {
|
|
4679
4782
|
io2.print(
|
|
4680
4783
|
`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}`
|
|
@@ -4746,10 +4849,38 @@ var LIBRARY_TAKE_OPTIONS = Object.freeze([
|
|
|
4746
4849
|
name: LIBRARY_OPTION.origin,
|
|
4747
4850
|
value: "<origin>",
|
|
4748
4851
|
required: false,
|
|
4749
|
-
description: `The platform's address. Where absent, ${DEFAULT_COMMAND_ORIGIN}.`
|
|
4852
|
+
description: `The platform's address. Where absent, the address ${ORIGIN_VARIABLE} holds, or ${DEFAULT_COMMAND_ORIGIN} where it is unset. Under ${MINTED_TOKEN_VARIABLE} the token is sent only to the address ${ORIGIN_VARIABLE} holds, or to ${DEFAULT_COMMAND_ORIGIN} where it is unset. Otherwise it names the address whose token the command reads from this computer's credential file, and that token is sent to that address alone.`
|
|
4750
4853
|
}
|
|
4751
4854
|
]);
|
|
4752
4855
|
var NOTHING_WRITTEN2 = "Nothing was written.";
|
|
4856
|
+
var LIBRARY_CREDENTIAL = Object.freeze({
|
|
4857
|
+
grant: "minted token",
|
|
4858
|
+
aGrant: "a minted token",
|
|
4859
|
+
call: `the ${LIBRARY_READ_ACTION} call`,
|
|
4860
|
+
mintedToken: true,
|
|
4861
|
+
piped: false,
|
|
4862
|
+
places: `the command reads no standard input: it reads a minted token from ${MINTED_TOKEN_VARIABLE} where it is set, as a tool connected by a minted token sets it from where its token is kept, or a job with no tool from its secret store, and otherwise this computer's token for the platform's address from its credential file, which ${LOGIN_SUBCOMMAND} and ${AUTHORIZE_SUBCOMMAND} write.`
|
|
4863
|
+
});
|
|
4864
|
+
var LIBRARY_READS = `no standard input: a minted token from ${MINTED_TOKEN_VARIABLE} where it is set, and otherwise this computer's token for the platform's address from its credential file, which ${LOGIN_SUBCOMMAND} and ${AUTHORIZE_SUBCOMMAND} write, sent only to that address; with neither, it prints the variable route first and then, where the address is admitted, the login route`;
|
|
4865
|
+
var TAKE_AFTER_LOGIN = `Once ${LOGIN_SUBCOMMAND} has printed authorized:, run this take line again. ${NOTHING_WRITTEN2}`;
|
|
4866
|
+
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}`;
|
|
4867
|
+
async function readTakeCredential(io2, named, afterLogin) {
|
|
4868
|
+
const line = { named, closing: NOTHING_WRITTEN2 };
|
|
4869
|
+
const read2 = await readCredential(io2, LIBRARY_CREDENTIAL, "", line);
|
|
4870
|
+
if ("unauthorized" in read2) {
|
|
4871
|
+
const lines = [noTokenSentence(read2.unauthorized.origin, read2.unauthorized.expiredAt), VARIABLE_ROUTE_SENTENCES];
|
|
4872
|
+
const origin = authorizationOriginOf(io2.env, named, NOTHING_WRITTEN2);
|
|
4873
|
+
if ("refusal" in origin) return { lines: [...lines, origin.refusal] };
|
|
4874
|
+
const unwritable = loginRouteRefusalOf(io2, NOTHING_WRITTEN2);
|
|
4875
|
+
if (unwritable !== null) return { lines: [...lines, unwritable] };
|
|
4876
|
+
return { lines: [...lines, ...loginRouteLines(io2, origin.origin, false), afterLogin] };
|
|
4877
|
+
}
|
|
4878
|
+
if ("refusal" in read2) return { lines: [read2.refusal] };
|
|
4879
|
+
io2.hide(read2.credential, REDACTED_TOKEN);
|
|
4880
|
+
const sent = credentialOriginOf(io2.env, read2, line);
|
|
4881
|
+
if ("refusal" in sent) return { lines: [sent.refusal] };
|
|
4882
|
+
return { bearer: read2.credential, origin: sent.origin };
|
|
4883
|
+
}
|
|
4753
4884
|
var LINES_IN_WINDOWS_FORM = process.platform === "win32";
|
|
4754
4885
|
var machineLineOf = (line, form = LINES_IN_WINDOWS_FORM, origin = DEFAULT_COMMAND_ORIGIN) => {
|
|
4755
4886
|
const windows = typeof form === "boolean" ? form : form.windows;
|
|
@@ -4788,13 +4919,13 @@ var libraryTakeOptionsOf = (call, cwd) => {
|
|
|
4788
4919
|
return { entry, root: resolve10(cwd, path ?? "."), named: path !== void 0, origin: origin.origin };
|
|
4789
4920
|
};
|
|
4790
4921
|
var inside = (parent, child) => {
|
|
4791
|
-
const between =
|
|
4922
|
+
const between = relative5(parent, child);
|
|
4792
4923
|
return between !== "" && !between.startsWith("..") && !isAbsolute4(between);
|
|
4793
4924
|
};
|
|
4794
|
-
var shownFrom = (root) => (path) => shownName(
|
|
4925
|
+
var shownFrom = (root) => (path) => shownName(relative5(root, path).split(sep3).join("/") || ".");
|
|
4795
4926
|
var repositoryRootOf = (io2, root) => {
|
|
4796
4927
|
for (let at = root; ; at = dirname4(at)) {
|
|
4797
|
-
if (io2.stat(
|
|
4928
|
+
if (io2.stat(join8(at, ".git")) !== null) return at;
|
|
4798
4929
|
if (dirname4(at) === at) return root;
|
|
4799
4930
|
}
|
|
4800
4931
|
};
|
|
@@ -4814,14 +4945,14 @@ var TAKE_FOLDER_WORDS = Object.freeze({
|
|
|
4814
4945
|
manifestCannot: "so the take cannot add its row to it"
|
|
4815
4946
|
});
|
|
4816
4947
|
var libraryFolderOf = (io2, root, words = TAKE_FOLDER_WORDS) => {
|
|
4817
|
-
const own =
|
|
4948
|
+
const own = join8(root, LIBRARY_FOLDER);
|
|
4818
4949
|
const inRoot = { folder: root, called: ROOT_CALLED };
|
|
4819
4950
|
const standing = io2.stat(own);
|
|
4820
4951
|
if (standing !== null) {
|
|
4821
4952
|
if (!standing.folder) return { refusal: `The project's ${LIBRARY_FOLDER} is a file, where the library folder stands. ${NOTHING_WRITTEN2}` };
|
|
4822
4953
|
return { folder: own, fresh: false, within: inRoot };
|
|
4823
4954
|
}
|
|
4824
|
-
const pointerAt =
|
|
4955
|
+
const pointerAt = join8(root, POINTER_FILE);
|
|
4825
4956
|
if (io2.stat(pointerAt) === null) return { folder: own, fresh: true, within: inRoot };
|
|
4826
4957
|
let named;
|
|
4827
4958
|
try {
|
|
@@ -4845,22 +4976,22 @@ var libraryFolderOf = (io2, root, words = TAKE_FOLDER_WORDS) => {
|
|
|
4845
4976
|
return { folder: at, fresh: false, within: { folder: repository, called: REPOSITORY_CALLED } };
|
|
4846
4977
|
};
|
|
4847
4978
|
var applicationFolderRefusalOf = (io2, options) => {
|
|
4848
|
-
const holds = (folder) => io2.stat(
|
|
4979
|
+
const holds = (folder) => io2.stat(join8(folder, LIBRARY_FOLDER)) !== null || io2.stat(join8(folder, POINTER_FILE)) !== null;
|
|
4849
4980
|
const parent = dirname4(options.root);
|
|
4850
4981
|
if (parent === options.root || holds(options.root) || !holds(parent)) return null;
|
|
4851
4982
|
return options.named ? `The folder ${LIBRARY_OPTION.path} names holds neither ${LIBRARY_FOLDER}/ nor ${POINTER_FILE}, and the folder above it holds one, so it may be an application's folder inside that project. Take the entry at the project's root: name the folder above it with ${LIBRARY_OPTION.path}. Or, where the folder is a project of its own, make an empty ${LIBRARY_FOLDER}/ folder in it and run the line again. ${NOTHING_WRITTEN2}` : `This folder holds neither ${LIBRARY_FOLDER}/ nor ${POINTER_FILE}, and the folder above it holds one, so this may be an application's folder inside that project. Take the entry at the project's root: run the line in the folder above. Or, where this folder is a project of its own, make an empty ${LIBRARY_FOLDER}/ folder here and run the line again. ${NOTHING_WRITTEN2}`;
|
|
4852
4983
|
};
|
|
4853
4984
|
var entryFoldersIn = (io2, library) => {
|
|
4854
|
-
const folders = (at) => (io2.listFolder(
|
|
4985
|
+
const folders = (at) => (io2.listFolder(join8(library, ...at.split("/"))) ?? []).filter((name) => !NEVER_PUBLISHED.has(name) && io2.stat(join8(library, ...at.split("/"), name))?.folder === true).sort();
|
|
4855
4986
|
return [
|
|
4856
4987
|
...folders("features"),
|
|
4857
4988
|
...folders("ui").filter((name) => name !== "assets").map((name) => `ui/${name}`),
|
|
4858
4989
|
...folders("ui/assets/fonts").map((name) => `fonts/${name}`),
|
|
4859
|
-
...io2.stat(
|
|
4990
|
+
...io2.stat(join8(library, "prd"))?.folder === true ? ["library/prd"] : []
|
|
4860
4991
|
];
|
|
4861
4992
|
};
|
|
4862
4993
|
var manifestRowsOf = (io2, library, words = TAKE_FOLDER_WORDS) => {
|
|
4863
|
-
const at =
|
|
4994
|
+
const at = join8(library, MANIFEST_FILE2);
|
|
4864
4995
|
if (io2.stat(at) === null) return { rows: null, stood: null };
|
|
4865
4996
|
try {
|
|
4866
4997
|
const stood = io2.readFile(at);
|
|
@@ -4902,7 +5033,7 @@ var listedEntryOf = (answer, name) => {
|
|
|
4902
5033
|
return { name, path, version, closure_hash: closure, files: listed2, source_commit: commit };
|
|
4903
5034
|
};
|
|
4904
5035
|
var LIST_READ = "The read of the entry's file list";
|
|
4905
|
-
var readOf = async (io2, origin, userAgent, body, what) => {
|
|
5036
|
+
var readOf = async (io2, origin, bearer, userAgent, body, what) => {
|
|
4906
5037
|
let status;
|
|
4907
5038
|
let text;
|
|
4908
5039
|
try {
|
|
@@ -4911,10 +5042,15 @@ var readOf = async (io2, origin, userAgent, body, what) => {
|
|
|
4911
5042
|
what,
|
|
4912
5043
|
() => io2.fetch(`${origin}${LIBRARY_READ_PATH}`, {
|
|
4913
5044
|
method: "POST",
|
|
4914
|
-
headers: { "user-agent": userAgent, "content-type": "application/json" },
|
|
4915
|
-
body: JSON.stringify(body)
|
|
5045
|
+
headers: { authorization: `Bearer ${bearer}`, "user-agent": userAgent, "content-type": "application/json" },
|
|
5046
|
+
body: JSON.stringify(body),
|
|
5047
|
+
redirect: "manual"
|
|
4916
5048
|
})
|
|
4917
5049
|
);
|
|
5050
|
+
if (redirected(response)) {
|
|
5051
|
+
await response.body?.cancel().catch(() => void 0);
|
|
5052
|
+
return { lines: [takeRedirectedLine(response.status)] };
|
|
5053
|
+
}
|
|
4918
5054
|
status = response.status;
|
|
4919
5055
|
text = await response.text();
|
|
4920
5056
|
} catch {
|
|
@@ -4924,16 +5060,17 @@ var readOf = async (io2, origin, userAgent, body, what) => {
|
|
|
4924
5060
|
if (status >= 200 && status <= 299 && answer !== null) return { answer };
|
|
4925
5061
|
const refusal = markedRefusalOf(status, text);
|
|
4926
5062
|
if (refusal === null) return { lines: [`${LIBRARY_READ_ACTION} was answered HTTP ${status}, not by the platform. ${NOTHING_WRITTEN2}`] };
|
|
5063
|
+
if (status >= PLATFORM_FAILURE_STATUS) return { lines: [...failureLines(LIBRARY_READ_ACTION, status, refusal), NOTHING_WRITTEN2] };
|
|
4927
5064
|
return { lines: [...refusalLines(LIBRARY_READ_ACTION, status, refusal), NOTHING_WRITTEN2] };
|
|
4928
5065
|
};
|
|
4929
|
-
var fileBytesOf = async (io2, origin, userAgent, name, file) => {
|
|
5066
|
+
var fileBytesOf = async (io2, origin, bearer, userAgent, name, file) => {
|
|
4930
5067
|
const shown2 = `${LIBRARY_FOLDER}/${shownName(file.path)}`;
|
|
4931
5068
|
let text = "";
|
|
4932
5069
|
let encoding = null;
|
|
4933
5070
|
let offset = 0;
|
|
4934
5071
|
let stamp = null;
|
|
4935
5072
|
for (; ; ) {
|
|
4936
|
-
const read2 = await readOf(io2, origin, userAgent, { name, file: file.path, limit: LIBRARY_READ_CHUNK, ...stamp !== null ? { offset, stamp } : {} }, `The read of ${shown2}`);
|
|
5073
|
+
const read2 = await readOf(io2, origin, bearer, userAgent, { name, file: file.path, limit: LIBRARY_READ_CHUNK, ...stamp !== null ? { offset, stamp } : {} }, `The read of ${shown2}`);
|
|
4937
5074
|
if ("lines" in read2) return { lines: [`${shown2}: the read stopped.`, ...read2.lines] };
|
|
4938
5075
|
const chunk = read2.answer.file;
|
|
4939
5076
|
const next = chunk?.next_offset;
|
|
@@ -4979,11 +5116,6 @@ var ofModuleType = (plan, name) => {
|
|
|
4979
5116
|
};
|
|
4980
5117
|
var MADE_LINE_HEAD = "made: ";
|
|
4981
5118
|
var madeLineOf = (made) => `${MADE_LINE_HEAD}${MADE_FOLDER}/package.json, since neither app/package.json nor the project root's package.json stood: private, named ${made.name}${made.module ? ", of type module" : ""}, with no scripts.`;
|
|
4982
|
-
var BYTE_ORDER_MARK3 = "\uFEFF";
|
|
4983
|
-
var jsonTextOf = (bytes) => {
|
|
4984
|
-
const text = bytes.toString("utf8");
|
|
4985
|
-
return text.startsWith(BYTE_ORDER_MARK3) ? text.slice(BYTE_ORDER_MARK3.length) : text;
|
|
4986
|
-
};
|
|
4987
5119
|
var openingCharacterClauseOf = (text) => {
|
|
4988
5120
|
const first = text.codePointAt(0);
|
|
4989
5121
|
if (first === void 0) return "";
|
|
@@ -4993,19 +5125,19 @@ var openingCharacterClauseOf = (text) => {
|
|
|
4993
5125
|
const control = first < 32 && first !== 9 && first !== 10 && first !== 13 || first >= 127 && first <= 159;
|
|
4994
5126
|
return control ? `: the file opens with ${point}, a control character` : "";
|
|
4995
5127
|
};
|
|
4996
|
-
var applicationFolderOf = (io2, root) => [
|
|
5128
|
+
var applicationFolderOf = (io2, root) => [join8(root, "app"), root].find((folder) => io2.stat(join8(folder, "package.json"))?.folder === false) ?? null;
|
|
4997
5129
|
var copyPlanOf = (io2, root, library, name) => {
|
|
4998
5130
|
const shown2 = shownFrom(root);
|
|
4999
5131
|
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}` };
|
|
5000
5132
|
const application = applicationFolderOf(io2, root);
|
|
5001
5133
|
if (application === null) {
|
|
5002
|
-
const app =
|
|
5134
|
+
const app = join8(root, MADE_FOLDER);
|
|
5003
5135
|
const standing = io2.stat(app);
|
|
5004
5136
|
if (standing !== null && !standing.folder || danglingLinkAt(io2, root, MADE_FOLDER)) {
|
|
5005
5137
|
return { refusal: `Something that is no folder stands at ${MADE_FOLDER}, where the take would make the application's package.json. ${NOTHING_WRITTEN2}` };
|
|
5006
5138
|
}
|
|
5007
|
-
const copy2 =
|
|
5008
|
-
const madeFile =
|
|
5139
|
+
const copy2 = join8(app, "lib", name);
|
|
5140
|
+
const madeFile = join8(app, "package.json");
|
|
5009
5141
|
if (io2.stat(madeFile) !== null || standing !== null && danglingLinkAt(io2, app, "package.json")) {
|
|
5010
5142
|
return { refusal: `Something that is no package file stands at ${MADE_FOLDER}/package.json, where the take would make it. ${NOTHING_WRITTEN2}` };
|
|
5011
5143
|
}
|
|
@@ -5018,7 +5150,7 @@ var copyPlanOf = (io2, root, library, name) => {
|
|
|
5018
5150
|
const declaredAt2 = `file:lib/${name}`;
|
|
5019
5151
|
return { application: app, copy: copy2, declared: madePackageFileOf(made, `${PACKAGE_SCOPE}${name}`, declaredAt2), declaredAt: declaredAt2, made };
|
|
5020
5152
|
}
|
|
5021
|
-
const packageFile =
|
|
5153
|
+
const packageFile = join8(application, "package.json");
|
|
5022
5154
|
const read2 = io2.readFile(packageFile);
|
|
5023
5155
|
const text = jsonTextOf(read2);
|
|
5024
5156
|
let parsed;
|
|
@@ -5030,12 +5162,12 @@ var copyPlanOf = (io2, root, library, name) => {
|
|
|
5030
5162
|
}
|
|
5031
5163
|
const packageName = `${PACKAGE_SCOPE}${name}`;
|
|
5032
5164
|
const specs = DEPENDENCY_SECTIONS.map((section) => parsed[section]?.[packageName]).filter((spec) => spec !== void 0);
|
|
5033
|
-
const place = application === root && io2.stat(
|
|
5165
|
+
const place = application === root && io2.stat(join8(root, "app"))?.folder === true ? `app/lib/${name}` : `lib/${name}`;
|
|
5034
5166
|
let copy;
|
|
5035
5167
|
let declared = null;
|
|
5036
5168
|
let declaredAt = null;
|
|
5037
5169
|
if (specs.length === 0) {
|
|
5038
|
-
copy =
|
|
5170
|
+
copy = join8(application, ...place.split("/"));
|
|
5039
5171
|
declaredAt = `file:${place}`;
|
|
5040
5172
|
declared = withDependency(text, parsed, packageName, declaredAt);
|
|
5041
5173
|
} else {
|
|
@@ -5061,11 +5193,11 @@ var stageRefusalOf = (io2, stage, beside) => {
|
|
|
5061
5193
|
return own ? null : `Something that is not the take's own stands at ${STAGE} beside ${beside}, the name the take writes under: move it, then run the line again. ${NOTHING_WRITTEN2}`;
|
|
5062
5194
|
};
|
|
5063
5195
|
var moveFolderIn = (io2, stage, target, files) => {
|
|
5064
|
-
const fresh =
|
|
5065
|
-
const old =
|
|
5196
|
+
const fresh = join8(stage, "new");
|
|
5197
|
+
const old = join8(stage, "old");
|
|
5066
5198
|
io2.remove(stage);
|
|
5067
5199
|
for (const [path, bytes] of files) {
|
|
5068
|
-
const at =
|
|
5200
|
+
const at = join8(fresh, ...path.split("/"));
|
|
5069
5201
|
io2.makeFolder(dirname4(at));
|
|
5070
5202
|
io2.writeFile(at, bytes);
|
|
5071
5203
|
}
|
|
@@ -5105,15 +5237,14 @@ var PACKAGES_LINE_HEAD = "packages: ";
|
|
|
5105
5237
|
var jsonEscapeOf = (character) => Array.from({ length: character.length }, (_, index) => `\\u${character.charCodeAt(index).toString(16).padStart(4, "0")}`).join("");
|
|
5106
5238
|
var shownJson = (text) => [...text].map((character) => shownWhole(character) === character ? character : jsonEscapeOf(character)).join("");
|
|
5107
5239
|
var packagesLineOf = (rows) => `${PACKAGES_LINE_HEAD}${shownJson(JSON.stringify(rows))}`;
|
|
5108
|
-
var applicationManifestPartialOf = (at) => `${at}.partial`;
|
|
5109
5240
|
var PARTIAL_LINK_SENTENCE = writtenLinkSentence(`The file the application manifest is written to first, ${MANIFEST_FILE2}.partial beside it,`);
|
|
5110
|
-
var danglingLinkAt = (io2, folder, name) => io2.stat(
|
|
5241
|
+
var danglingLinkAt = (io2, folder, name) => io2.stat(join8(folder, name)) === null && (io2.listFolder(folder) ?? []).some((held) => held.toLowerCase() === name.toLowerCase() && io2.stat(join8(folder, held)) === null);
|
|
5111
5242
|
var linkStandsAt = (io2, path) => {
|
|
5112
5243
|
const name = basename2(path);
|
|
5113
5244
|
if (!(io2.listFolder(dirname4(path)) ?? []).includes(name)) return false;
|
|
5114
5245
|
const real = io2.realPath(path);
|
|
5115
5246
|
const folder = io2.realPath(dirname4(path));
|
|
5116
|
-
return real === null || folder === null ||
|
|
5247
|
+
return real === null || folder === null || relative5(join8(folder, name), real) !== "";
|
|
5117
5248
|
};
|
|
5118
5249
|
var writesThroughLink = (io2, holder, path) => linkStandsAt(io2, path) || !heldInside(io2, holder, path);
|
|
5119
5250
|
var writtenNameRefusalOf = (io2, holder, names) => {
|
|
@@ -5122,11 +5253,11 @@ var writtenNameRefusalOf = (io2, holder, names) => {
|
|
|
5122
5253
|
};
|
|
5123
5254
|
var applicationManifestOf = (io2, root, library, folder = applicationFolderOf(io2, root)) => {
|
|
5124
5255
|
if (folder === null) return { at: null, unwritten: "no_folder" };
|
|
5125
|
-
const at =
|
|
5256
|
+
const at = join8(folder, MANIFEST_FILE2);
|
|
5126
5257
|
const standing = io2.stat(at);
|
|
5127
5258
|
if (standing === null) return { at, unwritten: "not_standing" };
|
|
5128
5259
|
if (!heldInside(io2, root, at)) return { refusal: linkSentence("The application manifest", ROOT_CALLED) };
|
|
5129
|
-
const libraryManifest =
|
|
5260
|
+
const libraryManifest = join8(library, MANIFEST_FILE2);
|
|
5130
5261
|
if (resolve10(at) === resolve10(libraryManifest) || realPlaceOf(io2, at) === realPlaceOf(io2, libraryManifest)) return { at, unwritten: "library_manifest" };
|
|
5131
5262
|
if (standing.folder) return { at, unwritten: "no_object" };
|
|
5132
5263
|
const partial = applicationManifestPartialOf(at);
|
|
@@ -5138,25 +5269,9 @@ var applicationManifestOf = (io2, root, library, folder = applicationFolderOf(io
|
|
|
5138
5269
|
}
|
|
5139
5270
|
return { at, unwritten: "no_object" };
|
|
5140
5271
|
};
|
|
5141
|
-
var applicationManifestTextOf = (parsed, packages) => `${JSON.stringify({ ...parsed, packages }, null, 2)}
|
|
5142
|
-
`;
|
|
5143
5272
|
var writeApplicationManifest = (io2, manifest, packages) => {
|
|
5144
5273
|
if (!("parsed" in manifest)) return manifest;
|
|
5145
|
-
|
|
5146
|
-
let made = false;
|
|
5147
|
-
try {
|
|
5148
|
-
if (io2.stat(partial)?.folder === false) io2.remove(partial);
|
|
5149
|
-
made = io2.stat(partial) === null;
|
|
5150
|
-
io2.writeNewFile(partial, applicationManifestTextOf(manifest.parsed, packages));
|
|
5151
|
-
io2.rename(partial, manifest.at);
|
|
5152
|
-
} catch {
|
|
5153
|
-
try {
|
|
5154
|
-
if (made) io2.remove(partial);
|
|
5155
|
-
} catch {
|
|
5156
|
-
}
|
|
5157
|
-
return { at: manifest.at, unwritten: "write_failed" };
|
|
5158
|
-
}
|
|
5159
|
-
return { written: manifest.at };
|
|
5274
|
+
return writeManifestMember(io2, manifest, "packages", packages) ? { written: manifest.at } : { at: manifest.at, unwritten: "write_failed" };
|
|
5160
5275
|
};
|
|
5161
5276
|
var packagesWhereOf = (root, outcome) => {
|
|
5162
5277
|
const shown2 = shownFrom(root);
|
|
@@ -5180,17 +5295,17 @@ var COPY_PACKAGES_CLAUSE = "copy the packages line into the application manifest
|
|
|
5180
5295
|
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; ";
|
|
5181
5296
|
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.`;
|
|
5182
5297
|
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";
|
|
5183
|
-
async function readTake(io2, options, userAgent, planOf, planeChecked = false) {
|
|
5298
|
+
async function readTake(io2, options, userAgent, bearer, planOf, planeChecked = false) {
|
|
5184
5299
|
const refuse = (...lines) => ({ lines });
|
|
5185
5300
|
const found = libraryFolderOf(io2, options.root);
|
|
5186
5301
|
if ("refusal" in found) return refuse(found.refusal);
|
|
5187
5302
|
const library = found.folder;
|
|
5188
5303
|
if (!heldInside(io2, found.within.folder, library)) return refuse(linkSentence("The project's library folder", found.within.called));
|
|
5189
|
-
const manifestAt =
|
|
5304
|
+
const manifestAt = join8(library, MANIFEST_FILE2);
|
|
5190
5305
|
const inLibrary = writtenNameRefusalOf(io2, found.within.folder, [
|
|
5191
5306
|
[WRITTEN_NAMES.manifest, manifestAt],
|
|
5192
5307
|
[WRITTEN_NAMES.manifestPartial, `${manifestAt}.partial`],
|
|
5193
|
-
[WRITTEN_NAMES.libraryStage,
|
|
5308
|
+
[WRITTEN_NAMES.libraryStage, join8(library, STAGE)]
|
|
5194
5309
|
]);
|
|
5195
5310
|
if (inLibrary !== null) return refuse(inLibrary);
|
|
5196
5311
|
const manifest = found.fresh ? { rows: null, stood: null } : manifestRowsOf(io2, library);
|
|
@@ -5200,11 +5315,11 @@ async function readTake(io2, options, userAgent, planOf, planeChecked = false) {
|
|
|
5200
5315
|
const older = await planeWireRefusal(io2, options.origin);
|
|
5201
5316
|
if (older !== null) return refuse(older);
|
|
5202
5317
|
}
|
|
5203
|
-
const list = await readOf(io2, options.origin, userAgent, { name: options.entry }, LIST_READ);
|
|
5318
|
+
const list = await readOf(io2, options.origin, bearer, userAgent, { name: options.entry }, LIST_READ);
|
|
5204
5319
|
if ("lines" in list) return refuse(...list.lines);
|
|
5205
5320
|
const entry = listedEntryOf(list.answer, options.entry);
|
|
5206
5321
|
if ("refusal" in entry) return refuse(entry.refusal);
|
|
5207
|
-
const entryFolder =
|
|
5322
|
+
const entryFolder = join8(library, ...entry.path.split("/"));
|
|
5208
5323
|
if (!heldInside(io2, found.within.folder, entryFolder)) return refuse(linkSentence("The entry's folder", found.within.called));
|
|
5209
5324
|
if (io2.stat(entryFolder) !== null && !(manifest.rows ?? []).some((row) => rowName(row) === entry.name)) {
|
|
5210
5325
|
return refuse(sourceSentence(`holds ${entry.name}'s folder and no row for it in its ${MANIFEST_FILE2}`));
|
|
@@ -5214,15 +5329,15 @@ async function readTake(io2, options, userAgent, planOf, planeChecked = false) {
|
|
|
5214
5329
|
if (plan !== null && "refusal" in plan) return refuse(plan.refusal, ...plan.after ?? []);
|
|
5215
5330
|
if (plan !== null && !heldInside(io2, options.root, plan.copy)) return refuse(linkSentence("The application's copy", ROOT_CALLED));
|
|
5216
5331
|
const besideCopy = plan === null ? null : writtenNameRefusalOf(io2, options.root, [
|
|
5217
|
-
...plan.declared !== null ? [[WRITTEN_NAMES.packageFile,
|
|
5332
|
+
...plan.declared !== null ? [[WRITTEN_NAMES.packageFile, join8(plan.application, "package.json")]] : [],
|
|
5218
5333
|
[WRITTEN_NAMES.copyStage, `${plan.copy}${STAGE}`]
|
|
5219
5334
|
]);
|
|
5220
5335
|
if (besideCopy !== null) return refuse(besideCopy);
|
|
5221
|
-
const staged = stageRefusalOf(io2,
|
|
5336
|
+
const staged = stageRefusalOf(io2, join8(library, STAGE), "the library folder") ?? (plan !== null ? stageRefusalOf(io2, `${plan.copy}${STAGE}`, "the application's copy") : null);
|
|
5222
5337
|
if (staged !== null) return refuse(staged);
|
|
5223
5338
|
const bytes = /* @__PURE__ */ new Map();
|
|
5224
5339
|
for (const file of entry.files) {
|
|
5225
|
-
const read2 = await fileBytesOf(io2, options.origin, userAgent, entry.name, file);
|
|
5340
|
+
const read2 = await fileBytesOf(io2, options.origin, bearer, userAgent, entry.name, file);
|
|
5226
5341
|
if ("lines" in read2) return refuse(...read2.lines);
|
|
5227
5342
|
const actual = sha256(read2.bytes);
|
|
5228
5343
|
if (actual !== file.sha256) return refuse(`${LIBRARY_FOLDER}/${shownName(file.path)}: SHA-256 ${actual} is not the listed value ${file.sha256}. ${NOTHING_WRITTEN2}`);
|
|
@@ -5242,11 +5357,11 @@ function writeTake(io2, root, { entry, library, entryFolder, rows, manifestStood
|
|
|
5242
5357
|
source_commit: entry.source_commit,
|
|
5243
5358
|
fetched_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5244
5359
|
});
|
|
5245
|
-
const manifestAt =
|
|
5360
|
+
const manifestAt = join8(library, MANIFEST_FILE2);
|
|
5246
5361
|
const manifestPartial = `${manifestAt}.partial`;
|
|
5247
5362
|
io2.writeFile(manifestPartial, manifestText);
|
|
5248
5363
|
io2.rename(manifestPartial, manifestAt);
|
|
5249
|
-
const entryStage =
|
|
5364
|
+
const entryStage = join8(library, STAGE);
|
|
5250
5365
|
try {
|
|
5251
5366
|
moveFolderIn(
|
|
5252
5367
|
io2,
|
|
@@ -5276,7 +5391,7 @@ function writeTake(io2, root, { entry, library, entryFolder, rows, manifestStood
|
|
|
5276
5391
|
plan.copy,
|
|
5277
5392
|
compiled.map((file) => [file.path.slice(entry.path.length + 1), bytes.get(file.path)])
|
|
5278
5393
|
);
|
|
5279
|
-
const packageFile =
|
|
5394
|
+
const packageFile = join8(plan.application, "package.json");
|
|
5280
5395
|
if (plan.declared !== null) io2.writeFile(packageFile, plan.declared);
|
|
5281
5396
|
if (plan.made !== void 0) io2.print(madeLineOf(plan.made));
|
|
5282
5397
|
io2.print(
|
|
@@ -5330,7 +5445,9 @@ async function libraryTake(call, io2) {
|
|
|
5330
5445
|
}
|
|
5331
5446
|
const fromApplication = applicationFolderRefusalOf(io2, options);
|
|
5332
5447
|
if (fromApplication !== null) return refuse(fromApplication);
|
|
5333
|
-
const
|
|
5448
|
+
const held = await readTakeCredential(io2, call.named[LIBRARY_OPTION.origin], TAKE_AFTER_LOGIN);
|
|
5449
|
+
if ("lines" in held) return refuse(...held.lines);
|
|
5450
|
+
const checked = await readTake(io2, { ...options, origin: held.origin }, agent.userAgent, held.bearer, (library, name) => copyPlanOf(io2, options.root, library, name));
|
|
5334
5451
|
if ("lines" in checked) return refuse(...checked.lines);
|
|
5335
5452
|
const application = applicationManifestOf(io2, options.root, checked.library, checked.plan?.application ?? applicationFolderOf(io2, options.root));
|
|
5336
5453
|
if ("refusal" in application) return refuse(application.refusal);
|
|
@@ -5341,9 +5458,10 @@ async function libraryTake(call, io2) {
|
|
|
5341
5458
|
var LIBRARY_ROW = Object.freeze({
|
|
5342
5459
|
name: LIBRARY_SUBCOMMAND,
|
|
5343
5460
|
acts: LIBRARY_ACTS,
|
|
5344
|
-
summary: "Takes a library entry into the project: reads its files from the library, checks each file's SHA-256 and the entry's closure hash, then writes its row in the library folder's manifest.json and replaces the entry's folder under that folder whole. For an entry with compiled modules it copies its package.json and lib/ into the application's folder, declares the copy where it is not declared, and runs npm install there. It reads no credential.",
|
|
5461
|
+
summary: "Takes a library entry into the project: reads its files from the library, checks each file's SHA-256 and the entry's closure hash, then writes its row in the library folder's manifest.json and replaces the entry's folder under that folder whole. For an entry with compiled modules it copies its package.json and lib/ into the application's folder, declares the copy where it is not declared, and runs npm install there. It reads under the credential check reads. With no credential, it sends nothing and prints the two routes to one, as check does.",
|
|
5345
5462
|
options: LIBRARY_TAKE_OPTIONS,
|
|
5346
|
-
credential:
|
|
5463
|
+
credential: LIBRARY_CREDENTIAL,
|
|
5464
|
+
reads: LIBRARY_READS,
|
|
5347
5465
|
statuses: {
|
|
5348
5466
|
done: "the entry taken, and installed where it carries compiled modules",
|
|
5349
5467
|
failed: "the entry, its row, and the copy written, and npm install failed or the application manifest's packages was not written",
|
|
@@ -5730,6 +5848,7 @@ var whoamiLinesOf = (origin, account, entry) => [
|
|
|
5730
5848
|
`${WHOAMI_LINE_HEADS.token}label ${entry.label !== null ? JSON.stringify(entry.label) : "none"}, expiring ${entry.expires_at !== null ? `at ${entry.expires_at}` : "never"}`
|
|
5731
5849
|
];
|
|
5732
5850
|
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.`;
|
|
5851
|
+
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, ${AUTHORIZE_SUBCOMMAND}${originWordsOf(origin)} run with nothing piped prints the ${MINT_TOKEN_ACTION} call a signed-in tool makes to authorize this computer, and ${PROVISION_SUBCOMMAND}${originWordsOf(origin)} run so prints the call that answers its line.`;
|
|
5733
5852
|
var whoamiAfterLoginSentence = (whoami2) => `Once login has printed authorized:, run ${whoami2} again.`;
|
|
5734
5853
|
async function whoami(call, io2) {
|
|
5735
5854
|
const origin = entryOriginOf(io2.env, call.named[DEPLOY_OPTION.origin], NOTHING_SENT_OR_READ);
|
|
@@ -5753,6 +5872,7 @@ async function whoami(call, io2) {
|
|
|
5753
5872
|
const expired2 = entry !== void 0 && entry.expires_at !== null && Date.parse(entry.expires_at) <= io2.clock().getTime();
|
|
5754
5873
|
if (entry === void 0 || expired2) {
|
|
5755
5874
|
io2.print(noComputerTokenSentence(origin.origin, entry === void 0 ? null : entry.expires_at));
|
|
5875
|
+
io2.print(noLoginRoutesSentence(origin.origin));
|
|
5756
5876
|
const reached = authorizationOriginOf(io2.env, call.named[DEPLOY_OPTION.origin], NOTHING_SENT_OR_READ);
|
|
5757
5877
|
if ("refusal" in reached) {
|
|
5758
5878
|
io2.print(reached.refusal);
|
|
@@ -5783,7 +5903,7 @@ async function whoami(call, io2) {
|
|
|
5783
5903
|
}
|
|
5784
5904
|
var WHOAMI_ROW = Object.freeze({
|
|
5785
5905
|
name: WHOAMI_SUBCOMMAND,
|
|
5786
|
-
summary: `Names the account this computer's token acts as at the platform's address: it reads the token the credential file keeps for the address and makes one ${READ_ACCOUNT_ACTION} call under it. It prints the address, the account's sign-in address and id, and the token's label and expiry. With no token for the address, it sends nothing and prints the login route. It prints no token.`,
|
|
5906
|
+
summary: `Names the account this computer's token acts as at the platform's address: it reads the token the credential file keeps for the address and makes one ${READ_ACCOUNT_ACTION} call under it. It prints the address, the account's sign-in address and id, and the token's label and expiry. With no token for the address, it sends nothing, names the ways on with no login (deploy_files, ${AUTHORIZE_SUBCOMMAND}, and ${PROVISION_SUBCOMMAND}), and then prints the login route. It prints no token.`,
|
|
5787
5907
|
options: WHOAMI_OPTIONS,
|
|
5788
5908
|
credential: null,
|
|
5789
5909
|
reads: "this computer's token for the origin from its credential file, never a minted token from TURNZERO_CLOUD_MINTED_TOKEN, and sends it only to that origin",
|
|
@@ -5791,7 +5911,7 @@ var WHOAMI_ROW = Object.freeze({
|
|
|
5791
5911
|
done: "the account was read",
|
|
5792
5912
|
failed: "the platform refused the read, where it no longer accepts the token among other causes: the printed lines name login",
|
|
5793
5913
|
unknown: "the read drew no answer, or an answer that is not a refusal",
|
|
5794
|
-
nothing_started: "nothing was read: an option or the credential file was refused, the platform was too old, or no live token stands for the address, and the printed lines
|
|
5914
|
+
nothing_started: "nothing was read: an option or the credential file was refused, 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"
|
|
5795
5915
|
},
|
|
5796
5916
|
variables: [[ORIGIN_VARIABLE, `the platform's address whose token is read where the line names no ${DEPLOY_OPTION.origin}.`]],
|
|
5797
5917
|
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}.`],
|
|
@@ -5856,7 +5976,7 @@ var startUpgradeCheck = (io2, subcommand, named, running) => {
|
|
|
5856
5976
|
const read2 = io2.registryFetch;
|
|
5857
5977
|
if (read2 === void 0 || !UPGRADE_CHECK_SUBCOMMANDS.includes(subcommand)) return NO_CHECK;
|
|
5858
5978
|
if (UPGRADE_CHECK_SKIP_VARIABLES.some((name) => io2.env[name] !== void 0)) return NO_CHECK;
|
|
5859
|
-
const origin = named[DEPLOY_OPTION.origin] ?? io2.env[ORIGIN_VARIABLE]
|
|
5979
|
+
const origin = originReadOf(named[DEPLOY_OPTION.origin] ?? io2.env[ORIGIN_VARIABLE]);
|
|
5860
5980
|
const tag = commandTagOf(origin);
|
|
5861
5981
|
const place = credentialFileOf(io2);
|
|
5862
5982
|
const path = (onWindows(io2) ? win324 : posix3).join(place.folder, UPGRADE_CHECK_FILE_NAME);
|
|
@@ -5900,7 +6020,7 @@ var startUpgradeCheck = (io2, subcommand, named, running) => {
|
|
|
5900
6020
|
};
|
|
5901
6021
|
|
|
5902
6022
|
// src/turnzero_cloud_command/provision.ts
|
|
5903
|
-
import { basename as basename3, dirname as dirname5, join as
|
|
6023
|
+
import { basename as basename3, dirname as dirname5, join as join9 } from "node:path";
|
|
5904
6024
|
var PROVISION_OPTIONS = Object.freeze([
|
|
5905
6025
|
{
|
|
5906
6026
|
name: PROVISION_OPTION.application,
|
|
@@ -5957,7 +6077,7 @@ var APPLICATION_FOLDER_MARKS = Object.freeze(["package.json", "manifest.json"]);
|
|
|
5957
6077
|
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. Where the application stands in another folder, move the file into that folder: a local run reads the environment file from the folder it runs in.`;
|
|
5958
6078
|
var outsideApplicationFolder = (io2, envFile) => APPLICATION_FOLDER_MARKS.every((mark) => {
|
|
5959
6079
|
try {
|
|
5960
|
-
return io2.stat(
|
|
6080
|
+
return io2.stat(join9(dirname5(envFile), mark))?.folder !== false;
|
|
5961
6081
|
} catch {
|
|
5962
6082
|
return false;
|
|
5963
6083
|
}
|
|
@@ -6299,7 +6419,7 @@ var PROVISION_ROW = Object.freeze({
|
|
|
6299
6419
|
});
|
|
6300
6420
|
|
|
6301
6421
|
// src/turnzero_cloud_command/scaffold.ts
|
|
6302
|
-
import { basename as basename4, dirname as dirname6, isAbsolute as isAbsolute5, join as
|
|
6422
|
+
import { basename as basename4, dirname as dirname6, isAbsolute as isAbsolute5, join as join10, relative as relative6, resolve as resolve11 } from "node:path";
|
|
6303
6423
|
|
|
6304
6424
|
// src/turnzero_cloud_command/scaffold_template.ts
|
|
6305
6425
|
var SCAFFOLD_TEMPLATE_FILES = Object.freeze({
|
|
@@ -7750,7 +7870,13 @@ var SCAFFOLD_OPTIONS = Object.freeze([
|
|
|
7750
7870
|
name: SCAFFOLD_OPTION.origin,
|
|
7751
7871
|
value: "<origin>",
|
|
7752
7872
|
required: false,
|
|
7753
|
-
description: `The platform's address.
|
|
7873
|
+
description: `The platform's address. Under a template that takes library entries, where absent, the address ${ORIGIN_VARIABLE} holds, or ${DEFAULT_COMMAND_ORIGIN} where it is unset. There, under ${MINTED_TOKEN_VARIABLE} the token is sent only to the address ${ORIGIN_VARIABLE} holds, or to ${DEFAULT_COMMAND_ORIGIN} where it is unset. Otherwise it names the address whose token the command reads from this computer's credential file, and that token is sent to that address alone. Under a template that takes none, where absent, ${DEFAULT_COMMAND_ORIGIN}.`
|
|
7874
|
+
},
|
|
7875
|
+
{
|
|
7876
|
+
name: SCAFFOLD_OPTION.application,
|
|
7877
|
+
value: "<application id>",
|
|
7878
|
+
required: false,
|
|
7879
|
+
description: `Under every template, the application the service deploys to, by its id, a lowercase UUID, as create_application answers it. The written ${APPLICATION_MANIFEST} names it in its application member, which binds the folder to that application, so a deploy or check line naming another is refused. Where absent, the first deploy writes the member.`
|
|
7754
7880
|
}
|
|
7755
7881
|
]);
|
|
7756
7882
|
var EMPTY_PACKAGES = '"packages": []';
|
|
@@ -7777,6 +7903,7 @@ var NEITHER_LEFT = Object.freeze({ tools: "written", claude: false });
|
|
|
7777
7903
|
var ROOT_TOOLS_STEP = `Add this 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}: ${ROOT_SENTENCE}`;
|
|
7778
7904
|
var ROOT_TOOLS_FAILED_STEP = `Add this 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}: ${ROOT_SENTENCE}`;
|
|
7779
7905
|
var ROOT_CLAUDE_STEP = `Add this sentence to ${CLAUDE_FILE} at the project's root, since Claude Code reads that file and then reads no ${TOOLS_FILE} by itself: ${ROOT_SENTENCE}`;
|
|
7906
|
+
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}`;
|
|
7780
7907
|
var RESOURCE_REFUSAL = Object.freeze({
|
|
7781
7908
|
/** A plural outside SCAFFOLD_NAME_FORM. */
|
|
7782
7909
|
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}`,
|
|
@@ -7787,11 +7914,17 @@ var RESOURCE_REFUSAL = Object.freeze({
|
|
|
7787
7914
|
/** A plural that does not end in s, with no `--singular`. */
|
|
7788
7915
|
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}`,
|
|
7789
7916
|
/** The plural `s` alone, with no `--singular`: its singular would be empty. */
|
|
7790
|
-
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}
|
|
7917
|
+
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}`,
|
|
7918
|
+
/** A singular, given or derived, of `json` (#5225): the resource's
|
|
7919
|
+
* invalid error, invalid_<singular>, would then be invalid_json, the
|
|
7920
|
+
* error of a body that does not parse, and a caller could no longer
|
|
7921
|
+
* tell the two apart (PLD-L0-106). */
|
|
7922
|
+
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}`
|
|
7791
7923
|
});
|
|
7792
7924
|
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}`;
|
|
7793
7925
|
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}`;
|
|
7794
7926
|
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}`;
|
|
7927
|
+
var JSON_SINGULAR = "json";
|
|
7795
7928
|
var resourceNamesOf = (call, template) => {
|
|
7796
7929
|
const plural = call.named[SCAFFOLD_OPTION.resource];
|
|
7797
7930
|
const singular = call.named[SCAFFOLD_OPTION.singular];
|
|
@@ -7802,10 +7935,11 @@ var resourceNamesOf = (call, template) => {
|
|
|
7802
7935
|
if (singular !== void 0 && !SCAFFOLD_NAME_FORM.test(singular)) return { refusal: RESOURCE_REFUSAL.singularForm };
|
|
7803
7936
|
const own = kept2.routes.find((word) => word === plural);
|
|
7804
7937
|
if (own !== void 0) return { refusal: ownRouteRefusal(own) };
|
|
7805
|
-
if (singular !== void 0) return { plural, singular };
|
|
7938
|
+
if (singular !== void 0) return singular === JSON_SINGULAR ? { refusal: RESOURCE_REFUSAL.jsonSingular } : { plural, singular };
|
|
7806
7939
|
if (!plural.endsWith("s")) return { refusal: RESOURCE_REFUSAL.noClosingS };
|
|
7807
7940
|
if (plural.length === 1) return { refusal: RESOURCE_REFUSAL.emptySingular };
|
|
7808
|
-
|
|
7941
|
+
const derived = plural.slice(0, -1);
|
|
7942
|
+
return derived === JSON_SINGULAR ? { refusal: RESOURCE_REFUSAL.jsonSingular } : { plural, singular: derived };
|
|
7809
7943
|
};
|
|
7810
7944
|
var CARRIED_FIELDS = Object.freeze([Object.freeze({ name: "title", kind: "text" })]);
|
|
7811
7945
|
var FIELD_KINDS_CLAUSE = `the kind ${orListed(SCAFFOLD_FIELD_KINDS)}`;
|
|
@@ -7857,10 +7991,12 @@ var scaffoldOptionsOf = (call, cwd) => {
|
|
|
7857
7991
|
if (names !== null && "refusal" in names) return names;
|
|
7858
7992
|
const fields = fieldsOf(call, template);
|
|
7859
7993
|
if (fields !== null && "refusal" in fields) return fields;
|
|
7994
|
+
const application = call.named[SCAFFOLD_OPTION.application];
|
|
7995
|
+
if (application !== void 0 && !UPLOAD_ID.test(application)) return { refusal: APPLICATION_FORM_REFUSAL };
|
|
7860
7996
|
const path = call.named[SCAFFOLD_OPTION.path];
|
|
7861
7997
|
const origin = commandOriginOf(call.named[SCAFFOLD_OPTION.origin] ?? DEFAULT_COMMAND_ORIGIN);
|
|
7862
7998
|
if ("refusal" in origin) return origin;
|
|
7863
|
-
return { template, names, fields, root: resolve11(cwd, path ?? "."), named: path !== void 0, origin: origin.origin };
|
|
7999
|
+
return { template, names, fields, root: resolve11(cwd, path ?? "."), named: path !== void 0, origin: origin.origin, application: application ?? null };
|
|
7864
8000
|
};
|
|
7865
8001
|
var capitalised = (name) => `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
|
|
7866
8002
|
var resourceFormsOf = (names) => [
|
|
@@ -8179,9 +8315,36 @@ var manifestWithMinute = (text, minute) => {
|
|
|
8179
8315
|
if (!WRITTEN_MINUTE.test(String(minute))) throw new Error("a minute a run writes is a whole number from 0 to 59");
|
|
8180
8316
|
return `${around[0]}${CRON_HEAD}${minute} ${around[1]}`;
|
|
8181
8317
|
};
|
|
8182
|
-
var
|
|
8318
|
+
var MANIFEST_VERSION_LINE = ' "manifest_version": 1,\n';
|
|
8319
|
+
var APPLICATION_LINE_OPEN = ' "application": "';
|
|
8320
|
+
var applicationLineOf = (application) => `${APPLICATION_LINE_OPEN}${application}",
|
|
8321
|
+
`;
|
|
8322
|
+
var manifestWithApplication = (text, application) => {
|
|
8323
|
+
if (application === null) return text;
|
|
8324
|
+
const around = text.split(MANIFEST_VERSION_LINE);
|
|
8325
|
+
if (around.length !== 2) throw new Error("the template's manifest does not hold one manifest_version line");
|
|
8326
|
+
return `${around[0]}${MANIFEST_VERSION_LINE}${applicationLineOf(application)}${around[1]}`;
|
|
8327
|
+
};
|
|
8328
|
+
var composedFilesOf = (files, rows, minute, application = null) => files.map(([path, text]) => [path, path === APPLICATION_MANIFEST ? manifestWithApplication(manifestWithMinute(manifestWithPackages(text, rows), minute), application) : text]);
|
|
8329
|
+
var APPLICATION_LINE_REST = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh",';
|
|
8330
|
+
var applicationLineRestPart = (text) => text.length <= APPLICATION_LINE_REST.length && [...text].every((character, index) => APPLICATION_LINE_REST[index] === "h" ? /^[0-9a-f]$/.test(character) : character === APPLICATION_LINE_REST[index]);
|
|
8331
|
+
var withoutApplicationLine = (staged, text) => {
|
|
8332
|
+
const at = text.indexOf(MANIFEST_VERSION_LINE);
|
|
8333
|
+
if (at < 0) return staged;
|
|
8334
|
+
const head = text.slice(0, at + MANIFEST_VERSION_LINE.length);
|
|
8335
|
+
if (staged.length <= head.length || !staged.startsWith(head)) return staged;
|
|
8336
|
+
const rest = staged.slice(head.length);
|
|
8337
|
+
if (APPLICATION_LINE_OPEN.startsWith(rest)) return true;
|
|
8338
|
+
if (!rest.startsWith(APPLICATION_LINE_OPEN)) return staged;
|
|
8339
|
+
const after = rest.slice(APPLICATION_LINE_OPEN.length);
|
|
8340
|
+
const lineEnd = after.indexOf("\n");
|
|
8341
|
+
if (lineEnd < 0) return applicationLineRestPart(after);
|
|
8342
|
+
const line = after.slice(0, lineEnd);
|
|
8343
|
+
if (line.length !== APPLICATION_LINE_REST.length || !applicationLineRestPart(line)) return false;
|
|
8344
|
+
return `${head}${after.slice(lineEnd + 1)}`;
|
|
8345
|
+
};
|
|
8183
8346
|
var inside2 = (parent, child) => {
|
|
8184
|
-
const between =
|
|
8347
|
+
const between = relative6(parent, child);
|
|
8185
8348
|
return between !== "" && !between.startsWith("..") && !isAbsolute5(between);
|
|
8186
8349
|
};
|
|
8187
8350
|
var ownManifestText = (staged, text) => {
|
|
@@ -8189,7 +8352,9 @@ var ownManifestText = (staged, text) => {
|
|
|
8189
8352
|
if (around.length !== 2) return false;
|
|
8190
8353
|
const carried = text.split(CARRIED_CRON);
|
|
8191
8354
|
if (carried.length > 2) return false;
|
|
8192
|
-
|
|
8355
|
+
const unbound = withoutApplicationLine(staged, text);
|
|
8356
|
+
if (typeof unbound === "boolean") return unbound;
|
|
8357
|
+
let read2 = unbound;
|
|
8193
8358
|
if (carried.length === 2) {
|
|
8194
8359
|
const head = `${carried[0]}${CRON_HEAD}`;
|
|
8195
8360
|
if (read2.length > head.length) {
|
|
@@ -8223,18 +8388,18 @@ var ownStage = (io2, stage, files) => {
|
|
|
8223
8388
|
const folders = new Set([...texts.keys()].flatMap((path) => path.split("/").slice(0, -1).map((_, index, segments) => segments.slice(0, index + 1).join("/"))));
|
|
8224
8389
|
const own = (at, prefix) => (io2.listFolder(at) ?? []).every((name) => {
|
|
8225
8390
|
const path = prefix === "" ? name : `${prefix}/${name}`;
|
|
8226
|
-
const standing = io2.stat(
|
|
8391
|
+
const standing = io2.stat(join10(at, name));
|
|
8227
8392
|
if (standing === null) return false;
|
|
8228
|
-
if (standing.folder) return folders.has(path) && own(
|
|
8393
|
+
if (standing.folder) return folders.has(path) && own(join10(at, name), path);
|
|
8229
8394
|
const text = texts.get(path);
|
|
8230
|
-
return text !== void 0 && ownFile(io2,
|
|
8395
|
+
return text !== void 0 && ownFile(io2, join10(at, name), standing.size, path, text);
|
|
8231
8396
|
});
|
|
8232
8397
|
return own(stage, "");
|
|
8233
8398
|
};
|
|
8234
|
-
var danglingAt = (io2, folder, name) => io2.stat(
|
|
8399
|
+
var danglingAt = (io2, folder, name) => io2.stat(join10(folder, name)) === null && (io2.listFolder(folder) ?? []).some((held) => held.toLowerCase() === name.toLowerCase() && io2.stat(join10(folder, held)) === null);
|
|
8235
8400
|
var standsAt = (io2, folder, name) => {
|
|
8236
8401
|
try {
|
|
8237
|
-
return io2.stat(
|
|
8402
|
+
return io2.stat(join10(folder, name)) !== null || danglingAt(io2, folder, name);
|
|
8238
8403
|
} catch {
|
|
8239
8404
|
return true;
|
|
8240
8405
|
}
|
|
@@ -8269,8 +8434,8 @@ var folderRefusalOf = (io2, options, files, takeLines) => {
|
|
|
8269
8434
|
}
|
|
8270
8435
|
if (standing2 !== null || dirname6(at) === at) break;
|
|
8271
8436
|
}
|
|
8272
|
-
const app =
|
|
8273
|
-
const stage =
|
|
8437
|
+
const app = join10(root, APPLICATION_FOLDER);
|
|
8438
|
+
const stage = join10(root, STAGE_FOLDER);
|
|
8274
8439
|
const notOwn = STAGE_NOT_OWN;
|
|
8275
8440
|
const standing = io2.stat(app);
|
|
8276
8441
|
if (standing === null ? danglingAt(io2, root, APPLICATION_FOLDER) : !standing.folder) return [noFolderSentence(APPLICATION_FOLDER, "the scaffold writes the application")];
|
|
@@ -8379,7 +8544,7 @@ var nextStepLines = (origin, windows = LINES_IN_WINDOWS_FORM, left = NEITHER_LEF
|
|
|
8379
8544
|
var writeTemplate = (io2, stage, app, files) => {
|
|
8380
8545
|
io2.remove(stage);
|
|
8381
8546
|
for (const [path, text] of files) {
|
|
8382
|
-
const at =
|
|
8547
|
+
const at = join10(stage, ...path.split("/"));
|
|
8383
8548
|
io2.makeFolder(dirname6(at));
|
|
8384
8549
|
io2.writeFile(at, text);
|
|
8385
8550
|
}
|
|
@@ -8392,7 +8557,7 @@ var writeTemplate = (io2, stage, app, files) => {
|
|
|
8392
8557
|
};
|
|
8393
8558
|
var writeRootSentence = (io2, root) => {
|
|
8394
8559
|
try {
|
|
8395
|
-
io2.writeNewFile(
|
|
8560
|
+
io2.writeNewFile(join10(root, TOOLS_FILE), `${ROOT_SENTENCE}
|
|
8396
8561
|
`);
|
|
8397
8562
|
return "written";
|
|
8398
8563
|
} catch (error) {
|
|
@@ -8449,9 +8614,11 @@ async function scaffold(call, io2) {
|
|
|
8449
8614
|
const { takes } = TEMPLATE_FACTS[template];
|
|
8450
8615
|
const defaulted = call.named[SCAFFOLD_OPTION.template] === void 0;
|
|
8451
8616
|
const files = writtenFilesOf(template, options.names, options.fields);
|
|
8452
|
-
const app =
|
|
8453
|
-
const stage =
|
|
8454
|
-
const
|
|
8617
|
+
const app = join10(root, APPLICATION_FOLDER);
|
|
8618
|
+
const stage = join10(root, STAGE_FOLDER);
|
|
8619
|
+
const takeLinesFor = (at) => takes.map((take) => machineLineOf(libraryTakeLineOf({ origin: at, entry: take.entry }), { windows: io2.windows ?? LINES_IN_WINDOWS_FORM, npx: ranThroughNpx(io2) }, at));
|
|
8620
|
+
let lineOrigin = origin;
|
|
8621
|
+
let takeLines = takeLinesFor(lineOrigin);
|
|
8455
8622
|
let reached = "checks";
|
|
8456
8623
|
const stateNow = () => {
|
|
8457
8624
|
if (reached === "template") return "template";
|
|
@@ -8478,10 +8645,14 @@ async function scaffold(call, io2) {
|
|
|
8478
8645
|
if ("lines" in found) return refuse(...found.lines);
|
|
8479
8646
|
held = found;
|
|
8480
8647
|
} else {
|
|
8648
|
+
const credential = await readTakeCredential(io2, call.named[SCAFFOLD_OPTION.origin], SCAFFOLD_AFTER_LOGIN);
|
|
8649
|
+
if ("lines" in credential) return refuse(...credential.lines);
|
|
8650
|
+
lineOrigin = credential.origin;
|
|
8651
|
+
takeLines = takeLinesFor(lineOrigin);
|
|
8481
8652
|
for (const take of takes) {
|
|
8482
|
-
const copy =
|
|
8653
|
+
const copy = join10(app, "lib", take.entry);
|
|
8483
8654
|
const planOf = (library) => writesOver(library, copy) ? { refusal: LIBRARY_OVER_APPLICATION } : { application: app, copy, declared: null, declaredAt: null };
|
|
8484
|
-
const read2 = await readTake(io2, { entry: take.entry, root, named: options.named, origin }, agent.userAgent, planOf, checks.length > 0);
|
|
8655
|
+
const read2 = await readTake(io2, { entry: take.entry, root, named: options.named, origin: credential.origin }, agent.userAgent, credential.bearer, planOf, checks.length > 0);
|
|
8485
8656
|
if ("lines" in read2) return refuse(...read2.lines);
|
|
8486
8657
|
if (read2.plan === null) {
|
|
8487
8658
|
return refuse(`The ${take.entry} entry the library served carries no compiled modules, which the service's package file declares and installs. That is the platform's to fix: report it. ${NOTHING_WRITTEN2}`);
|
|
@@ -8502,7 +8673,7 @@ async function scaffold(call, io2) {
|
|
|
8502
8673
|
reached = "writes";
|
|
8503
8674
|
if (io2.stat(root) === null) io2.makeFolder(root);
|
|
8504
8675
|
if (held.fresh) io2.makeFolder(held.library);
|
|
8505
|
-
writeTemplate(io2, stage, app, composedFilesOf(files, held.packages, minute));
|
|
8676
|
+
writeTemplate(io2, stage, app, composedFilesOf(files, held.packages, minute, options.application));
|
|
8506
8677
|
reached = "template";
|
|
8507
8678
|
const tools = toolsStands ? "stands" : writeRootSentence(io2, root);
|
|
8508
8679
|
if (defaulted) io2.print(DEFAULT_TEMPLATE_LINE);
|
|
@@ -8514,7 +8685,7 @@ async function scaffold(call, io2) {
|
|
|
8514
8685
|
release();
|
|
8515
8686
|
if (ended) return COMMAND_STATUS.unknown;
|
|
8516
8687
|
const taken = checks.map((check2, index) => ({ entry: takes[index].entry, version: check2.entry.version, line: takeLines[index] }));
|
|
8517
|
-
return await installAndTest(io2, app, template, taken,
|
|
8688
|
+
return await installAndTest(io2, app, template, taken, lineOrigin, { tools, claude: claudeStands });
|
|
8518
8689
|
} catch (error) {
|
|
8519
8690
|
const state = stateNow();
|
|
8520
8691
|
const arrivedAs = error instanceof ApplicationArrived ? state : null;
|
|
@@ -8564,11 +8735,15 @@ async function installAndTest(io2, app, template, taken, origin, left) {
|
|
|
8564
8735
|
for (const line of nextStepLines(origin, { windows: io2.windows ?? LINES_IN_WINDOWS_FORM, npx: ranThroughNpx(io2) }, left, template)) io2.print(line);
|
|
8565
8736
|
return COMMAND_STATUS.done;
|
|
8566
8737
|
}
|
|
8738
|
+
var SCAFFOLD_CREDENTIAL = LIBRARY_CREDENTIAL;
|
|
8739
|
+
var SCAFFOLD_READS = `under a template that takes library entries what library take reads, ${LIBRARY_READS}; and under a template that takes none, no credential`;
|
|
8740
|
+
var SCAFFOLD_AFTER_LOGIN = `Once ${LOGIN_SUBCOMMAND} has printed authorized:, run this scaffold line again. ${NOTHING_WRITTEN2}`;
|
|
8567
8741
|
var SCAFFOLD_ROW = Object.freeze({
|
|
8568
8742
|
name: SCAFFOLD_SUBCOMMAND,
|
|
8569
|
-
summary: `Writes a new service from a template into a project, ${SCAFFOLD_DEFAULT_TEMPLATE} where the line names no ${SCAFFOLD_OPTION.template}. A template that takes library entries, as database-service takes the Database entry and web-app takes three, takes each as library take does, checking each file's SHA-256 and each entry's closure hash before it writes anything. It then writes the template's files under app/, the manifest's packages member filled from the library folder's rows, runs npm install and npm test there, and prints the next steps. It starts a new application and writes over none.
|
|
8743
|
+
summary: `Writes a new service from a template into a project, ${SCAFFOLD_DEFAULT_TEMPLATE} where the line names no ${SCAFFOLD_OPTION.template}. A template that takes library entries, as database-service takes the Database entry and web-app takes three, takes each as library take does, under the credential library take reads, checking each file's SHA-256 and each entry's closure hash before it writes anything. It then writes the template's files under app/, the manifest's packages member filled from the library folder's rows, runs npm install and npm test there, and prints the next steps. It starts a new application and writes over none. A template that takes no entry reads no credential.`,
|
|
8570
8744
|
options: SCAFFOLD_OPTIONS,
|
|
8571
|
-
credential:
|
|
8745
|
+
credential: SCAFFOLD_CREDENTIAL,
|
|
8746
|
+
reads: SCAFFOLD_READS,
|
|
8572
8747
|
statuses: {
|
|
8573
8748
|
done: "the files written and installed, the entries taken where the template takes any, and the tests passed",
|
|
8574
8749
|
failed: "the files written, and npm install or the tests failed",
|
|
@@ -8577,6 +8752,7 @@ var SCAFFOLD_ROW = Object.freeze({
|
|
|
8577
8752
|
},
|
|
8578
8753
|
note: [
|
|
8579
8754
|
`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.`,
|
|
8755
|
+
`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.`,
|
|
8580
8756
|
`Under ${listedAnd(RESOURCE_TEMPLATES)}, with ${SCAFFOLD_OPTION.resource}, and ${SCAFFOLD_OPTION.singular} where the singular is not the plural without its closing s, it writes the same service under the resource's names, its table, its route, its functions, and its file names among them, and 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.`,
|
|
8581
8757
|
`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.`,
|
|
8582
8758
|
`${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 prints the way on, starts no npm, and ends with 128 and the signal's number.`,
|
|
@@ -8759,6 +8935,7 @@ var startErrorOf = (error) => {
|
|
|
8759
8935
|
var HELD_SIGNALS = Object.freeze(["SIGINT", "SIGTERM", "SIGHUP"]);
|
|
8760
8936
|
var endedStatusOf = (signal) => 128 + (constants2.signals[signal] ?? 0);
|
|
8761
8937
|
var SIGNALS_PASS_ON = process.platform !== "win32";
|
|
8938
|
+
var CHILD_GROUPS = process.platform !== "win32";
|
|
8762
8939
|
var passedOnSignalOf = (signal, interrupts, inputIsTerminal, passesOn = SIGNALS_PASS_ON) => {
|
|
8763
8940
|
if (!passesOn) return null;
|
|
8764
8941
|
if (signal !== "SIGINT") return signal;
|
|
@@ -8783,16 +8960,18 @@ var holdSignals = (signals, inputIsTerminal, passesOn, passOn, onInterrupt) => {
|
|
|
8783
8960
|
for (const [signal, handler] of held) signals.off(signal, handler);
|
|
8784
8961
|
};
|
|
8785
8962
|
};
|
|
8786
|
-
var childRunnerOf = (signals, inputIsTerminal, passesOn = SIGNALS_PASS_ON) => (run) => new Promise((resolve12) => {
|
|
8963
|
+
var childRunnerOf = (signals, inputIsTerminal, passesOn = SIGNALS_PASS_ON, groups = CHILD_GROUPS) => (run) => new Promise((resolve12) => {
|
|
8787
8964
|
const output = run.output;
|
|
8788
8965
|
let interrupted = false;
|
|
8966
|
+
const group = run.foreground === true && passesOn && groups;
|
|
8789
8967
|
try {
|
|
8790
8968
|
const child = spawn(run.command, [...run.args], {
|
|
8791
8969
|
cwd: run.cwd,
|
|
8792
8970
|
env: run.env,
|
|
8793
8971
|
shell: run.shell ?? false,
|
|
8794
8972
|
stdio: ["ignore", output ? "pipe" : "inherit", output ? "pipe" : "inherit"],
|
|
8795
|
-
windowsHide: false
|
|
8973
|
+
windowsHide: false,
|
|
8974
|
+
detached: group
|
|
8796
8975
|
});
|
|
8797
8976
|
if (output) {
|
|
8798
8977
|
child.stdout?.setEncoding("utf8").on("data", (chunk) => output(chunk, "stdout"));
|
|
@@ -8803,9 +8982,17 @@ var childRunnerOf = (signals, inputIsTerminal, passesOn = SIGNALS_PASS_ON) => (r
|
|
|
8803
8982
|
if (run.foreground) {
|
|
8804
8983
|
const release = holdSignals(
|
|
8805
8984
|
signals,
|
|
8806
|
-
|
|
8985
|
+
// A group of its own hears no terminal's interrupt, so the command hands on the first one too.
|
|
8986
|
+
group ? false : inputIsTerminal(),
|
|
8807
8987
|
passesOn,
|
|
8808
8988
|
(signal) => {
|
|
8989
|
+
if (group && child.pid !== void 0) {
|
|
8990
|
+
try {
|
|
8991
|
+
process.kill(-child.pid, signal);
|
|
8992
|
+
return;
|
|
8993
|
+
} catch {
|
|
8994
|
+
}
|
|
8995
|
+
}
|
|
8809
8996
|
child.kill(signal);
|
|
8810
8997
|
},
|
|
8811
8998
|
() => {
|
|
@@ -9182,7 +9369,7 @@ async function runGuarded(argv2, io2) {
|
|
|
9182
9369
|
if (parsed.kind === "refused") {
|
|
9183
9370
|
say(carriesTokenCode(argv2) ? `${parsed.sentence} ${TOKEN_CODE_UNREAD}` : parsed.sentence);
|
|
9184
9371
|
const npx = "ranThroughNpx" in io2 && io2.ranThroughNpx === true;
|
|
9185
|
-
const help = npx ? npxFormOf(`${SERVED} --help`, io2.env[ORIGIN_VARIABLE]
|
|
9372
|
+
const help = npx ? npxFormOf(`${SERVED} --help`, originReadOf(io2.env[ORIGIN_VARIABLE])) : `${SERVED} --help`;
|
|
9186
9373
|
say(`${io2.windows ?? ON_WINDOWS ? windowsLineOf(help) : help} prints the usage.`);
|
|
9187
9374
|
return COMMAND_STATUS.nothing_started;
|
|
9188
9375
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turnzero",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0-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",
|