turnzero 0.23.0-next.1 → 0.24.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 +1 -0
- package/dist/turnzero-cloud.mjs +356 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ Install it once with `npm install -g turnzero`, then run `turnzero login` once i
|
|
|
25
25
|
- `login` logs this computer in through your browser, `whoami` names the account its token acts as, and `logout` revokes its token.
|
|
26
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
|
+
- `session` signs a test end user of your application in and writes the session's cookie into a file that curl reads.
|
|
28
29
|
|
|
29
30
|
Every subcommand's options, statuses, and output are on the reference page: https://turnzero.ai/cloud/reference/turnzero-command/
|
|
30
31
|
|
package/dist/turnzero-cloud.mjs
CHANGED
|
@@ -258,6 +258,15 @@ var AUTHORIZED_COMPUTER_MEMBER = "authorized_computer";
|
|
|
258
258
|
var AUTHORIZE_SUBCOMMAND = "authorize";
|
|
259
259
|
var LOGOUT_SUBCOMMAND = "logout";
|
|
260
260
|
var WHOAMI_SUBCOMMAND = "whoami";
|
|
261
|
+
var SESSION_SUBCOMMAND = "session";
|
|
262
|
+
var SESSION_OPTION = {
|
|
263
|
+
application: "--application",
|
|
264
|
+
environment: "--environment",
|
|
265
|
+
endUser: "--end-user",
|
|
266
|
+
out: "--out",
|
|
267
|
+
origin: "--origin",
|
|
268
|
+
force: "--force"
|
|
269
|
+
};
|
|
261
270
|
var TOKEN_SUBCOMMAND = "token";
|
|
262
271
|
var TOKEN_REQUEST_ACT = "request";
|
|
263
272
|
var TOKEN_CLAIM_ACT = "claim";
|
|
@@ -767,9 +776,9 @@ var readMovingCredentialFile = (io2, place, say) => {
|
|
|
767
776
|
// src/turnzero_cloud_command/grant.ts
|
|
768
777
|
var GRANT_READ_BOUND_MS = 1e4;
|
|
769
778
|
var GRANT_INPUT_BYTES_MAX = 4096;
|
|
770
|
-
var readStandardInputEnded = (input, boundMs = GRANT_READ_BOUND_MS) => new Promise((
|
|
779
|
+
var readStandardInputEnded = (input, boundMs = GRANT_READ_BOUND_MS) => new Promise((resolve13) => {
|
|
771
780
|
if (input.isTTY) {
|
|
772
|
-
|
|
781
|
+
resolve13({ text: "", bytes: 0, ending: "terminal" });
|
|
773
782
|
return;
|
|
774
783
|
}
|
|
775
784
|
const chunks = [];
|
|
@@ -791,7 +800,7 @@ var readStandardInputEnded = (input, boundMs = GRANT_READ_BOUND_MS) => new Promi
|
|
|
791
800
|
input.removeListener("close", onEnd);
|
|
792
801
|
input.pause();
|
|
793
802
|
input.destroy?.();
|
|
794
|
-
|
|
803
|
+
resolve13({ text: Buffer.concat(chunks).toString("utf8"), bytes, ending });
|
|
795
804
|
};
|
|
796
805
|
const onEnd = () => finish("end");
|
|
797
806
|
const onError = () => finish("error");
|
|
@@ -3233,7 +3242,7 @@ var headCommitOf = (folder, reads = FILE_SYSTEM_READS) => {
|
|
|
3233
3242
|
|
|
3234
3243
|
// src/turnzero_cloud_command/options.ts
|
|
3235
3244
|
var afterOwnErrorOf = (row, call, io2) => typeof row.afterOwnError === "function" ? row.afterOwnError(call, io2) : row.afterOwnError;
|
|
3236
|
-
var PATH_OPTION_NAMES = Object.freeze([DEPLOY_OPTION.path, SECRET_OPTION.valueFile, PROVISION_OPTION.envFile]);
|
|
3245
|
+
var PATH_OPTION_NAMES = Object.freeze([DEPLOY_OPTION.path, SECRET_OPTION.valueFile, PROVISION_OPTION.envFile, SESSION_OPTION.out]);
|
|
3237
3246
|
var DEPLOY_OPTIONS = Object.freeze([
|
|
3238
3247
|
{
|
|
3239
3248
|
name: DEPLOY_OPTION.application,
|
|
@@ -4303,7 +4312,7 @@ var noTerminalSentence = (option, call) => `${option} reads the value from a ter
|
|
|
4303
4312
|
var readPromptedValue = (io2, option, fileOption, call) => {
|
|
4304
4313
|
const terminal = io2.terminal();
|
|
4305
4314
|
if (terminal === null) return Promise.resolve({ refusal: noTerminalSentence(option, call) });
|
|
4306
|
-
return new Promise((
|
|
4315
|
+
return new Promise((resolve13) => {
|
|
4307
4316
|
let typed = "";
|
|
4308
4317
|
let done = false;
|
|
4309
4318
|
let cancel = () => void 0;
|
|
@@ -4313,7 +4322,7 @@ var readPromptedValue = (io2, option, fileOption, call) => {
|
|
|
4313
4322
|
cancel();
|
|
4314
4323
|
terminal.write("\n");
|
|
4315
4324
|
terminal.close();
|
|
4316
|
-
|
|
4325
|
+
resolve13(read2);
|
|
4317
4326
|
};
|
|
4318
4327
|
cancel = io2.schedule(
|
|
4319
4328
|
PROMPT_BOUND_SECONDS * 1e3,
|
|
@@ -5650,8 +5659,8 @@ var openerRunOf = (io2, address) => {
|
|
|
5650
5659
|
};
|
|
5651
5660
|
var openBrowser = async (io2, address) => {
|
|
5652
5661
|
let cancel = () => void 0;
|
|
5653
|
-
const waited = new Promise((
|
|
5654
|
-
cancel = io2.schedule(OPENER_WAIT_MS, () =>
|
|
5662
|
+
const waited = new Promise((resolve13) => {
|
|
5663
|
+
cancel = io2.schedule(OPENER_WAIT_MS, () => resolve13("waiting"));
|
|
5655
5664
|
});
|
|
5656
5665
|
const ended = await Promise.race([io2.child(openerRunOf(io2, address)), waited]);
|
|
5657
5666
|
cancel();
|
|
@@ -5731,13 +5740,13 @@ async function login(call, io2) {
|
|
|
5731
5740
|
const endMs = Math.max(1e3, Date.parse(pending.expires_at) - io2.clock().getTime());
|
|
5732
5741
|
const minutes = minutesLeftOf(pending.expires_at, io2.clock());
|
|
5733
5742
|
let settle = () => void 0;
|
|
5734
|
-
const arrived = new Promise((
|
|
5735
|
-
settle =
|
|
5743
|
+
const arrived = new Promise((resolve13) => {
|
|
5744
|
+
settle = resolve13;
|
|
5736
5745
|
});
|
|
5737
5746
|
let taken = false;
|
|
5738
5747
|
let outcome = () => void 0;
|
|
5739
|
-
const outcomeKnown = new Promise((
|
|
5740
|
-
outcome =
|
|
5748
|
+
const outcomeKnown = new Promise((resolve13) => {
|
|
5749
|
+
outcome = resolve13;
|
|
5741
5750
|
});
|
|
5742
5751
|
const take = (arrival2) => {
|
|
5743
5752
|
if (taken) return false;
|
|
@@ -8973,6 +8982,326 @@ var SCAFFOLD_ROW = Object.freeze({
|
|
|
8973
8982
|
run: scaffold
|
|
8974
8983
|
});
|
|
8975
8984
|
|
|
8985
|
+
// src/turnzero_cloud_command/session.ts
|
|
8986
|
+
import { dirname as dirname7, isAbsolute as isAbsolute6, join as join11, relative as relative7, resolve as resolve12, sep as sep4 } from "node:path";
|
|
8987
|
+
var SIGN_IN_TEST_END_USER_ACTION = "sign_in_test_end_user";
|
|
8988
|
+
var SESSION_DELIVERY = "command";
|
|
8989
|
+
var NOTHING_SENT_OR_WRITTEN = "Nothing was sent and nothing was written.";
|
|
8990
|
+
var SESSION_ACT = "sign-in";
|
|
8991
|
+
var SESSION_CALL_BOUND_SECONDS = 60;
|
|
8992
|
+
var REDACTED_SESSION_COOKIE = "<session cookie>";
|
|
8993
|
+
var COOKIE_JAR_HEADER = "# Netscape HTTP Cookie File";
|
|
8994
|
+
var HTTP_ONLY_PREFIX = "#HttpOnly_";
|
|
8995
|
+
var SUGGESTED_JAR_NAME = "turnzero-session-cookies.txt";
|
|
8996
|
+
var SESSION_LINE_HEAD = "session: ";
|
|
8997
|
+
var SESSION_UNKNOWN_AGAIN = "Whether the end user was signed in is unknown, and nothing was written: run the line again.";
|
|
8998
|
+
var SESSION_RATE_CAPPED = "Nothing was written. Run the line again after the wait the answer names.";
|
|
8999
|
+
var SESSION_SIGNAL_ENDED = "The run was ended before the cookie jar was written, so nothing was written: run the line again.";
|
|
9000
|
+
var SESSION_AFTER_LOGIN = `Once login has printed authorized:, run this session line again. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9001
|
+
var SESSION_OPTIONS = Object.freeze([
|
|
9002
|
+
{
|
|
9003
|
+
name: SESSION_OPTION.application,
|
|
9004
|
+
value: "<application id>",
|
|
9005
|
+
required: true,
|
|
9006
|
+
description: `The application whose test end user signs in, by its id, as the line a ${SIGN_IN_TEST_END_USER_ACTION} call from your tool names it.`
|
|
9007
|
+
},
|
|
9008
|
+
{
|
|
9009
|
+
name: SESSION_OPTION.environment,
|
|
9010
|
+
value: "<environment>",
|
|
9011
|
+
required: true,
|
|
9012
|
+
description: `The environment the end user signs in to, ${COMMAND_ENVIRONMENTS.join(" or ")}.`
|
|
9013
|
+
},
|
|
9014
|
+
{
|
|
9015
|
+
name: SESSION_OPTION.endUser,
|
|
9016
|
+
value: "<end user id or label>",
|
|
9017
|
+
required: true,
|
|
9018
|
+
description: "The test end user, by the id or the label create_test_end_users answered."
|
|
9019
|
+
},
|
|
9020
|
+
{
|
|
9021
|
+
name: SESSION_OPTION.out,
|
|
9022
|
+
value: "<path>",
|
|
9023
|
+
required: true,
|
|
9024
|
+
description: `The cookie jar file the session's cookie is written into, which curl -b reads. Its folder must stand. A file that stands there is refused unless the line names ${SESSION_OPTION.force}, and a path inside the folder the command runs in is refused where that folder holds package.json, since a deploy from it would upload the jar.`
|
|
9025
|
+
},
|
|
9026
|
+
{
|
|
9027
|
+
name: SESSION_OPTION.origin,
|
|
9028
|
+
value: "<origin>",
|
|
9029
|
+
required: false,
|
|
9030
|
+
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.`
|
|
9031
|
+
},
|
|
9032
|
+
{
|
|
9033
|
+
name: SESSION_OPTION.force,
|
|
9034
|
+
value: "",
|
|
9035
|
+
required: false,
|
|
9036
|
+
flag: true,
|
|
9037
|
+
description: `Replaces the file ${SESSION_OPTION.out} names where one stands.`
|
|
9038
|
+
}
|
|
9039
|
+
]);
|
|
9040
|
+
var SESSION_CREDENTIAL = Object.freeze({
|
|
9041
|
+
grant: "minted token",
|
|
9042
|
+
aGrant: "a minted token",
|
|
9043
|
+
call: `the ${SIGN_IN_TEST_END_USER_ACTION} call`,
|
|
9044
|
+
mintedToken: true,
|
|
9045
|
+
piped: false,
|
|
9046
|
+
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.`
|
|
9047
|
+
});
|
|
9048
|
+
var SESSION_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`;
|
|
9049
|
+
var missingOptionSentence = (option) => `${SESSION_SUBCOMMAND} needs ${option.name} ${option.value}, as the line a ${SIGN_IN_TEST_END_USER_ACTION} call answers names it. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9050
|
+
var ENVIRONMENT_REFUSAL = `${SESSION_OPTION.environment} names the environment the end user signs in to, ${COMMAND_ENVIRONMENTS.join(" or ")}. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9051
|
+
var OUT_STANDS_REFUSAL = `A file stands where ${SESSION_OPTION.out} names the cookie jar, and ${SESSION_SUBCOMMAND} replaces one only where the line names ${SESSION_OPTION.force}. Name a new file, or add ${SESSION_OPTION.force} to replace it. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9052
|
+
var OUT_FOLDER_REFUSAL = `${SESSION_OPTION.out} names a folder: name a file for the cookie jar, in that folder or another. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9053
|
+
var OUT_PARENT_REFUSAL = `The folder ${SESSION_OPTION.out} names the cookie jar in does not stand: make it, or name a file in a folder that stands. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9054
|
+
var outInsideApplicationRefusal = (outside2) => `${SESSION_OPTION.out} names a file inside the folder the command runs in, which holds package.json, so a ${DEPLOY_SUBCOMMAND} from that folder would upload the session's cookie with the application. Name a file outside that folder${outside2 !== null ? `, such as ${outside2}` : ""}. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9055
|
+
var jarUntriedRefusal = (code) => `The cookie jar could not be written (${code}): the folder ${SESSION_OPTION.out} names a file in must admit a write. ${NOTHING_SENT_OR_WRITTEN}`;
|
|
9056
|
+
var jarUnwrittenSentence = (code) => `The end user was signed in, and the cookie jar could not be written (${code}), so the session's cookie was not kept: run the line again.`;
|
|
9057
|
+
var SESSION_NO_ANSWER = "The sign-in call drew no answer.";
|
|
9058
|
+
var sessionRedirected = (status) => {
|
|
9059
|
+
const how = answeredHow(status);
|
|
9060
|
+
return `The sign-in call was answered ${status >= 100 ? `${how}, a redirect` : how}, which the command never follows, so the credential was sent to no second address.`;
|
|
9061
|
+
};
|
|
9062
|
+
var sessionAnswerUnusable = (status) => `The sign-in call was answered HTTP ${status} with no answer the command can use: status signed_in, hostname, end_user, expires_at, and cookie, each in its form. Nothing of the answer was printed or written.`;
|
|
9063
|
+
var insideFolder = (folder, path) => {
|
|
9064
|
+
const between = relative7(folder, path);
|
|
9065
|
+
return between === "" || between !== ".." && !between.startsWith(`..${sep4}`) && !isAbsolute6(between);
|
|
9066
|
+
};
|
|
9067
|
+
var sessionOptionsOf = (call, cwd) => {
|
|
9068
|
+
for (const option of SESSION_OPTIONS) {
|
|
9069
|
+
if (option.required && (call.named[option.name] ?? "").trim() === "") return { refusal: missingOptionSentence(option) };
|
|
9070
|
+
}
|
|
9071
|
+
const environment = call.named[SESSION_OPTION.environment];
|
|
9072
|
+
if (!COMMAND_ENVIRONMENTS.includes(environment)) return { refusal: ENVIRONMENT_REFUSAL };
|
|
9073
|
+
const origin = commandOriginOf(call.named[SESSION_OPTION.origin] ?? DEFAULT_COMMAND_ORIGIN);
|
|
9074
|
+
if ("refusal" in origin) return origin;
|
|
9075
|
+
const outGiven = call.named[SESSION_OPTION.out];
|
|
9076
|
+
return {
|
|
9077
|
+
application: call.named[SESSION_OPTION.application],
|
|
9078
|
+
environment,
|
|
9079
|
+
endUser: call.named[SESSION_OPTION.endUser],
|
|
9080
|
+
outGiven,
|
|
9081
|
+
out: resolve12(cwd, outGiven),
|
|
9082
|
+
force: call.flags.has(SESSION_OPTION.force),
|
|
9083
|
+
origin: origin.origin
|
|
9084
|
+
};
|
|
9085
|
+
};
|
|
9086
|
+
var outRefusalOf = (io2, options) => {
|
|
9087
|
+
const standing = io2.stat(options.out);
|
|
9088
|
+
if (standing !== null && standing.folder) return OUT_FOLDER_REFUSAL;
|
|
9089
|
+
if (standing !== null && !options.force) return OUT_STANDS_REFUSAL;
|
|
9090
|
+
const cwd = resolve12(io2.cwd);
|
|
9091
|
+
const manifest = io2.stat(join11(cwd, "package.json"));
|
|
9092
|
+
if (manifest !== null && !manifest.folder && insideFolder(cwd, options.out)) {
|
|
9093
|
+
const above = dirname7(cwd);
|
|
9094
|
+
return outInsideApplicationRefusal(above !== cwd ? join11(above, SUGGESTED_JAR_NAME) : null);
|
|
9095
|
+
}
|
|
9096
|
+
const folder = io2.stat(dirname7(options.out));
|
|
9097
|
+
if (folder === null || !folder.folder) return OUT_PARENT_REFUSAL;
|
|
9098
|
+
return null;
|
|
9099
|
+
};
|
|
9100
|
+
var HOSTNAME_FORM = /^[A-Za-z0-9](?:[A-Za-z0-9.-]{0,251}[A-Za-z0-9])?$/;
|
|
9101
|
+
var COOKIE_NAME_FORM = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
9102
|
+
var COOKIE_VALUE_FORM = /^[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]+$/;
|
|
9103
|
+
var COOKIE_PATH_FORM = /^\/[\x21-\x3a\x3c-\x7e]*$/;
|
|
9104
|
+
var UTC_INSTANT_FORM = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/;
|
|
9105
|
+
var shownText = (value) => typeof value === "string" && value !== "" && value.length <= 256 && oneLine(value) === value;
|
|
9106
|
+
var sessionAnswerOf = (answer) => {
|
|
9107
|
+
const { status, hostname: hostname3, end_user: endUser, expires_at: expiresAt, cookie } = answer;
|
|
9108
|
+
if (status !== "signed_in") return null;
|
|
9109
|
+
if (typeof hostname3 !== "string" || !HOSTNAME_FORM.test(hostname3)) return null;
|
|
9110
|
+
if (endUser === null || typeof endUser !== "object" || Array.isArray(endUser)) return null;
|
|
9111
|
+
const { id, label } = endUser;
|
|
9112
|
+
if (!shownText(id) || label !== null && label !== void 0 && !shownText(label)) return null;
|
|
9113
|
+
if (typeof expiresAt !== "string" || !UTC_INSTANT_FORM.test(expiresAt) || !Number.isFinite(Date.parse(expiresAt))) return null;
|
|
9114
|
+
if (cookie === null || typeof cookie !== "object" || Array.isArray(cookie)) return null;
|
|
9115
|
+
const { name, value, path, secure, http_only: httpOnly } = cookie;
|
|
9116
|
+
if (typeof name !== "string" || !COOKIE_NAME_FORM.test(name)) return null;
|
|
9117
|
+
if (typeof value !== "string" || !COOKIE_VALUE_FORM.test(value)) return null;
|
|
9118
|
+
if (typeof path !== "string" || !COOKIE_PATH_FORM.test(path)) return null;
|
|
9119
|
+
if (typeof secure !== "boolean" || typeof httpOnly !== "boolean") return null;
|
|
9120
|
+
return { hostname: hostname3, endUser: { id, label: typeof label === "string" ? label : null }, expiresAt, cookie: { name, value, path, secure, httpOnly } };
|
|
9121
|
+
};
|
|
9122
|
+
var cookieJarTextOf = (session2) => `${COOKIE_JAR_HEADER}
|
|
9123
|
+
${[
|
|
9124
|
+
`${session2.cookie.httpOnly ? HTTP_ONLY_PREFIX : ""}${session2.hostname}`,
|
|
9125
|
+
"FALSE",
|
|
9126
|
+
session2.cookie.path,
|
|
9127
|
+
session2.cookie.secure ? "TRUE" : "FALSE",
|
|
9128
|
+
String(Math.floor(Date.parse(session2.expiresAt) / 1e3)),
|
|
9129
|
+
session2.cookie.name,
|
|
9130
|
+
session2.cookie.value
|
|
9131
|
+
].join(" ")}
|
|
9132
|
+
`;
|
|
9133
|
+
var sessionLineOf = (session2, outGiven) => `${SESSION_LINE_HEAD}${session2.hostname} for ${oneLine(session2.endUser.label ?? session2.endUser.id)} until ${session2.expiresAt}; cookie ${session2.cookie.name} written to ${outGiven} (curl -b ${outGiven})`;
|
|
9134
|
+
var hideCookieOf = (io2, answer) => {
|
|
9135
|
+
const cookie = answer?.cookie;
|
|
9136
|
+
if (cookie === null || typeof cookie !== "object" || Array.isArray(cookie)) return;
|
|
9137
|
+
const value = cookie.value;
|
|
9138
|
+
if (typeof value === "string" && value !== "") io2.hide(value, REDACTED_SESSION_COOKIE);
|
|
9139
|
+
};
|
|
9140
|
+
var postSignIn = async (io2, origin, bearer, userAgent, body) => {
|
|
9141
|
+
const controller = new AbortController();
|
|
9142
|
+
const cancel = io2.schedule(SESSION_CALL_BOUND_SECONDS * 1e3, () => controller.abort());
|
|
9143
|
+
try {
|
|
9144
|
+
const response = await io2.fetch(`${origin}${actionPathOf(SIGN_IN_TEST_END_USER_ACTION)}`, {
|
|
9145
|
+
method: "POST",
|
|
9146
|
+
headers: { authorization: `Bearer ${bearer}`, "user-agent": userAgent, "content-type": "application/json" },
|
|
9147
|
+
body: JSON.stringify(body),
|
|
9148
|
+
redirect: "manual",
|
|
9149
|
+
signal: controller.signal
|
|
9150
|
+
});
|
|
9151
|
+
return { status: response.status, redirected: redirected(response), text: await response.text() };
|
|
9152
|
+
} catch {
|
|
9153
|
+
return null;
|
|
9154
|
+
} finally {
|
|
9155
|
+
cancel();
|
|
9156
|
+
}
|
|
9157
|
+
};
|
|
9158
|
+
async function callSignIn(io2, options, origin, bearer, userAgent, refused) {
|
|
9159
|
+
const unknown = { status: COMMAND_STATUS.unknown };
|
|
9160
|
+
const answered = await postSignIn(io2, origin, bearer, userAgent, {
|
|
9161
|
+
application: options.application,
|
|
9162
|
+
environment: options.environment,
|
|
9163
|
+
end_user: options.endUser,
|
|
9164
|
+
delivery: SESSION_DELIVERY
|
|
9165
|
+
});
|
|
9166
|
+
if (answered === null) {
|
|
9167
|
+
io2.print(SESSION_NO_ANSWER);
|
|
9168
|
+
io2.print(SESSION_UNKNOWN_AGAIN);
|
|
9169
|
+
return unknown;
|
|
9170
|
+
}
|
|
9171
|
+
const parsed = planeObjectOf(answered.text);
|
|
9172
|
+
hideCookieOf(io2, parsed);
|
|
9173
|
+
if (answered.redirected) {
|
|
9174
|
+
io2.print(sessionRedirected(answered.status));
|
|
9175
|
+
io2.print(SESSION_UNKNOWN_AGAIN);
|
|
9176
|
+
return unknown;
|
|
9177
|
+
}
|
|
9178
|
+
if (answered.status >= PLATFORM_FAILURE_STATUS) {
|
|
9179
|
+
for (const line of failureLines(SESSION_ACT, answered.status, planeRefusalOf(answered.text))) io2.print(line);
|
|
9180
|
+
io2.print(SESSION_UNKNOWN_AGAIN);
|
|
9181
|
+
return unknown;
|
|
9182
|
+
}
|
|
9183
|
+
if (answered.status < 200 || answered.status > 299) {
|
|
9184
|
+
const refusal = markedRefusalOf(answered.status, answered.text);
|
|
9185
|
+
if (refusal === null) {
|
|
9186
|
+
io2.print(notThePlatformLine("sign-in call", answered.status));
|
|
9187
|
+
io2.print(SESSION_UNKNOWN_AGAIN);
|
|
9188
|
+
return unknown;
|
|
9189
|
+
}
|
|
9190
|
+
for (const line of refusalLines(SESSION_ACT, answered.status, refusal)) io2.print(line);
|
|
9191
|
+
if (answered.status === 429 && refusal.error === RATE_CAPPED) {
|
|
9192
|
+
io2.print(SESSION_RATE_CAPPED);
|
|
9193
|
+
} else {
|
|
9194
|
+
const sentence = refused();
|
|
9195
|
+
if (sentence !== null) io2.print(sentence);
|
|
9196
|
+
}
|
|
9197
|
+
return { status: COMMAND_STATUS.failed };
|
|
9198
|
+
}
|
|
9199
|
+
const usable = parsed === null ? null : sessionAnswerOf(parsed);
|
|
9200
|
+
if (usable === null) {
|
|
9201
|
+
io2.print(sessionAnswerUnusable(answered.status));
|
|
9202
|
+
io2.print(SESSION_UNKNOWN_AGAIN);
|
|
9203
|
+
return unknown;
|
|
9204
|
+
}
|
|
9205
|
+
return usable;
|
|
9206
|
+
}
|
|
9207
|
+
async function session(call, io2) {
|
|
9208
|
+
const options = sessionOptionsOf(call, io2.cwd);
|
|
9209
|
+
if ("refusal" in options) {
|
|
9210
|
+
io2.print(options.refusal);
|
|
9211
|
+
return COMMAND_STATUS.nothing_started;
|
|
9212
|
+
}
|
|
9213
|
+
const agent = userAgentOf(io2.env);
|
|
9214
|
+
if ("refusal" in agent) {
|
|
9215
|
+
io2.print(agent.refusal);
|
|
9216
|
+
return COMMAND_STATUS.nothing_started;
|
|
9217
|
+
}
|
|
9218
|
+
const out = outRefusalOf(io2, options);
|
|
9219
|
+
if (out !== null) {
|
|
9220
|
+
io2.print(out);
|
|
9221
|
+
return COMMAND_STATUS.nothing_started;
|
|
9222
|
+
}
|
|
9223
|
+
const named = call.named[SESSION_OPTION.origin];
|
|
9224
|
+
const credential = await readCredential(io2, SESSION_CREDENTIAL, "", { named, closing: NOTHING_SENT_OR_WRITTEN });
|
|
9225
|
+
if ("unauthorized" in credential) {
|
|
9226
|
+
io2.print(noTokenSentence(credential.unauthorized.origin, credential.unauthorized.expiredAt));
|
|
9227
|
+
io2.print(VARIABLE_ROUTE_SENTENCES);
|
|
9228
|
+
const reached = authorizationOriginOf(io2.env, named, NOTHING_SENT_OR_WRITTEN);
|
|
9229
|
+
if ("refusal" in reached) {
|
|
9230
|
+
io2.print(reached.refusal);
|
|
9231
|
+
return COMMAND_STATUS.nothing_started;
|
|
9232
|
+
}
|
|
9233
|
+
const unwritable = loginRouteRefusalOf(io2, NOTHING_SENT_OR_WRITTEN);
|
|
9234
|
+
if (unwritable !== null) {
|
|
9235
|
+
io2.print(unwritable);
|
|
9236
|
+
return COMMAND_STATUS.nothing_started;
|
|
9237
|
+
}
|
|
9238
|
+
for (const line of loginRouteLines(io2, reached.origin, false)) io2.print(line);
|
|
9239
|
+
io2.print(SESSION_AFTER_LOGIN);
|
|
9240
|
+
return COMMAND_STATUS.nothing_started;
|
|
9241
|
+
}
|
|
9242
|
+
if ("refusal" in credential) {
|
|
9243
|
+
io2.print(credential.refusal);
|
|
9244
|
+
return COMMAND_STATUS.nothing_started;
|
|
9245
|
+
}
|
|
9246
|
+
io2.hide(credential.credential, REDACTED_TOKEN);
|
|
9247
|
+
const sent = credentialOriginOf(io2.env, credential, { named, closing: NOTHING_SENT_OR_WRITTEN });
|
|
9248
|
+
if ("refusal" in sent) {
|
|
9249
|
+
io2.print(sent.refusal);
|
|
9250
|
+
return COMMAND_STATUS.nothing_started;
|
|
9251
|
+
}
|
|
9252
|
+
const origin = sent.origin;
|
|
9253
|
+
const refused = () => credential.source === "credential_file" ? fileReadSentence(origin) : variableReadSentence(origin, fileStandingOf(io2, origin, io2.print));
|
|
9254
|
+
const tag = runTagOf();
|
|
9255
|
+
const tried = tryWritingFile(io2, options.out, "owner only", tag);
|
|
9256
|
+
if ("unwritten" in tried) {
|
|
9257
|
+
dropWritingFile(io2, options.out, tag);
|
|
9258
|
+
io2.print(jarUntriedRefusal(tried.unwritten));
|
|
9259
|
+
return COMMAND_STATUS.nothing_started;
|
|
9260
|
+
}
|
|
9261
|
+
const release = io2.whenEnded(() => {
|
|
9262
|
+
io2.print(SESSION_SIGNAL_ENDED);
|
|
9263
|
+
dropWritingFile(io2, options.out, tag);
|
|
9264
|
+
});
|
|
9265
|
+
try {
|
|
9266
|
+
const older = await planeWireRefusal(io2, origin);
|
|
9267
|
+
if (older !== null) {
|
|
9268
|
+
io2.print(older);
|
|
9269
|
+
return COMMAND_STATUS.nothing_started;
|
|
9270
|
+
}
|
|
9271
|
+
const answer = await callSignIn(io2, options, origin, credential.credential, agent.userAgent, refused);
|
|
9272
|
+
if ("status" in answer) return answer.status;
|
|
9273
|
+
const written = writeThenRename(io2, options.out, cookieJarTextOf(answer), "owner only", tag);
|
|
9274
|
+
if ("unwritten" in written) {
|
|
9275
|
+
io2.print(jarUnwrittenSentence(written.unwritten));
|
|
9276
|
+
return COMMAND_STATUS.failed;
|
|
9277
|
+
}
|
|
9278
|
+
io2.print(sessionLineOf(answer, options.outGiven));
|
|
9279
|
+
return COMMAND_STATUS.done;
|
|
9280
|
+
} finally {
|
|
9281
|
+
dropWritingFile(io2, options.out, tag);
|
|
9282
|
+
release();
|
|
9283
|
+
}
|
|
9284
|
+
}
|
|
9285
|
+
var SESSION_ROW = Object.freeze({
|
|
9286
|
+
name: SESSION_SUBCOMMAND,
|
|
9287
|
+
summary: `Signs a test end user in and writes the session's cookie into a file curl -b reads, through one ${SIGN_IN_TEST_END_USER_ACTION} call under the credential ${DEPLOY_SUBCOMMAND} reads. It prints one line naming the hostname, the end user, the expiry, and the file, never the cookie's value. With no credential, it sends nothing and prints the two routes to one.`,
|
|
9288
|
+
options: SESSION_OPTIONS,
|
|
9289
|
+
credential: SESSION_CREDENTIAL,
|
|
9290
|
+
reads: SESSION_READS,
|
|
9291
|
+
statuses: {
|
|
9292
|
+
done: `the end user was signed in and the session's cookie written to the file ${SESSION_OPTION.out} names`,
|
|
9293
|
+
failed: "the platform refused the sign-in, the printed lines naming the platform's error and detail, or the end user was signed in and the cookie jar could not be written, so the session's cookie was not kept",
|
|
9294
|
+
unknown: "the sign-in call drew no answer, was redirected, failed, or drew an answer that is neither a refusal nor a session, and nothing was written",
|
|
9295
|
+
nothing_started: `nothing was sent, or nothing was written: an option, the file ${SESSION_OPTION.out} names, or the credential was refused, no credential was read and the two routes to one were printed, the platform was too old, or the jar's folder admitted no write`
|
|
9296
|
+
},
|
|
9297
|
+
note: [
|
|
9298
|
+
`A session line is the one your tool's ${SIGN_IN_TEST_END_USER_ACTION} call answers where the platform serves that action, and it carries no credential. Run it once: it signs the test end user in and writes the session's cookie into the file ${SESSION_OPTION.out} names, in the cookie file form curl -b reads, so the session's value never passes through your tool. Requests then carry the cookie with curl -b and that path.`,
|
|
9299
|
+
`${SESSION_SUBCOMMAND} writes the cookie jar under another name beside it, owner-only, and renames it into place, and it removes that file where the run ends before the rename. It writes over a file that stands only where the line names ${SESSION_OPTION.force}. It refuses a path inside the folder the command runs in where that folder holds package.json, since a deploy from it would upload the jar.`
|
|
9300
|
+
],
|
|
9301
|
+
afterOwnError: `whether the end user was signed in is unknown, and a file ${SESSION_OPTION.out} names may now stand, so run the line again, adding ${SESSION_OPTION.force} where it does`,
|
|
9302
|
+
run: session
|
|
9303
|
+
});
|
|
9304
|
+
|
|
8976
9305
|
// src/turnzero_cloud_command/token.ts
|
|
8977
9306
|
var TOKEN_OPTIONS = Object.freeze([
|
|
8978
9307
|
{
|
|
@@ -9050,9 +9379,9 @@ import { chmodSync, closeSync, openSync, readFileSync as readFileSync5, realpath
|
|
|
9050
9379
|
import { mkdirSync, readdirSync as readdirSync2, renameSync, rmSync, statSync as statSync4, write as writeAt } from "node:fs";
|
|
9051
9380
|
import { createServer } from "node:http";
|
|
9052
9381
|
import { constants as constants2 } from "node:os";
|
|
9053
|
-
import { sep as
|
|
9382
|
+
import { sep as sep5 } from "node:path";
|
|
9054
9383
|
import { ReadStream, isatty } from "node:tty";
|
|
9055
|
-
var listenLoopback = (answer) => new Promise((
|
|
9384
|
+
var listenLoopback = (answer) => new Promise((resolve13) => {
|
|
9056
9385
|
const server = createServer((request, response) => {
|
|
9057
9386
|
const url = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
9058
9387
|
const query = {};
|
|
@@ -9069,16 +9398,16 @@ var listenLoopback = (answer) => new Promise((resolve12) => {
|
|
|
9069
9398
|
}
|
|
9070
9399
|
);
|
|
9071
9400
|
});
|
|
9072
|
-
server.once("error", () =>
|
|
9401
|
+
server.once("error", () => resolve13(null));
|
|
9073
9402
|
server.listen(0, "127.0.0.1", () => {
|
|
9074
9403
|
const address = server.address();
|
|
9075
9404
|
if (address === null || typeof address === "string") {
|
|
9076
9405
|
server.close();
|
|
9077
|
-
|
|
9406
|
+
resolve13(null);
|
|
9078
9407
|
return;
|
|
9079
9408
|
}
|
|
9080
9409
|
let closed = false;
|
|
9081
|
-
|
|
9410
|
+
resolve13({
|
|
9082
9411
|
port: address.port,
|
|
9083
9412
|
close: () => {
|
|
9084
9413
|
if (closed) return;
|
|
@@ -9090,7 +9419,7 @@ var listenLoopback = (answer) => new Promise((resolve12) => {
|
|
|
9090
9419
|
});
|
|
9091
9420
|
});
|
|
9092
9421
|
});
|
|
9093
|
-
var ranThroughNpxOf = (entry) => entry !== void 0 && entry.includes(`${
|
|
9422
|
+
var ranThroughNpxOf = (entry) => entry !== void 0 && entry.includes(`${sep5}_npx${sep5}`);
|
|
9094
9423
|
var TERMINAL = process.platform === "win32" ? { input: "\\\\.\\CONIN$", output: "\\\\.\\CONOUT$", inputFlags: "r+" } : { input: "/dev/tty", output: "/dev/tty", inputFlags: "r" };
|
|
9095
9424
|
var terminalFor = (outputIsTerminal, open) => outputIsTerminal ? open() : null;
|
|
9096
9425
|
var openTerminal = () => {
|
|
@@ -9171,7 +9500,7 @@ var holdSignals = (signals, inputIsTerminal, passesOn, passOn, onInterrupt) => {
|
|
|
9171
9500
|
for (const [signal, handler] of held) signals.off(signal, handler);
|
|
9172
9501
|
};
|
|
9173
9502
|
};
|
|
9174
|
-
var childRunnerOf = (signals, inputIsTerminal, passesOn = SIGNALS_PASS_ON, groups = CHILD_GROUPS) => (run) => new Promise((
|
|
9503
|
+
var childRunnerOf = (signals, inputIsTerminal, passesOn = SIGNALS_PASS_ON, groups = CHILD_GROUPS) => (run) => new Promise((resolve13) => {
|
|
9175
9504
|
const output = run.output;
|
|
9176
9505
|
let interrupted = false;
|
|
9177
9506
|
const group = run.foreground === true && passesOn && groups;
|
|
@@ -9188,8 +9517,8 @@ var childRunnerOf = (signals, inputIsTerminal, passesOn = SIGNALS_PASS_ON, group
|
|
|
9188
9517
|
child.stdout?.setEncoding("utf8").on("data", (chunk) => output(chunk, "stdout"));
|
|
9189
9518
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => output(chunk, "stderr"));
|
|
9190
9519
|
}
|
|
9191
|
-
child.once("error", (error) =>
|
|
9192
|
-
child.once("close", (status, signal) =>
|
|
9520
|
+
child.once("error", (error) => resolve13({ status: null, signal: null, error: startErrorOf(error) }));
|
|
9521
|
+
child.once("close", (status, signal) => resolve13({ status, signal, error: null, ...interrupted ? { interrupted: true } : {} }));
|
|
9193
9522
|
if (run.foreground) {
|
|
9194
9523
|
const release = holdSignals(
|
|
9195
9524
|
signals,
|
|
@@ -9214,7 +9543,7 @@ var childRunnerOf = (signals, inputIsTerminal, passesOn = SIGNALS_PASS_ON, group
|
|
|
9214
9543
|
child.once("close", release);
|
|
9215
9544
|
}
|
|
9216
9545
|
} catch (error) {
|
|
9217
|
-
|
|
9546
|
+
resolve13({ status: null, signal: null, error: startErrorOf(error) });
|
|
9218
9547
|
}
|
|
9219
9548
|
});
|
|
9220
9549
|
var runChild = childRunnerOf(process, () => isatty(0));
|
|
@@ -9259,7 +9588,7 @@ var processIo = () => ({
|
|
|
9259
9588
|
// hold a subcommand lets go after a pause has seen it (scaffold.ts;
|
|
9260
9589
|
// PLD-L0-103). A bare timer of no length ends before the loop polls, and
|
|
9261
9590
|
// the signal would be dropped with the hold.
|
|
9262
|
-
sleep: (ms) => new Promise((
|
|
9591
|
+
sleep: (ms) => new Promise((resolve13) => setTimeout(() => setImmediate(resolve13), ms)),
|
|
9263
9592
|
schedule: (ms, fire) => {
|
|
9264
9593
|
const timer = setTimeout(fire, ms);
|
|
9265
9594
|
return () => clearTimeout(timer);
|
|
@@ -9291,10 +9620,10 @@ var processIo = () => ({
|
|
|
9291
9620
|
`);
|
|
9292
9621
|
}
|
|
9293
9622
|
});
|
|
9294
|
-
var writeWhole = (fd2, chunk) => new Promise((
|
|
9623
|
+
var writeWhole = (fd2, chunk) => new Promise((resolve13, reject) => {
|
|
9295
9624
|
const from = (offset) => {
|
|
9296
9625
|
if (offset >= chunk.length) {
|
|
9297
|
-
|
|
9626
|
+
resolve13();
|
|
9298
9627
|
return;
|
|
9299
9628
|
}
|
|
9300
9629
|
writeAt(fd2, chunk, offset, chunk.length - offset, null, (error, written) => error ? reject(error) : from(offset + written));
|
|
@@ -9374,7 +9703,8 @@ var SUBCOMMANDS = Object.freeze([
|
|
|
9374
9703
|
LOGOUT_ROW,
|
|
9375
9704
|
TOKEN_ROW,
|
|
9376
9705
|
LOGIN_ROW,
|
|
9377
|
-
WHOAMI_ROW
|
|
9706
|
+
WHOAMI_ROW,
|
|
9707
|
+
SESSION_ROW
|
|
9378
9708
|
]);
|
|
9379
9709
|
var SERVED = LINE_RUNNER;
|
|
9380
9710
|
var NPX_FORM_SENTENCE = `Where the command is not installed, install it with ${installLineOf(DEFAULT_COMMAND_ORIGIN)}. A person or a job may instead run the same line through npx: write ${commandRunnerOf("latest")} for ${LINE_RUNNER}. A tool where the command is not installed uses deploy_files within its caps, or the deploy skill's setup steps, never the npx line.`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turnzero",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.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",
|