turnzero 0.19.0-next.1 → 0.20.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 CHANGED
@@ -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, where you ask your tool to.
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/
@@ -910,10 +910,14 @@ var variableReadSentence = (origin, standing) => {
910
910
  };
911
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.`;
912
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}.`;
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 or check line alone and unsets it once that line ends.`;
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.`;
914
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:";
915
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.";
916
- var loginRouteLines = (io2, origin) => [LOGIN_ROUTE_OPENER, ...loginLinesFor(io2, origin), DEPLOY_FILES_ROUTE_SENTENCE];
916
+ var loginRouteLines = (io2, origin, deployFiles = true) => [
917
+ LOGIN_ROUTE_OPENER,
918
+ ...loginLinesFor(io2, origin),
919
+ ...deployFiles ? [DEPLOY_FILES_ROUTE_SENTENCE] : []
920
+ ];
917
921
  var loginRouteRefusalOf = (io2, closing) => {
918
922
  const place = credentialFileOf(io2);
919
923
  const tag = runTagOf();
@@ -1369,7 +1373,7 @@ var AUTHORIZE_ROW = Object.freeze({
1369
1373
  },
1370
1374
  variables: [[ORIGIN_VARIABLE, AUTHORIZATION_ORIGIN_VARIABLE_LINE]],
1371
1375
  note: [
1372
- `Authorizing this computer takes three acts once for each platform address, where you ask your tool to authorize it; 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.`,
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.`,
1373
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}.`
1374
1378
  ],
1375
1379
  afterOwnError: AUTHORIZE_AFTER_OWN_ERROR,
@@ -3794,7 +3798,7 @@ var DEPLOY_ROW = Object.freeze({
3794
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}.`,
3795
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.`,
3796
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}.`,
3797
- `A tool sets ${MINTED_TOKEN_VARIABLE} for the deploy or check 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.`,
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.`,
3798
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.`,
3799
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.`,
3800
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.`,
@@ -4845,10 +4849,38 @@ var LIBRARY_TAKE_OPTIONS = Object.freeze([
4845
4849
  name: LIBRARY_OPTION.origin,
4846
4850
  value: "<origin>",
4847
4851
  required: false,
4848
- 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.`
4849
4853
  }
4850
4854
  ]);
4851
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
+ }
4852
4884
  var LINES_IN_WINDOWS_FORM = process.platform === "win32";
