artifact-cafe 0.0.13 → 0.0.14

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 +628 -486
  2. package/package.json +12 -1
package/dist/index.js CHANGED
@@ -83,6 +83,74 @@ var artifactListResponseSchema = z.object({
83
83
  artifacts: z.array(artifactListItemSchema)
84
84
  });
85
85
 
86
+ // ../shared/src/agents.ts
87
+ var AGENT_BRANDS = [
88
+ {
89
+ id: "claude-code",
90
+ label: "Claude Code",
91
+ match: ["claude"],
92
+ short: "Claude"
93
+ },
94
+ {
95
+ id: "codex",
96
+ label: "OpenAI Codex",
97
+ match: ["codex"],
98
+ short: "Codex"
99
+ },
100
+ { id: "cursor", label: "Cursor", match: ["cursor"], short: "Cursor" },
101
+ {
102
+ id: "copilot",
103
+ label: "GitHub Copilot",
104
+ match: ["copilot"],
105
+ short: "Copilot"
106
+ },
107
+ {
108
+ id: "gemini-cli",
109
+ label: "Gemini CLI",
110
+ match: ["gemini"],
111
+ short: "Gemini"
112
+ },
113
+ { id: "opencode", label: "opencode", match: ["opencode"], short: "opencode" },
114
+ {
115
+ id: "openclaw",
116
+ label: "OpenClaw",
117
+ match: ["openclaw"],
118
+ short: "OpenClaw"
119
+ },
120
+ { id: "hermes", label: "Hermes", match: ["hermes"], short: "Hermes" }
121
+ ];
122
+ function normalizeAgent(raw) {
123
+ const value = raw?.trim().toLowerCase();
124
+ if (!value) {
125
+ return null;
126
+ }
127
+ return AGENT_BRANDS.find((brand) => brand.id === value) ?? AGENT_BRANDS.find((brand) => brand.match.some((m) => value.includes(m))) ?? null;
128
+ }
129
+ var ENV_FINGERPRINTS = [
130
+ { id: "claude-code", prefixes: ["CLAUDECODE", "CLAUDE_CODE_"] },
131
+ { id: "codex", prefixes: ["CODEX_"] },
132
+ { id: "cursor", prefixes: ["CURSOR_"] },
133
+ { id: "copilot", prefixes: ["COPILOT_", "GITHUB_COPILOT_"] },
134
+ { id: "gemini-cli", prefixes: ["GEMINI_CLI", "GEMINI_SANDBOX"] },
135
+ { id: "opencode", prefixes: ["OPENCODE"] }
136
+ ];
137
+ function detectAgentFromEnv(env) {
138
+ const explicit = env.ARTIFACT_CAFE_AGENT?.trim();
139
+ if (explicit) {
140
+ return explicit;
141
+ }
142
+ const keys = Object.keys(env).filter((key) => env[key]);
143
+ for (const { id, prefixes } of ENV_FINGERPRINTS) {
144
+ const hit = keys.some(
145
+ (key) => prefixes.some((prefix) => key.startsWith(prefix))
146
+ );
147
+ if (hit) {
148
+ return AGENT_BRANDS.find((brand) => brand.id === id)?.label;
149
+ }
150
+ }
151
+ return normalizeAgent(env.AI_AGENT)?.label;
152
+ }
153
+
86
154
  // ../shared/src/avatars.ts
87
155
  import { z as z2 } from "zod";
