@lazehq/cli 0.1.2 → 0.1.3

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 (3) hide show
  1. package/README.md +9 -5
  2. package/dist/laze.mjs +1256 -227
  3. package/package.json +1 -1
package/dist/laze.mjs CHANGED
@@ -6,9 +6,323 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/main.ts
9
- import { spawn as spawn2 } from "node:child_process";
10
9
  import { stdin } from "node:process";
11
10
 
11
+ // ../../packages/secret-guard/src/detector/secret-detector.ts
12
+ var REDACTION_MARKER = "[SECRET]";
13
+ function charClasses(value) {
14
+ let lower = 0;
15
+ let upper = 0;
16
+ let digit = 0;
17
+ for (let index = 0; index < value.length; index++) {
18
+ const code = value.charCodeAt(index);
19
+ if (code >= 97 && code <= 122) lower++;
20
+ else if (code >= 65 && code <= 90) upper++;
21
+ else if (code >= 48 && code <= 57) digit++;
22
+ }
23
+ return { lower, upper, digit };
24
+ }
25
+ function shannonEntropyBits(value) {
26
+ const sample = value.slice(0, 256).split("");
27
+ if (sample.length === 0) return 0;
28
+ const counts = /* @__PURE__ */ new Map();
29
+ for (const char of sample) counts.set(char, (counts.get(char) ?? 0) + 1);
30
+ let entropy = 0;
31
+ for (const count2 of counts.values()) {
32
+ const p = count2 / sample.length;
33
+ entropy -= p * Math.log2(p);
34
+ }
35
+ return entropy;
36
+ }
37
+ var SHAPED_MIN_BODY = 26;
38
+ var SHAPED_MAX_BODY = 120;
39
+ function isKeyShapedBody(body) {
40
+ if (body.length < SHAPED_MIN_BODY || body.length > SHAPED_MAX_BODY) return false;
41
+ const c = charClasses(body);
42
+ if (c.lower < 2 || c.upper < 2 || c.digit < 2) return false;
43
+ return shannonEntropyBits(body) >= 3.9;
44
+ }
45
+ var DIGEST_PREFIXES = new Set(
46
+ "sha1 sha224 sha256 sha384 sha512 sha3 md4 md5 blake2b blake2s blake3 crc32 xxh3 xxh64 base32 base58 base64 hex uuid urn cid etag hash digest checksum integrity".split(
47
+ " "
48
+ )
49
+ );
50
+ var HEX_BODY_CREDENTIAL_SEGMENTS = ["live", "test", "prod", "sk", "pk", "key", "secret", "token"];
51
+ var IDENTIFIER_PREFIXES = new Set(
52
+ "commit sha sha1 sha256 md5 hash digest trace span id uuid rev blob tree etag checksum".split(
53
+ " "
54
+ )
55
+ );
56
+ var RECORD_ID_PREFIXES = new Set(
57
+ "project provider card eval monitor scenario ses sess session thread conv span trace run msg task job step node team org user call req resp chatcmpl toolu asst file batch evt acct cus sub".split(
58
+ " "
59
+ )
60
+ );
61
+ var PUBLIC_KEY_PREFIXES = /* @__PURE__ */ new Set(["phc"]);
62
+ function isNonCredentialPrefix(prefix) {
63
+ const lower = prefix.toLowerCase();
64
+ return DIGEST_PREFIXES.has(lower) || IDENTIFIER_PREFIXES.has(lower) || RECORD_ID_PREFIXES.has(lower) || PUBLIC_KEY_PREFIXES.has(lower);
65
+ }
66
+ var PLACEHOLDER_VALUE = /^(?:x+|\*+|\.+|-+|_+|0+|(?:your|my|our|insert|replace|example|sample|dummy|fake|placeholder|changeme|redacted|removed|hidden|none|null|nil|undefined|todo|tbd|fixme)[a-z0-9_\- ]*)$/i;
67
+ var ENV_REFERENCE = /^(?:\$[A-Za-z_][A-Za-z0-9_]*|[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+)$/;
68
+ var CODE_EXPRESSION = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
69
+ var PATH_LIKE = /^[~.]{0,2}\/|\/[^/\s]*\.[a-z]{1,5}$/;
70
+ var URL_LIKE = /^[a-z][a-z0-9+.-]*:\/\//i;
71
+ var VERSION_LIKE = /^v?\d+(?:[._-]\d+)+/;
72
+ var HEX = /^[0-9a-f]{32,}$/i;
73
+ var BASE32 = /^[A-Z2-7]{32,}={0,6}$/;
74
+ var STRICT_INTRODUCTION = /[:=]\s*["'`]?$/;
75
+ var NOT_A_CREDENTIAL = [PLACEHOLDER_VALUE, ENV_REFERENCE, CODE_EXPRESSION, PATH_LIKE, URL_LIKE];
76
+ function isCredentialValue(value) {
77
+ if (value.length < 16) return false;
78
+ const looksLikeSomethingElse = NOT_A_CREDENTIAL.some((shape) => shape.test(value));
79
+ if (looksLikeSomethingElse) return false;
80
+ if (value.includes(REDACTION_MARKER)) return false;
81
+ return shannonEntropyBits(value) >= 2.9;
82
+ }
83
+ function isKeyMaterial(value) {
84
+ if (value.length < 20 || !isCredentialValue(value) || VERSION_LIKE.test(value)) return false;
85
+ if (shannonEntropyBits(value) < 3.4) return false;
86
+ if (HEX.test(value) || BASE32.test(value)) return true;
87
+ const c = charClasses(value);
88
+ return c.digit >= 2 && (c.lower >= 2 || c.upper >= 2);
89
+ }
90
+ function isBasicAuthPayload(value) {
91
+ if (value.endsWith("=")) return true;
92
+ const c = charClasses(value);
93
+ return c.digit > 0 || c.lower > 0 && c.upper > 0;
94
+ }
95
+ var CREDENTIAL_QUALIFIERS = "master|encryption|signing|private|access|api|auth|secret|refresh|session|bearer|verification|webhook|client|service|personal|root|admin";
96
+ var CREDENTIAL_KEYWORD = `(?:x[_.\\- ]?)?(?:(?:${CREDENTIAL_QUALIFIERS})[_.\\- ]?(?:api[_.\\- ]?)?key|(?:${CREDENTIAL_QUALIFIERS})?[_.\\- ]?(?:api[_.\\- ]?)?(?:token|secret|password|passwd|pwd|credentials?|authorization|cookie))`;
97
+ var TOKEN_START = "(?<![A-Za-z0-9_-])";
98
+ var TOKEN_END = "(?![A-Za-z0-9_-])";
99
+ var VENDOR_KEY_PATTERNS = [
100
+ String.raw`(?:sk|ik|pat|vk)-lw-[A-Za-z0-9_-]{3,}`,
101
+ String.raw`gl(?:pat|rt|dt|soat|ptt|cbt|imt|agent|ffct)-[A-Za-z0-9_-]{20,}`,
102
+ String.raw`npm_[A-Za-z0-9]{36}`,
103
+ String.raw`GOCSPX-[A-Za-z0-9_-]{20,}`,
104
+ String.raw`mb_[A-Za-z0-9+/=]{40,}`,
105
+ String.raw`dckr_pat_[A-Za-z0-9_-]{20,}`,
106
+ String.raw`shp(?:at|ss|ca|pa)_[0-9a-fA-F]{32}`,
107
+ String.raw`SG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}`,
108
+ String.raw`hf_[A-Za-z0-9]{30,}`,
109
+ String.raw`gsk_[A-Za-z0-9]{40,}`,
110
+ String.raw`pplx-[A-Za-z0-9]{30,}`,
111
+ String.raw`nvapi-[A-Za-z0-9_-]{40,}`,
112
+ String.raw`r8_[A-Za-z0-9]{30,}`,
113
+ String.raw`xai-[A-Za-z0-9]{40,}`,
114
+ String.raw`ntn_[A-Za-z0-9]{30,}`,
115
+ String.raw`secret_[A-Za-z0-9]{40,}`,
116
+ String.raw`dop_v1_[0-9a-f]{64}`,
117
+ String.raw`figd_[A-Za-z0-9_-]{30,}`,
118
+ String.raw`ATATT[A-Za-z0-9_=-]{100,}`,
119
+ String.raw`sq0(?:atp|csp)-[A-Za-z0-9_-]{20,}`,
120
+ String.raw`EAAG[A-Za-z0-9]{60,}`,
121
+ String.raw`key-[0-9a-f]{32}`,
122
+ String.raw`re_[A-Za-z0-9_-]{20,}`,
123
+ String.raw`phx_[A-Za-z0-9]{30,}`,
124
+ String.raw`lin_api_[A-Za-z0-9]{30,}`,
125
+ String.raw`sl\.[A-Za-z0-9_-]{60,}`,
126
+ String.raw`ya29\.[A-Za-z0-9_-]{20,}`,
127
+ String.raw`sbp_[0-9a-f]{40,}`,
128
+ String.raw`sntry(?:s|u)_[A-Za-z0-9_.-]{30,}`,
129
+ String.raw`fw_[A-Za-z0-9]{20,}`,
130
+ String.raw`gl(?:c|sa)_[A-Za-z0-9]{30,}`,
131
+ String.raw`NRAK-[A-Za-z0-9]{20,}`,
132
+ String.raw`PMAK-[A-Za-z0-9]{20,}-[A-Za-z0-9]{20,}`,
133
+ String.raw`dp\.(?:pt|st|ct|sa)\.[A-Za-z0-9_-]{20,}`,
134
+ String.raw`pat[A-Za-z0-9]{14}\.[0-9a-f]{64}`,
135
+ String.raw`[0-9]{8,10}:AA[A-Za-z0-9_-]{33}`
136
+ ];
137
+ function rx(pattern, flags = "") {
138
+ return new RegExp(pattern, `gd${flags}`);
139
+ }
140
+ function groupOf(match, index) {
141
+ return match[index] ?? "";
142
+ }
143
+ var RULES = [
144
+ {
145
+ id: "pem_private_key",
146
+ regex: rx(
147
+ String.raw`-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY(?: BLOCK)?-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY(?: BLOCK)?-----`
148
+ )
149
+ },
150
+ { id: "putty_private_key", regex: rx(String.raw`PuTTY-User-Key-File-\d+:[\s\S]*?(?:\n\s*\n|$)`) },
151
+ {
152
+ id: "kubeconfig_client_credentials",
153
+ regex: rx(String.raw`\b(client-(?:key|certificate)-data:\s*)[A-Za-z0-9+/=]{40,}`),
154
+ value: { kind: "after", group: 1 }
155
+ },
156
+ {
157
+ id: "aws_access_key_id",
158
+ regex: rx(String.raw`\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA)[0-9A-Z]{16}\b`)
159
+ },
160
+ {
161
+ id: "github_token",
162
+ regex: rx(String.raw`\b(?:gh[posru]_[A-Za-z0-9]{36,}|github_pat_[A-Za-z0-9_]{60,})\b`)
163
+ },
164
+ { id: "provider_api_key", regex: rx(String.raw`\bsk-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])`) },
165
+ { id: "stripe_secret_key", regex: rx(String.raw`\b[rs]k_(?:live|test)_[A-Za-z0-9]{16,}\b`) },
166
+ { id: "slack_token", regex: rx(String.raw`\bxox[abposr]-[A-Za-z0-9-]{10,}\b`) },
167
+ { id: "google_api_key", regex: rx(String.raw`\bAIza[0-9A-Za-z_-]{35}\b`) },
168
+ {
169
+ id: "vendor_api_key",
170
+ regex: rx(`${TOKEN_START}(?:${VENDOR_KEY_PATTERNS.join("|")})${TOKEN_END}`)
171
+ },
172
+ {
173
+ id: "jwt",
174
+ regex: rx(String.raw`\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b`)
175
+ },
176
+ {
177
+ id: "url_credentials",
178
+ regex: rx(String.raw`(?<=[a-z][a-z0-9+.-]{0,30})(:\/\/[^\s:@/]+:)([^\s:@/]+)(@)`, "i"),
179
+ value: { kind: "group", group: 2 },
180
+ precededBy: /[a-z][a-z0-9+.-]{0,30}$/i
181
+ },
182
+ {
183
+ id: "bearer_token",
184
+ regex: rx(String.raw`\b(Bearer\s+)[A-Za-z0-9._~+/-]{10,}=*`, "i"),
185
+ value: { kind: "after", group: 1 }
186
+ },
187
+ {
188
+ id: "authorization_scheme_token",
189
+ regex: rx(
190
+ String.raw`\b(Authorization:\s*(?:Token|SSWS|GenieKey|Splunk|OAuth)\s+)[A-Za-z0-9._~+/-]{10,}=*`,
191
+ "i"
192
+ ),
193
+ value: { kind: "after", group: 1 }
194
+ },
195
+ {
196
+ id: "basic_auth_credentials",
197
+ regex: rx(String.raw`\b(Basic\s+)([A-Za-z0-9+/]{16,}={0,2})(?![A-Za-z0-9+/=])`),
198
+ value: { kind: "after", group: 1 },
199
+ accept: (match) => isBasicAuthPayload(groupOf(match, 2))
200
+ },
201
+ {
202
+ id: "prefixed_hex_api_key",
203
+ regex: rx(
204
+ `${TOKEN_START}([A-Za-z][A-Za-z0-9]{1,11})_(?:${HEX_BODY_CREDENTIAL_SEGMENTS.join("|")})_([0-9a-f]{24,128})${TOKEN_END}`,
205
+ "i"
206
+ ),
207
+ accept: (match) => !IDENTIFIER_PREFIXES.has(groupOf(match, 1).toLowerCase()),
208
+ precondition: (text2) => text2.includes("_")
209
+ },
210
+ {
211
+ id: "shaped_api_key",
212
+ regex: rx(
213
+ `${TOKEN_START}([A-Za-z][A-Za-z0-9]{1,11})[_-]([A-Za-z0-9_+/-]{${SHAPED_MIN_BODY},})${TOKEN_END}`
214
+ ),
215
+ accept: (match) => !isNonCredentialPrefix(groupOf(match, 1)) && isKeyShapedBody(groupOf(match, 2)),
216
+ precondition: (text2) => text2.includes("_") || text2.includes("-")
217
+ },
218
+ {
219
+ id: "sensitive_assignment",
220
+ regex: rx(
221
+ String.raw`((?:^|[\W_])(?:${CREDENTIAL_KEYWORD})(?:\s+[A-Za-z]{1,8}){0,2}["'` + "`" + String.raw`]?(?:\s*[:=]{1,2}\s*|[ \t?-]+)["'` + "`" + String.raw`]?)([^\s"'` + "`" + String.raw`,;<>(){}\[\]\\]{16,})`,
222
+ "i"
223
+ ),
224
+ value: { kind: "group", group: 2 },
225
+ accept: (match) => {
226
+ const introduction = groupOf(match, 1);
227
+ const value = groupOf(match, 2);
228
+ return STRICT_INTRODUCTION.test(introduction) ? isCredentialValue(value) : isKeyMaterial(value);
229
+ }
230
+ }
231
+ ];
232
+ var SECRET_RULE_IDS = RULES.map((rule) => rule.id);
233
+
234
+ // ../../packages/secret-guard/src/redaction/redactor.ts
235
+ var REDACTED = "****";
236
+ var MIN_REDACTED_LENGTH = 4;
237
+ function redactionVariants({ values }) {
238
+ const variants = /* @__PURE__ */ new Set();
239
+ for (const value of values) {
240
+ if (value.length < MIN_REDACTED_LENGTH) continue;
241
+ variants.add(value);
242
+ variants.add(Buffer.from(value, "utf8").toString("base64"));
243
+ variants.add(encodeURIComponent(value));
244
+ }
245
+ return [...variants].toSorted((a, b) => b.length - a.length);
246
+ }
247
+ function escapeRegExp(value) {
248
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
249
+ }
250
+ function createRedactor({ values }) {
251
+ const variants = redactionVariants({ values });
252
+ if (variants.length === 0) return { push: (chunk) => chunk, flush: () => "" };
253
+ const pattern = new RegExp(variants.map(escapeRegExp).join("|"), "g");
254
+ const hold = Math.max(...variants.map((variant) => variant.length)) - 1;
255
+ let carry = "";
256
+ const scan = (text2, limit) => {
257
+ let out = "";
258
+ let at = 0;
259
+ pattern.lastIndex = 0;
260
+ for (const match of text2.matchAll(pattern)) {
261
+ if (match.index >= limit) break;
262
+ if (match.index < at) continue;
263
+ out += text2.slice(at, match.index) + REDACTED;
264
+ at = match.index + match[0].length;
265
+ }
266
+ const emitUntil = Math.max(at, limit);
267
+ carry = text2.slice(emitUntil);
268
+ return out + text2.slice(at, emitUntil);
269
+ };
270
+ return {
271
+ push(chunk) {
272
+ const text2 = carry + chunk;
273
+ return scan(text2, Math.max(0, text2.length - hold));
274
+ },
275
+ flush() {
276
+ const text2 = carry;
277
+ const out = scan(text2, text2.length);
278
+ carry = "";
279
+ return out;
280
+ }
281
+ };
282
+ }
283
+
284
+ // ../../packages/secret-guard/src/run/run-redacted.ts
285
+ import { spawn } from "node:child_process";
286
+ import { StringDecoder } from "node:string_decoder";
287
+ function runRedacted({
288
+ command,
289
+ env,
290
+ baseEnv,
291
+ stdout,
292
+ stderr,
293
+ cwd
294
+ }) {
295
+ const values = Object.values(env);
296
+ return new Promise((resolve6, reject) => {
297
+ const [file2, ...args] = command;
298
+ const child = spawn(file2, args, {
299
+ env: { ...baseEnv, ...env },
300
+ stdio: ["inherit", "pipe", "pipe"],
301
+ ...cwd ? { cwd } : {}
302
+ });
303
+ const pipe2 = (stream, sink) => {
304
+ const decoder = new StringDecoder("utf8");
305
+ const redactor = createRedactor({ values });
306
+ stream.on("data", (chunk) => sink.write(redactor.push(decoder.write(chunk))));
307
+ return new Promise(
308
+ (done) => stream.on("end", () => {
309
+ sink.write(redactor.push(decoder.end()) + redactor.flush());
310
+ done();
311
+ })
312
+ );
313
+ };
314
+ const drained = Promise.all([pipe2(child.stdout, stdout), pipe2(child.stderr, stderr)]);
315
+ child.on("error", reject);
316
+ child.on("close", (code, signal) => {
317
+ void drained.then(() => resolve6(code ?? (signal ? 128 + signalNumber(signal) : 1)));
318
+ });
319
+ });
320
+ }
321
+ function signalNumber(signal) {
322
+ const numbers = { SIGINT: 2, SIGKILL: 9, SIGTERM: 15 };
323
+ return numbers[signal] ?? 1;
324
+ }
325
+
12
326
  // ../../node_modules/.pnpm/temporal-polyfill@1.0.4/node_modules/temporal-polyfill/chunks/root.js
13
327
  var NativeTemporal = globalThis.Temporal;
14
328
 
@@ -18,7 +332,7 @@ var expectedFinite = (entityName, num) => `Non-finite ${entityName}: ${num}`;
18
332
  var forbiddenBigIntToNumber = (entityName) => `Cannot convert bigint to ${entityName}`;
19
333
  var invalidObject = "Invalid object";
20
334
  var numberOutOfRange = (entityName, val, min, max) => invalidEntity(entityName, val) + `; must be between ${min}-${max}`;
21
- var invalidEntity = (fieldName2, val) => `Invalid ${fieldName2}: ${val}`;
335
+ var invalidEntity = (fieldName3, val) => `Invalid ${fieldName3}: ${val}`;
22
336
 
23
337
  // ../../node_modules/.pnpm/temporal-utils@1.0.2/node_modules/temporal-utils/dist/utils.js
24
338
  var nanoInMicro = 1e3;
@@ -69,10 +383,10 @@ function requireObjectLike(arg) {
69
383
 
70
384
  // ../../node_modules/.pnpm/temporal-polyfill@1.0.4/node_modules/temporal-polyfill/chunks/internal.js
71
385
  var invalidEntity2 = invalidEntity;
72
- var missingField = (fieldName2) => `Missing ${fieldName2}`;
386
+ var missingField = (fieldName3) => `Missing ${fieldName3}`;
73
387
  var noValidFields = (validFields) => "No valid fields: " + validFields.join();
74
388
  var invalidBag = "Invalid bag";
75
- var invalidChoice = (fieldName2, val, choiceMap) => invalidEntity(fieldName2, val) + "; must be " + Object.keys(choiceMap).join();
389
+ var invalidChoice = (fieldName3, val, choiceMap) => invalidEntity(fieldName3, val) + "; must be " + Object.keys(choiceMap).join();
76
390
  var forbiddenValueOf = "Cannot use valueOf";
77
391
  var invalidCallingContext = "Invalid calling context";
78
392
  var missingYear = (allowEra) => "Missing year" + (allowEra ? "/era/eraYear" : "");
@@ -324,8 +638,8 @@ var maxValues = {
324
638
  };
325
639
  function constrainTimeFields(timeFields, overflow) {
326
640
  const constrainedFields = {};
327
- for (const fieldName2 of timeFieldNamesAsc) {
328
- constrainedFields[fieldName2] = clampEntity(fieldName2, timeFields[fieldName2], 0, maxValues[fieldName2] || 999, overflow);
641
+ for (const fieldName3 of timeFieldNamesAsc) {
642
+ constrainedFields[fieldName3] = clampEntity(fieldName3, timeFields[fieldName3], 0, maxValues[fieldName3] || 999, overflow);
329
643
  }
330
644
  return constrainedFields;
331
645
  }
@@ -1479,15 +1793,15 @@ function negateDuration(slots) {
1479
1793
  }
1480
1794
  function negateDurationFields(fields) {
1481
1795
  const res = {};
1482
- for (const fieldName2 of durationFieldNamesAsc) {
1483
- res[fieldName2] = -1 * fields[fieldName2] || 0;
1796
+ for (const fieldName3 of durationFieldNamesAsc) {
1797
+ res[fieldName3] = -1 * fields[fieldName3] || 0;
1484
1798
  }
1485
1799
  return res;
1486
1800
  }
1487
1801
  function computeDurationSign(fields, fieldNames = durationFieldNamesAsc) {
1488
1802
  let sign = 0;
1489
- for (const fieldName2 of fieldNames) {
1490
- const fieldSign = Math.sign(fields[fieldName2]);
1803
+ for (const fieldName3 of fieldNames) {
1804
+ const fieldSign = Math.sign(fields[fieldName3]);
1491
1805
  fieldSign && (sign && sign !== fieldSign && throwRangeError("Cannot mix duration signs"), sign = fieldSign);
1492
1806
  }
1493
1807
  return sign;
@@ -1675,14 +1989,14 @@ var zonedDateTimeFieldRefiners = {
1675
1989
  function readAndRefineBagFields(bag, validFieldNames, fieldRefiners, requiredFieldNames, disallowEmpty = !requiredFieldNames) {
1676
1990
  const res = {};
1677
1991
  let anyMatching = 0;
1678
- for (const fieldName2 of validFieldNames) {
1679
- let fieldVal = bag[fieldName2];
1992
+ for (const fieldName3 of validFieldNames) {
1993
+ let fieldVal = bag[fieldName3];
1680
1994
  if (void 0 !== fieldVal) {
1681
1995
  anyMatching = 1;
1682
- const refiner = fieldRefiners[fieldName2];
1683
- refiner && (fieldVal = refiner(fieldVal, fieldName2)), res[fieldName2] = fieldVal;
1996
+ const refiner = fieldRefiners[fieldName3];
1997
+ refiner && (fieldVal = refiner(fieldVal, fieldName3)), res[fieldName3] = fieldVal;
1684
1998
  } else {
1685
- requiredFieldNames && requiredFieldNames.includes(fieldName2) && throwTypeError(missingField(fieldName2));
1999
+ requiredFieldNames && requiredFieldNames.includes(fieldName3) && throwTypeError(missingField(fieldName3));
1686
2000
  }
1687
2001
  }
1688
2002
  return disallowEmpty && !anyMatching && throwTypeError(noValidFields(validFieldNames)), res;
@@ -3857,7 +4171,8 @@ var SWITCHES = /* @__PURE__ */ new Set([
3857
4171
  "once",
3858
4172
  "urgent",
3859
4173
  "reopen",
3860
- "ask-before-push"
4174
+ "ask-before-push",
4175
+ "dry-run"
3861
4176
  ]);
3862
4177
  function parseArgs({ argv }) {
3863
4178
  const words = [];
@@ -3895,17 +4210,18 @@ function flagText({
3895
4210
  }
3896
4211
 
3897
4212
  // src/rules/cli-version.rules.ts
3898
- var STAMPED = "0.1.2";
4213
+ var STAMPED = "0.1.3";
3899
4214
  function cliVersionOf({ stamped = STAMPED } = {}) {
3900
4215
  return stamped.startsWith("__") ? "dev" : stamped;
3901
4216
  }
3902
4217
 
3903
4218
  // src/services/agent-commands.service.ts
3904
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
3905
- import { join as join2 } from "node:path";
4219
+ import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
4220
+ import { join as join3 } from "node:path";
3906
4221
 
3907
4222
  // src/rules/cli-invocation.rules.ts
3908
4223
  var NPX_CACHE = /[\\/]_npx[\\/]/;
4224
+ var SANDBOX_CLI_PATH = "/opt/laze/laze.mjs";
3909
4225
  function lazeCommandOf({ program }) {
3910
4226
  const script = program.at(-1) ?? "";
3911
4227
  if (!NPX_CACHE.test(script)) return "laze";
@@ -3914,24 +4230,35 @@ function lazeCommandOf({ program }) {
3914
4230
 
3915
4231
  // src/rules/hook-settings.rules.ts
3916
4232
  var LAZE_HOOK_MARKER = "laze-cli-hook";
3917
- function withStopHook({
3918
- settings,
4233
+ var TIMEOUTS = { Stop: 30, PreToolUse: 5 };
4234
+ function withHook({
4235
+ hooks,
4236
+ event,
3919
4237
  command
3920
4238
  }) {
3921
- const hooks = { ...settings.hooks };
3922
- const stop = Array.isArray(hooks.Stop) ? hooks.Stop : [];
3923
- const others = stop.filter(
4239
+ const current = Array.isArray(hooks[event]) ? hooks[event] : [];
4240
+ const others = current.filter(
3924
4241
  (entry) => !entry.hooks?.some((hook) => hook.command?.includes(LAZE_HOOK_MARKER))
3925
4242
  );
3926
- hooks.Stop = [...others, { hooks: [{ type: "command", command, timeout: 30 }] }];
4243
+ hooks[event] = [...others, { hooks: [{ type: "command", command, timeout: TIMEOUTS[event] }] }];
4244
+ }
4245
+ function withLazeHooks({
4246
+ settings,
4247
+ stopCommand,
4248
+ preToolCommand
4249
+ }) {
4250
+ const hooks = { ...settings.hooks };
4251
+ withHook({ hooks, event: "Stop", command: stopCommand });
4252
+ withHook({ hooks, event: "PreToolUse", command: preToolCommand });
3927
4253
  return { ...settings, hooks };
3928
4254
  }
3929
- function stopHookCommand({
4255
+ function hookCommand({
3930
4256
  program,
3931
- profile
4257
+ profile,
4258
+ hook
3932
4259
  }) {
3933
4260
  const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
3934
- const words = [...program, "hook", "stop", "--profile", profile].map(quote).join(" ");
4261
+ const words = [...program, "hook", hook, "--profile", profile].map(quote).join(" ");
3935
4262
  return `${words} # ${LAZE_HOOK_MARKER}`;
3936
4263
  }
3937
4264
 
@@ -4023,20 +4350,16 @@ function readTranscript({ lines }) {
4023
4350
  return { contextTokens, contextWindow, compactions };
4024
4351
  }
4025
4352
 
4026
- // src/services/cli-context.ts
4027
- var CliUsageError = class extends Error {
4028
- constructor(message) {
4029
- super(message);
4030
- this.name = "CliUsageError";
4031
- }
4032
- };
4353
+ // src/services/claude-turn.service.ts
4354
+ import { mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "node:fs/promises";
4355
+ import { dirname as dirname2, join as join2 } from "node:path";
4033
4356
 
4034
4357
  // src/services/laze-client.service.ts
4035
4358
  var CLI_VERSION_HEADER = "x-laze-cli-version";
4036
4359
  var LazeApiError = class extends Error {
4037
- constructor(status, code, message) {
4360
+ constructor(status2, code, message) {
4038
4361
  super(message);
4039
- this.status = status;
4362
+ this.status = status2;
4040
4363
  this.code = code;
4041
4364
  this.name = "LazeApiError";
4042
4365
  }
@@ -4044,20 +4367,20 @@ var LazeApiError = class extends Error {
4044
4367
  code;
4045
4368
  };
4046
4369
  var LazeClient = class _LazeClient {
4047
- constructor(baseUrl, token, fetcher) {
4048
- this.baseUrl = baseUrl;
4049
- this.token = token;
4050
- this.fetcher = fetcher;
4051
- }
4052
- baseUrl;
4053
- token;
4054
- fetcher;
4370
+ constructor(options) {
4371
+ this.options = options;
4372
+ }
4373
+ options;
4055
4374
  static create({
4056
4375
  baseUrl,
4057
4376
  token,
4058
4377
  fetcher
4059
4378
  }) {
4060
- return new _LazeClient(baseUrl.replace(/\/+$/, ""), token, fetcher);
4379
+ return new _LazeClient({ baseUrl: baseUrl.replace(/\/+$/, ""), token, fetcher, headers: {} });
4380
+ }
4381
+ /** The same client, sending these headers too on every call. */
4382
+ withHeaders(headers) {
4383
+ return new _LazeClient({ ...this.options, headers: { ...this.options.headers, ...headers } });
4061
4384
  }
4062
4385
  get(path) {
4063
4386
  return this.send({ method: "GET", path });
@@ -4071,12 +4394,13 @@ var LazeClient = class _LazeClient {
4071
4394
  body
4072
4395
  }) {
4073
4396
  const headers = {
4397
+ ...this.options.headers,
4074
4398
  accept: "application/json",
4075
4399
  [CLI_VERSION_HEADER]: cliVersionOf()
4076
4400
  };
4077
4401
  if (body !== void 0) headers["content-type"] = "application/json";
4078
- if (this.token) headers.authorization = `Bearer ${this.token}`;
4079
- const response = await this.fetcher(`${this.baseUrl}${path}`, {
4402
+ if (this.options.token) headers.authorization = `Bearer ${this.options.token}`;
4403
+ const response = await this.options.fetcher(`${this.options.baseUrl}${path}`, {
4080
4404
  method,
4081
4405
  headers,
4082
4406
  ...body !== void 0 ? { body: JSON.stringify(body) } : {}
@@ -4087,15 +4411,23 @@ var LazeClient = class _LazeClient {
4087
4411
  return parsed;
4088
4412
  }
4089
4413
  };
4090
- function errorOf({ status, body }) {
4414
+ function errorOf({ status: status2, body }) {
4091
4415
  const record4 = typeof body === "object" && body !== null ? body : {};
4092
4416
  const nested = typeof record4.error === "object" && record4.error !== null ? record4.error : record4;
4093
- const fallback = typeof record4.error === "string" ? record4.error : `http_${status}`;
4417
+ const fallback = typeof record4.error === "string" ? record4.error : `http_${status2}`;
4094
4418
  const code = typeof nested.code === "string" ? nested.code : fallback;
4095
- const message = typeof nested.message === "string" ? nested.message : `Laze answered ${status}`;
4096
- return new LazeApiError(status, code, message);
4419
+ const message = typeof nested.message === "string" ? nested.message : `Laze answered ${status2}`;
4420
+ return new LazeApiError(status2, code, message);
4097
4421
  }
4098
4422
 
4423
+ // src/services/cli-context.ts
4424
+ var CliUsageError = class extends Error {
4425
+ constructor(message) {
4426
+ super(message);
4427
+ this.name = "CliUsageError";
4428
+ }
4429
+ };
4430
+
4099
4431
  // src/services/session.service.ts
4100
4432
  async function signedIn({
4101
4433
  context,
@@ -4126,6 +4458,85 @@ function serverUrlOf({ context, args }) {
4126
4458
  return flagText({ args, name: "url" }) || context.environment.url || "https://laze.langwatch.ai";
4127
4459
  }
4128
4460
 
4461
+ // src/services/claude-turn.service.ts
4462
+ var PRE_TOOL_TIMEOUT_MS = 3e3;
4463
+ function statePath({ context, agent }) {
4464
+ const safe = agent.replace(/[^a-zA-Z0-9_-]/g, "_");
4465
+ return join2(dirname2(context.environment.configPath), "turns", `${safe}.json`);
4466
+ }
4467
+ async function readState({ context, agent }) {
4468
+ return readFile2(statePath({ context, agent }), "utf8").then(
4469
+ (text2) => JSON.parse(text2),
4470
+ () => void 0
4471
+ );
4472
+ }
4473
+ function beat({
4474
+ client,
4475
+ state,
4476
+ working
4477
+ }) {
4478
+ const reported = working ? "working" : "idle";
4479
+ return client.post("/api/agents/me/turn", {
4480
+ channelId: state?.channelId ?? null,
4481
+ threadRootId: state?.threadRootId ?? null,
4482
+ state: state ? reported : null
4483
+ });
4484
+ }
4485
+ async function turnStarted({
4486
+ context,
4487
+ client,
4488
+ agent,
4489
+ messages
4490
+ }) {
4491
+ const last = messages.at(-1);
4492
+ if (!last) return;
4493
+ const state = { channelId: last.channelId, threadRootId: last.threadRootId ?? last.messageId };
4494
+ const path = statePath({ context, agent });
4495
+ await mkdir2(dirname2(path), { recursive: true, mode: 448 });
4496
+ await writeFile2(path, JSON.stringify(state));
4497
+ await beat({ client, state, working: true }).catch(() => void 0);
4498
+ }
4499
+ async function turnEnded({
4500
+ context,
4501
+ client,
4502
+ agent
4503
+ }) {
4504
+ const state = await readState({ context, agent });
4505
+ if (!state) return;
4506
+ await rm(statePath({ context, agent }), { force: true });
4507
+ await beat({ client, state, working: false }).catch(() => void 0);
4508
+ }
4509
+ async function preToolHook({ context, args }) {
4510
+ try {
4511
+ await context.readStdin();
4512
+ const { name: name2, profile } = await signedIn({ context, args });
4513
+ const client = LazeClient.create({
4514
+ baseUrl: profile.url,
4515
+ token: profile.token,
4516
+ fetcher: (input2, init) => context.fetcher(input2, { ...init, signal: AbortSignal.timeout(PRE_TOOL_TIMEOUT_MS) })
4517
+ });
4518
+ const state = await readState({ context, agent: name2 });
4519
+ const answer3 = await beat({ client, state, working: true });
4520
+ if (!answer3.stop) return;
4521
+ await turnEnded({ context, client, agent: name2 });
4522
+ const reason = `Stopped by ${answer3.stoppedBy ?? "someone"} in Laze.`;
4523
+ context.out(
4524
+ JSON.stringify({
4525
+ continue: false,
4526
+ stopReason: reason,
4527
+ hookSpecificOutput: {
4528
+ hookEventName: "PreToolUse",
4529
+ permissionDecision: "deny",
4530
+ permissionDecisionReason: reason
4531
+ }
4532
+ })
4533
+ );
4534
+ } catch (error62) {
4535
+ context.err(`laze: the PreToolUse hook skipped: ${String(error62)}
4536
+ `);
4537
+ }
4538
+ }
4539
+
4129
4540
  // src/services/chat-commands.service.ts
4130
4541
  var REQUEST_WAIT_SECONDS = 50;
4131
4542
  async function listChannels({ context, args }) {
@@ -4198,9 +4609,12 @@ function waitSecondsOf({ args }) {
4198
4609
  return timeout > 0 ? timeout : Infinity;
4199
4610
  }
4200
4611
  async function inbox({ context, args }) {
4201
- const { client } = await signedIn({ context, args });
4612
+ const { client, name: name2, profile } = await signedIn({ context, args });
4202
4613
  const waitSeconds = waitSecondsOf({ args });
4203
4614
  const messages = await receiveMessages({ context, client, waitSeconds });
4615
+ if (profile.kind === "agent" && context.environment.harness === "claude-code") {
4616
+ await turnStarted({ context, client, agent: name2, messages }).catch(() => void 0);
4617
+ }
4204
4618
  if (args.flags.json) return context.out(`${JSON.stringify(messages, null, 2)}
4205
4619
  `);
4206
4620
  if (messages.length === 0) return context.out("No new messages.\n");
@@ -4244,19 +4658,20 @@ function slugOf(name2) {
4244
4658
  return name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "agent";
4245
4659
  }
4246
4660
  async function installStopHook({ context, profile }) {
4247
- const directory = join2(context.cwd, ".claude");
4248
- const path = join2(directory, "settings.local.json");
4249
- const current = await readFile2(path, "utf8").then(
4661
+ const directory = join3(context.cwd, ".claude");
4662
+ const path = join3(directory, "settings.local.json");
4663
+ const current = await readFile3(path, "utf8").then(
4250
4664
  (text2) => JSON.parse(text2),
4251
4665
  () => ({})
4252
4666
  );
4253
- const command = stopHookCommand({ program: context.program, profile });
4254
- await mkdir2(directory, { recursive: true });
4255
- await writeFile2(
4256
- path,
4257
- `${JSON.stringify(withStopHook({ settings: current, command }), null, 2)}
4258
- `
4259
- );
4667
+ const settings = withLazeHooks({
4668
+ settings: current,
4669
+ stopCommand: hookCommand({ program: context.program, profile, hook: "stop" }),
4670
+ preToolCommand: hookCommand({ program: context.program, profile, hook: "pre-tool" })
4671
+ });
4672
+ await mkdir3(directory, { recursive: true });
4673
+ await writeFile3(path, `${JSON.stringify(settings, null, 2)}
4674
+ `);
4260
4675
  return path;
4261
4676
  }
4262
4677
  function nextStepsFor({
@@ -4304,7 +4719,7 @@ async function linkAgent({ context, args }) {
4304
4719
  context.out(
4305
4720
  [
4306
4721
  `Linked as ${linked.agent.name} (${linked.agent.id}), profile "${name2}" for ${context.cwd}.`,
4307
- ...hookPath ? [`Added the Laze Stop hook to ${hookPath}.`] : [],
4722
+ ...hookPath ? [`Added the Laze hooks to ${hookPath}.`] : [],
4308
4723
  "",
4309
4724
  nextStepsFor({ harness, profile: name2, laze: lazeCommandOf({ program: context.program }) }),
4310
4725
  ""
@@ -4316,7 +4731,7 @@ async function reportTranscript({
4316
4731
  input: input2
4317
4732
  }) {
4318
4733
  if (!input2.transcript_path || !input2.session_id) return;
4319
- const lines = (await readFile2(input2.transcript_path, "utf8")).split("\n");
4734
+ const lines = (await readFile3(input2.transcript_path, "utf8")).split("\n");
4320
4735
  const context = readTranscript({ lines });
4321
4736
  await client.post("/api/agents/me/activity", {
4322
4737
  sessionId: input2.session_id,
@@ -4328,14 +4743,15 @@ async function reportTranscript({
4328
4743
  async function handOver({ context, args }) {
4329
4744
  const raw = await context.readStdin();
4330
4745
  const input2 = raw.trim() ? JSON.parse(raw) : {};
4331
- const { client } = await signedIn({ context, args });
4746
+ const { client, name: agent } = await signedIn({ context, args });
4332
4747
  await reportTranscript({ client, input: input2 }).catch(
4333
4748
  (error62) => context.err(`laze: could not report context: ${String(error62)}
4334
4749
  `)
4335
4750
  );
4336
4751
  if (input2.stop_hook_active) return;
4337
4752
  const messages = await receiveMessages({ context, client, waitSeconds: 0 });
4338
- if (messages.length === 0) return;
4753
+ if (messages.length === 0) return turnEnded({ context, client, agent });
4754
+ await turnStarted({ context, client, agent, messages });
4339
4755
  context.out(
4340
4756
  JSON.stringify({
4341
4757
  hookSpecificOutput: { hookEventName: "Stop", additionalContext: hookContextOf({ messages }) }
@@ -4352,8 +4768,8 @@ async function stopHook({ context, args }) {
4352
4768
  }
4353
4769
 
4354
4770
  // src/services/app-commands.service.ts
4355
- import { mkdir as mkdir3, readdir, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
4356
- import { join as join3, relative, resolve as resolve2, sep } from "node:path";
4771
+ import { mkdir as mkdir4, readdir, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
4772
+ import { join as join4, relative, resolve as resolve2, sep } from "node:path";
4357
4773
 
4358
4774
  // ../../packages/module/src/feature-names.generated.ts
4359
4775
  var FEATURE_NAMES = [
@@ -4366,18 +4782,21 @@ var FEATURE_NAMES = [
4366
4782
  "authz",
4367
4783
  "billing",
4368
4784
  "chat",
4785
+ "compute",
4369
4786
  "connector",
4370
4787
  "entitlement",
4371
4788
  "feature-flag",
4372
4789
  "feed",
4373
- "marketing",
4374
4790
  "files",
4791
+ "marketing",
4375
4792
  "memory",
4376
4793
  "model-gateway",
4377
4794
  "notification",
4795
+ "openclaw",
4378
4796
  "role",
4379
4797
  "schedule",
4380
4798
  "skill",
4799
+ "slack",
4381
4800
  "vault",
4382
4801
  "wiki",
4383
4802
  "workspace",
@@ -24207,6 +24626,8 @@ var publishRequestSchemaDefinition = external_exports.object({
24207
24626
  visibility: appVisibilitySchema,
24208
24627
  status: external_exports.enum(["pending", "approved", "denied"]),
24209
24628
  requestedBy: appActorSchema,
24629
+ /** The name of who asked, on an app's waiting publish; absent where it is not read. */
24630
+ requestedByName: external_exports.string().nullable().optional(),
24210
24631
  decidedBy: appActorSchema.nullable(),
24211
24632
  createdAt: external_exports.iso.datetime(),
24212
24633
  decidedAt: external_exports.iso.datetime().nullable()
@@ -24230,6 +24651,8 @@ var appSchemaDefinition = external_exports.object({
24230
24651
  pendingPublish: publishRequestSchema.nullable(),
24231
24652
  /** The name of the person or agent who built it; absent when it can no longer be read. */
24232
24653
  ownerName: external_exports.string().nullable().optional(),
24654
+ /** Set while the app is archived: it serves nothing and leaves the app list. */
24655
+ archivedAt: external_exports.iso.datetime().nullable().optional(),
24233
24656
  createdAt: external_exports.iso.datetime(),
24234
24657
  updatedAt: external_exports.iso.datetime()
24235
24658
  });
@@ -24302,6 +24725,23 @@ var createAppInputSchemaDefinition = external_exports.object({
24302
24725
  visibility: appVisibilitySchema.default("internal")
24303
24726
  });
24304
24727
  var createAppInputSchema = createAppInputSchemaDefinition;
24728
+ var listAppsInputSchemaDefinition = external_exports.object({
24729
+ workspaceId: external_exports.string().min(1),
24730
+ /** True lists only archived apps; absent or false lists the others. */
24731
+ archived: external_exports.boolean().optional()
24732
+ });
24733
+ var listAppsInputSchema = listAppsInputSchemaDefinition;
24734
+ var renameAppInputSchemaDefinition = external_exports.object({
24735
+ ...appRefSchemaDefinition.shape,
24736
+ name: external_exports.string().trim().min(1).max(80)
24737
+ });
24738
+ var renameAppInputSchema = renameAppInputSchemaDefinition;
24739
+ var deleteAppInputSchemaDefinition = external_exports.object({
24740
+ ...appRefSchemaDefinition.shape,
24741
+ /** The app's name, typed by the person to confirm. */
24742
+ confirmName: external_exports.string().max(200)
24743
+ });
24744
+ var deleteAppInputSchema = deleteAppInputSchemaDefinition;
24305
24745
  var commitInputSchemaDefinition = external_exports.object({
24306
24746
  ...appRefSchemaDefinition.shape,
24307
24747
  /** The whole static build: files left out are gone from this version. */
@@ -24424,7 +24864,7 @@ var appViewerSchemaDefinition = external_exports.object({
24424
24864
  var AppApi = moduleApi()("app");
24425
24865
 
24426
24866
  // ../../modules/app/contract/src/app.trpc.ts
24427
- var appTrpc = defineTrpcContract("app").query("list").withInput(external_exports.object({ workspaceId: external_exports.string().min(1) })).withOutput(external_exports.array(appSchema)).query("get").withInput(appRefSchema).withOutput(appSchema).query("versions").withInput(appRefSchema).withOutput(external_exports.array(appVersionSchema)).mutation("revert").withInput(revertInputSchema).withOutput(appVersionSchema).mutation("publish").withInput(publishInputSchema).withOutput(publishOutcomeSchema).query("collections").withInput(siteRefSchema).withOutput(external_exports.array(collectionSchema)).query("records").withInput(listRecordsInputSchema).withOutput(recordPageSchema).mutation("writeRecord").withInput(writeRecordInputSchema).withOutput(appRecordSchema).mutation("deleteRecord").withInput(deleteRecordInputSchema).withOutput(external_exports.object({ deleted: external_exports.literal(true) })).query("exportRecords").withInput(exportRecordsInputSchema).withOutput(recordExportSchema).mutation("decidePublish").withInput(decidePublishInputSchema).withOutput(publishOutcomeSchema).mutation("signInHandoff").withInput(signInHandoffInputSchema).withOutput(signInHandoffSchema).build();
24867
+ var appTrpc = defineTrpcContract("app").query("list").withInput(listAppsInputSchema).withOutput(external_exports.array(appSchema)).query("get").withInput(appRefSchema).withOutput(appSchema).mutation("create").withInput(createAppInputSchema).withOutput(appSchema).mutation("rename").withInput(renameAppInputSchema).withOutput(appSchema).mutation("archive").withInput(appRefSchema).withOutput(appSchema).mutation("unarchive").withInput(appRefSchema).withOutput(appSchema).mutation("delete").withInput(deleteAppInputSchema).withOutput(external_exports.object({ deleted: external_exports.literal(true) })).query("versions").withInput(appRefSchema).withOutput(external_exports.array(appVersionSchema)).mutation("revert").withInput(revertInputSchema).withOutput(appVersionSchema).mutation("publish").withInput(publishInputSchema).withOutput(publishOutcomeSchema).query("collections").withInput(siteRefSchema).withOutput(external_exports.array(collectionSchema)).query("records").withInput(listRecordsInputSchema).withOutput(recordPageSchema).mutation("writeRecord").withInput(writeRecordInputSchema).withOutput(appRecordSchema).mutation("deleteRecord").withInput(deleteRecordInputSchema).withOutput(external_exports.object({ deleted: external_exports.literal(true) })).query("exportRecords").withInput(exportRecordsInputSchema).withOutput(recordExportSchema).mutation("decidePublish").withInput(decidePublishInputSchema).withOutput(publishOutcomeSchema).mutation("signInHandoff").withInput(signInHandoffInputSchema).withOutput(signInHandoffSchema).build();
24428
24868
 
24429
24869
  // ../../modules/app/contract/src/app.rest.ts
24430
24870
  var byKey = { workspaceId: true };
@@ -27239,7 +27679,7 @@ function folderOf({ context, word }) {
27239
27679
  return resolve2(context.cwd, word ?? ".");
27240
27680
  }
27241
27681
  async function readManifest(folder) {
27242
- const text2 = await readFile3(join3(folder, APP_MANIFEST_FILE), "utf8").catch(() => void 0);
27682
+ const text2 = await readFile4(join4(folder, APP_MANIFEST_FILE), "utf8").catch(() => void 0);
27243
27683
  if (text2 === void 0) return void 0;
27244
27684
  const parsed = appManifestSchema.safeParse(JSON.parse(text2));
27245
27685
  if (!parsed.success) {
@@ -27274,7 +27714,7 @@ async function jsonFlag({
27274
27714
  }) {
27275
27715
  const raw = flagText({ args, name: name2 });
27276
27716
  if (!raw) return void 0;
27277
- const text2 = raw.startsWith("@") ? await readFile3(resolve2(context.cwd, raw.slice(1)), "utf8") : raw;
27717
+ const text2 = raw.startsWith("@") ? await readFile4(resolve2(context.cwd, raw.slice(1)), "utf8") : raw;
27278
27718
  try {
27279
27719
  return JSON.parse(text2);
27280
27720
  } catch {
@@ -27287,7 +27727,7 @@ async function listFiles(folder) {
27287
27727
  while (pending.length > 0 && found.length <= MAX_FILES) {
27288
27728
  const current = pending.pop() ?? folder;
27289
27729
  for (const entry of await readdir(current, { withFileTypes: true })) {
27290
- const path = join3(current, entry.name);
27730
+ const path = join4(current, entry.name);
27291
27731
  const rel = relative(folder, path).split(sep).join("/");
27292
27732
  if (isSkippedPath({ path: rel })) continue;
27293
27733
  if (entry.isDirectory()) pending.push(path);
@@ -27328,17 +27768,17 @@ var appCreate = async ({ context, args }) => {
27328
27768
  ...visibility ? { visibility } : {}
27329
27769
  });
27330
27770
  const folder = resolve2(context.cwd, flagText({ args, name: "dir", fallback: app2.slug }));
27331
- await mkdir3(folder, { recursive: true });
27771
+ await mkdir4(folder, { recursive: true });
27332
27772
  const files = template ? templateFiles({ template, name: name2 }) : {};
27333
27773
  for (const [path, content] of Object.entries(files)) {
27334
- await writeFile3(join3(folder, path), content);
27774
+ await writeFile4(join4(folder, path), content);
27335
27775
  }
27336
27776
  const manifest = {
27337
27777
  app: { id: app2.id, name: app2.name, slug: app2.slug },
27338
27778
  collections: template ? { ...template.collections } : {},
27339
27779
  seed: template ? { ...template.seed } : {}
27340
27780
  };
27341
- await writeFile3(join3(folder, APP_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
27781
+ await writeFile4(join4(folder, APP_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
27342
27782
  `);
27343
27783
  print({
27344
27784
  context,
@@ -27369,7 +27809,7 @@ var appPush = async ({ context, args }) => {
27369
27809
  paths.map(
27370
27810
  async (path) => buildFileOf({
27371
27811
  path: relative(folder, path).split(sep).join("/"),
27372
- bytes: await readFile3(path)
27812
+ bytes: await readFile4(path)
27373
27813
  })
27374
27814
  )
27375
27815
  );
@@ -27600,7 +28040,7 @@ var appExport = async ({ context, args }) => {
27600
28040
  const out = flagText({ args, name: "out" });
27601
28041
  if (!out) return context.out(exported.body);
27602
28042
  const target = resolve2(context.cwd, out);
27603
- await writeFile3(target, exported.body);
28043
+ await writeFile4(target, exported.body);
27604
28044
  context.out(`Wrote ${exported.rows} records to ${relative(context.cwd, target)}
27605
28045
  `);
27606
28046
  };
@@ -27714,11 +28154,163 @@ async function workspaces({ context, args }) {
27714
28154
  }
27715
28155
  }
27716
28156
 
28157
+ // src/services/browser-commands.service.ts
28158
+ import { readFile as readFile5 } from "node:fs/promises";
28159
+ var PATHS = {
28160
+ machines: "/api/compute/machines",
28161
+ setup: "/api/compute/browser/setup",
28162
+ cdp: "/api/compute/browser/cdp",
28163
+ screen: "/api/compute/browser/screen",
28164
+ sleep: "/api/compute/machines/sleep",
28165
+ wake: "/api/compute/machines/wake",
28166
+ delete: "/api/compute/machines/delete"
28167
+ };
28168
+ var USAGE2 = [
28169
+ "Usage: laze browser setup <site> [--name <machine>] a machine with Chrome a person signs in on",
28170
+ " laze browser cdp <machine> a CDP address for this turn (Playwright connectOverCDP)",
28171
+ " laze browser screen <machine> [--site <site>] send the sign-in screen link again",
28172
+ " laze browser status [<machine>] the machines, their state, sites and cost",
28173
+ " laze browser sleep|wake|delete <machine>"
28174
+ ].join("\n");
28175
+ async function turnTokenOf({ context }) {
28176
+ const env = context.environment.inherited;
28177
+ const pinned = env.LAZE_TURN_TOKEN?.trim();
28178
+ if (pinned) return pinned;
28179
+ if (!env.LAZE_TURN_FILE) return "";
28180
+ return readFile5(env.LAZE_TURN_FILE, "utf8").then(
28181
+ (text2) => text2.trim(),
28182
+ () => ""
28183
+ );
28184
+ }
28185
+ async function computeClient({
28186
+ context,
28187
+ args
28188
+ }) {
28189
+ const { client } = await signedIn({ context, args });
28190
+ const turn = await turnTokenOf({ context });
28191
+ return turn ? client.withHeaders({ "x-laze-turn": turn }) : client;
28192
+ }
28193
+ function siteLine(machine) {
28194
+ return machine.sites.map((entry) => `${entry.site} (${entry.status.replaceAll("_", " ")})`).join(", ");
28195
+ }
28196
+ function statusLine(machine) {
28197
+ const cost = `${machine.monthCostUsd.toFixed(2)} of ${machine.monthlyBudgetUsd} USD this month`;
28198
+ const use = machine.inHumanUse ? ", a person has the screen" : "";
28199
+ return `${machine.name} ${machine.state}${use} ${siteLine(machine) || "no sites"} ${cost}
28200
+ `;
28201
+ }
28202
+ function machineArg({ args }) {
28203
+ const machine = args.words[2];
28204
+ if (!machine) throw new CliUsageError(USAGE2);
28205
+ return machine;
28206
+ }
28207
+ async function setup({ context, args }) {
28208
+ const site = args.words[2];
28209
+ if (!site) throw new CliUsageError(USAGE2);
28210
+ const name2 = flagText({ args, name: "name" }) || void 0;
28211
+ const client = await computeClient({ context, args });
28212
+ const machine = await client.post(PATHS.setup, { site, ...name2 ? { name: name2 } : {} });
28213
+ if (args.flags.json) return context.out(`${JSON.stringify(machine, null, 2)}
28214
+ `);
28215
+ context.out(
28216
+ `${machine.name} is ${machine.state} (${siteLine(machine)}).
28217
+ The screen link goes to your requester's DM once it is ready. Wait for their message that they signed in, then run \`laze browser cdp ${machine.name}\`.
28218
+ `
28219
+ );
28220
+ }
28221
+ async function cdp({ context, args }) {
28222
+ const client = await computeClient({ context, args });
28223
+ const access = await client.post(PATHS.cdp, { machine: machineArg({ args }) });
28224
+ if (args.flags.json) return context.out(`${JSON.stringify(access, null, 2)}
28225
+ `);
28226
+ if (access.status === "waking")
28227
+ return context.out(
28228
+ `${access.machine} is waking up. Run this again in ${access.retryAfterSeconds} seconds.
28229
+ `
28230
+ );
28231
+ context.out(`${access.url}
28232
+ `);
28233
+ context.err(`Valid until ${access.expiresAt}. Use it with chromium.connectOverCDP(url).
28234
+ `);
28235
+ }
28236
+ async function screen({ context, args }) {
28237
+ const client = await computeClient({ context, args });
28238
+ const site = flagText({ args, name: "site" }) || void 0;
28239
+ const link = await client.post(PATHS.screen, {
28240
+ machine: machineArg({ args }),
28241
+ ...site ? { site } : {}
28242
+ });
28243
+ if (args.flags.json) return context.out(`${JSON.stringify(link, null, 2)}
28244
+ `);
28245
+ context.out(
28246
+ link.url ? `${link.url}
28247
+ ` : `Sent a fresh screen link for ${link.machine.name} to your requester's DM.
28248
+ `
28249
+ );
28250
+ }
28251
+ async function status({ context, args }) {
28252
+ const client = await computeClient({ context, args });
28253
+ const wanted = args.words[2];
28254
+ const machines = (await client.get(PATHS.machines)).filter(
28255
+ (machine) => !wanted || machine.name === wanted
28256
+ );
28257
+ if (args.flags.json) return context.out(`${JSON.stringify(machines, null, 2)}
28258
+ `);
28259
+ if (machines.length === 0)
28260
+ return context.out(
28261
+ wanted ? `No machine named ${wanted}.
28262
+ ` : "No machines. `laze browser setup <site>` makes one.\n"
28263
+ );
28264
+ for (const machine of machines) context.out(statusLine(machine));
28265
+ }
28266
+ async function change({
28267
+ context,
28268
+ args,
28269
+ path
28270
+ }) {
28271
+ const client = await computeClient({ context, args });
28272
+ const machine = await client.post(path, { machine: machineArg({ args }) });
28273
+ if (args.flags.json) return context.out(`${JSON.stringify(machine, null, 2)}
28274
+ `);
28275
+ context.out(statusLine(machine));
28276
+ }
28277
+ async function browser({ context, args }) {
28278
+ const command = args.words[1];
28279
+ if (command === "setup") return setup({ context, args });
28280
+ if (command === "cdp") return cdp({ context, args });
28281
+ if (command === "screen") return screen({ context, args });
28282
+ if (command === "status") return status({ context, args });
28283
+ if (command === "sleep" || command === "wake" || command === "delete")
28284
+ return change({ context, args, path: PATHS[command] });
28285
+ throw new CliUsageError(USAGE2);
28286
+ }
28287
+
27717
28288
  // src/services/codex-adopt.service.ts
27718
- import { spawn } from "node:child_process";
28289
+ import { spawn as spawn2 } from "node:child_process";
27719
28290
  import { readdir as readdir2, stat } from "node:fs/promises";
27720
- import { join as join4 } from "node:path";
28291
+ import { join as join5 } from "node:path";
27721
28292
  import { createInterface } from "node:readline";
28293
+
28294
+ // src/rules/codex-requests.rules.ts
28295
+ var DECLINED_DECISION = {
28296
+ "item/commandExecution/requestApproval": "decline",
28297
+ "item/fileChange/requestApproval": "decline",
28298
+ execCommandApproval: "denied",
28299
+ applyPatchApproval: "denied"
28300
+ };
28301
+ function codexRequestReplyOf({ method }) {
28302
+ const decision = DECLINED_DECISION[method];
28303
+ if (decision) return { result: { decision } };
28304
+ if (method === "item/permissions/requestApproval") {
28305
+ return { result: { permissions: {}, scope: "turn" } };
28306
+ }
28307
+ if (method === "mcpServer/elicitation/request") {
28308
+ return { result: { action: "decline", content: null } };
28309
+ }
28310
+ return { error: { code: -32601, message: `${method} is not supported here` } };
28311
+ }
28312
+
28313
+ // src/services/codex-adopt.service.ts
27722
28314
  function record3(value) {
27723
28315
  return typeof value === "object" && value !== null ? value : {};
27724
28316
  }
@@ -27736,6 +28328,7 @@ function absorbTurnEvent({ result, message }) {
27736
28328
  const window2 = usage.modelContextWindow;
27737
28329
  if (typeof window2 === "number" && window2 > 0) result.contextWindow = window2;
27738
28330
  }
28331
+ var TURN_HEARTBEAT_MS = 1e4;
27739
28332
  var CodexAppServer = class _CodexAppServer {
27740
28333
  constructor(child) {
27741
28334
  this.child = child;
@@ -27750,7 +28343,7 @@ var CodexAppServer = class _CodexAppServer {
27750
28343
  inherited
27751
28344
  }) {
27752
28345
  const env = { ...inherited, CODEX_HOME: codexHome };
27753
- return new _CodexAppServer(spawn("codex", ["app-server"], { env, stdio: "pipe" }));
28346
+ return new _CodexAppServer(spawn2("codex", ["app-server"], { env, stdio: "pipe" }));
27754
28347
  }
27755
28348
  async call(method, params) {
27756
28349
  const id = ++this.#nextId;
@@ -27764,24 +28357,24 @@ var CodexAppServer = class _CodexAppServer {
27764
28357
  notify(method) {
27765
28358
  this.write({ method });
27766
28359
  }
27767
- /** Runs one turn and waits for it to complete, keeping its last agent message. */
27768
- async turn({ threadId, text: text2 }) {
28360
+ /** Starts one turn; `done` settles when it completes, keeping its last agent message. */
28361
+ async startTurn({ threadId, text: text2 }) {
27769
28362
  const result = { answer: "" };
27770
28363
  const done = new Promise((resolve6) => {
27771
28364
  const listen = (message) => {
27772
28365
  absorbTurnEvent({ result, message });
27773
28366
  if (message.method !== "turn/completed") return;
27774
28367
  this.#listeners.delete(listen);
27775
- resolve6();
28368
+ resolve6(result);
27776
28369
  };
27777
28370
  this.#listeners.add(listen);
27778
28371
  });
27779
- await this.call("turn/start", {
28372
+ const started = await this.call("turn/start", {
27780
28373
  threadId,
27781
28374
  input: [{ type: "text", text: text2, text_elements: [] }]
27782
28375
  });
27783
- await done;
27784
- return result;
28376
+ const turnId = record3(started.turn).id;
28377
+ return { turnId: typeof turnId === "string" ? turnId : null, done };
27785
28378
  }
27786
28379
  stop() {
27787
28380
  this.child.kill("SIGTERM");
@@ -27803,18 +28396,23 @@ var CodexAppServer = class _CodexAppServer {
27803
28396
  return;
27804
28397
  }
27805
28398
  if (message.method !== void 0 && message.id !== void 0) {
27806
- this.write({ id: message.id, result: { decision: "approved" } });
28399
+ this.write({
28400
+ id: message.id,
28401
+ ...codexRequestReplyOf({
28402
+ method: typeof message.method === "string" ? message.method : ""
28403
+ })
28404
+ });
27807
28405
  return;
27808
28406
  }
27809
28407
  for (const listen of this.#listeners) listen(message);
27810
28408
  }
27811
28409
  };
27812
28410
  async function newestThreadId({ codexHome }) {
27813
- const root = join4(codexHome, "sessions");
28411
+ const root = join5(codexHome, "sessions");
27814
28412
  const files = await readdir2(root, { recursive: true }).catch(() => []);
27815
28413
  const rollouts = files.filter((file2) => /rollout-.*\.jsonl$/.test(file2));
27816
28414
  const dated = await Promise.all(
27817
- rollouts.map(async (file2) => ({ file: file2, at: (await stat(join4(root, file2))).mtimeMs }))
28415
+ rollouts.map(async (file2) => ({ file: file2, at: (await stat(join5(root, file2))).mtimeMs }))
27818
28416
  );
27819
28417
  const newest = dated.toSorted((a, b) => b.at - a.at)[0]?.file ?? "";
27820
28418
  const id = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/.exec(newest);
@@ -27829,13 +28427,49 @@ function promptOf({ message }) {
27829
28427
  formatInboxMessage({ message }).split("\n").slice(0, -1).join("\n")
27830
28428
  ].join("\n");
27831
28429
  }
28430
+ async function heartbeat({
28431
+ client,
28432
+ message,
28433
+ state
28434
+ }) {
28435
+ return client.post("/api/agents/me/turn", {
28436
+ channelId: message.channelId,
28437
+ threadRootId: message.threadRootId ?? message.messageId,
28438
+ state
28439
+ }).catch(() => void 0);
28440
+ }
28441
+ async function watchTurn({
28442
+ context,
28443
+ client,
28444
+ codex,
28445
+ threadId,
28446
+ turn,
28447
+ message
28448
+ }) {
28449
+ const finished = new AbortController();
28450
+ void turn.done.then(() => finished.abort());
28451
+ while (!finished.signal.aborted) {
28452
+ const beat2 = await heartbeat({ client, message, state: "working" });
28453
+ if (beat2?.stop && turn.turnId) {
28454
+ await codex.call("turn/interrupt", { threadId, turnId: turn.turnId }).catch(() => void 0);
28455
+ return beat2.stoppedBy ?? "someone";
28456
+ }
28457
+ await context.sleep(TURN_HEARTBEAT_MS, finished.signal);
28458
+ }
28459
+ return void 0;
28460
+ }
27832
28461
  async function answer({
28462
+ context,
27833
28463
  codex,
27834
28464
  client,
27835
28465
  threadId,
27836
28466
  message
27837
28467
  }) {
27838
- const turn = await codex.turn({ threadId, text: promptOf({ message }) });
28468
+ const running = await codex.startTurn({ threadId, text: promptOf({ message }) });
28469
+ const stoppedBy = await watchTurn({ context, client, codex, threadId, turn: running, message });
28470
+ const turn = await running.done;
28471
+ await heartbeat({ client, message, state: "idle" });
28472
+ if (stoppedBy !== void 0) return `(stopped by ${stoppedBy})`;
27839
28473
  const body = turn.answer.trim() || "(no answer)";
27840
28474
  await client.post("/api/agents/me/replies", { messageId: message.messageId, body });
27841
28475
  if (turn.contextTokens !== void 0) {
@@ -27871,7 +28505,7 @@ async function adoptCodex({ context, args }) {
27871
28505
  waitSeconds: timeout > 0 ? timeout : Infinity
27872
28506
  });
27873
28507
  for (const message of messages) {
27874
- const body = await answer({ codex, client, threadId, message });
28508
+ const body = await answer({ context, codex, client, threadId, message });
27875
28509
  context.out(`Answered ${message.messageId}: ${body.slice(0, 120)}
27876
28510
  `);
27877
28511
  }
@@ -27883,7 +28517,7 @@ async function adoptCodex({ context, args }) {
27883
28517
  }
27884
28518
 
27885
28519
  // src/services/feed-commands.service.ts
27886
- import { readFile as readFile4 } from "node:fs/promises";
28520
+ import { readFile as readFile6 } from "node:fs/promises";
27887
28521
  import { resolve as resolve3 } from "node:path";
27888
28522
 
27889
28523
  // src/rules/feed-input.rules.ts
@@ -27942,7 +28576,7 @@ function feedFlagsOf({ args }) {
27942
28576
  }
27943
28577
 
27944
28578
  // src/services/feed-commands.service.ts
27945
- var USAGE2 = `Usage:
28579
+ var USAGE3 = `Usage:
27946
28580
  laze feed post <title> [--channel <channel>] [--tag <tag>] [--summary <line>]
27947
28581
  [--body <text|@file>] [--draft <text|@file>] [--draft-header "To: ...\\nRe: ..."]
27948
28582
  [--link <label>=<url>]... [--action <label>=<message>]... [--to <userId>]...
@@ -27966,7 +28600,7 @@ async function bodyOf({
27966
28600
  }) {
27967
28601
  const body = { ...flags.fields };
27968
28602
  for (const [field, source] of Object.entries(flags.texts)) {
27969
- body[field] = "file" in source ? await readFile4(resolve3(context.cwd, source.file), "utf8") : source.text;
28603
+ body[field] = "file" in source ? await readFile6(resolve3(context.cwd, source.file), "utf8") : source.text;
27970
28604
  }
27971
28605
  return body;
27972
28606
  }
@@ -27985,7 +28619,7 @@ function printItem({
27985
28619
  }
27986
28620
  async function feedPost({ context, args }) {
27987
28621
  const title = args.words.slice(2).join(" ").trim();
27988
- if (!title) throw new CliUsageError(USAGE2);
28622
+ if (!title) throw new CliUsageError(USAGE3);
27989
28623
  const flags = flagsOf(args);
27990
28624
  const { client } = await signedIn({ context, args });
27991
28625
  const channel = flagText({ args, name: "channel" });
@@ -28002,7 +28636,7 @@ async function feedPost({ context, args }) {
28002
28636
  }
28003
28637
  async function feedUpdate({ context, args }) {
28004
28638
  const itemId = args.words[2];
28005
- if (!itemId) throw new CliUsageError(USAGE2);
28639
+ if (!itemId) throw new CliUsageError(USAGE3);
28006
28640
  const flags = flagsOf(args);
28007
28641
  const { client } = await signedIn({ context, args });
28008
28642
  const body = await bodyOf({ context, flags });
@@ -28018,7 +28652,7 @@ async function feedUpdate({ context, args }) {
28018
28652
  async function feedReply({ context, args }) {
28019
28653
  const itemId = args.words[2];
28020
28654
  const text2 = args.words.slice(3).join(" ").trim();
28021
- if (!itemId || !text2) throw new CliUsageError(USAGE2);
28655
+ if (!itemId || !text2) throw new CliUsageError(USAGE3);
28022
28656
  const { client } = await signedIn({ context, args });
28023
28657
  const replied = await client.post("/api/feed/items/reply", {
28024
28658
  itemId,
@@ -28029,7 +28663,7 @@ async function feedReply({ context, args }) {
28029
28663
  }
28030
28664
  async function feedRead({ context, args }) {
28031
28665
  const itemId = args.words[2];
28032
- if (!itemId) throw new CliUsageError(USAGE2);
28666
+ if (!itemId) throw new CliUsageError(USAGE3);
28033
28667
  const { client } = await signedIn({ context, args });
28034
28668
  const read = await client.post("/api/feed/items/read", { itemId });
28035
28669
  printItem({ context, args, item: read });
@@ -28054,12 +28688,12 @@ var FEED_COMMANDS = {
28054
28688
  async function feed({ context, args }) {
28055
28689
  const name2 = args.words[1] ?? "";
28056
28690
  const command = Object.hasOwn(FEED_COMMANDS, name2) ? FEED_COMMANDS[name2] : void 0;
28057
- if (!command) throw new CliUsageError(USAGE2);
28691
+ if (!command) throw new CliUsageError(USAGE3);
28058
28692
  await command({ context, args });
28059
28693
  }
28060
28694
 
28061
28695
  // src/services/github-commands.service.ts
28062
- import { readFile as readFile5 } from "node:fs/promises";
28696
+ import { readFile as readFile7 } from "node:fs/promises";
28063
28697
 
28064
28698
  // src/rules/github-signature.rules.ts
28065
28699
  var TURN_SEPARATOR = "~";
@@ -28127,13 +28761,17 @@ function isLocalGhCall(args) {
28127
28761
  // src/services/github-commands.service.ts
28128
28762
  var REAL_GH = "/usr/bin/gh";
28129
28763
  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>`.";
28764
+ async function turnFileToken({ env }) {
28765
+ if (!env.LAZE_TURN_FILE) return "";
28766
+ return readFile7(env.LAZE_TURN_FILE, "utf8").then(
28767
+ (text2) => text2.trim(),
28768
+ () => ""
28769
+ );
28770
+ }
28130
28771
  async function credentialOf({ env }) {
28131
28772
  const placeholder = (env.GH_TOKEN ?? "").split(TURN_SEPARATOR)[0] ?? "";
28132
28773
  if (!placeholder.startsWith("laze-ph-")) throw new CliUsageError(NOT_SET);
28133
- const turn = env.LAZE_TURN_FILE ? await readFile5(env.LAZE_TURN_FILE, "utf8").then(
28134
- (text2) => text2.trim(),
28135
- () => ""
28136
- ) : "";
28774
+ const turn = env.LAZE_TURN_TOKEN?.trim() || await turnFileToken({ env });
28137
28775
  return turn ? `${placeholder}${TURN_SEPARATOR}${turn}` : placeholder;
28138
28776
  }
28139
28777
  function threadUrlOf({ env, workspaceId }) {
@@ -28159,7 +28797,7 @@ async function githubGh({
28159
28797
  args: args.rest,
28160
28798
  agentName: profile.name ?? "An agent",
28161
28799
  threadUrl: threadUrlOf({ env, workspaceId: profile.workspaceId }),
28162
- readBodyFile: (path) => path === "-" ? context.readStdin() : readFile5(path, "utf8")
28800
+ readBodyFile: (path) => path === "-" ? context.readStdin() : readFile7(path, "utf8")
28163
28801
  });
28164
28802
  return context.runCommand({
28165
28803
  command: [REAL_GH, ...ghArgs],
@@ -28258,7 +28896,7 @@ async function setAgentGithub({
28258
28896
  }
28259
28897
 
28260
28898
  // src/services/mcp-gateway.service.ts
28261
- import { readFile as readFile6 } from "node:fs/promises";
28899
+ import { readFile as readFile8 } from "node:fs/promises";
28262
28900
  import { createInterface as createInterface2 } from "node:readline";
28263
28901
  var TURN_HEADER = "x-laze-turn";
28264
28902
  function serveMcpGateway({
@@ -28272,6 +28910,7 @@ async function relayMcpLines({
28272
28910
  args,
28273
28911
  lines
28274
28912
  }) {
28913
+ const pending = lines[Symbol.asyncIterator]();
28275
28914
  const { profile } = await signedIn({ context, args });
28276
28915
  const env = context.environment.inherited;
28277
28916
  if (!profile.agentId || !env.LAZE_TURN_FILE)
@@ -28279,9 +28918,10 @@ async function relayMcpLines({
28279
28918
  const github2 = env.LAZE_GITHUB_CONNECTION;
28280
28919
  const url2 = `${profile.url.replace(/\/+$/, "")}/api/connectors/mcp/${encodeURIComponent(profile.agentId)}${github2 ? `?github=${encodeURIComponent(github2)}` : ""}`;
28281
28920
  const turnFile = env.LAZE_TURN_FILE;
28282
- for await (const line of lines) {
28921
+ for (let next = await pending.next(); !next.done; next = await pending.next()) {
28922
+ const line = next.value;
28283
28923
  if (!line.trim()) continue;
28284
- const turn = await readFile6(turnFile, "utf8").then(
28924
+ const turn = await readFile8(turnFile, "utf8").then(
28285
28925
  (text3) => text3.trim(),
28286
28926
  () => ""
28287
28927
  );
@@ -28426,14 +29066,14 @@ async function serveMcp({ context, args }) {
28426
29066
  }
28427
29067
 
28428
29068
  // src/services/mount-files.service.ts
28429
- import { mkdir as mkdir4, readFile as readFile7, rm, writeFile as writeFile4 } from "node:fs/promises";
28430
- import { dirname as dirname2, relative as relative2, resolve as resolve4 } from "node:path";
29069
+ import { mkdir as mkdir5, readFile as readFile9, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
29070
+ import { dirname as dirname3, relative as relative2, resolve as resolve4 } from "node:path";
28431
29071
  async function textArgument({
28432
29072
  context,
28433
29073
  value
28434
29074
  }) {
28435
29075
  if (value === "-") return context.readStdin();
28436
- if (value.startsWith("@")) return readFile7(resolve4(context.cwd, value.slice(1)), "utf8");
29076
+ if (value.startsWith("@")) return readFile9(resolve4(context.cwd, value.slice(1)), "utf8");
28437
29077
  return value;
28438
29078
  }
28439
29079
  function turnChannel({ context, args }) {
@@ -28446,21 +29086,21 @@ async function writeMount({
28446
29086
  files
28447
29087
  }) {
28448
29088
  const root = resolve4(context.cwd, flagText({ args, name: "dir", fallback: ".laze" }), folder);
28449
- await rm(root, { recursive: true, force: true });
28450
- await mkdir4(root, { recursive: true });
29089
+ await rm2(root, { recursive: true, force: true });
29090
+ await mkdir5(root, { recursive: true });
28451
29091
  for (const file2 of files) {
28452
29092
  const target = resolve4(root, file2.path);
28453
29093
  const inside = relative2(root, target);
28454
29094
  if (!inside || inside.startsWith(".."))
28455
29095
  throw new Error(`Refusing to write outside ${root}: ${file2.path}`);
28456
- await mkdir4(dirname2(target), { recursive: true });
28457
- await writeFile4(target, file2.content);
29096
+ await mkdir5(dirname3(target), { recursive: true });
29097
+ await writeFile5(target, file2.content);
28458
29098
  }
28459
29099
  return root;
28460
29100
  }
28461
29101
 
28462
29102
  // src/services/memory-commands.service.ts
28463
- var USAGE3 = `Usage:
29103
+ var USAGE4 = `Usage:
28464
29104
  laze memory get SOUL.md|AGENTS.md|MEMORY.md [--channel <channel>]
28465
29105
  laze memory set <file> <text|@file|-> [--channel <channel>] [--base <version>]
28466
29106
  laze memory append MEMORY.md <text|@file|-> [--channel <channel>]
@@ -28471,7 +29111,7 @@ MEMORY.md is the file of the turn's channel (LAZE_CHANNEL), or of --channel when
28471
29111
  var FILES = /* @__PURE__ */ new Set(["SOUL.md", "AGENTS.md", "MEMORY.md"]);
28472
29112
  function fileRef({ context, args }) {
28473
29113
  const file2 = args.words[2] ?? "";
28474
- if (!FILES.has(file2)) throw new CliUsageError(USAGE3);
29114
+ if (!FILES.has(file2)) throw new CliUsageError(USAGE4);
28475
29115
  const channel = turnChannel({ context, args });
28476
29116
  return { file: file2, ...channel ? { channel } : {} };
28477
29117
  }
@@ -28499,7 +29139,7 @@ async function memoryGet({ context, args }) {
28499
29139
  async function memorySet({ context, args }) {
28500
29140
  const ref = fileRef({ context, args });
28501
29141
  const value = args.words.slice(3).join(" ");
28502
- if (!value) throw new CliUsageError(USAGE3);
29142
+ if (!value) throw new CliUsageError(USAGE4);
28503
29143
  const content = await textArgument({ context, value });
28504
29144
  const base = args.flags.base;
28505
29145
  const { client } = await signedIn({ context, args });
@@ -28515,7 +29155,7 @@ async function memorySet({ context, args }) {
28515
29155
  async function memoryAppend({ context, args }) {
28516
29156
  const ref = fileRef({ context, args });
28517
29157
  const value = args.words.slice(3).join(" ");
28518
- if (!value) throw new CliUsageError(USAGE3);
29158
+ if (!value) throw new CliUsageError(USAGE4);
28519
29159
  const text2 = await textArgument({ context, value });
28520
29160
  const { client } = await signedIn({ context, args });
28521
29161
  const saved = await client.post("/api/memory/append", { ...ref, text: text2 });
@@ -28525,7 +29165,7 @@ async function memoryAppend({ context, args }) {
28525
29165
  }
28526
29166
  async function memorySearch({ context, args }) {
28527
29167
  const query = args.words.slice(2).join(" ").trim();
28528
- if (!query) throw new CliUsageError(USAGE3);
29168
+ if (!query) throw new CliUsageError(USAGE4);
28529
29169
  const { client } = await signedIn({ context, args });
28530
29170
  const hits = await client.post("/api/memory/search", { query });
28531
29171
  if (args.flags.json) return context.out(`${JSON.stringify(hits, null, 2)}
@@ -28558,12 +29198,12 @@ var MEMORY_COMMANDS = {
28558
29198
  async function memory({ context, args }) {
28559
29199
  const name2 = args.words[1] ?? "";
28560
29200
  const command = Object.hasOwn(MEMORY_COMMANDS, name2) ? MEMORY_COMMANDS[name2] : void 0;
28561
- if (!command) throw new CliUsageError(USAGE3);
29201
+ if (!command) throw new CliUsageError(USAGE4);
28562
29202
  await command({ context, args });
28563
29203
  }
28564
29204
 
28565
29205
  // src/services/schedule-commands.service.ts
28566
- var USAGE4 = `Usage:
29206
+ var USAGE5 = `Usage:
28567
29207
  laze schedule add <name> --prompt <text> (--cron "<m h dom mon dow>" | --rrule "<RRULE>" | --at <local time>)
28568
29208
  [--tz <IANA zone>] [--channel <channelId>] [--agent <agentId>]
28569
29209
  laze schedule list
@@ -28575,7 +29215,7 @@ function timingOf(args) {
28575
29215
  const given = [cron, rrule, at].filter(Boolean);
28576
29216
  if (given.length !== 1)
28577
29217
  throw new CliUsageError(`Give exactly one of --cron, --rrule or --at.
28578
- ${USAGE4}`);
29218
+ ${USAGE5}`);
28579
29219
  if (cron) return { kind: "cron", cron };
28580
29220
  if (rrule) return { kind: "rrule", rrule };
28581
29221
  return { kind: "once", at };
@@ -28590,7 +29230,7 @@ function printSchedule({ context, schedule: schedule2 }) {
28590
29230
  async function scheduleAdd({ context, args }) {
28591
29231
  const name2 = args.words.slice(2).join(" ").trim();
28592
29232
  const prompt = flagText({ args, name: "prompt" });
28593
- if (!name2 || !prompt) throw new CliUsageError(USAGE4);
29233
+ if (!name2 || !prompt) throw new CliUsageError(USAGE5);
28594
29234
  const timing = timingOf(args);
28595
29235
  const { client } = await signedIn({ context, args });
28596
29236
  const channelId = flagText({ args, name: "channel" });
@@ -28617,7 +29257,7 @@ async function scheduleList({ context, args }) {
28617
29257
  function byId(path) {
28618
29258
  return async ({ context, args }) => {
28619
29259
  const scheduleId = args.words[2];
28620
- if (!scheduleId) throw new CliUsageError(USAGE4);
29260
+ if (!scheduleId) throw new CliUsageError(USAGE5);
28621
29261
  const { client } = await signedIn({ context, args });
28622
29262
  const answer3 = await client.post(`/api/schedules/${path}`, {
28623
29263
  scheduleId
@@ -28631,7 +29271,7 @@ function byId(path) {
28631
29271
  }
28632
29272
  async function scheduleRuns({ context, args }) {
28633
29273
  const scheduleId = args.words[2];
28634
- if (!scheduleId) throw new CliUsageError(USAGE4);
29274
+ if (!scheduleId) throw new CliUsageError(USAGE5);
28635
29275
  const { client } = await signedIn({ context, args });
28636
29276
  const runs = await client.post("/api/schedules/runs", { scheduleId });
28637
29277
  if (args.flags.json) return context.out(`${JSON.stringify(runs, null, 2)}
@@ -28654,14 +29294,14 @@ var SCHEDULE_COMMANDS = {
28654
29294
  async function schedule({ context, args }) {
28655
29295
  const name2 = args.words[1] ?? "";
28656
29296
  const command = Object.hasOwn(SCHEDULE_COMMANDS, name2) ? SCHEDULE_COMMANDS[name2] : void 0;
28657
- if (!command) throw new CliUsageError(USAGE4);
29297
+ if (!command) throw new CliUsageError(USAGE5);
28658
29298
  await command({ context, args });
28659
29299
  }
28660
29300
 
28661
29301
  // src/services/skill-commands.service.ts
28662
- import { readdir as readdir3, readFile as readFile8 } from "node:fs/promises";
28663
- import { join as join5, relative as relative3, resolve as resolve5 } from "node:path";
28664
- var USAGE5 = `Usage:
29302
+ import { readdir as readdir3, readFile as readFile10 } from "node:fs/promises";
29303
+ import { join as join6, relative as relative3, resolve as resolve5 } from "node:path";
29304
+ var USAGE6 = `Usage:
28665
29305
  laze skill list
28666
29306
  laze skill install <catalogue-id> [--as <name>]
28667
29307
  laze skill install --from <folder> [--as <name>] a folder with a SKILL.md
@@ -28672,8 +29312,8 @@ async function folderFiles({ root }) {
28672
29312
  const files = [];
28673
29313
  for (const entry of entries) {
28674
29314
  if (!entry.isFile()) continue;
28675
- const full = join5(entry.parentPath, entry.name);
28676
- files.push({ path: relative3(root, full), content: await readFile8(full, "utf8") });
29315
+ const full = join6(entry.parentPath, entry.name);
29316
+ files.push({ path: relative3(root, full), content: await readFile10(full, "utf8") });
28677
29317
  }
28678
29318
  return files.toSorted((a, b) => a.path.localeCompare(b.path));
28679
29319
  }
@@ -28692,7 +29332,7 @@ async function skillList({ context, args }) {
28692
29332
  async function skillInstall({ context, args }) {
28693
29333
  const catalogueId = args.words[2];
28694
29334
  const from = flagText({ args, name: "from" });
28695
- if (!catalogueId === !from) throw new CliUsageError(USAGE5);
29335
+ if (!catalogueId === !from) throw new CliUsageError(USAGE6);
28696
29336
  const name2 = flagText({ args, name: "as" });
28697
29337
  const files = from ? await folderFiles({ root: resolve5(context.cwd, from) }) : void 0;
28698
29338
  const { client } = await signedIn({ context, args });
@@ -28708,7 +29348,7 @@ async function skillInstall({ context, args }) {
28708
29348
  }
28709
29349
  async function skillShow({ context, args }) {
28710
29350
  const name2 = args.words[2];
28711
- if (!name2) throw new CliUsageError(USAGE5);
29351
+ if (!name2) throw new CliUsageError(USAGE6);
28712
29352
  const { client } = await signedIn({ context, args });
28713
29353
  const skill2 = await client.post("/api/skills/show", { name: name2 });
28714
29354
  if (args.flags.json) return context.out(`${JSON.stringify(skill2, null, 2)}
@@ -28740,18 +29380,107 @@ var SKILL_COMMANDS = {
28740
29380
  async function skill({ context, args }) {
28741
29381
  const name2 = args.words[1] ?? "";
28742
29382
  const command = Object.hasOwn(SKILL_COMMANDS, name2) ? SKILL_COMMANDS[name2] : void 0;
28743
- if (!command) throw new CliUsageError(USAGE5);
29383
+ if (!command) throw new CliUsageError(USAGE6);
28744
29384
  await command({ context, args });
28745
29385
  }
28746
29386
 
29387
+ // src/services/update-commands.service.ts
29388
+ import { realpath } from "node:fs/promises";
29389
+
29390
+ // src/rules/cli-update.rules.ts
29391
+ var CLI_PACKAGE_LATEST = "@lazehq/cli@latest";
29392
+ var MANAGERS = [
29393
+ { manager: "bun", pattern: /[\\/]\.?bun[\\/]/, command: ["bun", "add", "-g"] },
29394
+ { manager: "pnpm", pattern: /[\\/]\.?pnpm[\\/]/, command: ["pnpm", "add", "-g"] },
29395
+ { manager: "yarn", pattern: /[\\/]yarn[\\/]global[\\/]/, command: ["yarn", "global", "add"] },
29396
+ {
29397
+ manager: "npm",
29398
+ pattern: /[\\/]_npx[\\/]|[\\/]node_modules[\\/]@lazehq[\\/]cli[\\/]/,
29399
+ command: ["npm", "install", "-g"]
29400
+ }
29401
+ ];
29402
+ var NPM_PREFIX = /^(.+)[\\/]lib[\\/]node_modules[\\/]@lazehq[\\/]cli[\\/]/;
29403
+ var NPX_CACHE2 = /[\\/]_npx[\\/]/;
29404
+ function updatePlanOf({ script }) {
29405
+ if (script === SANDBOX_CLI_PATH) return { kind: "sandbox" };
29406
+ const found = MANAGERS.find(({ pattern }) => pattern.test(script));
29407
+ if (!found) return { kind: "unmanaged", script };
29408
+ const prefix = found.manager === "npm" && !NPX_CACHE2.test(script) ? NPM_PREFIX.exec(script)?.[1] : void 0;
29409
+ return {
29410
+ kind: "install",
29411
+ manager: found.manager,
29412
+ command: [...found.command, ...prefix ? ["--prefix", prefix] : [], CLI_PACKAGE_LATEST]
29413
+ };
29414
+ }
29415
+
29416
+ // src/services/update-commands.service.ts
29417
+ var BY_HAND = `npm install -g ${CLI_PACKAGE_LATEST}`;
29418
+ async function realScriptOf({ program }) {
29419
+ const script = program.at(-1) ?? "";
29420
+ try {
29421
+ return await realpath(script);
29422
+ } catch {
29423
+ return script;
29424
+ }
29425
+ }
29426
+ async function update({
29427
+ context,
29428
+ args
29429
+ }) {
29430
+ const plan = updatePlanOf({ script: await realScriptOf({ program: context.program }) });
29431
+ if (plan.kind === "sandbox") {
29432
+ context.out("This laze comes with the Laze sandbox and updates with every Laze release.\n");
29433
+ return 0;
29434
+ }
29435
+ if (plan.kind === "unmanaged") {
29436
+ context.err(
29437
+ `This laze (${plan.script}) was not installed by a package manager. Install the published one with: ${BY_HAND}
29438
+ `
29439
+ );
29440
+ return 1;
29441
+ }
29442
+ const line = plan.command.join(" ");
29443
+ if (args.flags["dry-run"]) {
29444
+ context.out(`laze ${cliVersionOf()}, installed with ${plan.manager}. Would run: ${line}
29445
+ `);
29446
+ return 0;
29447
+ }
29448
+ context.out(`Updating laze ${cliVersionOf()} with ${plan.manager}: ${line}
29449
+ `);
29450
+ const code = await context.runCommand({ command: plan.command, env: {} });
29451
+ if (code !== 0) {
29452
+ context.err(`${plan.manager} exited ${code}. Update by hand with: ${line}
29453
+ `);
29454
+ return code;
29455
+ }
29456
+ context.out("Updated. `laze --version` names the new version.\n");
29457
+ return 0;
29458
+ }
29459
+
28747
29460
  // src/services/vault-commands.service.ts
28748
- import { readFile as readFile9 } from "node:fs/promises";
29461
+ import { createHash } from "node:crypto";
29462
+ import { mkdir as mkdir6, readFile as readFile11, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
29463
+ import { tmpdir } from "node:os";
29464
+ import { dirname as dirname4, join as join7 } from "node:path";
29465
+
29466
+ // ../../modules/vault/contract/src/resolution-note.ts
29467
+ var RESOLUTION_NOTE_MAX_CHARS = 500;
29468
+ function cleanResolutionNote(text2) {
29469
+ const oneLine = (text2 ?? "").replace(/[\r\n\u2028\u2029]+/g, " ").replace(/[\p{Cc}\p{Cf}]/gu, "").trim();
29470
+ return Array.from(oneLine).slice(0, RESOLUTION_NOTE_MAX_CHARS).join("").trim();
29471
+ }
29472
+ var resolutionNoteSchema = external_exports.string().max(4e3).default("").transform((text2) => cleanResolutionNote(text2));
28749
29473
 
28750
29474
  // ../../modules/vault/contract/src/vault-devices.ts
28751
29475
  var base64Url = external_exports.string().regex(/^[A-Za-z0-9_-]+$/).max(16384);
28752
29476
  var name = external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/);
28753
29477
  var fieldName = external_exports.string().regex(/^[A-Z][A-Z0-9_]{0,63}$/);
28754
- var vaultDeviceKindSchema = external_exports.enum(["passkey", "ios_secure_enclave"]);
29478
+ var vaultDeviceKindSchema = external_exports.enum([
29479
+ "passkey",
29480
+ "ios_secure_enclave",
29481
+ "macos_secure_enclave"
29482
+ ]);
29483
+ var NATIVE_DEVICE_KINDS = ["ios_secure_enclave", "macos_secure_enclave"];
28755
29484
  var vaultDeviceStateSchema = external_exports.enum(["pending", "active"]);
28756
29485
  var vaultDevicePasskeySchemaDefinition = external_exports.object({
28757
29486
  credentialId: base64Url,
@@ -28797,7 +29526,15 @@ var finishVaultDeviceEnrolmentInputSchemaDefinition = external_exports.object({
28797
29526
  credentialPublicKey: base64Url,
28798
29527
  credentialAlgorithm: external_exports.literal(-7),
28799
29528
  clientDataJSON: base64Url
28800
- })
29529
+ }).optional(),
29530
+ /** A Secure Enclave device instead of a passkey: its signing key and proof of holding it. */
29531
+ native: external_exports.object({
29532
+ kind: external_exports.enum(NATIVE_DEVICE_KINDS),
29533
+ /** P-256 SPKI DER; signs approvals of later devices. */
29534
+ signingPublicKey: base64Url,
29535
+ /** DER ECDSA over nativeSignedMessage({ purpose: "enrol", challenge }). */
29536
+ signature: base64Url
29537
+ }).optional()
28801
29538
  });
28802
29539
  var finishVaultDeviceEnrolmentInputSchema = finishVaultDeviceEnrolmentInputSchemaDefinition;
28803
29540
  var sealedRecipientSchema = external_exports.object({
@@ -28815,11 +29552,16 @@ var vaultDeviceAssertionSchemaDefinition = external_exports.object({
28815
29552
  authenticatorData: base64Url,
28816
29553
  signature: base64Url
28817
29554
  });
29555
+ var vaultNativeAssertionSchemaDefinition = external_exports.object({
29556
+ approverDeviceId: external_exports.string().min(1),
29557
+ /** DER ECDSA over nativeSignedMessage({ purpose: "approve", challenge }). */
29558
+ nativeSignature: base64Url
29559
+ });
28818
29560
  var approveVaultDeviceInputSchemaDefinition = external_exports.object({
28819
29561
  workspaceId: external_exports.string().min(1),
28820
29562
  deviceId: external_exports.string().min(1),
28821
29563
  challenge: external_exports.string().min(1),
28822
- assertion: vaultDeviceAssertionSchemaDefinition,
29564
+ assertion: external_exports.union([vaultDeviceAssertionSchemaDefinition, vaultNativeAssertionSchemaDefinition]),
28823
29565
  /** The new device's recipient for every sealed secret of the owner. */
28824
29566
  recipients: external_exports.array(
28825
29567
  external_exports.object({ connectionId: external_exports.string().min(1), recipient: sealedRecipientSchema })
@@ -28867,6 +29609,9 @@ var vaultSealedApprovalSchemaDefinition = external_exports.object({
28867
29609
  material: vaultSealedMaterialSchemaDefinition
28868
29610
  });
28869
29611
  var vaultSealedApprovalSchema = vaultSealedApprovalSchemaDefinition;
29612
+ var vaultUi = uiTokens("vault");
29613
+ var VaultSealedApprovalSlot = vaultUi.component("sealedApproval");
29614
+ var VaultNavSlot = vaultUi.component("nav");
28870
29615
  var answerSealedApprovalInputSchemaDefinition = external_exports.object({
28871
29616
  workspaceId: external_exports.string().min(1),
28872
29617
  requestId: external_exports.string().min(1),
@@ -28881,6 +29626,31 @@ var vaultRunSealedReleaseSchemaDefinition = external_exports.object({
28881
29626
  });
28882
29627
  var vaultRunSealedReleaseSchema = vaultRunSealedReleaseSchemaDefinition;
28883
29628
 
29629
+ // ../../modules/vault/contract/src/vault-paste.ts
29630
+ var connectionName = external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/);
29631
+ var fieldName2 = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).max(64);
29632
+ var compareVaultSecretInputSchemaDefinition = external_exports.object({
29633
+ workspaceId: external_exports.string().min(1),
29634
+ name: connectionName,
29635
+ /** One field of the bundle; without it any field holding the value reads as `same`. */
29636
+ field: fieldName2.optional(),
29637
+ value: external_exports.string().min(1).max(2e4)
29638
+ });
29639
+ var compareVaultSecretInputSchema = compareVaultSecretInputSchemaDefinition;
29640
+ var vaultSecretComparisonSchemaDefinition = external_exports.object({
29641
+ result: external_exports.enum(["absent", "same", "different"]),
29642
+ connectionId: external_exports.string().nullable()
29643
+ });
29644
+ var vaultSecretComparisonSchema = vaultSecretComparisonSchemaDefinition;
29645
+ var replaceVaultSecretValueInputSchemaDefinition = external_exports.object({
29646
+ workspaceId: external_exports.string().min(1),
29647
+ connectionId: external_exports.string().min(1),
29648
+ /** The field to replace; without it, the connection's only field. */
29649
+ field: fieldName2.optional(),
29650
+ value: external_exports.string().min(1).max(2e4)
29651
+ });
29652
+ var replaceVaultSecretValueInputSchema = replaceVaultSecretValueInputSchemaDefinition;
29653
+
28884
29654
  // ../../modules/vault/contract/src/vault-reason.ts
28885
29655
  var COMMAND_STARTS = new Set(
28886
29656
  "sudo laze kv aws curl wget npm pnpm yarn npx git gh kubectl helm docker terraform python python3 node bash sh zsh export cd make cargo go ssh scp psql uv pip echo cat env clickhouse-client".split(
@@ -28972,9 +29742,12 @@ var vaultEgressTargetSchemaDefinition = external_exports.object({
28972
29742
  upstream: external_exports.url(),
28973
29743
  header: external_exports.string().min(1).default("Authorization"),
28974
29744
  prefix: external_exports.string().default("Bearer "),
28975
- field: vaultFieldNameSchema
29745
+ field: vaultFieldNameSchema,
29746
+ /** Paths whose writes ask a person at any level, such as sending mail; `*` is one segment. */
29747
+ askPaths: external_exports.array(external_exports.string().regex(/^\/[^?\s]*$/)).default([])
28976
29748
  });
28977
29749
  var vaultEgressTargetSchema = vaultEgressTargetSchemaDefinition;
29750
+ var VAULT_MAX_LEASE_SECONDS = 2 * 86400;
28978
29751
  var vaultConnectionSchemaDefinition = external_exports.object({
28979
29752
  id: external_exports.string().min(1),
28980
29753
  workspaceId: external_exports.string().min(1),
@@ -28989,6 +29762,8 @@ var vaultConnectionSchemaDefinition = external_exports.object({
28989
29762
  approverUserIds: external_exports.array(external_exports.string().min(1)),
28990
29763
  /** The longest lease an approver may give, set by an admin. */
28991
29764
  maxLeaseSeconds: external_exports.number().int().positive().nullable(),
29765
+ /** Every use asks a person: no lease is offered and none covers it. */
29766
+ everyUseAsks: external_exports.boolean().default(false),
28992
29767
  fieldNames: external_exports.array(vaultFieldNameSchema),
28993
29768
  egress: vaultEgressTargetSchema.nullable(),
28994
29769
  status: vaultConnectionStatusSchema,
@@ -29012,7 +29787,8 @@ var createVaultConnectionInputSchemaDefinition = external_exports.object({
29012
29787
  sensitivity: vaultSensitivitySchema,
29013
29788
  rules: external_exports.string().max(2e3).default(""),
29014
29789
  approverUserIds: external_exports.array(external_exports.string().min(1)).default([]),
29015
- maxLeaseSeconds: external_exports.number().int().positive().nullable().default(null),
29790
+ maxLeaseSeconds: external_exports.number().int().positive().max(VAULT_MAX_LEASE_SECONDS).nullable().default(null),
29791
+ everyUseAsks: external_exports.boolean().default(false),
29016
29792
  secret: vaultSecretBundleSchema,
29017
29793
  egress: vaultEgressTargetSchema.nullable().default(null)
29018
29794
  });
@@ -29024,7 +29800,8 @@ var updateVaultConnectionInputSchemaDefinition = external_exports.object({
29024
29800
  sensitivity: vaultSensitivitySchema.optional(),
29025
29801
  rules: external_exports.string().max(2e3).optional(),
29026
29802
  approverUserIds: external_exports.array(external_exports.string().min(1)).optional(),
29027
- maxLeaseSeconds: external_exports.number().int().positive().nullable().optional(),
29803
+ maxLeaseSeconds: external_exports.number().int().positive().max(VAULT_MAX_LEASE_SECONDS).nullable().optional(),
29804
+ everyUseAsks: external_exports.boolean().optional(),
29028
29805
  secret: vaultSecretBundleSchema.optional(),
29029
29806
  egress: vaultEgressTargetSchema.nullable().optional()
29030
29807
  });
@@ -29073,8 +29850,14 @@ var openVaultTurnInputSchemaDefinition = external_exports.object({
29073
29850
  channel: external_exports.object({ id: external_exports.string().min(1), kind: external_exports.enum(["public", "private", "dm"]) }),
29074
29851
  /** False when the requester can no longer see the channel (a schedule's creator who left). */
29075
29852
  requesterSeesChannel: external_exports.boolean().default(true),
29076
- /** The requester's messages this turn, for the judge; other senders are never permission. */
29077
- requesterMessages: external_exports.array(external_exports.string()).default([])
29853
+ /** The thread the agent answers in, so its approval cards show there; null for the channel. */
29854
+ threadRootId: external_exports.string().min(1).nullable().default(null),
29855
+ /** Where the agent runs: a Laze sandbox, or a machine that reaches Laze over the network. */
29856
+ origin: external_exports.enum(["sandbox", "network"]).default("sandbox"),
29857
+ /** The requester's messages in this conversation, oldest first: the judge's permission. */
29858
+ requesterMessages: external_exports.array(external_exports.string()).default([]),
29859
+ /** Everyone else's messages, oldest first: context for the judge, never permission. */
29860
+ otherMessages: external_exports.array(external_exports.object({ sender: external_exports.string().min(1), text: external_exports.string() })).default([])
29078
29861
  });
29079
29862
  var vaultAlwaysApprovalActionSchema = external_exports.enum([
29080
29863
  "send_external",
@@ -29085,6 +29868,11 @@ var vaultAlwaysApprovalActionSchema = external_exports.enum([
29085
29868
  ]);
29086
29869
  var VAULT_ALWAYS_APPROVAL_ACTIONS = vaultAlwaysApprovalActionSchema.options;
29087
29870
  var vaultPurposeSchema = external_exports.enum(["use", "send_external"]);
29871
+ var vaultDraftPreviewSchemaDefinition = external_exports.object({
29872
+ header: external_exports.string().max(2e3),
29873
+ body: external_exports.string().max(2e4)
29874
+ });
29875
+ var vaultDraftPreviewSchema = vaultDraftPreviewSchemaDefinition;
29088
29876
  var vaultUseInputSchemaDefinition = external_exports.object({
29089
29877
  turnToken: external_exports.string().min(1),
29090
29878
  /** The connection's name as the agent asks for it, or its id. */
@@ -29095,14 +29883,22 @@ var vaultUseInputSchemaDefinition = external_exports.object({
29095
29883
  /** OAuth scopes this use needs; a grant without them is refused with the missing ones. */
29096
29884
  requiredScopes: external_exports.array(external_exports.string().min(1)).default([]),
29097
29885
  /** A floor on the connection's sensitivity, for a tool its owner marked more guarded. */
29098
- atLeast: vaultSensitivitySchema.optional()
29886
+ atLeast: vaultSensitivitySchema.optional(),
29887
+ /** The caller knows the use only reads, such as an MCP tool marked read-only. */
29888
+ readOnly: external_exports.boolean().default(false),
29889
+ /** A use that would ask comes back denied with no card: a lookup made only for another card. */
29890
+ withoutAsking: external_exports.boolean().default(false),
29891
+ /** What a send would send, shown on its card; never stored. */
29892
+ preview: vaultDraftPreviewSchema.optional()
29099
29893
  });
29100
29894
  var vaultRunReleaseSchemaDefinition = external_exports.discriminatedUnion("outcome", [
29101
29895
  external_exports.object({
29102
29896
  outcome: external_exports.literal("allow"),
29103
29897
  env: external_exports.record(external_exports.string(), external_exports.string()),
29104
29898
  /** A sealed connection's values, encrypted to the run's own key. */
29105
- sealed: vaultRunSealedReleaseSchema.optional()
29899
+ sealed: vaultRunSealedReleaseSchema.optional(),
29900
+ /** What the approver told the agent, for the run to print. */
29901
+ message: external_exports.string().optional()
29106
29902
  }),
29107
29903
  external_exports.object({
29108
29904
  outcome: external_exports.literal("ask"),
@@ -29125,7 +29921,12 @@ var vaultEgressResolveInputSchemaDefinition = external_exports.object({
29125
29921
  var vaultEgressResolutionSchemaDefinition = external_exports.discriminatedUnion("decision", [
29126
29922
  external_exports.object({ decision: external_exports.literal("allow"), value: external_exports.string() }),
29127
29923
  external_exports.object({ decision: external_exports.literal("pending"), requestId: external_exports.string() }),
29128
- external_exports.object({ decision: external_exports.literal("deny"), code: external_exports.string() })
29924
+ external_exports.object({
29925
+ decision: external_exports.literal("deny"),
29926
+ code: external_exports.string(),
29927
+ /** Who denied it and the note they sent the agent, for the proxy to pass on. */
29928
+ message: external_exports.string().optional()
29929
+ })
29129
29930
  ]);
29130
29931
  var vaultEgressReleaseInputSchemaDefinition = external_exports.object({
29131
29932
  agentId: external_exports.string().min(1),
@@ -29135,6 +29936,10 @@ var vaultEgressReleaseInputSchemaDefinition = external_exports.object({
29135
29936
  /** The run's P-256 public key, base64url; a sealed connection is released only to it. */
29136
29937
  recipient: external_exports.string().max(200).optional()
29137
29938
  });
29939
+ var vaultEgressStatusInputSchemaDefinition = external_exports.object({
29940
+ agentId: external_exports.string().min(1),
29941
+ requestId: external_exports.string().min(1)
29942
+ });
29138
29943
  var vaultApprovalStateSchema = external_exports.enum([
29139
29944
  "pending",
29140
29945
  "approved_once",
@@ -29165,7 +29970,9 @@ var vaultApprovalRequestSchemaDefinition = external_exports.object({
29165
29970
  answeredByUserId: external_exports.string().nullable(),
29166
29971
  answeredVia: vaultAnswerViaSchema.nullable(),
29167
29972
  answeredAt: external_exports.iso.datetime().nullable(),
29168
- leaseExpiresAt: external_exports.iso.datetime().nullable()
29973
+ leaseExpiresAt: external_exports.iso.datetime().nullable(),
29974
+ /** What the person who answered told the agent; empty when nothing. */
29975
+ resolutionNote: external_exports.string().default("")
29169
29976
  });
29170
29977
  var vaultApprovalRequestSchema = vaultApprovalRequestSchemaDefinition;
29171
29978
  var answerVaultApprovalInputSchemaDefinition = external_exports.object({
@@ -29174,7 +29981,9 @@ var answerVaultApprovalInputSchemaDefinition = external_exports.object({
29174
29981
  answer: external_exports.enum(["approve_once", "approve_for_agent", "deny"]),
29175
29982
  /** Approve for this agent only: up to the connection's admin maximum. */
29176
29983
  durationSeconds: external_exports.number().int().positive().optional(),
29177
- via: vaultAnswerViaSchema
29984
+ via: vaultAnswerViaSchema,
29985
+ /** A short reason, question or steer for the agent, sent with the answer. */
29986
+ resolutionNote: resolutionNoteSchema
29178
29987
  });
29179
29988
  var answerVaultApprovalInputSchema = answerVaultApprovalInputSchemaDefinition;
29180
29989
  var requestVaultActionInputSchemaDefinition = external_exports.object({
@@ -29210,6 +30019,40 @@ var vaultAuditPageSchemaDefinition = external_exports.object({
29210
30019
  chain: external_exports.object({ intact: external_exports.boolean(), brokenAtSeq: external_exports.number().int().nullable() })
29211
30020
  });
29212
30021
  var vaultAuditPageSchema = vaultAuditPageSchemaDefinition;
30022
+ var vaultMostAskedInputSchemaDefinition = external_exports.object({
30023
+ workspaceId: external_exports.string().min(1),
30024
+ days: external_exports.number().int().min(1).max(365).default(30)
30025
+ });
30026
+ var vaultMostAskedInputSchema = vaultMostAskedInputSchemaDefinition;
30027
+ var vaultMostAskedSchemaDefinition = external_exports.object({
30028
+ days: external_exports.number().int().positive(),
30029
+ since: external_exports.iso.datetime(),
30030
+ /** Per connection, the cards that reached a person, most first; owner actions are left out. */
30031
+ connections: external_exports.array(
30032
+ external_exports.object({
30033
+ connectionId: external_exports.string(),
30034
+ cards: external_exports.number().int().nonnegative(),
30035
+ /** Every ask that joined one of those cards. */
30036
+ asks: external_exports.number().int().nonnegative(),
30037
+ approved: external_exports.number().int().nonnegative(),
30038
+ denied: external_exports.number().int().nonnegative(),
30039
+ expired: external_exports.number().int().nonnegative(),
30040
+ pending: external_exports.number().int().nonnegative(),
30041
+ /** Cards by why the vault asked: `sensitivity_ask`, `judge_unsure`, `rate_limited`... */
30042
+ why: external_exports.record(external_exports.string(), external_exports.number().int().nonnegative()),
30043
+ /** The newest distinct reasons and commands callers gave, redacted like every audit line. */
30044
+ samples: external_exports.array(
30045
+ external_exports.object({
30046
+ at: external_exports.iso.datetime(),
30047
+ agentId: external_exports.string().nullable(),
30048
+ reason: external_exports.string().nullable(),
30049
+ command: external_exports.array(external_exports.string())
30050
+ })
30051
+ )
30052
+ })
30053
+ )
30054
+ });
30055
+ var vaultMostAskedSchema = vaultMostAskedSchemaDefinition;
29213
30056
  var vaultConnectionUseSchemaDefinition = external_exports.object({
29214
30057
  at: external_exports.iso.datetime(),
29215
30058
  action: external_exports.string(),
@@ -29233,6 +30076,22 @@ var saveGithubInstallationInputSchemaDefinition = external_exports.object({
29233
30076
  /** The connection to point at this installation again, after a reinstall. */
29234
30077
  connectionId: external_exports.string().optional()
29235
30078
  });
30079
+ var vaultJudgeStatusSchemaDefinition = external_exports.object({ configured: external_exports.boolean() });
30080
+ var vaultJudgeStatusSchema = vaultJudgeStatusSchemaDefinition;
30081
+ var vaultLimitSubjectSchema = external_exports.string().regex(/^[a-z0-9-]+(\.[a-z]+)?$/);
30082
+ var vaultLevelLimitSchemaDefinition = external_exports.object({
30083
+ subject: vaultLimitSubjectSchema,
30084
+ most: vaultSensitivitySchema.exclude(["auto"]),
30085
+ updatedByUserId: external_exports.string(),
30086
+ updatedAt: external_exports.iso.datetime()
30087
+ });
30088
+ var vaultLevelLimitSchema = vaultLevelLimitSchemaDefinition;
30089
+ var setVaultLevelLimitInputSchemaDefinition = external_exports.object({
30090
+ workspaceId: external_exports.string().min(1),
30091
+ subject: vaultLimitSubjectSchema,
30092
+ most: vaultSensitivitySchema.exclude(["auto"]).nullable()
30093
+ });
30094
+ var setVaultLevelLimitInputSchema = setVaultLevelLimitInputSchemaDefinition;
29236
30095
  var VaultApi = moduleApi()("vault");
29237
30096
  var workspaceInput = external_exports.object({ workspaceId: external_exports.string().min(1) });
29238
30097
  var connectionInput = external_exports.object({
@@ -29243,25 +30102,34 @@ var vaultTrpc = defineTrpcContract("vault").mutation("createConnection").withInp
29243
30102
  external_exports.object({ ...connectionInput.shape, limit: external_exports.number().int().min(1).max(200).optional() })
29244
30103
  ).withOutput(external_exports.array(vaultConnectionUseSchema)).mutation("revealConnection").withInput(external_exports.object({ ...connectionInput.shape, reason: external_exports.string().trim().min(1).max(200) })).withOutput(external_exports.object({ fields: external_exports.record(external_exports.string(), external_exports.string()) })).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(
29245
30104
  external_exports.object({ ...workspaceInput.shape, limit: external_exports.number().int().min(1).max(500).optional() })
29246
- ).withOutput(vaultAuditPageSchema).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(external_exports.object({ ...connectionInput.shape, reason: external_exports.string().trim().min(1).max(200) })).withOutput(vaultSealedMaterialSchema).query("getSealedApproval").withInput(external_exports.object({ ...workspaceInput.shape, requestId: external_exports.string().min(1) })).withOutput(vaultSealedApprovalSchema).mutation("answerSealedApproval").withInput(answerSealedApprovalInputSchema).withOutput(vaultApprovalRequestSchema).build();
30105
+ ).withOutput(vaultAuditPageSchema).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(external_exports.object({ ...connectionInput.shape, reason: external_exports.string().trim().min(1).max(200) })).withOutput(vaultSealedMaterialSchema).query("getSealedApproval").withInput(external_exports.object({ ...workspaceInput.shape, requestId: external_exports.string().min(1) })).withOutput(vaultSealedApprovalSchema).mutation("answerSealedApproval").withInput(answerSealedApprovalInputSchema).withOutput(vaultApprovalRequestSchema).mutation("compareSecret").withInput(compareVaultSecretInputSchema).withOutput(vaultSecretComparisonSchema).mutation("replaceSecretValue").withInput(replaceVaultSecretValueInputSchema).withOutput(vaultConnectionSchema).build();
29247
30106
 
29248
30107
  // ../../modules/vault/contract/src/vault.config.ts
29249
30108
  var positiveInt = external_exports.coerce.number().int().positive();
29250
30109
  var vaultConfig = Config.define((c) => ({
29251
- approvalTimeoutMinutes: c.env("VAULT_APPROVAL_TIMEOUT_MINUTES", positiveInt.default(60)),
30110
+ approvalTimeoutMinutes: c.env("VAULT_APPROVAL_TIMEOUT_MINUTES", positiveInt.default(720)),
29252
30111
  /** How long an approve-once answer waits for the same ask to come back for it. */
29253
30112
  approveOnceWindowMinutes: c.env("VAULT_APPROVE_ONCE_WINDOW_MINUTES", positiveInt.default(10)),
29254
30113
  /** The lease cap when an admin set none on the connection. */
29255
- defaultMaxLeaseHours: c.env("VAULT_DEFAULT_MAX_LEASE_HOURS", positiveInt.default(168)),
30114
+ defaultMaxLeaseHours: c.env("VAULT_DEFAULT_MAX_LEASE_HOURS", positiveInt.default(48)),
29256
30115
  turnTtlMinutes: c.env("VAULT_TURN_TTL_MINUTES", positiveInt.default(720)),
29257
30116
  judgeTimeoutMs: c.env("VAULT_JUDGE_TIMEOUT_MS", positiveInt.default(2e4)),
30117
+ /** How long one judge answer is reused for the same question. */
30118
+ judgeCacheMinutes: c.env("VAULT_JUDGE_CACHE_MINUTES", positiveInt.default(10)),
30119
+ /** The judge's price per input token in nano-USD, metered on the workspace's usage. */
30120
+ judgeNanoUsdPerInputToken: c.env(
30121
+ "VAULT_JUDGE_NANO_USD_PER_INPUT_TOKEN",
30122
+ positiveInt.default(100)
30123
+ ),
29258
30124
  /** How long after a process starts it still accepts turn tokens signed under the previous key. */
29259
30125
  previousKeyGraceMinutes: c.env(
29260
30126
  "VAULT_PREVIOUS_KEY_GRACE_MINUTES",
29261
30127
  external_exports.coerce.number().int().min(0).default(30)
29262
30128
  ),
29263
30129
  /** The origin passkeys are made on; unset accepts any localhost origin, for development. */
29264
- publicBaseUrl
30130
+ publicBaseUrl,
30131
+ /** A file each audit chain head is appended to as a JSON line; unset only logs the heads. */
30132
+ auditHeadFile: c.env("VAULT_AUDIT_HEAD_FILE", external_exports.string().min(1).optional())
29265
30133
  }));
29266
30134
 
29267
30135
  // ../../modules/vault/contract/src/vault.errors.ts
@@ -29289,7 +30157,20 @@ var vaultApprovalRequestedPayloadSchemaDefinition = external_exports.object({
29289
30157
  why: external_exports.string(),
29290
30158
  /** The longest lease the card may offer; null offers only once and deny. */
29291
30159
  maxLeaseSeconds: external_exports.number().int().positive().nullable(),
29292
- expiresAt: external_exports.iso.datetime()
30160
+ expiresAt: external_exports.iso.datetime(),
30161
+ /** The thread the agent was answering in; null when it answered in the channel. */
30162
+ threadRootId: external_exports.string().nullable().default(null),
30163
+ /** What the agent asked to do, as one line: a command, a request line or a tool. */
30164
+ line: external_exports.string().default(""),
30165
+ /** For a relay call, the host and method it goes to. */
30166
+ host: external_exports.string().nullable().default(null),
30167
+ method: external_exports.string().nullable().default(null),
30168
+ /** The automatic check's plain line on a Checked use; null when none ran. Odds stay in audit. */
30169
+ judgeLine: external_exports.string().nullable().default(null),
30170
+ /** Sealed to the owner's devices: Allow goes through the device's passkey, deny does not. */
30171
+ sealed: external_exports.boolean().default(false),
30172
+ /** For a send, the message it would send, read from the provider. */
30173
+ draft: vaultDraftPreviewSchema.nullable().default(null)
29293
30174
  });
29294
30175
  var vaultApprovalResolvedPayloadSchemaDefinition = external_exports.object({
29295
30176
  workspaceId: external_exports.string().min(1),
@@ -29298,7 +30179,16 @@ var vaultApprovalResolvedPayloadSchemaDefinition = external_exports.object({
29298
30179
  answeredByUserId: external_exports.string().nullable(),
29299
30180
  answeredVia: external_exports.string().nullable(),
29300
30181
  leaseExpiresAt: external_exports.iso.datetime().nullable(),
29301
- at: external_exports.iso.datetime()
30182
+ at: external_exports.iso.datetime(),
30183
+ /** Where the agent asked, so a note can steer its next turn there. */
30184
+ agentId: external_exports.string().default(""),
30185
+ channelId: external_exports.string().default(""),
30186
+ /** What the person who answered told the agent; empty when nothing. */
30187
+ resolutionNote: external_exports.string().default(""),
30188
+ /** The display name of who answered; empty for a timeout. */
30189
+ answeredByName: external_exports.string().default(""),
30190
+ /** A deny or a timeout: until when the same action is refused without a new card. */
30191
+ blockedUntil: external_exports.iso.datetime().nullable().default(null)
29302
30192
  });
29303
30193
 
29304
30194
  // ../../modules/vault/contract/src/vault-oauth.ts
@@ -29377,9 +30267,12 @@ async function sharedSecret(input2) {
29377
30267
  );
29378
30268
  return new Uint8Array(bits);
29379
30269
  }
30270
+ function agreementOf(privateKey) {
30271
+ return (publicKey) => sharedSecret({ privateKey, publicKey });
30272
+ }
29380
30273
  async function decryptFrom(input2) {
29381
30274
  const epk = fromBase64Url(input2.epk);
29382
- const ikm = await sharedSecret({ privateKey: input2.privateKey, publicKey: epk });
30275
+ const ikm = await input2.agree(epk);
29383
30276
  const key = await hkdfKey({ ikm, salt: concat(epk, input2.publicKey), info: input2.info });
29384
30277
  return gcmOpen({ key, sealed: fromBase64Url(input2.sealed), aad: input2.aad });
29385
30278
  }
@@ -29397,7 +30290,7 @@ function fieldsOf(value) {
29397
30290
  var releaseLabel = (requestId) => `laze-vault/release/v1/${requestId}`;
29398
30291
  async function openRunRelease(input2) {
29399
30292
  const plain = await decryptFrom({
29400
- privateKey: input2.runPrivateKey,
30293
+ agree: agreementOf(input2.runPrivateKey),
29401
30294
  publicKey: fromBase64Url(input2.runPublicKey),
29402
30295
  epk: input2.release.epk,
29403
30296
  sealed: input2.release.ciphertext,
@@ -29408,10 +30301,14 @@ async function openRunRelease(input2) {
29408
30301
  }
29409
30302
 
29410
30303
  // src/services/vault-commands.service.ts
29411
- var USAGE6 = `Usage: laze vault run <connection> --reason "<one plain sentence>" [--wait <s>] -- <command> [args...]
30304
+ var USAGE7 = `Usage: laze vault run <connection> --reason "<one plain sentence>" [--wait <s>] -- <command> [args...]
29412
30305
  Runs the command with the connection's values in its environment, inside a Laze agent's sandbox.`;
29413
30306
  var POLL_MS = 3e3;
29414
- var DEFAULT_WAIT_SECONDS = 120;
30307
+ var RESTART_WAIT_MS = 12e4;
30308
+ var RESTART_BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 1e4];
30309
+ var P2562 = { name: "ECDH", namedCurve: "P-256" };
30310
+ var ServerGoneError = class extends Error {
30311
+ };
29415
30312
  function proxyOf({ env }) {
29416
30313
  const raw = env.HTTP_PROXY ?? env.http_proxy;
29417
30314
  if (!raw) throw new CliUsageError("laze vault run works only inside a Laze agent's sandbox");
@@ -29423,42 +30320,103 @@ function proxyOf({ env }) {
29423
30320
  authorization: `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`
29424
30321
  };
29425
30322
  }
29426
- async function turnTokenOf({
30323
+ function turnFileOf({
29427
30324
  env
29428
30325
  }) {
29429
- const path = env.LAZE_TURN_FILE;
29430
- if (!path) throw new CliUsageError("laze vault run works only during an agent's turn");
30326
+ return env.LAZE_TURN_FILE || void 0;
30327
+ }
30328
+ async function turnTokenOf2({
30329
+ env,
30330
+ turnFile
30331
+ }) {
30332
+ const pinned = env.LAZE_TURN_TOKEN?.trim();
30333
+ if (pinned) return pinned;
30334
+ if (!turnFile) throw new CliUsageError("laze vault run works only during an agent's turn");
29431
30335
  try {
29432
- return (await readFile9(path, "utf8")).trim();
30336
+ return (await readFile11(turnFile, "utf8")).trim();
29433
30337
  } catch {
29434
30338
  throw new CliUsageError("laze vault run works only during an agent's turn");
29435
30339
  }
29436
30340
  }
29437
- async function askRelease({
29438
- context,
29439
- connection,
29440
- reason,
29441
- command,
29442
- recipient
30341
+ function runKeyPathOf({
30342
+ turnFile,
30343
+ connection
29443
30344
  }) {
29444
- const env = context.environment.inherited;
29445
- const proxy = proxyOf({ env });
29446
- const response = await context.fetcher(`${proxy.origin}/vault/release`, {
29447
- method: "POST",
29448
- headers: {
29449
- "content-type": "application/json",
29450
- "proxy-authorization": proxy.authorization,
29451
- "x-laze-turn": await turnTokenOf({ env })
29452
- },
29453
- body: JSON.stringify({ connection, reason, command, recipient })
30345
+ const name2 = createHash("sha256").update(connection).digest("hex");
30346
+ const base = turnFile ? dirname4(turnFile) : join7(tmpdir(), "laze-vault");
30347
+ return join7(base, "vault-run-keys", `${name2}.json`);
30348
+ }
30349
+ async function runKeyAt({ path }) {
30350
+ const saved = await readFile11(path, "utf8").then(
30351
+ (text2) => JSON.parse(text2),
30352
+ () => void 0
30353
+ );
30354
+ if (saved && typeof saved.publicKey === "string" && saved.privateKey) {
30355
+ const privateKey = await globalThis.crypto.subtle.importKey("jwk", saved.privateKey, P2562, false, ["deriveBits"]).catch(() => void 0);
30356
+ if (privateKey) return { privateKey, publicKey: saved.publicKey };
30357
+ }
30358
+ const fresh = await generateEcdhKeys();
30359
+ const jwk = await globalThis.crypto.subtle.exportKey("jwk", fresh.privateKey);
30360
+ await mkdir6(dirname4(path), { recursive: true, mode: 448 });
30361
+ await writeFile6(path, JSON.stringify({ publicKey: fresh.publicKey, privateKey: jwk }), {
30362
+ mode: 384
29454
30363
  });
30364
+ return fresh;
30365
+ }
30366
+ function connectionRefused(error62) {
30367
+ const cause = error62.cause;
30368
+ if (cause?.code === "ECONNREFUSED") return true;
30369
+ return cause?.errors?.some((inner) => inner.code === "ECONNREFUSED") ?? false;
30370
+ }
30371
+ async function sendOnce({
30372
+ context,
30373
+ url: url2,
30374
+ init
30375
+ }) {
30376
+ let response;
30377
+ try {
30378
+ response = await context.fetcher(url2, init);
30379
+ } catch (error62) {
30380
+ const wasRefused = connectionRefused(error62);
30381
+ if (init.method === "POST" && !wasRefused) throw error62;
30382
+ return { restarting: wasRefused ? "connection refused" : String(error62) };
30383
+ }
30384
+ if (response.status !== 502 && response.status !== 503) return { response };
30385
+ const code = codeOf(await response.clone().text());
30386
+ const neverReached = response.status === 503 && code === "vault_not_answering";
30387
+ if (init.method === "POST" && !neverReached) return { response };
30388
+ return { restarting: `status ${response.status}${code ? ` ${code}` : ""}` };
30389
+ }
30390
+ async function reach(input2) {
30391
+ const { context } = input2;
30392
+ const started = context.nowMs();
30393
+ for (let attempt = 0; ; attempt += 1) {
30394
+ const sent = await sendOnce(input2);
30395
+ if ("response" in sent) return sent.response;
30396
+ if (context.nowMs() - started >= RESTART_WAIT_MS) {
30397
+ throw new ServerGoneError(
30398
+ `laze vault: the Laze server did not come back (${sent.restarting})`
30399
+ );
30400
+ }
30401
+ if (attempt === 0) {
30402
+ context.err(
30403
+ `laze vault: the Laze server is not answering (${sent.restarting}), probably restarting; waiting up to 2 min...
30404
+ `
30405
+ );
30406
+ }
30407
+ await context.sleep(RESTART_BACKOFF_MS[Math.min(attempt, RESTART_BACKOFF_MS.length - 1)] ?? 0);
30408
+ }
30409
+ }
30410
+ function codeOf(text2) {
30411
+ return /"code"\s*:\s*"([^"]+)"/.exec(text2)?.[1];
30412
+ }
30413
+ async function releaseOf({ response }) {
29455
30414
  const text2 = await response.text();
29456
30415
  if (!response.ok) {
29457
- const code = /"code"\s*:\s*"([^"]+)"/.exec(text2)?.[1] ?? `status ${response.status}`;
29458
30416
  return {
29459
30417
  outcome: "deny",
29460
30418
  exitCode: 1,
29461
- message: `laze vault: the release was refused (${code})`
30419
+ message: `laze vault: the release was refused (${codeOf(text2) ?? `status ${response.status}`})`
29462
30420
  };
29463
30421
  }
29464
30422
  return JSON.parse(text2);
@@ -29476,49 +30434,91 @@ async function envOf({
29476
30434
  });
29477
30435
  return { ...release.env, ...sealed };
29478
30436
  }
30437
+ function deadlineOf({ args, nowMs }) {
30438
+ const wait = flagText({ args, name: "wait" }) || (args.flags.wait === true ? args.words[3] : "");
30439
+ if (!wait) return Infinity;
30440
+ if (!(Number(wait) >= 0)) throw new CliUsageError(`--wait takes a number of seconds
30441
+ ${USAGE7}`);
30442
+ return nowMs + Number(wait) * 1e3;
30443
+ }
30444
+ async function untilAnswered({
30445
+ context,
30446
+ first,
30447
+ origin,
30448
+ headers,
30449
+ deadline
30450
+ }) {
30451
+ let release = first;
30452
+ while (release.outcome === "ask") {
30453
+ if (context.nowMs() + POLL_MS > deadline) {
30454
+ context.err(`${release.message}. Not approved yet; ask again later.
30455
+ `);
30456
+ return "capped";
30457
+ }
30458
+ if (release === first) {
30459
+ context.err(`${release.message}. Waiting for an answer (cards expire after 12 hours)...
30460
+ `);
30461
+ }
30462
+ await context.sleep(POLL_MS);
30463
+ const url2 = `${origin}/vault/release/${encodeURIComponent(release.requestId)}`;
30464
+ release = await releaseOf({
30465
+ response: await reach({ context, url: url2, init: { method: "GET", headers } })
30466
+ });
30467
+ }
30468
+ return release;
30469
+ }
29479
30470
  async function vaultRun({
29480
30471
  context,
29481
30472
  args
29482
30473
  }) {
29483
30474
  const connection = args.words[2];
29484
30475
  const reason = flagText({ args, name: "reason" });
29485
- if (!connection || args.rest.length === 0) throw new CliUsageError(USAGE6);
30476
+ if (!connection || args.rest.length === 0) throw new CliUsageError(USAGE7);
29486
30477
  if (!reason) throw new CliUsageError(`Say why with --reason "<one plain sentence>"
29487
- ${USAGE6}`);
29488
- const waitSeconds = Number(flagText({ args, name: "wait", fallback: `${DEFAULT_WAIT_SECONDS}` }));
29489
- const deadline = context.nowMs() + Math.max(0, waitSeconds) * 1e3;
29490
- let told = false;
29491
- const run = await generateEcdhKeys();
29492
- for (; ; ) {
29493
- const release = await askRelease({
29494
- context,
29495
- connection,
29496
- reason,
29497
- command: args.rest,
29498
- recipient: run.publicKey
30478
+ ${USAGE7}`);
30479
+ const deadline = deadlineOf({ args, nowMs: context.nowMs() });
30480
+ const env = context.environment.inherited;
30481
+ const proxy = proxyOf({ env });
30482
+ const turnFile = turnFileOf({ env });
30483
+ const headers = {
30484
+ "proxy-authorization": proxy.authorization,
30485
+ "x-laze-turn": await turnTokenOf2({ env, turnFile })
30486
+ };
30487
+ const keyPath = runKeyPathOf({ turnFile, connection });
30488
+ const run = await runKeyAt({ path: keyPath });
30489
+ const body = JSON.stringify({ connection, reason, command: args.rest, recipient: run.publicKey });
30490
+ let release;
30491
+ try {
30492
+ const first = await releaseOf({
30493
+ response: await reach({
30494
+ context,
30495
+ url: `${proxy.origin}/vault/release`,
30496
+ init: { method: "POST", headers: { ...headers, "content-type": "application/json" }, body }
30497
+ })
29499
30498
  });
29500
- if (release.outcome === "allow") {
29501
- return context.runCommand({ command: args.rest, env: await envOf({ release, run }) });
29502
- }
29503
- if (release.outcome === "deny") {
29504
- context.err(`${release.message}
29505
- `);
29506
- return release.exitCode || 1;
29507
- }
29508
- if (context.nowMs() + POLL_MS > deadline) {
29509
- context.err(`${release.message}. Not approved yet; ask again later.
30499
+ release = await untilAnswered({ context, first, origin: proxy.origin, headers, deadline });
30500
+ } catch (error62) {
30501
+ if (!(error62 instanceof ServerGoneError)) throw error62;
30502
+ context.err(`${error62.message}. Run the command again to keep waiting.
29510
30503
  `);
29511
- return 75;
29512
- }
29513
- if (!told) context.err(`${release.message}...
30504
+ return 75;
30505
+ }
30506
+ if (release === "capped") return 75;
30507
+ if (release.outcome === "deny") {
30508
+ await rm3(keyPath, { force: true });
30509
+ context.err(`${release.message}
29514
30510
  `);
29515
- told = true;
29516
- await context.sleep(POLL_MS);
30511
+ return release.exitCode || 1;
29517
30512
  }
30513
+ const childEnv = await envOf({ release, run });
30514
+ await rm3(keyPath, { force: true });
30515
+ if (release.message) context.err(`${release.message}
30516
+ `);
30517
+ return context.runCommand({ command: args.rest, env: childEnv });
29518
30518
  }
29519
30519
 
29520
30520
  // src/services/wiki-commands.service.ts
29521
- var USAGE7 = `Usage:
30521
+ var USAGE8 = `Usage:
29522
30522
  laze wiki list
29523
30523
  laze wiki get <path>
29524
30524
  laze wiki put <path> <markdown|@file|-> [--title <title>] [--channel <channel>] [--base <version>]
@@ -29535,7 +30535,7 @@ async function wikiList({ context, args }) {
29535
30535
  }
29536
30536
  async function wikiGet({ context, args }) {
29537
30537
  const path = args.words[2];
29538
- if (!path) throw new CliUsageError(USAGE7);
30538
+ if (!path) throw new CliUsageError(USAGE8);
29539
30539
  const { client } = await signedIn({ context, args });
29540
30540
  const page = await client.post("/api/wiki/get", { path });
29541
30541
  if (args.flags.json) return context.out(`${JSON.stringify(page, null, 2)}
@@ -29552,7 +30552,7 @@ Linked from: ${page.backlinks.map((link) => link.path).join(", ")}
29552
30552
  async function wikiPut({ context, args }) {
29553
30553
  const path = args.words[2];
29554
30554
  const value = args.words.slice(3).join(" ");
29555
- if (!path || !value) throw new CliUsageError(USAGE7);
30555
+ if (!path || !value) throw new CliUsageError(USAGE8);
29556
30556
  const body = await textArgument({ context, value });
29557
30557
  const title = flagText({ args, name: "title" });
29558
30558
  const channel = flagText({ args, name: "channel" });
@@ -29572,7 +30572,7 @@ async function wikiPut({ context, args }) {
29572
30572
  }
29573
30573
  async function wikiSearch({ context, args }) {
29574
30574
  const query = args.words.slice(2).join(" ").trim();
29575
- if (!query) throw new CliUsageError(USAGE7);
30575
+ if (!query) throw new CliUsageError(USAGE8);
29576
30576
  const { client } = await signedIn({ context, args });
29577
30577
  const hits = await client.post("/api/wiki/search", { query });
29578
30578
  if (args.flags.json) return context.out(`${JSON.stringify(hits, null, 2)}
@@ -29584,7 +30584,7 @@ var WIKI_COMMANDS = { list: wikiList, get: wikiGet, put: wikiPut, search: wikiSe
29584
30584
  async function wiki({ context, args }) {
29585
30585
  const name2 = args.words[1] ?? "";
29586
30586
  const command = Object.hasOwn(WIKI_COMMANDS, name2) ? WIKI_COMMANDS[name2] : void 0;
29587
- if (!command) throw new CliUsageError(USAGE7);
30587
+ if (!command) throw new CliUsageError(USAGE8);
29588
30588
  await command({ context, args });
29589
30589
  }
29590
30590
 
@@ -29593,6 +30593,7 @@ var HELP = `laze: work in a Laze workspace from your terminal or your agent.
29593
30593
 
29594
30594
  laze login [--url <server>] [--token lz_...] sign in (opens a browser code)
29595
30595
  laze --version the version of this CLI
30596
+ laze update [--dry-run] install the latest release (npm, pnpm, bun or yarn)
29596
30597
  laze logout forget this profile
29597
30598
  laze whoami who this key speaks for
29598
30599
  laze workspaces your signed-in profiles
@@ -29613,6 +30614,8 @@ var HELP = `laze: work in a Laze workspace from your terminal or your agent.
29613
30614
  laze agent link --token <token> connect this Claude Code or Codex session
29614
30615
  laze adopt codex [--thread <id>] answer Laze messages in a Codex thread
29615
30616
  laze vault run <name> --reason "<why>" -- <cmd> run a command with a vault connection (agents)
30617
+ laze browser setup|cdp|screen|status <machine> a machine with Chrome a person signs in on, for agents
30618
+ Sites without an API need a person's login: offer a browser machine, never ask for a password
29616
30619
  laze github whoami | watch <owner/repo> | unwatch | watches the agent's GitHub identity and watches
29617
30620
  laze agent set-github <agent> <connection|none> [--ask-before-push] pick an agent's GitHub
29618
30621
  laze mcp the same as an MCP server on stdio
@@ -29625,6 +30628,7 @@ var COMMANDS = {
29625
30628
  logout,
29626
30629
  whoami,
29627
30630
  workspaces,
30631
+ update,
29628
30632
  "channels list": listChannels,
29629
30633
  channels: listChannels,
29630
30634
  post,
@@ -29635,6 +30639,7 @@ var COMMANDS = {
29635
30639
  agents: listAgents,
29636
30640
  "agent link": linkAgent,
29637
30641
  "hook stop": stopHook,
30642
+ "hook pre-tool": preToolHook,
29638
30643
  adopt: adoptCodex,
29639
30644
  feed,
29640
30645
  schedule,
@@ -29643,6 +30648,7 @@ var COMMANDS = {
29643
30648
  skill,
29644
30649
  app,
29645
30650
  "vault run": vaultRun,
30651
+ browser,
29646
30652
  github,
29647
30653
  "agent set-github": setAgentGithub,
29648
30654
  "mcp gateway": serveMcpGateway,
@@ -29680,21 +30686,27 @@ async function runCli({
29680
30686
  }
29681
30687
 
29682
30688
  // src/main.ts
29683
- function runCommand({
30689
+ async function runCommand({
29684
30690
  command,
29685
30691
  inherited,
29686
- env
30692
+ env,
30693
+ stdout = process.stdout,
30694
+ stderr = process.stderr
29687
30695
  }) {
29688
30696
  const [program = "", ...rest] = command;
29689
- return new Promise((resolve6) => {
29690
- const child = spawn2(program, rest, { stdio: "inherit", env: { ...inherited, ...env } });
29691
- child.on("error", (error62) => {
29692
- process.stderr.write(`laze vault: ${error62.message}
29693
- `);
29694
- resolve6(127);
30697
+ try {
30698
+ return await runRedacted({
30699
+ command: [program, ...rest],
30700
+ env,
30701
+ baseEnv: inherited,
30702
+ stdout,
30703
+ stderr
29695
30704
  });
29696
- child.on("exit", (code, signal) => resolve6(code ?? (signal ? 128 : 1)));
29697
- });
30705
+ } catch (error62) {
30706
+ stderr.write(`laze vault: ${error62 instanceof Error ? error62.message : String(error62)}
30707
+ `);
30708
+ return 127;
30709
+ }
29698
30710
  }
29699
30711
  async function readStdin() {
29700
30712
  if (stdin.isTTY) return "";
@@ -29702,6 +30714,20 @@ async function readStdin() {
29702
30714
  for await (const chunk of stdin) chunks.push(chunk);
29703
30715
  return Buffer.concat(chunks).toString("utf8");
29704
30716
  }
30717
+ function sleep(ms, signal) {
30718
+ return new Promise((resolve6) => {
30719
+ if (signal?.aborted) return resolve6();
30720
+ const stop = () => {
30721
+ clearTimeout(timer);
30722
+ resolve6();
30723
+ };
30724
+ const timer = setTimeout(() => {
30725
+ signal?.removeEventListener("abort", stop);
30726
+ resolve6();
30727
+ }, ms);
30728
+ signal?.addEventListener("abort", stop, { once: true });
30729
+ });
30730
+ }
29705
30731
  if (true) {
29706
30732
  const environment = cliEnvironment();
29707
30733
  process.exitCode = await runCli({
@@ -29715,9 +30741,12 @@ if (true) {
29715
30741
  out: (text2) => void process.stdout.write(text2),
29716
30742
  err: (text2) => void process.stderr.write(text2),
29717
30743
  readStdin,
29718
- sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
30744
+ sleep,
29719
30745
  nowMs: () => nowInstant().epochMilliseconds,
29720
30746
  runCommand: (input2) => runCommand({ ...input2, inherited: environment.inherited })
29721
30747
  }
29722
30748
  });
29723
30749
  }
30750
+ export {
30751
+ runCommand
30752
+ };