@lazehq/cli 0.1.34 → 0.1.35

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.
Files changed (2) hide show
  1. package/dist/laze.mjs +400 -288
  2. package/package.json +1 -1
package/dist/laze.mjs CHANGED
@@ -35708,7 +35708,7 @@ function flagText({
35708
35708
  }
35709
35709
 
35710
35710
  // src/rules/cli-version.rules.ts
35711
- var STAMPED = "0.1.34";
35711
+ var STAMPED = "0.1.35";
35712
35712
  function cliVersionOf({ stamped = STAMPED } = {}) {
35713
35713
  return stamped.startsWith("__") ? "dev" : stamped;
35714
35714
  }
@@ -62006,7 +62006,9 @@ var updateVaultConnectionInputSchemaDefinition = external_exports.object({
62006
62006
  maxLeaseSeconds: external_exports.number().int().positive().max(VAULT_MAX_LEASE_SECONDS).nullable().optional(),
62007
62007
  everyUseAsks: external_exports.boolean().optional(),
62008
62008
  secret: vaultSecretBundleSchema.optional(),
62009
- egress: vaultEgressTargetSchema.nullable().optional()
62009
+ egress: vaultEgressTargetSchema.nullable().optional(),
62010
+ /** A fresh passkey or Face ID proof, needed to lower a key from Always ask or Human only. */
62011
+ reauthToken: external_exports.string().min(1).optional()
62010
62012
  });
62011
62013
  var updateVaultConnectionInputSchema = updateVaultConnectionInputSchemaDefinition;
62012
62014
  var vaultGrantSubjectSchemaDefinition = external_exports.object({
@@ -62023,7 +62025,9 @@ var vaultGrantSchemaDefinition = external_exports.object({
62023
62025
  lend: external_exports.boolean(),
62024
62026
  grantedByUserId: external_exports.string(),
62025
62027
  expiresAt: external_exports.iso.datetime().nullable(),
62026
- createdAt: external_exports.iso.datetime()
62028
+ createdAt: external_exports.iso.datetime(),
62029
+ /** A share asked from the CLI or by an agent: nothing reaches it until the owner's passkey. */
62030
+ awaitingOwner: external_exports.boolean().optional()
62027
62031
  });
62028
62032
  var vaultGrantSchema = vaultGrantSchemaDefinition;
62029
62033
  var grantVaultConnectionInputSchemaDefinition = external_exports.object({
@@ -62041,6 +62045,33 @@ var lendVaultConnectionInputSchemaDefinition = external_exports.object({
62041
62045
  durationSeconds: external_exports.number().int().positive().nullable().default(null)
62042
62046
  });
62043
62047
  var lendVaultConnectionInputSchema = lendVaultConnectionInputSchemaDefinition;
62048
+ var shareVaultConnectionInputSchemaDefinition = external_exports.object({
62049
+ workspaceId: external_exports.string().min(1),
62050
+ connectionId: external_exports.string().min(1),
62051
+ /** The teammate whose own agents may use the key; they never see its value. */
62052
+ userId: external_exports.string().min(1),
62053
+ durationSeconds: external_exports.number().int().positive().nullable().default(null),
62054
+ /** A fresh passkey or Face ID proof makes the share live; without one it waits for the owner. */
62055
+ reauthToken: external_exports.string().min(1).optional()
62056
+ });
62057
+ var shareVaultConnectionInputSchema = shareVaultConnectionInputSchemaDefinition;
62058
+ var approveVaultShareInputSchemaDefinition = external_exports.object({
62059
+ workspaceId: external_exports.string().min(1),
62060
+ grantId: external_exports.string().min(1),
62061
+ reauthToken: external_exports.string().min(1)
62062
+ });
62063
+ var approveVaultShareInputSchema = approveVaultShareInputSchemaDefinition;
62064
+ var requestVaultShareInputSchemaDefinition = external_exports.object({
62065
+ turnToken: external_exports.string().min(1),
62066
+ connection: external_exports.string().min(1),
62067
+ userId: external_exports.string().min(1),
62068
+ durationSeconds: external_exports.number().int().positive().nullable().default(null)
62069
+ });
62070
+ var setVaultLevelInTurnInputSchemaDefinition = external_exports.object({
62071
+ turnToken: external_exports.string().min(1),
62072
+ connection: external_exports.string().min(1),
62073
+ sensitivity: vaultSensitivitySchema
62074
+ });
62044
62075
  var vaultAgentKindSchema = external_exports.enum(["personal", "team"]);
62045
62076
  var openVaultTurnInputSchemaDefinition = external_exports.object({
62046
62077
  workspaceId: external_exports.string().min(1),
@@ -62298,7 +62329,14 @@ var vaultConnectionUseSchemaDefinition = external_exports.object({
62298
62329
  leaseId: external_exports.string().nullable().default(null),
62299
62330
  origin: vaultUseOriginSchema.nullable().default(null),
62300
62331
  /** How a person's read happened: `page` (Reveal) or `own_secret` (the CLI or Laze for them). */
62301
- via: external_exports.string().nullable().default(null)
62332
+ via: external_exports.string().nullable().default(null),
62333
+ /** On a share or a tier change: the person it named, the new tier, and the proof used. */
62334
+ change: external_exports.object({
62335
+ subjectUserId: external_exports.string().nullable(),
62336
+ to: vaultSensitivitySchema.nullable(),
62337
+ direction: external_exports.enum(["raised", "lowered"]).nullable(),
62338
+ method: external_exports.string().nullable()
62339
+ }).nullable().optional()
62302
62340
  });
62303
62341
  var vaultConnectionUseSchema = vaultConnectionUseSchemaDefinition;
62304
62342
  var vaultRevokeAtProviderSchema = external_exports.enum(["revoked", "failed", "not_supported"]);
@@ -62395,7 +62433,7 @@ var vaultTrpc = defineTrpcContract("vault").mutation("createConnection").withInp
62395
62433
  reason: external_exports.string().trim().min(1).max(200),
62396
62434
  reauthToken: external_exports.string().min(1).max(200).optional()
62397
62435
  })
62398
- ).withOutput(external_exports.object({ fields: external_exports.record(external_exports.string(), external_exports.string()) })).mutation("beginReauth").withInput(workspaceInput).withOutput(vaultReauthChallengeSchema).mutation("confirmReauth").withInput(confirmVaultReauthInputSchema).withOutput(vaultReauthProofSchema).mutation("grantConnection").withInput(grantVaultConnectionInputSchema).withOutput(vaultGrantSchema).mutation("lendToChannel").withInput(lendVaultConnectionInputSchema).withOutput(vaultGrantSchema).mutation("revokeGrant").withInput(external_exports.object({ workspaceId: external_exports.string().min(1), grantId: external_exports.string().min(1) })).withOutput(external_exports.object({ revoked: external_exports.literal(true) })).query("listGrants").withInput(connectionInput).withOutput(external_exports.array(vaultGrantSchema)).query("listApprovals").withInput(external_exports.object({ ...workspaceInput.shape, state: vaultApprovalStateSchema.optional() })).withOutput(external_exports.array(vaultApprovalRequestSchema)).mutation("answerApproval").withInput(answerVaultApprovalInputSchema).withOutput(vaultApprovalRequestSchema).query("listAudit").withInput(vaultAuditReadInputSchema).withOutput(vaultAuditPageSchema).query("listActivity").withInput(vaultAuditReadInputSchema).withOutput(external_exports.array(vaultAuditEntrySchema)).query("listLeases").withInput(workspaceInput).withOutput(external_exports.array(vaultLeaseSchema)).mutation("endLease").withInput(endVaultLeaseInputSchema).withOutput(external_exports.object({ ended: external_exports.literal(true) })).query("mostAsked").withInput(vaultMostAskedInputSchema).withOutput(vaultMostAskedSchema).query("judgeStatus").withInput(workspaceInput).withOutput(vaultJudgeStatusSchema).query("listLevelLimits").withInput(workspaceInput).withOutput(external_exports.array(vaultLevelLimitSchema)).mutation("setLevelLimit").withInput(setVaultLevelLimitInputSchema).withOutput(external_exports.array(vaultLevelLimitSchema)).query("listDevices").withInput(workspaceInput).withOutput(external_exports.array(vaultDeviceSchema)).mutation("beginDeviceEnrolment").withInput(workspaceInput).withOutput(vaultDeviceEnrolmentSchema).mutation("finishDeviceEnrolment").withInput(finishVaultDeviceEnrolmentInputSchema).withOutput(vaultDeviceSchema).mutation("beginDeviceApproval").withInput(external_exports.object({ ...workspaceInput.shape, deviceId: external_exports.string().min(1) })).withOutput(external_exports.object({ challenge: external_exports.string(), sealed: external_exports.array(vaultSealedMaterialSchema) })).mutation("approveDevice").withInput(approveVaultDeviceInputSchema).withOutput(vaultDeviceSchema).mutation("removeDevice").withInput(external_exports.object({ ...workspaceInput.shape, deviceId: external_exports.string().min(1) })).withOutput(external_exports.object({ removed: external_exports.literal(true) })).mutation("createSealedConnection").withInput(createSealedConnectionInputSchema).withOutput(vaultConnectionSchema).mutation("resealConnection").withInput(resealConnectionInputSchema).withOutput(vaultConnectionSchema).mutation("openSealedConnection").withInput(
62436
+ ).withOutput(external_exports.object({ fields: external_exports.record(external_exports.string(), external_exports.string()) })).mutation("beginReauth").withInput(workspaceInput).withOutput(vaultReauthChallengeSchema).mutation("confirmReauth").withInput(confirmVaultReauthInputSchema).withOutput(vaultReauthProofSchema).mutation("grantConnection").withInput(grantVaultConnectionInputSchema).withOutput(vaultGrantSchema).mutation("lendToChannel").withInput(lendVaultConnectionInputSchema).withOutput(vaultGrantSchema).mutation("revokeGrant").withInput(external_exports.object({ workspaceId: external_exports.string().min(1), grantId: external_exports.string().min(1) })).withOutput(external_exports.object({ revoked: external_exports.literal(true) })).query("listGrants").withInput(connectionInput).withOutput(external_exports.array(vaultGrantSchema)).mutation("shareConnection").withInput(shareVaultConnectionInputSchema).withOutput(vaultGrantSchema).mutation("approveShare").withInput(approveVaultShareInputSchema).withOutput(vaultGrantSchema).query("listApprovals").withInput(external_exports.object({ ...workspaceInput.shape, state: vaultApprovalStateSchema.optional() })).withOutput(external_exports.array(vaultApprovalRequestSchema)).mutation("answerApproval").withInput(answerVaultApprovalInputSchema).withOutput(vaultApprovalRequestSchema).query("listAudit").withInput(vaultAuditReadInputSchema).withOutput(vaultAuditPageSchema).query("listActivity").withInput(vaultAuditReadInputSchema).withOutput(external_exports.array(vaultAuditEntrySchema)).query("listLeases").withInput(workspaceInput).withOutput(external_exports.array(vaultLeaseSchema)).mutation("endLease").withInput(endVaultLeaseInputSchema).withOutput(external_exports.object({ ended: external_exports.literal(true) })).query("mostAsked").withInput(vaultMostAskedInputSchema).withOutput(vaultMostAskedSchema).query("judgeStatus").withInput(workspaceInput).withOutput(vaultJudgeStatusSchema).query("listLevelLimits").withInput(workspaceInput).withOutput(external_exports.array(vaultLevelLimitSchema)).mutation("setLevelLimit").withInput(setVaultLevelLimitInputSchema).withOutput(external_exports.array(vaultLevelLimitSchema)).query("listDevices").withInput(workspaceInput).withOutput(external_exports.array(vaultDeviceSchema)).mutation("beginDeviceEnrolment").withInput(workspaceInput).withOutput(vaultDeviceEnrolmentSchema).mutation("finishDeviceEnrolment").withInput(finishVaultDeviceEnrolmentInputSchema).withOutput(vaultDeviceSchema).mutation("beginDeviceApproval").withInput(external_exports.object({ ...workspaceInput.shape, deviceId: external_exports.string().min(1) })).withOutput(external_exports.object({ challenge: external_exports.string(), sealed: external_exports.array(vaultSealedMaterialSchema) })).mutation("approveDevice").withInput(approveVaultDeviceInputSchema).withOutput(vaultDeviceSchema).mutation("removeDevice").withInput(external_exports.object({ ...workspaceInput.shape, deviceId: external_exports.string().min(1) })).withOutput(external_exports.object({ removed: external_exports.literal(true) })).mutation("createSealedConnection").withInput(createSealedConnectionInputSchema).withOutput(vaultConnectionSchema).mutation("resealConnection").withInput(resealConnectionInputSchema).withOutput(vaultConnectionSchema).mutation("openSealedConnection").withInput(
62399
62437
  external_exports.object({
62400
62438
  ...connectionInput.shape,
62401
62439
  reason: external_exports.string().trim().min(1).max(200),
@@ -74504,10 +74542,15 @@ var appPull = async ({ context, args }) => {
74504
74542
  args,
74505
74543
  value: { ...pulled, folder },
74506
74544
  text: `Pulled ${pulled.files.length} files of ${pulled.app.name} (${site} version ${pulled.version.slice(0, 7)}) into ${where}
74507
- ` + (names.length ? `Its data: laze app records list <${names.join("|")}> --app ${pulled.app.slug}
74508
- ` : "It keeps no records.\n")
74545
+ ` + recordsHint({ names, slug: pulled.app.slug })
74509
74546
  });
74510
74547
  };
74548
+ function recordsHint({ names, slug }) {
74549
+ if (names.length === 0) return "It keeps no records.\n";
74550
+ const which = names.length === 1 ? names[0] : `<${names.join("|")}>`;
74551
+ return `Its data: laze app records list ${which} --app ${slug}
74552
+ `;
74553
+ }
74511
74554
  var appPublish = async ({ context, args }) => {
74512
74555
  const client = await appClient({ context, args });
74513
74556
  const appId = await appIdOf({ client, args, folder: folderOf({ context, word: void 0 }) });
@@ -79169,11 +79212,11 @@ async function connect({
79169
79212
  }
79170
79213
 
79171
79214
  // src/services/connector-commands.service.ts
79172
- import { readFile as readFile23 } from "node:fs/promises";
79215
+ import { readFile as readFile24 } from "node:fs/promises";
79173
79216
  import { resolve as resolve8 } from "node:path";
79174
79217
 
79175
79218
  // src/services/vault-keychain-commands.service.ts
79176
- import { readFile as readFile22 } from "node:fs/promises";
79219
+ import { readFile as readFile23 } from "node:fs/promises";
79177
79220
 
79178
79221
  // src/rules/vault-input.rules.ts
79179
79222
  function secondsOf(text2) {
@@ -79444,6 +79487,298 @@ var listDevices = async ({ context, args }) => {
79444
79487
  }
79445
79488
  };
79446
79489
 
79490
+ // src/services/vault-commands.service.ts
79491
+ import { createHash as createHash2 } from "node:crypto";
79492
+ import { mkdir as mkdir17, readFile as readFile22, rm as rm10, writeFile as writeFile18 } from "node:fs/promises";
79493
+ import { tmpdir as tmpdir2 } from "node:os";
79494
+ import { dirname as dirname17, join as join19 } from "node:path";
79495
+ var USAGE8 = `Usage: laze vault run <connection> --reason "<one plain sentence>" [--wait <s>] -- <command> [args...]
79496
+ Runs the command with the connection's values in its environment, on a Laze agent's computer
79497
+ or on a machine it runs on.`;
79498
+ var POLL_MS2 = 3e3;
79499
+ var RESTART_WAIT_MS = 12e4;
79500
+ var RESTART_BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 1e4];
79501
+ var P2562 = { name: "ECDH", namedCurve: "P-256" };
79502
+ var ServerGoneError = class extends Error {
79503
+ };
79504
+ function proxyOf({ env }) {
79505
+ const raw = env.LAZE_VAULT_PROXY || env.HTTP_PROXY || env.http_proxy;
79506
+ if (!raw) {
79507
+ throw new CliUsageError(
79508
+ "laze vault run works only on a Laze agent's computer or on a machine it runs on"
79509
+ );
79510
+ }
79511
+ const url2 = new URL(raw);
79512
+ const user = decodeURIComponent(url2.username);
79513
+ const password = decodeURIComponent(url2.password);
79514
+ return {
79515
+ origin: url2.origin,
79516
+ authorization: `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`
79517
+ };
79518
+ }
79519
+ function turnFileOf({
79520
+ env
79521
+ }) {
79522
+ return env.LAZE_TURN_FILE || void 0;
79523
+ }
79524
+ async function turnTokenOf3({
79525
+ env,
79526
+ turnFile,
79527
+ command = "laze vault run"
79528
+ }) {
79529
+ const pinned = env.LAZE_TURN_TOKEN?.trim();
79530
+ if (pinned) return pinned;
79531
+ if (!turnFile) throw new CliUsageError(`${command} works only during an agent's turn`);
79532
+ try {
79533
+ return (await readFile22(turnFile, "utf8")).trim();
79534
+ } catch {
79535
+ throw new CliUsageError(`${command} works only during an agent's turn`);
79536
+ }
79537
+ }
79538
+ function runKeyPathOf({
79539
+ turnFile,
79540
+ connection
79541
+ }) {
79542
+ const name2 = createHash2("sha256").update(connection).digest("hex");
79543
+ const base = turnFile ? dirname17(turnFile) : join19(tmpdir2(), "laze-vault");
79544
+ return join19(base, "vault-run-keys", `${name2}.json`);
79545
+ }
79546
+ async function runKeyAt({ path }) {
79547
+ const saved3 = await readFile22(path, "utf8").then(
79548
+ (text2) => JSON.parse(text2),
79549
+ () => void 0
79550
+ );
79551
+ if (saved3 && typeof saved3.publicKey === "string" && saved3.privateKey) {
79552
+ const privateKey = await globalThis.crypto.subtle.importKey("jwk", saved3.privateKey, P2562, false, ["deriveBits"]).catch(() => void 0);
79553
+ if (privateKey) return { privateKey, publicKey: saved3.publicKey };
79554
+ }
79555
+ const fresh = await generateEcdhKeys();
79556
+ const jwk = await globalThis.crypto.subtle.exportKey("jwk", fresh.privateKey);
79557
+ await mkdir17(dirname17(path), { recursive: true, mode: 448 });
79558
+ await writeFile18(path, JSON.stringify({ publicKey: fresh.publicKey, privateKey: jwk }), {
79559
+ mode: 384
79560
+ });
79561
+ return fresh;
79562
+ }
79563
+ function connectionRefused(error62) {
79564
+ const cause = error62.cause;
79565
+ if (cause?.code === "ECONNREFUSED") return true;
79566
+ return cause?.errors?.some((inner) => inner.code === "ECONNREFUSED") ?? false;
79567
+ }
79568
+ async function sendOnce({
79569
+ context,
79570
+ url: url2,
79571
+ init
79572
+ }) {
79573
+ let response;
79574
+ try {
79575
+ response = await context.fetcher(url2, init);
79576
+ } catch (error62) {
79577
+ const wasRefused = connectionRefused(error62);
79578
+ if (init.method === "POST" && !wasRefused) throw error62;
79579
+ return { restarting: wasRefused ? "connection refused" : String(error62) };
79580
+ }
79581
+ if (response.status !== 502 && response.status !== 503) return { response };
79582
+ const code = codeOf(await response.clone().text());
79583
+ const neverReached = response.status === 503 && code === "vault_not_answering";
79584
+ if (init.method === "POST" && !neverReached) return { response };
79585
+ return { restarting: `status ${response.status}${code ? ` ${code}` : ""}` };
79586
+ }
79587
+ async function reach(input2) {
79588
+ const { context } = input2;
79589
+ const started = context.nowMs();
79590
+ for (let attempt = 0; ; attempt += 1) {
79591
+ const sent = await sendOnce(input2);
79592
+ if ("response" in sent) return sent.response;
79593
+ if (context.nowMs() - started >= RESTART_WAIT_MS) {
79594
+ throw new ServerGoneError(
79595
+ `laze vault: the Laze server did not come back (${sent.restarting})`
79596
+ );
79597
+ }
79598
+ if (attempt === 0) {
79599
+ context.err(
79600
+ `laze vault: the Laze server is not answering (${sent.restarting}), probably restarting; waiting up to 2 min...
79601
+ `
79602
+ );
79603
+ }
79604
+ await context.sleep(RESTART_BACKOFF_MS[Math.min(attempt, RESTART_BACKOFF_MS.length - 1)] ?? 0);
79605
+ }
79606
+ }
79607
+ function codeOf(text2) {
79608
+ return /"code"\s*:\s*"([^"]+)"/.exec(text2)?.[1];
79609
+ }
79610
+ async function releaseOf({ response }) {
79611
+ const text2 = await response.text();
79612
+ if (!response.ok) {
79613
+ return {
79614
+ outcome: "deny",
79615
+ exitCode: 1,
79616
+ message: `laze vault: the release was refused (${codeOf(text2) ?? `status ${response.status}`})`
79617
+ };
79618
+ }
79619
+ return JSON.parse(text2);
79620
+ }
79621
+ async function envOf({
79622
+ release: release2,
79623
+ run: run4
79624
+ }) {
79625
+ if (!release2.sealed) return release2.env;
79626
+ const sealed = await openRunRelease({
79627
+ release: release2.sealed,
79628
+ runPrivateKey: run4.privateKey,
79629
+ runPublicKey: run4.publicKey,
79630
+ requestId: release2.sealed.requestId
79631
+ });
79632
+ return { ...release2.env, ...sealed };
79633
+ }
79634
+ function deadlineOf({ args, nowMs }) {
79635
+ const wait = flagText({ args, name: "wait" }) || (args.flags.wait === true ? args.words[3] : "");
79636
+ if (!wait) return Infinity;
79637
+ if (!(Number(wait) >= 0)) throw new CliUsageError(`--wait takes a number of seconds
79638
+ ${USAGE8}`);
79639
+ return nowMs + Number(wait) * 1e3;
79640
+ }
79641
+ async function untilAnswered({
79642
+ context,
79643
+ first,
79644
+ origin,
79645
+ headers,
79646
+ deadline
79647
+ }) {
79648
+ let release2 = first;
79649
+ while (release2.outcome === "ask") {
79650
+ if (context.nowMs() + POLL_MS2 > deadline) {
79651
+ context.err(`${release2.message}. Not approved yet; ask again later.
79652
+ `);
79653
+ return "capped";
79654
+ }
79655
+ if (release2 === first) {
79656
+ context.err(`${release2.message}. Waiting for an answer (cards expire after 12 hours)...
79657
+ `);
79658
+ }
79659
+ await context.sleep(POLL_MS2);
79660
+ const url2 = `${origin}/vault/release/${encodeURIComponent(release2.requestId)}`;
79661
+ release2 = await releaseOf({
79662
+ response: await reach({ context, url: url2, init: { method: "GET", headers } })
79663
+ });
79664
+ }
79665
+ return release2;
79666
+ }
79667
+ async function vaultRelease({
79668
+ context,
79669
+ args,
79670
+ connection,
79671
+ reason,
79672
+ command
79673
+ }) {
79674
+ const deadline = deadlineOf({ args, nowMs: context.nowMs() });
79675
+ const env = context.environment.inherited;
79676
+ const proxy = proxyOf({ env });
79677
+ const turnFile = turnFileOf({ env });
79678
+ const headers = {
79679
+ "proxy-authorization": proxy.authorization,
79680
+ "x-laze-turn": await turnTokenOf3({ env, turnFile })
79681
+ };
79682
+ const keyPath = runKeyPathOf({ turnFile, connection });
79683
+ const run4 = await runKeyAt({ path: keyPath });
79684
+ const body = JSON.stringify({ connection, reason, command, recipient: run4.publicKey });
79685
+ let release2;
79686
+ try {
79687
+ const first = await releaseOf({
79688
+ response: await reach({
79689
+ context,
79690
+ url: `${proxy.origin}/vault/release`,
79691
+ init: { method: "POST", headers: { ...headers, "content-type": "application/json" }, body }
79692
+ })
79693
+ });
79694
+ release2 = await untilAnswered({ context, first, origin: proxy.origin, headers, deadline });
79695
+ } catch (error62) {
79696
+ if (!(error62 instanceof ServerGoneError)) throw error62;
79697
+ context.err(`${error62.message}. Run the command again to keep waiting.
79698
+ `);
79699
+ return { exitCode: 75 };
79700
+ }
79701
+ if (release2 === "capped") return { exitCode: 75 };
79702
+ if (release2.outcome === "deny") {
79703
+ await rm10(keyPath, { force: true });
79704
+ context.err(`${release2.message}
79705
+ `);
79706
+ return { exitCode: release2.exitCode || 1 };
79707
+ }
79708
+ const values = await envOf({ release: release2, run: run4 });
79709
+ await rm10(keyPath, { force: true });
79710
+ if (release2.message) context.err(`${release2.message}
79711
+ `);
79712
+ return { env: values };
79713
+ }
79714
+ async function vaultRun({
79715
+ context,
79716
+ args
79717
+ }) {
79718
+ const connection = args.words[2];
79719
+ const reason = flagText({ args, name: "reason" });
79720
+ if (!connection || args.rest.length === 0) throw new CliUsageError(USAGE8);
79721
+ if (!reason) throw new CliUsageError(`Say why with --reason "<one plain sentence>"
79722
+ ${USAGE8}`);
79723
+ const released = await vaultRelease({ context, args, connection, reason, command: args.rest });
79724
+ if ("exitCode" in released) return released.exitCode;
79725
+ return context.runCommand({ command: args.rest, env: released.env });
79726
+ }
79727
+
79728
+ // src/services/vault-share-commands.service.ts
79729
+ var SHARE_USAGE2 = "Usage: laze vault share <connection> --with <email|userId> [--for 1h]";
79730
+ var WAITING_LINE = "It waits for the owner's approval with a passkey or Face ID, in Settings > Vault.";
79731
+ async function personOf({ client, who }) {
79732
+ const members3 = await client.get("/api/workspaces/current/members");
79733
+ const wanted = who.toLowerCase();
79734
+ const found = members3.find(
79735
+ (member) => member.userId === who || member.email.toLowerCase() === wanted
79736
+ );
79737
+ if (!found) throw new CliUsageError(`No one in this workspace is ${who}.`);
79738
+ return found.userId;
79739
+ }
79740
+ async function inTurn({
79741
+ context,
79742
+ client,
79743
+ command
79744
+ }) {
79745
+ const env = context.environment.inherited;
79746
+ const turn = await turnTokenOf3({ env, turnFile: turnFileOf({ env }), command });
79747
+ return client.withHeaders({ "x-laze-turn": turn });
79748
+ }
79749
+ function printShare({ context, grant }) {
79750
+ printGrant({ context, grant });
79751
+ if (grant.awaitingOwner) context.out(`${WAITING_LINE}
79752
+ `);
79753
+ }
79754
+ var shareWith = async ({ context, args }) => {
79755
+ const word3 = args.words[2];
79756
+ const who = flagText({ args, name: "with" });
79757
+ if (!word3 || !who) throw new CliUsageError(SHARE_USAGE2);
79758
+ const { client, profile } = await signedIn({ context, args });
79759
+ const userId = await personOf({ client, who });
79760
+ const grant = profile.kind === "agent" ? await (await inTurn({ context, client, command: "laze vault share" })).post("/api/vault/turn/share", {
79761
+ connection: word3,
79762
+ userId,
79763
+ ...durationOf(args)
79764
+ }) : await client.post("/api/vault/shares", {
79765
+ connectionId: (await connectionOf({ client, args, word: word3 })).id,
79766
+ userId,
79767
+ ...durationOf(args)
79768
+ });
79769
+ if (args.flags.json) return printJson4({ context, data: grant });
79770
+ printShare({ context, grant });
79771
+ };
79772
+ async function setLevelInTurn({
79773
+ context,
79774
+ client,
79775
+ connection,
79776
+ sensitivity
79777
+ }) {
79778
+ const turned = await inTurn({ context, client, command: "laze vault update --level" });
79779
+ return turned.post("/api/vault/turn/level", { connection, sensitivity });
79780
+ }
79781
+
79447
79782
  // src/services/vault-keychain-commands.service.ts
79448
79783
  var VAULT_USAGE = `Usage (your own key; an agent's key is refused, values are never printed):
79449
79784
  laze vault list [--json] connections you manage or see: names, fields, levels
@@ -79462,6 +79797,9 @@ var VAULT_USAGE = `Usage (your own key; an agent's key is refused, values are ne
79462
79797
  laze vault rm|revoke <connection> delete it; revoke also takes it back at the provider
79463
79798
  laze vault uses <connection> [--limit <n>] who used it, how, where, under which approval
79464
79799
  laze vault grant <connection> --to person|group|channel|agent:<id> [--for 1h]
79800
+ laze vault share <connection> --with <email|userId> [--for 1h] your own key, with one teammate:
79801
+ it waits for your passkey in Settings > Vault; Always ask and Human only keys are shared
79802
+ only from there. Lowering a key's level is done only there too; raising works anywhere
79465
79803
  laze vault lend <connection> --channel <channelId> [--for 1h]
79466
79804
  laze vault grants <connection> | laze vault ungrant <grantId>
79467
79805
  laze vault leases | laze vault end-lease <leaseId>
@@ -79495,7 +79833,7 @@ async function secretText({ context, args }) {
79495
79833
  );
79496
79834
  }
79497
79835
  const file3 = flagText({ args, name: "from-file" });
79498
- const text2 = file3 ? await readFile22(file3, "utf8") : await context.readStdin();
79836
+ const text2 = file3 ? await readFile23(file3, "utf8") : await context.readStdin();
79499
79837
  const trimmed = text2.replace(/\r?\n$/, "");
79500
79838
  if (!trimmed.trim()) throw new CliUsageError("No value: pipe it on stdin or use --from-file.");
79501
79839
  return trimmed;
@@ -79554,10 +79892,21 @@ var add3 = async ({ context, args }) => {
79554
79892
  );
79555
79893
  };
79556
79894
  var update = async ({ context, args }) => {
79557
- const { client } = await signedIn({ context, args });
79558
- const target = await connectionOf({ client, args, word: args.words[2] });
79895
+ const { client, profile } = await signedIn({ context, args });
79559
79896
  const settings2 = settingsOf(args);
79560
79897
  if (Object.keys(settings2).length === 0) throw new CliUsageError(VAULT_USAGE);
79898
+ const word3 = args.words[2];
79899
+ if (profile.kind === "agent" && word3 && settings2.sensitivity && Object.keys(settings2).length === 1) {
79900
+ const raised = await setLevelInTurn({
79901
+ context,
79902
+ client,
79903
+ connection: word3,
79904
+ sensitivity: settings2.sensitivity
79905
+ });
79906
+ if (args.flags.json) return printJson4({ context, data: raised });
79907
+ return printConnection({ context, row: raised });
79908
+ }
79909
+ const target = await connectionOf({ client, args, word: word3 });
79561
79910
  const updated = await client.post("/api/vault/connections/update", {
79562
79911
  connectionId: target.id,
79563
79912
  ...settings2
@@ -79649,6 +79998,7 @@ var VAULT_COMMANDS = {
79649
79998
  revoke: removal("revoke"),
79650
79999
  uses,
79651
80000
  grant: grantTo,
80001
+ share: shareWith,
79652
80002
  lend: lendTo,
79653
80003
  grants: grantsOf,
79654
80004
  ungrant: revokeGrant,
@@ -79705,7 +80055,7 @@ async function keyText({ context, args }) {
79705
80055
  throw new CliUsageError("A key never goes in a flag: pipe it on stdin or use --from-file.");
79706
80056
  }
79707
80057
  const file3 = flagText({ args, name: "from-file" });
79708
- const text2 = file3 ? await readFile23(resolve8(context.cwd, file3), "utf8") : await context.readStdin();
80058
+ const text2 = file3 ? await readFile24(resolve8(context.cwd, file3), "utf8") : await context.readStdin();
79709
80059
  const trimmed = text2.trim();
79710
80060
  if (!trimmed) throw new CliUsageError("No key: pipe it on stdin or use --from-file.");
79711
80061
  return trimmed;
@@ -80069,16 +80419,16 @@ function dndLine({ pausedUntil }) {
80069
80419
  }
80070
80420
 
80071
80421
  // src/services/dnd-commands.service.ts
80072
- var USAGE8 = "Usage: laze dnd 30m|1h|2h|until <HH:mm|ISO time> [--tz <zone>], laze dnd off";
80422
+ var USAGE9 = "Usage: laze dnd 30m|1h|2h|until <HH:mm|ISO time> [--tz <zone>], laze dnd off";
80073
80423
  async function dnd({ context, args }) {
80074
80424
  const timeZone = flagText({ args, name: "tz" }) || Intl.DateTimeFormat().resolvedOptions().timeZone;
80075
80425
  let input2;
80076
80426
  try {
80077
80427
  input2 = dndInputOf({ words: args.words.slice(1), now: nowInstant(), timeZone });
80078
80428
  } catch {
80079
- throw new CliUsageError(USAGE8);
80429
+ throw new CliUsageError(USAGE9);
80080
80430
  }
80081
- if (!input2) throw new CliUsageError(USAGE8);
80431
+ if (!input2) throw new CliUsageError(USAGE9);
80082
80432
  const { client } = await signedIn({ context, args });
80083
80433
  const settings2 = await client.post(
80084
80434
  "/api/agents/me/notifications/snooze",
@@ -80130,7 +80480,7 @@ async function domains({ context, args }) {
80130
80480
  }
80131
80481
 
80132
80482
  // src/services/feed-commands.service.ts
80133
- import { readFile as readFile25 } from "node:fs/promises";
80483
+ import { readFile as readFile26 } from "node:fs/promises";
80134
80484
  import { resolve as resolve10 } from "node:path";
80135
80485
 
80136
80486
  // src/rules/feed-input.rules.ts
@@ -80255,7 +80605,7 @@ function feedFlagsOf({ args }) {
80255
80605
  }
80256
80606
 
80257
80607
  // src/services/feed-files.service.ts
80258
- import { readFile as readFile24 } from "node:fs/promises";
80608
+ import { readFile as readFile25 } from "node:fs/promises";
80259
80609
  import { basename as basename4, resolve as resolve9 } from "node:path";
80260
80610
 
80261
80611
  // ../../modules/feed/contract/src/feed.ts
@@ -80830,7 +81180,7 @@ async function local({ context, path }) {
80830
81180
  return {
80831
81181
  fileName,
80832
81182
  contentType: contentTypeOf(fileName),
80833
- bytes: new Uint8Array(await readFile24(full))
81183
+ bytes: new Uint8Array(await readFile25(full))
80834
81184
  };
80835
81185
  }
80836
81186
  async function directWith({ client, to }) {
@@ -80868,7 +81218,7 @@ async function feedAttachmentIdsOf({
80868
81218
  }
80869
81219
 
80870
81220
  // src/services/feed-commands.service.ts
80871
- var USAGE9 = `Usage:
81221
+ var USAGE10 = `Usage:
80872
81222
  laze feed post <title> [--channel <channel>] [--tag <tag>] [--summary <line>]
80873
81223
  [--draft <text|@file>] [--draft-header "To: ...\\nRe: ..."]
80874
81224
  [--metric <json|@file.json> | --table <csv|@file.csv>]
@@ -80908,7 +81258,7 @@ async function bodyOf2({
80908
81258
  flags: flags2
80909
81259
  }) {
80910
81260
  const body = { ...flags2.fields };
80911
- const read3 = (source) => "file" in source ? readFile25(resolve10(context.cwd, source.file), "utf8") : source.text;
81261
+ const read3 = (source) => "file" in source ? readFile26(resolve10(context.cwd, source.file), "utf8") : source.text;
80912
81262
  for (const [field, source] of Object.entries(flags2.texts)) body[field] = await read3(source);
80913
81263
  if (flags2.rich) {
80914
81264
  const text2 = await read3(flags2.rich.source);
@@ -80937,7 +81287,7 @@ function printItem({
80937
81287
  }
80938
81288
  async function feedPost({ context, args }) {
80939
81289
  const title = args.words.slice(2).join(" ").trim();
80940
- if (!title) throw new CliUsageError(USAGE9);
81290
+ if (!title) throw new CliUsageError(USAGE10);
80941
81291
  const flags2 = flagsOf(args);
80942
81292
  const { client } = await signedIn({ context, args });
80943
81293
  const channel = flagText({ args, name: "channel" });
@@ -80959,7 +81309,7 @@ async function feedPost({ context, args }) {
80959
81309
  }
80960
81310
  async function feedUpdate({ context, args }) {
80961
81311
  const itemId = args.words[2];
80962
- if (!itemId) throw new CliUsageError(USAGE9);
81312
+ if (!itemId) throw new CliUsageError(USAGE10);
80963
81313
  const flags2 = flagsOf(args);
80964
81314
  const { client } = await signedIn({ context, args });
80965
81315
  const body = await bodyOf2({ context, flags: flags2 });
@@ -80975,7 +81325,7 @@ async function feedUpdate({ context, args }) {
80975
81325
  async function feedReply({ context, args }) {
80976
81326
  const itemId = args.words[2];
80977
81327
  const text2 = args.words.slice(3).join(" ").trim();
80978
- if (!itemId || !text2) throw new CliUsageError(USAGE9);
81328
+ if (!itemId || !text2) throw new CliUsageError(USAGE10);
80979
81329
  const { client } = await signedIn({ context, args });
80980
81330
  const replied = await client.post("/api/feed/items/reply", {
80981
81331
  itemId,
@@ -80987,7 +81337,7 @@ async function feedReply({ context, args }) {
80987
81337
  }
80988
81338
  async function feedRead({ context, args }) {
80989
81339
  const itemId = args.words[2];
80990
- if (!itemId) throw new CliUsageError(USAGE9);
81340
+ if (!itemId) throw new CliUsageError(USAGE10);
80991
81341
  const { client } = await signedIn({ context, args });
80992
81342
  const read3 = await client.post("/api/feed/items/read", { itemId });
80993
81343
  printItem({ context, args, item: read3 });
@@ -81006,7 +81356,7 @@ async function feedClose({ context, args }) {
81006
81356
  const itemId = args.words[2];
81007
81357
  const loop = flagText({ args, name: "loop" });
81008
81358
  const note = flagText({ args, name: "note" });
81009
- if (!note || Boolean(itemId) === Boolean(loop)) throw new CliUsageError(USAGE9);
81359
+ if (!note || Boolean(itemId) === Boolean(loop)) throw new CliUsageError(USAGE10);
81010
81360
  const { client } = await signedIn({ context, args });
81011
81361
  const closed = await client.post("/api/feed/items/close", {
81012
81362
  ...itemId ? { itemId } : { loop },
@@ -81032,7 +81382,7 @@ async function feedLoops({ context, args }) {
81032
81382
  }
81033
81383
  async function feedGroupSummary({ context, args }) {
81034
81384
  const group = args.words.slice(2).join(" ").trim();
81035
- if (!group) throw new CliUsageError(USAGE9);
81385
+ if (!group) throw new CliUsageError(USAGE10);
81036
81386
  const { client } = await signedIn({ context, args });
81037
81387
  const pointer = await client.post("/api/feed/group-summary", { group });
81038
81388
  if (args.flags.json) return context.out(`${JSON.stringify(pointer, null, 2)}
@@ -81070,7 +81420,7 @@ async function textFlag({
81070
81420
  }) {
81071
81421
  const value = flagText({ args, name: name2 });
81072
81422
  if (!value.startsWith("@")) return value;
81073
- return readFile25(resolve10(context.cwd, value.slice(1)), "utf8");
81423
+ return readFile26(resolve10(context.cwd, value.slice(1)), "utf8");
81074
81424
  }
81075
81425
  async function feedList({ context, args }) {
81076
81426
  const { client } = await signedIn({ context, args });
@@ -81102,7 +81452,7 @@ async function feedCount({ context, args }) {
81102
81452
  }
81103
81453
  async function feedAct({ context, args }) {
81104
81454
  const [itemId, actionId] = [args.words[2], args.words[3]];
81105
- if (!itemId || !actionId) throw new CliUsageError(USAGE9);
81455
+ if (!itemId || !actionId) throw new CliUsageError(USAGE10);
81106
81456
  const draft = await textFlag({ context, args, name: "draft" });
81107
81457
  const { client } = await signedIn({ context, args });
81108
81458
  const item = await client.post("/api/feed/mine/act", {
@@ -81115,7 +81465,7 @@ async function feedAct({ context, args }) {
81115
81465
  async function feedComment({ context, args }) {
81116
81466
  const itemId = args.words[2];
81117
81467
  const text2 = args.words.slice(3).join(" ").trim();
81118
- if (!itemId || !text2) throw new CliUsageError(USAGE9);
81468
+ if (!itemId || !text2) throw new CliUsageError(USAGE10);
81119
81469
  const draft = await textFlag({ context, args, name: "draft" });
81120
81470
  const { client } = await signedIn({ context, args });
81121
81471
  const item = await client.post("/api/feed/mine/comment", {
@@ -81129,7 +81479,7 @@ async function feedComment({ context, args }) {
81129
81479
  function moveItem(path) {
81130
81480
  return async ({ context, args }) => {
81131
81481
  const itemId = args.words[2];
81132
- if (!itemId) throw new CliUsageError(USAGE9);
81482
+ if (!itemId) throw new CliUsageError(USAGE10);
81133
81483
  const { client } = await signedIn({ context, args });
81134
81484
  const item = await client.post(`/api/feed/mine/${path}`, { itemId });
81135
81485
  printMoved({ context, args, item });
@@ -81138,14 +81488,14 @@ function moveItem(path) {
81138
81488
  async function feedSnooze({ context, args }) {
81139
81489
  const itemId = args.words[2];
81140
81490
  const until = flagText({ args, name: "until" });
81141
- if (!itemId || !until) throw new CliUsageError(USAGE9);
81491
+ if (!itemId || !until) throw new CliUsageError(USAGE10);
81142
81492
  const { client } = await signedIn({ context, args });
81143
81493
  const item = await client.post("/api/feed/mine/snooze", { itemId, until });
81144
81494
  printMoved({ context, args, item });
81145
81495
  }
81146
81496
  async function feedChannel({ context, args }) {
81147
81497
  const channelId = args.words[2];
81148
- if (!channelId) throw new CliUsageError(USAGE9);
81498
+ if (!channelId) throw new CliUsageError(USAGE10);
81149
81499
  const { client } = await signedIn({ context, args });
81150
81500
  const items = await client.post("/api/feed/channel", { channelId });
81151
81501
  if (args.flags.json) return context.out(`${JSON.stringify(items, null, 2)}
@@ -81175,12 +81525,12 @@ var FEED_COMMANDS = {
81175
81525
  async function feed({ context, args }) {
81176
81526
  const name2 = args.words[1] ?? "";
81177
81527
  const command = Object.hasOwn(FEED_COMMANDS, name2) ? FEED_COMMANDS[name2] : void 0;
81178
- if (!command) throw new CliUsageError(USAGE9);
81528
+ if (!command) throw new CliUsageError(USAGE10);
81179
81529
  await command({ context, args });
81180
81530
  }
81181
81531
 
81182
81532
  // src/services/github-commands.service.ts
81183
- import { readFile as readFile26 } from "node:fs/promises";
81533
+ import { readFile as readFile27 } from "node:fs/promises";
81184
81534
 
81185
81535
  // src/rules/github-signature.rules.ts
81186
81536
  var TURN_SEPARATOR = "~";
@@ -81250,7 +81600,7 @@ var REAL_GH = "/usr/bin/gh";
81250
81600
  var NOT_SET = "laze: agent_github_not_set: this agent has no GitHub connection. Its owner sets one on the agent's settings page or with `laze agent set-github <agent> <connection>`.";
81251
81601
  async function turnFileToken({ env }) {
81252
81602
  if (!env.LAZE_TURN_FILE) return "";
81253
- return readFile26(env.LAZE_TURN_FILE, "utf8").then(
81603
+ return readFile27(env.LAZE_TURN_FILE, "utf8").then(
81254
81604
  (text2) => text2.trim(),
81255
81605
  () => ""
81256
81606
  );
@@ -81284,7 +81634,7 @@ async function githubGh({
81284
81634
  args: args.rest,
81285
81635
  agentName: profile.name ?? "An agent",
81286
81636
  threadUrl: threadUrlOf({ env, workspaceId: profile.workspaceId }),
81287
- readBodyFile: (path) => path === "-" ? context.readStdin() : readFile26(path, "utf8")
81637
+ readBodyFile: (path) => path === "-" ? context.readStdin() : readFile27(path, "utf8")
81288
81638
  });
81289
81639
  return context.runCommand({
81290
81640
  command: [REAL_GH, ...ghArgs],
@@ -81383,7 +81733,7 @@ async function setAgentGithub({
81383
81733
  }
81384
81734
 
81385
81735
  // src/services/history-commands.service.ts
81386
- import { readFile as readFile27 } from "node:fs/promises";
81736
+ import { readFile as readFile28 } from "node:fs/promises";
81387
81737
 
81388
81738
  // src/rules/history-format.rules.ts
81389
81739
  function timeOf2(iso) {
@@ -81474,12 +81824,12 @@ function beforeOf({ args }) {
81474
81824
  throw new CliUsageError(HISTORY_USAGE);
81475
81825
  }
81476
81826
  }
81477
- async function turnTokenOf3({ context }) {
81827
+ async function turnTokenOf4({ context }) {
81478
81828
  const env = context.environment.inherited;
81479
81829
  const pinned = env.LAZE_TURN_TOKEN?.trim();
81480
81830
  if (pinned) return pinned;
81481
81831
  if (!env.LAZE_TURN_FILE) return "";
81482
- return readFile27(env.LAZE_TURN_FILE, "utf8").then(
81832
+ return readFile28(env.LAZE_TURN_FILE, "utf8").then(
81483
81833
  (text2) => text2.trim(),
81484
81834
  () => ""
81485
81835
  );
@@ -81493,7 +81843,7 @@ async function history({
81493
81843
  const limit = limitOf3({ args });
81494
81844
  const before = beforeOf({ args });
81495
81845
  const { client: signed2 } = await signedIn({ context, args });
81496
- const turn = await turnTokenOf3({ context });
81846
+ const turn = await turnTokenOf4({ context });
81497
81847
  const client = turn ? signed2.withHeaders({ "x-laze-turn": turn }) : signed2;
81498
81848
  const laze = await client.post("/api/agents/me/history", {
81499
81849
  channel,
@@ -81671,19 +82021,19 @@ async function ip({ context, args }) {
81671
82021
  }
81672
82022
 
81673
82023
  // src/services/join-commands.service.ts
81674
- import { mkdir as mkdir17, readFile as readFile28, rename as rename5, rm as rm10, writeFile as writeFile18 } from "node:fs/promises";
81675
- import { dirname as dirname17, join as join19 } from "node:path";
82024
+ import { mkdir as mkdir18, readFile as readFile29, rename as rename5, rm as rm11, writeFile as writeFile19 } from "node:fs/promises";
82025
+ import { dirname as dirname18, join as join20 } from "node:path";
81676
82026
  var DEFAULT_WAIT_SECONDS2 = 100;
81677
82027
  var JOIN_USAGE = "Usage: laze join [--workspace <name|id|invite link>] [--for <email>] [--name <name>] [--note <text>] [--timeout <seconds>] [--project <folder>]";
81678
82028
  function pendingPathOf({ context }) {
81679
- return join19(dirname17(context.environment.configPath), "join", "pending.json");
82029
+ return join20(dirname18(context.environment.configPath), "join", "pending.json");
81680
82030
  }
81681
82031
  async function readPending({
81682
82032
  context,
81683
82033
  url: url2,
81684
82034
  args
81685
82035
  }) {
81686
- const pending = await readFile28(pendingPathOf({ context }), "utf8").then(
82036
+ const pending = await readFile29(pendingPathOf({ context }), "utf8").then(
81687
82037
  (text2) => JSON.parse(text2),
81688
82038
  () => void 0
81689
82039
  );
@@ -81693,13 +82043,13 @@ async function readPending({
81693
82043
  }
81694
82044
  async function keepPending({ context, pending }) {
81695
82045
  const path = pendingPathOf({ context });
81696
- await mkdir17(dirname17(path), { recursive: true, mode: 448 });
82046
+ await mkdir18(dirname18(path), { recursive: true, mode: 448 });
81697
82047
  const temporary = `${path}.${process.pid}.tmp`;
81698
- await writeFile18(temporary, JSON.stringify(pending), { mode: 384 });
82048
+ await writeFile19(temporary, JSON.stringify(pending), { mode: 384 });
81699
82049
  await rename5(temporary, path);
81700
82050
  }
81701
82051
  async function dropPending({ context }) {
81702
- await rm10(pendingPathOf({ context }), { force: true });
82052
+ await rm11(pendingPathOf({ context }), { force: true });
81703
82053
  }
81704
82054
  function waitSecondsOf3({ args }) {
81705
82055
  const raw = flagText({ args, name: "timeout" });
@@ -82011,244 +82361,6 @@ function machineAccessCommand(name2) {
82011
82361
  return name2 && Object.hasOwn(COMMANDS, name2) ? COMMANDS[name2] : void 0;
82012
82362
  }
82013
82363
 
82014
- // src/services/vault-commands.service.ts
82015
- import { createHash as createHash2 } from "node:crypto";
82016
- import { mkdir as mkdir18, readFile as readFile29, rm as rm11, writeFile as writeFile19 } from "node:fs/promises";
82017
- import { tmpdir as tmpdir2 } from "node:os";
82018
- import { dirname as dirname18, join as join20 } from "node:path";
82019
- var USAGE10 = `Usage: laze vault run <connection> --reason "<one plain sentence>" [--wait <s>] -- <command> [args...]
82020
- Runs the command with the connection's values in its environment, on a Laze agent's computer
82021
- or on a machine it runs on.`;
82022
- var POLL_MS2 = 3e3;
82023
- var RESTART_WAIT_MS = 12e4;
82024
- var RESTART_BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 1e4];
82025
- var P2562 = { name: "ECDH", namedCurve: "P-256" };
82026
- var ServerGoneError = class extends Error {
82027
- };
82028
- function proxyOf({ env }) {
82029
- const raw = env.LAZE_VAULT_PROXY || env.HTTP_PROXY || env.http_proxy;
82030
- if (!raw) {
82031
- throw new CliUsageError(
82032
- "laze vault run works only on a Laze agent's computer or on a machine it runs on"
82033
- );
82034
- }
82035
- const url2 = new URL(raw);
82036
- const user = decodeURIComponent(url2.username);
82037
- const password = decodeURIComponent(url2.password);
82038
- return {
82039
- origin: url2.origin,
82040
- authorization: `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`
82041
- };
82042
- }
82043
- function turnFileOf({
82044
- env
82045
- }) {
82046
- return env.LAZE_TURN_FILE || void 0;
82047
- }
82048
- async function turnTokenOf4({
82049
- env,
82050
- turnFile,
82051
- command = "laze vault run"
82052
- }) {
82053
- const pinned = env.LAZE_TURN_TOKEN?.trim();
82054
- if (pinned) return pinned;
82055
- if (!turnFile) throw new CliUsageError(`${command} works only during an agent's turn`);
82056
- try {
82057
- return (await readFile29(turnFile, "utf8")).trim();
82058
- } catch {
82059
- throw new CliUsageError(`${command} works only during an agent's turn`);
82060
- }
82061
- }
82062
- function runKeyPathOf({
82063
- turnFile,
82064
- connection
82065
- }) {
82066
- const name2 = createHash2("sha256").update(connection).digest("hex");
82067
- const base = turnFile ? dirname18(turnFile) : join20(tmpdir2(), "laze-vault");
82068
- return join20(base, "vault-run-keys", `${name2}.json`);
82069
- }
82070
- async function runKeyAt({ path }) {
82071
- const saved3 = await readFile29(path, "utf8").then(
82072
- (text2) => JSON.parse(text2),
82073
- () => void 0
82074
- );
82075
- if (saved3 && typeof saved3.publicKey === "string" && saved3.privateKey) {
82076
- const privateKey = await globalThis.crypto.subtle.importKey("jwk", saved3.privateKey, P2562, false, ["deriveBits"]).catch(() => void 0);
82077
- if (privateKey) return { privateKey, publicKey: saved3.publicKey };
82078
- }
82079
- const fresh = await generateEcdhKeys();
82080
- const jwk = await globalThis.crypto.subtle.exportKey("jwk", fresh.privateKey);
82081
- await mkdir18(dirname18(path), { recursive: true, mode: 448 });
82082
- await writeFile19(path, JSON.stringify({ publicKey: fresh.publicKey, privateKey: jwk }), {
82083
- mode: 384
82084
- });
82085
- return fresh;
82086
- }
82087
- function connectionRefused(error62) {
82088
- const cause = error62.cause;
82089
- if (cause?.code === "ECONNREFUSED") return true;
82090
- return cause?.errors?.some((inner) => inner.code === "ECONNREFUSED") ?? false;
82091
- }
82092
- async function sendOnce({
82093
- context,
82094
- url: url2,
82095
- init
82096
- }) {
82097
- let response;
82098
- try {
82099
- response = await context.fetcher(url2, init);
82100
- } catch (error62) {
82101
- const wasRefused = connectionRefused(error62);
82102
- if (init.method === "POST" && !wasRefused) throw error62;
82103
- return { restarting: wasRefused ? "connection refused" : String(error62) };
82104
- }
82105
- if (response.status !== 502 && response.status !== 503) return { response };
82106
- const code = codeOf(await response.clone().text());
82107
- const neverReached = response.status === 503 && code === "vault_not_answering";
82108
- if (init.method === "POST" && !neverReached) return { response };
82109
- return { restarting: `status ${response.status}${code ? ` ${code}` : ""}` };
82110
- }
82111
- async function reach(input2) {
82112
- const { context } = input2;
82113
- const started = context.nowMs();
82114
- for (let attempt = 0; ; attempt += 1) {
82115
- const sent = await sendOnce(input2);
82116
- if ("response" in sent) return sent.response;
82117
- if (context.nowMs() - started >= RESTART_WAIT_MS) {
82118
- throw new ServerGoneError(
82119
- `laze vault: the Laze server did not come back (${sent.restarting})`
82120
- );
82121
- }
82122
- if (attempt === 0) {
82123
- context.err(
82124
- `laze vault: the Laze server is not answering (${sent.restarting}), probably restarting; waiting up to 2 min...
82125
- `
82126
- );
82127
- }
82128
- await context.sleep(RESTART_BACKOFF_MS[Math.min(attempt, RESTART_BACKOFF_MS.length - 1)] ?? 0);
82129
- }
82130
- }
82131
- function codeOf(text2) {
82132
- return /"code"\s*:\s*"([^"]+)"/.exec(text2)?.[1];
82133
- }
82134
- async function releaseOf({ response }) {
82135
- const text2 = await response.text();
82136
- if (!response.ok) {
82137
- return {
82138
- outcome: "deny",
82139
- exitCode: 1,
82140
- message: `laze vault: the release was refused (${codeOf(text2) ?? `status ${response.status}`})`
82141
- };
82142
- }
82143
- return JSON.parse(text2);
82144
- }
82145
- async function envOf({
82146
- release: release2,
82147
- run: run4
82148
- }) {
82149
- if (!release2.sealed) return release2.env;
82150
- const sealed = await openRunRelease({
82151
- release: release2.sealed,
82152
- runPrivateKey: run4.privateKey,
82153
- runPublicKey: run4.publicKey,
82154
- requestId: release2.sealed.requestId
82155
- });
82156
- return { ...release2.env, ...sealed };
82157
- }
82158
- function deadlineOf({ args, nowMs }) {
82159
- const wait = flagText({ args, name: "wait" }) || (args.flags.wait === true ? args.words[3] : "");
82160
- if (!wait) return Infinity;
82161
- if (!(Number(wait) >= 0)) throw new CliUsageError(`--wait takes a number of seconds
82162
- ${USAGE10}`);
82163
- return nowMs + Number(wait) * 1e3;
82164
- }
82165
- async function untilAnswered({
82166
- context,
82167
- first,
82168
- origin,
82169
- headers,
82170
- deadline
82171
- }) {
82172
- let release2 = first;
82173
- while (release2.outcome === "ask") {
82174
- if (context.nowMs() + POLL_MS2 > deadline) {
82175
- context.err(`${release2.message}. Not approved yet; ask again later.
82176
- `);
82177
- return "capped";
82178
- }
82179
- if (release2 === first) {
82180
- context.err(`${release2.message}. Waiting for an answer (cards expire after 12 hours)...
82181
- `);
82182
- }
82183
- await context.sleep(POLL_MS2);
82184
- const url2 = `${origin}/vault/release/${encodeURIComponent(release2.requestId)}`;
82185
- release2 = await releaseOf({
82186
- response: await reach({ context, url: url2, init: { method: "GET", headers } })
82187
- });
82188
- }
82189
- return release2;
82190
- }
82191
- async function vaultRelease({
82192
- context,
82193
- args,
82194
- connection,
82195
- reason,
82196
- command
82197
- }) {
82198
- const deadline = deadlineOf({ args, nowMs: context.nowMs() });
82199
- const env = context.environment.inherited;
82200
- const proxy = proxyOf({ env });
82201
- const turnFile = turnFileOf({ env });
82202
- const headers = {
82203
- "proxy-authorization": proxy.authorization,
82204
- "x-laze-turn": await turnTokenOf4({ env, turnFile })
82205
- };
82206
- const keyPath = runKeyPathOf({ turnFile, connection });
82207
- const run4 = await runKeyAt({ path: keyPath });
82208
- const body = JSON.stringify({ connection, reason, command, recipient: run4.publicKey });
82209
- let release2;
82210
- try {
82211
- const first = await releaseOf({
82212
- response: await reach({
82213
- context,
82214
- url: `${proxy.origin}/vault/release`,
82215
- init: { method: "POST", headers: { ...headers, "content-type": "application/json" }, body }
82216
- })
82217
- });
82218
- release2 = await untilAnswered({ context, first, origin: proxy.origin, headers, deadline });
82219
- } catch (error62) {
82220
- if (!(error62 instanceof ServerGoneError)) throw error62;
82221
- context.err(`${error62.message}. Run the command again to keep waiting.
82222
- `);
82223
- return { exitCode: 75 };
82224
- }
82225
- if (release2 === "capped") return { exitCode: 75 };
82226
- if (release2.outcome === "deny") {
82227
- await rm11(keyPath, { force: true });
82228
- context.err(`${release2.message}
82229
- `);
82230
- return { exitCode: release2.exitCode || 1 };
82231
- }
82232
- const values = await envOf({ release: release2, run: run4 });
82233
- await rm11(keyPath, { force: true });
82234
- if (release2.message) context.err(`${release2.message}
82235
- `);
82236
- return { env: values };
82237
- }
82238
- async function vaultRun({
82239
- context,
82240
- args
82241
- }) {
82242
- const connection = args.words[2];
82243
- const reason = flagText({ args, name: "reason" });
82244
- if (!connection || args.rest.length === 0) throw new CliUsageError(USAGE10);
82245
- if (!reason) throw new CliUsageError(`Say why with --reason "<one plain sentence>"
82246
- ${USAGE10}`);
82247
- const released = await vaultRelease({ context, args, connection, reason, command: args.rest });
82248
- if ("exitCode" in released) return released.exitCode;
82249
- return context.runCommand({ command: args.rest, env: released.env });
82250
- }
82251
-
82252
82364
  // src/services/machine-commands.service.ts
82253
82365
  var USAGE11 = [
82254
82366
  "Usage: laze machine connect <name> the command to run on your own machine (people)",
@@ -84665,7 +84777,7 @@ async function postInTurn({
84665
84777
  body
84666
84778
  }) {
84667
84779
  const env = context.environment.inherited;
84668
- const turn = await turnTokenOf4({
84780
+ const turn = await turnTokenOf3({
84669
84781
  env,
84670
84782
  turnFile: turnFileOf({ env }),
84671
84783
  command: "laze vault save"
@@ -86301,7 +86413,7 @@ var HELP = `laze: work in a Laze workspace from your terminal or your agent.
86301
86413
  laze vault scrub [--dry-run] move secrets found in messages to the vault now (admins)
86302
86414
  laze vault list|add|update|replace|compare|rm|revoke|uses your keychains with your own key
86303
86415
  a value comes from stdin or --from-file, never a flag; no command prints a value
86304
- laze vault grant|lend|grants|ungrant|leases|end-lease|limits|set-limit sharing, leases, limits
86416
+ laze vault grant|share|lend|grants|ungrant|leases|end-lease|limits|set-limit sharing, leases, limits
86305
86417
  laze vault audit|activity|most-asked|approvals|judge|devices what happened (audit: admins)
86306
86418
  laze vault every laze vault command, with its flags
86307
86419
  laze connect suggest <service> --reason "<why>" a connect card for whoever asked (agents)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazehq/cli",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "The Laze command line: sign in, read and post in chat, and connect your own Claude Code or Codex session as an agent.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",