artifact-cafe 0.0.13 → 0.0.15

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 +869 -608
  2. package/package.json +12 -1
package/dist/index.js CHANGED
@@ -83,35 +83,125 @@ var artifactListResponseSchema = z.object({
83
83
  artifacts: z.array(artifactListItemSchema)
84
84
  });
85
85
 
86
- // ../shared/src/avatars.ts
86
+ // ../shared/src/agent-tokens.ts
87
87
  import { z as z2 } from "zod";
88
+ var createAgentTokenSchema = z2.object({
89
+ externalId: z2.string().min(1).max(1024),
90
+ name: z2.string().trim().min(1).max(100).optional(),
91
+ ttlSeconds: z2.number().int().positive().max(3600)
92
+ });
93
+ var agentTokenResponseSchema = z2.object({
94
+ expiresAt: z2.string().datetime(),
95
+ token: z2.string(),
96
+ workspaceId: z2.string()
97
+ });
98
+
99
+ // ../shared/src/agents.ts
100
+ var AGENT_BRANDS = [
101
+ {
102
+ id: "claude-code",
103
+ label: "Claude Code",
104
+ match: ["claude"],
105
+ short: "Claude"
106
+ },
107
+ {
108
+ id: "codex",
109
+ label: "OpenAI Codex",
110
+ match: ["codex"],
111
+ short: "Codex"
112
+ },
113
+ { id: "cursor", label: "Cursor", match: ["cursor"], short: "Cursor" },
114
+ {
115
+ id: "copilot",
116
+ label: "GitHub Copilot",
117
+ match: ["copilot"],
118
+ short: "Copilot"
119
+ },
120
+ {
121
+ id: "gemini-cli",
122
+ label: "Gemini CLI",
123
+ match: ["gemini"],
124
+ short: "Gemini"
125
+ },
126
+ { id: "opencode", label: "opencode", match: ["opencode"], short: "opencode" },
127
+ {
128
+ id: "openclaw",
129
+ label: "OpenClaw",
130
+ match: ["openclaw"],
131
+ short: "OpenClaw"
132
+ },
133
+ { id: "hermes", label: "Hermes", match: ["hermes"], short: "Hermes" }
134
+ ];
135
+ function normalizeAgent(raw) {
136
+ const value = raw?.trim().toLowerCase();
137
+ if (!value) {
138
+ return null;
139
+ }
140
+ return AGENT_BRANDS.find((brand) => brand.id === value) ?? AGENT_BRANDS.find((brand) => brand.match.some((m) => value.includes(m))) ?? null;
141
+ }
142
+ var ENV_FINGERPRINTS = [
143
+ { id: "claude-code", prefixes: ["CLAUDECODE", "CLAUDE_CODE_"] },
144
+ { id: "codex", prefixes: ["CODEX_"] },
145
+ { id: "cursor", prefixes: ["CURSOR_"] },
146
+ { id: "copilot", prefixes: ["COPILOT_", "GITHUB_COPILOT_"] },
147
+ { id: "gemini-cli", prefixes: ["GEMINI_CLI", "GEMINI_SANDBOX"] },
148
+ { id: "opencode", prefixes: ["OPENCODE"] }
149
+ ];
150
+ function detectAgentFromEnv(env) {
151
+ const explicit = env.ARTIFACT_CAFE_AGENT?.trim();
152
+ if (explicit) {
153
+ return explicit;
154
+ }
155
+ const keys = Object.keys(env).filter((key) => env[key]);
156
+ for (const { id, prefixes } of ENV_FINGERPRINTS) {
157
+ const hit = keys.some(
158
+ (key) => prefixes.some((prefix) => key.startsWith(prefix))
159
+ );
160
+ if (hit) {
161
+ return AGENT_BRANDS.find((brand) => brand.id === id)?.label;
162
+ }
163
+ }
164
+ return normalizeAgent(env.AI_AGENT)?.label;
165
+ }
166
+
167
+ // ../shared/src/avatars.ts
168
+ import { z as z3 } from "zod";
88
169
  var AVATAR_CONTENT_TYPES = [
89
170
  "image/png",
90
171
  "image/jpeg",
91
172
  "image/webp"
92
173
  ];
93
174
  var AVATAR_MAX_BYTES = 2 * 1024 * 1024;
94
- var avatarContentTypeSchema = z2.enum(AVATAR_CONTENT_TYPES);
95
- var userAvatarResponseSchema = z2.object({
96
- image: z2.string().nullable()
175
+ var avatarContentTypeSchema = z3.enum(AVATAR_CONTENT_TYPES);
176
+ var userAvatarResponseSchema = z3.object({
177
+ image: z3.string().nullable()
97
178
  });
98
- var workspaceAvatarResponseSchema = z2.object({
99
- logo: z2.string().nullable()
179
+ var workspaceAvatarResponseSchema = z3.object({
180
+ logo: z3.string().nullable()
100
181
  });
101
182
 
102
183
  // ../shared/src/comments.ts
103
- import { z as z5 } from "zod";
184
+ import { z as z7 } from "zod";
104
185
 
105
- // ../shared/src/publish.ts
186
+ // ../shared/src/emoji.ts
106
187
  import { z as z4 } from "zod";
188
+ var SINGLE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
189
+ var REDUNDANT_VS16 = new RegExp("(\\p{Emoji_Presentation})\\uFE0F", "gu");
190
+ function canonicalizeEmoji(s) {
191
+ return s.replace(REDUNDANT_VS16, "$1");
192
+ }
193
+ 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
+
195
+ // ../shared/src/publish.ts
196
+ import { z as z6 } from "zod";
107
197
 
108
198
  // ../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({
199
+ import { z as z5 } from "zod";
200
+ var reviewModeSchema = z5.enum(["open", "readonly", "off"]);
201
+ var setReviewModeSchema = z5.object({
112
202
  mode: reviewModeSchema
113
203
  });
114
- var reviewModeResponseSchema = z3.object({
204
+ var reviewModeResponseSchema = z5.object({
115
205
  mode: reviewModeSchema
116
206
  });
117
207
 
@@ -128,266 +218,276 @@ function isSafeArtifactPath(p) {
128
218
  }
129
219
  return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
130
220
  }
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, {
221
+ var CARD_DESCRIPTION_MAX = 300;
222
+ 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, {
135
227
  message: "path must be a safe relative path"
136
228
  }),
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),
229
+ size: z6.number().int().positive()
230
+ });
231
+ var publishSourceSchema = z6.object({
232
+ agent: z6.string().optional(),
233
+ runId: z6.string().optional(),
234
+ type: z6.literal("cli")
235
+ });
236
+ var createPublishSessionSchema = z6.object({
237
+ artifactId: z6.string().nullable(),
238
+ // One-line share-card summary (`--description`). Overrides whatever the
239
+ // entry HTML declares; leave it off and the API reads the document's own
240
+ // `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),
148
244
  // SPEC-2 §6.4 — workspace placement is available only to authenticated
149
245
  // members; the API enforces membership and folder ownership. folderId
150
246
  // without workspaceId resolves against the account's personal workspace.
151
- folderId: z4.string().min(1).optional(),
247
+ folderId: z6.string().min(1).optional(),
152
248
  // Bullet-point changelog for this version, authored by the publisher.
153
249
  // Optional on every publish (incl. v1); stored immutably on the version.
154
- notes: z4.string().max(4e3).optional(),
250
+ notes: z6.string().max(4e3).optional(),
155
251
  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()
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()
167
263
  })
168
264
  ),
169
- publishSessionId: z4.string(),
170
- skippedFiles: z4.array(z4.string()),
171
- versionId: z4.string()
265
+ publishSessionId: z6.string(),
266
+ skippedFiles: z6.array(z6.string()),
267
+ versionId: z6.string()
172
268
  });
173
- var finalizeResponseSchema = z4.object({
174
- artifactId: z4.string(),
269
+ var finalizeResponseSchema = z6.object({
270
+ artifactId: z6.string(),
175
271
  // API-key publishes create the artifact already claimed (SPEC §5.4) —
176
272
  // the CLI uses this to tell "claim URL pending" from "already yours".
177
- claimed: z4.boolean(),
273
+ claimed: z6.boolean(),
178
274
  // 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(),
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(),
184
280
  // SPEC-2 §5.2 — who can open the review URL. A publish into a folder
185
281
  // inherits that folder's visibility, so this is the only place the CLI
186
282
  // learns that a link it just printed is members-only. Optional so a new
187
283
  // CLI against an older API degrades to "unknown" instead of failing the
188
284
  // 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()
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()
197
294
  }).nullable(),
295
+ // Custom icon, or null to render the default document glyph.
296
+ emoji: z6.string().nullable(),
198
297
  // SPEC-2 §6.4 — placement + visibility surface on the artifact.
199
- folderId: z4.string().nullable(),
200
- id: z4.string(),
298
+ folderId: z6.string().nullable(),
299
+ id: z6.string(),
201
300
  // SPEC-2 §5.8 — what a viewer may do once in (open / readonly / off).
202
301
  reviewMode: reviewModeSchema,
203
- slug: z4.string(),
204
- title: z4.string(),
205
- visibility: z4.enum(["public", "unlisted", "workspace", "private"]),
206
- workspaceId: z4.string().nullable()
302
+ slug: z6.string(),
303
+ title: z6.string(),
304
+ visibility: z6.enum(["public", "unlisted", "workspace", "private"]),
305
+ workspaceId: z6.string().nullable()
207
306
  });
208
307
 
209
308
  // ../shared/src/comments.ts
210
- var anchorPageSchema = z5.string().max(1024).refine(isSafeArtifactPath, {
309
+ var anchorPageSchema = z7.string().max(1024).refine(isSafeArtifactPath, {
211
310
  message: "page must be a safe relative path"
212
311
  }).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()
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()
218
317
  });
219
- var pointAnchorSchema = z5.object({
318
+ var pointAnchorSchema = z7.object({
220
319
  element: elementSnapshotSchema.optional(),
221
320
  // elementPath is the primary anchor; x/y are offsets within the
222
321
  // element's box, normalized 0..1 (SPEC.md §8.1).
223
- elementPath: z5.string().max(2048),
322
+ elementPath: z7.string().max(2048),
224
323
  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),
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),
234
333
  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")
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")
240
339
  });