4853
4885
  var machineLineOf = (line, form = LINES_IN_WINDOWS_FORM, origin = DEFAULT_COMMAND_ORIGIN) => {
4854
4886
  const windows = typeof form === "boolean" ? form : form.windows;
@@ -5001,7 +5033,7 @@ var listedEntryOf = (answer, name) => {
5001
5033
  return { name, path, version, closure_hash: closure, files: listed2, source_commit: commit };
5002
5034
  };
5003
5035
  var LIST_READ = "The read of the entry's file list";
5004
- var readOf = async (io2, origin, userAgent, body, what) => {
5036
+ var readOf = async (io2, origin, bearer, userAgent, body, what) => {
5005
5037
  let status;
5006
5038
  let text;
5007
5039
  try {
@@ -5010,10 +5042,15 @@ var readOf = async (io2, origin, userAgent, body, what) => {
5010
5042
  what,
5011
5043
  () => io2.fetch(`${origin}${LIBRARY_READ_PATH}`, {
5012
5044
  method: "POST",
5013
- headers: { "user-agent": userAgent, "content-type": "application/json" },
5014
- body: JSON.stringify(body)
5045
+ headers: { authorization: `Bearer ${bearer}`, "user-agent": userAgent, "content-type": "application/json" },
5046
+ body: JSON.stringify(body),
5047
+ redirect: "manual"
5015
5048
  })
5016
5049
  );
5050
+ if (redirected(response)) {
5051
+ await response.body?.cancel().catch(() => void 0);
5052
+ return { lines: [takeRedirectedLine(response.status)] };
5053
+ }
5017
5054
  status = response.status;
5018
5055
  text = await response.text();
5019
5056
  } catch {
@@ -5026,14 +5063,14 @@ var readOf = async (io2, origin, userAgent, body, what) => {
5026
5063
  if (status >= PLATFORM_FAILURE_STATUS) return { lines: [...failureLines(LIBRARY_READ_ACTION, status, refusal), NOTHING_WRITTEN2] };
5027
5064
  return { lines: [...refusalLines(LIBRARY_READ_ACTION, status, refusal), NOTHING_WRITTEN2] };
5028
5065
  };
5029
- var fileBytesOf = async (io2, origin, userAgent, name, file) => {
5066
+ var fileBytesOf = async (io2, origin, bearer, userAgent, name, file) => {
5030
5067
  const shown2 = `${LIBRARY_FOLDER}/${shownName(file.path)}`;
5031
5068
  let text = "";
5032
5069
  let encoding = null;
5033
5070
  let offset = 0;
5034
5071
  let stamp = null;
5035
5072
  for (; ; ) {
5036
- 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}`);
5037
5074
  if ("lines" in read2) return { lines: [`${shown2}: the read stopped.`, ...read2.lines] };
5038
5075
  const chunk = read2.answer.file;
5039
5076
  const next = chunk?.next_offset;
@@ -5258,7 +5295,7 @@ var COPY_PACKAGES_CLAUSE = "copy the packages line into the application manifest
5258
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; ";
5259
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.`;
5260
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";
5261
- async function readTake(io2, options, userAgent, planOf, planeChecked = false) {
5298
+ async function readTake(io2, options, userAgent, bearer, planOf, planeChecked = false) {
5262
5299
  const refuse = (...lines) => ({ lines });
5263
5300
  const found = libraryFolderOf(io2, options.root);
5264
5301
  if ("refusal" in found) return refuse(found.refusal);
@@ -5278,7 +5315,7 @@ async function readTake(io2, options, userAgent, planOf, planeChecked = false) {
5278
5315
  const older = await planeWireRefusal(io2, options.origin);
5279
5316
  if (older !== null) return refuse(older);
5280
5317
  }
5281
- 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);
5282
5319
  if ("lines" in list) return refuse(...list.lines);
5283
5320
  const entry = listedEntryOf(list.answer, options.entry);
5284
5321
  if ("refusal" in entry) return refuse(entry.refusal);
@@ -5300,7 +5337,7 @@ async function readTake(io2, options, userAgent, planOf, planeChecked = false) {
5300
5337
  if (staged !== null) return refuse(staged);
5301
5338
  const bytes = /* @__PURE__ */ new Map();
5302
5339
  for (const file of entry.files) {
5303
- const read2 = await fileBytesOf(io2, options.origin, userAgent, entry.name, file);
5340
+ const read2 = await fileBytesOf(io2, options.origin, bearer, userAgent, entry.name, file);
5304
5341
  if ("lines" in read2) return refuse(...read2.lines);
5305
5342
  const actual = sha256(read2.bytes);
5306
5343
  if (actual !== file.sha256) return refuse(`${LIBRARY_FOLDER}/${shownName(file.path)}: SHA-256 ${actual} is not the listed value ${file.sha256}. ${NOTHING_WRITTEN2}`);
@@ -5408,7 +5445,9 @@ async function libraryTake(call, io2) {
5408
5445
  }
5409
5446
  const fromApplication = applicationFolderRefusalOf(io2, options);
5410
5447
  if (fromApplication !== null) return refuse(fromApplication);
5411
- const checked = await readTake(io2, options, agent.userAgent, (library, name) => copyPlanOf(io2, options.root, library, name));
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));
5412
5451
  if ("lines" in checked) return refuse(...checked.lines);
5413
5452
  const application = applicationManifestOf(io2, options.root, checked.library, checked.plan?.application ?? applicationFolderOf(io2, options.root));
5414
5453
  if ("refusal" in application) return refuse(application.refusal);
@@ -5419,9 +5458,10 @@ async function libraryTake(call, io2) {
5419
5458
  var LIBRARY_ROW = Object.freeze({
5420
5459
  name: LIBRARY_SUBCOMMAND,
5421
5460
  acts: LIBRARY_ACTS,
5422
- 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.",
5423
5462
  options: LIBRARY_TAKE_OPTIONS,
5424
- credential: null,
5463
+ credential: LIBRARY_CREDENTIAL,
5464
+ reads: LIBRARY_READS,
5425
5465
  statuses: {
5426
5466
  done: "the entry taken, and installed where it carries compiled modules",
5427
5467
  failed: "the entry, its row, and the copy written, and npm install failed or the application manifest's packages was not written",
@@ -5808,6 +5848,7 @@ var whoamiLinesOf = (origin, account, entry) => [
5808
5848
  `${WHOAMI_LINE_HEADS.token}label ${entry.label !== null ? JSON.stringify(entry.label) : "none"}, expiring ${entry.expires_at !== null ? `at ${entry.expires_at}` : "never"}`
5809
5849
  ];
5810
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.`;
5811
5852
  var whoamiAfterLoginSentence = (whoami2) => `Once login has printed authorized:, run ${whoami2} again.`;
5812
5853
  async function whoami(call, io2) {
5813
5854
  const origin = entryOriginOf(io2.env, call.named[DEPLOY_OPTION.origin], NOTHING_SENT_OR_READ);
@@ -5831,6 +5872,7 @@ async function whoami(call, io2) {
5831
5872
  const expired2 = entry !== void 0 && entry.expires_at !== null && Date.parse(entry.expires_at) <= io2.clock().getTime();
5832
5873
  if (entry === void 0 || expired2) {
5833
5874
  io2.print(noComputerTokenSentence(origin.origin, entry === void 0 ? null : entry.expires_at));
5875
+ io2.print(noLoginRoutesSentence(origin.origin));
5834
5876
  const reached = authorizationOriginOf(io2.env, call.named[DEPLOY_OPTION.origin], NOTHING_SENT_OR_READ);
5835
5877
  if ("refusal" in reached) {
5836
5878
  io2.print(reached.refusal);
@@ -5861,7 +5903,7 @@ async function whoami(call, io2) {
5861
5903
  }
5862
5904
  var WHOAMI_ROW = Object.freeze({
5863
5905
  name: WHOAMI_SUBCOMMAND,
5864
- 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.`,
5865
5907
  options: WHOAMI_OPTIONS,
5866
5908
  credential: null,
5867
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",
@@ -5869,7 +5911,7 @@ var WHOAMI_ROW = Object.freeze({
5869
5911
  done: "the account was read",
5870
5912
  failed: "the platform refused the read, where it no longer accepts the token among other causes: the printed lines name login",
5871
5913
  unknown: "the read drew no answer, or an answer that is not a refusal",
5872
- 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 give the login route"
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"
5873
5915
  },
5874
5916
  variables: [[ORIGIN_VARIABLE, `the platform's address whose token is read where the line names no ${DEPLOY_OPTION.origin}.`]],
5875
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}.`],
@@ -7828,7 +7870,7 @@ var SCAFFOLD_OPTIONS = Object.freeze([
7828
7870
  name: SCAFFOLD_OPTION.origin,
7829
7871
  value: "<origin>",
7830
7872
  required: false,
7831
- description: `The platform's address. Where absent, ${DEFAULT_COMMAND_ORIGIN}.`
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}.`
7832
7874
  },
7833
7875
  {
7834
7876
  name: SCAFFOLD_OPTION.application,
@@ -8574,7 +8616,9 @@ async function scaffold(call, io2) {
8574
8616
  const files = writtenFilesOf(template, options.names, options.fields);
8575
8617
  const app = join10(root, APPLICATION_FOLDER);
8576
8618
  const stage = join10(root, STAGE_FOLDER);
8577
- const takeLines = takes.map((take) => machineLineOf(libraryTakeLineOf({ origin, entry: take.entry }), { windows: io2.windows ?? LINES_IN_WINDOWS_FORM, npx: ranThroughNpx(io2) }, origin));
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);
8578
8622
  let reached = "checks";
8579
8623
  const stateNow = () => {
8580
8624
  if (reached === "template") return "template";
@@ -8601,10 +8645,14 @@ async function scaffold(call, io2) {
8601
8645
  if ("lines" in found) return refuse(...found.lines);
8602
8646
  held = found;
8603
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);
8604
8652
  for (const take of takes) {
8605
8653
  const copy = join10(app, "lib", take.entry);
8606
8654
  const planOf = (library) => writesOver(library, copy) ? { refusal: LIBRARY_OVER_APPLICATION } : { application: app, copy, declared: null, declaredAt: null };
8607
- 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);
8608
8656
  if ("lines" in read2) return refuse(...read2.lines);
8609
8657
  if (read2.plan === null) {
8610
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}`);
@@ -8637,7 +8685,7 @@ async function scaffold(call, io2) {
8637
8685
  release();
8638
8686
  if (ended) return COMMAND_STATUS.unknown;
8639
8687
  const taken = checks.map((check2, index) => ({ entry: takes[index].entry, version: check2.entry.version, line: takeLines[index] }));
8640
- return await installAndTest(io2, app, template, taken, origin, { tools, claude: claudeStands });
8688
+ return await installAndTest(io2, app, template, taken, lineOrigin, { tools, claude: claudeStands });
8641
8689
  } catch (error) {
8642
8690
  const state = stateNow();
8643
8691
  const arrivedAs = error instanceof ApplicationArrived ? state : null;
@@ -8687,11 +8735,15 @@ async function installAndTest(io2, app, template, taken, origin, left) {
8687
8735
  for (const line of nextStepLines(origin, { windows: io2.windows ?? LINES_IN_WINDOWS_FORM, npx: ranThroughNpx(io2) }, left, template)) io2.print(line);
8688
8736
  return COMMAND_STATUS.done;
8689
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}`;
8690
8741
  var SCAFFOLD_ROW = Object.freeze({
8691
8742
  name: SCAFFOLD_SUBCOMMAND,
8692
- 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. It reads no credential.`,
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.`,
8693
8744
  options: SCAFFOLD_OPTIONS,
8694
- credential: null,
8745
+ credential: SCAFFOLD_CREDENTIAL,
8746
+ reads: SCAFFOLD_READS,
8695
8747
  statuses: {
8696
8748
  done: "the files written and installed, the entries taken where the template takes any, and the tests passed",
8697
8749
  failed: "the files written, and npm install or the tests failed",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turnzero",
3
- "version": "0.19.0-next.1",
3
+ "version": "0.20.0",
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",