artifact-cafe 0.0.16 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +931 -615
  2. package/package.json +10 -9
package/dist/index.js CHANGED
@@ -7,24 +7,17 @@ import { defineCommand, runMain, showUsage } from "citty";
7
7
  var AMBER = "\x1B[38;2;214;138;57m";
8
8
  var BOLD = "\x1B[1m";
9
9
  var RESET = "\x1B[0m";
10
- var MUG = [
11
- " \u2596",
12
- " \u2598 \u259D",
13
- " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E",
14
- " \u2502 \u251C\u2500\u256E",
15
- " \u2502 \u2502 \u2502",
16
- " \u2502 \u251C\u2500\u256F",
17
- " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
18
- " \u203E\u203E\u203E\u203E\u203E\u203E\u203E\u203E"
19
- ];
10
+ var TICK_TOP = " \u250C \u2510";
11
+ var GLYPH = " \xE3";
12
+ var TICK_BOTTOM = " \u2514 \u2518";
20
13
  var WORDMARK = " artifact.cafe";
21
14
  function printBanner() {
22
15
  const color = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
23
16
  const paint = (line, codes) => color ? `${codes}${line}${RESET}` : line;
24
17
  console.log();
25
- for (const line of MUG) {
26
- console.log(paint(line, AMBER));
27
- }
18
+ console.log(paint(TICK_TOP, AMBER));
19
+ console.log(paint(GLYPH, BOLD));
20
+ console.log(paint(TICK_BOTTOM, AMBER));
28
21
  console.log();
29
22
  console.log(paint(WORDMARK, AMBER + BOLD));
30
23
  console.log();
@@ -69,8 +62,10 @@ var meResponseSchema = z.object({
69
62
  userId: z.string()
70
63
  });
71
64
  var artifactListItemSchema = z.object({
65
+ autoDeleteAt: z.string().datetime().nullable().optional(),
72
66
  createdAt: z.string(),
73
67
  currentVersionNumber: z.number().int().positive().nullable(),
68
+ expiresAt: z.string().datetime().nullable().optional(),
74
69
  id: z.string(),
75
70
  openCommentCount: z.number().int().nonnegative(),
76
71
  slug: z.string(),
@@ -181,7 +176,7 @@ var workspaceAvatarResponseSchema = z3.object({
181
176
  });
182
177
 
183
178
  // ../shared/src/comments.ts
184
- import { z as z7 } from "zod";
179
+ import { z as z8 } from "zod";
185
180
 
186
181
  // ../shared/src/emoji.ts
187
182
  import { z as z4 } from "zod";
@@ -193,15 +188,38 @@ function canonicalizeEmoji(s) {
193
188
  var iconEmojiSchema = z4.string().max(64).transform(canonicalizeEmoji).refine((s) => SINGLE_EMOJI.test(s), { message: "Must be a single emoji." }).nullable().describe("A single emoji shown as the icon, or null for the default.");
194
189
 
195
190
  // ../shared/src/publish.ts
196
- import { z as z6 } from "zod";
191
+ import { z as z7 } from "zod";
197
192
 
198
- // ../shared/src/review-mode.ts
193
+ // ../shared/src/expiration.ts
199
194
  import { z as z5 } from "zod";
200
- var reviewModeSchema = z5.enum(["open", "readonly", "off"]);
201
- var setReviewModeSchema = z5.object({
195
+ var MAX_EXPIRATION_SECONDS = 2147483647;
196
+ var expirationSecondsSchema = z5.number().int().positive().max(MAX_EXPIRATION_SECONDS);
197
+ var setExpirationSchema = z5.object({
198
+ expiresInSeconds: expirationSecondsSchema.nullable()
199
+ });
200
+ var expirationResponseSchema = z5.object({
201
+ autoDeleteAt: z5.string().datetime().nullable(),
202
+ expiresAt: z5.string().datetime().nullable()
203
+ });
204
+ function parseArtifactDuration(value) {
205
+ const match = /^([1-9]\d*)([mhd])$/.exec(value);
206
+ const units = { d: 86400, h: 3600, m: 60 };
207
+ const seconds = match ? Number(match[1]) * (units[match[2]] ?? 0) : 0;
208
+ if (!expirationSecondsSchema.safeParse(seconds).success) {
209
+ throw new Error(
210
+ `Duration must be a positive whole number followed by m, h, or d (for example 30m, 1h, 7d), up to ${MAX_EXPIRATION_SECONDS} seconds.`
211
+ );
212
+ }
213
+ return seconds;
214
+ }
215
+
216
+ // ../shared/src/review-mode.ts
217
+ import { z as z6 } from "zod";
218
+ var reviewModeSchema = z6.enum(["open", "readonly", "off"]);
219
+ var setReviewModeSchema = z6.object({
202
220
  mode: reviewModeSchema
203
221
  });
204
- var reviewModeResponseSchema = z5.object({
222
+ var reviewModeResponseSchema = z6.object({
205
223
  mode: reviewModeSchema
206
224
  });
207
225
 
@@ -220,274 +238,300 @@ function isSafeArtifactPath(p) {
220
238
  }
221
239
  var CARD_DESCRIPTION_MAX = 300;
222
240
  var CARD_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
223
- var manifestFileSchema = z6.object({
224
- hash: z6.string().regex(/^sha256_[a-f0-9]{64}$/),
225
- mimeType: z6.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
226
- path: z6.string().min(1).max(1024).refine(isSafeArtifactPath, {
241
+ var manifestFileSchema = z7.object({
242
+ hash: z7.string().regex(/^sha256_[a-f0-9]{64}$/),
243
+ mimeType: z7.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
244
+ path: z7.string().min(1).max(1024).refine(isSafeArtifactPath, {
227
245
  message: "path must be a safe relative path"
228
246
  }),
229
- size: z6.number().int().positive()
247
+ size: z7.number().int().positive()
230
248
  });
231
- var publishSourceSchema = z6.object({
232
- agent: z6.string().optional(),
233
- runId: z6.string().optional(),
234
- type: z6.literal("cli")
249
+ var publishSourceSchema = z7.object({
250
+ agent: z7.string().optional(),
251
+ runId: z7.string().optional(),
252
+ type: z7.literal("cli")
235
253
  });
236
- var createPublishSessionSchema = z6.object({
237
- artifactId: z6.string().nullable(),
254
+ var createPublishSessionSchema = z7.object({
255
+ artifactId: z7.string().nullable(),
238
256
  // One-line share-card summary (`--description`). Overrides whatever the
239
257
  // entry HTML declares; leave it off and the API reads the document's own
240
258
  // `og:description`/`description` at finalize.
241
- description: z6.string().max(CARD_DESCRIPTION_MAX).optional(),
242
- entryPath: z6.string().optional(),
243
- files: z6.array(manifestFileSchema).min(1).max(1e3),
259
+ description: z7.string().max(CARD_DESCRIPTION_MAX).optional(),
260
+ entryPath: z7.string().optional(),
261
+ expiresInSeconds: expirationSecondsSchema.optional(),
262
+ files: z7.array(manifestFileSchema).min(1).max(1e3),
244
263
  // SPEC-2 §6.4 — workspace placement is available only to authenticated
245
264
  // members; the API enforces membership and folder ownership. folderId
246
265
  // without workspaceId resolves against the account's personal workspace.
247
- folderId: z6.string().min(1).optional(),
266
+ folderId: z7.string().min(1).optional(),
248
267
  // Bullet-point changelog for this version, authored by the publisher.
249
268
  // Optional on every publish (incl. v1); stored immutably on the version.
250
- notes: z6.string().max(4e3).optional(),
269
+ notes: z7.string().max(4e3).optional(),
251
270
  source: publishSourceSchema,
252
- title: z6.string().max(200).optional(),
253
- workspaceId: z6.string().min(1).optional()
254
- });
255
- var publishSessionResponseSchema = z6.object({
256
- artifactId: z6.string(),
257
- expiresAt: z6.string(),
258
- filesToUpload: z6.array(
259
- z6.object({
260
- method: z6.literal("PUT"),
261
- path: z6.string(),
262
- uploadUrl: z6.string().url()
271
+ title: z7.string().max(200).optional(),
272
+ workspaceId: z7.string().min(1).optional()
273
+ });
274
+ var publishSessionResponseSchema = z7.object({
275
+ artifactId: z7.string(),
276
+ expiresAt: z7.string(),
277
+ expiresInSeconds: expirationSecondsSchema.nullable().optional(),
278
+ filesToUpload: z7.array(
279
+ z7.object({
280
+ method: z7.literal("PUT"),
281
+ path: z7.string(),
282
+ uploadUrl: z7.string().url()
263
283
  })
264
284
  ),
265
- publishSessionId: z6.string(),
266
- skippedFiles: z6.array(z6.string()),
267
- versionId: z6.string()
285
+ publishSessionId: z7.string(),
286
+ skippedFiles: z7.array(z7.string()),
287
+ versionId: z7.string()
268
288
  });
269
- var finalizeResponseSchema = z6.object({
270
- artifactId: z6.string(),
289
+ var finalizeResponseSchema = z7.object({
290
+ artifactId: z7.string(),
291
+ autoDeleteAt: z7.string().datetime().nullable().optional(),
271
292
  // API-key publishes create the artifact already claimed (SPEC §5.4) —
272
293
  // the CLI uses this to tell "claim URL pending" from "already yours".
273
- claimed: z6.boolean(),
294
+ claimed: z7.boolean(),
274
295
  // Returned only on first publish of a new artifact (one-time).
275
- claimUrl: z6.string().url().optional(),
276
- publishToken: z6.string().optional(),
277
- url: z6.string().url(),
278
- versionId: z6.string(),
279
- versionNumber: z6.number().int().positive(),
296
+ claimUrl: z7.string().url().optional(),
297
+ expiresAt: z7.string().datetime().nullable().optional(),
298
+ publishToken: z7.string().optional(),
299
+ url: z7.string().url(),
300
+ versionId: z7.string(),
301
+ versionNumber: z7.number().int().positive(),
280
302
  // SPEC-2 §5.2 — who can open the review URL. A publish into a folder
281
303
  // inherits that folder's visibility, so this is the only place the CLI
282
304
  // learns that a link it just printed is members-only. Optional so a new
283
305
  // CLI against an older API degrades to "unknown" instead of failing the
284
306
  // publish over a field that only shapes the closing message.
285
- visibility: z6.enum(["public", "unlisted", "workspace", "private"]).optional(),
286
- workspaceId: z6.string().nullable().optional()
287
- });
288
- var artifactResponseSchema = z6.object({
289
- claimed: z6.boolean(),
290
- currentVersion: z6.object({
291
- entryUrl: z6.string().url(),
292
- id: z6.string(),
293
- versionNumber: z6.number().int().positive()
307
+ visibility: z7.enum(["public", "unlisted", "workspace", "private"]).optional(),
308
+ workspaceId: z7.string().nullable().optional()
309
+ });
310
+ var artifactResponseSchema = z7.object({
311
+ autoDeleteAt: z7.string().datetime().nullable().optional(),
312
+ claimed: z7.boolean(),
313
+ currentVersion: z7.object({
314
+ entryUrl: z7.string().url(),
315
+ id: z7.string(),
316
+ versionNumber: z7.number().int().positive()
294
317
  }).nullable(),
295
318
  // Custom icon, or null to render the default document glyph.
296
- emoji: z6.string().nullable(),
319
+ emoji: z7.string().nullable(),
320
+ expiresAt: z7.string().datetime().nullable().optional(),
297
321
  // SPEC-2 §6.4 — placement + visibility surface on the artifact.
298
- folderId: z6.string().nullable(),
299
- id: z6.string(),
322
+ folderId: z7.string().nullable(),
323
+ id: z7.string(),
300
324
  // SPEC-2 §5.8 — what a viewer may do once in (open / readonly / off).
301
325
  reviewMode: reviewModeSchema,
302
- slug: z6.string(),
303
- title: z6.string(),
304
- visibility: z6.enum(["public", "unlisted", "workspace", "private"]),
305
- workspaceId: z6.string().nullable()
326
+ slug: z7.string(),
327
+ title: z7.string(),
328
+ visibility: z7.enum(["public", "unlisted", "workspace", "private"]),
329
+ workspaceId: z7.string().nullable()
306
330
  });
331
+ function resolvePublishEntry(files, explicit) {
332
+ if (explicit !== void 0) {
333
+ if (!files.some((file) => file.path === explicit)) {
334
+ throw new Error(`Entry file "${explicit}" is not in the manifest.`);
335
+ }
336
+ return explicit;
337
+ }
338
+ const htmlFiles = files.filter(
339
+ (file) => file.mimeType === "text/html" && !file.path.includes("/")
340
+ );
341
+ if (htmlFiles.some((file) => file.path === "index.html")) {
342
+ return "index.html";
343
+ }
344
+ if (htmlFiles.length > 1) {
345
+ throw new Error(
346
+ `Multiple HTML files found (${htmlFiles.map((file) => file.path).join(", ")}). Specify an entry file.`
347
+ );
348
+ }
349
+ return htmlFiles[0]?.path;
350
+ }
307
351
 
308
352
  // ../shared/src/comments.ts
309
- var anchorPageSchema = z7.string().max(1024).refine(isSafeArtifactPath, {
353
+ var anchorPageSchema = z8.string().max(1024).refine(isSafeArtifactPath, {
310
354
  message: "page must be a safe relative path"
311
355
  }).optional();
312
- var elementSnapshotSchema = z7.object({
313
- classes: z7.string().max(256).optional(),
314
- id: z7.string().max(256).optional(),
315
- tag: z7.string().min(1).max(128),
316
- text: z7.string().max(200).optional()
356
+ var elementSnapshotSchema = z8.object({
357
+ classes: z8.string().max(256).optional(),
358
+ id: z8.string().max(256).optional(),
359
+ tag: z8.string().min(1).max(128),
360
+ text: z8.string().max(200).optional()
317
361
  });
318
- var pointAnchorSchema = z7.object({
362
+ var pointAnchorSchema = z8.object({
319
363
  element: elementSnapshotSchema.optional(),
320
364
  // elementPath is the primary anchor; x/y are offsets within the
321
365
  // element's box, normalized 0..1 (SPEC.md §8.1).
322
- elementPath: z7.string().max(2048),
366
+ elementPath: z8.string().max(2048),
323
367
  page: anchorPageSchema,
324
- type: z7.literal("point"),
325
- viewportHeight: z7.number().int().positive().max(1e5),
326
- viewportWidth: z7.number().int().positive().max(1e5),
327
- x: z7.number().min(0).max(1),
328
- y: z7.number().min(0).max(1)
329
- });
330
- var textAnchorSchema = z7.object({
331
- elementPath: z7.string().max(2048),
332
- endOffset: z7.number().int().nonnegative().max(1e8),
368
+ type: z8.literal("point"),
369
+ viewportHeight: z8.number().int().positive().max(1e5),
370
+ viewportWidth: z8.number().int().positive().max(1e5),
371
+ x: z8.number().min(0).max(1),
372
+ y: z8.number().min(0).max(1)
373
+ });
374
+ var textAnchorSchema = z8.object({
375
+ elementPath: z8.string().max(2048),
376
+ endOffset: z8.number().int().nonnegative().max(1e8),
333
377
  page: anchorPageSchema,
334
- prefix: z7.string().max(200).optional(),
335
- quote: z7.string().min(1).max(1e3),
336
- startOffset: z7.number().int().nonnegative().max(1e8),
337
- suffix: z7.string().max(200).optional(),
338
- type: z7.literal("text")
378
+ prefix: z8.string().max(200).optional(),
379
+ quote: z8.string().min(1).max(1e3),
380
+ startOffset: z8.number().int().nonnegative().max(1e8),
381
+ suffix: z8.string().max(200).optional(),
382
+ type: z8.literal("text")
339
383
  });
340
- var pageAnchorSchema = z7.object({
384
+ var pageAnchorSchema = z8.object({
341
385
  page: anchorPageSchema,
342
- type: z7.literal("page")
386
+ type: z8.literal("page")
343
387
  });
344
- var anchorSchema = z7.discriminatedUnion("type", [
388
+ var anchorSchema = z8.discriminatedUnion("type", [
345
389
  pointAnchorSchema,
346
390
  textAnchorSchema,
347
391
  pageAnchorSchema
348
392
  ]);
349
- var displayNameSchema = z7.string().trim().min(1).max(64);
350
- var guestSchema = z7.object({
393
+ var displayNameSchema = z8.string().trim().min(1).max(64);
394
+ var guestSchema = z8.object({
351
395
  displayName: displayNameSchema,
352
- guestId: z7.string().min(16)
396
+ guestId: z8.string().min(16)
353
397
  });
354
- var viaLabelSchema = z7.string().trim().min(1).max(64);
355
- var createCommentThreadSchema = z7.object({
398
+ var viaLabelSchema = z8.string().trim().min(1).max(64);
399
+ var createCommentThreadSchema = z8.object({
356
400
  anchor: anchorSchema,
357
- body: z7.string().min(1).max(1e4),
401
+ body: z8.string().min(1).max(1e4),
358
402
  guest: guestSchema.optional(),
359
- versionId: z7.string(),
403
+ versionId: z8.string(),
360
404
  via: viaLabelSchema.optional()
361
405
  });
362
- var createCommentMessageSchema = z7.object({
363
- body: z7.string().min(1).max(1e4),
406
+ var createCommentMessageSchema = z8.object({
407
+ body: z8.string().min(1).max(1e4),
364
408
  guest: guestSchema.optional(),
365
409
  via: viaLabelSchema.optional()
366
410
  });
367
- var editCommentMessageSchema = z7.object({
368
- body: z7.string().min(1).max(1e4),
411
+ var editCommentMessageSchema = z8.object({
412
+ body: z8.string().min(1).max(1e4),
369
413
  guest: guestSchema.optional()
370
414
  });
371
- var threadActionSchema = z7.object({
415
+ var threadActionSchema = z8.object({
372
416
  guest: guestSchema.optional()
373
417
  });
374
- var reactionEmojiSchema = z7.string().max(64).transform(canonicalizeEmoji).refine((s) => SINGLE_EMOJI.test(s), { message: "Must be a single emoji." }).describe("A single emoji, e.g. \u{1F44D}");
375
- var toggleReactionSchema = z7.object({
418
+ var reactionEmojiSchema = z8.string().max(64).transform(canonicalizeEmoji).refine((s) => SINGLE_EMOJI.test(s), { message: "Must be a single emoji." }).describe("A single emoji, e.g. \u{1F44D}");
419
+ var toggleReactionSchema = z8.object({
376
420
  emoji: reactionEmojiSchema,
377
421
  guest: guestSchema.optional()
378
422
  });
379
- var commentAgentSchema = z7.object({
380
- id: z7.string().nullable(),
381
- label: z7.string()
423
+ var commentAgentSchema = z8.object({
424
+ id: z8.string().nullable(),
425
+ label: z8.string()
382
426
  });
383
- var commentAuthorSchema = z7.object({
427
+ var commentAuthorSchema = z8.object({
384
428
  agent: commentAgentSchema.nullable().default(null),
385
- avatarUrl: z7.string().nullable(),
386
- displayName: z7.string(),
387
- guestId: z7.string().nullable(),
388
- isViewer: z7.boolean(),
389
- kind: z7.enum(["member", "guest"]),
429
+ avatarUrl: z8.string().nullable(),
430
+ displayName: z8.string(),
431
+ guestId: z8.string().nullable(),
432
+ isViewer: z8.boolean(),
433
+ kind: z8.enum(["member", "guest"]),
390
434
  // Optional "via" attribution (§6.4). Null for plain comments; a bounded
391
435
  // label the shell appends after the display name. Defaulted for the same
392
436
  // reason as `agent`: a server predating §6.4 omits the key entirely.
393
- via: z7.string().nullable().default(null)
437
+ via: z8.string().nullable().default(null)
394
438
  });
395
- var messageReactionSchema = z7.object({
396
- emoji: z7.string(),
397
- reactors: z7.array(commentAuthorSchema)
439
+ var messageReactionSchema = z8.object({
440
+ emoji: z8.string(),
441
+ reactors: z8.array(commentAuthorSchema)
398
442
  });
399
- var commentMessageSchema = z7.object({
443
+ var commentMessageSchema = z8.object({
400
444
  author: commentAuthorSchema,
401
445
  // Empty string for a deleted message — the body is stripped server-side
402
446
  // (§6.7.1 tombstone), never sent to the client and hidden there.
403
- body: z7.string(),
404
- createdAt: z7.string(),
447
+ body: z8.string(),
448
+ createdAt: z8.string(),
405
449
  // Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
406
450
  // the shell renders "Comment deleted" instead of the (absent) body.
407
- deleted: z7.boolean(),
451
+ deleted: z8.boolean(),
408
452
  // Last edit time (§6.7.1), or null if never edited — drives the "edited"
409
453
  // badge. No edit history is kept.
410
- editedAt: z7.string().nullable(),
411
- id: z7.string(),
454
+ editedAt: z8.string().nullable(),
455
+ id: z8.string(),
412
456
  // Defaulted, not required: a new CLI parsing an older server's
413
457
  // response must not fail on the missing key.
414
- reactions: z7.array(messageReactionSchema).default([])
458
+ reactions: z8.array(messageReactionSchema).default([])
415
459
  });
416
- var threadActorSchema = z7.object({
417
- displayName: z7.string(),
418
- kind: z7.enum(["member", "guest", "agent"])
460
+ var threadActorSchema = z8.object({
461
+ displayName: z8.string(),
462
+ kind: z8.enum(["member", "guest", "agent"])
419
463
  });
420
- var commentThreadSchema = z7.object({
464
+ var commentThreadSchema = z8.object({
421
465
  // §6.6.1 — set while open to signal "seen, working on it"; cleared on
422
466
  // reopen, kept (but superseded in display) once resolved. Defaulted so
423
467
  // clients parsing an older server's response don't fail.
424
- acknowledgedAt: z7.string().nullable().default(null),
468
+ acknowledgedAt: z8.string().nullable().default(null),
425
469
  acknowledgedBy: threadActorSchema.nullable().default(null),
426
470
  anchor: anchorSchema,
427
- artifactId: z7.string(),
428
- createdAt: z7.string(),
471
+ artifactId: z8.string(),
472
+ createdAt: z8.string(),
429
473
  createdBy: commentAuthorSchema,
430
- id: z7.string(),
431
- messages: z7.array(commentMessageSchema),
474
+ id: z8.string(),
475
+ messages: z8.array(commentMessageSchema),
432
476
  // Null on threads resolved before actor recording existed, and always
433
477
  // null while open.
434
478
  resolvedBy: threadActorSchema.nullable().default(null),
435
- status: z7.enum(["open", "resolved"]),
436
- versionId: z7.string()
479
+ status: z8.enum(["open", "resolved"]),
480
+ versionId: z8.string()
437
481
  });
438
- var commentThreadListSchema = z7.object({
439
- threads: z7.array(commentThreadSchema)
482
+ var commentThreadListSchema = z8.object({
483
+ threads: z8.array(commentThreadSchema)
440
484
  });
441
- var commentExportQuerySchema = z7.object({
442
- status: z7.enum(["open", "resolved", "all"]).default("open"),
443
- version: z7.enum(["current", "all"]).default("current")
485
+ var commentExportQuerySchema = z8.object({
486
+ status: z8.enum(["open", "resolved", "all"]).default("open"),
487
+ version: z8.enum(["current", "all"]).default("current")
444
488
  });
445
- var exportReactionSchema = z7.object({
446
- count: z7.number().int().positive(),
447
- emoji: z7.string()
489
+ var exportReactionSchema = z8.object({
490
+ count: z8.number().int().positive(),
491
+ emoji: z8.string()
448
492
  });
449
- var commentExportReplySchema = z7.object({
450
- author: z7.string(),
451
- authorKind: z7.enum(["member", "guest"]),
452
- body: z7.string(),
453
- createdAt: z7.string(),
493
+ var commentExportReplySchema = z8.object({
494
+ author: z8.string(),
495
+ authorKind: z8.enum(["member", "guest"]),
496
+ body: z8.string(),
497
+ createdAt: z8.string(),
454
498
  // Soft-delete tombstone (§6.7.1): body is "" when true.
455
- deleted: z7.boolean(),
456
- editedAt: z7.string().nullable(),
499
+ deleted: z8.boolean(),
500
+ editedAt: z8.string().nullable(),
457
501
  // The message id — what `edit`/`delete` target (§5.1).
458
- id: z7.string(),
459
- reactions: z7.array(exportReactionSchema).optional()
502
+ id: z8.string(),
503
+ reactions: z8.array(exportReactionSchema).optional()
460
504
  });
461
- var commentExportThreadSchema = z7.object({
505
+ var commentExportThreadSchema = z8.object({
462
506
  // §6.6.1 — lets a polling agent skip threads it already acknowledged
463
507
  // (acknowledged threads stay in status=open exports on purpose).
464
- acknowledgedAt: z7.string().nullable().default(null),
508
+ acknowledgedAt: z8.string().nullable().default(null),
465
509
  anchor: anchorSchema,
466
- author: z7.string(),
467
- authorKind: z7.enum(["member", "guest"]),
468
- body: z7.string(),
469
- createdAt: z7.string(),
470
- deleted: z7.boolean(),
471
- editedAt: z7.string().nullable(),
472
- id: z7.string(),
510
+ author: z8.string(),
511
+ authorKind: z8.enum(["member", "guest"]),
512
+ body: z8.string(),
513
+ createdAt: z8.string(),
514
+ deleted: z8.boolean(),
515
+ editedAt: z8.string().nullable(),
516
+ id: z8.string(),
473
517
  // The opening message's id (distinct from the thread `id`) — `edit`/
474
518
  // `delete` operate on messages, so the CLI needs it to target the first
475
519
  // comment, not just the replies.
476
- messageId: z7.string(),
477
- reactions: z7.array(exportReactionSchema).optional(),
478
- replies: z7.array(commentExportReplySchema),
520
+ messageId: z8.string(),
521
+ reactions: z8.array(exportReactionSchema).optional(),
522
+ replies: z8.array(commentExportReplySchema),
479
523
  resolvedBy: threadActorSchema.nullable().default(null),
480
- status: z7.enum(["open", "resolved"]),
481
- versionNumber: z7.number().int().positive()
524
+ status: z8.enum(["open", "resolved"]),
525
+ versionNumber: z8.number().int().positive()
482
526
  });
483
- var commentExportSchema = z7.object({
484
- artifactId: z7.string(),
485
- comments: z7.array(commentExportThreadSchema),
527
+ var commentExportSchema = z8.object({
528
+ artifactId: z8.string(),
529
+ comments: z8.array(commentExportThreadSchema),
486
530
  // The artifact's current version (null before the first finalize).
487
- versionNumber: z7.number().int().positive().nullable()
531
+ versionNumber: z8.number().int().positive().nullable()
488
532
  });
489
- var commentReadResponseSchema = z7.object({
490
- lastSeenAt: z7.string()
533
+ var commentReadResponseSchema = z8.object({
534
+ lastSeenAt: z8.string()
491
535
  });
492
536
  function elementLabel(el) {
493
537
  if (el.id) {
@@ -516,6 +560,8 @@ var DATA_LIMITS = {
516
560
  defaultWriteRate: "10/minute/ip",
517
561
  maxCollections: 10,
518
562
  maxFieldsPerCollection: 50,
563
+ /** Owner-plane batch insert: records per request. */
564
+ maxInsertBatch: 100,
519
565
  maxListLimit: 200,
520
566
  maxManifestBytes: 64 * 1024,
521
567
  maxRecordBytes: 16 * 1024,
@@ -663,46 +709,50 @@ function validateRecordInput(collection, input, opts = {}) {
663
709
  }
664
710
 
665
711
  // ../shared/src/data-api.ts
666
- import { z as z8 } from "zod";
667
- var dataRecordSchema = z8.object({
668
- created_at: z8.string(),
669
- created_by: z8.string().nullable(),
670
- id: z8.string(),
671
- updated_at: z8.string()
672
- }).catchall(z8.unknown());
673
- var dataPublicRecordSchema = z8.object({
674
- created_at: z8.string(),
675
- created_by: z8.never().optional(),
676
- id: z8.string(),
677
- mine: z8.boolean(),
678
- updated_at: z8.string()
679
- }).catchall(z8.unknown());
680
- var dataPublicListResponseSchema = z8.object({
681
- nextCursor: z8.string().nullable(),
682
- records: z8.array(dataPublicRecordSchema)
683
- });
684
- var dataListResponseSchema = z8.object({
685
- nextCursor: z8.string().nullable(),
686
- records: z8.array(dataRecordSchema)
687
- });
688
- var dataCollectionSummarySchema = z8.object({
689
- access: z8.record(z8.string(), z8.string()).optional(),
690
- count: z8.number().int().nonnegative().optional(),
691
- name: z8.string(),
692
- rateLimit: z8.string().optional()
693
- });
694
- var dataSummaryResponseSchema = z8.object({
695
- collections: z8.array(dataCollectionSummarySchema),
696
- enabled: z8.boolean()
697
- });
698
- var dataClearResponseSchema = z8.object({
699
- cleared: z8.number().int().nonnegative()
712
+ import { z as z9 } from "zod";
713
+ var dataRecordSchema = z9.object({
714
+ created_at: z9.string(),
715
+ created_by: z9.string().nullable(),
716
+ id: z9.string(),
717
+ updated_at: z9.string()
718
+ }).catchall(z9.unknown());
719
+ var dataPublicRecordSchema = z9.object({
720
+ created_at: z9.string(),
721
+ created_by: z9.never().optional(),
722
+ id: z9.string(),
723
+ mine: z9.boolean(),
724
+ updated_at: z9.string()
725
+ }).catchall(z9.unknown());
726
+ var dataPublicListResponseSchema = z9.object({
727
+ nextCursor: z9.string().nullable(),
728
+ records: z9.array(dataPublicRecordSchema)
729
+ });
730
+ var dataListResponseSchema = z9.object({
731
+ nextCursor: z9.string().nullable(),
732
+ records: z9.array(dataRecordSchema)
733
+ });
734
+ var dataRecordResponseSchema = z9.object({ record: dataRecordSchema });
735
+ var dataBatchInsertResponseSchema = z9.object({
736
+ records: z9.array(dataRecordSchema)
737
+ });
738
+ var dataCollectionSummarySchema = z9.object({
739
+ access: z9.record(z9.string(), z9.string()).optional(),
740
+ count: z9.number().int().nonnegative().optional(),
741
+ name: z9.string(),
742
+ rateLimit: z9.string().optional()
743
+ });
744
+ var dataSummaryResponseSchema = z9.object({
745
+ collections: z9.array(dataCollectionSummarySchema),
746
+ enabled: z9.boolean()
747
+ });
748
+ var dataClearResponseSchema = z9.object({
749
+ cleared: z9.number().int().nonnegative()
700
750
  });
701
751
 
702
752
  // ../shared/src/data-manifest.ts
703
- import { z as z9 } from "zod";
704
- var nameSchema = z9.string().max(DATA_NAME_MAX).regex(DATA_NAME_RE, "must match ^[a-z][a-z0-9_]*$");
705
- var fieldTypeSchema = z9.enum([
753
+ import { z as z10 } from "zod";
754
+ var nameSchema = z10.string().max(DATA_NAME_MAX).regex(DATA_NAME_RE, "must match ^[a-z][a-z0-9_]*$");
755
+ var fieldTypeSchema = z10.enum([
706
756
  "string",
707
757
  "number",
708
758
  "integer",
@@ -713,13 +763,13 @@ var fieldTypeSchema = z9.enum([
713
763
  "array",
714
764
  "object"
715
765
  ]);
716
- var fieldSchema = z9.strictObject({
717
- default: z9.unknown().optional(),
718
- maximum: z9.number().optional(),
719
- maxLength: z9.number().int().positive().optional(),
720
- minimum: z9.number().optional(),
721
- required: z9.boolean().optional(),
722
- trim: z9.boolean().optional(),
766
+ var fieldSchema = z10.strictObject({
767
+ default: z10.unknown().optional(),
768
+ maximum: z10.number().optional(),
769
+ maxLength: z10.number().int().positive().optional(),
770
+ minimum: z10.number().optional(),
771
+ required: z10.boolean().optional(),
772
+ trim: z10.boolean().optional(),
723
773
  type: fieldTypeSchema
724
774
  }).superRefine((field, ctx) => {
725
775
  if (field.default === void 0) {
@@ -745,18 +795,18 @@ function defaultMatchesType(field) {
745
795
  { value: field.default }
746
796
  ).ok;
747
797
  }
748
- var accessLevelSchema = z9.enum(["public", "creator", "owner", "none"]);
749
- var accessSchema = z9.strictObject({
798
+ var accessLevelSchema = z10.enum(["public", "creator", "owner", "none"]);
799
+ var accessSchema = z10.strictObject({
750
800
  delete: accessLevelSchema.default("owner"),
751
801
  insert: accessLevelSchema.default("owner"),
752
802
  read: accessLevelSchema.default("public"),
753
803
  update: accessLevelSchema.default("owner")
754
804
  }).prefault({});
755
- var collectionSchema = z9.strictObject({
805
+ var collectionSchema = z10.strictObject({
756
806
  access: accessSchema,
757
- fields: z9.record(nameSchema, fieldSchema),
758
- publicMutation: z9.literal("open").optional(),
759
- rateLimit: z9.string().regex(DATA_RATE_LIMIT_RE, 'expected "N/hour/ip" or "N/minute/ip"').optional()
807
+ fields: z10.record(nameSchema, fieldSchema),
808
+ publicMutation: z10.literal("open").optional(),
809
+ rateLimit: z10.string().regex(DATA_RATE_LIMIT_RE, 'expected "N/hour/ip" or "N/minute/ip"').optional()
760
810
  }).superRefine((collection, ctx) => {
761
811
  const fieldNames = Object.keys(collection.fields);
762
812
  if (fieldNames.length > DATA_LIMITS.maxFieldsPerCollection) {
@@ -785,8 +835,8 @@ var collectionSchema = z9.strictObject({
785
835
  }
786
836
  }
787
837
  });
788
- var dataManifestSchema = z9.strictObject({
789
- collections: z9.record(nameSchema, collectionSchema)
838
+ var dataManifestSchema = z10.strictObject({
839
+ collections: z10.record(nameSchema, collectionSchema)
790
840
  }).superRefine((manifest, ctx) => {
791
841
  if (Object.keys(manifest.collections).length > DATA_LIMITS.maxCollections) {
792
842
  ctx.addIssue({
@@ -825,245 +875,245 @@ function parseDataManifest(text) {
825
875
  }
826
876
 
827
877
  // ../shared/src/errors.ts
828
- import { z as z10 } from "zod";
829
- var apiErrorSchema = z10.object({
830
- code: z10.string(),
831
- docsUrl: z10.string().url(),
832
- message: z10.string(),
833
- retryAfter: z10.number().int().positive().optional(),
834
- scope: z10.enum(["ip", "artifact"]).optional()
878
+ import { z as z11 } from "zod";
879
+ var apiErrorSchema = z11.object({
880
+ code: z11.string(),
881
+ docsUrl: z11.string().url(),
882
+ message: z11.string(),
883
+ retryAfter: z11.number().int().positive().optional(),
884
+ scope: z11.enum(["ip", "artifact"]).optional()
835
885
  });
836
886
 
837
887
  // ../shared/src/feedback.ts
838
- import { z as z11 } from "zod";
839
- var feedbackRequestSchema = z11.object({
840
- email: z11.string().trim().toLowerCase().email().optional().or(z11.literal("")),
841
- message: z11.string().trim().min(1).max(4e3)
888
+ import { z as z12 } from "zod";
889
+ var feedbackRequestSchema = z12.object({
890
+ email: z12.string().trim().toLowerCase().email().optional().or(z12.literal("")),
891
+ message: z12.string().trim().min(1).max(4e3)
842
892
  });
843
- var feedbackResponseSchema = z11.object({
844
- sent: z11.literal(true)
893
+ var feedbackResponseSchema = z12.object({
894
+ sent: z12.literal(true)
845
895
  });
846
896
 
847
897
  // ../shared/src/grants.ts
848
- import { z as z12 } from "zod";
849
- var grantRoleSchema = z12.enum(["viewer"]);
850
- var createGrantSchema = z12.object({
851
- email: z12.string().trim().toLowerCase().email().optional(),
898
+ import { z as z13 } from "zod";
899
+ var grantRoleSchema = z13.enum(["viewer"]);
900
+ var createGrantSchema = z13.object({
901
+ email: z13.string().trim().toLowerCase().email().optional(),
852
902
  role: grantRoleSchema.default("viewer"),
853
- userId: z12.string().min(1).optional()
903
+ userId: z13.string().min(1).optional()
854
904
  }).refine((body) => Boolean(body.email) !== Boolean(body.userId), {
855
905
  message: "provide exactly one of email or userId"
856
906
  });
857
- var grantSchema = z12.object({
858
- createdAt: z12.string(),
859
- email: z12.string().email().nullable(),
860
- expiresAt: z12.string().nullable(),
861
- id: z12.string(),
907
+ var grantSchema = z13.object({
908
+ createdAt: z13.string(),
909
+ email: z13.string().email().nullable(),
910
+ expiresAt: z13.string().nullable(),
911
+ id: z13.string(),
862
912
  // The subject's avatar once they have an account; null otherwise.
863
- image: z12.string().nullable(),
913
+ image: z13.string().nullable(),
864
914
  // Present on the invited account after redemption / when granting an
865
915
  // existing user; null for a still-pending email invite.
866
- name: z12.string().nullable(),
867
- redeemedAt: z12.string().nullable(),
916
+ name: z13.string().nullable(),
917
+ redeemedAt: z13.string().nullable(),
868
918
  role: grantRoleSchema,
869
919
  // Shown as "· pending" beside the role until the link is redeemed.
870
- status: z12.enum(["pending", "active"]),
920
+ status: z13.enum(["pending", "active"]),
871
921
  // Display fragment of the live invitation token, or null once redeemed.
872
- tokenPrefix: z12.string().nullable(),
873
- userId: z12.string().nullable()
922
+ tokenPrefix: z13.string().nullable(),
923
+ userId: z13.string().nullable()
874
924
  });
875
925
  var createdGrantSchema = grantSchema.extend({
876
- url: z12.string().url()
926
+ url: z13.string().url()
877
927
  });
878
- var grantListResponseSchema = z12.object({
879
- grants: z12.array(grantSchema),
928
+ var grantListResponseSchema = z13.object({
929
+ grants: z13.array(grantSchema),
880
930
  // The artifact's current general-access value, so the share surface can
881
931
  // render the "General access" line without a second read.
882
- visibility: z12.enum(["public", "unlisted", "workspace", "private"]),
932
+ visibility: z13.enum(["public", "unlisted", "workspace", "private"]),
883
933
  // The owning workspace's logo, so the share modal can draw the workspace
884
934
  // itself rather than a generic glyph. Null when none was uploaded.
885
- workspaceLogo: z12.string().nullable().optional(),
935
+ workspaceLogo: z13.string().nullable().optional(),
886
936
  // The owning workspace's display name, so "People with access" can name the
887
937
  // audience `workspace` visibility actually admits ("All Acme members") rather
888
938
  // than leaving an empty list to imply nobody. Null for a workspace-less
889
939
  // artifact; optional so an older server still parses.
890
- workspaceName: z12.string().nullable().optional()
940
+ workspaceName: z13.string().nullable().optional()
891
941
  });
892
- var revokeGrantResponseSchema = z12.object({
893
- revoked: z12.literal(true)
942
+ var revokeGrantResponseSchema = z13.object({
943
+ revoked: z13.literal(true)
894
944
  });
895
- var acceptGrantSchema = z12.object({
896
- token: z12.string().min(1)
945
+ var acceptGrantSchema = z13.object({
946
+ token: z13.string().min(1)
897
947
  });
898
- var acceptGrantResponseSchema = z12.object({
899
- artifactId: z12.string(),
948
+ var acceptGrantResponseSchema = z13.object({
949
+ artifactId: z13.string(),
900
950
  // The review shell URL to land on, carrying ?invited=1 for the banner.
901
- artifactUrl: z12.string().url(),
902
- slug: z12.string(),
903
- title: z12.string()
904
- });
905
- var workspaceGuestSchema = z12.object({
906
- artifacts: z12.array(
907
- z12.object({
908
- artifactId: z12.string(),
909
- grantId: z12.string(),
910
- slug: z12.string(),
911
- status: z12.enum(["pending", "active"]),
912
- title: z12.string()
951
+ artifactUrl: z13.string().url(),
952
+ slug: z13.string(),
953
+ title: z13.string()
954
+ });
955
+ var workspaceGuestSchema = z13.object({
956
+ artifacts: z13.array(
957
+ z13.object({
958
+ artifactId: z13.string(),
959
+ grantId: z13.string(),
960
+ slug: z13.string(),
961
+ status: z13.enum(["pending", "active"]),
962
+ title: z13.string()
913
963
  })
914
964
  ),
915
- email: z12.string().nullable(),
916
- image: z12.string().nullable(),
917
- name: z12.string().nullable(),
918
- userId: z12.string().nullable()
919
- });
920
- var workspaceGuestListResponseSchema = z12.object({
921
- guests: z12.array(workspaceGuestSchema)
922
- });
923
- var sharedWithMeArtifactSchema = z12.object({
924
- artifactId: z12.string(),
925
- slug: z12.string(),
926
- title: z12.string(),
927
- url: z12.string().url(),
965
+ email: z13.string().nullable(),
966
+ image: z13.string().nullable(),
967
+ name: z13.string().nullable(),
968
+ userId: z13.string().nullable()
969
+ });
970
+ var workspaceGuestListResponseSchema = z13.object({
971
+ guests: z13.array(workspaceGuestSchema)
972
+ });
973
+ var sharedWithMeArtifactSchema = z13.object({
974
+ artifactId: z13.string(),
975
+ slug: z13.string(),
976
+ title: z13.string(),
977
+ url: z13.string().url(),
928
978
  // Who shared it, for the "Shared with me" list.
929
- workspaceName: z12.string()
979
+ workspaceName: z13.string()
930
980
  });
931
- var sharedWithMeListResponseSchema = z12.object({
932
- artifacts: z12.array(sharedWithMeArtifactSchema)
981
+ var sharedWithMeListResponseSchema = z13.object({
982
+ artifacts: z13.array(sharedWithMeArtifactSchema)
933
983
  });
934
984
 
935
985
  // ../shared/src/html-meta.ts
936
986
  var HEAD_SCAN_CHARS = 64 * 1024;
937
987
 
938
988
  // ../shared/src/me.ts
939
- import { z as z14 } from "zod";
989
+ import { z as z15 } from "zod";
940
990
 
941
991
  // ../shared/src/workspaces.ts
942
- import { z as z13 } from "zod";
992
+ import { z as z14 } from "zod";
943
993
  var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
944
- var workspaceHandleUnavailableReasonSchema = z13.enum([
994
+ var workspaceHandleUnavailableReasonSchema = z14.enum([
945
995
  "held",
946
996
  "invalid",
947
997
  "reserved",
948
998
  "taken"
949
999
  ]);
950
- var workspaceHandleAvailabilitySchema = z13.object({
951
- available: z13.boolean(),
952
- handle: z13.string(),
1000
+ var workspaceHandleAvailabilitySchema = z14.object({
1001
+ available: z14.boolean(),
1002
+ handle: z14.string(),
953
1003
  // Human-readable sentence for the field hint; null when available.
954
- message: z13.string().nullable(),
1004
+ message: z14.string().nullable(),
955
1005
  reason: workspaceHandleUnavailableReasonSchema.nullable()
956
1006
  });
957
- var workspaceRoleSchema = z13.enum(["owner", "admin", "member"]);
958
- var workspaceInviteRoleSchema = z13.enum(["admin", "member"]);
959
- var workspaceHandleSchema = z13.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
1007
+ var workspaceRoleSchema = z14.enum(["owner", "admin", "member"]);
1008
+ var workspaceInviteRoleSchema = z14.enum(["admin", "member"]);
1009
+ var workspaceHandleSchema = z14.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
960
1010
  message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
961
1011
  });
962
- var createWorkspaceSchema = z13.object({
1012
+ var createWorkspaceSchema = z14.object({
963
1013
  handle: workspaceHandleSchema.optional(),
964
- name: z13.string().trim().min(1).max(100)
1014
+ name: z14.string().trim().min(1).max(100)
965
1015
  });
966
- var updateWorkspaceSchema = z13.object({
1016
+ var updateWorkspaceSchema = z14.object({
967
1017
  handle: workspaceHandleSchema.optional(),
968
- name: z13.string().trim().min(1).max(100).optional()
1018
+ name: z14.string().trim().min(1).max(100).optional()
969
1019
  }).refine((body) => body.handle !== void 0 || body.name !== void 0, {
970
1020
  message: "provide at least one of name or handle"
971
1021
  });
972
- var workspaceSchema = z13.object({
973
- createdAt: z13.string(),
974
- handle: z13.string(),
1022
+ var workspaceSchema = z14.object({
1023
+ createdAt: z14.string(),
1024
+ handle: z14.string(),
975
1025
  // ISO timestamp when `{handle}.artifact.cafe` was provisioned, or null.
976
1026
  // Set by the one-time, self-serve enable flow (§4.4); never auto at
977
1027
  // create, and always null for personal workspaces.
978
- hostnameProvisionedAt: z13.string().nullable(),
979
- id: z13.string(),
1028
+ hostnameProvisionedAt: z14.string().nullable(),
1029
+ id: z14.string(),
980
1030
  // Auto-provisioned solo workspace, one per user: non-deletable, takes
981
1031
  // no members, renameable like any workspace.
982
- isPersonal: z13.boolean(),
1032
+ isPersonal: z14.boolean(),
983
1033
  // Uploaded logo URL, or null for the initials fallback. Set through the
984
1034
  // dedicated /workspaces/:id/avatar route, never the generic update.
985
- logo: z13.string().nullable(),
986
- name: z13.string(),
1035
+ logo: z14.string().nullable(),
1036
+ name: z14.string(),
987
1037
  role: workspaceRoleSchema.nullable(),
988
- updatedAt: z13.string()
1038
+ updatedAt: z14.string()
989
1039
  });
990
- var workspaceListResponseSchema = z13.object({
991
- workspaces: z13.array(workspaceSchema)
1040
+ var workspaceListResponseSchema = z14.object({
1041
+ workspaces: z14.array(workspaceSchema)
992
1042
  });
993
- var deleteWorkspaceResponseSchema = z13.object({
994
- deleted: z13.literal(true)
1043
+ var deleteWorkspaceResponseSchema = z14.object({
1044
+ deleted: z14.literal(true)
995
1045
  });
996
- var workspaceMemberSchema = z13.object({
997
- createdAt: z13.string(),
998
- email: z13.string().email(),
999
- image: z13.string().nullable(),
1000
- name: z13.string(),
1046
+ var workspaceMemberSchema = z14.object({
1047
+ createdAt: z14.string(),
1048
+ email: z14.string().email(),
1049
+ image: z14.string().nullable(),
1050
+ name: z14.string(),
1001
1051
  role: workspaceRoleSchema,
1002
- userId: z13.string()
1052
+ userId: z14.string()
1003
1053
  });
1004
- var workspaceMemberListResponseSchema = z13.object({
1005
- members: z13.array(workspaceMemberSchema)
1054
+ var workspaceMemberListResponseSchema = z14.object({
1055
+ members: z14.array(workspaceMemberSchema)
1006
1056
  });
1007
- var updateWorkspaceMemberSchema = z13.object({
1057
+ var updateWorkspaceMemberSchema = z14.object({
1008
1058
  role: workspaceRoleSchema
1009
1059
  });
1010
- var removeWorkspaceMemberResponseSchema = z13.object({
1011
- removed: z13.literal(true)
1060
+ var removeWorkspaceMemberResponseSchema = z14.object({
1061
+ removed: z14.literal(true)
1012
1062
  });
1013
- var createWorkspaceInviteSchema = z13.object({
1014
- email: z13.string().trim().toLowerCase().email().optional(),
1015
- expiresAt: z13.string().datetime(),
1016
- maxUses: z13.number().int().positive().optional(),
1063
+ var createWorkspaceInviteSchema = z14.object({
1064
+ email: z14.string().trim().toLowerCase().email().optional(),
1065
+ expiresAt: z14.string().datetime(),
1066
+ maxUses: z14.number().int().positive().optional(),
1017
1067
  role: workspaceInviteRoleSchema
1018
1068
  });
1019
- var workspaceInviteSchema = z13.object({
1020
- createdAt: z13.string(),
1021
- email: z13.string().email().nullable(),
1022
- expiresAt: z13.string(),
1023
- id: z13.string(),
1024
- maxUses: z13.number().int().positive().nullable(),
1069
+ var workspaceInviteSchema = z14.object({
1070
+ createdAt: z14.string(),
1071
+ email: z14.string().email().nullable(),
1072
+ expiresAt: z14.string(),
1073
+ id: z14.string(),
1074
+ maxUses: z14.number().int().positive().nullable(),
1025
1075
  role: workspaceInviteRoleSchema,
1026
- tokenPrefix: z13.string(),
1027
- useCount: z13.number().int().nonnegative()
1076
+ tokenPrefix: z14.string(),
1077
+ useCount: z14.number().int().nonnegative()
1028
1078
  });
1029
1079
  var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
1030
- url: z13.string().url()
1080
+ url: z14.string().url()
1031
1081
  });
1032
- var workspaceInviteListResponseSchema = z13.object({
1033
- invites: z13.array(workspaceInviteSchema)
1082
+ var workspaceInviteListResponseSchema = z14.object({
1083
+ invites: z14.array(workspaceInviteSchema)
1034
1084
  });
1035
- var revokeWorkspaceInviteResponseSchema = z13.object({
1036
- revoked: z13.literal(true)
1085
+ var revokeWorkspaceInviteResponseSchema = z14.object({
1086
+ revoked: z14.literal(true)
1037
1087
  });
1038
- var acceptWorkspaceInviteSchema = z13.object({
1039
- token: z13.string().min(1)
1088
+ var acceptWorkspaceInviteSchema = z14.object({
1089
+ token: z14.string().min(1)
1040
1090
  });
1041
- var acceptWorkspaceInviteResponseSchema = z13.object({
1091
+ var acceptWorkspaceInviteResponseSchema = z14.object({
1042
1092
  role: workspaceRoleSchema,
1043
1093
  workspace: workspaceSchema
1044
1094
  });
1045
- var folderVisibilitySchema = z13.enum(["public", "workspace"]);
1046
- var artifactVisibilitySchema = z13.enum([
1095
+ var folderVisibilitySchema = z14.enum(["public", "workspace"]);
1096
+ var artifactVisibilitySchema = z14.enum([
1047
1097
  "public",
1048
1098
  "unlisted",
1049
1099
  "workspace",
1050
1100
  "private"
1051
1101
  ]);
1052
- var createFolderSchema = z13.object({
1102
+ var createFolderSchema = z14.object({
1053
1103
  // Optional custom icon; omitted (or null) leaves the default glyph.
1054
1104
  emoji: iconEmojiSchema.optional(),
1055
- name: z13.string().trim().min(1).max(100),
1105
+ name: z14.string().trim().min(1).max(100),
1056
1106
  visibility: folderVisibilitySchema
1057
1107
  });
1058
- var updateFolderSchema = z13.object({
1108
+ var updateFolderSchema = z14.object({
1059
1109
  // With `visibility`: bulk-update the folder's artifacts to match
1060
1110
  // (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
1061
- cascade: z13.boolean().optional(),
1111
+ cascade: z14.boolean().optional(),
1062
1112
  // Explicit null clears the icon back to the default glyph, so this is
1063
1113
  // a field where "sent as null" and "omitted" mean different things.
1064
1114
  emoji: iconEmojiSchema.optional(),
1065
- name: z13.string().trim().min(1).max(100).optional(),
1066
- sortOrder: z13.number().int().min(0).optional(),
1115
+ name: z14.string().trim().min(1).max(100).optional(),
1116
+ sortOrder: z14.number().int().min(0).optional(),
1067
1117
  visibility: folderVisibilitySchema.optional()
1068
1118
  }).refine(
1069
1119
  (body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0 || body.emoji !== void 0,
@@ -1071,93 +1121,95 @@ var updateFolderSchema = z13.object({
1071
1121
  ).refine((body) => !body.cascade || body.visibility !== void 0, {
1072
1122
  message: "cascade requires visibility"
1073
1123
  });
1074
- var folderSchema = z13.object({
1075
- createdAt: z13.string(),
1124
+ var folderSchema = z14.object({
1125
+ createdAt: z14.string(),
1076
1126
  // Custom icon, or null to render the default folder glyph.
1077
- emoji: z13.string().nullable(),
1078
- id: z13.string(),
1079
- name: z13.string(),
1127
+ emoji: z14.string().nullable(),
1128
+ id: z14.string(),
1129
+ name: z14.string(),
1080
1130
  // Permalink segment for the public folder page (/w/{handle}/f/{slug}).
1081
1131
  // Server-derived from the name; renames regenerate it.
1082
- slug: z13.string(),
1083
- sortOrder: z13.number().int(),
1084
- updatedAt: z13.string(),
1132
+ slug: z14.string(),
1133
+ sortOrder: z14.number().int(),
1134
+ updatedAt: z14.string(),
1085
1135
  visibility: folderVisibilitySchema,
1086
- workspaceId: z13.string()
1136
+ workspaceId: z14.string()
1087
1137
  });
1088
- var folderListResponseSchema = z13.object({
1089
- folders: z13.array(folderSchema)
1138
+ var folderListResponseSchema = z14.object({
1139
+ folders: z14.array(folderSchema)
1090
1140
  });
1091
- var deleteFolderResponseSchema = z13.object({
1092
- deleted: z13.literal(true)
1141
+ var deleteFolderResponseSchema = z14.object({
1142
+ deleted: z14.literal(true)
1093
1143
  });
1094
- var workspaceArtifactSchema = z13.object({
1095
- createdAt: z13.string(),
1096
- currentVersionNumber: z13.number().int().nullable(),
1144
+ var workspaceArtifactSchema = z14.object({
1145
+ autoDeleteAt: z14.string().datetime().nullable().optional(),
1146
+ createdAt: z14.string(),
1147
+ currentVersionNumber: z14.number().int().nullable(),
1097
1148
  // Custom icon, or null to render the default document glyph.
1098
- emoji: z13.string().nullable(),
1099
- folderId: z13.string().nullable(),
1100
- id: z13.string(),
1101
- openCommentCount: z13.number().int().nonnegative(),
1149
+ emoji: z14.string().nullable(),
1150
+ expiresAt: z14.string().datetime().nullable().optional(),
1151
+ folderId: z14.string().nullable(),
1152
+ id: z14.string(),
1153
+ openCommentCount: z14.number().int().nonnegative(),
1102
1154
  reviewMode: reviewModeSchema,
1103
- slug: z13.string(),
1104
- title: z13.string(),
1105
- updatedAt: z13.string(),
1106
- url: z13.string().url(),
1107
- versionCount: z13.number().int().nonnegative(),
1155
+ slug: z14.string(),
1156
+ title: z14.string(),
1157
+ updatedAt: z14.string(),
1158
+ url: z14.string().url(),
1159
+ versionCount: z14.number().int().nonnegative(),
1108
1160
  visibility: artifactVisibilitySchema
1109
1161
  });
1110
- var workspaceArtifactListResponseSchema = z13.object({
1111
- artifacts: z13.array(workspaceArtifactSchema)
1162
+ var workspaceArtifactListResponseSchema = z14.object({
1163
+ artifacts: z14.array(workspaceArtifactSchema)
1112
1164
  });
1113
- var workspaceArtifactSearchResultSchema = z13.object({
1114
- id: z13.string(),
1115
- slug: z13.string(),
1116
- title: z13.string()
1165
+ var workspaceArtifactSearchResultSchema = z14.object({
1166
+ id: z14.string(),
1167
+ slug: z14.string(),
1168
+ title: z14.string()
1117
1169
  });
1118
- var workspaceArtifactSearchResponseSchema = z13.object({
1119
- results: z13.array(workspaceArtifactSearchResultSchema)
1170
+ var workspaceArtifactSearchResponseSchema = z14.object({
1171
+ results: z14.array(workspaceArtifactSearchResultSchema)
1120
1172
  });
1121
1173
 
1122
1174
  // ../shared/src/me.ts
1123
- var personSchema = z14.object({
1124
- image: z14.string().nullable(),
1125
- name: z14.string()
1175
+ var personSchema = z15.object({
1176
+ image: z15.string().nullable(),
1177
+ name: z15.string()
1126
1178
  });
1127
- var ownArtifactSchema = z14.object({
1179
+ var ownArtifactSchema = z15.object({
1180
+ autoDeleteAt: z15.string().datetime().nullable().optional(),
1128
1181
  // Everyone else who has published a version, oldest first. Any member may
1129
1182
  // publish a new one (SPEC-2 §5.1), so an artifact's byline is often more
1130
1183
  // than one person and the card stacks their faces rather than crediting
1131
1184
  // whoever shipped last. Excludes `publishedBy`; empty on a solo artifact.
1132
- coPublishers: z14.array(personSchema),
1133
- createdAt: z14.string(),
1185
+ coPublishers: z15.array(personSchema),
1186
+ createdAt: z15.string(),
1134
1187
  // SPEC-2 §5.1 — who *made* the artifact: the author of its first
1135
1188
  // finalized version, the same row `viewerCanManage` is decided on. Not
1136
1189
  // the same person as `publishedBy` once an agent ships v2, which is why
1137
1190
  // the dashboard filters on both. Null for a token-only publish.
1138
- createdBy: z14.object({ image: z14.string().nullable(), name: z14.string() }).nullable(),
1139
- currentVersionNumber: z14.number().nullable(),
1191
+ createdBy: z15.object({ image: z15.string().nullable(), name: z15.string() }).nullable(),
1192
+ currentVersionNumber: z15.number().nullable(),
1140
1193
  // Custom icon, or null to render the default document glyph.
1141
- emoji: z14.string().nullable(),
1142
- // Owned artifacts normally never expire; non-null only while an
1143
- // expiry is still set (e.g. an unclaimed publish surfaced here later).
1144
- expiresAt: z14.string().nullable(),
1194
+ emoji: z15.string().nullable(),
1195
+ // Effective expiry: explicit temporary lifetime or anonymous retention.
1196
+ expiresAt: z15.string().nullable(),
1145
1197
  // SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
1146
1198
  // both null for personal artifacts. `folderId` drives the dashboard's
1147
1199
  // move control and folder filter; `folderName` is the display label,
1148
1200
  // `folderEmoji` its icon (null = the default folder glyph).
1149
- folderEmoji: z14.string().nullable(),
1150
- folderId: z14.string().nullable(),
1151
- folderName: z14.string().nullable(),
1201
+ folderEmoji: z15.string().nullable(),
1202
+ folderId: z15.string().nullable(),
1203
+ folderName: z15.string().nullable(),
1152
1204
  // Whether a publish token is still active — revocable from the
1153
1205
  // dashboard (§6.18); the token itself is never returned.
1154
- hasPublishToken: z14.boolean(),
1206
+ hasPublishToken: z15.boolean(),
1155
1207
  // Owner preference: hide the version list from reviewers in the review
1156
1208
  // shell. Managers still see it.
1157
- hideVersions: z14.boolean(),
1158
- id: z14.string(),
1159
- openThreads: z14.number(),
1160
- protected: z14.boolean(),
1209
+ hideVersions: z15.boolean(),
1210
+ id: z15.string(),
1211
+ openThreads: z15.number(),
1212
+ protected: z15.boolean(),
1161
1213
  // Who published the current version (falling back to the claiming
1162
1214
  // owner) — the dashboard shows a name and avatar per artifact so a
1163
1215
  // shared workspace reads as people's work, not anonymous rows. Null
@@ -1166,44 +1218,44 @@ var ownArtifactSchema = z14.object({
1166
1218
  // SPEC-2 §5.8 — the review dial, so the dashboard's settings dialog can
1167
1219
  // render current state without a second fetch.
1168
1220
  reviewMode: reviewModeSchema,
1169
- slug: z14.string(),
1170
- title: z14.string(),
1171
- totalViews: z14.number(),
1221
+ slug: z15.string(),
1222
+ title: z15.string(),
1223
+ totalViews: z15.number(),
1172
1224
  // SPEC §6.20 — messages by others newer than the viewer's last-seen
1173
1225
  // watermark. 0 for signed-out/API-key-less contexts.
1174
- unreadComments: z14.number(),
1175
- updatedAt: z14.string(),
1176
- url: z14.string(),
1177
- versionCount: z14.number(),
1226
+ unreadComments: z15.number(),
1227
+ updatedAt: z15.string(),
1228
+ url: z15.string(),
1229
+ versionCount: z15.number(),
1178
1230
  // SPEC-2 §5.1 — whether the caller clears the manage bar for this
1179
1231
  // artifact (workspace admin, or the person who first published it).
1180
1232
  // Only a manager's folder move inherits the folder's visibility, so the
1181
1233
  // dashboard warns exactly the people the change can actually affect, and
1182
1234
  // only a manager is offered the Share entry (§5.7).
1183
- viewerCanManage: z14.boolean(),
1235
+ viewerCanManage: z15.boolean(),
1184
1236
  // SPEC-2 §5.2 — the artifact's own visibility, the single enforcement
1185
1237
  // source. The dashboard needs it to tell whether a folder move would
1186
1238
  // widen exposure before it sends the PATCH (§3.4), and to render the
1187
1239
  // audience glyph — one per level — on rows and cards (§5.7).
1188
1240
  visibility: artifactVisibilitySchema
1189
1241
  });
1190
- var ownArtifactsResponseSchema = z14.object({
1191
- artifacts: z14.array(ownArtifactSchema)
1242
+ var ownArtifactsResponseSchema = z15.object({
1243
+ artifacts: z15.array(ownArtifactSchema)
1192
1244
  });
1193
- var revokePublishTokenResponseSchema = z14.object({
1194
- revoked: z14.boolean()
1245
+ var revokePublishTokenResponseSchema = z15.object({
1246
+ revoked: z15.boolean()
1195
1247
  });
1196
1248
 
1197
1249
  // ../shared/src/owner.ts
1198
- import { z as z15 } from "zod";
1250
+ import { z as z16 } from "zod";
1199
1251
  var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
1200
- var claimResponseSchema = z15.object({
1201
- artifactId: z15.string(),
1202
- slug: z15.string(),
1203
- title: z15.string(),
1204
- url: z15.string().url()
1252
+ var claimResponseSchema = z16.object({
1253
+ artifactId: z16.string(),
1254
+ slug: z16.string(),
1255
+ title: z16.string(),
1256
+ url: z16.string().url()
1205
1257
  });
1206
- var updateArtifactSchema = z15.object({
1258
+ var updateArtifactSchema = z16.object({
1207
1259
  // Custom icon; explicit null clears it back to the default glyph.
1208
1260
  // Part of what the artifact *is* (like the title), so it takes the
1209
1261
  // same manage rights as title/slug — not the member-level folder move.
@@ -1211,159 +1263,159 @@ var updateArtifactSchema = z15.object({
1211
1263
  // SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
1212
1264
  // folder; `workspaceId: null` moves the artifact out of its workspace
1213
1265
  // (§5.5 ownership transfer).
1214
- folderId: z15.string().min(1).nullable().optional(),
1266
+ folderId: z16.string().min(1).nullable().optional(),
1215
1267
  // Keep the version list out of the review shell. Presentation only:
1216
1268
  // versions stay immutable, and a `/vN/` URL still resolves.
1217
- hideVersions: z15.boolean().optional(),
1218
- slug: z15.string().min(3).max(63).regex(SLUG_PATTERN, {
1269
+ hideVersions: z16.boolean().optional(),
1270
+ slug: z16.string().min(3).max(63).regex(SLUG_PATTERN, {
1219
1271
  message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
1220
1272
  }).optional(),
1221
- title: z15.string().min(1).max(200).optional(),
1273
+ title: z16.string().min(1).max(200).optional(),
1222
1274
  visibility: artifactVisibilitySchema.optional(),
1223
- workspaceId: z15.string().min(1).nullable().optional()
1275
+ workspaceId: z16.string().min(1).nullable().optional()
1224
1276
  }).refine(
1225
1277
  (body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0 || body.emoji !== void 0 || body.hideVersions !== void 0,
1226
1278
  {
1227
1279
  message: "provide at least one of title, slug, visibility, folderId, workspaceId, emoji, hideVersions"
1228
1280
  }
1229
1281
  );
1230
- var deleteArtifactResponseSchema = z15.object({
1231
- deleted: z15.literal(true)
1282
+ var deleteArtifactResponseSchema = z16.object({
1283
+ deleted: z16.literal(true)
1232
1284
  });
1233
1285
 
1234
1286
  // ../shared/src/presence.ts
1235
- import { z as z16 } from "zod";
1287
+ import { z as z17 } from "zod";
1236
1288
  var AGENT_STATES = ["listening", "editing", "publishing"];
1237
- var agentStateSchema = z16.enum(AGENT_STATES);
1289
+ var agentStateSchema = z17.enum(AGENT_STATES);
1238
1290
  var AGENT_PRESENCE = {
1239
1291
  editingTtlSeconds: 300,
1240
1292
  heartbeatSeconds: 15,
1241
1293
  shellPollSeconds: 15,
1242
1294
  ttlSeconds: 45
1243
1295
  };
1244
- var agentHeartbeatBodySchema = z16.object({
1296
+ var agentHeartbeatBodySchema = z17.object({
1245
1297
  state: agentStateSchema.optional()
1246
1298
  });
1247
- var agentHeartbeatResponseSchema = z16.object({
1248
- listening: z16.literal(true),
1299
+ var agentHeartbeatResponseSchema = z17.object({
1300
+ listening: z17.literal(true),
1249
1301
  state: agentStateSchema.optional(),
1250
- ttlSeconds: z16.number().int().positive()
1302
+ ttlSeconds: z17.number().int().positive()
1251
1303
  });
1252
- var agentPresenceSchema = z16.object({
1253
- agent: z16.object({ id: z16.string().nullable(), label: z16.string() }).nullable().default(null),
1254
- listening: z16.boolean(),
1304
+ var agentPresenceSchema = z17.object({
1305
+ agent: z17.object({ id: z17.string().nullable(), label: z17.string() }).nullable().default(null),
1306
+ listening: z17.boolean(),
1255
1307
  state: agentStateSchema.nullable().optional(),
1256
- versionNumber: z16.number().int().positive().nullable()
1308
+ versionNumber: z17.number().int().positive().nullable()
1257
1309
  });
1258
1310
 
1259
1311
  // ../shared/src/protection.ts
1260
- import { z as z17 } from "zod";
1312
+ import { z as z18 } from "zod";
1261
1313
  var PASSWORD_MIN = 6;
1262
1314
  var PASSWORD_MAX = 128;
1263
- var gatePasswordSchema = z17.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
1264
- var setProtectionSchema = z17.object({
1315
+ var gatePasswordSchema = z18.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
1316
+ var setProtectionSchema = z18.object({
1265
1317
  password: gatePasswordSchema.nullable()
1266
1318
  });
1267
- var protectionResponseSchema = z17.object({
1268
- protected: z17.boolean()
1319
+ var protectionResponseSchema = z18.object({
1320
+ protected: z18.boolean()
1269
1321
  });
1270
- var unlockSchema = z17.object({
1271
- password: z17.string().min(1).max(1024)
1322
+ var unlockSchema = z18.object({
1323
+ password: z18.string().min(1).max(1024)
1272
1324
  });
1273
- var unlockResponseSchema = z17.object({
1274
- unlocked: z17.literal(true)
1325
+ var unlockResponseSchema = z18.object({
1326
+ unlocked: z18.literal(true)
1275
1327
  });
1276
1328
 
1277
1329
  // ../shared/src/report.ts
1278
- import { z as z18 } from "zod";
1279
- var reportRequestSchema = z18.object({
1280
- reason: z18.string().max(1e3).optional()
1330
+ import { z as z19 } from "zod";
1331
+ var reportRequestSchema = z19.object({
1332
+ reason: z19.string().max(1e3).optional()
1281
1333
  });
1282
- var reportResponseSchema = z18.object({
1283
- reported: z18.literal(true)
1334
+ var reportResponseSchema = z19.object({
1335
+ reported: z19.literal(true)
1284
1336
  });
1285
1337
 
1286
1338
  // ../shared/src/reviewers.ts
1287
- import { z as z19 } from "zod";
1288
- var artifactReviewerSchema = z19.object({
1289
- avatarUrl: z19.string().nullable(),
1290
- displayName: z19.string(),
1339
+ import { z as z20 } from "zod";
1340
+ var artifactReviewerSchema = z20.object({
1341
+ avatarUrl: z20.string().nullable(),
1342
+ displayName: z20.string(),
1291
1343
  // Exactly one of userId/guestId is set, per `kind`.
1292
- guestId: z19.string().nullable(),
1344
+ guestId: z20.string().nullable(),
1293
1345
  // "account", not commentAuthorSchema's "member" — the whole point here is
1294
1346
  // that these people need not be workspace members. A guest can be named in
1295
1347
  // a comment but never notified: guest_identities carries a display name and
1296
1348
  // no email, so there is nowhere to send anything.
1297
- kind: z19.enum(["account", "guest"]),
1298
- userId: z19.string().nullable()
1349
+ kind: z20.enum(["account", "guest"]),
1350
+ userId: z20.string().nullable()
1299
1351
  });
1300
- var artifactReviewerListResponseSchema = z19.object({
1301
- reviewers: z19.array(artifactReviewerSchema)
1352
+ var artifactReviewerListResponseSchema = z20.object({
1353
+ reviewers: z20.array(artifactReviewerSchema)
1302
1354
  });
1303
1355
 
1304
1356
  // ../shared/src/stats.ts
1305
- import { z as z20 } from "zod";
1306
- var avgViewSecondsSchema = z20.number().int().nonnegative().nullable();
1307
- var versionViewStatsSchema = z20.object({
1357
+ import { z as z21 } from "zod";
1358
+ var avgViewSecondsSchema = z21.number().int().nonnegative().nullable();
1359
+ var versionViewStatsSchema = z21.object({
1308
1360
  avgViewSeconds: avgViewSecondsSchema,
1309
1361
  // Views from inside the review shell's iframe vs. direct visits to
1310
1362
  // the render URL. views = embeddedViews + directViews.
1311
- directViews: z20.number().int().nonnegative(),
1312
- embeddedViews: z20.number().int().nonnegative(),
1363
+ directViews: z21.number().int().nonnegative(),
1364
+ embeddedViews: z21.number().int().nonnegative(),
1313
1365
  // Distinct hashed viewer IPs across the version's lifetime.
1314
- uniqueViewers: z20.number().int().nonnegative(),
1315
- versionNumber: z20.number().int().positive(),
1316
- views: z20.number().int().nonnegative()
1366
+ uniqueViewers: z21.number().int().nonnegative(),
1367
+ versionNumber: z21.number().int().positive(),
1368
+ views: z21.number().int().nonnegative()
1317
1369
  });
1318
- var dailyViewsSchema = z20.object({
1370
+ var dailyViewsSchema = z21.object({
1319
1371
  avgViewSeconds: avgViewSecondsSchema,
1320
1372
  // UTC day, YYYY-MM-DD.
1321
- day: z20.string(),
1322
- uniqueViewers: z20.number().int().nonnegative(),
1323
- views: z20.number().int().nonnegative()
1373
+ day: z21.string(),
1374
+ uniqueViewers: z21.number().int().nonnegative(),
1375
+ views: z21.number().int().nonnegative()
1324
1376
  });
1325
- var artifactStatsResponseSchema = z20.object({
1377
+ var artifactStatsResponseSchema = z21.object({
1326
1378
  avgViewSeconds: avgViewSecondsSchema,
1327
- daily: z20.array(dailyViewsSchema),
1379
+ daily: z21.array(dailyViewsSchema),
1328
1380
  // Distinct hashed viewer IPs across all versions and days.
1329
- totalUniqueViewers: z20.number().int().nonnegative(),
1330
- totalViews: z20.number().int().nonnegative(),
1331
- versions: z20.array(versionViewStatsSchema)
1381
+ totalUniqueViewers: z21.number().int().nonnegative(),
1382
+ totalViews: z21.number().int().nonnegative(),
1383
+ versions: z21.array(versionViewStatsSchema)
1332
1384
  });
1333
1385
 
1334
1386
  // ../shared/src/versions.ts
1335
- import { z as z21 } from "zod";
1336
- var versionSourceSchema = z21.object({
1337
- agentName: z21.string().nullable(),
1338
- type: z21.string()
1387
+ import { z as z22 } from "zod";
1388
+ var versionSourceSchema = z22.object({
1389
+ agentName: z22.string().nullable(),
1390
+ type: z22.string()
1339
1391
  });
1340
- var versionChangesSchema = z21.object({
1341
- added: z21.array(z21.string()),
1342
- changed: z21.array(z21.string()),
1343
- removed: z21.array(z21.string())
1392
+ var versionChangesSchema = z22.object({
1393
+ added: z22.array(z22.string()),
1394
+ changed: z22.array(z22.string()),
1395
+ removed: z22.array(z22.string())
1344
1396
  });
1345
- var versionSummarySchema = z21.object({
1397
+ var versionSummarySchema = z22.object({
1346
1398
  // Absent for the first version — there is no previous version to diff against.
1347
1399
  changes: versionChangesSchema.nullable(),
1348
- createdAt: z21.string(),
1349
- entryUrl: z21.string().url(),
1350
- fileCount: z21.number().int().nonnegative(),
1351
- id: z21.string(),
1352
- isCurrent: z21.boolean(),
1400
+ createdAt: z22.string(),
1401
+ entryUrl: z22.string().url(),
1402
+ fileCount: z22.number().int().nonnegative(),
1403
+ id: z22.string(),
1404
+ isCurrent: z22.boolean(),
1353
1405
  // Author-supplied bullet-point changelog; null when the publish omitted it.
1354
- notes: z21.string().nullable(),
1406
+ notes: z22.string().nullable(),
1355
1407
  // The account that published this version, with their own avatar where
1356
1408
  // they've set one. Any workspace member can publish a new version
1357
1409
  // (SPEC-2 §5.1), so "which teammate" outranks "which agent" — the agent
1358
1410
  // is the tool, this is the person. Null for token-only publishes, which
1359
1411
  // carry no account.
1360
- publishedBy: z21.object({ image: z21.string().nullable(), name: z21.string() }).nullable(),
1412
+ publishedBy: z22.object({ image: z22.string().nullable(), name: z22.string() }).nullable(),
1361
1413
  source: versionSourceSchema,
1362
- totalSizeBytes: z21.number().int().nonnegative(),
1363
- versionNumber: z21.number().int().positive()
1414
+ totalSizeBytes: z22.number().int().nonnegative(),
1415
+ versionNumber: z22.number().int().positive()
1364
1416
  });
1365
- var versionListResponseSchema = z21.object({
1366
- versions: z21.array(versionSummarySchema)
1417
+ var versionListResponseSchema = z22.object({
1418
+ versions: z22.array(versionSummarySchema)
1367
1419
  });
1368
1420
 
1369
1421
  // src/agent-identity.ts
@@ -1544,6 +1596,21 @@ function createApiClient(apiUrl2, publishToken) {
1544
1596
  (d) => dataSummaryResponseSchema.parse(d)
1545
1597
  );
1546
1598
  },
1599
+ async insertData(artifactId, collection, fields) {
1600
+ return request(
1601
+ `${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}`,
1602
+ { body: JSON.stringify(fields), headers, method: "POST" },
1603
+ (d) => dataRecordResponseSchema.parse(d).record
1604
+ );
1605
+ },
1606
+ /** Owner-plane batch: up to DATA_LIMITS.maxInsertBatch, all-or-nothing. */
1607
+ async insertDataBatch(artifactId, collection, records) {
1608
+ return request(
1609
+ `${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}`,
1610
+ { body: JSON.stringify(records), headers, method: "POST" },
1611
+ (d) => dataBatchInsertResponseSchema.parse(d)
1612
+ );
1613
+ },
1547
1614
  // SPEC §6.18 — everything owned by the account this client's API
1548
1615
  // key (or session) belongs to.
1549
1616
  async listArtifacts() {
@@ -1647,6 +1714,13 @@ function createApiClient(apiUrl2, publishToken) {
1647
1714
  () => void 0
1648
1715
  );
1649
1716
  },
1717
+ async setExpiration(artifactId, expiresInSeconds) {
1718
+ return request(
1719
+ `${base}/api/v1/artifacts/${artifactId}/expiration`,
1720
+ { body: JSON.stringify({ expiresInSeconds }), headers, method: "PUT" },
1721
+ (d) => expirationResponseSchema.parse(d)
1722
+ );
1723
+ },
1650
1724
  // SPEC §6.16 — set a password (string) or clear it (null). Authorized
1651
1725
  // by the publish token this client was created with.
1652
1726
  async setProtection(artifactId, password) {
@@ -1703,17 +1777,17 @@ import {
1703
1777
  writeFileSync as writeFileSync2
1704
1778
  } from "fs";
1705
1779
  import path2 from "path";
1706
- import { z as z23 } from "zod";
1780
+ import { z as z24 } from "zod";
1707
1781
 
1708
1782
  // src/credentials.ts
1709
1783
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
1710
1784
  import { homedir } from "os";
1711
1785
  import path from "path";
1712
- import { z as z22 } from "zod";
1786
+ import { z as z23 } from "zod";
1713
1787
  var DEFAULT_API_URL = "https://artifact.cafe";
1714
- var credentialsSchema = z22.object({
1715
- keys: z22.record(z22.string(), z22.string()),
1716
- version: z22.literal(1)
1788
+ var credentialsSchema = z23.object({
1789
+ keys: z23.record(z23.string(), z23.string()),
1790
+ version: z23.literal(1)
1717
1791
  });
1718
1792
  function credentialsPath() {
1719
1793
  return path.join(homedir(), ".artifactcafe", "credentials");
@@ -1763,24 +1837,24 @@ function resolveApiKey(apiUrl2, flag) {
1763
1837
  }
1764
1838
 
1765
1839
  // src/config.ts
1766
- var configSchema = z23.object({
1767
- apiUrl: z23.string(),
1768
- artifactId: z23.string(),
1769
- folder: z23.string().optional(),
1770
- folderId: z23.string().optional(),
1840
+ var configSchema = z24.object({
1841
+ apiUrl: z24.string(),
1842
+ artifactId: z24.string(),
1843
+ folder: z24.string().optional(),
1844
+ folderId: z24.string().optional(),
1771
1845
  // Optional: a folder linked by `link` to an artifact you own carries no
1772
1846
  // token — owner actions authorize via your stored login instead. A first
1773
1847
  // publish still writes the anonymous-mode token here.
1774
- publishToken: z23.string().optional(),
1775
- slug: z23.string(),
1776
- url: z23.string(),
1777
- workspace: z23.string().optional(),
1778
- workspaceId: z23.string().optional(),
1779
- workspaceKind: z23.enum(["personal", "workspace"]).optional()
1780
- });
1781
- var projectConfigSchema = z23.object({
1782
- folder: z23.string().trim().min(1).optional(),
1783
- workspace: z23.string().trim().min(1)
1848
+ publishToken: z24.string().optional(),
1849
+ slug: z24.string(),
1850
+ url: z24.string(),
1851
+ workspace: z24.string().optional(),
1852
+ workspaceId: z24.string().optional(),
1853
+ workspaceKind: z24.enum(["personal", "workspace"]).optional()
1854
+ });
1855
+ var projectConfigSchema = z24.object({
1856
+ folder: z24.string().trim().min(1).optional(),
1857
+ workspace: z24.string().trim().min(1)
1784
1858
  }).strict();
1785
1859
  var CONFIG_DIR = ".artifactcafe";
1786
1860
  var CONFIG_FILE = "config.json";
@@ -1816,7 +1890,7 @@ function readProjectConfig(dir) {
1816
1890
  JSON.parse(readFileSync2(file, "utf8"))
1817
1891
  );
1818
1892
  } catch (error) {
1819
- const message = error instanceof z23.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1893
+ const message = error instanceof z24.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1820
1894
  throw new Error(`Invalid ${file}: ${message}.`);
1821
1895
  }
1822
1896
  }
@@ -2214,7 +2288,7 @@ import path8 from "path";
2214
2288
 
2215
2289
  // src/publish.ts
2216
2290
  import { exec } from "child_process";
2217
- import { readFileSync as readFileSync5 } from "fs";
2291
+ import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
2218
2292
  import path7 from "path";
2219
2293
  import { createInterface } from "readline";
2220
2294
 
@@ -2312,6 +2386,75 @@ async function dataLs(opts) {
2312
2386
  console.log(` ${JSON.stringify(fields)}`);
2313
2387
  }
2314
2388
  }
2389
+ var RECORD_SHAPE_HINT = `a JSON object or array of objects, e.g. '{"name":"Ada"}'`;
2390
+ function isRecord(value) {
2391
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2392
+ }
2393
+ async function dataInsert(opts) {
2394
+ if (opts.record === void 0 === (opts.file === void 0)) {
2395
+ throw new Error("Pass exactly one of --record '<json>' or --file <path>.");
2396
+ }
2397
+ const text = opts.record ?? readFileSync3(opts.file, "utf8");
2398
+ let parsed;
2399
+ try {
2400
+ parsed = JSON.parse(text);
2401
+ } catch {
2402
+ throw new Error(`Records must be ${RECORD_SHAPE_HINT}.`);
2403
+ }
2404
+ const linked = requireLinkedArtifact(opts.dir);
2405
+ const client = createApiClient(linked.apiUrl, linked.publishToken);
2406
+ if (isRecord(parsed)) {
2407
+ const record = await client.insertData(
2408
+ linked.artifactId,
2409
+ opts.collection,
2410
+ parsed
2411
+ );
2412
+ if (opts.json) {
2413
+ console.log(JSON.stringify({ collection: opts.collection, record }));
2414
+ return;
2415
+ }
2416
+ console.log(`Inserted ${record.id} into "${opts.collection}".`);
2417
+ return;
2418
+ }
2419
+ if (!(Array.isArray(parsed) && parsed.length > 0 && parsed.every(isRecord))) {
2420
+ throw new Error(`Records must be ${RECORD_SHAPE_HINT}.`);
2421
+ }
2422
+ const records = [];
2423
+ for (let i = 0; i < parsed.length; i += DATA_LIMITS.maxInsertBatch) {
2424
+ try {
2425
+ const page = await client.insertDataBatch(
2426
+ linked.artifactId,
2427
+ opts.collection,
2428
+ parsed.slice(i, i + DATA_LIMITS.maxInsertBatch)
2429
+ );
2430
+ records.push(...page.records);
2431
+ } catch (err) {
2432
+ if (records.length === 0) {
2433
+ throw err;
2434
+ }
2435
+ const note = ` (the first ${records.length} records were inserted; records ${i}+ may not have been)`;
2436
+ if (err instanceof ApiRequestError) {
2437
+ throw new ApiRequestError(
2438
+ err.status,
2439
+ err.code,
2440
+ `${err.message}${note}`,
2441
+ err.retryAfter
2442
+ );
2443
+ }
2444
+ throw new Error(
2445
+ `${err instanceof Error ? err.message : String(err)}${note}`
2446
+ );
2447
+ }
2448
+ }
2449
+ if (opts.json) {
2450
+ console.log(JSON.stringify({ collection: opts.collection, records }));
2451
+ return;
2452
+ }
2453
+ const plural = records.length === 1 ? "" : "s";
2454
+ console.log(
2455
+ `Inserted ${records.length} record${plural} into "${opts.collection}".`
2456
+ );
2457
+ }
2315
2458
  async function dataExport(opts) {
2316
2459
  const linked = requireLinkedArtifact(opts.dir);
2317
2460
  const client = createApiClient(linked.apiUrl, linked.publishToken);
@@ -2363,7 +2506,7 @@ async function dataClear(opts) {
2363
2506
 
2364
2507
  // src/manifest.ts
2365
2508
  import { createHash } from "crypto";
2366
- import { readdirSync, readFileSync as readFileSync4, statSync } from "fs";
2509
+ import { lstatSync, readdirSync, readFileSync as readFileSync4, statSync } from "fs";
2367
2510
  import path6 from "path";
2368
2511
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".artifactcafe"]);
2369
2512
  var IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store", "artifact-cafe.json"]);
@@ -2385,6 +2528,7 @@ function isIgnoredFile(name) {
2385
2528
  var MIME_TYPES = {
2386
2529
  ".avif": "image/avif",
2387
2530
  ".css": "text/css",
2531
+ ".csv": "text/csv",
2388
2532
  ".gif": "image/gif",
2389
2533
  ".htm": "text/html",
2390
2534
  ".html": "text/html",
@@ -2394,6 +2538,7 @@ var MIME_TYPES = {
2394
2538
  ".js": "text/javascript",
2395
2539
  ".json": "application/json",
2396
2540
  ".map": "application/json",
2541
+ ".markdown": "text/markdown",
2397
2542
  ".md": "text/markdown",
2398
2543
  ".mjs": "text/javascript",
2399
2544
  ".mp3": "audio/mpeg",
@@ -2415,6 +2560,31 @@ var MIME_TYPES = {
2415
2560
  function mimeFor(filePath) {
2416
2561
  return MIME_TYPES[path6.extname(filePath).toLowerCase()] ?? "application/octet-stream";
2417
2562
  }
2563
+ function localFile(absPath, rel) {
2564
+ if (isIgnoredFile(path6.basename(rel)) || !isSafeArtifactPath(rel)) {
2565
+ return null;
2566
+ }
2567
+ const { size } = statSync(absPath);
2568
+ if (size === 0) {
2569
+ return null;
2570
+ }
2571
+ return {
2572
+ absPath,
2573
+ hash: `sha256_${createHash("sha256").update(readFileSync4(absPath)).digest("hex")}`,
2574
+ mimeType: mimeFor(rel),
2575
+ path: rel,
2576
+ size
2577
+ };
2578
+ }
2579
+ function scanPath(target) {
2580
+ const stat = lstatSync(target);
2581
+ if (stat.isDirectory()) {
2582
+ return scanDirectory(target);
2583
+ }
2584
+ const rel = path6.basename(target);
2585
+ const file = stat.isFile() ? localFile(target, rel) : null;
2586
+ return file ? { files: [file], skipped: [] } : { files: [], skipped: [rel] };
2587
+ }
2418
2588
  function scanDirectory(dir) {
2419
2589
  const files = [];
2420
2590
  const skipped = [];
@@ -2437,23 +2607,12 @@ function scanDirectory(dir) {
2437
2607
  if (!entry.isFile()) {
2438
2608
  continue;
2439
2609
  }
2440
- if (isIgnoredFile(entry.name) || !isSafeArtifactPath(rel)) {
2610
+ const file = localFile(abs, rel);
2611
+ if (!file) {
2441
2612
  skipped.push(rel);
2442
2613
  continue;
2443
2614
  }
2444
- const { size } = statSync(abs);
2445
- if (size === 0) {
2446
- skipped.push(rel);
2447
- continue;
2448
- }
2449
- const content = readFileSync4(abs);
2450
- files.push({
2451
- absPath: abs,
2452
- hash: `sha256_${createHash("sha256").update(content).digest("hex")}`,
2453
- mimeType: mimeFor(rel),
2454
- path: rel,
2455
- size
2456
- });
2615
+ files.push(file);
2457
2616
  }
2458
2617
  }
2459
2618
  walk(dir, "");
@@ -2461,27 +2620,37 @@ function scanDirectory(dir) {
2461
2620
  return { files, skipped };
2462
2621
  }
2463
2622
  function resolveEntry(files, explicit) {
2464
- if (explicit) {
2465
- if (!files.some((f) => f.path === explicit)) {
2466
- throw new Error(
2467
- `Entry file "${explicit}" not found in the publish folder.`
2468
- );
2623
+ return resolvePublishEntry(files, explicit);
2624
+ }
2625
+
2626
+ // src/spinner.ts
2627
+ var FRAMES = ["\xE3", "\xE4", "\xE5"];
2628
+ var INTERVAL_MS = 420;
2629
+ var AMBER2 = "\x1B[38;2;214;138;57m";
2630
+ var RESET2 = "\x1B[0m";
2631
+ var CLEAR_LINE = "\r\x1B[2K";
2632
+ function startBrewing(label, enabled) {
2633
+ const stream = process.stderr;
2634
+ if (!(enabled && stream.isTTY) || process.env.CI) {
2635
+ return { stop: () => void 0 };
2636
+ }
2637
+ const color = !process.env.NO_COLOR;
2638
+ let frame = 0;
2639
+ const draw = () => {
2640
+ const glyph = FRAMES[frame % FRAMES.length];
2641
+ stream.write(
2642
+ `${CLEAR_LINE} ${color ? `${AMBER2}${glyph}${RESET2}` : glyph} ${label}`
2643
+ );
2644
+ frame += 1;
2645
+ };
2646
+ draw();
2647
+ const timer = setInterval(draw, INTERVAL_MS);
2648
+ return {
2649
+ stop() {
2650
+ clearInterval(timer);
2651
+ stream.write(CLEAR_LINE);
2469
2652
  }
2470
- return explicit;
2471
- }
2472
- if (files.some((f) => f.path === "index.html")) {
2473
- return "index.html";
2474
- }
2475
- const htmlFiles = files.filter(
2476
- (f) => f.path.endsWith(".html") && !f.path.includes("/")
2477
- );
2478
- const [onlyHtmlFile] = htmlFiles;
2479
- if (onlyHtmlFile && htmlFiles.length === 1) {
2480
- return onlyHtmlFile.path;
2481
- }
2482
- throw new Error(
2483
- htmlFiles.length === 0 ? "No entry file found. Add an index.html or pass --entry <file>." : `Multiple HTML files found (${htmlFiles.map((f) => f.path).join(", ")}). Pass --entry <file>.`
2484
- );
2653
+ };
2485
2654
  }
2486
2655
 
2487
2656
  // src/publish.ts
@@ -2492,8 +2661,10 @@ var REVIEW_LABEL = {
2492
2661
  readonly: "read-only \u2014 existing comments only"
2493
2662
  };
2494
2663
  async function publish(opts) {
2495
- const dir = path7.resolve(opts.dir);
2664
+ const target = path7.resolve(opts.dir);
2665
+ const dir = statSync2(target).isFile() ? path7.dirname(target) : target;
2496
2666
  const config = readConfig(dir);
2667
+ const expiresInSeconds = opts.expiresIn === void 0 ? void 0 : parseArtifactDuration(opts.expiresIn);
2497
2668
  if (opts.password !== void 0) {
2498
2669
  const parsed = gatePasswordSchema.safeParse(opts.password);
2499
2670
  if (!parsed.success) {
@@ -2518,11 +2689,11 @@ async function publish(opts) {
2518
2689
  apiUrl: apiUrl2,
2519
2690
  config,
2520
2691
  isNewArtifact: artifactId === null,
2521
- opts
2692
+ opts: { ...opts, dir }
2522
2693
  });
2523
- const { files, skipped } = scanDirectory(dir);
2694
+ const { files, skipped } = scanPath(target);
2524
2695
  if (files.length === 0) {
2525
- throw new Error(`Nothing to publish in ${dir} (after ignore rules).`);
2696
+ throw new Error(`Nothing to publish at ${target} (after ignore rules).`);
2526
2697
  }
2527
2698
  const entryPath = resolveEntry(files, opts.entry);
2528
2699
  const dataInfo = inspectDataManifest(files);
@@ -2536,6 +2707,7 @@ async function publish(opts) {
2536
2707
  }
2537
2708
  const sessionBody = {
2538
2709
  artifactId,
2710
+ ...expiresInSeconds === void 0 ? {} : { expiresInSeconds },
2539
2711
  // Overrides the entry HTML's own meta description on the share card.
2540
2712
  // Omitted entirely when absent, so the document keeps speaking for itself.
2541
2713
  ...opts.description?.trim() ? { description: opts.description.trim() } : {},
@@ -2570,11 +2742,17 @@ async function publish(opts) {
2570
2742
  client = createApiClient(apiUrl2, credential);
2571
2743
  session = await client.createSession(sessionBody);
2572
2744
  }
2745
+ if (expiresInSeconds !== void 0 && session.expiresInSeconds !== expiresInSeconds) {
2746
+ throw new Error(
2747
+ "This API does not support temporary publishing. Nothing was uploaded or finalized; upgrade the API before using --expires-in."
2748
+ );
2749
+ }
2573
2750
  const byPath = new Map(files.map((f) => [f.path, f]));
2574
2751
  log(
2575
2752
  ` uploading ${session.filesToUpload.length} file(s)` + (session.skippedFiles.length ? `, ${session.skippedFiles.length} unchanged` : "")
2576
2753
  );
2577
2754
  const presence = startPublishingPresence(client, session.artifactId);
2755
+ const brewing = startBrewing("brewing\u2026", !opts.json);
2578
2756
  try {
2579
2757
  await inBatches(
2580
2758
  session.filesToUpload,
@@ -2591,10 +2769,18 @@ async function publish(opts) {
2591
2769
  );
2592
2770
  }
2593
2771
  );
2772
+ } catch (error) {
2773
+ brewing.stop();
2774
+ throw error;
2594
2775
  } finally {
2595
2776
  presence.stop();
2596
2777
  }
2597
- const result = await client.finalize(session.publishSessionId);
2778
+ let result;
2779
+ try {
2780
+ result = await client.finalize(session.publishSessionId);
2781
+ } finally {
2782
+ brewing.stop();
2783
+ }
2598
2784
  if (usingAgentToken && result.workspaceId) {
2599
2785
  placement.workspaceId = result.workspaceId;
2600
2786
  }
@@ -2634,6 +2820,8 @@ async function publish(opts) {
2634
2820
  JSON.stringify(
2635
2821
  {
2636
2822
  artifactId: result.artifactId,
2823
+ autoDeleteAt: result.autoDeleteAt ?? null,
2824
+ expiresAt: result.expiresAt ?? null,
2637
2825
  ...dataInfo.config ? { data: dataInfo.config } : {},
2638
2826
  claimed: result.claimed,
2639
2827
  destination: placement.destination,
@@ -2678,7 +2866,7 @@ ${result.url}
2678
2866
  ${result.claimUrl}
2679
2867
  `
2680
2868
  );
2681
- } else if (result.claimed) {
2869
+ } else if (result.claimed && !result.expiresAt) {
2682
2870
  console.log("Claimed by your account \u2014 no expiry.\n");
2683
2871
  }
2684
2872
  if (opts.password !== void 0) {
@@ -2714,6 +2902,12 @@ ${result.claimUrl}
2714
2902
  }
2715
2903
  }
2716
2904
  if (!opts.json) {
2905
+ if (result.expiresAt) {
2906
+ console.log(
2907
+ `${result.autoDeleteAt ? "Temporary artifact" : "Anonymous artifact"} \u2014 expires at ${result.expiresAt}. All versions, comments, files, and artifact data will be automatically deleted.
2908
+ `
2909
+ );
2910
+ }
2717
2911
  for (const announcement of dataInfo.announcements) {
2718
2912
  console.log(announcement);
2719
2913
  }
@@ -3007,6 +3201,35 @@ function printCliError(err, json) {
3007
3201
  }
3008
3202
  }
3009
3203
 
3204
+ // src/expiration.ts
3205
+ import path9 from "path";
3206
+ async function expiration(opts) {
3207
+ if (opts.duration !== void 0 === opts.keep) {
3208
+ throw new Error("Provide exactly one of --in <duration> or --keep.");
3209
+ }
3210
+ const seconds = opts.keep ? null : parseArtifactDuration(opts.duration);
3211
+ const target = resolveArtifactTarget(path9.resolve(opts.dir), opts);
3212
+ const result = await createApiClient(
3213
+ target.apiUrl,
3214
+ target.credential
3215
+ ).setExpiration(target.artifactId, seconds);
3216
+ if (opts.json) {
3217
+ console.log(JSON.stringify(result, null, 2));
3218
+ return;
3219
+ }
3220
+ if (result.autoDeleteAt) {
3221
+ console.log(`Temporary artifact \u2014 expires at ${result.expiresAt}.`);
3222
+ } else if (result.expiresAt) {
3223
+ console.log(
3224
+ `Temporary deadline cleared. Anonymous retention still expires at ${result.expiresAt}; claim the artifact to keep it.`
3225
+ );
3226
+ } else {
3227
+ console.log(
3228
+ "Artifact kept \u2014 no automatic expiry. Its link, versions, and comments are preserved."
3229
+ );
3230
+ }
3231
+ }
3232
+
3010
3233
  // src/folders.ts
3011
3234
  async function folders(opts) {
3012
3235
  if (opts.action !== void 0 && opts.action !== "create") {
@@ -3103,7 +3326,7 @@ function printFolder(folder) {
3103
3326
  }
3104
3327
 
3105
3328
  // src/link.ts
3106
- import path9 from "path";
3329
+ import path10 from "path";
3107
3330
  import { createInterface as createInterface2 } from "readline";
3108
3331
 
3109
3332
  // src/list.ts
@@ -3332,7 +3555,7 @@ async function link(opts) {
3332
3555
  }
3333
3556
  chosen = await pickArtifact(artifacts);
3334
3557
  }
3335
- const dir = path9.resolve(opts.dir);
3558
+ const dir = path10.resolve(opts.dir);
3336
3559
  writeConfig(dir, {
3337
3560
  apiUrl: apiUrl2,
3338
3561
  artifactId: chosen.id,
@@ -3500,7 +3723,7 @@ function logout(opts) {
3500
3723
  }
3501
3724
 
3502
3725
  // src/message.ts
3503
- import path10 from "path";
3726
+ import path11 from "path";
3504
3727
  async function readStdin2() {
3505
3728
  if (process.stdin.isTTY) {
3506
3729
  return "";
@@ -3524,7 +3747,7 @@ async function editMessage(opts) {
3524
3747
  'Nothing to save. Pass the new text with --body "\u2026", or pipe it on stdin.'
3525
3748
  );
3526
3749
  }
3527
- const dir = path10.resolve(opts.dir);
3750
+ const dir = path11.resolve(opts.dir);
3528
3751
  const target = resolveArtifactTarget(dir, {
3529
3752
  apiKey: opts.apiKey,
3530
3753
  artifact: opts.artifact
@@ -3544,7 +3767,7 @@ async function deleteMessage(opts) {
3544
3767
  "Pass the message id to delete \u2014 get it from `artifact-cafe comments --json`."
3545
3768
  );
3546
3769
  }
3547
- const dir = path10.resolve(opts.dir);
3770
+ const dir = path11.resolve(opts.dir);
3548
3771
  const target = resolveArtifactTarget(dir, {
3549
3772
  apiKey: opts.apiKey,
3550
3773
  artifact: opts.artifact
@@ -3559,12 +3782,12 @@ async function deleteMessage(opts) {
3559
3782
  }
3560
3783
 
3561
3784
  // src/move.ts
3562
- import path11 from "path";
3785
+ import path12 from "path";
3563
3786
  async function move(opts) {
3564
3787
  if (Boolean(opts.folder) === Boolean(opts.unfiled)) {
3565
3788
  throw new Error("Pass exactly one of --folder <name-or-id> or --unfiled.");
3566
3789
  }
3567
- const dir = path11.resolve(opts.dir);
3790
+ const dir = path12.resolve(opts.dir);
3568
3791
  const target = resolveArtifactTarget(dir, {
3569
3792
  apiKey: opts.apiKey,
3570
3793
  artifact: opts.artifact
@@ -3654,9 +3877,9 @@ async function move(opts) {
3654
3877
  }
3655
3878
 
3656
3879
  // src/open.ts
3657
- import path12 from "path";
3880
+ import path13 from "path";
3658
3881
  async function openArtifact(opts) {
3659
- const dir = path12.resolve(opts.dir);
3882
+ const dir = path13.resolve(opts.dir);
3660
3883
  const target = resolveArtifactTarget(dir, {
3661
3884
  apiKey: opts.apiKey,
3662
3885
  artifact: opts.artifact
@@ -3674,7 +3897,7 @@ async function openArtifact(opts) {
3674
3897
  }
3675
3898
 
3676
3899
  // src/protect.ts
3677
- import path13 from "path";
3900
+ import path14 from "path";
3678
3901
  async function protect(opts) {
3679
3902
  if (opts.off && opts.password !== void 0) {
3680
3903
  throw new Error("Pass either --password or --off, not both.");
@@ -3691,7 +3914,7 @@ async function protect(opts) {
3691
3914
  throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
3692
3915
  }
3693
3916
  }
3694
- const dir = path13.resolve(opts.dir);
3917
+ const dir = path14.resolve(opts.dir);
3695
3918
  const target = resolveArtifactTarget(dir, {
3696
3919
  apiKey: opts.apiKey,
3697
3920
  artifact: opts.artifact
@@ -3716,7 +3939,7 @@ async function protect(opts) {
3716
3939
  }
3717
3940
 
3718
3941
  // src/reply.ts
3719
- import path14 from "path";
3942
+ import path15 from "path";
3720
3943
  async function readStdin3() {
3721
3944
  if (process.stdin.isTTY) {
3722
3945
  return "";
@@ -3740,7 +3963,7 @@ async function replyToThread(opts) {
3740
3963
  'Nothing to post. Pass the reply with --body "\u2026", or pipe it on stdin.'
3741
3964
  );
3742
3965
  }
3743
- const dir = path14.resolve(opts.dir);
3966
+ const dir = path15.resolve(opts.dir);
3744
3967
  const target = resolveArtifactTarget(dir, {
3745
3968
  apiKey: opts.apiKey,
3746
3969
  artifact: opts.artifact
@@ -3769,7 +3992,7 @@ async function replyToThread(opts) {
3769
3992
  }
3770
3993
 
3771
3994
  // src/resolve.ts
3772
- import path15 from "path";
3995
+ import path16 from "path";
3773
3996
  async function changeThreadStatus(opts) {
3774
3997
  const threadId = opts.thread?.trim();
3775
3998
  if (!threadId) {
@@ -3777,7 +4000,7 @@ async function changeThreadStatus(opts) {
3777
4000
  `Pass the thread id to ${opts.action} \u2014 get it from \`artifact-cafe comments --json\`.`
3778
4001
  );
3779
4002
  }
3780
- const dir = path15.resolve(opts.dir);
4003
+ const dir = path16.resolve(opts.dir);
3781
4004
  const target = resolveArtifactTarget(dir, {
3782
4005
  apiKey: opts.apiKey,
3783
4006
  artifact: opts.artifact
@@ -3814,7 +4037,7 @@ async function changeThreadStatus(opts) {
3814
4037
  }
3815
4038
 
3816
4039
  // src/review.ts
3817
- import path16 from "path";
4040
+ import path17 from "path";
3818
4041
  var OUTCOME = {
3819
4042
  off: "Plain artifact \u2014 the review tools are hidden entirely",
3820
4043
  open: "Open for review \u2014 anyone who can open it can comment",
@@ -3828,7 +4051,7 @@ async function review(opts) {
3828
4051
  );
3829
4052
  }
3830
4053
  const mode = parsed.data;
3831
- const dir = path16.resolve(opts.dir);
4054
+ const dir = path17.resolve(opts.dir);
3832
4055
  const target = resolveArtifactTarget(dir, {
3833
4056
  apiKey: opts.apiKey,
3834
4057
  artifact: opts.artifact
@@ -3850,10 +4073,10 @@ async function review(opts) {
3850
4073
  }
3851
4074
 
3852
4075
  // src/share.ts
3853
- import path17 from "path";
4076
+ import path18 from "path";
3854
4077
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3855
4078
  async function share(opts) {
3856
- const dir = path17.resolve(opts.dir);
4079
+ const dir = path18.resolve(opts.dir);
3857
4080
  const target = resolveArtifactTarget(dir, {
3858
4081
  apiKey: opts.apiKey,
3859
4082
  artifact: opts.artifact
@@ -3908,7 +4131,7 @@ General access: ${visibility}`);
3908
4131
  console.log("");
3909
4132
  }
3910
4133
  async function unshare(opts) {
3911
- const dir = path17.resolve(opts.dir);
4134
+ const dir = path18.resolve(opts.dir);
3912
4135
  const target = resolveArtifactTarget(dir, {
3913
4136
  apiKey: opts.apiKey,
3914
4137
  artifact: opts.artifact
@@ -3931,7 +4154,7 @@ async function unshare(opts) {
3931
4154
  }
3932
4155
 
3933
4156
  // src/status.ts
3934
- import path18 from "path";
4157
+ import path19 from "path";
3935
4158
  async function status(opts) {
3936
4159
  if (!AGENT_STATES.includes(opts.state)) {
3937
4160
  throw new Error(
@@ -3939,7 +4162,7 @@ async function status(opts) {
3939
4162
  );
3940
4163
  }
3941
4164
  const state = opts.state;
3942
- const dir = path18.resolve(opts.dir);
4165
+ const dir = path19.resolve(opts.dir);
3943
4166
  const target = resolveArtifactTarget(dir, {
3944
4167
  apiKey: opts.apiKey,
3945
4168
  artifact: opts.artifact
@@ -3968,26 +4191,26 @@ async function status(opts) {
3968
4191
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
3969
4192
  import { createRequire } from "module";
3970
4193
  import { homedir as homedir2 } from "os";
3971
- import path19 from "path";
3972
- import { z as z24 } from "zod";
4194
+ import path20 from "path";
4195
+ import { z as z25 } from "zod";
3973
4196
  var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
3974
4197
  var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
3975
4198
  var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
3976
4199
  var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
3977
4200
  var FETCH_TIMEOUT_MS = 2e3;
3978
4201
  var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
3979
- var cacheSchema = z24.object({
3980
- checkedAt: z24.number(),
3981
- cliLatest: z24.string().nullable(),
3982
- skillLatest: z24.string().nullable(),
3983
- version: z24.literal(1)
4202
+ var cacheSchema = z25.object({
4203
+ checkedAt: z25.number(),
4204
+ cliLatest: z25.string().nullable(),
4205
+ skillLatest: z25.string().nullable(),
4206
+ version: z25.literal(1)
3984
4207
  });
3985
4208
  function cliVersion() {
3986
4209
  const pkg = createRequire(import.meta.url)("../package.json");
3987
4210
  return pkg.version;
3988
4211
  }
3989
4212
  function cachePath() {
3990
- return path19.join(homedir2(), ".artifactcafe", "update-check.json");
4213
+ return path20.join(homedir2(), ".artifactcafe", "update-check.json");
3991
4214
  }
3992
4215
  function readCache() {
3993
4216
  try {
@@ -3998,7 +4221,7 @@ function readCache() {
3998
4221
  }
3999
4222
  function writeCache(cache) {
4000
4223
  try {
4001
- mkdirSync3(path19.dirname(cachePath()), { mode: 448, recursive: true });
4224
+ mkdirSync3(path20.dirname(cachePath()), { mode: 448, recursive: true });
4002
4225
  writeFileSync3(cachePath(), `${JSON.stringify(cache, null, 2)}
4003
4226
  `);
4004
4227
  } catch {
@@ -4035,7 +4258,7 @@ async function fetchText(url) {
4035
4258
  }
4036
4259
  async function fetchLatestVersions(previous) {
4037
4260
  const [cli, skill] = await Promise.all([
4038
- fetchJson(REGISTRY_LATEST_URL).then((body) => z24.object({ version: z24.string() }).parse(body).version).catch(() => null),
4261
+ fetchJson(REGISTRY_LATEST_URL).then((body) => z25.object({ version: z25.string() }).parse(body).version).catch(() => null),
4039
4262
  fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
4040
4263
  ]);
4041
4264
  return {
@@ -4051,7 +4274,7 @@ function installedSkillVersions() {
4051
4274
  const versions = [];
4052
4275
  for (const root of roots) {
4053
4276
  for (const dir of dirs) {
4054
- const file = path19.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
4277
+ const file = path20.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
4055
4278
  if (!existsSync3(file)) {
4056
4279
  continue;
4057
4280
  }
@@ -4179,10 +4402,14 @@ var publish2 = defineCommand({
4179
4402
  },
4180
4403
  dir: {
4181
4404
  default: ".",
4182
- description: "Directory to publish",
4405
+ description: "File or directory to publish",
4183
4406
  type: "positional"
4184
4407
  },
4185
4408
  entry: { description: "Entry file (default: auto-detect)", type: "string" },
4409
+ "expires-in": {
4410
+ description: "Temporary lifetime: 30m, 1h, 7d (ordinary republishes preserve it)",
4411
+ type: "string"
4412
+ },
4186
4413
  folder: {
4187
4414
  description: "Publish into this workspace folder name (or id)",
4188
4415
  type: "string"
@@ -4207,7 +4434,10 @@ var publish2 = defineCommand({
4207
4434
  type: "string"
4208
4435
  }
4209
4436
  },
4210
- meta: { description: "Publish a folder as an artifact", name: "publish" },
4437
+ meta: {
4438
+ description: "Publish a file or folder as an artifact",
4439
+ name: "publish"
4440
+ },
4211
4441
  async run({ args }) {
4212
4442
  try {
4213
4443
  await publish({
@@ -4216,6 +4446,7 @@ var publish2 = defineCommand({
4216
4446
  description: readStringArg(process.argv.slice(2), "--description"),
4217
4447
  dir: args.dir,
4218
4448
  entry: args.entry,
4449
+ expiresIn: args["expires-in"],
4219
4450
  folder: args.folder,
4220
4451
  json: Boolean(args.json),
4221
4452
  notes: readStringArg(process.argv.slice(2), "--notes"),
@@ -4471,6 +4702,48 @@ var link2 = defineCommand({
4471
4702
  }
4472
4703
  }
4473
4704
  });
4705
+ var expiration2 = defineCommand({
4706
+ args: {
4707
+ "api-key": {
4708
+ description: "Account or agent token (default: env or login)",
4709
+ type: "string"
4710
+ },
4711
+ artifact: {
4712
+ description: "Artifact ID instead of the linked folder",
4713
+ type: "string"
4714
+ },
4715
+ dir: {
4716
+ default: ".",
4717
+ description: "Published directory",
4718
+ type: "positional"
4719
+ },
4720
+ in: { description: "Reset lifetime from now: 30m, 1h, 7d", type: "string" },
4721
+ json: { description: "Machine-readable output", type: "boolean" },
4722
+ keep: {
4723
+ description: "Clear the explicit temporary deadline",
4724
+ type: "boolean"
4725
+ }
4726
+ },
4727
+ meta: {
4728
+ description: "Extend a temporary artifact or keep it",
4729
+ name: "expiration"
4730
+ },
4731
+ async run({ args }) {
4732
+ try {
4733
+ await expiration({
4734
+ apiKey: args["api-key"],
4735
+ artifact: args.artifact,
4736
+ dir: args.dir,
4737
+ duration: args.in,
4738
+ json: Boolean(args.json),
4739
+ keep: Boolean(args.keep)
4740
+ });
4741
+ } catch (err) {
4742
+ printCliError(err, Boolean(args.json));
4743
+ process.exitCode = 1;
4744
+ }
4745
+ }
4746
+ });
4474
4747
  var comments2 = defineCommand({
4475
4748
  args: {
4476
4749
  "api-key": {
@@ -5139,6 +5412,47 @@ var dataLsCommand = defineCommand({
5139
5412
  }
5140
5413
  }
5141
5414
  });
5415
+ var dataInsertCommand = defineCommand({
5416
+ args: {
5417
+ collection: {
5418
+ description: "Collection name",
5419
+ required: true,
5420
+ type: "positional"
5421
+ },
5422
+ dir: {
5423
+ default: ".",
5424
+ description: "Published directory",
5425
+ type: "positional"
5426
+ },
5427
+ file: {
5428
+ description: "Read the record(s) from a JSON file instead of --record",
5429
+ type: "string"
5430
+ },
5431
+ json: { description: "Machine-readable output", type: "boolean" },
5432
+ record: {
5433
+ description: "A JSON object, or an array of objects for a batch",
5434
+ type: "string"
5435
+ }
5436
+ },
5437
+ meta: {
5438
+ description: "Insert one or many records into a collection",
5439
+ name: "insert"
5440
+ },
5441
+ async run({ args }) {
5442
+ try {
5443
+ await dataInsert({
5444
+ collection: args.collection,
5445
+ dir: args.dir,
5446
+ file: args.file,
5447
+ json: Boolean(args.json),
5448
+ record: args.record
5449
+ });
5450
+ } catch (err) {
5451
+ printCliError(err, Boolean(args.json));
5452
+ process.exit(1);
5453
+ }
5454
+ }
5455
+ });
5142
5456
  var dataExportCommand = defineCommand({
5143
5457
  args: {
5144
5458
  dir: {
@@ -5201,12 +5515,13 @@ var dataClearCommand = defineCommand({
5201
5515
  });
5202
5516
  var data = defineCommand({
5203
5517
  meta: {
5204
- description: "Read or clear the artifact's shared records",
5518
+ description: "Read, insert, or clear the artifact's shared records",
5205
5519
  name: "data"
5206
5520
  },
5207
5521
  subCommands: {
5208
5522
  clear: dataClearCommand,
5209
5523
  export: dataExportCommand,
5524
+ insert: dataInsertCommand,
5210
5525
  ls: dataLsCommand
5211
5526
  }
5212
5527
  });
@@ -5235,6 +5550,7 @@ var main = defineCommand({
5235
5550
  data,
5236
5551
  delete: del,
5237
5552
  edit,
5553
+ expiration: expiration2,
5238
5554
  folders: folders2,
5239
5555
  link: link2,
5240
5556
  list: list2,