@lazehq/cli 0.1.26 → 0.1.28

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 +274 -50
  2. package/package.json +1 -1
package/dist/laze.mjs CHANGED
@@ -30997,6 +30997,41 @@ function isBasicAuthPayload(value) {
30997
30997
  }
30998
30998
  var CREDENTIAL_QUALIFIERS = "master|encryption|signing|private|access|api|auth|secret|refresh|session|bearer|verification|webhook|client|service|personal|root|admin";
30999
30999
  var CREDENTIAL_KEYWORD = `(?:x[_.\\- ]?)?(?:(?:${CREDENTIAL_QUALIFIERS})[_.\\- ]?(?:api[_.\\- ]?)?key|(?:${CREDENTIAL_QUALIFIERS})?[_.\\- ]?(?:api[_.\\- ]?)?(?:token|secret|password|passwd|pwd|credentials?|authorization|cookie))`;
31000
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
31001
+ var GIT_SHA = /^[0-9a-f]{40}$/;
31002
+ var DOMAIN_PATH = /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}\//;
31003
+ var NAMED_KEY_MIN = 24;
31004
+ var NAMED_KEY_MAX = 256;
31005
+ function isSlug(value) {
31006
+ const parts = value.split(/[-_.]/);
31007
+ if (parts.length < 3) return false;
31008
+ const short = parts.every((part) => /^(?:[a-z0-9]{1,10}|[A-Z0-9]{1,10})$/.test(part));
31009
+ return short && parts.some((part) => /^[A-Za-z]{3,}$/.test(part));
31010
+ }
31011
+ var NOT_A_NAMED_KEY = [
31012
+ /^\/|\/\//,
31013
+ GIT_SHA,
31014
+ DOMAIN_PATH,
31015
+ PATH_LIKE,
31016
+ CODE_EXPRESSION,
31017
+ ENV_REFERENCE,
31018
+ VERSION_LIKE
31019
+ ];
31020
+ function isNamedKeyValue(value) {
31021
+ if (value.length < NAMED_KEY_MIN || value.length > NAMED_KEY_MAX) return false;
31022
+ if (UUID.test(value)) return true;
31023
+ if (isSlug(value)) return false;
31024
+ if (NOT_A_NAMED_KEY.some((shape) => shape.test(value))) return false;
31025
+ const prefix = /^([A-Za-z][A-Za-z0-9]{1,11})[_-]/.exec(value)?.[1];
31026
+ if (prefix && isNonCredentialPrefix(prefix)) return false;
31027
+ if (HEX.test(value)) return true;
31028
+ if (shannonEntropyBits(value) < 3.5) return false;
31029
+ const c = charClasses(value);
31030
+ return c.digit >= 2 && c.lower + c.upper >= 8 && (c.lower >= 2 || c.upper >= 2);
31031
+ }
31032
+ var KEY_WORD = String.raw`(?:api[_.\- ]?key|apikey|access[_.\- ]?key|key|token|secret|password|passwd|pwd)`;
31033
+ var QUOTE = "[\"'`]";
31034
+ var KEY_WORD_GAP = String.raw`(?:[ \t]{0,3}(?:is|was|here|it's|its|below|:|=|->|=>|-{1,2}))`;
31000
31035
  var TOKEN_START = "(?<![A-Za-z0-9_-])";
31001
31036
  var TOKEN_END = "(?![A-Za-z0-9_-])";
31002
31037
  var VENDOR_KEY_PATTERNS = [
@@ -31125,11 +31160,21 @@ var RULES = [
31125
31160
  "i"
31126
31161
  ),
31127
31162
  value: { kind: "group", group: 2 },
31163
+ dropsFullStop: true,
31128
31164
  accept: (match) => {
31129
31165
  const introduction = groupOf(match, 1);
31130
31166
  const value = groupOf(match, 2);
31131
31167
  return STRICT_INTRODUCTION.test(introduction) ? isCredentialValue(value) : isKeyMaterial(value);
31132
31168
  }
31169
+ },
31170
+ {
31171
+ id: "named_key",
31172
+ regex: rx(
31173
+ String.raw`\b(${KEY_WORD})\b(${KEY_WORD_GAP}{0,3}[ \t]*(?:\r?\n[ \t]*)?${QUOTE}?)` + String.raw`([A-Za-z0-9+/=_.-]{${NAMED_KEY_MIN - 1},${NAMED_KEY_MAX - 1}}[A-Za-z0-9+/=_-])` + String.raw`(?![A-Za-z0-9+/=_-]|\.[A-Za-z0-9])`,
31174
+ "i"
31175
+ ),
31176
+ value: { kind: "group", group: 3 },
31177
+ accept: (match) => /[\s:="'`]/.test(groupOf(match, 2)) && isNamedKeyValue(groupOf(match, 3))
31133
31178
  }
31134
31179
  ];
