turnzero 0.33.1 → 0.34.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.
@@ -220,6 +220,7 @@ var provisionBareLineOf = (call) => commandLineOf(call.origin, PROVISION_SUBCOMM
220
220
  ...call.envFile !== void 0 ? { quoted: [[PROVISION_OPTION.envFile, call.envFile]] } : {}
221
221
  });
222
222
  var DEPLOY_ROTATE_FLAG = "--rotate-database-credential";
223
+ var DEPLOY_JSON_FLAG = "--json";
223
224
  var DEPLOY_PREPARED_LINE_HEAD = "prepared: ";
224
225
  var CREDENTIAL_LOOKUP_BUSY = "credential_lookup_busy";
225
226
  var RUN_SUBCOMMAND = "run";
@@ -901,8 +902,7 @@ var readStandardInputEnded = (input, boundMs = GRANT_READ_BOUND_MS) => new Promi
901
902
  });
902
903
  var unendedReadingOf = (read2) => read2.ending === "error" ? "error" : read2.ending === "full" ? "full" : read2.ending === "bound" && read2.bytes > 0 ? "bound" : null;
903
904
  var unendedReadSentence = (what, reading, closing, wayOn) => `${reading === "error" ? `The command could not read the ${what} piped to it to its end` : reading === "full" ? `The input piped to the command was longer than any ${what} can be` : `The ${what} piped to the command did not end within ${GRANT_READ_BOUND_MS / 1e3} seconds`}, so ${closing} ${wayOn}`;
904
- var NOTHING_PIPED_WAY_ON = "or, to run with nothing piped, close standard input or redirect it from an empty source";
905
- var pipeWholeWayOn = (aWhat, call, nothingPiped = false) => `Pipe ${aWhat} whole and end the input, as the line ${call} answers does${nothingPiped ? `, ${NOTHING_PIPED_WAY_ON}` : ""}.`;
905
+ var pipeWholeWayOn = (aWhat, call) => `Pipe ${aWhat} whole and end the input, as the line ${call} answers does.`;
906
906
 
907
907
  // src/turnzero_cloud_command/line_form.ts
908
908
  var onWindows = (io2) => io2.windows ?? process.platform === "win32";
