turnzero 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/turnzero-cloud.mjs +28 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ To install it yourself, run `npm install -g turnzero`. It needs Node.js 24 or la
|
|
|
33
33
|
- `tzmesh register` registers a tzMesh standing agent and stores its program secret in your account's secret store or a local file, without printing it.
|
|
34
34
|
- `run` starts your application on your computer with that file's settings. With `client/` and a numeric port it also serves the client files. Add `--sign-in <hostname>` to use browser sign-in locally.
|
|
35
35
|
- `scaffold` writes a new service into your project from a template, installs it, and runs its tests.
|
|
36
|
-
- `login`
|
|
36
|
+
- `login` signs in the Turn Zero CLI on this computer through your browser, `whoami` names the account its token acts as, and `logout` revokes its token.
|
|
37
37
|
- `authorize` gives this computer a token through your signed-in tool, which then tells you it did.
|
|
38
38
|
- `token request` and `token claim` get a token for another computer or for an unattended run.
|
|
39
39
|
- `session` signs a test end user of your application in and writes the session's cookie into a file that curl reads.
|
package/dist/turnzero-cloud.mjs
CHANGED
|
@@ -1067,7 +1067,7 @@ var fileReadSentence = (origin) => `The credential this run read was this comput
|
|
|
1067
1067
|
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}.`;
|
|
1068
1068
|
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 command. It sets ${ORIGIN_VARIABLE} to the address that minted the token if that is not ${DEFAULT_COMMAND_ORIGIN}, then runs the command again. Such a tool cannot use ${MINT_TOKEN_ACTION}, which works only under a sign-in. A tool sets ${MINTED_TOKEN_VARIABLE} for one command at a time and unsets it once that command ends.`;
|
|
1069
1069
|
var authorizeRouteSentence = (origin) => `${AUTHORIZE_SUBCOMMAND}${originWordsOf(origin)} run with nothing piped prints the ${MINT_TOKEN_ACTION} call a signed-in tool makes to authorize this computer.`;
|
|
1070
|
-
var LOGIN_ROUTE_OPENER = "A person
|
|
1070
|
+
var LOGIN_ROUTE_OPENER = "A person signs in on this computer once, in a terminal of their own, and its token then stays on this computer for 30 days:";
|
|
1071
1071
|
var DEPLOY_ROUTES_SENTENCE = "For regular use, prefer this command wherever your tool can install and run it and reach the platform. A first simple test within the caps of your tool's tzcloud_deploy_files call may deploy through tzcloud_deploy_files, as may any folder within them where this command cannot work. Past the caps, where your tool may not install or run this command, upload a zip with tzcloud_mint_upload_grant, then name it as tzcloud_deploy's artifact.";
|
|
1072
1072
|
var deviceRouteSentence = (deviceLine) => `An AI tool that cannot authorize this computer through a signed-in Turn Zero connection, with no terminal for a person, runs ${deviceLine} and sends the person its link.`;
|
|
1073
1073
|
var loginRouteLines = (io2, origin, deployFiles = true) => [
|
|
@@ -1186,7 +1186,7 @@ var firstActNamed = (kind, origin = DEFAULT_COMMAND_ORIGIN) => kind === "tzmesh"
|
|
|
1186
1186
|
`${TZMESH_SUBCOMMAND} ${TZMESH_REGISTER_ACT}`
|
|
1187
1187
|
) : `${kind === "authorized computer" ? AUTHORIZE_SUBCOMMAND : `${TOKEN_SUBCOMMAND} ${TOKEN_REQUEST_ACT}`}${originWordsOf(origin)}`;
|
|
1188
1188
|
var noConnectionLine = (origin) => `Where your tool holds no connection signed in to ${origin}, ${MINT_TOKEN_ACTION} is not among its tools: a person connects the tool and signs in, in a browser, before the call.`;
|
|
1189
|
-
var loginWaySentence = (loginLines) => `A person can instead
|
|
1189
|
+
var loginWaySentence = (loginLines) => `A person can instead sign in on this computer themselves, in a terminal of their own, by running ${loginLines.join(", then ")}, and approving the sign-in in the browser.`;
|
|
1190
1190
|
var AUTHORIZE_HAND_OVER_SENTENCE = "If your tool's own safety check refuses the answered line, a deploy can still go through a route that does not require the turnzero command. That is tzcloud_deploy_files within its caps or, past them where your tool can zip and upload the folder, tzcloud_mint_upload_grant followed by tzcloud_deploy naming the uploaded zip as artifact. Tell the person which route you take and why. Only where no such route serves, give the person that exact line to run once in their own terminal, never a rewritten one.";
|
|
1191
1191
|
var TZMESH_HAND_OVER_SENTENCE = "If your tool's own safety check refuses the answered line, give the person that exact line to run once in their own terminal, never a rewritten one.";
|
|
1192
1192
|
var OPTIONS_LINE_HEAD = "options: ";
|
|
@@ -1350,7 +1350,7 @@ var mintedTokenOf = (answer, kind = "authorized computer") => {
|
|
|
1350
1350
|
authorizedComputer: view[AUTHORIZED_COMPUTER_MEMBER] === true
|
|
1351
1351
|
};
|
|
1352
1352
|
};
|
|
1353
|
-
var mintAgainSentence = (kind, origin = DEFAULT_COMMAND_ORIGIN, login2 = null) => login2 !== null ? `Run ${login2} again to
|
|
1353
|
+
var mintAgainSentence = (kind, origin = DEFAULT_COMMAND_ORIGIN, login2 = null) => login2 !== null ? `Run ${login2} again to sign in on this computer.` : `Call ${MINT_TOKEN_ACTION} from your tool again with the challenge ${firstActNamed(kind, origin)} prints, and run the line it answers.`;
|
|
1354
1354
|
var exchangeUnknownSentence = (kind, origin = DEFAULT_COMMAND_ORIGIN, login2 = null) => `Whether a token was minted is unknown: list_tokens from your tool shows each token by its label, and revoke_token ends one nothing holds. ${mintAgainSentence(kind, origin, login2)}`;
|
|
1355
1355
|
var noPendingSentence = (origin, kind, presented = "code") => `No request is pending for ${origin} on this computer, so ${presented === "mint" ? "nothing was sent: the verifier the mint needs" : "the token code was not presented and nothing was sent: the verifier it needs"} is kept only on the computer whose ${firstActNamed(kind)} printed its challenge. Run ${firstActNamed(kind, origin)} on this computer, call ${MINT_TOKEN_ACTION} from your tool with the challenge it prints, and run the line it answers.`;
|
|
1356
1356
|
var otherLivingRequestOf = (pending, origin, now) => {
|
|
@@ -2140,6 +2140,8 @@ var ABSENT_FILE_ERROR = "no_such_file";
|
|
|
2140
2140
|
var freshExportLine = (absent) => `${absent === 1 ? "A file the manifest names was" : `${absent} files the manifest names were`} answered as an absent name and ${absent === 1 ? "was" : "were"} not read. A fresh export has the application's files as they now stand: call tzcloud_request_export from your tool, then ${toolNameOf(DOWNLOAD_GRANT_ACTION)} with download: true once tzcloud_read_export answers it completed.`;
|
|
2141
2141
|
var runAgainLine = (again) => `Run the line again before its grant expires, or take a fresh one. ${again} It skips the files on disk and reads again each file whose read failed.`;
|
|
2142
2142
|
var MANIFEST_READ = "The manifest read";
|
|
2143
|
+
var MANIFEST_READ_ACT = "manifest read";
|
|
2144
|
+
var FILE_READ_ACT = "file read";
|
|
2143
2145
|
var fileReadOf = (natural) => `The read of ${shownWhole(natural)}`;
|
|
2144
2146
|
var APPLICATION_MARK = "package.json";
|
|
2145
2147
|
async function exportDownload(call, io2) {
|
|
@@ -2245,7 +2247,7 @@ async function exportDownload(call, io2) {
|
|
|
2245
2247
|
let manifestRead;
|
|
2246
2248
|
for (; ; ) {
|
|
2247
2249
|
if (pause !== null) await pause;
|
|
2248
|
-
manifestRead = await get(
|
|
2250
|
+
manifestRead = await get(MANIFEST_READ_ACT, exportArea, exportFileName(options.export, EXPORT_MANIFEST_FILE), MANIFEST_READ);
|
|
2249
2251
|
if (manifestRead instanceof Response || manifestRead.kind !== "capped") break;
|
|
2250
2252
|
if (!waitOutCap()) {
|
|
2251
2253
|
manifestRead = { kind: "ended", lines: cappedEnd };
|
|
@@ -2330,7 +2332,7 @@ async function exportDownload(call, io2) {
|
|
|
2330
2332
|
const entry = entries[ordinal];
|
|
2331
2333
|
const target = targetOf(locals[ordinal]);
|
|
2332
2334
|
if (target === null || !addressable(entry)) return { kind: "failed", reason: "its name holds a . or .. segment, which no address carries", unread: true };
|
|
2333
|
-
const answered = await get(
|
|
2335
|
+
const answered = await get(FILE_READ_ACT, entry.area, entry.name, fileReadOf(entry.natural));
|
|
2334
2336
|
if (!(answered instanceof Response)) return answered;
|
|
2335
2337
|
const length = lengthOf(answered);
|
|
2336
2338
|
if (length === null) {
|
|
@@ -6943,22 +6945,22 @@ var openBrowser = async (io2, address) => {
|
|
|
6943
6945
|
cancel();
|
|
6944
6946
|
return ended === "waiting" || ended.error === null && ended.status === 0;
|
|
6945
6947
|
};
|
|
6946
|
-
var addressLeadSentence = (origin) => `To
|
|
6947
|
-
var reusedLoginRequestSentence = (origin, expiresAt) => `A
|
|
6948
|
-
var replacedLoginRequestSentence = (origin, expiredAt) => `The
|
|
6948
|
+
var addressLeadSentence = (origin) => `To sign in to the Turn Zero CLI on this computer at ${origin}, approve the sign-in on this page:`;
|
|
6949
|
+
var reusedLoginRequestSentence = (origin, expiresAt) => `A sign-in request for ${origin} is pending until ${expiresAt}, so this sign-in uses it again and makes no new verifier.`;
|
|
6950
|
+
var replacedLoginRequestSentence = (origin, expiredAt) => `The sign-in request pending for ${origin} expired at ${expiredAt}, so a new one replaces it, and the address below carries its challenge. An approval or a line made against the old challenge is refused at its exchange.`;
|
|
6949
6951
|
var minutesWords = (minutes) => `${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
6950
6952
|
var minutesLeftOf = (expiresAt, now) => Math.max(1, Math.ceil((Date.parse(expiresAt) - now.getTime()) / 6e4));
|
|
6951
6953
|
var openingSentence = (minutes) => `Opening it in your default browser. If you use another one, copy the address above into it. This command waits up to ${minutesWords(minutes)} for the approval.`;
|
|
6952
|
-
var noBrowserSentence = "Open the address in a browser on any computer, approve the
|
|
6954
|
+
var noBrowserSentence = "Open the address in a browser on any computer, approve the sign-in, and paste here the code the page shows.";
|
|
6953
6955
|
var unboundSentence = "No port on this computer could be bound for the browser to answer, so paste here the code the page shows once you approve.";
|
|
6954
|
-
var unopenedSentence = "The browser could not be opened here: open the address yourself, approve the
|
|
6955
|
-
var noSendSentence = (seconds) => `No answer has come from the browser in ${seconds} seconds. Where the page shows a code, paste it here; the browser's answer still completes the
|
|
6956
|
+
var unopenedSentence = "The browser could not be opened here: open the address yourself, approve the sign-in, and paste here the code the page shows if it shows one.";
|
|
6957
|
+
var noSendSentence = (seconds) => `No answer has come from the browser in ${seconds} seconds. Where the page shows a code, paste it here; the browser's answer still completes the sign-in.`;
|
|
6956
6958
|
var noTerminalSentence2 = "This run has no terminal to paste a code into, so it waits for the browser's answer alone.";
|
|
6957
|
-
var toolRouteSentence = (authorizeLine) => `This line waits for a person to approve the
|
|
6959
|
+
var toolRouteSentence = (authorizeLine) => `This line waits for a person to approve the sign-in in a browser. A tool signed in to Turn Zero needs no browser: stop this line, run ${authorizeLine}, and make the mint_token call it prints.`;
|
|
6958
6960
|
var noWayInSentence = (line, deviceLine, minutes = PENDING_REQUEST_MS / 6e4) => `This run has no terminal to paste a code into and no port for the browser to answer, so nothing was minted and the request stays pending for ${minutesWords(minutes)}. Run ${line} in a terminal of your own. ${deviceRouteSentence(deviceLine)}`;
|
|
6959
|
-
var endedSentence = (minutes, line) => `No approval arrived within ${minutesWords(minutes)}, so nothing was minted and nothing was changed. Run ${line} again to
|
|
6960
|
-
var stoppedSentence = (line) => `The code prompt was stopped, so nothing was minted. Run ${line} again to
|
|
6961
|
-
var notCodeSentence = (line) => `What was pasted is not a token code, so nothing was sent and nothing was minted. Run ${line} again to
|
|
6961
|
+
var endedSentence = (minutes, line) => `No approval arrived within ${minutesWords(minutes)}, so nothing was minted and nothing was changed. Run ${line} again to sign in on this computer.`;
|
|
6962
|
+
var stoppedSentence = (line) => `The code prompt was stopped, so nothing was minted. Run ${line} again to sign in on this computer.`;
|
|
6963
|
+
var notCodeSentence = (line) => `What was pasted is not a token code, so nothing was sent and nothing was minted. Run ${line} again to sign in on this computer.`;
|
|
6962
6964
|
var LINK_LINE_HEAD = "link: ";
|
|
6963
6965
|
var devicePersonSentence = (label, expiresAt) => `Open this link on any device, sign in to Turn Zero, and approve the sign-in for the computer ${JSON.stringify(label)} before ${expiresAt}; the turnzero command there finishes on its own once you approve.`;
|
|
6964
6966
|
var deviceEndedSentence = (expiresAt, line) => `No approval arrived before the request ended at ${expiresAt}, so nothing was minted and nothing was changed. Run ${line} again for a new link.`;
|
|
@@ -6967,7 +6969,9 @@ var deviceStoppedSentence = (expiresAt, line) => `The run was stopped while it w
|
|
|
6967
6969
|
var CODE_PROMPT_WORDS = "Code (not shown), then Enter: ";
|
|
6968
6970
|
var LINE_PIECES = /[^\r\n]*(?:\r\n|\r|\n)|[^\r\n]+/g;
|
|
6969
6971
|
var escaped = (text) => text.replace(/[&<>"']/g, (character) => `&#${character.charCodeAt(0)};`);
|
|
6970
|
-
var
|
|
6972
|
+
var BROWSER_PAGE_STYLE = 'body{margin:0;min-height:100vh;display:grid;place-items:center;background:#fafbf7;color:#102b23;font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}main{box-sizing:border-box;width:calc(100% - 1.5rem);max-width:536px;padding:32px;border:1px solid #d7e1d7;border-radius:20px;background:#fff;box-shadow:0 12px 36px #102b230c}.brand{display:flex;align-items:center;gap:10px;margin:0 0 30px;color:#205942;font-weight:600}.brand img{width:33px;height:33px;object-fit:contain}.brand span{font-size:19px;letter-spacing:-0.35px}h1{margin:0 0 10px;font-size:28px;line-height:1.2;font-weight:650;letter-spacing:-0.8px}p{margin:0;font-size:15px;line-height:1.55;color:#4a5b53;overflow-wrap:anywhere}';
|
|
6973
|
+
var browserPageMarkOf = (origin) => `${origin}/assets/favicon.svg`;
|
|
6974
|
+
var browserPageOf = (title, sentence, origin) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escaped(title)}</title><style>${BROWSER_PAGE_STYLE}</style></head><body><main><div class="brand"><img alt="" width="33" height="33" referrerpolicy="no-referrer" onerror="this.remove()" src="${escaped(browserPageMarkOf(origin))}"><span>Turn Zero</span></div><h1>${escaped(title)}</h1><p>${escaped(sentence)}</p></main></body></html>`;
|
|
6971
6975
|
var answerHeaders = (origin, html) => ({
|
|
6972
6976
|
"access-control-allow-origin": origin,
|
|
6973
6977
|
"access-control-allow-private-network": "true",
|
|
@@ -6978,17 +6982,17 @@ var stateOnlyAnswer = (origin) => ({ status: 204, headers: answerHeaders(origin,
|
|
|
6978
6982
|
var outcomeAnswer = (origin, authorized) => ({
|
|
6979
6983
|
status: 200,
|
|
6980
6984
|
headers: answerHeaders(origin, true),
|
|
6981
|
-
body: authorized ? browserPageOf("
|
|
6985
|
+
body: authorized ? browserPageOf("Signed in", `The Turn Zero CLI on this computer is signed in at ${origin}. Return to your AI tool and continue the setup or deployment you started. You can close this tab.`, origin) : browserPageOf("The sign-in did not complete", "The Turn Zero CLI on this computer did not complete the sign-in; its terminal says why and what to run next. You can close this tab.", origin)
|
|
6982
6986
|
});
|
|
6983
6987
|
var wrongStateAnswer = (origin) => ({
|
|
6984
6988
|
status: 400,
|
|
6985
6989
|
headers: answerHeaders(origin, true),
|
|
6986
|
-
body: browserPageOf("Not this
|
|
6990
|
+
body: browserPageOf("Not this sign-in", "This address is not the sign-in the Turn Zero CLI on this computer is waiting for. Nothing was changed.", origin)
|
|
6987
6991
|
});
|
|
6988
6992
|
var endedAnswer = (origin) => ({
|
|
6989
6993
|
status: 410,
|
|
6990
6994
|
headers: answerHeaders(origin, true),
|
|
6991
|
-
body: browserPageOf("This
|
|
6995
|
+
body: browserPageOf("This sign-in has ended", "The Turn Zero CLI on this computer is no longer waiting for this sign-in. Run turnzero login again.", origin)
|
|
6992
6996
|
});
|
|
6993
6997
|
var notFoundAnswer = (origin) => ({ status: 404, headers: answerHeaders(origin, false), body: "" });
|
|
6994
6998
|
var loopbackHandlerOf = (origin, state, arrive) => {
|
|
@@ -7173,7 +7177,7 @@ var LOGIN_OPTIONS = Object.freeze([
|
|
|
7173
7177
|
name: LOGIN_OPTION.origin,
|
|
7174
7178
|
value: "<origin>",
|
|
7175
7179
|
required: false,
|
|
7176
|
-
description: `The platform's address this computer
|
|
7180
|
+
description: `The platform's address this computer signs in at. If omitted, the address in ${ORIGIN_VARIABLE}, or ${DEFAULT_COMMAND_ORIGIN} if that is unset. A ${LOGIN_OPTION.origin} given in the command must be ${DEFAULT_COMMAND_ORIGIN}, ${DEVELOPMENT_COMMAND_ORIGIN}, or the address in ${ORIGIN_VARIABLE}.`
|
|
7177
7181
|
},
|
|
7178
7182
|
{
|
|
7179
7183
|
name: LOGIN_OPTION.noBrowser,
|
|
@@ -7190,16 +7194,16 @@ var LOGIN_OPTIONS = Object.freeze([
|
|
|
7190
7194
|
description: `For an AI tool that cannot authorize this computer through a signed-in Turn Zero connection, with no terminal for a person. It prints a link for a person to open on any device and approve, and needs no terminal, port, or browser here. It checks every ${DEVICE_POLL_SECONDS} seconds for up to an hour, and keeps the token once the person approves. Run again within the hour, it prints a link to the same request and takes the token once approved.`
|
|
7191
7195
|
}
|
|
7192
7196
|
]);
|
|
7193
|
-
var LOGIN_AFTER_OWN_ERROR = "the
|
|
7197
|
+
var LOGIN_AFTER_OWN_ERROR = "the sign-in may not have completed. Where a token was minted, list_tokens from your tool shows it by its label, and revoke_token ends it. Run login again to sign in on this computer";
|
|
7194
7198
|
var LOGIN_ROW = Object.freeze({
|
|
7195
7199
|
name: LOGIN_SUBCOMMAND,
|
|
7196
|
-
summary: `
|
|
7200
|
+
summary: `Signs the Turn Zero CLI in on this computer at the platform's address. It opens the platform's sign-in page for the CLI (${COMPUTER_LOGIN_PATH}) in your default browser, where you approve the sign-in. It keeps the token the platform mints in this computer's credential file. It then reads the account the token acts as, and prints authorized: with the address, the account's sign-in address (or its id if it has none), the token's label, and its expiry. If the browser cannot respond, it asks for the code the page shows. It prints no token, code, or verifier.`,
|
|
7197
7201
|
example: LOGIN_SUBCOMMAND,
|
|
7198
7202
|
options: LOGIN_OPTIONS,
|
|
7199
7203
|
credential: null,
|
|
7200
7204
|
reads: `nothing from standard input. With the browser, it waits for the page to respond on a port of this computer. With --no-browser, it reads the code the page shows from the terminal, hidden. With ${LOGIN_OPTION.device}, it asks the platform every ${DEVICE_POLL_SECONDS} seconds whether the link was approved. Once the token is kept, it reads the account through read_account under it`,
|
|
7201
7205
|
statuses: {
|
|
7202
|
-
done: "this computer
|
|
7206
|
+
done: "this computer signed in",
|
|
7203
7207
|
failed: "a token was minted and not kept, or the token this computer had before was not revoked: the printed lines name revoke_token",
|
|
7204
7208
|
unknown: "the exchange got no response, or a response that is not a refusal: list_tokens shows whether a token was minted",
|
|
7205
7209
|
nothing_started: "nothing was minted. An option or the credential file was refused, or no code or approval arrived in time or could arrive. Or the prompt was stopped, or given what is not a token code. Or the platform was too old, or the exchange was refused"
|
|
@@ -8000,7 +8004,7 @@ var PROVISION_ROW = Object.freeze({
|
|
|
8000
8004
|
nothing_started: "nothing was re-minted"
|
|
8001
8005
|
},
|
|
8002
8006
|
note: [
|
|
8003
|
-
`Run ${PROVISION_BARE_LINE_WORDS} from the folder the application runs in. With no token for the address, it prints a challenge and the tzcloud_submit_manifest call with local_run and ${CODE_CHALLENGE_MEMBER}. Your tool makes that call and runs the command it returns once, as given, before its grant expires. That command presents the grant with this computer's verifier. On a computer that is
|
|
8007
|
+
`Run ${PROVISION_BARE_LINE_WORDS} from the folder the application runs in. With no token for the address, it prints a challenge and the tzcloud_submit_manifest call with local_run and ${CODE_CHALLENGE_MEMBER}. Your tool makes that call and runs the command it returns once, as given, before its grant expires. That command presents the grant with this computer's verifier. On a computer that is signed in, the plain command re-mints at once.`,
|
|
8004
8008
|
`Each run revokes the development credentials the application had before it. A deployed development environment keeps the revoked values until it restarts, so the command's last re-mint restarts it if development serves a version. Its redeploy takes both re-minted credentials from custody, the connection string and the platform credential, and does not count toward the day's deploys. If a deploy of development is still in flight, the re-mint's response gives tzcloud_restart_application on development, and your tool calls it once that deploy ends. ${PROVISION_SUBCOMMAND} writes no ${PORT_SETTING}: ${RUN_SUBCOMMAND} sets it from ${RUN_OPTION.port}.`
|
|
8005
8009
|
],
|
|
8006
8010
|
afterOwnError: provisionAfterOwnError,
|