artifact-cafe 0.0.9 → 0.0.10

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 +482 -384
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -83,11 +83,27 @@ var artifactListResponseSchema = z.object({
83
83
  artifacts: z.array(artifactListItemSchema)
84
84
  });
85
85
 
86
+ // ../shared/src/avatars.ts
87
+ import { z as z2 } from "zod";
88
+ var AVATAR_CONTENT_TYPES = [
89
+ "image/png",
90
+ "image/jpeg",
91
+ "image/webp"
92
+ ];
93
+ 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()
97
+ });
98
+ var workspaceAvatarResponseSchema = z2.object({
99
+ logo: z2.string().nullable()
100
+ });
101
+
86
102
  // ../shared/src/comments.ts
87
- import { z as z3 } from "zod";
103
+ import { z as z4 } from "zod";
88
104
 
89
105
  // ../shared/src/publish.ts
90
- import { z as z2 } from "zod";
106
+ import { z as z3 } from "zod";
91
107
  function isSafeArtifactPath(p) {
92
108
  if (p.length === 0 || p.length > 1024) {
93
109
  return false;
@@ -100,244 +116,258 @@ function isSafeArtifactPath(p) {
100
116
  }
101
117
  return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
102
118
  }
103
- var manifestFileSchema = z2.object({
104
- hash: z2.string().regex(/^sha256_[a-f0-9]{64}$/),
105
- mimeType: z2.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
106
- path: z2.string().min(1).max(1024).refine(isSafeArtifactPath, {
119
+ var manifestFileSchema = z3.object({
120
+ hash: z3.string().regex(/^sha256_[a-f0-9]{64}$/),
121
+ mimeType: z3.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
122
+ path: z3.string().min(1).max(1024).refine(isSafeArtifactPath, {
107
123
  message: "path must be a safe relative path"
108
124
  }),
109
- size: z2.number().int().positive()
125
+ size: z3.number().int().positive()
110
126
  });
111
- var publishSourceSchema = z2.object({
112
- agent: z2.string().optional(),
113
- runId: z2.string().optional(),
114
- type: z2.literal("cli")
127
+ var publishSourceSchema = z3.object({
128
+ agent: z3.string().optional(),
129
+ runId: z3.string().optional(),
130
+ type: z3.literal("cli")
115
131
  });
116
- var createPublishSessionSchema = z2.object({
117
- artifactId: z2.string().nullable(),
118
- entryPath: z2.string().optional(),
119
- files: z2.array(manifestFileSchema).min(1).max(1e3),
132
+ var createPublishSessionSchema = z3.object({
133
+ artifactId: z3.string().nullable(),
134
+ entryPath: z3.string().optional(),
135
+ files: z3.array(manifestFileSchema).min(1).max(1e3),
120
136
  // SPEC-2 §6.4 — workspace placement is available only to authenticated
121
137
  // members; the API enforces membership and folder ownership. folderId
122
138
  // without workspaceId resolves against the account's personal workspace.
123
- folderId: z2.string().min(1).optional(),
139
+ folderId: z3.string().min(1).optional(),
140
+ // Bullet-point changelog for this version, authored by the publisher.
141
+ // Optional on every publish (incl. v1); stored immutably on the version.
142
+ notes: z3.string().max(4e3).optional(),
124
143
  source: publishSourceSchema,
125
- title: z2.string().max(200).optional(),
126
- workspaceId: z2.string().min(1).optional()
127
- });
128
- var publishSessionResponseSchema = z2.object({
129
- artifactId: z2.string(),
130
- expiresAt: z2.string(),
131
- filesToUpload: z2.array(
132
- z2.object({
133
- method: z2.literal("PUT"),
134
- path: z2.string(),
135
- uploadUrl: z2.string().url()
144
+ title: z3.string().max(200).optional(),
145
+ workspaceId: z3.string().min(1).optional()
146
+ });
147
+ var publishSessionResponseSchema = z3.object({
148
+ artifactId: z3.string(),
149
+ expiresAt: z3.string(),
150
+ filesToUpload: z3.array(
151
+ z3.object({
152
+ method: z3.literal("PUT"),
153
+ path: z3.string(),
154
+ uploadUrl: z3.string().url()
136
155
  })
137
156
  ),
138
- publishSessionId: z2.string(),
139
- skippedFiles: z2.array(z2.string()),
140
- versionId: z2.string()
157
+ publishSessionId: z3.string(),
158
+ skippedFiles: z3.array(z3.string()),
159
+ versionId: z3.string()
141
160
  });
142
- var finalizeResponseSchema = z2.object({
143
- artifactId: z2.string(),
161
+ var finalizeResponseSchema = z3.object({
162
+ artifactId: z3.string(),
144
163
  // API-key publishes create the artifact already claimed (SPEC §5.4) —
145
164
  // the CLI uses this to tell "claim URL pending" from "already yours".
146
- claimed: z2.boolean(),
165
+ claimed: z3.boolean(),
147
166
  // Returned only on first publish of a new artifact (one-time).
148
- claimUrl: z2.string().url().optional(),
149
- publishToken: z2.string().optional(),
150
- url: z2.string().url(),
151
- versionId: z2.string(),
152
- versionNumber: z2.number().int().positive()
153
- });
154
- var artifactResponseSchema = z2.object({
155
- claimed: z2.boolean(),
156
- currentVersion: z2.object({
157
- entryUrl: z2.string().url(),
158
- id: z2.string(),
159
- versionNumber: z2.number().int().positive()
167
+ claimUrl: z3.string().url().optional(),
168
+ publishToken: z3.string().optional(),
169
+ url: z3.string().url(),
170
+ versionId: z3.string(),
171
+ versionNumber: z3.number().int().positive()
172
+ });
173
+ var artifactResponseSchema = z3.object({
174
+ claimed: z3.boolean(),
175
+ currentVersion: z3.object({
176
+ entryUrl: z3.string().url(),
177
+ id: z3.string(),
178
+ versionNumber: z3.number().int().positive()
160
179
  }).nullable(),
161
180
  // SPEC-2 §6.4 — placement + visibility surface on the artifact.
162
- folderId: z2.string().nullable(),
163
- id: z2.string(),
164
- slug: z2.string(),
165
- title: z2.string(),
166
- visibility: z2.enum(["public", "unlisted", "workspace"]),
167
- workspaceId: z2.string().nullable()
181
+ folderId: z3.string().nullable(),
182
+ id: z3.string(),
183
+ slug: z3.string(),
184
+ title: z3.string(),
185
+ visibility: z3.enum(["public", "unlisted", "workspace"]),
186
+ workspaceId: z3.string().nullable()
168
187
  });
169
188
 
170
189
  // ../shared/src/comments.ts
171
- var anchorPageSchema = z3.string().max(1024).refine(isSafeArtifactPath, {
190
+ var anchorPageSchema = z4.string().max(1024).refine(isSafeArtifactPath, {
172
191
  message: "page must be a safe relative path"
173
192
  }).optional();
174
- var elementSnapshotSchema = z3.object({
175
- classes: z3.string().max(256).optional(),
176
- id: z3.string().max(256).optional(),
177
- tag: z3.string().min(1).max(128),
178
- text: z3.string().max(200).optional()
193
+ var elementSnapshotSchema = z4.object({
194
+ classes: z4.string().max(256).optional(),
195
+ id: z4.string().max(256).optional(),
196
+ tag: z4.string().min(1).max(128),
197
+ text: z4.string().max(200).optional()
179
198
  });
180
- var pointAnchorSchema = z3.object({
199
+ var pointAnchorSchema = z4.object({
181
200
  element: elementSnapshotSchema.optional(),
182
201
  // elementPath is the primary anchor; x/y are offsets within the
183
202
  // element's box, normalized 0..1 (SPEC.md §8.1).
184
- elementPath: z3.string().max(2048),
203
+ elementPath: z4.string().max(2048),
185
204
  page: anchorPageSchema,
186
- type: z3.literal("point"),
187
- viewportHeight: z3.number().int().positive().max(1e5),
188
- viewportWidth: z3.number().int().positive().max(1e5),
189
- x: z3.number().min(0).max(1),
190
- y: z3.number().min(0).max(1)
191
- });
192
- var textAnchorSchema = z3.object({
193
- elementPath: z3.string().max(2048),
194
- endOffset: z3.number().int().nonnegative().max(1e8),
205
+ type: z4.literal("point"),
206
+ viewportHeight: z4.number().int().positive().max(1e5),
207
+ viewportWidth: z4.number().int().positive().max(1e5),
208
+ x: z4.number().min(0).max(1),
209
+ y: z4.number().min(0).max(1)
210
+ });
211
+ var textAnchorSchema = z4.object({
212
+ elementPath: z4.string().max(2048),
213
+ endOffset: z4.number().int().nonnegative().max(1e8),
195
214
  page: anchorPageSchema,
196
- prefix: z3.string().max(200).optional(),
197
- quote: z3.string().min(1).max(1e3),
198
- startOffset: z3.number().int().nonnegative().max(1e8),
199
- suffix: z3.string().max(200).optional(),
200
- type: z3.literal("text")
215
+ prefix: z4.string().max(200).optional(),
216
+ quote: z4.string().min(1).max(1e3),
217
+ startOffset: z4.number().int().nonnegative().max(1e8),
218
+ suffix: z4.string().max(200).optional(),
219
+ type: z4.literal("text")
201
220
  });
202
- var pageAnchorSchema = z3.object({
221
+ var pageAnchorSchema = z4.object({
203
222
  page: anchorPageSchema,
204
- type: z3.literal("page")
223
+ type: z4.literal("page")
205
224
  });
206
- var anchorSchema = z3.discriminatedUnion("type", [
225
+ var anchorSchema = z4.discriminatedUnion("type", [
207
226
  pointAnchorSchema,
208
227
  textAnchorSchema,
209
228
  pageAnchorSchema
210
229
  ]);
211
- var displayNameSchema = z3.string().trim().min(1).max(64);
212
- var guestSchema = z3.object({
230
+ var displayNameSchema = z4.string().trim().min(1).max(64);
231
+ var guestSchema = z4.object({
213
232
  displayName: displayNameSchema,
214
- guestId: z3.string().min(16)
233
+ guestId: z4.string().min(16)
215
234
  });
216
- var viaLabelSchema = z3.string().trim().min(1).max(64);
217
- var createCommentThreadSchema = z3.object({
235
+ var viaLabelSchema = z4.string().trim().min(1).max(64);
236
+ var createCommentThreadSchema = z4.object({
218
237
  anchor: anchorSchema,
219
- body: z3.string().min(1).max(1e4),
238
+ body: z4.string().min(1).max(1e4),
220
239
  guest: guestSchema.optional(),
221
- versionId: z3.string(),
240
+ versionId: z4.string(),
222
241
  via: viaLabelSchema.optional()
223
242
  });
224
- var createCommentMessageSchema = z3.object({
225
- body: z3.string().min(1).max(1e4),
243
+ var createCommentMessageSchema = z4.object({
244
+ body: z4.string().min(1).max(1e4),
226
245
  guest: guestSchema.optional(),
227
246
  via: viaLabelSchema.optional()
228
247
  });
229
- var editCommentMessageSchema = z3.object({
230
- body: z3.string().min(1).max(1e4),
248
+ var editCommentMessageSchema = z4.object({
249
+ body: z4.string().min(1).max(1e4),
231
250
  guest: guestSchema.optional()
232
251
  });
233
- var threadActionSchema = z3.object({
252
+ var threadActionSchema = z4.object({
234
253
  guest: guestSchema.optional()
235
254
  });
236
- var REACTION_EMOJI = [
237
- "\u{1F44D}",
238
- "\u{1F44E}",
239
- "\u2764\uFE0F",
240
- "\u{1F389}",
241
- "\u{1F4A1}",
242
- "\u{1F440}",
243
- "\u{1F680}",
244
- "\u2615"
245
- ];
246
- var reactionEmojiSchema = z3.enum(REACTION_EMOJI);
247
- var toggleReactionSchema = z3.object({
255
+ var SINGLE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
256
+ var REDUNDANT_VS16 = new RegExp("(\\p{Emoji_Presentation})\\uFE0F", "gu");
257
+ function canonicalizeEmoji(s) {
258
+ return s.replace(REDUNDANT_VS16, "$1");
259
+ }
260
+ var reactionEmojiSchema = z4.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}");
261
+ var toggleReactionSchema = z4.object({
248
262
  emoji: reactionEmojiSchema,
249
263
  guest: guestSchema.optional()
250
264
  });
251
- var commentAuthorSchema = z3.object({
252
- avatarUrl: z3.string().nullable(),
253
- displayName: z3.string(),
254
- guestId: z3.string().nullable(),
255
- isViewer: z3.boolean(),
256
- kind: z3.enum(["member", "guest"]),
265
+ var commentAuthorSchema = z4.object({
266
+ avatarUrl: z4.string().nullable(),
267
+ displayName: z4.string(),
268
+ guestId: z4.string().nullable(),
269
+ isViewer: z4.boolean(),
270
+ kind: z4.enum(["member", "guest"]),
257
271
  // Optional "via" attribution (§6.4). Null for plain comments; a bounded
258
272
  // label the shell appends after the display name.
259
- via: z3.string().nullable()
273
+ via: z4.string().nullable()
260
274
  });
261
- var messageReactionSchema = z3.object({
262
- emoji: z3.string(),
263
- reactors: z3.array(commentAuthorSchema)
275
+ var messageReactionSchema = z4.object({
276
+ emoji: z4.string(),
277
+ reactors: z4.array(commentAuthorSchema)
264
278
  });
265
- var commentMessageSchema = z3.object({
279
+ var commentMessageSchema = z4.object({
266
280
  author: commentAuthorSchema,
267
281
  // Empty string for a deleted message — the body is stripped server-side
268
282
  // (§6.7.1 tombstone), never sent to the client and hidden there.
269
- body: z3.string(),
270
- createdAt: z3.string(),
283
+ body: z4.string(),
284
+ createdAt: z4.string(),
271
285
  // Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
272
286
  // the shell renders "Comment deleted" instead of the (absent) body.
273
- deleted: z3.boolean(),
287
+ deleted: z4.boolean(),
274
288
  // Last edit time (§6.7.1), or null if never edited — drives the "edited"
275
289
  // badge. No edit history is kept.
276
- editedAt: z3.string().nullable(),
277
- id: z3.string(),
290
+ editedAt: z4.string().nullable(),
291
+ id: z4.string(),
278
292
  // Defaulted, not required: a new CLI parsing an older server's
279
293
  // response must not fail on the missing key.
280
- reactions: z3.array(messageReactionSchema).default([])
281
- });
282
- var commentThreadSchema = z3.object({
294
+ reactions: z4.array(messageReactionSchema).default([])
295
+ });
296
+ var threadActorSchema = z4.object({
297
+ displayName: z4.string(),
298
+ kind: z4.enum(["member", "guest", "agent"])
299
+ });
300
+ var commentThreadSchema = z4.object({
301
+ // §6.6.1 — set while open to signal "seen, working on it"; cleared on
302
+ // reopen, kept (but superseded in display) once resolved. Defaulted so
303
+ // clients parsing an older server's response don't fail.
304
+ acknowledgedAt: z4.string().nullable().default(null),
305
+ acknowledgedBy: threadActorSchema.nullable().default(null),
283
306
  anchor: anchorSchema,
284
- artifactId: z3.string(),
285
- createdAt: z3.string(),
307
+ artifactId: z4.string(),
308
+ createdAt: z4.string(),
286
309
  createdBy: commentAuthorSchema,
287
- id: z3.string(),
288
- messages: z3.array(commentMessageSchema),
289
- status: z3.enum(["open", "resolved"]),
290
- versionId: z3.string()
291
- });
292
- var commentThreadListSchema = z3.object({
293
- threads: z3.array(commentThreadSchema)
294
- });
295
- var commentExportQuerySchema = z3.object({
296
- status: z3.enum(["open", "resolved", "all"]).default("open"),
297
- version: z3.enum(["current", "all"]).default("current")
298
- });
299
- var exportReactionSchema = z3.object({
300
- count: z3.number().int().positive(),
301
- emoji: z3.string()
302
- });
303
- var commentExportReplySchema = z3.object({
304
- author: z3.string(),
305
- authorKind: z3.enum(["member", "guest"]),
306
- body: z3.string(),
307
- createdAt: z3.string(),
310
+ id: z4.string(),
311
+ messages: z4.array(commentMessageSchema),
312
+ // Null on threads resolved before actor recording existed, and always
313
+ // null while open.
314
+ resolvedBy: threadActorSchema.nullable().default(null),
315
+ status: z4.enum(["open", "resolved"]),
316
+ versionId: z4.string()
317
+ });
318
+ var commentThreadListSchema = z4.object({
319
+ threads: z4.array(commentThreadSchema)
320
+ });
321
+ var commentExportQuerySchema = z4.object({
322
+ status: z4.enum(["open", "resolved", "all"]).default("open"),
323
+ version: z4.enum(["current", "all"]).default("current")
324
+ });
325
+ var exportReactionSchema = z4.object({
326
+ count: z4.number().int().positive(),
327
+ emoji: z4.string()
328
+ });
329
+ var commentExportReplySchema = z4.object({
330
+ author: z4.string(),
331
+ authorKind: z4.enum(["member", "guest"]),
332
+ body: z4.string(),
333
+ createdAt: z4.string(),
308
334
  // Soft-delete tombstone (§6.7.1): body is "" when true.
309
- deleted: z3.boolean(),
310
- editedAt: z3.string().nullable(),
335
+ deleted: z4.boolean(),
336
+ editedAt: z4.string().nullable(),
311
337
  // The message id — what `edit`/`delete` target (§5.1).
312
- id: z3.string(),
313
- reactions: z3.array(exportReactionSchema).optional()
338
+ id: z4.string(),
339
+ reactions: z4.array(exportReactionSchema).optional()
314
340
  });
315
- var commentExportThreadSchema = z3.object({
341
+ var commentExportThreadSchema = z4.object({
342
+ // §6.6.1 — lets a polling agent skip threads it already acknowledged
343
+ // (acknowledged threads stay in status=open exports on purpose).
344
+ acknowledgedAt: z4.string().nullable().default(null),
316
345
  anchor: anchorSchema,
317
- author: z3.string(),
318
- authorKind: z3.enum(["member", "guest"]),
319
- body: z3.string(),
320
- createdAt: z3.string(),
321
- deleted: z3.boolean(),
322
- editedAt: z3.string().nullable(),
323
- id: z3.string(),
346
+ author: z4.string(),
347
+ authorKind: z4.enum(["member", "guest"]),
348
+ body: z4.string(),
349
+ createdAt: z4.string(),
350
+ deleted: z4.boolean(),
351
+ editedAt: z4.string().nullable(),
352
+ id: z4.string(),
324
353
  // The opening message's id (distinct from the thread `id`) — `edit`/
325
354
  // `delete` operate on messages, so the CLI needs it to target the first
326
355
  // comment, not just the replies.
327
- messageId: z3.string(),
328
- reactions: z3.array(exportReactionSchema).optional(),
329
- replies: z3.array(commentExportReplySchema),
330
- status: z3.enum(["open", "resolved"]),
331
- versionNumber: z3.number().int().positive()
332
- });
333
- var commentExportSchema = z3.object({
334
- artifactId: z3.string(),
335
- comments: z3.array(commentExportThreadSchema),
356
+ messageId: z4.string(),
357
+ reactions: z4.array(exportReactionSchema).optional(),
358
+ replies: z4.array(commentExportReplySchema),
359
+ resolvedBy: threadActorSchema.nullable().default(null),
360
+ status: z4.enum(["open", "resolved"]),
361
+ versionNumber: z4.number().int().positive()
362
+ });
363
+ var commentExportSchema = z4.object({
364
+ artifactId: z4.string(),
365
+ comments: z4.array(commentExportThreadSchema),
336
366
  // The artifact's current version (null before the first finalize).
337
- versionNumber: z3.number().int().positive().nullable()
367
+ versionNumber: z4.number().int().positive().nullable()
338
368
  });
339
- var commentReadResponseSchema = z3.object({
340
- lastSeenAt: z3.string()
369
+ var commentReadResponseSchema = z4.object({
370
+ lastSeenAt: z4.string()
341
371
  });
342
372
  function elementLabel(el) {
343
373
  if (el.id) {
@@ -348,163 +378,166 @@ function elementLabel(el) {
348
378
  }
349
379
 
350
380
  // ../shared/src/errors.ts
351
- import { z as z4 } from "zod";
352
- var apiErrorSchema = z4.object({
353
- code: z4.string(),
354
- docsUrl: z4.string().url(),
355
- message: z4.string(),
356
- retryAfter: z4.number().int().positive().optional()
381
+ import { z as z5 } from "zod";
382
+ var apiErrorSchema = z5.object({
383
+ code: z5.string(),
384
+ docsUrl: z5.string().url(),
385
+ message: z5.string(),
386
+ retryAfter: z5.number().int().positive().optional()
357
387
  });
358
388
 
359
389
  // ../shared/src/feedback.ts
360
- import { z as z5 } from "zod";
361
- var feedbackRequestSchema = z5.object({
362
- email: z5.string().trim().toLowerCase().email().optional().or(z5.literal("")),
363
- message: z5.string().trim().min(1).max(4e3)
390
+ import { z as z6 } from "zod";
391
+ var feedbackRequestSchema = z6.object({
392
+ email: z6.string().trim().toLowerCase().email().optional().or(z6.literal("")),
393
+ message: z6.string().trim().min(1).max(4e3)
364
394
  });
365
- var feedbackResponseSchema = z5.object({
366
- sent: z5.literal(true)
395
+ var feedbackResponseSchema = z6.object({
396
+ sent: z6.literal(true)
367
397
  });
368
398
 
369
399
  // ../shared/src/me.ts
370
- import { z as z6 } from "zod";
371
- var ownArtifactSchema = z6.object({
372
- createdAt: z6.string(),
373
- currentVersionNumber: z6.number().nullable(),
400
+ import { z as z7 } from "zod";
401
+ var ownArtifactSchema = z7.object({
402
+ createdAt: z7.string(),
403
+ currentVersionNumber: z7.number().nullable(),
374
404
  // Owned artifacts normally never expire; non-null only while an
375
405
  // expiry is still set (e.g. an unclaimed publish surfaced here later).
376
- expiresAt: z6.string().nullable(),
406
+ expiresAt: z7.string().nullable(),
377
407
  // SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
378
408
  // both null for personal artifacts. `folderId` drives the dashboard's
379
409
  // move control and folder filter; `folderName` is the display label.
380
- folderId: z6.string().nullable(),
381
- folderName: z6.string().nullable(),
410
+ folderId: z7.string().nullable(),
411
+ folderName: z7.string().nullable(),
382
412
  // Whether a publish token is still active — revocable from the
383
413
  // dashboard (§6.18); the token itself is never returned.
384
- hasPublishToken: z6.boolean(),
385
- id: z6.string(),
386
- openThreads: z6.number(),
387
- protected: z6.boolean(),
388
- slug: z6.string(),
389
- title: z6.string(),
390
- totalViews: z6.number(),
414
+ hasPublishToken: z7.boolean(),
415
+ id: z7.string(),
416
+ openThreads: z7.number(),
417
+ protected: z7.boolean(),
418
+ slug: z7.string(),
419
+ title: z7.string(),
420
+ totalViews: z7.number(),
391
421
  // SPEC §6.20 — messages by others newer than the viewer's last-seen
392
422
  // watermark. 0 for signed-out/API-key-less contexts.
393
- unreadComments: z6.number(),
394
- updatedAt: z6.string(),
395
- url: z6.string(),
396
- versionCount: z6.number()
423
+ unreadComments: z7.number(),
424
+ updatedAt: z7.string(),
425
+ url: z7.string(),
426
+ versionCount: z7.number()
397
427
  });
398
- var ownArtifactsResponseSchema = z6.object({
399
- artifacts: z6.array(ownArtifactSchema)
428
+ var ownArtifactsResponseSchema = z7.object({
429
+ artifacts: z7.array(ownArtifactSchema)
400
430
  });
401
- var revokePublishTokenResponseSchema = z6.object({
402
- revoked: z6.boolean()
431
+ var revokePublishTokenResponseSchema = z7.object({
432
+ revoked: z7.boolean()
403
433
  });
404
434
 
405
435
  // ../shared/src/owner.ts
406
- import { z as z8 } from "zod";
436
+ import { z as z9 } from "zod";
407
437
 
408
438
  // ../shared/src/workspaces.ts
409
- import { z as z7 } from "zod";
439
+ import { z as z8 } from "zod";
410
440
  var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
411
- var workspaceRoleSchema = z7.enum(["owner", "admin", "member"]);
412
- var workspaceInviteRoleSchema = z7.enum(["admin", "member"]);
413
- var workspaceHandleSchema = z7.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
441
+ var workspaceRoleSchema = z8.enum(["owner", "admin", "member"]);
442
+ var workspaceInviteRoleSchema = z8.enum(["admin", "member"]);
443
+ var workspaceHandleSchema = z8.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
414
444
  message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
415
445
  });
416
- var createWorkspaceSchema = z7.object({
446
+ var createWorkspaceSchema = z8.object({
417
447
  handle: workspaceHandleSchema.optional(),
418
- name: z7.string().trim().min(1).max(100)
448
+ name: z8.string().trim().min(1).max(100)
419
449
  });
420
- var updateWorkspaceSchema = z7.object({
450
+ var updateWorkspaceSchema = z8.object({
421
451
  handle: workspaceHandleSchema.optional(),
422
- name: z7.string().trim().min(1).max(100).optional()
452
+ name: z8.string().trim().min(1).max(100).optional()
423
453
  }).refine((body) => body.handle !== void 0 || body.name !== void 0, {
424
454
  message: "provide at least one of name or handle"
425
455
  });
426
- var workspaceSchema = z7.object({
427
- createdAt: z7.string(),
428
- handle: z7.string(),
429
- id: z7.string(),
456
+ var workspaceSchema = z8.object({
457
+ createdAt: z8.string(),
458
+ handle: z8.string(),
459
+ id: z8.string(),
430
460
  // Auto-provisioned solo workspace, one per user: non-deletable, takes
431
461
  // no members, renameable like any workspace.
432
- isPersonal: z7.boolean(),
433
- name: z7.string(),
462
+ isPersonal: z8.boolean(),
463
+ // Uploaded logo URL, or null for the initials fallback. Set through the
464
+ // dedicated /workspaces/:id/avatar route, never the generic update.
465
+ logo: z8.string().nullable(),
466
+ name: z8.string(),
434
467
  role: workspaceRoleSchema.nullable(),
435
- updatedAt: z7.string()
468
+ updatedAt: z8.string()
436
469
  });
437
- var workspaceListResponseSchema = z7.object({
438
- workspaces: z7.array(workspaceSchema)
470
+ var workspaceListResponseSchema = z8.object({
471
+ workspaces: z8.array(workspaceSchema)
439
472
  });
440
- var deleteWorkspaceResponseSchema = z7.object({
441
- deleted: z7.literal(true)
473
+ var deleteWorkspaceResponseSchema = z8.object({
474
+ deleted: z8.literal(true)
442
475
  });
443
- var workspaceMemberSchema = z7.object({
444
- createdAt: z7.string(),
445
- email: z7.string().email(),
446
- image: z7.string().nullable(),
447
- name: z7.string(),
476
+ var workspaceMemberSchema = z8.object({
477
+ createdAt: z8.string(),
478
+ email: z8.string().email(),
479
+ image: z8.string().nullable(),
480
+ name: z8.string(),
448
481
  role: workspaceRoleSchema,
449
- userId: z7.string()
482
+ userId: z8.string()
450
483
  });
451
- var workspaceMemberListResponseSchema = z7.object({
452
- members: z7.array(workspaceMemberSchema)
484
+ var workspaceMemberListResponseSchema = z8.object({
485
+ members: z8.array(workspaceMemberSchema)
453
486
  });
454
- var updateWorkspaceMemberSchema = z7.object({
487
+ var updateWorkspaceMemberSchema = z8.object({
455
488
  role: workspaceRoleSchema
456
489
  });
457
- var removeWorkspaceMemberResponseSchema = z7.object({
458
- removed: z7.literal(true)
490
+ var removeWorkspaceMemberResponseSchema = z8.object({
491
+ removed: z8.literal(true)
459
492
  });
460
- var createWorkspaceInviteSchema = z7.object({
461
- email: z7.string().trim().toLowerCase().email().optional(),
462
- expiresAt: z7.string().datetime(),
463
- maxUses: z7.number().int().positive().optional(),
493
+ var createWorkspaceInviteSchema = z8.object({
494
+ email: z8.string().trim().toLowerCase().email().optional(),
495
+ expiresAt: z8.string().datetime(),
496
+ maxUses: z8.number().int().positive().optional(),
464
497
  role: workspaceInviteRoleSchema
465
498
  });
466
- var workspaceInviteSchema = z7.object({
467
- createdAt: z7.string(),
468
- email: z7.string().email().nullable(),
469
- expiresAt: z7.string(),
470
- id: z7.string(),
471
- maxUses: z7.number().int().positive().nullable(),
499
+ var workspaceInviteSchema = z8.object({
500
+ createdAt: z8.string(),
501
+ email: z8.string().email().nullable(),
502
+ expiresAt: z8.string(),
503
+ id: z8.string(),
504
+ maxUses: z8.number().int().positive().nullable(),
472
505
  role: workspaceInviteRoleSchema,
473
- tokenPrefix: z7.string(),
474
- useCount: z7.number().int().nonnegative()
506
+ tokenPrefix: z8.string(),
507
+ useCount: z8.number().int().nonnegative()
475
508
  });
476
509
  var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
477
- url: z7.string().url()
510
+ url: z8.string().url()
478
511
  });
479
- var workspaceInviteListResponseSchema = z7.object({
480
- invites: z7.array(workspaceInviteSchema)
512
+ var workspaceInviteListResponseSchema = z8.object({
513
+ invites: z8.array(workspaceInviteSchema)
481
514
  });
482
- var revokeWorkspaceInviteResponseSchema = z7.object({
483
- revoked: z7.literal(true)
515
+ var revokeWorkspaceInviteResponseSchema = z8.object({
516
+ revoked: z8.literal(true)
484
517
  });
485
- var acceptWorkspaceInviteSchema = z7.object({
486
- token: z7.string().min(1)
518
+ var acceptWorkspaceInviteSchema = z8.object({
519
+ token: z8.string().min(1)
487
520
  });
488
- var acceptWorkspaceInviteResponseSchema = z7.object({
521
+ var acceptWorkspaceInviteResponseSchema = z8.object({
489
522
  role: workspaceRoleSchema,
490
523
  workspace: workspaceSchema
491
524
  });
492
- var folderVisibilitySchema = z7.enum(["public", "workspace"]);
493
- var artifactVisibilitySchema = z7.enum([
525
+ var folderVisibilitySchema = z8.enum(["public", "workspace"]);
526
+ var artifactVisibilitySchema = z8.enum([
494
527
  "public",
495
528
  "unlisted",
496
529
  "workspace"
497
530
  ]);
498
- var createFolderSchema = z7.object({
499
- name: z7.string().trim().min(1).max(100),
531
+ var createFolderSchema = z8.object({
532
+ name: z8.string().trim().min(1).max(100),
500
533
  visibility: folderVisibilitySchema
501
534
  });
502
- var updateFolderSchema = z7.object({
535
+ var updateFolderSchema = z8.object({
503
536
  // With `visibility`: bulk-update the folder's artifacts to match
504
537
  // (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
505
- cascade: z7.boolean().optional(),
506
- name: z7.string().trim().min(1).max(100).optional(),
507
- sortOrder: z7.number().int().min(0).optional(),
538
+ cascade: z8.boolean().optional(),
539
+ name: z8.string().trim().min(1).max(100).optional(),
540
+ sortOrder: z8.number().int().min(0).optional(),
508
541
  visibility: folderVisibilitySchema.optional()
509
542
  }).refine(
510
543
  (body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0,
@@ -512,168 +545,170 @@ var updateFolderSchema = z7.object({
512
545
  ).refine((body) => !body.cascade || body.visibility !== void 0, {
513
546
  message: "cascade requires visibility"
514
547
  });
515
- var folderSchema = z7.object({
516
- createdAt: z7.string(),
517
- id: z7.string(),
518
- name: z7.string(),
548
+ var folderSchema = z8.object({
549
+ createdAt: z8.string(),
550
+ id: z8.string(),
551
+ name: z8.string(),
519
552
  // Permalink segment for the public folder page (/w/{handle}/f/{slug}).
520
553
  // Server-derived from the name; renames regenerate it.
521
- slug: z7.string(),
522
- sortOrder: z7.number().int(),
523
- updatedAt: z7.string(),
554
+ slug: z8.string(),
555
+ sortOrder: z8.number().int(),
556
+ updatedAt: z8.string(),
524
557
  visibility: folderVisibilitySchema,
525
- workspaceId: z7.string()
558
+ workspaceId: z8.string()
526
559
  });
527
- var folderListResponseSchema = z7.object({
528
- folders: z7.array(folderSchema)
560
+ var folderListResponseSchema = z8.object({
561
+ folders: z8.array(folderSchema)
529
562
  });
530
- var deleteFolderResponseSchema = z7.object({
531
- deleted: z7.literal(true)
563
+ var deleteFolderResponseSchema = z8.object({
564
+ deleted: z8.literal(true)
532
565
  });
533
- var workspaceArtifactSchema = z7.object({
534
- createdAt: z7.string(),
535
- currentVersionNumber: z7.number().int().nullable(),
536
- folderId: z7.string().nullable(),
537
- id: z7.string(),
538
- openCommentCount: z7.number().int().nonnegative(),
539
- slug: z7.string(),
540
- title: z7.string(),
541
- updatedAt: z7.string(),
542
- url: z7.string().url(),
543
- versionCount: z7.number().int().nonnegative(),
566
+ var workspaceArtifactSchema = z8.object({
567
+ createdAt: z8.string(),
568
+ currentVersionNumber: z8.number().int().nullable(),
569
+ folderId: z8.string().nullable(),
570
+ id: z8.string(),
571
+ openCommentCount: z8.number().int().nonnegative(),
572
+ slug: z8.string(),
573
+ title: z8.string(),
574
+ updatedAt: z8.string(),
575
+ url: z8.string().url(),
576
+ versionCount: z8.number().int().nonnegative(),
544
577
  visibility: artifactVisibilitySchema
545
578
  });
546
- var workspaceArtifactListResponseSchema = z7.object({
547
- artifacts: z7.array(workspaceArtifactSchema)
579
+ var workspaceArtifactListResponseSchema = z8.object({
580
+ artifacts: z8.array(workspaceArtifactSchema)
548
581
  });
549
582
 
550
583
  // ../shared/src/owner.ts
551
584
  var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
552
- var claimResponseSchema = z8.object({
553
- artifactId: z8.string(),
554
- slug: z8.string(),
555
- title: z8.string(),
556
- url: z8.string().url()
585
+ var claimResponseSchema = z9.object({
586
+ artifactId: z9.string(),
587
+ slug: z9.string(),
588
+ title: z9.string(),
589
+ url: z9.string().url()
557
590
  });
558
- var updateArtifactSchema = z8.object({
591
+ var updateArtifactSchema = z9.object({
559
592
  // SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
560
593
  // folder; `workspaceId: null` moves the artifact out of its workspace
561
594
  // (§5.5 ownership transfer).
562
- folderId: z8.string().min(1).nullable().optional(),
563
- slug: z8.string().min(3).max(63).regex(SLUG_PATTERN, {
595
+ folderId: z9.string().min(1).nullable().optional(),
596
+ slug: z9.string().min(3).max(63).regex(SLUG_PATTERN, {
564
597
  message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
565
598
  }).optional(),
566
- title: z8.string().min(1).max(200).optional(),
599
+ title: z9.string().min(1).max(200).optional(),
567
600
  visibility: artifactVisibilitySchema.optional(),
568
- workspaceId: z8.string().min(1).nullable().optional()
601
+ workspaceId: z9.string().min(1).nullable().optional()
569
602
  }).refine(
570
603
  (body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0,
571
604
  {
572
605
  message: "provide at least one of title, slug, visibility, folderId, workspaceId"
573
606
  }
574
607
  );
575
- var deleteArtifactResponseSchema = z8.object({
576
- deleted: z8.literal(true)
608
+ var deleteArtifactResponseSchema = z9.object({
609
+ deleted: z9.literal(true)
577
610
  });
578
611
 
579
612
  // ../shared/src/presence.ts
580
- import { z as z9 } from "zod";
613
+ import { z as z10 } from "zod";
581
614
  var AGENT_PRESENCE = {
582
615
  heartbeatSeconds: 15,
583
616
  shellPollSeconds: 15,
584
617
  ttlSeconds: 45
585
618
  };
586
- var agentHeartbeatResponseSchema = z9.object({
587
- listening: z9.literal(true),
588
- ttlSeconds: z9.number().int().positive()
619
+ var agentHeartbeatResponseSchema = z10.object({
620
+ listening: z10.literal(true),
621
+ ttlSeconds: z10.number().int().positive()
589
622
  });
590
- var agentPresenceSchema = z9.object({
591
- listening: z9.boolean(),
592
- versionNumber: z9.number().int().positive().nullable()
623
+ var agentPresenceSchema = z10.object({
624
+ listening: z10.boolean(),
625
+ versionNumber: z10.number().int().positive().nullable()
593
626
  });
594
627
 
595
628
  // ../shared/src/protection.ts
596
- import { z as z10 } from "zod";
629
+ import { z as z11 } from "zod";
597
630
  var PASSWORD_MIN = 6;
598
631
  var PASSWORD_MAX = 128;
599
- var gatePasswordSchema = z10.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
600
- var setProtectionSchema = z10.object({
632
+ var gatePasswordSchema = z11.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
633
+ var setProtectionSchema = z11.object({
601
634
  password: gatePasswordSchema.nullable()
602
635
  });
603
- var protectionResponseSchema = z10.object({
604
- protected: z10.boolean()
636
+ var protectionResponseSchema = z11.object({
637
+ protected: z11.boolean()
605
638
  });
606
- var unlockSchema = z10.object({
607
- password: z10.string().min(1).max(1024)
639
+ var unlockSchema = z11.object({
640
+ password: z11.string().min(1).max(1024)
608
641
  });
609
- var unlockResponseSchema = z10.object({
610
- unlocked: z10.literal(true)
642
+ var unlockResponseSchema = z11.object({
643
+ unlocked: z11.literal(true)
611
644
  });
612
645
 
613
646
  // ../shared/src/report.ts
614
- import { z as z11 } from "zod";
615
- var reportRequestSchema = z11.object({
616
- reason: z11.string().max(1e3).optional()
647
+ import { z as z12 } from "zod";
648
+ var reportRequestSchema = z12.object({
649
+ reason: z12.string().max(1e3).optional()
617
650
  });
618
- var reportResponseSchema = z11.object({
619
- reported: z11.literal(true)
651
+ var reportResponseSchema = z12.object({
652
+ reported: z12.literal(true)
620
653
  });
621
654
 
622
655
  // ../shared/src/stats.ts
623
- import { z as z12 } from "zod";
624
- var avgViewSecondsSchema = z12.number().int().nonnegative().nullable();
625
- var versionViewStatsSchema = z12.object({
656
+ import { z as z13 } from "zod";
657
+ var avgViewSecondsSchema = z13.number().int().nonnegative().nullable();
658
+ var versionViewStatsSchema = z13.object({
626
659
  avgViewSeconds: avgViewSecondsSchema,
627
660
  // Views from inside the review shell's iframe vs. direct visits to
628
661
  // the render URL. views = embeddedViews + directViews.
629
- directViews: z12.number().int().nonnegative(),
630
- embeddedViews: z12.number().int().nonnegative(),
662
+ directViews: z13.number().int().nonnegative(),
663
+ embeddedViews: z13.number().int().nonnegative(),
631
664
  // Distinct hashed viewer IPs across the version's lifetime.
632
- uniqueViewers: z12.number().int().nonnegative(),
633
- versionNumber: z12.number().int().positive(),
634
- views: z12.number().int().nonnegative()
665
+ uniqueViewers: z13.number().int().nonnegative(),
666
+ versionNumber: z13.number().int().positive(),
667
+ views: z13.number().int().nonnegative()
635
668
  });
636
- var dailyViewsSchema = z12.object({
669
+ var dailyViewsSchema = z13.object({
637
670
  avgViewSeconds: avgViewSecondsSchema,
638
671
  // UTC day, YYYY-MM-DD.
639
- day: z12.string(),
640
- uniqueViewers: z12.number().int().nonnegative(),
641
- views: z12.number().int().nonnegative()
672
+ day: z13.string(),
673
+ uniqueViewers: z13.number().int().nonnegative(),
674
+ views: z13.number().int().nonnegative()
642
675
  });
643
- var artifactStatsResponseSchema = z12.object({
676
+ var artifactStatsResponseSchema = z13.object({
644
677
  avgViewSeconds: avgViewSecondsSchema,
645
- daily: z12.array(dailyViewsSchema),
678
+ daily: z13.array(dailyViewsSchema),
646
679
  // Distinct hashed viewer IPs across all versions and days.
647
- totalUniqueViewers: z12.number().int().nonnegative(),
648
- totalViews: z12.number().int().nonnegative(),
649
- versions: z12.array(versionViewStatsSchema)
680
+ totalUniqueViewers: z13.number().int().nonnegative(),
681
+ totalViews: z13.number().int().nonnegative(),
682
+ versions: z13.array(versionViewStatsSchema)
650
683
  });
651
684
 
652
685
  // ../shared/src/versions.ts
653
- import { z as z13 } from "zod";
654
- var versionSourceSchema = z13.object({
655
- agentName: z13.string().nullable(),
656
- type: z13.string()
686
+ import { z as z14 } from "zod";
687
+ var versionSourceSchema = z14.object({
688
+ agentName: z14.string().nullable(),
689
+ type: z14.string()
657
690
  });
658
- var versionChangesSchema = z13.object({
659
- added: z13.array(z13.string()),
660
- changed: z13.array(z13.string()),
661
- removed: z13.array(z13.string())
691
+ var versionChangesSchema = z14.object({
692
+ added: z14.array(z14.string()),
693
+ changed: z14.array(z14.string()),
694
+ removed: z14.array(z14.string())
662
695
  });
663
- var versionSummarySchema = z13.object({
696
+ var versionSummarySchema = z14.object({
664
697
  // Absent for the first version — there is no previous version to diff against.
665
698
  changes: versionChangesSchema.nullable(),
666
- createdAt: z13.string(),
667
- entryUrl: z13.string().url(),
668
- fileCount: z13.number().int().nonnegative(),
669
- id: z13.string(),
670
- isCurrent: z13.boolean(),
699
+ createdAt: z14.string(),
700
+ entryUrl: z14.string().url(),
701
+ fileCount: z14.number().int().nonnegative(),
702
+ id: z14.string(),
703
+ isCurrent: z14.boolean(),
704
+ // Author-supplied bullet-point changelog; null when the publish omitted it.
705
+ notes: z14.string().nullable(),
671
706
  source: versionSourceSchema,
672
- totalSizeBytes: z13.number().int().nonnegative(),
673
- versionNumber: z13.number().int().positive()
707
+ totalSizeBytes: z14.number().int().nonnegative(),
708
+ versionNumber: z14.number().int().positive()
674
709
  });
675
- var versionListResponseSchema = z13.object({
676
- versions: z13.array(versionSummarySchema)
710
+ var versionListResponseSchema = z14.object({
711
+ versions: z14.array(versionSummarySchema)
677
712
  });
678
713
 
679
714
  // src/api.ts
@@ -729,6 +764,16 @@ function createApiClient(apiUrl2, publishToken) {
729
764
  return {
730
765
  // SPEC §6.19 — mark the agent as actively waiting for feedback.
731
766
  // Authorized by the publish token this client was created with.
767
+ // SPEC §6.6.1 — mark an open thread acknowledged ("seen, working on
768
+ // it") without resolving it. Publish token or account key only —
769
+ // the server rejects guests.
770
+ async acknowledgeThread(threadId) {
771
+ return request(
772
+ `${base}/api/v1/comment-threads/${threadId}/acknowledge`,
773
+ { body: "{}", headers, method: "POST" },
774
+ (d) => commentThreadSchema.parse(d)
775
+ );
776
+ },
732
777
  async agentHeartbeat(artifactId) {
733
778
  return request(
734
779
  `${base}/api/v1/artifacts/${artifactId}/agent-presence`,
@@ -894,17 +939,17 @@ import {
894
939
  writeFileSync as writeFileSync2
895
940
  } from "fs";
896
941
  import path2 from "path";
897
- import { z as z15 } from "zod";
942
+ import { z as z16 } from "zod";
898
943
 
899
944
  // src/credentials.ts
900
945
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
901
946
  import { homedir } from "os";
902
947
  import path from "path";
903
- import { z as z14 } from "zod";
948
+ import { z as z15 } from "zod";
904
949
  var DEFAULT_API_URL = "https://artifact.cafe";
905
- var credentialsSchema = z14.object({
906
- keys: z14.record(z14.string(), z14.string()),
907
- version: z14.literal(1)
950
+ var credentialsSchema = z15.object({
951
+ keys: z15.record(z15.string(), z15.string()),
952
+ version: z15.literal(1)
908
953
  });
909
954
  function credentialsPath() {
910
955
  return path.join(homedir(), ".artifactcafe", "credentials");
@@ -954,24 +999,24 @@ function resolveApiKey(apiUrl2, flag) {
954
999
  }
955
1000
 
956
1001
  // src/config.ts
957
- var configSchema = z15.object({
958
- apiUrl: z15.string(),
959
- artifactId: z15.string(),
960
- folder: z15.string().optional(),
961
- folderId: z15.string().optional(),
1002
+ var configSchema = z16.object({
1003
+ apiUrl: z16.string(),
1004
+ artifactId: z16.string(),
1005
+ folder: z16.string().optional(),
1006
+ folderId: z16.string().optional(),
962
1007
  // Optional: a folder linked by `link` to an artifact you own carries no
963
1008
  // token — owner actions authorize via your stored login instead. A first
964
1009
  // publish still writes the anonymous-mode token here.
965
- publishToken: z15.string().optional(),
966
- slug: z15.string(),
967
- url: z15.string(),
968
- workspace: z15.string().optional(),
969
- workspaceId: z15.string().optional(),
970
- workspaceKind: z15.enum(["personal", "workspace"]).optional()
971
- });
972
- var projectConfigSchema = z15.object({
973
- folder: z15.string().trim().min(1).optional(),
974
- workspace: z15.string().trim().min(1)
1010
+ publishToken: z16.string().optional(),
1011
+ slug: z16.string(),
1012
+ url: z16.string(),
1013
+ workspace: z16.string().optional(),
1014
+ workspaceId: z16.string().optional(),
1015
+ workspaceKind: z16.enum(["personal", "workspace"]).optional()
1016
+ });
1017
+ var projectConfigSchema = z16.object({
1018
+ folder: z16.string().trim().min(1).optional(),
1019
+ workspace: z16.string().trim().min(1)
975
1020
  }).strict();
976
1021
  var CONFIG_DIR = ".artifactcafe";
977
1022
  var CONFIG_FILE = "config.json";
@@ -1007,7 +1052,7 @@ function readProjectConfig(dir) {
1007
1052
  JSON.parse(readFileSync2(file, "utf8"))
1008
1053
  );
1009
1054
  } catch (error) {
1010
- const message = error instanceof z15.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1055
+ const message = error instanceof z16.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1011
1056
  throw new Error(`Invalid ${file}: ${message}.`);
1012
1057
  }
1013
1058
  }
@@ -1356,8 +1401,14 @@ function printThread(t) {
1356
1401
  const marker = t.status === "resolved" ? "\u2713" : "\u25CF";
1357
1402
  const date = t.createdAt.slice(0, 10);
1358
1403
  const edited = t.editedAt && !t.deleted ? " \xB7 edited" : "";
1404
+ let state = "";
1405
+ if (t.status === "resolved" && t.resolvedBy) {
1406
+ state = ` \xB7 resolved by ${t.resolvedBy.displayName}`;
1407
+ } else if (t.status === "open" && t.acknowledgedAt) {
1408
+ state = " \xB7 acknowledged";
1409
+ }
1359
1410
  console.log(
1360
- `${marker} ${t.author} \xB7 v${t.versionNumber} \xB7 ${date} \xB7 ${t.id}${edited}`
1411
+ `${marker} ${t.author} \xB7 v${t.versionNumber} \xB7 ${date} \xB7 ${t.id}${edited}${state}`
1361
1412
  );
1362
1413
  if (t.anchor.type === "text") {
1363
1414
  console.log(` \u21B3 "${t.anchor.quote}"`);
@@ -1558,6 +1609,9 @@ async function publish(opts) {
1558
1609
  entryPath,
1559
1610
  files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
1560
1611
  ...placement.folderId ? { folderId: placement.folderId } : {},
1612
+ // Agents often pass a double-quoted string where "\n" is a literal
1613
+ // backslash-n; turn those back into real newlines so bullets survive.
1614
+ ...opts.notes?.trim() ? { notes: opts.notes.replace(/\\n/g, "\n").trim() } : {},
1561
1615
  source: {
1562
1616
  agent: process.env.ARTIFACT_CAFE_AGENT,
1563
1617
  runId: process.env.ARTIFACT_CAFE_RUN_ID,
@@ -2442,10 +2496,29 @@ async function changeThreadStatus(opts) {
2442
2496
  artifact: opts.artifact
2443
2497
  });
2444
2498
  const client = createApiClient(target.apiUrl, target.credential);
2445
- const thread = opts.action === "resolve" ? await client.resolveThread(threadId) : await client.reopenThread(threadId);
2499
+ const call = {
2500
+ ack: () => client.acknowledgeThread(threadId),
2501
+ reopen: () => client.reopenThread(threadId),
2502
+ resolve: () => client.resolveThread(threadId)
2503
+ }[opts.action];
2504
+ const thread = await call();
2446
2505
  if (opts.json) {
2447
2506
  console.log(
2448
- JSON.stringify({ id: thread.id, status: thread.status }, null, 2)
2507
+ JSON.stringify(
2508
+ {
2509
+ acknowledgedAt: thread.acknowledgedAt,
2510
+ id: thread.id,
2511
+ status: thread.status
2512
+ },
2513
+ null,
2514
+ 2
2515
+ )
2516
+ );
2517
+ return;
2518
+ }
2519
+ if (opts.action === "ack") {
2520
+ console.log(
2521
+ `\u2713 Acknowledged ${thread.id} \u2014 reviewers now see it as being handled.`
2449
2522
  );
2450
2523
  return;
2451
2524
  }
@@ -2458,18 +2531,18 @@ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as rea
2458
2531
  import { createRequire } from "module";
2459
2532
  import { homedir as homedir2 } from "os";
2460
2533
  import path14 from "path";
2461
- import { z as z16 } from "zod";
2534
+ import { z as z17 } from "zod";
2462
2535
  var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
2463
2536
  var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
2464
2537
  var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
2465
2538
  var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
2466
2539
  var FETCH_TIMEOUT_MS = 2e3;
2467
2540
  var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
2468
- var cacheSchema = z16.object({
2469
- checkedAt: z16.number(),
2470
- cliLatest: z16.string().nullable(),
2471
- skillLatest: z16.string().nullable(),
2472
- version: z16.literal(1)
2541
+ var cacheSchema = z17.object({
2542
+ checkedAt: z17.number(),
2543
+ cliLatest: z17.string().nullable(),
2544
+ skillLatest: z17.string().nullable(),
2545
+ version: z17.literal(1)
2473
2546
  });
2474
2547
  function cliVersion() {
2475
2548
  const pkg = createRequire(import.meta.url)("../package.json");
@@ -2524,7 +2597,7 @@ async function fetchText(url) {
2524
2597
  }
2525
2598
  async function fetchLatestVersions(previous) {
2526
2599
  const [cli, skill] = await Promise.all([
2527
- fetchJson(REGISTRY_LATEST_URL).then((body) => z16.object({ version: z16.string() }).parse(body).version).catch(() => null),
2600
+ fetchJson(REGISTRY_LATEST_URL).then((body) => z17.object({ version: z17.string() }).parse(body).version).catch(() => null),
2528
2601
  fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
2529
2602
  ]);
2530
2603
  return {
@@ -2637,6 +2710,20 @@ function printWorkspace(workspace) {
2637
2710
  }
2638
2711
 
2639
2712
  // src/index.ts
2713
+ function readNotesArg(argv) {
2714
+ for (let i = 0; i < argv.length; i++) {
2715
+ const token = argv[i];
2716
+ if (token === void 0) {
2717
+ continue;
2718
+ }
2719
+ if (token === "--notes") {
2720
+ return argv[i + 1];
2721
+ }
2722
+ if (token.startsWith("--notes=")) {
2723
+ return token.slice("--notes=".length);
2724
+ }
2725
+ }
2726
+ }
2640
2727
  var publish2 = defineCommand({
2641
2728
  args: {
2642
2729
  "api-key": {
@@ -2659,6 +2746,10 @@ var publish2 = defineCommand({
2659
2746
  },
2660
2747
  json: { description: "Machine-readable output", type: "boolean" },
2661
2748
  "no-open": { description: "Don't open the review URL", type: "boolean" },
2749
+ notes: {
2750
+ description: "Version notes \u2014 bullet-point changelog of what changed (newline-separated)",
2751
+ type: "string"
2752
+ },
2662
2753
  password: {
2663
2754
  description: "Password-protect the artifact (min 6 chars)",
2664
2755
  type: "string"
@@ -2679,6 +2770,7 @@ var publish2 = defineCommand({
2679
2770
  entry: args.entry,
2680
2771
  folder: args.folder,
2681
2772
  json: Boolean(args.json),
2773
+ notes: readNotesArg(process.argv.slice(2)),
2682
2774
  open: !args["no-open"],
2683
2775
  password: args.password,
2684
2776
  title: args.title,
@@ -3046,7 +3138,11 @@ var threadStatusCommand = (action) => defineCommand({
3046
3138
  }
3047
3139
  },
3048
3140
  meta: {
3049
- description: action === "resolve" ? "Mark a comment thread resolved" : "Reopen a resolved comment thread",
3141
+ description: {
3142
+ ack: "Mark a thread acknowledged (seen, working on it)",
3143
+ reopen: "Reopen a resolved comment thread",
3144
+ resolve: "Mark a comment thread resolved"
3145
+ }[action],
3050
3146
  name: action
3051
3147
  },
3052
3148
  async run({ args }) {
@@ -3067,6 +3163,7 @@ var threadStatusCommand = (action) => defineCommand({
3067
3163
  });
3068
3164
  var resolve = threadStatusCommand("resolve");
3069
3165
  var reopen = threadStatusCommand("reopen");
3166
+ var ack = threadStatusCommand("ack");
3070
3167
  var reply = defineCommand({
3071
3168
  args: {
3072
3169
  "api-key": {
@@ -3333,6 +3430,7 @@ var main = defineCommand({
3333
3430
  await showUsage(cmd);
3334
3431
  },
3335
3432
  subCommands: {
3433
+ ack,
3336
3434
  comment: comment2,
3337
3435
  comments: comments2,
3338
3436
  dashboard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artifact-cafe",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Publish AI-generated artifacts for review on artifact.cafe",
5
5
  "type": "module",
6
6
  "bin": {