31135
31180
  var SECRET_RULE_IDS = RULES.map((rule) => rule.id);
@@ -31157,12 +31202,21 @@ function spanStartOf({ rule, text: text2, fullStart }) {
31157
31202
  const before = rule.precededBy.exec(text2.slice(from, fullStart));
31158
31203
  return before ? fullStart - before[0].length : fullStart;
31159
31204
  }
31205
+ function withoutFullStop({
31206
+ text: text2,
31207
+ span
31208
+ }) {
31209
+ let end = span.end;
31210
+ while (end > span.start && text2[end - 1] === "." && !/\S/.test(text2[end] ?? "")) end--;
31211
+ return end === span.end ? span : { start: span.start, end, spanEnd: Math.min(span.spanEnd, end) };
31212
+ }
31160
31213
  function rawMatches(rule, text2) {
31161
31214
  const out = [];
31162
31215
  rule.regex.lastIndex = 0;
31163
31216
  for (const match of text2.matchAll(rule.regex)) {
31164
31217
  if (rule.accept && !rule.accept(match)) continue;
31165
- const span = valueSpan(rule, match);
31218
+ const found = valueSpan(rule, match);
31219
+ const span = found && rule.dropsFullStop ? withoutFullStop({ text: text2, span: found }) : found;
31166
31220
  if (!span || span.end <= span.start) continue;
31167
31221
  const spanStart = spanStartOf({ rule, text: text2, fullStart: match.index });
31168
31222
  out.push({ ruleId: rule.id, spanStart, ...span });
@@ -31322,7 +31376,33 @@ function suggestedName({
31322
31376
  const line = lineBreak === -1 ? before : before.slice(lineBreak + 1);
31323
31377
  const assigned = ASSIGNMENT.exec(line)?.[1]?.toUpperCase();
31324
31378
  if (assigned && isValidSecretName(assigned)) return assigned;
31325
- return defaultName({ kind, value, before });
31379
+ const lead = before.replace(/\s+$/, "");
31380
+ const lastLine = lead.slice(lead.search(/[^\r\n]*$/));
31381
+ const named = KEY_WORD_KINDS.has(kind) ? keyWordName({ line: lastLine }) : null;
31382
+ return named ?? defaultName({ kind, value, before });
31383
+ }
31384
+ var KEY_WORD_KINDS = /* @__PURE__ */ new Set(["named_key", "sensitive_assignment"]);
31385
+ var VENDOR_KEY_WORD = /([A-Za-z][A-Za-z0-9]{1,30})[ _.-]+(?:(?:api|access)[ _.-]?)?(key|token|secret|password|passwd|pwd)\b/gi;
31386
+ var NOT_VENDOR_WORDS = new Set(
31387
+ "a an the my your our their his her its this that these new old here is of for with and or to my api access secret private public personal prod production live test dev staging master admin root bearer auth client service session refresh read write full main default".split(
31388
+ " "
31389
+ )
31390
+ );
31391
+ var KEY_WORD_SUFFIXES = {
31392
+ key: "_API_KEY",
31393
+ token: "_TOKEN",
31394
+ secret: "_SECRET",
31395
+ password: "_PASSWORD",
31396
+ passwd: "_PASSWORD",
31397
+ pwd: "_PASSWORD"
31398
+ };
31399
+ function keyWordName({ line }) {
31400
+ const last = [...line.slice(-80).matchAll(VENDOR_KEY_WORD)].at(-1);
31401
+ const vendor = last?.[1]?.toLowerCase();
31402
+ const word3 = last?.[2]?.toLowerCase();
31403
+ if (!vendor || !word3 || NOT_VENDOR_WORDS.has(vendor)) return null;
31404
+ const name2 = `${vendor.toUpperCase()}${KEY_WORD_SUFFIXES[word3] ?? "_KEY"}`;
31405
+ return isValidSecretName(name2) ? name2 : null;
31326
31406
  }
31327
31407
  var SAFE_CUT_LOOKAHEAD = 4096;
31328
31408
  var PEM_BEGIN = "-----BEGIN";
@@ -35426,6 +35506,8 @@ function cliEnvironment({
35426
35506
  } = {}) {
35427
35507
  return {
35428
35508
  configPath: env.LAZE_CLI_CONFIG || join(homedir(), ".laze", "config.json"),
35509
+ // Claude Code names a subagent's commands `claude-code_<version>_agent`.
35510
+ subagent: /^claude-code_.+_agent$/.test(env.AI_AGENT ?? ""),
35429
35511
  profile: env.LAZE_PROFILE || void 0,
35430
35512
  url: env.LAZE_URL || void 0,
35431
35513
  opsToken: env.LAZE_OPS_TOKEN || void 0,
@@ -35625,7 +35707,7 @@ function flagText({
35625
35707
  }
35626
35708
 
35627
35709
  // src/rules/cli-version.rules.ts
35628
- var STAMPED = "0.1.26";
35710
+ var STAMPED = "0.1.28";
35629
35711
  function cliVersionOf({ stamped = STAMPED } = {}) {
35630
35712
  return stamped.startsWith("__") ? "dev" : stamped;
35631
35713
  }
@@ -57848,6 +57930,17 @@ var chatScrubReportSchemaDefinition = external_exports.object({
57848
57930
  channels: external_exports.array(chatScrubChannelSchemaDefinition)
57849
57931
  });
57850
57932
  var chatScrubReportSchema = chatScrubReportSchemaDefinition;
57933
+ var saveMessageSecretBodySchemaDefinition = external_exports.object({
57934
+ /** One token, as keys are: a value with spaces is text, not a key. */
57935
+ value: external_exports.string().min(8).max(4096).regex(/^\S+$/),
57936
+ name: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/),
57937
+ description: external_exports.string().trim().min(1).max(120)
57938
+ });
57939
+ var messageSecretSavedSchemaDefinition = external_exports.object({
57940
+ outcome: external_exports.enum(["named", "already_named"]),
57941
+ name: external_exports.string(),
57942
+ connectionId: external_exports.string()
57943
+ });
57851
57944
 
57852
57945
  // ../../modules/chat/contract/src/chat.sent.ts
57853
57946
  var SENT_PAGE_MAX = 50;
@@ -62603,6 +62696,16 @@ var savePendingSecretBodySchemaDefinition = external_exports.object({
62603
62696
  name: connectionName2,
62604
62697
  description: external_exports.string().trim().min(1).max(120)
62605
62698
  });
62699
+ var saveMessageSecretInputSchemaDefinition = external_exports.object({
62700
+ workspaceId: external_exports.string().min(1),
62701
+ channelId: external_exports.string().min(1),
62702
+ messageId: external_exports.string().min(1),
62703
+ /** The message's author, a person: the keychain the value goes to. */
62704
+ ownerUserId: external_exports.string().min(1),
62705
+ value: external_exports.string().min(8).max(4096).regex(/^\S+$/),
62706
+ name: connectionName2,
62707
+ description: external_exports.string().trim().min(1).max(120)
62708
+ });
62606
62709
  var vaultPendingSecretSavedSchemaDefinition = external_exports.object({
62607
62710
  outcome: external_exports.enum(["named", "already_named"]),
62608
62711
  name: connectionName2,
@@ -66817,14 +66920,26 @@ async function liveElsewhere({
66817
66920
  }
66818
66921
  return false;
66819
66922
  }
66820
- function notBoundText({ meta: meta3 }) {
66821
- return `Laze: this agent listens in another session${meta3.bound ? `: ${meta3.bound}` : ""}. Create a new Laze agent with this session: laze agent request. Or move this agent here: laze agent bind.`;
66923
+ function refuseInSubagent({ context, args }) {
66924
+ if (!context.environment.subagent || flagText({ args, name: "session" })) return;
66925
+ throw new CliUsageError(
66926
+ "This runs inside a Claude Code subagent, which shares its parent's session: it would take that session. Run it in the session itself, or pass --session <id>."
66927
+ );
66928
+ }
66929
+ function notBoundText({
66930
+ meta: meta3,
66931
+ agent: agent2,
66932
+ configPath
66933
+ }) {
66934
+ const who = `${agent2 ? `"${agent2}"` : "this agent"} (config ${configPath})`;
66935
+ return `Laze: ${who} listens in another session${meta3.bound ? `: ${meta3.bound}` : ""}. Create a new Laze agent with this session: laze agent request. Or move this agent here: laze agent bind.`;
66822
66936
  }
66823
66937
  async function notBoundNotice({
66824
66938
  context,
66825
66939
  client,
66826
66940
  session,
66827
- error: error62
66941
+ error: error62,
66942
+ profile
66828
66943
  }) {
66829
66944
  if (!(error62 instanceof LazeApiError) || error62.code !== "session_not_bound") return void 0;
66830
66945
  const meta3 = error62.meta ?? {};
@@ -66844,7 +66959,10 @@ async function notBoundNotice({
66844
66959
  if (told) return { notice: void 0 };
66845
66960
  await mkdir3(dirname3(path), { recursive: true, mode: 448 });
66846
66961
  await writeFile3(path, String(context.nowMs()), { mode: 384 });
66847
- return { notice: notBoundText({ meta: meta3 }) };
66962
+ const agent2 = (await context.store.read()).profiles[profile]?.name;
66963
+ return {
66964
+ notice: notBoundText({ meta: meta3, agent: agent2, configPath: context.environment.configPath })
66965
+ };
66848
66966
  }
66849
66967
 
66850
66968
  // src/services/plugin-hook.service.ts
@@ -67676,7 +67794,8 @@ async function settleRound(round) {
67676
67794
  context,
67677
67795
  client: forSession({ client, session }),
67678
67796
  session,
67679
- error: error62
67797
+ error: error62,
67798
+ profile: name2
67680
67799
  });
67681
67800
  if (!refused) throw error62;
67682
67801
  return refused;
@@ -69321,6 +69440,7 @@ async function linkCodexSession({
69321
69440
 
69322
69441
  // src/services/laze-plugin.service.ts
69323
69442
  import { mkdir as mkdir11, readFile as readFile11, writeFile as writeFile11 } from "node:fs/promises";
69443
+ import { homedir as homedir3 } from "node:os";
69324
69444
  import { dirname as dirname11, join as join13 } from "node:path";
69325
69445
 
69326
69446
  // src/rules/laze-plugin.rules.ts
@@ -69461,15 +69581,7 @@ async function writeLazePlugin({ context }) {
69461
69581
  }
69462
69582
  return folder;
69463
69583
  }
69464
- async function registerWithClaude({ context, folder }) {
69465
- const quiet = ">/dev/null 2>&1 </dev/null";
69466
- const script = [
69467
- `claude plugin marketplace add ${quote2(folder)} --scope user ${quiet}`,
69468
- `claude plugin marketplace update ${LAZE_PLUGIN} ${quiet}`,
69469
- // install is a no-op for a plugin Claude Code already has; update refreshes its cached copy.
69470
- `{ claude plugin install ${LAZE_PLUGIN}@${LAZE_PLUGIN} --scope user ${quiet} || true; }`,
69471
- `claude plugin update ${LAZE_PLUGIN}@${LAZE_PLUGIN} ${quiet}`
69472
- ].join("; ");
69584
+ async function claudeScript({ context, script }) {
69473
69585
  const stop3 = new AbortController();
69474
69586
  const command = ["sh", "-c", script];
69475
69587
  const run4 = context.runCommand({ command, env: {}, signal: stop3.signal }).catch(() => 1);
@@ -69480,6 +69592,44 @@ async function registerWithClaude({ context, folder }) {
69480
69592
  stop3.abort();
69481
69593
  return code === 0;
69482
69594
  }
69595
+ var QUIET = ">/dev/null 2>&1 </dev/null";
69596
+ async function registerWithClaude({ context, folder }) {
69597
+ const script = [
69598
+ `claude plugin marketplace add ${quote2(folder)} --scope user ${QUIET}`,
69599
+ `claude plugin marketplace update ${LAZE_PLUGIN} ${QUIET}`,
69600
+ // install is a no-op for a plugin Claude Code already has; update refreshes its cached copy.
69601
+ `{ claude plugin install ${LAZE_PLUGIN}@${LAZE_PLUGIN} --scope user ${QUIET} || true; }`,
69602
+ `claude plugin update ${LAZE_PLUGIN}@${LAZE_PLUGIN} ${QUIET}`
69603
+ ].join("; ");
69604
+ return claudeScript({ context, script });
69605
+ }
69606
+ var RUN_FILES = ["hooks/hooks.json", "monitors/monitors.json"];
69607
+ async function cachedPluginOf({ context }) {
69608
+ const inherited = context.environment.inherited;
69609
+ const home = inherited.HOME || homedir3();
69610
+ const claude = inherited.CLAUDE_CONFIG_DIR || join13(home, ".claude");
69611
+ const listed = await readFile11(join13(claude, "plugins", "installed_plugins.json"), "utf8").then(
69612
+ (text2) => JSON.parse(text2),
69613
+ () => void 0
69614
+ );
69615
+ return listed?.plugins?.[`${LAZE_PLUGIN}@${LAZE_PLUGIN}`]?.[0]?.installPath;
69616
+ }
69617
+ async function refreshStaleCache({ context, folder }) {
69618
+ const cached2 = await cachedPluginOf({ context });
69619
+ if (!cached2) return true;
69620
+ const read3 = (path) => readFile11(path, "utf8").catch(() => "");
69621
+ for (const file3 of RUN_FILES) {
69622
+ const ours = await read3(join13(folder, LAZE_PLUGIN, file3));
69623
+ if (ours === await read3(join13(cached2, file3))) continue;
69624
+ const plugin2 = `${LAZE_PLUGIN}@${LAZE_PLUGIN}`;
69625
+ const script = [
69626
+ `claude plugin uninstall ${plugin2} --scope user ${QUIET}`,
69627
+ `claude plugin install ${plugin2} --scope user ${QUIET}`
69628
+ ].join("; ");
69629
+ return claudeScript({ context, script });
69630
+ }
69631
+ return true;
69632
+ }
69483
69633
  function registeredMarkOf({ context }) {
69484
69634
  return join13(pluginFolderOf({ context }), "registered");
69485
69635
  }
@@ -69491,7 +69641,7 @@ async function pluginRegistered({ context }) {
69491
69641
  }
69492
69642
  async function installLazePlugin({ context }) {
69493
69643
  const folder = await writeLazePlugin({ context });
69494
- const registered = await registerWithClaude({ context, folder });
69644
+ const registered = await registerWithClaude({ context, folder }) && await refreshStaleCache({ context, folder });
69495
69645
  if (registered) await writeFile11(registeredMarkOf({ context }), cliVersionOf());
69496
69646
  return { folder, registered };
69497
69647
  }
@@ -69840,6 +69990,7 @@ function sandboxed({ context, error: error62 }) {
69840
69990
  async function linkAgent({ context, args }) {
69841
69991
  const token2 = flagText({ args, name: "token" });
69842
69992
  if (!token2) throw new CliUsageError("Usage: laze agent link --token <token>");
69993
+ refuseInSubagent({ context, args });
69843
69994
  const url2 = serverUrlOf({ context, args });
69844
69995
  const { harness, pid } = await harnessOf2({ context, args });
69845
69996
  const client = LazeClient.create({ baseUrl: url2, fetcher: context.fetcher });
@@ -69989,7 +70140,13 @@ async function handOver({ context, args }) {
69989
70140
  try {
69990
70141
  fresh = await receiveMessages({ context, client: claim3, waitSeconds: 0 });
69991
70142
  } catch (error62) {
69992
- const refused = await notBoundNotice({ context, client: claim3, session, error: error62 });
70143
+ const refused = await notBoundNotice({
70144
+ context,
70145
+ client: claim3,
70146
+ session,
70147
+ error: error62,
70148
+ profile: agent2
70149
+ });
69993
70150
  if (!refused) throw error62;
69994
70151
  if (refused.notice) stopContext({ context, text: refused.notice });
69995
70152
  return;
@@ -70723,17 +70880,30 @@ async function setAgentPool({
70723
70880
 
70724
70881
  // src/services/agent-session-commands.service.ts
70725
70882
  var NO_SESSION = "Run this inside the Claude Code or Codex session the agent should listen in, or pass --session <id>.";
70883
+ var NO_AGENT = "This session and its folder have no agent: name one with `laze agent bind --profile <name>`.";
70726
70884
  var DEFAULT_WAIT_SECONDS = 300;
70727
70885
  async function bindAgentSession({
70728
70886
  context,
70729
70887
  args
70730
70888
  }) {
70889
+ refuseInSubagent({ context, args });
70731
70890
  const session = await currentSessionOf({ context, args });
70732
70891
  if (!session) throw new CliUsageError(NO_SESSION);
70733
- const { client, name: name2 } = await signedIn({ context, args });
70892
+ const key = sessionKeyOf({ session });
70893
+ const config2 = await context.store.read();
70894
+ const held = config2.sessions?.[key];
70895
+ const own2 = held && config2.profiles[held] ? held : void 0;
70896
+ const explicit = flagText({ args, name: "profile" }) || void 0;
70897
+ const target = explicit ?? own2 ?? boundProfileOf({ bindings: config2.bindings, cwd: context.cwd });
70898
+ if (!target) throw new CliUsageError(NO_AGENT);
70899
+ const named = { ...args, flags: { ...args.flags, profile: target } };
70900
+ const { client, name: name2, profile } = await signedIn({ context, args: named });
70734
70901
  await forSession({ client, session }).post("/api/agents/me/bind", {});
70735
- await context.store.bindSession({ session: sessionKeyOf({ session }), profile: name2 });
70736
- context.out("This agent now listens in this session.\n");
70902
+ await context.store.bindSession({ session: key, profile: name2 });
70903
+ const where = `profile ${name2}, config ${context.environment.configPath}`;
70904
+ const from = own2 && own2 !== name2 ? ` It moved from "${config2.profiles[own2]?.name}".` : "";
70905
+ context.out(`"${profile.name}" now listens in this session (${where}).${from}
70906
+ `);
70737
70907
  }
70738
70908
  async function mintedRequestProfile({
70739
70909
  context,
@@ -71898,7 +72068,9 @@ var appPinSchemaDefinition = external_exports.object({
71898
72068
  pinnedByName: external_exports.string().nullable(),
71899
72069
  pinnedAt: external_exports.iso.datetime(),
71900
72070
  /** Set for a browser tab, null for an app. */
71901
- browser: browserPinSchemaDefinition.nullable()
72071
+ browser: browserPinSchemaDefinition.nullable(),
72072
+ /** Whether the reader may unpin it; a browser tab only its owner, their agents or an Admin. */
72073
+ canUnpin: external_exports.boolean().default(true)
71902
72074
  });
71903
72075
  var appPinViewSchemaDefinition = external_exports.object({
71904
72076
  activeAppId: external_exports.string().nullable(),
@@ -72404,6 +72576,7 @@ var AppNavSlot = appUi.component("nav");
72404
72576
  var AppPanelSlot = appUi.component("panel");
72405
72577
  var AppCardSlot = appUi.component("card");
72406
72578
  var AppPinStripSlot = appUi.component("pin-strip");
72579
+ var AppPinListSlot = appUi.component("pin-list");
72407
72580
 
72408
72581
  // ../../modules/app/contract/src/app.config.ts
72409
72582
  var appConfig = Config.define((c) => ({
@@ -76347,7 +76520,13 @@ async function monitorRound(turn) {
76347
76520
  const named = { ...turn.args, flags: { ...turn.args.flags, profile: turn.profile } };
76348
76521
  const { client } = await signedIn({ context, args: named });
76349
76522
  const claim3 = forSession({ client, session });
76350
- const refused = await notBoundNotice({ context, client: claim3, session, error: error62 });
76523
+ const refused = await notBoundNotice({
76524
+ context,
76525
+ client: claim3,
76526
+ session,
76527
+ error: error62,
76528
+ profile: turn.profile
76529
+ });
76351
76530
  if (refused?.rebound) return true;
76352
76531
  if (refused?.notice) context.out(`${refused.notice}
76353
76532
  `);
@@ -81431,6 +81610,7 @@ var MCP_REFUSED_COMMANDS = /* @__PURE__ */ new Set([
81431
81610
  "vault add",
81432
81611
  "vault replace",
81433
81612
  "vault compare",
81613
+ "vault save-from-message",
81434
81614
  "connect mcp-key",
81435
81615
  "connect card-key"
81436
81616
  ]);
@@ -83502,15 +83682,20 @@ async function update2({
83502
83682
  return 0;
83503
83683
  }
83504
83684
 
83505
- // src/services/vault-save-commands.service.ts
83506
- var USAGE18 = `Usage: laze vault save <pending> --name <name> --description "<what it is for>"
83507
- Names a key Laze held from a message you received, in the sender's vault.`;
83685
+ // src/services/vault-save-message-commands.service.ts
83686
+ var SAVE_USAGE = `Usage: laze vault save <pending> --name <name> --description "<what it is for>"
83687
+ Names a key Laze held from a message you received, in the sender's vault.
83688
+ or: laze vault save --from-message <message-id> --name <name> --description "<what>" <<'EOF'
83689
+ Saves a key a person sent in that message, which Laze did not hold, to their own vault: the key
83690
+ on stdin, exactly as it is in the message. The message then shows {{vault:<name>}}. Run it in
83691
+ your turn in the message's conversation; it needs no vault access. MCP cannot send stdin.`;
83508
83692
  var KEPT_IN_LAZE = "The key never left Laze: the chat, its history and your session hold only a reference, so there is nothing to rotate. Tell the person that.";
83509
- async function vaultSave({ context, args }) {
83510
- const pending = args.words[2];
83511
- const name2 = flagText({ args, name: "name" });
83512
- const description = flagText({ args, name: "description" });
83513
- if (!pending || !name2 || !description) throw new CliUsageError(USAGE18);
83693
+ async function postInTurn({
83694
+ context,
83695
+ args,
83696
+ path,
83697
+ body
83698
+ }) {
83514
83699
  const env = context.environment.inherited;
83515
83700
  const turn = await turnTokenOf4({
83516
83701
  env,
@@ -83518,11 +83703,13 @@ async function vaultSave({ context, args }) {
83518
83703
  command: "laze vault save"
83519
83704
  });
83520
83705
  const { client } = await signedIn({ context, args });
83521
- const saved3 = await client.withHeaders({ "x-laze-turn": turn }).post("/api/vault/pending-secrets/save", {
83522
- pending,
83523
- name: name2,
83524
- description
83525
- });
83706
+ return client.withHeaders({ "x-laze-turn": turn }).post(path, body);
83707
+ }
83708
+ function printSaved({
83709
+ context,
83710
+ args,
83711
+ saved: saved3
83712
+ }) {
83526
83713
  if (args.flags.json) return context.out(`${JSON.stringify(saved3, null, 2)}
83527
83714
  `);
83528
83715
  const done = saved3.outcome === "named" ? "Saved as" : "Already named";
@@ -83532,6 +83719,40 @@ ${KEPT_IN_LAZE}
83532
83719
  `
83533
83720
  );
83534
83721
  }
83722
+ async function vaultSaveFromMessage({
83723
+ context,
83724
+ args
83725
+ }) {
83726
+ const messageId = flagText({ args, name: "from-message" });
83727
+ const name2 = flagText({ args, name: "name" });
83728
+ const description = flagText({ args, name: "description" });
83729
+ if (!messageId || !name2 || !description) throw new CliUsageError(SAVE_USAGE);
83730
+ const value = (await context.readStdin()).trim();
83731
+ if (!value) throw new CliUsageError(SAVE_USAGE);
83732
+ const saved3 = await postInTurn({
83733
+ context,
83734
+ args,
83735
+ path: `/api/chat/messages/${encodeURIComponent(messageId)}/save-secret`,
83736
+ body: { value, name: name2, description }
83737
+ });
83738
+ printSaved({ context, args, saved: saved3 });
83739
+ }
83740
+
83741
+ // src/services/vault-save-commands.service.ts
83742
+ async function vaultSave({ context, args }) {
83743
+ if (flagText({ args, name: "from-message" })) return vaultSaveFromMessage({ context, args });
83744
+ const pending = args.words[2];
83745
+ const name2 = flagText({ args, name: "name" });
83746
+ const description = flagText({ args, name: "description" });
83747
+ if (!pending || !name2 || !description) throw new CliUsageError(SAVE_USAGE);
83748
+ const saved3 = await postInTurn({
83749
+ context,
83750
+ args,
83751
+ path: "/api/vault/pending-secrets/save",
83752
+ body: { pending, name: name2, description }
83753
+ });
83754
+ printSaved({ context, args, saved: saved3 });
83755
+ }
83535
83756
 
83536
83757
  // src/services/vault-scrub-commands.service.ts
83537
83758
  function lineOf3(channel) {
@@ -84265,7 +84486,7 @@ var PATHS3 = {
84265
84486
  var STATUS_WAIT_SECONDS2 = 50;
84266
84487
  var STATUS_CALLS2 = 2;
84267
84488
  var SETTLED = /* @__PURE__ */ new Set(["sent", "denied", "expired", "failed"]);
84268
- var USAGE19 = [
84489
+ var USAGE18 = [
84269
84490
  "Usage: laze whatsapp chats <machine> [--limit <n>] the chats on the machine, newest first",
84270
84491
  " laze whatsapp read <machine> <chat> [--limit <n>] a chat's last messages, oldest first",
84271
84492
  ' laze whatsapp send <machine> <chat> "<text>" send as the owner; an agent waits for their approval',
@@ -84275,14 +84496,14 @@ var USAGE19 = [
84275
84496
  ].join("\n");
84276
84497
  function word2({ args, at }) {
84277
84498
  const value = args.words[at];
84278
- if (!value) throw new CliUsageError(USAGE19);
84499
+ if (!value) throw new CliUsageError(USAGE18);
84279
84500
  return value;
84280
84501
  }
84281
84502
  function limitOf5({ args }) {
84282
84503
  const text2 = flagText({ args, name: "limit" });
84283
84504
  if (!text2) return {};
84284
84505
  const limit = Number(text2);
84285
- if (!Number.isInteger(limit) || limit < 1) throw new CliUsageError(USAGE19);
84506
+ if (!Number.isInteger(limit) || limit < 1) throw new CliUsageError(USAGE18);
84286
84507
  return { limit };
84287
84508
  }
84288
84509
  async function chats({ context, args }) {
@@ -84344,7 +84565,7 @@ Still waiting. Run \`laze whatsapp status ${send4.id}\` later to see the answer.
84344
84565
  }
84345
84566
  async function send3({ context, args }) {
84346
84567
  const text2 = args.words.slice(4).join(" ") || args.rest.join(" ");
84347
- if (!text2) throw new CliUsageError(USAGE19);
84568
+ if (!text2) throw new CliUsageError(USAGE18);
84348
84569
  const client = await computeClient({ context, args });
84349
84570
  const asked = await client.post(PATHS3.send, {
84350
84571
  machine: word2({ args, at: 2 }),
@@ -84374,11 +84595,11 @@ async function whatsapp({
84374
84595
  if (command === "status") return status7({ context, args });
84375
84596
  const linking = whatsappPairingCommand(command);
84376
84597
  if (linking) return linking({ context, args });
84377
- throw new CliUsageError(USAGE19);
84598
+ throw new CliUsageError(USAGE18);
84378
84599
  }
84379
84600
 
84380
84601
  // src/services/wiki-commands.service.ts
84381
- var USAGE20 = `Usage:
84602
+ var USAGE19 = `Usage:
84382
84603
  laze wiki list
84383
84604
  laze wiki get <path>
84384
84605
  laze wiki put <path> <markdown|@file|-> [--title <title>] [--channel <channel>] [--base <version>]
@@ -84398,7 +84619,7 @@ async function wikiList({ context, args }) {
84398
84619
  }
84399
84620
  async function wikiGet({ context, args }) {
84400
84621
  const path = args.words[2];
84401
- if (!path) throw new CliUsageError(USAGE20);
84622
+ if (!path) throw new CliUsageError(USAGE19);
84402
84623
  const { client } = await signedIn({ context, args });
84403
84624
  const page = await client.post("/api/wiki/get", { path });
84404
84625
  if (args.flags.json) return context.out(`${JSON.stringify(page, null, 2)}
@@ -84415,7 +84636,7 @@ Linked from: ${page.backlinks.map((link) => link.path).join(", ")}
84415
84636
  async function wikiPut({ context, args }) {
84416
84637
  const path = args.words[2];
84417
84638
  const value = args.words.slice(3).join(" ");
84418
- if (!path || !value) throw new CliUsageError(USAGE20);
84639
+ if (!path || !value) throw new CliUsageError(USAGE19);
84419
84640
  const body = await textArgument({ context, value });
84420
84641
  const title = flagText({ args, name: "title" });
84421
84642
  const channel = flagText({ args, name: "channel" });
@@ -84435,7 +84656,7 @@ async function wikiPut({ context, args }) {
84435
84656
  }
84436
84657
  async function wikiSearch({ context, args }) {
84437
84658
  const query = args.words.slice(2).join(" ").trim();
84438
- if (!query) throw new CliUsageError(USAGE20);
84659
+ if (!query) throw new CliUsageError(USAGE19);
84439
84660
  const { client } = await signedIn({ context, args });
84440
84661
  const hits = await client.post("/api/wiki/search", { query });
84441
84662
  if (args.flags.json) return context.out(`${JSON.stringify(hits, null, 2)}
@@ -84445,7 +84666,7 @@ async function wikiSearch({ context, args }) {
84445
84666
  }
84446
84667
  async function wikiHistory({ context, args }) {
84447
84668
  const path = args.words[2];
84448
- if (!path) throw new CliUsageError(USAGE20);
84669
+ if (!path) throw new CliUsageError(USAGE19);
84449
84670
  const { client } = await signedIn({ context, args });
84450
84671
  const revisions = await client.post("/api/wiki/history", { path });
84451
84672
  if (args.flags.json) return context.out(`${JSON.stringify(revisions, null, 2)}
@@ -84459,7 +84680,7 @@ async function wikiHistory({ context, args }) {
84459
84680
  }
84460
84681
  async function wikiMove({ context, args }) {
84461
84682
  const [, , path, to] = args.words;
84462
- if (!path || !to) throw new CliUsageError(USAGE20);
84683
+ if (!path || !to) throw new CliUsageError(USAGE19);
84463
84684
  const { client } = await signedIn({ context, args });
84464
84685
  const page = await client.post("/api/wiki/move", { path, to });
84465
84686
  if (args.flags.json) return context.out(`${JSON.stringify(page, null, 2)}
@@ -84469,7 +84690,7 @@ async function wikiMove({ context, args }) {
84469
84690
  }
84470
84691
  async function wikiRemove({ context, args }) {
84471
84692
  const path = args.words[2];
84472
- if (!path) throw new CliUsageError(USAGE20);
84693
+ if (!path) throw new CliUsageError(USAGE19);
84473
84694
  const { client } = await signedIn({ context, args });
84474
84695
  const removed = await client.post("/api/wiki/remove", { path });
84475
84696
  if (args.flags.json) return context.out(`${JSON.stringify(removed)}
@@ -84489,7 +84710,7 @@ var WIKI_COMMANDS = {
84489
84710
  async function wiki({ context, args }) {
84490
84711
  const name2 = args.words[1] ?? "";
84491
84712
  const command = Object.hasOwn(WIKI_COMMANDS, name2) ? WIKI_COMMANDS[name2] : void 0;
84492
- if (!command) throw new CliUsageError(USAGE20);
84713
+ if (!command) throw new CliUsageError(USAGE19);
84493
84714
  await command({ context, args });
84494
84715
  }
84495
84716
 
@@ -85107,6 +85328,8 @@ var HELP = `laze: work in a Laze workspace from your terminal or your agent.
85107
85328
  laze adopt codex --status | --stop the background adopt that laze agent link started
85108
85329
  laze vault run <name> --reason "<why>" -- <cmd> run a command with a vault connection (agents)
85109
85330
  laze vault save <pending> --name <name> --description "<text>" name a key Laze held from a message (agents)
85331
+ laze vault save --from-message <id> --name <name> --description "<text>" save a key a person sent,
85332
+ read from stdin, to their own vault; the message keeps {{vault:<name>}} (agents, no vault access)
85110
85333
  laze vault scrub [--dry-run] move secrets found in messages to the vault now (admins)
85111
85334
  laze vault list|add|update|replace|compare|rm|revoke|uses your keychains with your own key
85112
85335
  a value comes from stdin or --from-file, never a flag; no command prints a value
@@ -85262,6 +85485,7 @@ var COMMANDS3 = {
85262
85485
  app,
85263
85486
  "vault run": vaultRun,
85264
85487
  "vault save": vaultSave,
85488
+ "vault save-from-message": vaultSaveFromMessage,
85265
85489
  connect,
85266
85490
  slack,
85267
85491
  "connect providers": connectAsPerson,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazehq/cli",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
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",