88
156
  var AVATAR_CONTENT_TYPES = [
@@ -100,18 +168,27 @@ var workspaceAvatarResponseSchema = z2.object({
100
168
  });
101
169
 
102
170
  // ../shared/src/comments.ts
103
- import { z as z5 } from "zod";
171
+ import { z as z6 } from "zod";
172
+
173
+ // ../shared/src/emoji.ts
174
+ import { z as z3 } from "zod";
175
+ var SINGLE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
176
+ var REDUNDANT_VS16 = new RegExp("(\\p{Emoji_Presentation})\\uFE0F", "gu");
177
+ function canonicalizeEmoji(s) {
178
+ return s.replace(REDUNDANT_VS16, "$1");
179
+ }
180
+ var iconEmojiSchema = z3.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.");
104
181
 
105
182
  // ../shared/src/publish.ts
106
- import { z as z4 } from "zod";
183
+ import { z as z5 } from "zod";
107
184
 
108
185
  // ../shared/src/review-mode.ts
109
- import { z as z3 } from "zod";
110
- var reviewModeSchema = z3.enum(["open", "readonly", "off"]);
111
- var setReviewModeSchema = z3.object({
186
+ import { z as z4 } from "zod";
187
+ var reviewModeSchema = z4.enum(["open", "readonly", "off"]);
188
+ var setReviewModeSchema = z4.object({
112
189
  mode: reviewModeSchema
113
190
  });
114
- var reviewModeResponseSchema = z3.object({
191
+ var reviewModeResponseSchema = z4.object({
115
192
  mode: reviewModeSchema
116
193
  });
117
194
 
@@ -128,266 +205,275 @@ function isSafeArtifactPath(p) {
128
205
  }
129
206
  return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
130
207
  }
131
- var manifestFileSchema = z4.object({
132
- hash: z4.string().regex(/^sha256_[a-f0-9]{64}$/),
133
- mimeType: z4.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
134
- path: z4.string().min(1).max(1024).refine(isSafeArtifactPath, {
208
+ var CARD_DESCRIPTION_MAX = 300;
209
+ var CARD_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
210
+ var manifestFileSchema = z5.object({
211
+ hash: z5.string().regex(/^sha256_[a-f0-9]{64}$/),
212
+ mimeType: z5.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
213
+ path: z5.string().min(1).max(1024).refine(isSafeArtifactPath, {
135
214
  message: "path must be a safe relative path"
136
215
  }),
137
- size: z4.number().int().positive()
138
- });
139
- var publishSourceSchema = z4.object({
140
- agent: z4.string().optional(),
141
- runId: z4.string().optional(),
142
- type: z4.literal("cli")
143
- });
144
- var createPublishSessionSchema = z4.object({
145
- artifactId: z4.string().nullable(),
146
- entryPath: z4.string().optional(),
147
- files: z4.array(manifestFileSchema).min(1).max(1e3),
216
+ size: z5.number().int().positive()
217
+ });
218
+ var publishSourceSchema = z5.object({
219
+ agent: z5.string().optional(),
220
+ runId: z5.string().optional(),
221
+ type: z5.literal("cli")
222
+ });
223
+ var createPublishSessionSchema = z5.object({
224
+ artifactId: z5.string().nullable(),
225
+ // One-line share-card summary (`--description`). Overrides whatever the
226
+ // entry HTML declares; leave it off and the API reads the document's own
227
+ // `og:description`/`description` at finalize.
228
+ description: z5.string().max(CARD_DESCRIPTION_MAX).optional(),
229
+ entryPath: z5.string().optional(),
230
+ files: z5.array(manifestFileSchema).min(1).max(1e3),
148
231
  // SPEC-2 §6.4 — workspace placement is available only to authenticated
149
232
  // members; the API enforces membership and folder ownership. folderId
150
233
  // without workspaceId resolves against the account's personal workspace.
151
- folderId: z4.string().min(1).optional(),
234
+ folderId: z5.string().min(1).optional(),
152
235
  // Bullet-point changelog for this version, authored by the publisher.
153
236
  // Optional on every publish (incl. v1); stored immutably on the version.
154
- notes: z4.string().max(4e3).optional(),
237
+ notes: z5.string().max(4e3).optional(),
155
238
  source: publishSourceSchema,
156
- title: z4.string().max(200).optional(),
157
- workspaceId: z4.string().min(1).optional()
158
- });
159
- var publishSessionResponseSchema = z4.object({
160
- artifactId: z4.string(),
161
- expiresAt: z4.string(),
162
- filesToUpload: z4.array(
163
- z4.object({
164
- method: z4.literal("PUT"),
165
- path: z4.string(),
166
- uploadUrl: z4.string().url()
239
+ title: z5.string().max(200).optional(),
240
+ workspaceId: z5.string().min(1).optional()
241
+ });
242
+ var publishSessionResponseSchema = z5.object({
243
+ artifactId: z5.string(),
244
+ expiresAt: z5.string(),
245
+ filesToUpload: z5.array(
246
+ z5.object({
247
+ method: z5.literal("PUT"),
248
+ path: z5.string(),
249
+ uploadUrl: z5.string().url()
167
250
  })
168
251
  ),
169
- publishSessionId: z4.string(),
170
- skippedFiles: z4.array(z4.string()),
171
- versionId: z4.string()
252
+ publishSessionId: z5.string(),
253
+ skippedFiles: z5.array(z5.string()),
254
+ versionId: z5.string()
172
255
  });
173
- var finalizeResponseSchema = z4.object({
174
- artifactId: z4.string(),
256
+ var finalizeResponseSchema = z5.object({
257
+ artifactId: z5.string(),
175
258
  // API-key publishes create the artifact already claimed (SPEC §5.4) —
176
259
  // the CLI uses this to tell "claim URL pending" from "already yours".
177
- claimed: z4.boolean(),
260
+ claimed: z5.boolean(),
178
261
  // Returned only on first publish of a new artifact (one-time).
179
- claimUrl: z4.string().url().optional(),
180
- publishToken: z4.string().optional(),
181
- url: z4.string().url(),
182
- versionId: z4.string(),
183
- versionNumber: z4.number().int().positive(),
262
+ claimUrl: z5.string().url().optional(),
263
+ publishToken: z5.string().optional(),
264
+ url: z5.string().url(),
265
+ versionId: z5.string(),
266
+ versionNumber: z5.number().int().positive(),
184
267
  // SPEC-2 §5.2 — who can open the review URL. A publish into a folder
185
268
  // inherits that folder's visibility, so this is the only place the CLI
186
269
  // learns that a link it just printed is members-only. Optional so a new
187
270
  // CLI against an older API degrades to "unknown" instead of failing the
188
271
  // publish over a field that only shapes the closing message.
189
- visibility: z4.enum(["public", "unlisted", "workspace", "private"]).optional()
190
- });
191
- var artifactResponseSchema = z4.object({
192
- claimed: z4.boolean(),
193
- currentVersion: z4.object({
194
- entryUrl: z4.string().url(),
195
- id: z4.string(),
196
- versionNumber: z4.number().int().positive()
272
+ visibility: z5.enum(["public", "unlisted", "workspace", "private"]).optional()
273
+ });
274
+ var artifactResponseSchema = z5.object({
275
+ claimed: z5.boolean(),
276
+ currentVersion: z5.object({
277
+ entryUrl: z5.string().url(),
278
+ id: z5.string(),
279
+ versionNumber: z5.number().int().positive()
197
280
  }).nullable(),
281
+ // Custom icon, or null to render the default document glyph.
282
+ emoji: z5.string().nullable(),
198
283
  // SPEC-2 §6.4 — placement + visibility surface on the artifact.
199
- folderId: z4.string().nullable(),
200
- id: z4.string(),
284
+ folderId: z5.string().nullable(),
285
+ id: z5.string(),
201
286
  // SPEC-2 §5.8 — what a viewer may do once in (open / readonly / off).
202
287
  reviewMode: reviewModeSchema,
203
- slug: z4.string(),
204
- title: z4.string(),
205
- visibility: z4.enum(["public", "unlisted", "workspace", "private"]),
206
- workspaceId: z4.string().nullable()
288
+ slug: z5.string(),
289
+ title: z5.string(),
290
+ visibility: z5.enum(["public", "unlisted", "workspace", "private"]),
291
+ workspaceId: z5.string().nullable()
207
292
  });
208
293
 
209
294
  // ../shared/src/comments.ts
210
- var anchorPageSchema = z5.string().max(1024).refine(isSafeArtifactPath, {
295
+ var anchorPageSchema = z6.string().max(1024).refine(isSafeArtifactPath, {
211
296
  message: "page must be a safe relative path"
212
297
  }).optional();
213
- var elementSnapshotSchema = z5.object({
214
- classes: z5.string().max(256).optional(),
215
- id: z5.string().max(256).optional(),
216
- tag: z5.string().min(1).max(128),
217
- text: z5.string().max(200).optional()
298
+ var elementSnapshotSchema = z6.object({
299
+ classes: z6.string().max(256).optional(),
300
+ id: z6.string().max(256).optional(),
301
+ tag: z6.string().min(1).max(128),
302
+ text: z6.string().max(200).optional()
218
303
  });
219
- var pointAnchorSchema = z5.object({
304
+ var pointAnchorSchema = z6.object({
220
305
  element: elementSnapshotSchema.optional(),
221
306
  // elementPath is the primary anchor; x/y are offsets within the
222
307
  // element's box, normalized 0..1 (SPEC.md §8.1).
223
- elementPath: z5.string().max(2048),
308
+ elementPath: z6.string().max(2048),
224
309
  page: anchorPageSchema,
225
- type: z5.literal("point"),
226
- viewportHeight: z5.number().int().positive().max(1e5),
227
- viewportWidth: z5.number().int().positive().max(1e5),
228
- x: z5.number().min(0).max(1),
229
- y: z5.number().min(0).max(1)
230
- });
231
- var textAnchorSchema = z5.object({
232
- elementPath: z5.string().max(2048),
233
- endOffset: z5.number().int().nonnegative().max(1e8),
310
+ type: z6.literal("point"),
311
+ viewportHeight: z6.number().int().positive().max(1e5),
312
+ viewportWidth: z6.number().int().positive().max(1e5),
313
+ x: z6.number().min(0).max(1),
314
+ y: z6.number().min(0).max(1)
315
+ });
316
+ var textAnchorSchema = z6.object({
317
+ elementPath: z6.string().max(2048),
318
+ endOffset: z6.number().int().nonnegative().max(1e8),
234
319
  page: anchorPageSchema,
235
- prefix: z5.string().max(200).optional(),
236
- quote: z5.string().min(1).max(1e3),
237
- startOffset: z5.number().int().nonnegative().max(1e8),
238
- suffix: z5.string().max(200).optional(),
239
- type: z5.literal("text")
320
+ prefix: z6.string().max(200).optional(),
321
+ quote: z6.string().min(1).max(1e3),
322
+ startOffset: z6.number().int().nonnegative().max(1e8),
323
+ suffix: z6.string().max(200).optional(),
324
+ type: z6.literal("text")
240
325
  });
241
- var pageAnchorSchema = z5.object({
326
+ var pageAnchorSchema = z6.object({
242
327
  page: anchorPageSchema,
243
- type: z5.literal("page")
328
+ type: z6.literal("page")
244
329
  });
245
- var anchorSchema = z5.discriminatedUnion("type", [
330
+ var anchorSchema = z6.discriminatedUnion("type", [
246
331
  pointAnchorSchema,
247
332
  textAnchorSchema,
248
333
  pageAnchorSchema
249
334
  ]);
250
- var displayNameSchema = z5.string().trim().min(1).max(64);
251
- var guestSchema = z5.object({
335
+ var displayNameSchema = z6.string().trim().min(1).max(64);
336
+ var guestSchema = z6.object({
252
337
  displayName: displayNameSchema,
253
- guestId: z5.string().min(16)
338
+ guestId: z6.string().min(16)
254
339
  });
255
- var viaLabelSchema = z5.string().trim().min(1).max(64);
256
- var createCommentThreadSchema = z5.object({
340
+ var viaLabelSchema = z6.string().trim().min(1).max(64);
341
+ var createCommentThreadSchema = z6.object({
257
342
  anchor: anchorSchema,
258
- body: z5.string().min(1).max(1e4),
343
+ body: z6.string().min(1).max(1e4),
259
344
  guest: guestSchema.optional(),
260
- versionId: z5.string(),
345
+ versionId: z6.string(),
261
346
  via: viaLabelSchema.optional()
262
347
  });
263
- var createCommentMessageSchema = z5.object({
264
- body: z5.string().min(1).max(1e4),
348
+ var createCommentMessageSchema = z6.object({
349
+ body: z6.string().min(1).max(1e4),
265
350
  guest: guestSchema.optional(),
266
351
  via: viaLabelSchema.optional()
267
352
  });
268
- var editCommentMessageSchema = z5.object({
269
- body: z5.string().min(1).max(1e4),
353
+ var editCommentMessageSchema = z6.object({
354
+ body: z6.string().min(1).max(1e4),
270
355
  guest: guestSchema.optional()
271
356
  });
272
- var threadActionSchema = z5.object({
357
+ var threadActionSchema = z6.object({
273
358
  guest: guestSchema.optional()
274
359
  });
275
- var SINGLE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
276
- var REDUNDANT_VS16 = new RegExp("(\\p{Emoji_Presentation})\\uFE0F", "gu");
277
- function canonicalizeEmoji(s) {
278
- return s.replace(REDUNDANT_VS16, "$1");
279
- }
280
- var reactionEmojiSchema = z5.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}");
281
- var toggleReactionSchema = z5.object({
360
+ var reactionEmojiSchema = z6.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}");
361
+ var toggleReactionSchema = z6.object({
282
362
  emoji: reactionEmojiSchema,
283
363
  guest: guestSchema.optional()
284
364
  });
285
- var commentAuthorSchema = z5.object({
286
- avatarUrl: z5.string().nullable(),
287
- displayName: z5.string(),
288
- guestId: z5.string().nullable(),
289
- isViewer: z5.boolean(),
290
- kind: z5.enum(["member", "guest"]),
365
+ var commentAgentSchema = z6.object({
366
+ id: z6.string().nullable(),
367
+ label: z6.string()
368
+ });
369
+ var commentAuthorSchema = z6.object({
370
+ agent: commentAgentSchema.nullable().default(null),
371
+ avatarUrl: z6.string().nullable(),
372
+ displayName: z6.string(),
373
+ guestId: z6.string().nullable(),
374
+ isViewer: z6.boolean(),
375
+ kind: z6.enum(["member", "guest"]),
291
376
  // Optional "via" attribution (§6.4). Null for plain comments; a bounded
292
- // label the shell appends after the display name.
293
- via: z5.string().nullable()
377
+ // label the shell appends after the display name. Defaulted for the same
378
+ // reason as `agent`: a server predating §6.4 omits the key entirely.
379
+ via: z6.string().nullable().default(null)
294
380
  });
295
- var messageReactionSchema = z5.object({
296
- emoji: z5.string(),
297
- reactors: z5.array(commentAuthorSchema)
381
+ var messageReactionSchema = z6.object({
382
+ emoji: z6.string(),
383
+ reactors: z6.array(commentAuthorSchema)
298
384
  });
299
- var commentMessageSchema = z5.object({
385
+ var commentMessageSchema = z6.object({
300
386
  author: commentAuthorSchema,
301
387
  // Empty string for a deleted message — the body is stripped server-side
302
388
  // (§6.7.1 tombstone), never sent to the client and hidden there.
303
- body: z5.string(),
304
- createdAt: z5.string(),
389
+ body: z6.string(),
390
+ createdAt: z6.string(),
305
391
  // Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
306
392
  // the shell renders "Comment deleted" instead of the (absent) body.
307
- deleted: z5.boolean(),
393
+ deleted: z6.boolean(),
308
394
  // Last edit time (§6.7.1), or null if never edited — drives the "edited"
309
395
  // badge. No edit history is kept.
310
- editedAt: z5.string().nullable(),
311
- id: z5.string(),
396
+ editedAt: z6.string().nullable(),
397
+ id: z6.string(),
312
398
  // Defaulted, not required: a new CLI parsing an older server's
313
399
  // response must not fail on the missing key.
314
- reactions: z5.array(messageReactionSchema).default([])
400
+ reactions: z6.array(messageReactionSchema).default([])
315
401
  });
316
- var threadActorSchema = z5.object({
317
- displayName: z5.string(),
318
- kind: z5.enum(["member", "guest", "agent"])
402
+ var threadActorSchema = z6.object({
403
+ displayName: z6.string(),
404
+ kind: z6.enum(["member", "guest", "agent"])
319
405
  });
320
- var commentThreadSchema = z5.object({
406
+ var commentThreadSchema = z6.object({
321
407
  // §6.6.1 — set while open to signal "seen, working on it"; cleared on
322
408
  // reopen, kept (but superseded in display) once resolved. Defaulted so
323
409
  // clients parsing an older server's response don't fail.
324
- acknowledgedAt: z5.string().nullable().default(null),
410
+ acknowledgedAt: z6.string().nullable().default(null),
325
411
  acknowledgedBy: threadActorSchema.nullable().default(null),
326
412
  anchor: anchorSchema,
327
- artifactId: z5.string(),
328
- createdAt: z5.string(),
413
+ artifactId: z6.string(),
414
+ createdAt: z6.string(),
329
415
  createdBy: commentAuthorSchema,
330
- id: z5.string(),
331
- messages: z5.array(commentMessageSchema),
416
+ id: z6.string(),
417
+ messages: z6.array(commentMessageSchema),
332
418
  // Null on threads resolved before actor recording existed, and always
333
419
  // null while open.
334
420
  resolvedBy: threadActorSchema.nullable().default(null),
335
- status: z5.enum(["open", "resolved"]),
336
- versionId: z5.string()
421
+ status: z6.enum(["open", "resolved"]),
422
+ versionId: z6.string()
337
423
  });
338
- var commentThreadListSchema = z5.object({
339
- threads: z5.array(commentThreadSchema)
424
+ var commentThreadListSchema = z6.object({
425
+ threads: z6.array(commentThreadSchema)
340
426
  });
341
- var commentExportQuerySchema = z5.object({
342
- status: z5.enum(["open", "resolved", "all"]).default("open"),
343
- version: z5.enum(["current", "all"]).default("current")
427
+ var commentExportQuerySchema = z6.object({
428
+ status: z6.enum(["open", "resolved", "all"]).default("open"),
429
+ version: z6.enum(["current", "all"]).default("current")
344
430
  });
345
- var exportReactionSchema = z5.object({
346
- count: z5.number().int().positive(),
347
- emoji: z5.string()
431
+ var exportReactionSchema = z6.object({
432
+ count: z6.number().int().positive(),
433
+ emoji: z6.string()
348
434
  });
349
- var commentExportReplySchema = z5.object({
350
- author: z5.string(),
351
- authorKind: z5.enum(["member", "guest"]),
352
- body: z5.string(),
353
- createdAt: z5.string(),
435
+ var commentExportReplySchema = z6.object({
436
+ author: z6.string(),
437
+ authorKind: z6.enum(["member", "guest"]),
438
+ body: z6.string(),
439
+ createdAt: z6.string(),
354
440
  // Soft-delete tombstone (§6.7.1): body is "" when true.
355
- deleted: z5.boolean(),
356
- editedAt: z5.string().nullable(),
441
+ deleted: z6.boolean(),
442
+ editedAt: z6.string().nullable(),
357
443
  // The message id — what `edit`/`delete` target (§5.1).
358
- id: z5.string(),
359
- reactions: z5.array(exportReactionSchema).optional()
444
+ id: z6.string(),
445
+ reactions: z6.array(exportReactionSchema).optional()
360
446
  });
361
- var commentExportThreadSchema = z5.object({
447
+ var commentExportThreadSchema = z6.object({
362
448
  // §6.6.1 — lets a polling agent skip threads it already acknowledged
363
449
  // (acknowledged threads stay in status=open exports on purpose).
364
- acknowledgedAt: z5.string().nullable().default(null),
450
+ acknowledgedAt: z6.string().nullable().default(null),
365
451
  anchor: anchorSchema,
366
- author: z5.string(),
367
- authorKind: z5.enum(["member", "guest"]),
368
- body: z5.string(),
369
- createdAt: z5.string(),
370
- deleted: z5.boolean(),
371
- editedAt: z5.string().nullable(),
372
- id: z5.string(),
452
+ author: z6.string(),
453
+ authorKind: z6.enum(["member", "guest"]),
454
+ body: z6.string(),
455
+ createdAt: z6.string(),
456
+ deleted: z6.boolean(),
457
+ editedAt: z6.string().nullable(),
458
+ id: z6.string(),
373
459
  // The opening message's id (distinct from the thread `id`) — `edit`/
374
460
  // `delete` operate on messages, so the CLI needs it to target the first
375
461
  // comment, not just the replies.
376
- messageId: z5.string(),
377
- reactions: z5.array(exportReactionSchema).optional(),
378
- replies: z5.array(commentExportReplySchema),
462
+ messageId: z6.string(),
463
+ reactions: z6.array(exportReactionSchema).optional(),
464
+ replies: z6.array(commentExportReplySchema),
379
465
  resolvedBy: threadActorSchema.nullable().default(null),
380
- status: z5.enum(["open", "resolved"]),
381
- versionNumber: z5.number().int().positive()
466
+ status: z6.enum(["open", "resolved"]),
467
+ versionNumber: z6.number().int().positive()
382
468
  });
383
- var commentExportSchema = z5.object({
384
- artifactId: z5.string(),
385
- comments: z5.array(commentExportThreadSchema),
469
+ var commentExportSchema = z6.object({
470
+ artifactId: z6.string(),
471
+ comments: z6.array(commentExportThreadSchema),
386
472
  // The artifact's current version (null before the first finalize).
387
- versionNumber: z5.number().int().positive().nullable()
473
+ versionNumber: z6.number().int().positive().nullable()
388
474
  });
389
- var commentReadResponseSchema = z5.object({
390
- lastSeenAt: z5.string()
475
+ var commentReadResponseSchema = z6.object({
476
+ lastSeenAt: z6.string()
391
477
  });
392
478
  function elementLabel(el) {
393
479
  if (el.id) {
@@ -398,499 +484,528 @@ function elementLabel(el) {
398
484
  }
399
485
 
400
486
  // ../shared/src/errors.ts
401
- import { z as z6 } from "zod";
402
- var apiErrorSchema = z6.object({
403
- code: z6.string(),
404
- docsUrl: z6.string().url(),
405
- message: z6.string(),
406
- retryAfter: z6.number().int().positive().optional()
487
+ import { z as z7 } from "zod";
488
+ var apiErrorSchema = z7.object({
489
+ code: z7.string(),
490
+ docsUrl: z7.string().url(),
491
+ message: z7.string(),
492
+ retryAfter: z7.number().int().positive().optional()
407
493
  });
408
494
 
409
495
  // ../shared/src/feedback.ts
410
- import { z as z7 } from "zod";
411
- var feedbackRequestSchema = z7.object({
412
- email: z7.string().trim().toLowerCase().email().optional().or(z7.literal("")),
413
- message: z7.string().trim().min(1).max(4e3)
496
+ import { z as z8 } from "zod";
497
+ var feedbackRequestSchema = z8.object({
498
+ email: z8.string().trim().toLowerCase().email().optional().or(z8.literal("")),
499
+ message: z8.string().trim().min(1).max(4e3)
414
500
  });
415
- var feedbackResponseSchema = z7.object({
416
- sent: z7.literal(true)
501
+ var feedbackResponseSchema = z8.object({
502
+ sent: z8.literal(true)
417
503
  });
418
504
 
419
505
  // ../shared/src/grants.ts
420
- import { z as z8 } from "zod";
421
- var grantRoleSchema = z8.enum(["viewer"]);
422
- var createGrantSchema = z8.object({
423
- email: z8.string().trim().toLowerCase().email().optional(),
506
+ import { z as z9 } from "zod";
507
+ var grantRoleSchema = z9.enum(["viewer"]);
508
+ var createGrantSchema = z9.object({
509
+ email: z9.string().trim().toLowerCase().email().optional(),
424
510
  role: grantRoleSchema.default("viewer"),
425
- userId: z8.string().min(1).optional()
511
+ userId: z9.string().min(1).optional()
426
512
  }).refine((body) => Boolean(body.email) !== Boolean(body.userId), {
427
513
  message: "provide exactly one of email or userId"
428
514
  });
429
- var grantSchema = z8.object({
430
- createdAt: z8.string(),
431
- email: z8.string().email().nullable(),
432
- expiresAt: z8.string().nullable(),
433
- id: z8.string(),
515
+ var grantSchema = z9.object({
516
+ createdAt: z9.string(),
517
+ email: z9.string().email().nullable(),
518
+ expiresAt: z9.string().nullable(),
519
+ id: z9.string(),
434
520
  // The subject's avatar once they have an account; null otherwise.
435
- image: z8.string().nullable(),
521
+ image: z9.string().nullable(),
436
522
  // Present on the invited account after redemption / when granting an
437
523
  // existing user; null for a still-pending email invite.
438
- name: z8.string().nullable(),
439
- redeemedAt: z8.string().nullable(),
524
+ name: z9.string().nullable(),
525
+ redeemedAt: z9.string().nullable(),
440
526
  role: grantRoleSchema,
441
527
  // Shown as "· pending" beside the role until the link is redeemed.
442
- status: z8.enum(["pending", "active"]),
528
+ status: z9.enum(["pending", "active"]),
443
529
  // Display fragment of the live invitation token, or null once redeemed.
444
- tokenPrefix: z8.string().nullable(),
445
- userId: z8.string().nullable()
530
+ tokenPrefix: z9.string().nullable(),
531
+ userId: z9.string().nullable()
446
532
  });
447
533
  var createdGrantSchema = grantSchema.extend({
448
- url: z8.string().url()
534
+ url: z9.string().url()
449
535
  });
450
- var grantListResponseSchema = z8.object({
451
- grants: z8.array(grantSchema),
536
+ var grantListResponseSchema = z9.object({
537
+ grants: z9.array(grantSchema),
452
538
  // The artifact's current general-access value, so the share surface can
453
539
  // render the "General access" line without a second read.
454
- visibility: z8.enum(["public", "unlisted", "workspace", "private"]),
540
+ visibility: z9.enum(["public", "unlisted", "workspace", "private"]),
455
541
  // The owning workspace's logo, so the share modal can draw the workspace
456
542
  // itself rather than a generic glyph. Null when none was uploaded.
457
- workspaceLogo: z8.string().nullable().optional(),
543
+ workspaceLogo: z9.string().nullable().optional(),
458
544
  // The owning workspace's display name, so "People with access" can name the
459
545
  // audience `workspace` visibility actually admits ("All Acme members") rather
460
546
  // than leaving an empty list to imply nobody. Null for a workspace-less
461
547
  // artifact; optional so an older server still parses.
462
- workspaceName: z8.string().nullable().optional()
548
+ workspaceName: z9.string().nullable().optional()
463
549
  });
464
- var revokeGrantResponseSchema = z8.object({
465
- revoked: z8.literal(true)
550
+ var revokeGrantResponseSchema = z9.object({
551
+ revoked: z9.literal(true)
466
552
  });
467
- var acceptGrantSchema = z8.object({
468
- token: z8.string().min(1)
553
+ var acceptGrantSchema = z9.object({
554
+ token: z9.string().min(1)
469
555
  });
470
- var acceptGrantResponseSchema = z8.object({
471
- artifactId: z8.string(),
556
+ var acceptGrantResponseSchema = z9.object({
557
+ artifactId: z9.string(),
472
558
  // The review shell URL to land on, carrying ?invited=1 for the banner.
473
- artifactUrl: z8.string().url(),
474
- slug: z8.string(),
475
- title: z8.string()
476
- });
477
- var workspaceGuestSchema = z8.object({
478
- artifacts: z8.array(
479
- z8.object({
480
- artifactId: z8.string(),
481
- grantId: z8.string(),
482
- slug: z8.string(),
483
- status: z8.enum(["pending", "active"]),
484
- title: z8.string()
559
+ artifactUrl: z9.string().url(),
560
+ slug: z9.string(),
561
+ title: z9.string()
562
+ });
563
+ var workspaceGuestSchema = z9.object({
564
+ artifacts: z9.array(
565
+ z9.object({
566
+ artifactId: z9.string(),
567
+ grantId: z9.string(),
568
+ slug: z9.string(),
569
+ status: z9.enum(["pending", "active"]),
570
+ title: z9.string()
485
571
  })
486
572
  ),
487
- email: z8.string().nullable(),
488
- image: z8.string().nullable(),
489
- name: z8.string().nullable(),
490
- userId: z8.string().nullable()
491
- });
492
- var workspaceGuestListResponseSchema = z8.object({
493
- guests: z8.array(workspaceGuestSchema)
494
- });
495
- var sharedWithMeArtifactSchema = z8.object({
496
- artifactId: z8.string(),
497
- slug: z8.string(),
498
- title: z8.string(),
499
- url: z8.string().url(),
573
+ email: z9.string().nullable(),
574
+ image: z9.string().nullable(),
575
+ name: z9.string().nullable(),
576
+ userId: z9.string().nullable()
577
+ });
578
+ var workspaceGuestListResponseSchema = z9.object({
579
+ guests: z9.array(workspaceGuestSchema)
580
+ });
581
+ var sharedWithMeArtifactSchema = z9.object({
582
+ artifactId: z9.string(),
583
+ slug: z9.string(),
584
+ title: z9.string(),
585
+ url: z9.string().url(),
500
586
  // Who shared it, for the "Shared with me" list.
501
- workspaceName: z8.string()
587
+ workspaceName: z9.string()
502
588
  });
503
- var sharedWithMeListResponseSchema = z8.object({
504
- artifacts: z8.array(sharedWithMeArtifactSchema)
589
+ var sharedWithMeListResponseSchema = z9.object({
590
+ artifacts: z9.array(sharedWithMeArtifactSchema)
505
591
  });
506
592
 
593
+ // ../shared/src/html-meta.ts
594
+ var HEAD_SCAN_CHARS = 64 * 1024;
595
+
507
596
  // ../shared/src/me.ts
508
- import { z as z10 } from "zod";
597
+ import { z as z11 } from "zod";
509
598
 
510
599
  // ../shared/src/workspaces.ts
511
- import { z as z9 } from "zod";
600
+ import { z as z10 } from "zod";
512
601
  var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
513
- var workspaceHandleUnavailableReasonSchema = z9.enum([
602
+ var workspaceHandleUnavailableReasonSchema = z10.enum([
514
603
  "held",
515
604
  "invalid",
516
605
  "reserved",
517
606
  "taken"
518
607
  ]);
519
- var workspaceHandleAvailabilitySchema = z9.object({
520
- available: z9.boolean(),
521
- handle: z9.string(),
608
+ var workspaceHandleAvailabilitySchema = z10.object({
609
+ available: z10.boolean(),
610
+ handle: z10.string(),
522
611
  // Human-readable sentence for the field hint; null when available.
523
- message: z9.string().nullable(),
612
+ message: z10.string().nullable(),
524
613
  reason: workspaceHandleUnavailableReasonSchema.nullable()
525
614
  });
526
- var workspaceRoleSchema = z9.enum(["owner", "admin", "member"]);
527
- var workspaceInviteRoleSchema = z9.enum(["admin", "member"]);
528
- var workspaceHandleSchema = z9.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
615
+ var workspaceRoleSchema = z10.enum(["owner", "admin", "member"]);
616
+ var workspaceInviteRoleSchema = z10.enum(["admin", "member"]);
617
+ var workspaceHandleSchema = z10.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
529
618
  message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
530
619
  });
531
- var createWorkspaceSchema = z9.object({
620
+ var createWorkspaceSchema = z10.object({
532
621
  handle: workspaceHandleSchema.optional(),
533
- name: z9.string().trim().min(1).max(100)
622
+ name: z10.string().trim().min(1).max(100)
534
623
  });
535
- var updateWorkspaceSchema = z9.object({
624
+ var updateWorkspaceSchema = z10.object({
536
625
  handle: workspaceHandleSchema.optional(),
537
- name: z9.string().trim().min(1).max(100).optional()
626
+ name: z10.string().trim().min(1).max(100).optional()
538
627
  }).refine((body) => body.handle !== void 0 || body.name !== void 0, {
539
628
  message: "provide at least one of name or handle"
540
629
  });
541
- var workspaceSchema = z9.object({
542
- createdAt: z9.string(),
543
- handle: z9.string(),
630
+ var workspaceSchema = z10.object({
631
+ createdAt: z10.string(),
632
+ handle: z10.string(),
544
633
  // ISO timestamp when `{handle}.artifact.cafe` was provisioned, or null.
545
634
  // Set by the one-time, self-serve enable flow (§4.4); never auto at
546
635
  // create, and always null for personal workspaces.
547
- hostnameProvisionedAt: z9.string().nullable(),
548
- id: z9.string(),
636
+ hostnameProvisionedAt: z10.string().nullable(),
637
+ id: z10.string(),
549
638
  // Auto-provisioned solo workspace, one per user: non-deletable, takes
550
639
  // no members, renameable like any workspace.
551
- isPersonal: z9.boolean(),
640
+ isPersonal: z10.boolean(),
552
641
  // Uploaded logo URL, or null for the initials fallback. Set through the
553
642
  // dedicated /workspaces/:id/avatar route, never the generic update.
554
- logo: z9.string().nullable(),
555
- name: z9.string(),
643
+ logo: z10.string().nullable(),
644
+ name: z10.string(),
556
645
  role: workspaceRoleSchema.nullable(),
557
- updatedAt: z9.string()
646
+ updatedAt: z10.string()
558
647
  });
559
- var workspaceListResponseSchema = z9.object({
560
- workspaces: z9.array(workspaceSchema)
648
+ var workspaceListResponseSchema = z10.object({
649
+ workspaces: z10.array(workspaceSchema)
561
650
  });
562
- var deleteWorkspaceResponseSchema = z9.object({
563
- deleted: z9.literal(true)
651
+ var deleteWorkspaceResponseSchema = z10.object({
652
+ deleted: z10.literal(true)
564
653
  });
565
- var workspaceMemberSchema = z9.object({
566
- createdAt: z9.string(),
567
- email: z9.string().email(),
568
- image: z9.string().nullable(),
569
- name: z9.string(),
654
+ var workspaceMemberSchema = z10.object({
655
+ createdAt: z10.string(),
656
+ email: z10.string().email(),
657
+ image: z10.string().nullable(),
658
+ name: z10.string(),
570
659
  role: workspaceRoleSchema,
571
- userId: z9.string()
660
+ userId: z10.string()
572
661
  });
573
- var workspaceMemberListResponseSchema = z9.object({
574
- members: z9.array(workspaceMemberSchema)
662
+ var workspaceMemberListResponseSchema = z10.object({
663
+ members: z10.array(workspaceMemberSchema)
575
664
  });
576
- var updateWorkspaceMemberSchema = z9.object({
665
+ var updateWorkspaceMemberSchema = z10.object({
577
666
  role: workspaceRoleSchema
578
667
  });
579
- var removeWorkspaceMemberResponseSchema = z9.object({
580
- removed: z9.literal(true)
668
+ var removeWorkspaceMemberResponseSchema = z10.object({
669
+ removed: z10.literal(true)
581
670
  });
582
- var createWorkspaceInviteSchema = z9.object({
583
- email: z9.string().trim().toLowerCase().email().optional(),
584
- expiresAt: z9.string().datetime(),
585
- maxUses: z9.number().int().positive().optional(),
671
+ var createWorkspaceInviteSchema = z10.object({
672
+ email: z10.string().trim().toLowerCase().email().optional(),
673
+ expiresAt: z10.string().datetime(),
674
+ maxUses: z10.number().int().positive().optional(),
586
675
  role: workspaceInviteRoleSchema
587
676
  });
588
- var workspaceInviteSchema = z9.object({
589
- createdAt: z9.string(),
590
- email: z9.string().email().nullable(),
591
- expiresAt: z9.string(),
592
- id: z9.string(),
593
- maxUses: z9.number().int().positive().nullable(),
677
+ var workspaceInviteSchema = z10.object({
678
+ createdAt: z10.string(),
679
+ email: z10.string().email().nullable(),
680
+ expiresAt: z10.string(),
681
+ id: z10.string(),
682
+ maxUses: z10.number().int().positive().nullable(),
594
683
  role: workspaceInviteRoleSchema,
595
- tokenPrefix: z9.string(),
596
- useCount: z9.number().int().nonnegative()
684
+ tokenPrefix: z10.string(),
685
+ useCount: z10.number().int().nonnegative()
597
686
  });
598
687
  var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
599
- url: z9.string().url()
688
+ url: z10.string().url()
600
689
  });
601
- var workspaceInviteListResponseSchema = z9.object({
602
- invites: z9.array(workspaceInviteSchema)
690
+ var workspaceInviteListResponseSchema = z10.object({
691
+ invites: z10.array(workspaceInviteSchema)
603
692
  });
604
- var revokeWorkspaceInviteResponseSchema = z9.object({
605
- revoked: z9.literal(true)
693
+ var revokeWorkspaceInviteResponseSchema = z10.object({
694
+ revoked: z10.literal(true)
606
695
  });
607
- var acceptWorkspaceInviteSchema = z9.object({
608
- token: z9.string().min(1)
696
+ var acceptWorkspaceInviteSchema = z10.object({
697
+ token: z10.string().min(1)
609
698
  });
610
- var acceptWorkspaceInviteResponseSchema = z9.object({
699
+ var acceptWorkspaceInviteResponseSchema = z10.object({
611
700
  role: workspaceRoleSchema,
612
701
  workspace: workspaceSchema
613
702
  });
614
- var folderVisibilitySchema = z9.enum(["public", "workspace"]);
615
- var artifactVisibilitySchema = z9.enum([
703
+ var folderVisibilitySchema = z10.enum(["public", "workspace"]);
704
+ var artifactVisibilitySchema = z10.enum([
616
705
  "public",
617
706
  "unlisted",
618
707
  "workspace",
619
708
  "private"
620
709
  ]);
621
- var createFolderSchema = z9.object({
622
- name: z9.string().trim().min(1).max(100),
710
+ var createFolderSchema = z10.object({
711
+ // Optional custom icon; omitted (or null) leaves the default glyph.
712
+ emoji: iconEmojiSchema.optional(),
713
+ name: z10.string().trim().min(1).max(100),
623
714
  visibility: folderVisibilitySchema
624
715
  });
625
- var updateFolderSchema = z9.object({
716
+ var updateFolderSchema = z10.object({
626
717
  // With `visibility`: bulk-update the folder's artifacts to match
627
718
  // (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
628
- cascade: z9.boolean().optional(),
629
- name: z9.string().trim().min(1).max(100).optional(),
630
- sortOrder: z9.number().int().min(0).optional(),
719
+ cascade: z10.boolean().optional(),
720
+ // Explicit null clears the icon back to the default glyph, so this is
721
+ // a field where "sent as null" and "omitted" mean different things.
722
+ emoji: iconEmojiSchema.optional(),
723
+ name: z10.string().trim().min(1).max(100).optional(),
724
+ sortOrder: z10.number().int().min(0).optional(),
631
725
  visibility: folderVisibilitySchema.optional()
632
726
  }).refine(
633
- (body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0,
634
- { message: "provide at least one of name, visibility, sortOrder" }
727
+ (body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0 || body.emoji !== void 0,
728
+ { message: "provide at least one of name, visibility, sortOrder, emoji" }
635
729
  ).refine((body) => !body.cascade || body.visibility !== void 0, {
636
730
  message: "cascade requires visibility"
637
731
  });
638
- var folderSchema = z9.object({
639
- createdAt: z9.string(),
640
- id: z9.string(),
641
- name: z9.string(),
732
+ var folderSchema = z10.object({
733
+ createdAt: z10.string(),
734
+ // Custom icon, or null to render the default folder glyph.
735
+ emoji: z10.string().nullable(),
736
+ id: z10.string(),
737
+ name: z10.string(),
642
738
  // Permalink segment for the public folder page (/w/{handle}/f/{slug}).
643
739
  // Server-derived from the name; renames regenerate it.
644
- slug: z9.string(),
645
- sortOrder: z9.number().int(),
646
- updatedAt: z9.string(),
740
+ slug: z10.string(),
741
+ sortOrder: z10.number().int(),
742
+ updatedAt: z10.string(),
647
743
  visibility: folderVisibilitySchema,
648
- workspaceId: z9.string()
744
+ workspaceId: z10.string()
649
745
  });
650
- var folderListResponseSchema = z9.object({
651
- folders: z9.array(folderSchema)
746
+ var folderListResponseSchema = z10.object({
747
+ folders: z10.array(folderSchema)
652
748
  });
653
- var deleteFolderResponseSchema = z9.object({
654
- deleted: z9.literal(true)
749
+ var deleteFolderResponseSchema = z10.object({
750
+ deleted: z10.literal(true)
655
751
  });
656
- var workspaceArtifactSchema = z9.object({
657
- createdAt: z9.string(),
658
- currentVersionNumber: z9.number().int().nullable(),
659
- folderId: z9.string().nullable(),
660
- id: z9.string(),
661
- openCommentCount: z9.number().int().nonnegative(),
752
+ var workspaceArtifactSchema = z10.object({
753
+ createdAt: z10.string(),
754
+ currentVersionNumber: z10.number().int().nullable(),
755
+ // Custom icon, or null to render the default document glyph.
756
+ emoji: z10.string().nullable(),
757
+ folderId: z10.string().nullable(),
758
+ id: z10.string(),
759
+ openCommentCount: z10.number().int().nonnegative(),
662
760
  reviewMode: reviewModeSchema,
663
- slug: z9.string(),
664
- title: z9.string(),
665
- updatedAt: z9.string(),
666
- url: z9.string().url(),
667
- versionCount: z9.number().int().nonnegative(),
761
+ slug: z10.string(),
762
+ title: z10.string(),
763
+ updatedAt: z10.string(),
764
+ url: z10.string().url(),
765
+ versionCount: z10.number().int().nonnegative(),
668
766
  visibility: artifactVisibilitySchema
669
767
  });
670
- var workspaceArtifactListResponseSchema = z9.object({
671
- artifacts: z9.array(workspaceArtifactSchema)
768
+ var workspaceArtifactListResponseSchema = z10.object({
769
+ artifacts: z10.array(workspaceArtifactSchema)
672
770
  });
673
- var workspaceArtifactSearchResultSchema = z9.object({
674
- id: z9.string(),
675
- slug: z9.string(),
676
- title: z9.string()
771
+ var workspaceArtifactSearchResultSchema = z10.object({
772
+ id: z10.string(),
773
+ slug: z10.string(),
774
+ title: z10.string()
677
775
  });
678
- var workspaceArtifactSearchResponseSchema = z9.object({
679
- results: z9.array(workspaceArtifactSearchResultSchema)
776
+ var workspaceArtifactSearchResponseSchema = z10.object({
777
+ results: z10.array(workspaceArtifactSearchResultSchema)
680
778
  });
681
779
 
682
780
  // ../shared/src/me.ts
683
- var ownArtifactSchema = z10.object({
684
- createdAt: z10.string(),
685
- currentVersionNumber: z10.number().nullable(),
781
+ var ownArtifactSchema = z11.object({
782
+ createdAt: z11.string(),
783
+ currentVersionNumber: z11.number().nullable(),
784
+ // Custom icon, or null to render the default document glyph.
785
+ emoji: z11.string().nullable(),
686
786
  // Owned artifacts normally never expire; non-null only while an
687
787
  // expiry is still set (e.g. an unclaimed publish surfaced here later).
688
- expiresAt: z10.string().nullable(),
788
+ expiresAt: z11.string().nullable(),
689
789
  // SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
690
790
  // both null for personal artifacts. `folderId` drives the dashboard's
691
- // move control and folder filter; `folderName` is the display label.
692
- folderId: z10.string().nullable(),
693
- folderName: z10.string().nullable(),
791
+ // move control and folder filter; `folderName` is the display label,
792
+ // `folderEmoji` its icon (null = the default folder glyph).
793
+ folderEmoji: z11.string().nullable(),
794
+ folderId: z11.string().nullable(),
795
+ folderName: z11.string().nullable(),
694
796
  // Whether a publish token is still active — revocable from the
695
797
  // dashboard (§6.18); the token itself is never returned.
696
- hasPublishToken: z10.boolean(),
697
- id: z10.string(),
698
- openThreads: z10.number(),
699
- protected: z10.boolean(),
798
+ hasPublishToken: z11.boolean(),
799
+ id: z11.string(),
800
+ openThreads: z11.number(),
801
+ protected: z11.boolean(),
700
802
  // Who published the current version (falling back to the claiming
701
803
  // owner) — the dashboard shows a name and avatar per artifact so a
702
804
  // shared workspace reads as people's work, not anonymous rows. Null
703
805
  // when the publish was token-only and the artifact is unclaimed.
704
- publishedBy: z10.object({ image: z10.string().nullable(), name: z10.string() }).nullable(),
806
+ publishedBy: z11.object({ image: z11.string().nullable(), name: z11.string() }).nullable(),
705
807
  // SPEC-2 §5.8 — the review dial, so the dashboard's settings dialog can
706
808
  // render current state without a second fetch.
707
809
  reviewMode: reviewModeSchema,
708
- slug: z10.string(),
709
- title: z10.string(),
710
- totalViews: z10.number(),
810
+ slug: z11.string(),
811
+ title: z11.string(),
812
+ totalViews: z11.number(),
711
813
  // SPEC §6.20 — messages by others newer than the viewer's last-seen
712
814
  // watermark. 0 for signed-out/API-key-less contexts.
713
- unreadComments: z10.number(),
714
- updatedAt: z10.string(),
715
- url: z10.string(),
716
- versionCount: z10.number(),
815
+ unreadComments: z11.number(),
816
+ updatedAt: z11.string(),
817
+ url: z11.string(),
818
+ versionCount: z11.number(),
717
819
  // SPEC-2 §5.1 — whether the caller clears the manage bar for this
718
820
  // artifact (workspace admin, or the person who first published it).
719
821
  // Only a manager's folder move inherits the folder's visibility, so the
720
822
  // dashboard warns exactly the people the change can actually affect, and
721
823
  // only a manager is offered the Share entry (§5.7).
722
- viewerCanManage: z10.boolean(),
824
+ viewerCanManage: z11.boolean(),
723
825
  // SPEC-2 §5.2 — the artifact's own visibility, the single enforcement
724
826
  // source. The dashboard needs it to tell whether a folder move would
725
- // widen exposure before it sends the PATCH (§3.4), and to render the lock
726
- // glyph on `private` rows and cards (§5.7).
827
+ // widen exposure before it sends the PATCH (§3.4), and to render the
828
+ // audience glyph — one per level — on rows and cards (§5.7).
727
829
  visibility: artifactVisibilitySchema
728
830
  });
729
- var ownArtifactsResponseSchema = z10.object({
730
- artifacts: z10.array(ownArtifactSchema)
831
+ var ownArtifactsResponseSchema = z11.object({
832
+ artifacts: z11.array(ownArtifactSchema)
731
833
  });
732
- var revokePublishTokenResponseSchema = z10.object({
733
- revoked: z10.boolean()
834
+ var revokePublishTokenResponseSchema = z11.object({
835
+ revoked: z11.boolean()
734
836
  });
735
837
 
736
838
  // ../shared/src/owner.ts
737
- import { z as z11 } from "zod";
839
+ import { z as z12 } from "zod";
738
840
  var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
739
- var claimResponseSchema = z11.object({
740
- artifactId: z11.string(),
741
- slug: z11.string(),
742
- title: z11.string(),
743
- url: z11.string().url()
744
- });
745
- var updateArtifactSchema = z11.object({
841
+ var claimResponseSchema = z12.object({
842
+ artifactId: z12.string(),
843
+ slug: z12.string(),
844
+ title: z12.string(),
845
+ url: z12.string().url()
846
+ });
847
+ var updateArtifactSchema = z12.object({
848
+ // Custom icon; explicit null clears it back to the default glyph.
849
+ // Part of what the artifact *is* (like the title), so it takes the
850
+ // same manage rights as title/slug — not the member-level folder move.
851
+ emoji: iconEmojiSchema.optional(),
746
852
  // SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
747
853
  // folder; `workspaceId: null` moves the artifact out of its workspace
748
854
  // (§5.5 ownership transfer).
749
- folderId: z11.string().min(1).nullable().optional(),
750
- slug: z11.string().min(3).max(63).regex(SLUG_PATTERN, {
855
+ folderId: z12.string().min(1).nullable().optional(),
856
+ slug: z12.string().min(3).max(63).regex(SLUG_PATTERN, {
751
857
  message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
752
858
  }).optional(),
753
- title: z11.string().min(1).max(200).optional(),
859
+ title: z12.string().min(1).max(200).optional(),
754
860
  visibility: artifactVisibilitySchema.optional(),
755
- workspaceId: z11.string().min(1).nullable().optional()
861
+ workspaceId: z12.string().min(1).nullable().optional()
756
862
  }).refine(
757
- (body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0,
863
+ (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,
758
864
  {
759
- message: "provide at least one of title, slug, visibility, folderId, workspaceId"
865
+ message: "provide at least one of title, slug, visibility, folderId, workspaceId, emoji"
760
866
  }
761
867
  );
762
- var deleteArtifactResponseSchema = z11.object({
763
- deleted: z11.literal(true)
868
+ var deleteArtifactResponseSchema = z12.object({
869
+ deleted: z12.literal(true)
764
870
  });
765
871
 
766
872
  // ../shared/src/presence.ts
767
- import { z as z12 } from "zod";
873
+ import { z as z13 } from "zod";
768
874
  var AGENT_STATES = ["listening", "editing", "publishing"];
769
- var agentStateSchema = z12.enum(AGENT_STATES);
875
+ var agentStateSchema = z13.enum(AGENT_STATES);
770
876
  var AGENT_PRESENCE = {
771
877
  editingTtlSeconds: 300,
772
878
  heartbeatSeconds: 15,
773
879
  shellPollSeconds: 15,
774
880
  ttlSeconds: 45
775
881
  };
776
- var agentHeartbeatBodySchema = z12.object({
882
+ var agentHeartbeatBodySchema = z13.object({
777
883
  state: agentStateSchema.optional()
778
884
  });
779
- var agentHeartbeatResponseSchema = z12.object({
780
- listening: z12.literal(true),
885
+ var agentHeartbeatResponseSchema = z13.object({
886
+ listening: z13.literal(true),
781
887
  state: agentStateSchema.optional(),
782
- ttlSeconds: z12.number().int().positive()
888
+ ttlSeconds: z13.number().int().positive()
783
889
  });
784
- var agentPresenceSchema = z12.object({
785
- listening: z12.boolean(),
890
+ var agentPresenceSchema = z13.object({
891
+ agent: z13.object({ id: z13.string().nullable(), label: z13.string() }).nullable().default(null),
892
+ listening: z13.boolean(),
786
893
  state: agentStateSchema.nullable().optional(),
787
- versionNumber: z12.number().int().positive().nullable()
894
+ versionNumber: z13.number().int().positive().nullable()
788
895
  });
789
896
 
790
897
  // ../shared/src/protection.ts
791
- import { z as z13 } from "zod";
898
+ import { z as z14 } from "zod";
792
899
  var PASSWORD_MIN = 6;
793
900
  var PASSWORD_MAX = 128;
794
- var gatePasswordSchema = z13.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
795
- var setProtectionSchema = z13.object({
901
+ var gatePasswordSchema = z14.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
902
+ var setProtectionSchema = z14.object({
796
903
  password: gatePasswordSchema.nullable()
797
904
  });
798
- var protectionResponseSchema = z13.object({
799
- protected: z13.boolean()
905
+ var protectionResponseSchema = z14.object({
906
+ protected: z14.boolean()
800
907
  });
801
- var unlockSchema = z13.object({
802
- password: z13.string().min(1).max(1024)
908
+ var unlockSchema = z14.object({
909
+ password: z14.string().min(1).max(1024)
803
910
  });
804
- var unlockResponseSchema = z13.object({
805
- unlocked: z13.literal(true)
911
+ var unlockResponseSchema = z14.object({
912
+ unlocked: z14.literal(true)
806
913
  });
807
914
 
808
915
  // ../shared/src/report.ts
809
- import { z as z14 } from "zod";
810
- var reportRequestSchema = z14.object({
811
- reason: z14.string().max(1e3).optional()
916
+ import { z as z15 } from "zod";
917
+ var reportRequestSchema = z15.object({
918
+ reason: z15.string().max(1e3).optional()
812
919
  });
813
- var reportResponseSchema = z14.object({
814
- reported: z14.literal(true)
920
+ var reportResponseSchema = z15.object({
921
+ reported: z15.literal(true)
815
922
  });
816
923
 
817
924
  // ../shared/src/reviewers.ts
818
- import { z as z15 } from "zod";
819
- var artifactReviewerSchema = z15.object({
820
- avatarUrl: z15.string().nullable(),
821
- displayName: z15.string(),
925
+ import { z as z16 } from "zod";
926
+ var artifactReviewerSchema = z16.object({
927
+ avatarUrl: z16.string().nullable(),
928
+ displayName: z16.string(),
822
929
  // Exactly one of userId/guestId is set, per `kind`.
823
- guestId: z15.string().nullable(),
930
+ guestId: z16.string().nullable(),
824
931
  // "account", not commentAuthorSchema's "member" — the whole point here is
825
932
  // that these people need not be workspace members. A guest can be named in
826
933
  // a comment but never notified: guest_identities carries a display name and
827
934
  // no email, so there is nowhere to send anything.
828
- kind: z15.enum(["account", "guest"]),
829
- userId: z15.string().nullable()
935
+ kind: z16.enum(["account", "guest"]),
936
+ userId: z16.string().nullable()
830
937
  });
831
- var artifactReviewerListResponseSchema = z15.object({
832
- reviewers: z15.array(artifactReviewerSchema)
938
+ var artifactReviewerListResponseSchema = z16.object({
939
+ reviewers: z16.array(artifactReviewerSchema)
833
940
  });
834
941
 
835
942
  // ../shared/src/stats.ts
836
- import { z as z16 } from "zod";
837
- var avgViewSecondsSchema = z16.number().int().nonnegative().nullable();
838
- var versionViewStatsSchema = z16.object({
943
+ import { z as z17 } from "zod";
944
+ var avgViewSecondsSchema = z17.number().int().nonnegative().nullable();
945
+ var versionViewStatsSchema = z17.object({
839
946
  avgViewSeconds: avgViewSecondsSchema,
840
947
  // Views from inside the review shell's iframe vs. direct visits to
841
948
  // the render URL. views = embeddedViews + directViews.
842
- directViews: z16.number().int().nonnegative(),
843
- embeddedViews: z16.number().int().nonnegative(),
949
+ directViews: z17.number().int().nonnegative(),
950
+ embeddedViews: z17.number().int().nonnegative(),
844
951
  // Distinct hashed viewer IPs across the version's lifetime.
845
- uniqueViewers: z16.number().int().nonnegative(),
846
- versionNumber: z16.number().int().positive(),
847
- views: z16.number().int().nonnegative()
952
+ uniqueViewers: z17.number().int().nonnegative(),
953
+ versionNumber: z17.number().int().positive(),
954
+ views: z17.number().int().nonnegative()
848
955
  });
849
- var dailyViewsSchema = z16.object({
956
+ var dailyViewsSchema = z17.object({
850
957
  avgViewSeconds: avgViewSecondsSchema,
851
958
  // UTC day, YYYY-MM-DD.
852
- day: z16.string(),
853
- uniqueViewers: z16.number().int().nonnegative(),
854
- views: z16.number().int().nonnegative()
959
+ day: z17.string(),
960
+ uniqueViewers: z17.number().int().nonnegative(),
961
+ views: z17.number().int().nonnegative()
855
962
  });
856
- var artifactStatsResponseSchema = z16.object({
963
+ var artifactStatsResponseSchema = z17.object({
857
964
  avgViewSeconds: avgViewSecondsSchema,
858
- daily: z16.array(dailyViewsSchema),
965
+ daily: z17.array(dailyViewsSchema),
859
966
  // Distinct hashed viewer IPs across all versions and days.
860
- totalUniqueViewers: z16.number().int().nonnegative(),
861
- totalViews: z16.number().int().nonnegative(),
862
- versions: z16.array(versionViewStatsSchema)
967
+ totalUniqueViewers: z17.number().int().nonnegative(),
968
+ totalViews: z17.number().int().nonnegative(),
969
+ versions: z17.array(versionViewStatsSchema)
863
970
  });
864
971
 
865
972
  // ../shared/src/versions.ts
866
- import { z as z17 } from "zod";
867
- var versionSourceSchema = z17.object({
868
- agentName: z17.string().nullable(),
869
- type: z17.string()
973
+ import { z as z18 } from "zod";
974
+ var versionSourceSchema = z18.object({
975
+ agentName: z18.string().nullable(),
976
+ type: z18.string()
870
977
  });
871
- var versionChangesSchema = z17.object({
872
- added: z17.array(z17.string()),
873
- changed: z17.array(z17.string()),
874
- removed: z17.array(z17.string())
978
+ var versionChangesSchema = z18.object({
979
+ added: z18.array(z18.string()),
980
+ changed: z18.array(z18.string()),
981
+ removed: z18.array(z18.string())
875
982
  });
876
- var versionSummarySchema = z17.object({
983
+ var versionSummarySchema = z18.object({
877
984
  // Absent for the first version — there is no previous version to diff against.
878
985
  changes: versionChangesSchema.nullable(),
879
- createdAt: z17.string(),
880
- entryUrl: z17.string().url(),
881
- fileCount: z17.number().int().nonnegative(),
882
- id: z17.string(),
883
- isCurrent: z17.boolean(),
986
+ createdAt: z18.string(),
987
+ entryUrl: z18.string().url(),
988
+ fileCount: z18.number().int().nonnegative(),
989
+ id: z18.string(),
990
+ isCurrent: z18.boolean(),
884
991
  // Author-supplied bullet-point changelog; null when the publish omitted it.
885
- notes: z17.string().nullable(),
992
+ notes: z18.string().nullable(),
886
993
  source: versionSourceSchema,
887
- totalSizeBytes: z17.number().int().nonnegative(),
888
- versionNumber: z17.number().int().positive()
994
+ totalSizeBytes: z18.number().int().nonnegative(),
995
+ versionNumber: z18.number().int().positive()
889
996
  });
890
- var versionListResponseSchema = z17.object({
891
- versions: z17.array(versionSummarySchema)
997
+ var versionListResponseSchema = z18.object({
998
+ versions: z18.array(versionSummarySchema)
892
999
  });
893
1000
 
1001
+ // src/agent-identity.ts
1002
+ function agentLabel(explicit) {
1003
+ if (explicit !== void 0) {
1004
+ return explicit.trim() || void 0;
1005
+ }
1006
+ return detectAgentFromEnv(process.env);
1007
+ }
1008
+
894
1009
  // src/api.ts
895
1010
  var ApiRequestError = class extends Error {
896
1011
  constructor(status2, code, message, retryAfter) {
@@ -1108,11 +1223,13 @@ function createApiClient(apiUrl2, publishToken) {
1108
1223
  // SPEC §6.5 — post a reply into a thread. Authorized by this client's
1109
1224
  // credential: the artifact's publish token (the reply renders as the
1110
1225
  // agent) or the owner's account key (renders as the owner). No guest
1111
- // payload — the credential is the identity.
1112
- async replyThread(threadId, body) {
1226
+ // payload — the credential is the identity. `via` names the tool: it
1227
+ // becomes the agent's byline on a token reply, a "via" label on an
1228
+ // account one.
1229
+ async replyThread(threadId, body, via) {
1113
1230
  return request(
1114
1231
  `${base}/api/v1/comment-threads/${threadId}/messages`,
1115
- { body: JSON.stringify({ body }), headers, method: "POST" },
1232
+ { body: JSON.stringify({ body, via }), headers, method: "POST" },
1116
1233
  (d) => commentThreadSchema.parse(d)
1117
1234
  );
1118
1235
  },
@@ -1189,17 +1306,17 @@ import {
1189
1306
  writeFileSync as writeFileSync2
1190
1307
  } from "fs";
1191
1308
  import path2 from "path";
1192
- import { z as z19 } from "zod";
1309
+ import { z as z20 } from "zod";
1193
1310
 
1194
1311
  // src/credentials.ts
1195
1312
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
1196
1313
  import { homedir } from "os";
1197
1314
  import path from "path";
1198
- import { z as z18 } from "zod";
1315
+ import { z as z19 } from "zod";
1199
1316
  var DEFAULT_API_URL = "https://artifact.cafe";
1200
- var credentialsSchema = z18.object({
1201
- keys: z18.record(z18.string(), z18.string()),
1202
- version: z18.literal(1)
1317
+ var credentialsSchema = z19.object({
1318
+ keys: z19.record(z19.string(), z19.string()),
1319
+ version: z19.literal(1)
1203
1320
  });
1204
1321
  function credentialsPath() {
1205
1322
  return path.join(homedir(), ".artifactcafe", "credentials");
@@ -1249,24 +1366,24 @@ function resolveApiKey(apiUrl2, flag) {
1249
1366
  }
1250
1367
 
1251
1368
  // src/config.ts
1252
- var configSchema = z19.object({
1253
- apiUrl: z19.string(),
1254
- artifactId: z19.string(),
1255
- folder: z19.string().optional(),
1256
- folderId: z19.string().optional(),
1369
+ var configSchema = z20.object({
1370
+ apiUrl: z20.string(),
1371
+ artifactId: z20.string(),
1372
+ folder: z20.string().optional(),
1373
+ folderId: z20.string().optional(),
1257
1374
  // Optional: a folder linked by `link` to an artifact you own carries no
1258
1375
  // token — owner actions authorize via your stored login instead. A first
1259
1376
  // publish still writes the anonymous-mode token here.
1260
- publishToken: z19.string().optional(),
1261
- slug: z19.string(),
1262
- url: z19.string(),
1263
- workspace: z19.string().optional(),
1264
- workspaceId: z19.string().optional(),
1265
- workspaceKind: z19.enum(["personal", "workspace"]).optional()
1266
- });
1267
- var projectConfigSchema = z19.object({
1268
- folder: z19.string().trim().min(1).optional(),
1269
- workspace: z19.string().trim().min(1)
1377
+ publishToken: z20.string().optional(),
1378
+ slug: z20.string(),
1379
+ url: z20.string(),
1380
+ workspace: z20.string().optional(),
1381
+ workspaceId: z20.string().optional(),
1382
+ workspaceKind: z20.enum(["personal", "workspace"]).optional()
1383
+ });
1384
+ var projectConfigSchema = z20.object({
1385
+ folder: z20.string().trim().min(1).optional(),
1386
+ workspace: z20.string().trim().min(1)
1270
1387
  }).strict();
1271
1388
  var CONFIG_DIR = ".artifactcafe";
1272
1389
  var CONFIG_FILE = "config.json";
@@ -1302,7 +1419,7 @@ function readProjectConfig(dir) {
1302
1419
  JSON.parse(readFileSync2(file, "utf8"))
1303
1420
  );
1304
1421
  } catch (error) {
1305
- const message = error instanceof z19.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1422
+ const message = error instanceof z20.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1306
1423
  throw new Error(`Invalid ${file}: ${message}.`);
1307
1424
  }
1308
1425
  }
@@ -1441,7 +1558,10 @@ async function comment(opts) {
1441
1558
  anchor,
1442
1559
  body,
1443
1560
  versionId: artifact.currentVersion.id,
1444
- via: opts.via
1561
+ // An account comment keeps its own name and takes the tool as a "via"
1562
+ // label (§6.4) — detected when neither flag is given, so an agent
1563
+ // commenting with your key still says which tool typed it.
1564
+ via: agentLabel(opts.as ?? opts.via)
1445
1565
  });
1446
1566
  } catch (err) {
1447
1567
  if (err instanceof ApiRequestError && err.status === 401) {
@@ -1873,6 +1993,9 @@ async function publish(opts) {
1873
1993
  }
1874
1994
  const sessionBody = {
1875
1995
  artifactId,
1996
+ // Overrides the entry HTML's own meta description on the share card.
1997
+ // Omitted entirely when absent, so the document keeps speaking for itself.
1998
+ ...opts.description?.trim() ? { description: opts.description.trim() } : {},
1876
1999
  entryPath,
1877
2000
  files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
1878
2001
  ...placement.folderId ? { folderId: placement.folderId } : {},
@@ -1880,7 +2003,10 @@ async function publish(opts) {
1880
2003
  // backslash-n; turn those back into real newlines so bullets survive.
1881
2004
  ...opts.notes?.trim() ? { notes: opts.notes.replace(/\\n/g, "\n").trim() } : {},
1882
2005
  source: {
1883
- agent: process.env.ARTIFACT_CAFE_AGENT,
2006
+ // ARTIFACT_CAFE_AGENT if it's set, else whichever agent's environment
2007
+ // we recognize — this is what names the agent on every reply to a
2008
+ // thread opened against this version (§6.5).
2009
+ agent: agentLabel(),
1884
2010
  runId: process.env.ARTIFACT_CAFE_RUN_ID,
1885
2011
  type: "cli"
1886
2012
  },
@@ -2994,7 +3120,7 @@ async function replyToThread(opts) {
2994
3120
  artifact: opts.artifact
2995
3121
  });
2996
3122
  const client = createApiClient(target.apiUrl, target.credential);
2997
- let thread = await client.replyThread(threadId, body);
3123
+ let thread = await client.replyThread(threadId, body, agentLabel(opts.as));
2998
3124
  if (opts.resolve) {
2999
3125
  thread = await client.resolveThread(threadId);
3000
3126
  }
@@ -3217,18 +3343,18 @@ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as rea
3217
3343
  import { createRequire } from "module";
3218
3344
  import { homedir as homedir2 } from "os";
3219
3345
  import path18 from "path";
3220
- import { z as z20 } from "zod";
3346
+ import { z as z21 } from "zod";
3221
3347
  var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
3222
3348
  var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
3223
3349
  var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
3224
3350
  var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
3225
3351
  var FETCH_TIMEOUT_MS = 2e3;
3226
3352
  var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
3227
- var cacheSchema = z20.object({
3228
- checkedAt: z20.number(),
3229
- cliLatest: z20.string().nullable(),
3230
- skillLatest: z20.string().nullable(),
3231
- version: z20.literal(1)
3353
+ var cacheSchema = z21.object({
3354
+ checkedAt: z21.number(),
3355
+ cliLatest: z21.string().nullable(),
3356
+ skillLatest: z21.string().nullable(),
3357
+ version: z21.literal(1)
3232
3358
  });
3233
3359
  function cliVersion() {
3234
3360
  const pkg = createRequire(import.meta.url)("../package.json");
@@ -3283,7 +3409,7 @@ async function fetchText(url) {
3283
3409
  }
3284
3410
  async function fetchLatestVersions(previous) {
3285
3411
  const [cli, skill] = await Promise.all([
3286
- fetchJson(REGISTRY_LATEST_URL).then((body) => z20.object({ version: z20.string() }).parse(body).version).catch(() => null),
3412
+ fetchJson(REGISTRY_LATEST_URL).then((body) => z21.object({ version: z21.string() }).parse(body).version).catch(() => null),
3287
3413
  fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
3288
3414
  ]);
3289
3415
  return {
@@ -3396,17 +3522,18 @@ function printWorkspace(workspace) {
3396
3522
  }
3397
3523
 
3398
3524
  // src/index.ts
3399
- function readNotesArg(argv) {
3525
+ function readStringArg(argv, flag) {
3526
+ const prefix = `${flag}=`;
3400
3527
  for (let i = 0; i < argv.length; i++) {
3401
3528
  const token = argv[i];
3402
3529
  if (token === void 0) {
3403
3530
  continue;
3404
3531
  }
3405
- if (token === "--notes") {
3532
+ if (token === flag) {
3406
3533
  return argv[i + 1];
3407
3534
  }
3408
- if (token.startsWith("--notes=")) {
3409
- return token.slice("--notes=".length);
3535
+ if (token.startsWith(prefix)) {
3536
+ return token.slice(prefix.length);
3410
3537
  }
3411
3538
  }
3412
3539
  }
@@ -3420,6 +3547,10 @@ var publish2 = defineCommand({
3420
3547
  description: "Publish to an existing artifact ID",
3421
3548
  type: "string"
3422
3549
  },
3550
+ description: {
3551
+ description: "One-line share-card summary (default: the entry HTML's own meta description)",
3552
+ type: "string"
3553
+ },
3423
3554
  dir: {
3424
3555
  default: ".",
3425
3556
  description: "Directory to publish",
@@ -3456,11 +3587,12 @@ var publish2 = defineCommand({
3456
3587
  await publish({
3457
3588
  apiKey: args["api-key"],
3458
3589
  artifact: args.artifact,
3590
+ description: readStringArg(process.argv.slice(2), "--description"),
3459
3591
  dir: args.dir,
3460
3592
  entry: args.entry,
3461
3593
  folder: args.folder,
3462
3594
  json: Boolean(args.json),
3463
- notes: readNotesArg(process.argv.slice(2)),
3595
+ notes: readStringArg(process.argv.slice(2), "--notes"),
3464
3596
  open: !args["no-open"],
3465
3597
  password: args.password,
3466
3598
  review: args.review,
@@ -4017,6 +4149,10 @@ var reply = defineCommand({
4017
4149
  description: "Act on an artifact ID instead of the linked folder",
4018
4150
  type: "string"
4019
4151
  },
4152
+ as: {
4153
+ description: 'Who is replying, e.g. "Claude Code" (default: detected; "" to opt out)',
4154
+ type: "string"
4155
+ },
4020
4156
  body: {
4021
4157
  description: "Reply text (omit to read from stdin)",
4022
4158
  type: "string"
@@ -4046,6 +4182,7 @@ var reply = defineCommand({
4046
4182
  await replyToThread({
4047
4183
  apiKey: args["api-key"],
4048
4184
  artifact: args.artifact,
4185
+ as: args.as,
4049
4186
  body: args.body,
4050
4187
  dir: args.dir,
4051
4188
  json: Boolean(args.json),
@@ -4068,6 +4205,10 @@ var comment2 = defineCommand({
4068
4205
  description: "Act on an artifact ID instead of the linked folder",
4069
4206
  type: "string"
4070
4207
  },
4208
+ as: {
4209
+ description: 'Tool posting on your behalf, e.g. "Claude Code" (default: detected)',
4210
+ type: "string"
4211
+ },
4071
4212
  body: {
4072
4213
  description: "Comment text (omit to read from stdin)",
4073
4214
  type: "string"
@@ -4095,7 +4236,7 @@ var comment2 = defineCommand({
4095
4236
  type: "string"
4096
4237
  },
4097
4238
  via: {
4098
- description: 'Attribution label, e.g. "Claude Code"',
4239
+ description: "Older name for --as",
4099
4240
  type: "string"
4100
4241
  }
4101
4242
  },
@@ -4108,6 +4249,7 @@ var comment2 = defineCommand({
4108
4249
  await comment({
4109
4250
  apiKey: args["api-key"],
4110
4251
  artifact: args.artifact,
4252
+ as: args.as,
4111
4253
  body: args.body,
4112
4254
  dir: args.dir,
4113
4255
  json: Boolean(args.json),