@@ -1068,6 +1068,7 @@ var guarded = (io2) => {
1068
1068
  let valuePrinted = false;
1069
1069
  return {
1070
1070
  ...io2,
1071
+ jsonResult: { printed: false, lines: [] },
1071
1072
  print: (line) => {
1072
1073
  io2.print(guardedOf(line));
1073
1074
  },
@@ -1099,8 +1100,7 @@ var TOKEN_CODE_UNREAD = `${TOKEN_CODE_UNREAD_FIRST} ${TOKEN_CODE_RUN_AGAIN}`;
1099
1100
  var tokenCodeFloorSentence = (version, found) => `${TURNZERO_CLOUD_PACKAGE} ${version} needs Node.js ${COMMAND_NODE_FLOOR} or later, and this machine runs Node.js ${found}, so nothing was read and nothing was started. A token code the line carried was not read: it is live until it expires, and it mints nothing without this computer's verifier. Install Node.js ${COMMAND_NODE_FLOOR} or later, then run the line again.`;
1100
1101
  var NOTHING_SENT = "Nothing was sent and nothing was started.";
1101
1102
  var MINT_UNPRESENTED = `Nothing was presented. ${TOKEN_CODE_RUN_AGAIN}`;
1102
- var namesMint = (argv2) => argv2[0] === AUTHORIZE_SUBCOMMAND && argv2.slice(1).some((argument) => argument === MINT_OPTION || argument.startsWith(`${MINT_OPTION}=`));
1103
- var carriesTokenCode = (argv2) => argv2[0] === AUTHORIZE_SUBCOMMAND && !namesMint(argv2) || argv2[0] === TOKEN_SUBCOMMAND && argv2[1] === TOKEN_CLAIM_ACT;
1103
+ var carriesTokenCode = (argv2) => argv2[0] === TOKEN_SUBCOMMAND && argv2[1] === TOKEN_CLAIM_ACT;
1104
1104
  var LABEL_FALLBACK = "authorized computer";
1105
1105
  var computerLabelOf = (host) => {
1106
1106
  let label = "";
@@ -1482,12 +1482,12 @@ async function kept(io2, place, tag, origin, minted, userAgent, say, named = fal
1482
1482
  say(standsSentence(`The token this computer held before for ${origin}, ${before.token_id},`, before.token_id, before.expires_at));
1483
1483
  return COMMAND_STATUS.failed;
1484
1484
  }
1485
- var unendedCodeRefusal = (reading, nothingPiped = false) => `${unendedReadSentence(AUTHORIZE_CREDENTIAL.grant, reading, "nothing was sent and nothing was changed.", pipeWholeWayOn(AUTHORIZE_CREDENTIAL.aGrant, AUTHORIZE_CREDENTIAL.call, nothingPiped))} ${TOKEN_CODE_UNREAD}`;
1486
- var emptyCodeRefusal = (kind, origin = DEFAULT_COMMAND_ORIGIN) => `The token code piped to the command was empty, so nothing was sent and nothing was changed. Pipe a token code whole, as the line the ${MINT_TOKEN_ACTION} call answers does, or ${kind === "authorized computer" ? `pipe nothing to run ${AUTHORIZE_SUBCOMMAND}'s first act` : `run ${firstActNamed(kind, origin)} for the first act`}, which prints the challenge. ${TOKEN_CODE_UNREAD}`;
1485
+ var unendedCodeRefusal = (reading) => `${unendedReadSentence(AUTHORIZE_CREDENTIAL.grant, reading, "nothing was sent and nothing was changed.", pipeWholeWayOn(AUTHORIZE_CREDENTIAL.aGrant, AUTHORIZE_CREDENTIAL.call))} ${TOKEN_CODE_UNREAD}`;
1486
+ var emptyCodeRefusal = (kind, origin = DEFAULT_COMMAND_ORIGIN) => `The token code piped to the command was empty, so nothing was sent and nothing was changed. Pipe a token code whole, as the line the ${MINT_TOKEN_ACTION} call answers does, or run ${firstActNamed(kind, origin)} for the first act, which prints the challenge. ${TOKEN_CODE_UNREAD}`;
1487
1487
  var readTokenCode = async (io2, kind, origin = DEFAULT_COMMAND_ORIGIN) => {
1488
1488
  const read2 = await readStandardInputEnded(io2.input, io2.grantBoundMs);
1489
1489
  const unended = unendedReadingOf(read2);
1490
- if (unended !== null) return { refusal: unendedCodeRefusal(unended, kind === "authorized computer" && read2.bytes === 0) };
1490
+ if (unended !== null) return { refusal: unendedCodeRefusal(unended) };
1491
1491
  const code = read2.text.trim();
1492
1492
  return code === "" && read2.bytes > 0 ? { refusal: emptyCodeRefusal(kind, origin) } : { code };
1493
1493
  };
@@ -1512,64 +1512,62 @@ var AUTHORIZE_CREDENTIAL = Object.freeze({
1512
1512
  grant: "token code",
1513
1513
  aGrant: "a token code",
1514
1514
  call: `the ${MINT_TOKEN_ACTION} call`,
1515
- mintedToken: false,
1516
- pipes: `if the command pipes one in, as the command from a ${MINT_TOKEN_ACTION} call that sets no completion does`,
1517
- bare: `with nothing piped and no ${MINT_OPTION}, it keeps a verifier and prints the challenge and the ${MINT_TOKEN_ACTION} call that authorize this computer. That call returns ${AUTHORIZE_SUBCOMMAND} ${MINT_OPTION} <id>, a command that contains no code: with it, ${AUTHORIZE_SUBCOMMAND} reads nothing on standard input and presents the verifier for that mint`
1515
+ mintedToken: false
1518
1516
  });
1517
+ var AUTHORIZE_READS = `standard input only to refuse a token code piped to it. With nothing piped and no ${MINT_OPTION}, it keeps a verifier and prints the challenge and the ${MINT_TOKEN_ACTION} call that authorize this computer. That call returns ${AUTHORIZE_SUBCOMMAND} ${MINT_OPTION} <id>, a command that contains no code: with it, ${AUTHORIZE_SUBCOMMAND} reads no standard input and presents the verifier for that mint`;
1518
+ var heldLinePlaceholderFor = (io2, origin) => printedLineOf(io2, commandLineOf(origin, AUTHORIZE_SUBCOMMAND, { options: [[MINT_OPTION, "<id>"]], ...origin !== DEFAULT_COMMAND_ORIGIN ? { origin } : {} }), origin);
1519
+ var pipedCodeRetiredSentence = (line) => `${AUTHORIZE_SUBCOMMAND} no longer reads a token code piped to it, so nothing was sent: run the command the ${MINT_TOKEN_ACTION} call returns, ${line}, as given.`;
1519
1520
  var AUTHORIZATION_ORIGIN_VARIABLE_LINE = `the platform's address the authorization reaches if the command has no ${DEPLOY_OPTION.origin}. It is the one address besides ${DEFAULT_COMMAND_ORIGIN} and ${DEVELOPMENT_COMMAND_ORIGIN} that ${DEPLOY_OPTION.origin} may give. It may be an address of this machine only if this variable is set to it.`;
1520
1521
  var CREDENTIAL_FILE_WORDS = `${CREDENTIAL_FILE_NAME} in a folder ${CREDENTIAL_FOLDER_NAME} under ${CONFIG_HOME_VARIABLE}, or under .config in your home folder if that is unset, readable by you alone. On Windows it is in a folder ${WINDOWS_CREDENTIAL_FOLDER_NAME} under your profile folder, ${PROFILE_VARIABLE}. A file under ${LOCAL_APP_DATA_VARIABLE} from an older version is moved there once`;
1521
1522
  var AUTHORIZE_AFTER_OWN_ERROR = "a token code the line carried may not have been presented. That code is live until it expires, and mints nothing without this computer's verifier. Where a token was minted, list_tokens from your tool shows it by its label, and revoke_token ends it";
1522
1523
  var MINT_AFTER_OWN_ERROR = "the verifier may not have been presented. Where a token was minted, list_tokens from your tool shows it by its label, and revoke_token ends it";
1524
+ var FIRST_ACT_AFTER_OWN_ERROR = "nothing was sent, so no token was minted";
1523
1525
  async function authorize(call, io2) {
1524
1526
  const mint = call.named[MINT_OPTION];
1525
1527
  if (mint !== void 0 && !MINT_ID_FORM.test(mint)) {
1526
1528
  io2.print(MINT_ID_REFUSAL);
1527
1529
  return COMMAND_STATUS.nothing_started;
1528
1530
  }
1529
- const unread = mint !== void 0 ? MINT_UNPRESENTED : TOKEN_CODE_UNREAD;
1531
+ const closing = mint !== void 0 ? ` ${MINT_UNPRESENTED}` : "";
1530
1532
  const origin = authorizationOriginOf(io2.env, call.named[DEPLOY_OPTION.origin], NOTHING_SENT);
1531
1533
  if ("refusal" in origin) {
1532
- io2.print(`${origin.refusal} ${unread}`);
1534
+ io2.print(`${origin.refusal}${closing}`);
1533
1535
  return COMMAND_STATUS.nothing_started;
1534
1536
  }
1535
1537
  const agent = userAgentOf(io2.env);
1536
1538
  if ("refusal" in agent) {
1537
- io2.print(`${agent.refusal} ${unread}`);
1539
+ io2.print(`${agent.refusal}${closing}`);
1538
1540
  return COMMAND_STATUS.nothing_started;
1539
1541
  }
1540
1542
  if (mint !== void 0) return completeAct(io2, origin.origin, "authorized computer", { mint }, agent.userAgent, io2.print);
1541
- const read2 = await readTokenCode(io2, "authorized computer", origin.origin);
1542
- if ("refusal" in read2) {
1543
- io2.print(read2.refusal);
1544
- return COMMAND_STATUS.nothing_started;
1545
- }
1546
- const { code } = read2;
1547
- if (code === "") return firstAct(io2, origin.origin, "authorized computer", io2.print);
1548
- if (!isTokenCode(code)) {
1549
- io2.print(notTokenCodeSentence(`Run ${firstActNamed("authorized computer", origin.origin)} with nothing piped to print the challenge, or run the line the ${MINT_TOKEN_ACTION} call answered as given.`));
1543
+ const read2 = await readStandardInputEnded(io2.input, io2.grantBoundMs);
1544
+ if (read2.bytes > 0) {
1545
+ const piped = read2.text.trim();
1546
+ if (isTokenCode(piped)) io2.hide(piped, REDACTED_TOKEN_CODE);
1547
+ io2.print(pipedCodeRetiredSentence(heldLinePlaceholderFor(io2, origin.origin)));
1550
1548
  return COMMAND_STATUS.nothing_started;
1551
1549
  }
1552
- io2.hide(code, REDACTED_TOKEN_CODE);
1553
- return completeAct(io2, origin.origin, "authorized computer", { code }, agent.userAgent, io2.print);
1550
+ return firstAct(io2, origin.origin, "authorized computer", io2.print);
1554
1551
  }
1555
1552
  var AUTHORIZE_ROW = Object.freeze({
1556
1553
  name: AUTHORIZE_SUBCOMMAND,
1557
- summary: `Authorizes this computer at the platform's address, in two steps around one ${MINT_TOKEN_ACTION} call your tool makes. Run with nothing piped, it keeps a verifier in this computer's credential file, and prints the challenge and the call. Run as the command the call returns, with ${MINT_OPTION} and no code, or with a piped token code, it presents the verifier. It then keeps the minted token in that file, and prints authorized:. It prints no token, code, or verifier.`,
1554
+ summary: `Authorizes this computer at the platform's address, in two steps around one ${MINT_TOKEN_ACTION} call your tool makes. Run without ${MINT_OPTION}, it keeps a verifier in this computer's credential file, and prints the challenge and the call. Run as the command the call returns, with ${MINT_OPTION} and no code, it presents the verifier. It then keeps the minted token in that file, and prints authorized:. It reads standard input only to refuse a token code piped to it. It prints no token, code, or verifier.`,
1558
1555
  example: AUTHORIZE_SUBCOMMAND,
1559
1556
  options: AUTHORIZE_OPTIONS,
1560
- credential: AUTHORIZE_CREDENTIAL,
1557
+ credential: null,
1558
+ reads: AUTHORIZE_READS,
1561
1559
  statuses: {
1562
1560
  done: "the challenge printed, or this computer authorized",
1563
1561
  failed: "a token was minted and not kept, or the token this computer had before was not revoked: the printed lines name revoke_token",
1564
1562
  unknown: "the exchange got no response, or a response that is not a refusal: list_tokens shows whether a token was minted",
1565
- nothing_started: `nothing was minted. An option, the mint's id, the credential file, or the token code was refused. Or no request was pending, the platform was too old, or the exchange was refused`
1563
+ nothing_started: `nothing was minted. An option, the mint's id, the credential file, or a token code piped to it was refused. Or no request was pending, the platform was too old, or the exchange was refused`
1566
1564
  },
1567
1565
  variables: [[ORIGIN_VARIABLE, AUTHORIZATION_ORIGIN_VARIABLE_LINE]],
1568
1566
  note: [
1569
- `Authorizing this computer takes three steps, once for each platform address. Your signed-in tool takes them 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 command it returns once, as given, on this computer. That command contains no code, and it keeps the token.`,
1567
+ `Authorizing this computer takes three steps, once for each platform address. Your signed-in tool takes them itself and then reports to you; login, run in a terminal of your own, is your own way. ${AUTHORIZE_SUBCOMMAND} run without ${MINT_OPTION} 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 command it returns once, as given, on this computer. That command contains no code, and it keeps the token.`,
1570
1568
  `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}.`
1571
1569
  ],
1572
- afterOwnError: (call) => call.named[MINT_OPTION] !== void 0 ? MINT_AFTER_OWN_ERROR : AUTHORIZE_AFTER_OWN_ERROR,
1570
+ afterOwnError: (call) => call.named[MINT_OPTION] !== void 0 ? MINT_AFTER_OWN_ERROR : FIRST_ACT_AFTER_OWN_ERROR,
1573
1571
  run: authorize
1574
1572
  });
1575
1573
 
@@ -3500,6 +3498,13 @@ var DEPLOY_OPTIONS = Object.freeze([
3500
3498
  flag: true,
3501
3499
  description: "Rotates production's database credential with this deploy, as given in the command a deploy call with rotate_database_credential returns."
3502
3500
  },
3501
+ {
3502
+ name: DEPLOY_JSON_FLAG,
3503
+ value: "",
3504
+ required: false,
3505
+ flag: true,
3506
+ description: "Prints the result as one JSON object on standard output when the run ends, and every other line on standard error: contract_version, status, outcome, application, environment, upload, version, address, failed_step, and error. The exit status is the same."
3507
+ },
3503
3508
  {
3504
3509
  name: DEPLOY_OPTION.env,
3505
3510
  value: "<environment>",
@@ -3857,6 +3862,9 @@ var UPLOADING_LINE_HEAD = "uploading: ";
3857
3862
  var UPLOADED_LINE_HEAD = "uploaded: ";
3858
3863
  var STARTED_LINE_HEAD = "started: ";
3859
3864
  var WAITING_LINE_HEAD = "waiting: ";
3865
+ var ADDRESS_LINE_HEAD = "address: ";
3866
+ var ADDRESS_HOSTNAME = /^(?=.{1,253}$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;
3867
+ var addressLineOf = (hostname3) => typeof hostname3 === "string" && ADDRESS_HOSTNAME.test(hostname3) ? `${ADDRESS_LINE_HEAD}https://${hostname3}/` : null;
3860
3868
  var WAITING_AFTER_SECONDS = 25;
3861
3869
  var waitingLineOf = (seconds) => `${WAITING_LINE_HEAD}${seconds} seconds since the last progress line; the deploy is still running`;
3862
3870
  var deployOptionsOf = (named, cwd, flags = /* @__PURE__ */ new Set()) => {
@@ -4067,7 +4075,78 @@ var commandStartBodyOf = (application, upload, environment, commit) => ({
4067
4075
  ...startBodyOf(application, upload, environment),
4068
4076
  ...commit !== null ? { commit } : {}
4069
4077
  });
4078
+ var DEPLOY_RESULT_CONTRACT_VERSION = 1;
4079
+ var DEPLOY_OUTCOMES = Object.freeze({
4080
+ [PROGRESS_EXIT_STATUS.deployed]: "deployed",
4081
+ [PROGRESS_EXIT_STATUS.failed]: "failed",
4082
+ [PROGRESS_EXIT_STATUS.refused]: "unknown",
4083
+ [PROGRESS_EXIT_STATUS.nothing_started]: "nothing_started",
4084
+ unwaited: "started"
4085
+ });
4086
+ var PREPARED_FACTS = new RegExp(`^${PREPARED_LINE_HEAD}upload (\\S+) to (\\S+), `);
4087
+ var ANSWER_LINE_ERROR = /^.+ (?:refused|failed) \(HTTP \d{3}\): (\S+)$/;
4088
+ var SETTLED_FAILED_FACTS = new RegExp(`^${PROGRESS_SETTLED_FAILED}(?: at (\\S+))?: (\\S+)$`);
4089
+ var deployResultOf = (status, application, lines, waited = true) => {
4090
+ const result = {
4091
+ contract_version: DEPLOY_RESULT_CONTRACT_VERSION,
4092
+ status,
4093
+ outcome: status === PROGRESS_EXIT_STATUS.deployed && !waited ? DEPLOY_OUTCOMES.unwaited : DEPLOY_OUTCOMES[status] ?? "unknown",
4094
+ application: application !== void 0 && UPLOAD_ID.test(application) ? application : null,
4095
+ environment: null,
4096
+ upload: null,
4097
+ version: null,
4098
+ address: null,
4099
+ failed_step: null,
4100
+ error: null
4101
+ };
4102
+ let answered = null;
4103
+ for (const line of lines) {
4104
+ const prepared = PREPARED_FACTS.exec(line);
4105
+ if (prepared !== null) {
4106
+ result.upload = prepared[1];
4107
+ result.environment = prepared[2];
4108
+ } else if (line.startsWith(PROGRESS_SETTLED_DEPLOYED)) {
4109
+ const version = Number(line.slice(PROGRESS_SETTLED_DEPLOYED.length));
4110
+ result.version = Number.isInteger(version) ? version : null;
4111
+ } else if (line.startsWith(ADDRESS_LINE_HEAD)) {
4112
+ result.address = line.slice(ADDRESS_LINE_HEAD.length);
4113
+ } else {
4114
+ const failed = SETTLED_FAILED_FACTS.exec(line);
4115
+ if (failed !== null) {
4116
+ result.failed_step = failed[1] ?? null;
4117
+ result.error = failed[2];
4118
+ } else if (answered === null) answered = ANSWER_LINE_ERROR.exec(line)?.[1] ?? null;
4119
+ }
4120
+ }
4121
+ result.error ??= answered;
4122
+ return result;
4123
+ };
4124
+ var jsonResultHoldOf = (io2) => io2.jsonResult ?? { printed: false, lines: [] };
4125
+ var printDeployResultOnce = (io2, held, result) => {
4126
+ if (held.printed) return;
4127
+ held.printed = true;
4128
+ io2.print(JSON.stringify(result));
4129
+ };
4070
4130
  async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
4131
+ if (!flags.has(DEPLOY_JSON_FLAG)) return deployRun(named, io2, flags);
4132
+ const held = jsonResultHoldOf(io2);
4133
+ const toError = io2.printError ?? io2.print;
4134
+ const print = (line) => {
4135
+ held.lines.push(line);
4136
+ toError(line);
4137
+ };
4138
+ const waited = (io2.env[DEPLOY_NO_WAIT_VARIABLE] ?? "") === "";
4139
+ let status;
4140
+ try {
4141
+ status = await deployRun(named, { ...io2, print }, flags);
4142
+ } catch (error) {
4143
+ printDeployResultOnce(io2, held, deployResultOf(PROGRESS_EXIT_STATUS.refused, named[DEPLOY_OPTION.application], held.lines));
4144
+ throw error;
4145
+ }
4146
+ printDeployResultOnce(io2, held, deployResultOf(status, named[DEPLOY_OPTION.application], held.lines, waited));
4147
+ return status;
4148
+ }
4149
+ async function deployRun(named, io2, flags) {
4071
4150
  const options = deployOptionsOf(named, io2.cwd, flags);
4072
4151
  if ("refusal" in options) {
4073
4152
  io2.print(options.refusal);
@@ -4266,7 +4345,11 @@ async function deploy(named, io2, flags = /* @__PURE__ */ new Set()) {
4266
4345
  }
4267
4346
  if (partial !== "") io2.print(oneLine(partial));
4268
4347
  cursor += lines.length;
4269
- if (settled !== null) return settled;
4348
+ if (settled !== null) {
4349
+ const addressLine = settled === PROGRESS_EXIT_STATUS.deployed ? addressLineOf(answer.hostname) : null;
4350
+ if (addressLine !== null) io2.print(addressLine);
4351
+ return settled;
4352
+ }
4270
4353
  if (lines.length > 0 || partial !== "") lastProgressAt = io2.clock().getTime();
4271
4354
  else waitingIfSilent();
4272
4355
  if (lines.length === 0) await io2.sleep(PROGRESS_PAUSE_SECONDS * 1e3);
@@ -11081,6 +11164,7 @@ async function runGuarded(argv2, io2) {
11081
11164
  const belowFloor = nodeFloorRefusal(found, version);
11082
11165
  if (belowFloor !== null) {
11083
11166
  outputOf(argv2, io2)(carriesTokenCode(argv2) ? tokenCodeFloorSentence(version, found) : belowFloor);
11167
+ if (jsonDeployOf(argv2)) printDeployResultOnce(io2, jsonResultHoldOf(io2), deployResultOf(COMMAND_STATUS.nothing_started, deployApplicationOf(argv2), []));
11084
11168
  return COMMAND_STATUS.nothing_started;
11085
11169
  }
11086
11170
  const parsed = parseArguments(argv2, io2.windows ?? ON_WINDOWS);
@@ -11107,6 +11191,7 @@ Get started: ${SETUP_GUIDE}`);
11107
11191
  say(`Paste this into Claude, ChatGPT, or Codex:
11108
11192
  ${SETUP_PROMPT}`);
11109
11193
  }
11194
+ if (jsonDeployOf(argv2)) printDeployResultOnce(io2, jsonResultHoldOf(io2), deployResultOf(COMMAND_STATUS.nothing_started, deployApplicationOf(argv2), []));
11110
11195
  return COMMAND_STATUS.nothing_started;
11111
11196
  }
11112
11197
  const upgrade = startUpgradeCheck(io2, parsed.subcommand.name, parsed.call.named, version);
@@ -11115,7 +11200,22 @@ ${SETUP_PROMPT}`);
11115
11200
  for (const line of notices.finish(upgrade)) say(line);
11116
11201
  return status;
11117
11202
  }
11118
- var outputOf = (argv2, io2) => argv2[0] === TOKEN_SUBCOMMAND && argv2[1] === TOKEN_CLAIM_ACT ? io2.printError ?? io2.print : io2.print;
11203
+ var jsonDeployOf = (argv2) => argv2[0] === DEPLOY_SUBCOMMAND && argv2.includes(DEPLOY_JSON_FLAG);
11204
+ var deployApplicationOf = (argv2) => {
11205
+ const option = DEPLOY_OPTION.application;
11206
+ for (let index = 1; index < argv2.length; index += 1) {
11207
+ const argument = argv2[index];
11208
+ if (argument === option) return argv2[index + 1];
11209
+ if (argument.startsWith(`${option}=`)) return argument.slice(option.length + 1);
11210
+ }
11211
+ return void 0;
11212
+ };
11213
+ var printUnknownResult = (argv2, io2) => {
11214
+ if (!jsonDeployOf(argv2)) return;
11215
+ const held = jsonResultHoldOf(io2);
11216
+ printDeployResultOnce(io2, held, deployResultOf(COMMAND_STATUS.unknown, deployApplicationOf(argv2), held.lines));
11217
+ };
11218
+ var outputOf = (argv2, io2) => argv2[0] === TOKEN_SUBCOMMAND && argv2[1] === TOKEN_CLAIM_ACT || jsonDeployOf(argv2) ? io2.printError ?? io2.print : io2.print;
11119
11219
  var NOTHING_REACHED = "no subcommand was reached, so nothing was started";
11120
11220
  var ownErrorSentence = (error, afterOwnError = NOTHING_REACHED) => `The command stopped on an error of its own (${error instanceof Error ? error.message : String(error)}). ${afterOwnError.charAt(0).toUpperCase()}${afterOwnError.slice(1)}.`;
11121
11221
  var ownErrorLineOf = (argv2, io2, error) => {
@@ -11126,6 +11226,7 @@ async function runProcess(argv2, unguarded2, io2 = guarded(unguarded2)) {
11126
11226
  try {
11127
11227
  return await runGuarded(argv2, io2);
11128
11228
  } catch (error) {
11229
+ printUnknownResult(argv2, io2);
11129
11230
  outputOf(argv2, io2)(ownErrorLineOf(argv2, io2, error));
11130
11231
  return COMMAND_STATUS.unknown;
11131
11232
  }
@@ -11136,6 +11237,7 @@ var outsideErrorHandler = (argv2, io2, end) => {
11136
11237
  if (handled) return;
11137
11238
  handled = true;
11138
11239
  try {
11240
+ printUnknownResult(argv2, io2);
11139
11241
  outputOf(argv2, io2)(ownErrorLineOf(argv2, io2, error));
11140
11242
  } finally {
11141
11243
  end(COMMAND_STATUS.unknown);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turnzero",
3
- "version": "0.33.1",
3
+ "version": "0.34.0",
4
4
  "type": "module",
5
5
  "description": "Deploy and run AI-built apps with Turn Zero Cloud hosting and backend services.",
6
6
  "license": "Apache-2.0",