241
- var pageAnchorSchema = z5.object({
340
+ var pageAnchorSchema = z7.object({
242
341
  page: anchorPageSchema,
243
- type: z5.literal("page")
342
+ type: z7.literal("page")
244
343
  });
245
- var anchorSchema = z5.discriminatedUnion("type", [
344
+ var anchorSchema = z7.discriminatedUnion("type", [
246
345
  pointAnchorSchema,
247
346
  textAnchorSchema,
248
347
  pageAnchorSchema
249
348
  ]);
250
- var displayNameSchema = z5.string().trim().min(1).max(64);
251
- var guestSchema = z5.object({
349
+ var displayNameSchema = z7.string().trim().min(1).max(64);
350
+ var guestSchema = z7.object({
252
351
  displayName: displayNameSchema,
253
- guestId: z5.string().min(16)
352
+ guestId: z7.string().min(16)
254
353
  });
255
- var viaLabelSchema = z5.string().trim().min(1).max(64);
256
- var createCommentThreadSchema = z5.object({
354
+ var viaLabelSchema = z7.string().trim().min(1).max(64);
355
+ var createCommentThreadSchema = z7.object({
257
356
  anchor: anchorSchema,
258
- body: z5.string().min(1).max(1e4),
357
+ body: z7.string().min(1).max(1e4),
259
358
  guest: guestSchema.optional(),
260
- versionId: z5.string(),
359
+ versionId: z7.string(),
261
360
  via: viaLabelSchema.optional()
262
361
  });
263
- var createCommentMessageSchema = z5.object({
264
- body: z5.string().min(1).max(1e4),
362
+ var createCommentMessageSchema = z7.object({
363
+ body: z7.string().min(1).max(1e4),
265
364
  guest: guestSchema.optional(),
266
365
  via: viaLabelSchema.optional()
267
366
  });
268
- var editCommentMessageSchema = z5.object({
269
- body: z5.string().min(1).max(1e4),
367
+ var editCommentMessageSchema = z7.object({
368
+ body: z7.string().min(1).max(1e4),
270
369
  guest: guestSchema.optional()
271
370
  });
272
- var threadActionSchema = z5.object({
371
+ var threadActionSchema = z7.object({
273
372
  guest: guestSchema.optional()
274
373
  });
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({
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({
282
376
  emoji: reactionEmojiSchema,
283
377
  guest: guestSchema.optional()
284
378
  });
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"]),
379
+ var commentAgentSchema = z7.object({
380
+ id: z7.string().nullable(),
381
+ label: z7.string()
382
+ });
383
+ var commentAuthorSchema = z7.object({
384
+ 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"]),
291
390
  // 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()
391
+ // label the shell appends after the display name. Defaulted for the same
392
+ // reason as `agent`: a server predating §6.4 omits the key entirely.
393
+ via: z7.string().nullable().default(null)
294
394
  });
295
- var messageReactionSchema = z5.object({
296
- emoji: z5.string(),
297
- reactors: z5.array(commentAuthorSchema)
395
+ var messageReactionSchema = z7.object({
396
+ emoji: z7.string(),
397
+ reactors: z7.array(commentAuthorSchema)
298
398
  });
299
- var commentMessageSchema = z5.object({
399
+ var commentMessageSchema = z7.object({
300
400
  author: commentAuthorSchema,
301
401
  // Empty string for a deleted message — the body is stripped server-side
302
402
  // (§6.7.1 tombstone), never sent to the client and hidden there.
303
- body: z5.string(),
304
- createdAt: z5.string(),
403
+ body: z7.string(),
404
+ createdAt: z7.string(),
305
405
  // Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
306
406
  // the shell renders "Comment deleted" instead of the (absent) body.
307
- deleted: z5.boolean(),
407
+ deleted: z7.boolean(),
308
408
  // Last edit time (§6.7.1), or null if never edited — drives the "edited"
309
409
  // badge. No edit history is kept.
310
- editedAt: z5.string().nullable(),
311
- id: z5.string(),
410
+ editedAt: z7.string().nullable(),
411
+ id: z7.string(),
312
412
  // Defaulted, not required: a new CLI parsing an older server's
313
413
  // response must not fail on the missing key.
314
- reactions: z5.array(messageReactionSchema).default([])
414
+ reactions: z7.array(messageReactionSchema).default([])
315
415
  });
316
- var threadActorSchema = z5.object({
317
- displayName: z5.string(),
318
- kind: z5.enum(["member", "guest", "agent"])
416
+ var threadActorSchema = z7.object({
417
+ displayName: z7.string(),
418
+ kind: z7.enum(["member", "guest", "agent"])
319
419
  });
320
- var commentThreadSchema = z5.object({
420
+ var commentThreadSchema = z7.object({
321
421
  // §6.6.1 — set while open to signal "seen, working on it"; cleared on
322
422
  // reopen, kept (but superseded in display) once resolved. Defaulted so
323
423
  // clients parsing an older server's response don't fail.
324
- acknowledgedAt: z5.string().nullable().default(null),
424
+ acknowledgedAt: z7.string().nullable().default(null),
325
425
  acknowledgedBy: threadActorSchema.nullable().default(null),
326
426
  anchor: anchorSchema,
327
- artifactId: z5.string(),
328
- createdAt: z5.string(),
427
+ artifactId: z7.string(),
428
+ createdAt: z7.string(),
329
429
  createdBy: commentAuthorSchema,
330
- id: z5.string(),
331
- messages: z5.array(commentMessageSchema),
430
+ id: z7.string(),
431
+ messages: z7.array(commentMessageSchema),
332
432
  // Null on threads resolved before actor recording existed, and always
333
433
  // null while open.
334
434
  resolvedBy: threadActorSchema.nullable().default(null),
335
- status: z5.enum(["open", "resolved"]),
336
- versionId: z5.string()
435
+ status: z7.enum(["open", "resolved"]),
436
+ versionId: z7.string()
337
437
  });
338
- var commentThreadListSchema = z5.object({
339
- threads: z5.array(commentThreadSchema)
438
+ var commentThreadListSchema = z7.object({
439
+ threads: z7.array(commentThreadSchema)
340
440
  });
341
- var commentExportQuerySchema = z5.object({
342
- status: z5.enum(["open", "resolved", "all"]).default("open"),
343
- version: z5.enum(["current", "all"]).default("current")
441
+ var commentExportQuerySchema = z7.object({
442
+ status: z7.enum(["open", "resolved", "all"]).default("open"),
443
+ version: z7.enum(["current", "all"]).default("current")
344
444
  });
345
- var exportReactionSchema = z5.object({
346
- count: z5.number().int().positive(),
347
- emoji: z5.string()
445
+ var exportReactionSchema = z7.object({
446
+ count: z7.number().int().positive(),
447
+ emoji: z7.string()
348
448
  });
349
- var commentExportReplySchema = z5.object({
350
- author: z5.string(),
351
- authorKind: z5.enum(["member", "guest"]),
352
- body: z5.string(),
353
- createdAt: z5.string(),
449
+ var commentExportReplySchema = z7.object({
450
+ author: z7.string(),
451
+ authorKind: z7.enum(["member", "guest"]),
452
+ body: z7.string(),
453
+ createdAt: z7.string(),
354
454
  // Soft-delete tombstone (§6.7.1): body is "" when true.
355
- deleted: z5.boolean(),
356
- editedAt: z5.string().nullable(),
455
+ deleted: z7.boolean(),
456
+ editedAt: z7.string().nullable(),
357
457
  // The message id — what `edit`/`delete` target (§5.1).
358
- id: z5.string(),
359
- reactions: z5.array(exportReactionSchema).optional()
458
+ id: z7.string(),
459
+ reactions: z7.array(exportReactionSchema).optional()
360
460
  });
361
- var commentExportThreadSchema = z5.object({
461
+ var commentExportThreadSchema = z7.object({
362
462
  // §6.6.1 — lets a polling agent skip threads it already acknowledged
363
463
  // (acknowledged threads stay in status=open exports on purpose).
364
- acknowledgedAt: z5.string().nullable().default(null),
464
+ acknowledgedAt: z7.string().nullable().default(null),
365
465
  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(),
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(),
373
473
  // The opening message's id (distinct from the thread `id`) — `edit`/
374
474
  // `delete` operate on messages, so the CLI needs it to target the first
375
475
  // comment, not just the replies.
376
- messageId: z5.string(),
377
- reactions: z5.array(exportReactionSchema).optional(),
378
- replies: z5.array(commentExportReplySchema),
476
+ messageId: z7.string(),
477
+ reactions: z7.array(exportReactionSchema).optional(),
478
+ replies: z7.array(commentExportReplySchema),
379
479
  resolvedBy: threadActorSchema.nullable().default(null),
380
- status: z5.enum(["open", "resolved"]),
381
- versionNumber: z5.number().int().positive()
480
+ status: z7.enum(["open", "resolved"]),
481
+ versionNumber: z7.number().int().positive()
382
482
  });
383
- var commentExportSchema = z5.object({
384
- artifactId: z5.string(),
385
- comments: z5.array(commentExportThreadSchema),
483
+ var commentExportSchema = z7.object({
484
+ artifactId: z7.string(),
485
+ comments: z7.array(commentExportThreadSchema),
386
486
  // The artifact's current version (null before the first finalize).
387
- versionNumber: z5.number().int().positive().nullable()
487
+ versionNumber: z7.number().int().positive().nullable()
388
488
  });
389
- var commentReadResponseSchema = z5.object({
390
- lastSeenAt: z5.string()
489
+ var commentReadResponseSchema = z7.object({
490
+ lastSeenAt: z7.string()
391
491
  });
392
492
  function elementLabel(el) {
393
493
  if (el.id) {
@@ -398,499 +498,554 @@ function elementLabel(el) {
398
498
  }
399
499
 
400
500
  // ../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()
501
+ import { z as z8 } from "zod";
502
+ var apiErrorSchema = z8.object({
503
+ code: z8.string(),
504
+ docsUrl: z8.string().url(),
505
+ message: z8.string(),
506
+ retryAfter: z8.number().int().positive().optional()
407
507
  });
408
508
 
409
509
  // ../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)
510
+ import { z as z9 } from "zod";
511
+ var feedbackRequestSchema = z9.object({
512
+ email: z9.string().trim().toLowerCase().email().optional().or(z9.literal("")),
513
+ message: z9.string().trim().min(1).max(4e3)
414
514
  });
415
- var feedbackResponseSchema = z7.object({
416
- sent: z7.literal(true)
515
+ var feedbackResponseSchema = z9.object({
516
+ sent: z9.literal(true)
417
517
  });
418
518
 
419
519
  // ../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(),
520
+ import { z as z10 } from "zod";
521
+ var grantRoleSchema = z10.enum(["viewer"]);
522
+ var createGrantSchema = z10.object({
523
+ email: z10.string().trim().toLowerCase().email().optional(),
424
524
  role: grantRoleSchema.default("viewer"),
425
- userId: z8.string().min(1).optional()
525
+ userId: z10.string().min(1).optional()
426
526
  }).refine((body) => Boolean(body.email) !== Boolean(body.userId), {
427
527
  message: "provide exactly one of email or userId"
428
528
  });
429
- var grantSchema = z8.object({
430
- createdAt: z8.string(),
431
- email: z8.string().email().nullable(),
432
- expiresAt: z8.string().nullable(),
433
- id: z8.string(),
529
+ var grantSchema = z10.object({
530
+ createdAt: z10.string(),
531
+ email: z10.string().email().nullable(),
532
+ expiresAt: z10.string().nullable(),
533
+ id: z10.string(),
434
534
  // The subject's avatar once they have an account; null otherwise.
435
- image: z8.string().nullable(),
535
+ image: z10.string().nullable(),
436
536
  // Present on the invited account after redemption / when granting an
437
537
  // existing user; null for a still-pending email invite.
438
- name: z8.string().nullable(),
439
- redeemedAt: z8.string().nullable(),
538
+ name: z10.string().nullable(),
539
+ redeemedAt: z10.string().nullable(),
440
540
  role: grantRoleSchema,
441
541
  // Shown as "· pending" beside the role until the link is redeemed.
442
- status: z8.enum(["pending", "active"]),
542
+ status: z10.enum(["pending", "active"]),
443
543
  // Display fragment of the live invitation token, or null once redeemed.
444
- tokenPrefix: z8.string().nullable(),
445
- userId: z8.string().nullable()
544
+ tokenPrefix: z10.string().nullable(),
545
+ userId: z10.string().nullable()
446
546
  });
447
547
  var createdGrantSchema = grantSchema.extend({
448
- url: z8.string().url()
548
+ url: z10.string().url()
449
549
  });
450
- var grantListResponseSchema = z8.object({
451
- grants: z8.array(grantSchema),
550
+ var grantListResponseSchema = z10.object({
551
+ grants: z10.array(grantSchema),
452
552
  // The artifact's current general-access value, so the share surface can
453
553
  // render the "General access" line without a second read.
454
- visibility: z8.enum(["public", "unlisted", "workspace", "private"]),
554
+ visibility: z10.enum(["public", "unlisted", "workspace", "private"]),
455
555
  // The owning workspace's logo, so the share modal can draw the workspace
456
556
  // itself rather than a generic glyph. Null when none was uploaded.
457
- workspaceLogo: z8.string().nullable().optional(),
557
+ workspaceLogo: z10.string().nullable().optional(),
458
558
  // The owning workspace's display name, so "People with access" can name the
459
559
  // audience `workspace` visibility actually admits ("All Acme members") rather
460
560
  // than leaving an empty list to imply nobody. Null for a workspace-less
461
561
  // artifact; optional so an older server still parses.
462
- workspaceName: z8.string().nullable().optional()
562
+ workspaceName: z10.string().nullable().optional()
463
563
  });
464
- var revokeGrantResponseSchema = z8.object({
465
- revoked: z8.literal(true)
564
+ var revokeGrantResponseSchema = z10.object({
565
+ revoked: z10.literal(true)
466
566
  });
467
- var acceptGrantSchema = z8.object({
468
- token: z8.string().min(1)
567
+ var acceptGrantSchema = z10.object({
568
+ token: z10.string().min(1)
469
569
  });
470
- var acceptGrantResponseSchema = z8.object({
471
- artifactId: z8.string(),
570
+ var acceptGrantResponseSchema = z10.object({
571
+ artifactId: z10.string(),
472
572
  // 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()
573
+ artifactUrl: z10.string().url(),
574
+ slug: z10.string(),
575
+ title: z10.string()
576
+ });
577
+ var workspaceGuestSchema = z10.object({
578
+ artifacts: z10.array(
579
+ z10.object({
580
+ artifactId: z10.string(),
581
+ grantId: z10.string(),
582
+ slug: z10.string(),
583
+ status: z10.enum(["pending", "active"]),
584
+ title: z10.string()
485
585
  })
486
586
  ),
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(),
587
+ email: z10.string().nullable(),
588
+ image: z10.string().nullable(),
589
+ name: z10.string().nullable(),
590
+ userId: z10.string().nullable()
591
+ });
592
+ var workspaceGuestListResponseSchema = z10.object({
593
+ guests: z10.array(workspaceGuestSchema)
594
+ });
595
+ var sharedWithMeArtifactSchema = z10.object({
596
+ artifactId: z10.string(),
597
+ slug: z10.string(),
598
+ title: z10.string(),
599
+ url: z10.string().url(),
500
600
  // Who shared it, for the "Shared with me" list.
501
- workspaceName: z8.string()
601
+ workspaceName: z10.string()
502
602
  });
503
- var sharedWithMeListResponseSchema = z8.object({
504
- artifacts: z8.array(sharedWithMeArtifactSchema)
603
+ var sharedWithMeListResponseSchema = z10.object({
604
+ artifacts: z10.array(sharedWithMeArtifactSchema)
505
605
  });
506
606
 
607
+ // ../shared/src/html-meta.ts
608
+ var HEAD_SCAN_CHARS = 64 * 1024;
609
+
507
610
  // ../shared/src/me.ts
508
- import { z as z10 } from "zod";
611
+ import { z as z12 } from "zod";
509
612
 
510
613
  // ../shared/src/workspaces.ts
511
- import { z as z9 } from "zod";
614
+ import { z as z11 } from "zod";
512
615
  var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
513
- var workspaceHandleUnavailableReasonSchema = z9.enum([
616
+ var workspaceHandleUnavailableReasonSchema = z11.enum([
514
617
  "held",
515
618
  "invalid",
516
619
  "reserved",
517
620
  "taken"
518
621
  ]);
519
- var workspaceHandleAvailabilitySchema = z9.object({
520
- available: z9.boolean(),
521
- handle: z9.string(),
622
+ var workspaceHandleAvailabilitySchema = z11.object({
623
+ available: z11.boolean(),
624
+ handle: z11.string(),
522
625
  // Human-readable sentence for the field hint; null when available.
523
- message: z9.string().nullable(),
626
+ message: z11.string().nullable(),
524
627
  reason: workspaceHandleUnavailableReasonSchema.nullable()
525
628
  });
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, {
629
+ var workspaceRoleSchema = z11.enum(["owner", "admin", "member"]);
630
+ var workspaceInviteRoleSchema = z11.enum(["admin", "member"]);
631
+ var workspaceHandleSchema = z11.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
529
632
  message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
530
633
  });
531
- var createWorkspaceSchema = z9.object({
634
+ var createWorkspaceSchema = z11.object({
532
635
  handle: workspaceHandleSchema.optional(),
533
- name: z9.string().trim().min(1).max(100)
636
+ name: z11.string().trim().min(1).max(100)
534
637
  });
535
- var updateWorkspaceSchema = z9.object({
638
+ var updateWorkspaceSchema = z11.object({
536
639
  handle: workspaceHandleSchema.optional(),
537
- name: z9.string().trim().min(1).max(100).optional()
640
+ name: z11.string().trim().min(1).max(100).optional()
538
641
  }).refine((body) => body.handle !== void 0 || body.name !== void 0, {
539
642
  message: "provide at least one of name or handle"
540
643
  });
541
- var workspaceSchema = z9.object({
542
- createdAt: z9.string(),
543
- handle: z9.string(),
644
+ var workspaceSchema = z11.object({
645
+ createdAt: z11.string(),
646
+ handle: z11.string(),
544
647
  // ISO timestamp when `{handle}.artifact.cafe` was provisioned, or null.
545
648
  // Set by the one-time, self-serve enable flow (§4.4); never auto at
546
649
  // create, and always null for personal workspaces.
547
- hostnameProvisionedAt: z9.string().nullable(),
548
- id: z9.string(),
650
+ hostnameProvisionedAt: z11.string().nullable(),
651
+ id: z11.string(),
549
652
  // Auto-provisioned solo workspace, one per user: non-deletable, takes
550
653
  // no members, renameable like any workspace.
551
- isPersonal: z9.boolean(),
654
+ isPersonal: z11.boolean(),
552
655
  // Uploaded logo URL, or null for the initials fallback. Set through the
553
656
  // dedicated /workspaces/:id/avatar route, never the generic update.
554
- logo: z9.string().nullable(),
555
- name: z9.string(),
657
+ logo: z11.string().nullable(),
658
+ name: z11.string(),
556
659
  role: workspaceRoleSchema.nullable(),
557
- updatedAt: z9.string()
660
+ updatedAt: z11.string()
558
661
  });
559
- var workspaceListResponseSchema = z9.object({
560
- workspaces: z9.array(workspaceSchema)
662
+ var workspaceListResponseSchema = z11.object({
663
+ workspaces: z11.array(workspaceSchema)
561
664
  });
562
- var deleteWorkspaceResponseSchema = z9.object({
563
- deleted: z9.literal(true)
665
+ var deleteWorkspaceResponseSchema = z11.object({
666
+ deleted: z11.literal(true)
564
667
  });
565
- var workspaceMemberSchema = z9.object({
566
- createdAt: z9.string(),
567
- email: z9.string().email(),
568
- image: z9.string().nullable(),
569
- name: z9.string(),
668
+ var workspaceMemberSchema = z11.object({
669
+ createdAt: z11.string(),
670
+ email: z11.string().email(),
671
+ image: z11.string().nullable(),
672
+ name: z11.string(),
570
673
  role: workspaceRoleSchema,
571
- userId: z9.string()
674
+ userId: z11.string()
572
675
  });
573
- var workspaceMemberListResponseSchema = z9.object({
574
- members: z9.array(workspaceMemberSchema)
676
+ var workspaceMemberListResponseSchema = z11.object({
677
+ members: z11.array(workspaceMemberSchema)
575
678
  });
576
- var updateWorkspaceMemberSchema = z9.object({
679
+ var updateWorkspaceMemberSchema = z11.object({
577
680
  role: workspaceRoleSchema
578
681
  });
579
- var removeWorkspaceMemberResponseSchema = z9.object({
580
- removed: z9.literal(true)
682
+ var removeWorkspaceMemberResponseSchema = z11.object({
683
+ removed: z11.literal(true)
581
684
  });
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(),
685
+ var createWorkspaceInviteSchema = z11.object({
686
+ email: z11.string().trim().toLowerCase().email().optional(),
687
+ expiresAt: z11.string().datetime(),
688
+ maxUses: z11.number().int().positive().optional(),
586
689
  role: workspaceInviteRoleSchema
587
690
  });
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(),
691
+ var workspaceInviteSchema = z11.object({
692
+ createdAt: z11.string(),
693
+ email: z11.string().email().nullable(),
694
+ expiresAt: z11.string(),
695
+ id: z11.string(),
696
+ maxUses: z11.number().int().positive().nullable(),
594
697
  role: workspaceInviteRoleSchema,
595
- tokenPrefix: z9.string(),
596
- useCount: z9.number().int().nonnegative()
698
+ tokenPrefix: z11.string(),
699
+ useCount: z11.number().int().nonnegative()
597
700
  });
598
701
  var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
599
- url: z9.string().url()
702
+ url: z11.string().url()
600
703
  });
601
- var workspaceInviteListResponseSchema = z9.object({
602
- invites: z9.array(workspaceInviteSchema)
704
+ var workspaceInviteListResponseSchema = z11.object({
705
+ invites: z11.array(workspaceInviteSchema)
603
706
  });
604
- var revokeWorkspaceInviteResponseSchema = z9.object({
605
- revoked: z9.literal(true)
707
+ var revokeWorkspaceInviteResponseSchema = z11.object({
708
+ revoked: z11.literal(true)
606
709
  });
607
- var acceptWorkspaceInviteSchema = z9.object({
608
- token: z9.string().min(1)
710
+ var acceptWorkspaceInviteSchema = z11.object({
711
+ token: z11.string().min(1)
609
712
  });
610
- var acceptWorkspaceInviteResponseSchema = z9.object({
713
+ var acceptWorkspaceInviteResponseSchema = z11.object({
611
714
  role: workspaceRoleSchema,
612
715
  workspace: workspaceSchema
613
716
  });
614
- var folderVisibilitySchema = z9.enum(["public", "workspace"]);
615
- var artifactVisibilitySchema = z9.enum([
717
+ var folderVisibilitySchema = z11.enum(["public", "workspace"]);
718
+ var artifactVisibilitySchema = z11.enum([
616
719
  "public",
617
720
  "unlisted",
618
721
  "workspace",
619
722
  "private"
620
723
  ]);
621
- var createFolderSchema = z9.object({
622
- name: z9.string().trim().min(1).max(100),
724
+ var createFolderSchema = z11.object({
725
+ // Optional custom icon; omitted (or null) leaves the default glyph.
726
+ emoji: iconEmojiSchema.optional(),
727
+ name: z11.string().trim().min(1).max(100),
623
728
  visibility: folderVisibilitySchema
624
729
  });
625
- var updateFolderSchema = z9.object({
730
+ var updateFolderSchema = z11.object({
626
731
  // With `visibility`: bulk-update the folder's artifacts to match
627
732
  // (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(),
733
+ cascade: z11.boolean().optional(),
734
+ // Explicit null clears the icon back to the default glyph, so this is
735
+ // a field where "sent as null" and "omitted" mean different things.
736
+ emoji: iconEmojiSchema.optional(),
737
+ name: z11.string().trim().min(1).max(100).optional(),
738
+ sortOrder: z11.number().int().min(0).optional(),
631
739
  visibility: folderVisibilitySchema.optional()
632
740
  }).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" }
741
+ (body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0 || body.emoji !== void 0,
742
+ { message: "provide at least one of name, visibility, sortOrder, emoji" }
635
743
  ).refine((body) => !body.cascade || body.visibility !== void 0, {
636
744
  message: "cascade requires visibility"
637
745
  });
638
- var folderSchema = z9.object({
639
- createdAt: z9.string(),
640
- id: z9.string(),
641
- name: z9.string(),
746
+ var folderSchema = z11.object({
747
+ createdAt: z11.string(),
748
+ // Custom icon, or null to render the default folder glyph.
749
+ emoji: z11.string().nullable(),
750
+ id: z11.string(),
751
+ name: z11.string(),
642
752
  // Permalink segment for the public folder page (/w/{handle}/f/{slug}).
643
753
  // Server-derived from the name; renames regenerate it.
644
- slug: z9.string(),
645
- sortOrder: z9.number().int(),
646
- updatedAt: z9.string(),
754
+ slug: z11.string(),
755
+ sortOrder: z11.number().int(),
756
+ updatedAt: z11.string(),
647
757
  visibility: folderVisibilitySchema,
648
- workspaceId: z9.string()
758
+ workspaceId: z11.string()
649
759
  });
650
- var folderListResponseSchema = z9.object({
651
- folders: z9.array(folderSchema)
760
+ var folderListResponseSchema = z11.object({
761
+ folders: z11.array(folderSchema)
652
762
  });
653
- var deleteFolderResponseSchema = z9.object({
654
- deleted: z9.literal(true)
763
+ var deleteFolderResponseSchema = z11.object({
764
+ deleted: z11.literal(true)
655
765
  });
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(),
766
+ var workspaceArtifactSchema = z11.object({
767
+ createdAt: z11.string(),
768
+ currentVersionNumber: z11.number().int().nullable(),
769
+ // Custom icon, or null to render the default document glyph.
770
+ emoji: z11.string().nullable(),
771
+ folderId: z11.string().nullable(),
772
+ id: z11.string(),
773
+ openCommentCount: z11.number().int().nonnegative(),
662
774
  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(),
775
+ slug: z11.string(),
776
+ title: z11.string(),
777
+ updatedAt: z11.string(),
778
+ url: z11.string().url(),
779
+ versionCount: z11.number().int().nonnegative(),
668
780
  visibility: artifactVisibilitySchema
669
781
  });
670
- var workspaceArtifactListResponseSchema = z9.object({
671
- artifacts: z9.array(workspaceArtifactSchema)
782
+ var workspaceArtifactListResponseSchema = z11.object({
783
+ artifacts: z11.array(workspaceArtifactSchema)
672
784
  });
673
- var workspaceArtifactSearchResultSchema = z9.object({
674
- id: z9.string(),
675
- slug: z9.string(),
676
- title: z9.string()
785
+ var workspaceArtifactSearchResultSchema = z11.object({
786
+ id: z11.string(),
787
+ slug: z11.string(),
788
+ title: z11.string()
677
789
  });
678
- var workspaceArtifactSearchResponseSchema = z9.object({
679
- results: z9.array(workspaceArtifactSearchResultSchema)
790
+ var workspaceArtifactSearchResponseSchema = z11.object({
791
+ results: z11.array(workspaceArtifactSearchResultSchema)
680
792
  });
681
793
 
682
794
  // ../shared/src/me.ts
683
- var ownArtifactSchema = z10.object({
684
- createdAt: z10.string(),
685
- currentVersionNumber: z10.number().nullable(),
795
+ var personSchema = z12.object({
796
+ image: z12.string().nullable(),
797
+ name: z12.string()
798
+ });
799
+ var ownArtifactSchema = z12.object({
800
+ // Everyone else who has published a version, oldest first. Any member may
801
+ // publish a new one (SPEC-2 §5.1), so an artifact's byline is often more
802
+ // than one person and the card stacks their faces rather than crediting
803
+ // whoever shipped last. Excludes `publishedBy`; empty on a solo artifact.
804
+ coPublishers: z12.array(personSchema),
805
+ createdAt: z12.string(),
806
+ // SPEC-2 §5.1 — who *made* the artifact: the author of its first
807
+ // finalized version, the same row `viewerCanManage` is decided on. Not
808
+ // the same person as `publishedBy` once an agent ships v2, which is why
809
+ // the dashboard filters on both. Null for a token-only publish.
810
+ createdBy: z12.object({ image: z12.string().nullable(), name: z12.string() }).nullable(),
811
+ currentVersionNumber: z12.number().nullable(),
812
+ // Custom icon, or null to render the default document glyph.
813
+ emoji: z12.string().nullable(),
686
814
  // Owned artifacts normally never expire; non-null only while an
687
815
  // expiry is still set (e.g. an unclaimed publish surfaced here later).
688
- expiresAt: z10.string().nullable(),
816
+ expiresAt: z12.string().nullable(),
689
817
  // SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
690
818
  // 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(),
819
+ // move control and folder filter; `folderName` is the display label,
820
+ // `folderEmoji` its icon (null = the default folder glyph).
821
+ folderEmoji: z12.string().nullable(),
822
+ folderId: z12.string().nullable(),
823
+ folderName: z12.string().nullable(),
694
824
  // Whether a publish token is still active — revocable from the
695
825
  // 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(),
826
+ hasPublishToken: z12.boolean(),
827
+ // Owner preference: hide the version list from reviewers in the review
828
+ // shell. Managers still see it.
829
+ hideVersions: z12.boolean(),
830
+ id: z12.string(),
831
+ openThreads: z12.number(),
832
+ protected: z12.boolean(),
700
833
  // Who published the current version (falling back to the claiming
701
834
  // owner) — the dashboard shows a name and avatar per artifact so a
702
835
  // shared workspace reads as people's work, not anonymous rows. Null
703
836
  // when the publish was token-only and the artifact is unclaimed.
704
- publishedBy: z10.object({ image: z10.string().nullable(), name: z10.string() }).nullable(),
837
+ publishedBy: personSchema.nullable(),
705
838
  // SPEC-2 §5.8 — the review dial, so the dashboard's settings dialog can
706
839
  // render current state without a second fetch.
707
840
  reviewMode: reviewModeSchema,
708
- slug: z10.string(),
709
- title: z10.string(),
710
- totalViews: z10.number(),
841
+ slug: z12.string(),
842
+ title: z12.string(),
843
+ totalViews: z12.number(),
711
844
  // SPEC §6.20 — messages by others newer than the viewer's last-seen
712
845
  // 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(),
846
+ unreadComments: z12.number(),
847
+ updatedAt: z12.string(),
848
+ url: z12.string(),
849
+ versionCount: z12.number(),
717
850
  // SPEC-2 §5.1 — whether the caller clears the manage bar for this
718
851
  // artifact (workspace admin, or the person who first published it).
719
852
  // Only a manager's folder move inherits the folder's visibility, so the
720
853
  // dashboard warns exactly the people the change can actually affect, and
721
854
  // only a manager is offered the Share entry (§5.7).
722
- viewerCanManage: z10.boolean(),
855
+ viewerCanManage: z12.boolean(),
723
856
  // SPEC-2 §5.2 — the artifact's own visibility, the single enforcement
724
857
  // 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).
858
+ // widen exposure before it sends the PATCH (§3.4), and to render the
859
+ // audience glyph — one per level — on rows and cards (§5.7).
727
860
  visibility: artifactVisibilitySchema
728
861
  });
729
- var ownArtifactsResponseSchema = z10.object({
730
- artifacts: z10.array(ownArtifactSchema)
862
+ var ownArtifactsResponseSchema = z12.object({
863
+ artifacts: z12.array(ownArtifactSchema)
731
864
  });
732
- var revokePublishTokenResponseSchema = z10.object({
733
- revoked: z10.boolean()
865
+ var revokePublishTokenResponseSchema = z12.object({
866
+ revoked: z12.boolean()
734
867
  });
735
868
 
736
869
  // ../shared/src/owner.ts
737
- import { z as z11 } from "zod";
870
+ import { z as z13 } from "zod";
738
871
  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({
872
+ var claimResponseSchema = z13.object({
873
+ artifactId: z13.string(),
874
+ slug: z13.string(),
875
+ title: z13.string(),
876
+ url: z13.string().url()
877
+ });
878
+ var updateArtifactSchema = z13.object({
879
+ // Custom icon; explicit null clears it back to the default glyph.
880
+ // Part of what the artifact *is* (like the title), so it takes the
881
+ // same manage rights as title/slug — not the member-level folder move.
882
+ emoji: iconEmojiSchema.optional(),
746
883
  // SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
747
884
  // folder; `workspaceId: null` moves the artifact out of its workspace
748
885
  // (§5.5 ownership transfer).
749
- folderId: z11.string().min(1).nullable().optional(),
750
- slug: z11.string().min(3).max(63).regex(SLUG_PATTERN, {
886
+ folderId: z13.string().min(1).nullable().optional(),
887
+ // Keep the version list out of the review shell. Presentation only:
888
+ // versions stay immutable, and a `/vN/` URL still resolves.
889
+ hideVersions: z13.boolean().optional(),
890
+ slug: z13.string().min(3).max(63).regex(SLUG_PATTERN, {
751
891
  message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
752
892
  }).optional(),
753
- title: z11.string().min(1).max(200).optional(),
893
+ title: z13.string().min(1).max(200).optional(),
754
894
  visibility: artifactVisibilitySchema.optional(),
755
- workspaceId: z11.string().min(1).nullable().optional()
895
+ workspaceId: z13.string().min(1).nullable().optional()
756
896
  }).refine(
757
- (body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0,
897
+ (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,
758
898
  {
759
- message: "provide at least one of title, slug, visibility, folderId, workspaceId"
899
+ message: "provide at least one of title, slug, visibility, folderId, workspaceId, emoji, hideVersions"
760
900
  }
761
901
  );
762
- var deleteArtifactResponseSchema = z11.object({
763
- deleted: z11.literal(true)
902
+ var deleteArtifactResponseSchema = z13.object({
903
+ deleted: z13.literal(true)
764
904
  });
765
905
 
766
906
  // ../shared/src/presence.ts
767
- import { z as z12 } from "zod";
907
+ import { z as z14 } from "zod";
768
908
  var AGENT_STATES = ["listening", "editing", "publishing"];
769
- var agentStateSchema = z12.enum(AGENT_STATES);
909
+ var agentStateSchema = z14.enum(AGENT_STATES);
770
910
  var AGENT_PRESENCE = {
771
911
  editingTtlSeconds: 300,
772
912
  heartbeatSeconds: 15,
773
913
  shellPollSeconds: 15,
774
914
  ttlSeconds: 45
775
915
  };
776
- var agentHeartbeatBodySchema = z12.object({
916
+ var agentHeartbeatBodySchema = z14.object({
777
917
  state: agentStateSchema.optional()
778
918
  });
779
- var agentHeartbeatResponseSchema = z12.object({
780
- listening: z12.literal(true),
919
+ var agentHeartbeatResponseSchema = z14.object({
920
+ listening: z14.literal(true),
781
921
  state: agentStateSchema.optional(),
782
- ttlSeconds: z12.number().int().positive()
922
+ ttlSeconds: z14.number().int().positive()
783
923
  });
784
- var agentPresenceSchema = z12.object({
785
- listening: z12.boolean(),
924
+ var agentPresenceSchema = z14.object({
925
+ agent: z14.object({ id: z14.string().nullable(), label: z14.string() }).nullable().default(null),
926
+ listening: z14.boolean(),
786
927
  state: agentStateSchema.nullable().optional(),
787
- versionNumber: z12.number().int().positive().nullable()
928
+ versionNumber: z14.number().int().positive().nullable()
788
929
  });
789
930
 
790
931
  // ../shared/src/protection.ts
791
- import { z as z13 } from "zod";
932
+ import { z as z15 } from "zod";
792
933
  var PASSWORD_MIN = 6;
793
934
  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({
935
+ var gatePasswordSchema = z15.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
936
+ var setProtectionSchema = z15.object({
796
937
  password: gatePasswordSchema.nullable()
797
938
  });
798
- var protectionResponseSchema = z13.object({
799
- protected: z13.boolean()
939
+ var protectionResponseSchema = z15.object({
940
+ protected: z15.boolean()
800
941
  });
801
- var unlockSchema = z13.object({
802
- password: z13.string().min(1).max(1024)
942
+ var unlockSchema = z15.object({
943
+ password: z15.string().min(1).max(1024)
803
944
  });
804
- var unlockResponseSchema = z13.object({
805
- unlocked: z13.literal(true)
945
+ var unlockResponseSchema = z15.object({
946
+ unlocked: z15.literal(true)
806
947
  });
807
948
 
808
949
  // ../shared/src/report.ts
809
- import { z as z14 } from "zod";
810
- var reportRequestSchema = z14.object({
811
- reason: z14.string().max(1e3).optional()
950
+ import { z as z16 } from "zod";
951
+ var reportRequestSchema = z16.object({
952
+ reason: z16.string().max(1e3).optional()
812
953
  });
813
- var reportResponseSchema = z14.object({
814
- reported: z14.literal(true)
954
+ var reportResponseSchema = z16.object({
955
+ reported: z16.literal(true)
815
956
  });
816
957
 
817
958
  // ../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(),
959
+ import { z as z17 } from "zod";
960
+ var artifactReviewerSchema = z17.object({
961
+ avatarUrl: z17.string().nullable(),
962
+ displayName: z17.string(),
822
963
  // Exactly one of userId/guestId is set, per `kind`.
823
- guestId: z15.string().nullable(),
964
+ guestId: z17.string().nullable(),
824
965
  // "account", not commentAuthorSchema's "member" — the whole point here is
825
966
  // that these people need not be workspace members. A guest can be named in
826
967
  // a comment but never notified: guest_identities carries a display name and
827
968
  // no email, so there is nowhere to send anything.
828
- kind: z15.enum(["account", "guest"]),
829
- userId: z15.string().nullable()
969
+ kind: z17.enum(["account", "guest"]),
970
+ userId: z17.string().nullable()
830
971
  });
831
- var artifactReviewerListResponseSchema = z15.object({
832
- reviewers: z15.array(artifactReviewerSchema)
972
+ var artifactReviewerListResponseSchema = z17.object({
973
+ reviewers: z17.array(artifactReviewerSchema)
833
974
  });
834
975
 
835
976
  // ../shared/src/stats.ts
836
- import { z as z16 } from "zod";
837
- var avgViewSecondsSchema = z16.number().int().nonnegative().nullable();
838
- var versionViewStatsSchema = z16.object({
977
+ import { z as z18 } from "zod";
978
+ var avgViewSecondsSchema = z18.number().int().nonnegative().nullable();
979
+ var versionViewStatsSchema = z18.object({
839
980
  avgViewSeconds: avgViewSecondsSchema,
840
981
  // Views from inside the review shell's iframe vs. direct visits to
841
982
  // the render URL. views = embeddedViews + directViews.
842
- directViews: z16.number().int().nonnegative(),
843
- embeddedViews: z16.number().int().nonnegative(),
983
+ directViews: z18.number().int().nonnegative(),
984
+ embeddedViews: z18.number().int().nonnegative(),
844
985
  // 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()
986
+ uniqueViewers: z18.number().int().nonnegative(),
987
+ versionNumber: z18.number().int().positive(),
988
+ views: z18.number().int().nonnegative()
848
989
  });
849
- var dailyViewsSchema = z16.object({
990
+ var dailyViewsSchema = z18.object({
850
991
  avgViewSeconds: avgViewSecondsSchema,
851
992
  // UTC day, YYYY-MM-DD.
852
- day: z16.string(),
853
- uniqueViewers: z16.number().int().nonnegative(),
854
- views: z16.number().int().nonnegative()
993
+ day: z18.string(),
994
+ uniqueViewers: z18.number().int().nonnegative(),
995
+ views: z18.number().int().nonnegative()
855
996
  });
856
- var artifactStatsResponseSchema = z16.object({
997
+ var artifactStatsResponseSchema = z18.object({
857
998
  avgViewSeconds: avgViewSecondsSchema,
858
- daily: z16.array(dailyViewsSchema),
999
+ daily: z18.array(dailyViewsSchema),
859
1000
  // 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)
1001
+ totalUniqueViewers: z18.number().int().nonnegative(),
1002
+ totalViews: z18.number().int().nonnegative(),
1003
+ versions: z18.array(versionViewStatsSchema)
863
1004
  });
864
1005
 
865
1006
  // ../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()
1007
+ import { z as z19 } from "zod";
1008
+ var versionSourceSchema = z19.object({
1009
+ agentName: z19.string().nullable(),
1010
+ type: z19.string()
870
1011
  });
871
- var versionChangesSchema = z17.object({
872
- added: z17.array(z17.string()),
873
- changed: z17.array(z17.string()),
874
- removed: z17.array(z17.string())
1012
+ var versionChangesSchema = z19.object({
1013
+ added: z19.array(z19.string()),
1014
+ changed: z19.array(z19.string()),
1015
+ removed: z19.array(z19.string())
875
1016
  });
876
- var versionSummarySchema = z17.object({
1017
+ var versionSummarySchema = z19.object({
877
1018
  // Absent for the first version — there is no previous version to diff against.
878
1019
  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(),
1020
+ createdAt: z19.string(),
1021
+ entryUrl: z19.string().url(),
1022
+ fileCount: z19.number().int().nonnegative(),
1023
+ id: z19.string(),
1024
+ isCurrent: z19.boolean(),
884
1025
  // Author-supplied bullet-point changelog; null when the publish omitted it.
885
- notes: z17.string().nullable(),
1026
+ notes: z19.string().nullable(),
1027
+ // The account that published this version, with their own avatar where
1028
+ // they've set one. Any workspace member can publish a new version
1029
+ // (SPEC-2 §5.1), so "which teammate" outranks "which agent" — the agent
1030
+ // is the tool, this is the person. Null for token-only publishes, which
1031
+ // carry no account.
1032
+ publishedBy: z19.object({ image: z19.string().nullable(), name: z19.string() }).nullable(),
886
1033
  source: versionSourceSchema,
887
- totalSizeBytes: z17.number().int().nonnegative(),
888
- versionNumber: z17.number().int().positive()
1034
+ totalSizeBytes: z19.number().int().nonnegative(),
1035
+ versionNumber: z19.number().int().positive()
889
1036
  });
890
- var versionListResponseSchema = z17.object({
891
- versions: z17.array(versionSummarySchema)
1037
+ var versionListResponseSchema = z19.object({
1038
+ versions: z19.array(versionSummarySchema)
892
1039
  });
893
1040
 
1041
+ // src/agent-identity.ts
1042
+ function agentLabel(explicit) {
1043
+ if (explicit !== void 0) {
1044
+ return explicit.trim() || void 0;
1045
+ }
1046
+ return detectAgentFromEnv(process.env);
1047
+ }
1048
+
894
1049
  // src/api.ts
895
1050
  var ApiRequestError = class extends Error {
896
1051
  constructor(status2, code, message, retryAfter) {
@@ -1108,11 +1263,13 @@ function createApiClient(apiUrl2, publishToken) {
1108
1263
  // SPEC §6.5 — post a reply into a thread. Authorized by this client's
1109
1264
  // credential: the artifact's publish token (the reply renders as the
1110
1265
  // 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) {
1266
+ // payload — the credential is the identity. `via` names the tool: it
1267
+ // becomes the agent's byline on a token reply, a "via" label on an
1268
+ // account one.
1269
+ async replyThread(threadId, body, via) {
1113
1270
  return request(
1114
1271
  `${base}/api/v1/comment-threads/${threadId}/messages`,
1115
- { body: JSON.stringify({ body }), headers, method: "POST" },
1272
+ { body: JSON.stringify({ body, via }), headers, method: "POST" },
1116
1273
  (d) => commentThreadSchema.parse(d)
1117
1274
  );
1118
1275
  },
@@ -1189,17 +1346,17 @@ import {
1189
1346
  writeFileSync as writeFileSync2
1190
1347
  } from "fs";
1191
1348
  import path2 from "path";
1192
- import { z as z19 } from "zod";
1349
+ import { z as z21 } from "zod";
1193
1350
 
1194
1351
  // src/credentials.ts
1195
1352
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
1196
1353
  import { homedir } from "os";
1197
1354
  import path from "path";
1198
- import { z as z18 } from "zod";
1355
+ import { z as z20 } from "zod";
1199
1356
  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)
1357
+ var credentialsSchema = z20.object({
1358
+ keys: z20.record(z20.string(), z20.string()),
1359
+ version: z20.literal(1)
1203
1360
  });
1204
1361
  function credentialsPath() {
1205
1362
  return path.join(homedir(), ".artifactcafe", "credentials");
@@ -1249,24 +1406,24 @@ function resolveApiKey(apiUrl2, flag) {
1249
1406
  }
1250
1407
 
1251
1408
  // 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(),
1409
+ var configSchema = z21.object({
1410
+ apiUrl: z21.string(),
1411
+ artifactId: z21.string(),
1412
+ folder: z21.string().optional(),
1413
+ folderId: z21.string().optional(),
1257
1414
  // Optional: a folder linked by `link` to an artifact you own carries no
1258
1415
  // token — owner actions authorize via your stored login instead. A first
1259
1416
  // 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)
1417
+ publishToken: z21.string().optional(),
1418
+ slug: z21.string(),
1419
+ url: z21.string(),
1420
+ workspace: z21.string().optional(),
1421
+ workspaceId: z21.string().optional(),
1422
+ workspaceKind: z21.enum(["personal", "workspace"]).optional()
1423
+ });
1424
+ var projectConfigSchema = z21.object({
1425
+ folder: z21.string().trim().min(1).optional(),
1426
+ workspace: z21.string().trim().min(1)
1270
1427
  }).strict();
1271
1428
  var CONFIG_DIR = ".artifactcafe";
1272
1429
  var CONFIG_FILE = "config.json";
@@ -1302,7 +1459,7 @@ function readProjectConfig(dir) {
1302
1459
  JSON.parse(readFileSync2(file, "utf8"))
1303
1460
  );
1304
1461
  } catch (error) {
1305
- const message = error instanceof z19.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1462
+ const message = error instanceof z21.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1306
1463
  throw new Error(`Invalid ${file}: ${message}.`);
1307
1464
  }
1308
1465
  }
@@ -1367,7 +1524,8 @@ function resolveArtifactTarget(dir, opts = {}) {
1367
1524
  );
1368
1525
  }
1369
1526
  const folderToken = artifactId === config?.artifactId ? config.publishToken : void 0;
1370
- const credential = folderToken ?? process.env.ARTIFACT_CAFE_TOKEN ?? resolveApiKey(apiUrl2, opts.apiKey);
1527
+ const apiKey = resolveApiKey(apiUrl2, opts.apiKey);
1528
+ const credential = (apiKey?.startsWith("aat_") ? apiKey : void 0) ?? folderToken ?? process.env.ARTIFACT_CAFE_TOKEN ?? apiKey;
1371
1529
  const url = artifactId === config?.artifactId ? config.url : void 0;
1372
1530
  return { apiUrl: apiUrl2, artifactId, credential, url };
1373
1531
  }
@@ -1441,7 +1599,10 @@ async function comment(opts) {
1441
1599
  anchor,
1442
1600
  body,
1443
1601
  versionId: artifact.currentVersion.id,
1444
- via: opts.via
1602
+ // An account comment keeps its own name and takes the tool as a "via"
1603
+ // label (§6.4) — detected when neither flag is given, so an agent
1604
+ // commenting with your key still says which tool typed it.
1605
+ via: agentLabel(opts.as ?? opts.via)
1445
1606
  });
1446
1607
  } catch (err) {
1447
1608
  if (err instanceof ApiRequestError && err.status === 401) {
@@ -1851,6 +2012,7 @@ async function publish(opts) {
1851
2012
  const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
1852
2013
  const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
1853
2014
  const apiKey = resolveApiKey(apiUrl2, opts.apiKey);
2015
+ const usingAgentToken = apiKey?.startsWith("aat_") === true;
1854
2016
  const placement = await resolvePlacement({
1855
2017
  apiKey,
1856
2018
  apiUrl: apiUrl2,
@@ -1873,6 +2035,9 @@ async function publish(opts) {
1873
2035
  }
1874
2036
  const sessionBody = {
1875
2037
  artifactId,
2038
+ // Overrides the entry HTML's own meta description on the share card.
2039
+ // Omitted entirely when absent, so the document keeps speaking for itself.
2040
+ ...opts.description?.trim() ? { description: opts.description.trim() } : {},
1876
2041
  entryPath,
1877
2042
  files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
1878
2043
  ...placement.folderId ? { folderId: placement.folderId } : {},
@@ -1880,7 +2045,10 @@ async function publish(opts) {
1880
2045
  // backslash-n; turn those back into real newlines so bullets survive.
1881
2046
  ...opts.notes?.trim() ? { notes: opts.notes.replace(/\\n/g, "\n").trim() } : {},
1882
2047
  source: {
1883
- agent: process.env.ARTIFACT_CAFE_AGENT,
2048
+ // ARTIFACT_CAFE_AGENT if it's set, else whichever agent's environment
2049
+ // we recognize — this is what names the agent on every reply to a
2050
+ // thread opened against this version (§6.5).
2051
+ agent: agentLabel(),
1884
2052
  runId: process.env.ARTIFACT_CAFE_RUN_ID,
1885
2053
  type: "cli"
1886
2054
  },
@@ -1893,7 +2061,7 @@ async function publish(opts) {
1893
2061
  try {
1894
2062
  session = await client.createSession(sessionBody);
1895
2063
  } catch (err) {
1896
- const keyRefused = apiKey !== void 0 && token !== void 0 && apiKey !== token && artifactId !== null && err instanceof ApiRequestError && (err.status === 401 || err.status === 403);
2064
+ const keyRefused = apiKey !== void 0 && !usingAgentToken && token !== void 0 && apiKey !== token && artifactId !== null && err instanceof ApiRequestError && (err.status === 401 || err.status === 403);
1897
2065
  if (!keyRefused) {
1898
2066
  throw err;
1899
2067
  }
@@ -1926,16 +2094,19 @@ async function publish(opts) {
1926
2094
  presence.stop();
1927
2095
  }
1928
2096
  const result = await client.finalize(session.publishSessionId);
2097
+ if (usingAgentToken && result.workspaceId) {
2098
+ placement.workspaceId = result.workspaceId;
2099
+ }
1929
2100
  const rememberedProjectFile = placement.rememberProjectDefault ? writeProjectConfig(dir, placement.rememberProjectDefault) : null;
1930
- const existingToken = config?.artifactId === result.artifactId ? config.publishToken : void 0;
1931
- const shouldWriteConfig = result.publishToken !== void 0 || config?.artifactId === result.artifactId || opts.workspace !== void 0 || opts.folder !== void 0 || placement.workspaceId !== void 0;
2101
+ const existingToken = !usingAgentToken && config?.artifactId === result.artifactId ? config.publishToken : void 0;
2102
+ const shouldWriteConfig = usingAgentToken || result.publishToken !== void 0 || config?.artifactId === result.artifactId || opts.workspace !== void 0 || opts.folder !== void 0 || placement.workspaceId !== void 0;
1932
2103
  if (shouldWriteConfig) {
1933
2104
  writeConfig(dir, {
1934
2105
  apiUrl: apiUrl2,
1935
2106
  artifactId: result.artifactId,
1936
2107
  ...placement.folder ? { folder: placement.folder } : {},
1937
2108
  ...placement.folderId ? { folderId: placement.folderId } : {},
1938
- ...result.publishToken ?? existingToken ? { publishToken: result.publishToken ?? existingToken } : {},
2109
+ ...!usingAgentToken && (result.publishToken ?? existingToken) ? { publishToken: result.publishToken ?? existingToken } : {},
1939
2110
  slug: slugFromUrl(result.url),
1940
2111
  url: result.url,
1941
2112
  ...placement.workspace ? { workspace: placement.workspace } : {},
@@ -2055,6 +2226,18 @@ function accessNote(visibility) {
2055
2226
  }
2056
2227
  async function resolvePlacement(params) {
2057
2228
  const { apiKey, apiUrl: apiUrl2, config, isNewArtifact, opts } = params;
2229
+ if (apiKey?.startsWith("aat_")) {
2230
+ if (opts.workspace !== void 0 || opts.folder !== void 0) {
2231
+ throw new Error(
2232
+ "An agent token selects its own workspace; omit --workspace and --folder."
2233
+ );
2234
+ }
2235
+ return {
2236
+ destination: "workspace",
2237
+ destinationLabel: "Agent token's workspace",
2238
+ destinationSource: "agent-token"
2239
+ };
2240
+ }
2058
2241
  const hasPlacementFlag = opts.workspace !== void 0 || opts.folder !== void 0;
2059
2242
  if (!hasPlacementFlag && config?.workspaceId) {
2060
2243
  const isPersonal = config.workspaceKind === "personal";
@@ -2415,100 +2598,45 @@ function printFolder(folder) {
2415
2598
  // src/link.ts
2416
2599
  import path8 from "path";
2417
2600
  import { createInterface as createInterface2 } from "readline";
2418
- async function link(opts) {
2419
- const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
2420
- /\/$/,
2421
- ""
2422
- );
2423
- const key = resolveApiKey(apiUrl2, opts.apiKey);
2424
- if (!key) {
2425
- throw new Error(
2426
- `Linking needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
2427
- );
2428
- }
2429
- const { artifacts } = await createApiClient(apiUrl2, key).listArtifacts();
2430
- if (artifacts.length === 0) {
2431
- throw new Error(
2432
- "No artifacts in your account yet \u2014 publish one first, or claim one via its claim link."
2433
- );
2434
- }
2435
- const selector = opts.artifact ?? opts.slug;
2436
- let chosen;
2437
- if (opts.artifact) {
2438
- chosen = artifacts.find((a) => a.id === opts.artifact);
2439
- } else if (opts.slug) {
2440
- chosen = artifacts.find((a) => a.slug === opts.slug);
2441
- }
2442
- if (selector && !chosen) {
2443
- throw new Error(
2444
- `No artifact "${selector}" in your account \u2014 run \`artifact-cafe list\` to see them.`
2445
- );
2446
- }
2447
- if (opts.json && !chosen) {
2448
- console.log(JSON.stringify({ artifacts }, null, 2));
2449
- return;
2450
- }
2451
- if (!chosen) {
2452
- if (!process.stdin.isTTY) {
2453
- throw new Error(
2454
- "Pick an artifact with --artifact <id> or --slug <slug> (or --json to list them). Interactive selection needs a terminal."
2455
- );
2456
- }
2457
- chosen = await pickArtifact(artifacts);
2458
- }
2459
- const dir = path8.resolve(opts.dir);
2460
- writeConfig(dir, {
2461
- apiUrl: apiUrl2,
2462
- artifactId: chosen.id,
2463
- slug: chosen.slug,
2464
- url: chosen.url
2465
- // No publishToken — this folder authorizes via your login, not a secret.
2466
- });
2467
- if (opts.json) {
2468
- console.log(
2469
- JSON.stringify(
2470
- {
2471
- artifactId: chosen.id,
2472
- linked: true,
2473
- slug: chosen.slug,
2474
- url: chosen.url
2475
- },
2476
- null,
2477
- 2
2478
- )
2479
- );
2480
- return;
2481
- }
2482
- console.log(`
2483
- \u2713 Linked ${dir} \u2192 "${chosen.title}" \u2014 ${chosen.url}`);
2484
- console.log(
2485
- " Owner actions (publish, comments --wait, protect) use your login.\n"
2601
+
2602
+ // src/list.ts
2603
+ async function teamGroups(client) {
2604
+ const { workspaces: workspaces3 } = await client.listWorkspaces();
2605
+ return await Promise.all(
2606
+ workspaces3.filter((workspace) => !workspace.isPersonal).map(async (workspace) => ({
2607
+ artifacts: (await client.listWorkspaceArtifacts(workspace.id)).artifacts,
2608
+ handle: workspace.handle,
2609
+ id: workspace.id,
2610
+ name: workspace.name
2611
+ }))
2486
2612
  );
2487
2613
  }
2488
- function pickArtifact(artifacts) {
2489
- console.log("Your artifacts:\n");
2490
- artifacts.forEach((a, i) => {
2491
- const v = a.currentVersionNumber ? `v${a.currentVersionNumber}` : "\u2014";
2492
- console.log(
2493
- ` ${String(i + 1).padStart(2)}. ${a.title} \xB7 ${v} \xB7 ${a.url}`
2494
- );
2495
- });
2496
- const rl = createInterface2({ input: process.stdin, output: process.stdout });
2497
- return new Promise((resolve2, reject) => {
2498
- rl.question(`
2499
- Link which? [1-${artifacts.length}]: `, (answer) => {
2500
- rl.close();
2501
- const n = Number(answer.trim());
2502
- if (!(Number.isInteger(n) && n >= 1 && n <= artifacts.length)) {
2503
- reject(new Error(`Enter a number between 1 and ${artifacts.length}.`));
2504
- return;
2505
- }
2506
- resolve2(artifacts[n - 1]);
2507
- });
2508
- });
2614
+ async function reachableArtifacts(client) {
2615
+ const [own, groups] = await Promise.all([
2616
+ client.listArtifacts(),
2617
+ teamGroups(client)
2618
+ ]);
2619
+ return [
2620
+ ...own.artifacts.map((artifact) => ({
2621
+ currentVersionNumber: artifact.currentVersionNumber,
2622
+ id: artifact.id,
2623
+ slug: artifact.slug,
2624
+ title: artifact.title,
2625
+ url: artifact.url,
2626
+ workspaceHandle: null
2627
+ })),
2628
+ ...groups.flatMap(
2629
+ (group) => group.artifacts.map((artifact) => ({
2630
+ currentVersionNumber: artifact.currentVersionNumber,
2631
+ id: artifact.id,
2632
+ slug: artifact.slug,
2633
+ title: artifact.title,
2634
+ url: artifact.url,
2635
+ workspaceHandle: group.handle
2636
+ }))
2637
+ )
2638
+ ];
2509
2639
  }
2510
-
2511
- // src/list.ts
2512
2640
  async function list(opts) {
2513
2641
  const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
2514
2642
  /\/$/,
@@ -2525,22 +2653,44 @@ async function list(opts) {
2525
2653
  await listWorkspace(client, opts);
2526
2654
  return;
2527
2655
  }
2528
- const data = await client.listArtifacts();
2656
+ const [data, groups] = await Promise.all([
2657
+ client.listArtifacts(),
2658
+ teamGroups(client)
2659
+ ]);
2529
2660
  if (opts.json) {
2530
- console.log(JSON.stringify(data, null, 2));
2661
+ console.log(
2662
+ JSON.stringify({ artifacts: data.artifacts, workspaces: groups }, null, 2)
2663
+ );
2531
2664
  return;
2532
2665
  }
2533
- if (data.artifacts.length === 0) {
2666
+ const total = data.artifacts.length + groups.reduce((sum, group) => sum + group.artifacts.length, 0);
2667
+ if (total === 0) {
2534
2668
  console.log(
2535
- "No artifacts in your account yet \u2014 publish while logged in, or claim one via its claim link."
2669
+ "No artifacts you can publish to yet \u2014 publish while logged in, or claim one via its claim link."
2536
2670
  );
2537
2671
  return;
2538
2672
  }
2539
- const plural = data.artifacts.length === 1 ? "" : "s";
2540
- console.log(`${data.artifacts.length} artifact${plural}
2673
+ if (data.artifacts.length > 0) {
2674
+ const plural = data.artifacts.length === 1 ? "" : "s";
2675
+ console.log(`${data.artifacts.length} artifact${plural}
2541
2676
  `);
2542
- for (const artifact of data.artifacts) {
2543
- printArtifact(artifact);
2677
+ for (const artifact of data.artifacts) {
2678
+ printArtifact(artifact);
2679
+ }
2680
+ }
2681
+ for (const group of groups) {
2682
+ if (group.artifacts.length === 0) {
2683
+ continue;
2684
+ }
2685
+ const plural = group.artifacts.length === 1 ? "" : "s";
2686
+ console.log(
2687
+ `${group.artifacts.length} artifact${plural} in ${group.handle}
2688
+ `
2689
+ );
2690
+ for (const artifact of group.artifacts) {
2691
+ printWorkspaceArtifact(artifact);
2692
+ }
2693
+ console.log("");
2544
2694
  }
2545
2695
  }
2546
2696
  async function listWorkspace(client, opts) {
@@ -2633,6 +2783,101 @@ function relativeTime(iso) {
2633
2783
  return iso.slice(0, 10);
2634
2784
  }
2635
2785
 
2786
+ // src/link.ts
2787
+ async function link(opts) {
2788
+ const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
2789
+ /\/$/,
2790
+ ""
2791
+ );
2792
+ const key = resolveApiKey(apiUrl2, opts.apiKey);
2793
+ if (!key) {
2794
+ throw new Error(
2795
+ `Linking needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
2796
+ );
2797
+ }
2798
+ const artifacts = await reachableArtifacts(createApiClient(apiUrl2, key));
2799
+ if (artifacts.length === 0) {
2800
+ throw new Error(
2801
+ "No artifacts you can publish to yet \u2014 publish one first, or claim one via its claim link."
2802
+ );
2803
+ }
2804
+ const selector = opts.artifact ?? opts.slug;
2805
+ let chosen;
2806
+ if (opts.artifact) {
2807
+ chosen = artifacts.find((a) => a.id === opts.artifact);
2808
+ } else if (opts.slug) {
2809
+ chosen = artifacts.find((a) => a.slug === opts.slug);
2810
+ }
2811
+ if (selector && !chosen) {
2812
+ throw new Error(
2813
+ `No artifact "${selector}" you can publish to \u2014 run \`artifact-cafe list\` to see them.`
2814
+ );
2815
+ }
2816
+ if (opts.json && !chosen) {
2817
+ console.log(JSON.stringify({ artifacts }, null, 2));
2818
+ return;
2819
+ }
2820
+ if (!chosen) {
2821
+ if (!process.stdin.isTTY) {
2822
+ throw new Error(
2823
+ "Pick an artifact with --artifact <id> or --slug <slug> (or --json to list them). Interactive selection needs a terminal."
2824
+ );
2825
+ }
2826
+ chosen = await pickArtifact(artifacts);
2827
+ }
2828
+ const dir = path8.resolve(opts.dir);
2829
+ writeConfig(dir, {
2830
+ apiUrl: apiUrl2,
2831
+ artifactId: chosen.id,
2832
+ slug: chosen.slug,
2833
+ url: chosen.url
2834
+ // No publishToken — this folder authorizes via your login, not a secret.
2835
+ });
2836
+ if (opts.json) {
2837
+ console.log(
2838
+ JSON.stringify(
2839
+ {
2840
+ artifactId: chosen.id,
2841
+ linked: true,
2842
+ slug: chosen.slug,
2843
+ url: chosen.url
2844
+ },
2845
+ null,
2846
+ 2
2847
+ )
2848
+ );
2849
+ return;
2850
+ }
2851
+ console.log(`
2852
+ \u2713 Linked ${dir} \u2192 "${chosen.title}" \u2014 ${chosen.url}`);
2853
+ console.log(
2854
+ " Owner actions (publish, comments --wait, protect) use your login.\n"
2855
+ );
2856
+ }
2857
+ function pickArtifact(artifacts) {
2858
+ console.log("Artifacts you can publish to:\n");
2859
+ artifacts.forEach((a, i) => {
2860
+ const v = a.currentVersionNumber ? `v${a.currentVersionNumber}` : "\u2014";
2861
+ const where = a.workspaceHandle ? ` \xB7 ${a.workspaceHandle}` : "";
2862
+ console.log(
2863
+ ` ${String(i + 1).padStart(2)}. ${a.title} \xB7 ${v}${where} \xB7 ${a.url}`
2864
+ );
2865
+ });
2866
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
2867
+ return new Promise((resolve2, reject) => {
2868
+ rl.question(`
2869
+ Link which? [1-${artifacts.length}]: `, (answer) => {
2870
+ rl.close();
2871
+ const n = Number(answer.trim());
2872
+ if (!(Number.isInteger(n) && n >= 1 && n <= artifacts.length)) {
2873
+ reject(new Error(`Enter a number between 1 and ${artifacts.length}.`));
2874
+ return;
2875
+ }
2876
+ resolve2(artifacts[n - 1]);
2877
+ });
2878
+ });
2879
+ }
2880
+
2636
2881
  // src/login.ts
2637
2882
  import { createInterface as createInterface3 } from "readline";
2638
2883
  function apiUrl() {
@@ -2994,7 +3239,7 @@ async function replyToThread(opts) {
2994
3239
  artifact: opts.artifact
2995
3240
  });
2996
3241
  const client = createApiClient(target.apiUrl, target.credential);
2997
- let thread = await client.replyThread(threadId, body);
3242
+ let thread = await client.replyThread(threadId, body, agentLabel(opts.as));
2998
3243
  if (opts.resolve) {
2999
3244
  thread = await client.resolveThread(threadId);
3000
3245
  }
@@ -3217,18 +3462,18 @@ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as rea
3217
3462
  import { createRequire } from "module";
3218
3463
  import { homedir as homedir2 } from "os";
3219
3464
  import path18 from "path";
3220
- import { z as z20 } from "zod";
3465
+ import { z as z22 } from "zod";
3221
3466
  var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
3222
3467
  var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
3223
3468
  var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
3224
3469
  var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
3225
3470
  var FETCH_TIMEOUT_MS = 2e3;
3226
3471
  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)
3472
+ var cacheSchema = z22.object({
3473
+ checkedAt: z22.number(),
3474
+ cliLatest: z22.string().nullable(),
3475
+ skillLatest: z22.string().nullable(),
3476
+ version: z22.literal(1)
3232
3477
  });
3233
3478
  function cliVersion() {
3234
3479
  const pkg = createRequire(import.meta.url)("../package.json");
@@ -3283,7 +3528,7 @@ async function fetchText(url) {
3283
3528
  }
3284
3529
  async function fetchLatestVersions(previous) {
3285
3530
  const [cli, skill] = await Promise.all([
3286
- fetchJson(REGISTRY_LATEST_URL).then((body) => z20.object({ version: z20.string() }).parse(body).version).catch(() => null),
3531
+ fetchJson(REGISTRY_LATEST_URL).then((body) => z22.object({ version: z22.string() }).parse(body).version).catch(() => null),
3287
3532
  fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
3288
3533
  ]);
3289
3534
  return {
@@ -3396,30 +3641,35 @@ function printWorkspace(workspace) {
3396
3641
  }
3397
3642
 
3398
3643
  // src/index.ts
3399
- function readNotesArg(argv) {
3644
+ function readStringArg(argv, flag) {
3645
+ const prefix = `${flag}=`;
3400
3646
  for (let i = 0; i < argv.length; i++) {
3401
3647
  const token = argv[i];
3402
3648
  if (token === void 0) {
3403
3649
  continue;
3404
3650
  }
3405
- if (token === "--notes") {
3651
+ if (token === flag) {
3406
3652
  return argv[i + 1];
3407
3653
  }
3408
- if (token.startsWith("--notes=")) {
3409
- return token.slice("--notes=".length);
3654
+ if (token.startsWith(prefix)) {
3655
+ return token.slice(prefix.length);
3410
3656
  }
3411
3657
  }
3412
3658
  }
3413
3659
  var publish2 = defineCommand({
3414
3660
  args: {
3415
3661
  "api-key": {
3416
- description: "Publish with an account API key (default: stored login)",
3662
+ description: "Publish with an account key or agent token (default: env or login)",
3417
3663
  type: "string"
3418
3664
  },
3419
3665
  artifact: {
3420
3666
  description: "Publish to an existing artifact ID",
3421
3667
  type: "string"
3422
3668
  },
3669
+ description: {
3670
+ description: "One-line share-card summary (default: the entry HTML's own meta description)",
3671
+ type: "string"
3672
+ },
3423
3673
  dir: {
3424
3674
  default: ".",
3425
3675
  description: "Directory to publish",
@@ -3456,11 +3706,12 @@ var publish2 = defineCommand({
3456
3706
  await publish({
3457
3707
  apiKey: args["api-key"],
3458
3708
  artifact: args.artifact,
3709
+ description: readStringArg(process.argv.slice(2), "--description"),
3459
3710
  dir: args.dir,
3460
3711
  entry: args.entry,
3461
3712
  folder: args.folder,
3462
3713
  json: Boolean(args.json),
3463
- notes: readNotesArg(process.argv.slice(2)),
3714
+ notes: readStringArg(process.argv.slice(2), "--notes"),
3464
3715
  open: !args["no-open"],
3465
3716
  password: args.password,
3466
3717
  review: args.review,
@@ -3541,7 +3792,7 @@ var list2 = defineCommand({
3541
3792
  }
3542
3793
  },
3543
3794
  meta: {
3544
- description: "List the artifacts your account owns",
3795
+ description: "List the artifacts your account can publish to",
3545
3796
  name: "list"
3546
3797
  },
3547
3798
  async run({ args }) {
@@ -3695,7 +3946,7 @@ var link2 = defineCommand({
3695
3946
  }
3696
3947
  },
3697
3948
  meta: {
3698
- description: "Link a folder to one of your artifacts (pick from your account)",
3949
+ description: "Link a folder to an artifact you can publish to (yours or a teammate's)",
3699
3950
  name: "link"
3700
3951
  },
3701
3952
  async run({ args }) {
@@ -3716,7 +3967,7 @@ var link2 = defineCommand({
3716
3967
  var comments2 = defineCommand({
3717
3968
  args: {
3718
3969
  "api-key": {
3719
- description: "Authorize with an account API key (default: stored login)",
3970
+ description: "Authorize with an account key or agent token (default: env or login)",
3720
3971
  type: "string"
3721
3972
  },
3722
3973
  artifact: {
@@ -3780,7 +4031,7 @@ var comments2 = defineCommand({
3780
4031
  var listen = defineCommand({
3781
4032
  args: {
3782
4033
  "api-key": {
3783
- description: "Authorize with an account API key (default: stored login)",
4034
+ description: "Authorize with an account key or agent token (default: env or login)",
3784
4035
  type: "string"
3785
4036
  },
3786
4037
  artifact: {
@@ -3840,7 +4091,7 @@ var listen = defineCommand({
3840
4091
  var statusCmd = defineCommand({
3841
4092
  args: {
3842
4093
  "api-key": {
3843
- description: "Authorize with an account API key (default: stored login)",
4094
+ description: "Authorize with an account key or agent token (default: env or login)",
3844
4095
  type: "string"
3845
4096
  },
3846
4097
  artifact: {
@@ -3881,7 +4132,7 @@ var statusCmd = defineCommand({
3881
4132
  var protect2 = defineCommand({
3882
4133
  args: {
3883
4134
  "api-key": {
3884
- description: "Authorize with an account API key (default: stored login)",
4135
+ description: "Authorize with an account key or agent token (default: env or login)",
3885
4136
  type: "string"
3886
4137
  },
3887
4138
  artifact: {
@@ -3920,7 +4171,7 @@ var protect2 = defineCommand({
3920
4171
  var review2 = defineCommand({
3921
4172
  args: {
3922
4173
  "api-key": {
3923
- description: "Authorize with an account API key (default: stored login)",
4174
+ description: "Authorize with an account key or agent token (default: env or login)",
3924
4175
  type: "string"
3925
4176
  },
3926
4177
  artifact: {
@@ -3961,7 +4212,7 @@ var review2 = defineCommand({
3961
4212
  var threadStatusCommand = (action) => defineCommand({
3962
4213
  args: {
3963
4214
  "api-key": {
3964
- description: "Authorize with an account API key (default: stored login)",
4215
+ description: "Authorize with an account key or agent token (default: env or login)",
3965
4216
  type: "string"
3966
4217
  },
3967
4218
  artifact: {
@@ -4010,13 +4261,17 @@ var ack = threadStatusCommand("ack");
4010
4261
  var reply = defineCommand({
4011
4262
  args: {
4012
4263
  "api-key": {
4013
- description: "Authorize with an account API key (default: stored login)",
4264
+ description: "Authorize with an account key or agent token (default: env or login)",
4014
4265
  type: "string"
4015
4266
  },
4016
4267
  artifact: {
4017
4268
  description: "Act on an artifact ID instead of the linked folder",
4018
4269
  type: "string"
4019
4270
  },
4271
+ as: {
4272
+ description: 'Who is replying, e.g. "Claude Code" (default: detected; "" to opt out)',
4273
+ type: "string"
4274
+ },
4020
4275
  body: {
4021
4276
  description: "Reply text (omit to read from stdin)",
4022
4277
  type: "string"
@@ -4046,6 +4301,7 @@ var reply = defineCommand({
4046
4301
  await replyToThread({
4047
4302
  apiKey: args["api-key"],
4048
4303
  artifact: args.artifact,
4304
+ as: args.as,
4049
4305
  body: args.body,
4050
4306
  dir: args.dir,
4051
4307
  json: Boolean(args.json),
@@ -4068,6 +4324,10 @@ var comment2 = defineCommand({
4068
4324
  description: "Act on an artifact ID instead of the linked folder",
4069
4325
  type: "string"
4070
4326
  },
4327
+ as: {
4328
+ description: 'Tool posting on your behalf, e.g. "Claude Code" (default: detected)',
4329
+ type: "string"
4330
+ },
4071
4331
  body: {
4072
4332
  description: "Comment text (omit to read from stdin)",
4073
4333
  type: "string"
@@ -4095,7 +4355,7 @@ var comment2 = defineCommand({
4095
4355
  type: "string"
4096
4356
  },
4097
4357
  via: {
4098
- description: 'Attribution label, e.g. "Claude Code"',
4358
+ description: "Older name for --as",
4099
4359
  type: "string"
4100
4360
  }
4101
4361
  },
@@ -4108,6 +4368,7 @@ var comment2 = defineCommand({
4108
4368
  await comment({
4109
4369
  apiKey: args["api-key"],
4110
4370
  artifact: args.artifact,
4371
+ as: args.as,
4111
4372
  body: args.body,
4112
4373
  dir: args.dir,
4113
4374
  json: Boolean(args.json),
@@ -4126,7 +4387,7 @@ var comment2 = defineCommand({
4126
4387
  var edit = defineCommand({
4127
4388
  args: {
4128
4389
  "api-key": {
4129
- description: "Authorize with an account API key (default: stored login)",
4390
+ description: "Authorize with an account key or agent token (default: env or login)",
4130
4391
  type: "string"
4131
4392
  },
4132
4393
  artifact: {
@@ -4169,7 +4430,7 @@ var edit = defineCommand({
4169
4430
  var del = defineCommand({
4170
4431
  args: {
4171
4432
  "api-key": {
4172
- description: "Authorize with an account API key (default: stored login)",
4433
+ description: "Authorize with an account key or agent token (default: env or login)",
4173
4434
  type: "string"
4174
4435
  },
4175
4436
  artifact: {