artifact-cafe 0.0.11 → 0.0.13

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 +961 -475
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -100,10 +100,22 @@ var workspaceAvatarResponseSchema = z2.object({
100
100
  });
101
101
 
102
102
  // ../shared/src/comments.ts
103
- import { z as z4 } from "zod";
103
+ import { z as z5 } from "zod";
104
104
 
105
105
  // ../shared/src/publish.ts
106
+ import { z as z4 } from "zod";
107
+
108
+ // ../shared/src/review-mode.ts
106
109
  import { z as z3 } from "zod";
110
+ var reviewModeSchema = z3.enum(["open", "readonly", "off"]);
111
+ var setReviewModeSchema = z3.object({
112
+ mode: reviewModeSchema
113
+ });
114
+ var reviewModeResponseSchema = z3.object({
115
+ mode: reviewModeSchema
116
+ });
117
+
118
+ // ../shared/src/publish.ts
107
119
  function isSafeArtifactPath(p) {
108
120
  if (p.length === 0 || p.length > 1024) {
109
121
  return false;
@@ -116,140 +128,148 @@ function isSafeArtifactPath(p) {
116
128
  }
117
129
  return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
118
130
  }
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, {
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, {
123
135
  message: "path must be a safe relative path"
124
136
  }),
125
- size: z3.number().int().positive()
137
+ size: z4.number().int().positive()
126
138
  });
127
- var publishSourceSchema = z3.object({
128
- agent: z3.string().optional(),
129
- runId: z3.string().optional(),
130
- type: z3.literal("cli")
139
+ var publishSourceSchema = z4.object({
140
+ agent: z4.string().optional(),
141
+ runId: z4.string().optional(),
142
+ type: z4.literal("cli")
131
143
  });
132
- var createPublishSessionSchema = z3.object({
133
- artifactId: z3.string().nullable(),
134
- entryPath: z3.string().optional(),
135
- files: z3.array(manifestFileSchema).min(1).max(1e3),
144
+ var createPublishSessionSchema = z4.object({
145
+ artifactId: z4.string().nullable(),
146
+ entryPath: z4.string().optional(),
147
+ files: z4.array(manifestFileSchema).min(1).max(1e3),
136
148
  // SPEC-2 §6.4 — workspace placement is available only to authenticated
137
149
  // members; the API enforces membership and folder ownership. folderId
138
150
  // without workspaceId resolves against the account's personal workspace.
139
- folderId: z3.string().min(1).optional(),
151
+ folderId: z4.string().min(1).optional(),
140
152
  // Bullet-point changelog for this version, authored by the publisher.
141
153
  // Optional on every publish (incl. v1); stored immutably on the version.
142
- notes: z3.string().max(4e3).optional(),
154
+ notes: z4.string().max(4e3).optional(),
143
155
  source: publishSourceSchema,
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()
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()
155
167
  })
156
168
  ),
157
- publishSessionId: z3.string(),
158
- skippedFiles: z3.array(z3.string()),
159
- versionId: z3.string()
169
+ publishSessionId: z4.string(),
170
+ skippedFiles: z4.array(z4.string()),
171
+ versionId: z4.string()
160
172
  });
161
- var finalizeResponseSchema = z3.object({
162
- artifactId: z3.string(),
173
+ var finalizeResponseSchema = z4.object({
174
+ artifactId: z4.string(),
163
175
  // API-key publishes create the artifact already claimed (SPEC §5.4) —
164
176
  // the CLI uses this to tell "claim URL pending" from "already yours".
165
- claimed: z3.boolean(),
177
+ claimed: z4.boolean(),
166
178
  // Returned only on first publish of a new artifact (one-time).
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()
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(),
184
+ // SPEC-2 §5.2 — who can open the review URL. A publish into a folder
185
+ // inherits that folder's visibility, so this is the only place the CLI
186
+ // learns that a link it just printed is members-only. Optional so a new
187
+ // CLI against an older API degrades to "unknown" instead of failing the
188
+ // 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()
179
197
  }).nullable(),
180
198
  // SPEC-2 §6.4 — placement + visibility surface on the artifact.
181
- folderId: z3.string().nullable(),
182
- id: z3.string(),
183
- slug: z3.string(),
184
- title: z3.string(),
185
- visibility: z3.enum(["public", "unlisted", "workspace", "private"]),
186
- workspaceId: z3.string().nullable()
199
+ folderId: z4.string().nullable(),
200
+ id: z4.string(),
201
+ // SPEC-2 §5.8 — what a viewer may do once in (open / readonly / off).
202
+ reviewMode: reviewModeSchema,
203
+ slug: z4.string(),
204
+ title: z4.string(),
205
+ visibility: z4.enum(["public", "unlisted", "workspace", "private"]),
206
+ workspaceId: z4.string().nullable()
187
207
  });
188
208
 
189
209
  // ../shared/src/comments.ts
190
- var anchorPageSchema = z4.string().max(1024).refine(isSafeArtifactPath, {
210
+ var anchorPageSchema = z5.string().max(1024).refine(isSafeArtifactPath, {
191
211
  message: "page must be a safe relative path"
192
212
  }).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()
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()
198
218
  });
199
- var pointAnchorSchema = z4.object({
219
+ var pointAnchorSchema = z5.object({
200
220
  element: elementSnapshotSchema.optional(),
201
221
  // elementPath is the primary anchor; x/y are offsets within the
202
222
  // element's box, normalized 0..1 (SPEC.md §8.1).
203
- elementPath: z4.string().max(2048),
223
+ elementPath: z5.string().max(2048),
204
224
  page: anchorPageSchema,
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),
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),
214
234
  page: anchorPageSchema,
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")
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")
220
240
  });
221
- var pageAnchorSchema = z4.object({
241
+ var pageAnchorSchema = z5.object({
222
242
  page: anchorPageSchema,
223
- type: z4.literal("page")
243
+ type: z5.literal("page")
224
244
  });
225
- var anchorSchema = z4.discriminatedUnion("type", [
245
+ var anchorSchema = z5.discriminatedUnion("type", [
226
246
  pointAnchorSchema,
227
247
  textAnchorSchema,
228
248
  pageAnchorSchema
229
249
  ]);
230
- var displayNameSchema = z4.string().trim().min(1).max(64);
231
- var guestSchema = z4.object({
250
+ var displayNameSchema = z5.string().trim().min(1).max(64);
251
+ var guestSchema = z5.object({
232
252
  displayName: displayNameSchema,
233
- guestId: z4.string().min(16)
253
+ guestId: z5.string().min(16)
234
254
  });
235
- var viaLabelSchema = z4.string().trim().min(1).max(64);
236
- var createCommentThreadSchema = z4.object({
255
+ var viaLabelSchema = z5.string().trim().min(1).max(64);
256
+ var createCommentThreadSchema = z5.object({
237
257
  anchor: anchorSchema,
238
- body: z4.string().min(1).max(1e4),
258
+ body: z5.string().min(1).max(1e4),
239
259
  guest: guestSchema.optional(),
240
- versionId: z4.string(),
260
+ versionId: z5.string(),
241
261
  via: viaLabelSchema.optional()
242
262
  });
243
- var createCommentMessageSchema = z4.object({
244
- body: z4.string().min(1).max(1e4),
263
+ var createCommentMessageSchema = z5.object({
264
+ body: z5.string().min(1).max(1e4),
245
265
  guest: guestSchema.optional(),
246
266
  via: viaLabelSchema.optional()
247
267
  });
248
- var editCommentMessageSchema = z4.object({
249
- body: z4.string().min(1).max(1e4),
268
+ var editCommentMessageSchema = z5.object({
269
+ body: z5.string().min(1).max(1e4),
250
270
  guest: guestSchema.optional()
251
271
  });
252
- var threadActionSchema = z4.object({
272
+ var threadActionSchema = z5.object({
253
273
  guest: guestSchema.optional()
254
274
  });
255
275
  var SINGLE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
@@ -257,117 +277,117 @@ var REDUNDANT_VS16 = new RegExp("(\\p{Emoji_Presentation})\\uFE0F", "gu");
257
277
  function canonicalizeEmoji(s) {
258
278
  return s.replace(REDUNDANT_VS16, "$1");
259
279
  }
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({
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({
262
282
  emoji: reactionEmojiSchema,
263
283
  guest: guestSchema.optional()
264
284
  });
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"]),
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"]),
271
291
  // Optional "via" attribution (§6.4). Null for plain comments; a bounded
272
292
  // label the shell appends after the display name.
273
- via: z4.string().nullable()
293
+ via: z5.string().nullable()
274
294
  });
275
- var messageReactionSchema = z4.object({
276
- emoji: z4.string(),
277
- reactors: z4.array(commentAuthorSchema)
295
+ var messageReactionSchema = z5.object({
296
+ emoji: z5.string(),
297
+ reactors: z5.array(commentAuthorSchema)
278
298
  });
279
- var commentMessageSchema = z4.object({
299
+ var commentMessageSchema = z5.object({
280
300
  author: commentAuthorSchema,
281
301
  // Empty string for a deleted message — the body is stripped server-side
282
302
  // (§6.7.1 tombstone), never sent to the client and hidden there.
283
- body: z4.string(),
284
- createdAt: z4.string(),
303
+ body: z5.string(),
304
+ createdAt: z5.string(),
285
305
  // Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
286
306
  // the shell renders "Comment deleted" instead of the (absent) body.
287
- deleted: z4.boolean(),
307
+ deleted: z5.boolean(),
288
308
  // Last edit time (§6.7.1), or null if never edited — drives the "edited"
289
309
  // badge. No edit history is kept.
290
- editedAt: z4.string().nullable(),
291
- id: z4.string(),
310
+ editedAt: z5.string().nullable(),
311
+ id: z5.string(),
292
312
  // Defaulted, not required: a new CLI parsing an older server's
293
313
  // response must not fail on the missing key.
294
- reactions: z4.array(messageReactionSchema).default([])
314
+ reactions: z5.array(messageReactionSchema).default([])
295
315
  });
296
- var threadActorSchema = z4.object({
297
- displayName: z4.string(),
298
- kind: z4.enum(["member", "guest", "agent"])
316
+ var threadActorSchema = z5.object({
317
+ displayName: z5.string(),
318
+ kind: z5.enum(["member", "guest", "agent"])
299
319
  });
300
- var commentThreadSchema = z4.object({
320
+ var commentThreadSchema = z5.object({
301
321
  // §6.6.1 — set while open to signal "seen, working on it"; cleared on
302
322
  // reopen, kept (but superseded in display) once resolved. Defaulted so
303
323
  // clients parsing an older server's response don't fail.
304
- acknowledgedAt: z4.string().nullable().default(null),
324
+ acknowledgedAt: z5.string().nullable().default(null),
305
325
  acknowledgedBy: threadActorSchema.nullable().default(null),
306
326
  anchor: anchorSchema,
307
- artifactId: z4.string(),
308
- createdAt: z4.string(),
327
+ artifactId: z5.string(),
328
+ createdAt: z5.string(),
309
329
  createdBy: commentAuthorSchema,
310
- id: z4.string(),
311
- messages: z4.array(commentMessageSchema),
330
+ id: z5.string(),
331
+ messages: z5.array(commentMessageSchema),
312
332
  // Null on threads resolved before actor recording existed, and always
313
333
  // null while open.
314
334
  resolvedBy: threadActorSchema.nullable().default(null),
315
- status: z4.enum(["open", "resolved"]),
316
- versionId: z4.string()
335
+ status: z5.enum(["open", "resolved"]),
336
+ versionId: z5.string()
317
337
  });
318
- var commentThreadListSchema = z4.object({
319
- threads: z4.array(commentThreadSchema)
338
+ var commentThreadListSchema = z5.object({
339
+ threads: z5.array(commentThreadSchema)
320
340
  });
321
- var commentExportQuerySchema = z4.object({
322
- status: z4.enum(["open", "resolved", "all"]).default("open"),
323
- version: z4.enum(["current", "all"]).default("current")
341
+ var commentExportQuerySchema = z5.object({
342
+ status: z5.enum(["open", "resolved", "all"]).default("open"),
343
+ version: z5.enum(["current", "all"]).default("current")
324
344
  });
325
- var exportReactionSchema = z4.object({
326
- count: z4.number().int().positive(),
327
- emoji: z4.string()
345
+ var exportReactionSchema = z5.object({
346
+ count: z5.number().int().positive(),
347
+ emoji: z5.string()
328
348
  });
329
- var commentExportReplySchema = z4.object({
330
- author: z4.string(),
331
- authorKind: z4.enum(["member", "guest"]),
332
- body: z4.string(),
333
- createdAt: z4.string(),
349
+ var commentExportReplySchema = z5.object({
350
+ author: z5.string(),
351
+ authorKind: z5.enum(["member", "guest"]),
352
+ body: z5.string(),
353
+ createdAt: z5.string(),
334
354
  // Soft-delete tombstone (§6.7.1): body is "" when true.
335
- deleted: z4.boolean(),
336
- editedAt: z4.string().nullable(),
355
+ deleted: z5.boolean(),
356
+ editedAt: z5.string().nullable(),
337
357
  // The message id — what `edit`/`delete` target (§5.1).
338
- id: z4.string(),
339
- reactions: z4.array(exportReactionSchema).optional()
358
+ id: z5.string(),
359
+ reactions: z5.array(exportReactionSchema).optional()
340
360
  });
341
- var commentExportThreadSchema = z4.object({
361
+ var commentExportThreadSchema = z5.object({
342
362
  // §6.6.1 — lets a polling agent skip threads it already acknowledged
343
363
  // (acknowledged threads stay in status=open exports on purpose).
344
- acknowledgedAt: z4.string().nullable().default(null),
364
+ acknowledgedAt: z5.string().nullable().default(null),
345
365
  anchor: anchorSchema,
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(),
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(),
353
373
  // The opening message's id (distinct from the thread `id`) — `edit`/
354
374
  // `delete` operate on messages, so the CLI needs it to target the first
355
375
  // comment, not just the replies.
356
- messageId: z4.string(),
357
- reactions: z4.array(exportReactionSchema).optional(),
358
- replies: z4.array(commentExportReplySchema),
376
+ messageId: z5.string(),
377
+ reactions: z5.array(exportReactionSchema).optional(),
378
+ replies: z5.array(commentExportReplySchema),
359
379
  resolvedBy: threadActorSchema.nullable().default(null),
360
- status: z4.enum(["open", "resolved"]),
361
- versionNumber: z4.number().int().positive()
380
+ status: z5.enum(["open", "resolved"]),
381
+ versionNumber: z5.number().int().positive()
362
382
  });
363
- var commentExportSchema = z4.object({
364
- artifactId: z4.string(),
365
- comments: z4.array(commentExportThreadSchema),
383
+ var commentExportSchema = z5.object({
384
+ artifactId: z5.string(),
385
+ comments: z5.array(commentExportThreadSchema),
366
386
  // The artifact's current version (null before the first finalize).
367
- versionNumber: z4.number().int().positive().nullable()
387
+ versionNumber: z5.number().int().positive().nullable()
368
388
  });
369
- var commentReadResponseSchema = z4.object({
370
- lastSeenAt: z4.string()
389
+ var commentReadResponseSchema = z5.object({
390
+ lastSeenAt: z5.string()
371
391
  });
372
392
  function elementLabel(el) {
373
393
  if (el.id) {
@@ -378,228 +398,236 @@ function elementLabel(el) {
378
398
  }
379
399
 
380
400
  // ../shared/src/errors.ts
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()
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()
387
407
  });
388
408
 
389
409
  // ../shared/src/feedback.ts
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)
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)
394
414
  });
395
- var feedbackResponseSchema = z6.object({
396
- sent: z6.literal(true)
415
+ var feedbackResponseSchema = z7.object({
416
+ sent: z7.literal(true)
397
417
  });
398
418
 
399
419
  // ../shared/src/grants.ts
400
- import { z as z7 } from "zod";
401
- var grantRoleSchema = z7.enum(["viewer"]);
402
- var createGrantSchema = z7.object({
403
- email: z7.string().trim().toLowerCase().email().optional(),
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(),
404
424
  role: grantRoleSchema.default("viewer"),
405
- userId: z7.string().min(1).optional()
425
+ userId: z8.string().min(1).optional()
406
426
  }).refine((body) => Boolean(body.email) !== Boolean(body.userId), {
407
427
  message: "provide exactly one of email or userId"
408
428
  });
409
- var grantSchema = z7.object({
410
- createdAt: z7.string(),
411
- email: z7.string().email().nullable(),
412
- expiresAt: z7.string().nullable(),
413
- id: z7.string(),
429
+ var grantSchema = z8.object({
430
+ createdAt: z8.string(),
431
+ email: z8.string().email().nullable(),
432
+ expiresAt: z8.string().nullable(),
433
+ id: z8.string(),
414
434
  // The subject's avatar once they have an account; null otherwise.
415
- image: z7.string().nullable(),
435
+ image: z8.string().nullable(),
416
436
  // Present on the invited account after redemption / when granting an
417
437
  // existing user; null for a still-pending email invite.
418
- name: z7.string().nullable(),
419
- redeemedAt: z7.string().nullable(),
438
+ name: z8.string().nullable(),
439
+ redeemedAt: z8.string().nullable(),
420
440
  role: grantRoleSchema,
421
441
  // Shown as "· pending" beside the role until the link is redeemed.
422
- status: z7.enum(["pending", "active"]),
442
+ status: z8.enum(["pending", "active"]),
423
443
  // Display fragment of the live invitation token, or null once redeemed.
424
- tokenPrefix: z7.string().nullable(),
425
- userId: z7.string().nullable()
444
+ tokenPrefix: z8.string().nullable(),
445
+ userId: z8.string().nullable()
426
446
  });
427
447
  var createdGrantSchema = grantSchema.extend({
428
- url: z7.string().url()
448
+ url: z8.string().url()
429
449
  });
430
- var grantListResponseSchema = z7.object({
431
- grants: z7.array(grantSchema),
450
+ var grantListResponseSchema = z8.object({
451
+ grants: z8.array(grantSchema),
432
452
  // The artifact's current general-access value, so the share surface can
433
453
  // render the "General access" line without a second read.
434
- visibility: z7.enum(["public", "unlisted", "workspace", "private"])
435
- });
436
- var revokeGrantResponseSchema = z7.object({
437
- revoked: z7.literal(true)
454
+ visibility: z8.enum(["public", "unlisted", "workspace", "private"]),
455
+ // The owning workspace's logo, so the share modal can draw the workspace
456
+ // itself rather than a generic glyph. Null when none was uploaded.
457
+ workspaceLogo: z8.string().nullable().optional(),
458
+ // The owning workspace's display name, so "People with access" can name the
459
+ // audience `workspace` visibility actually admits ("All Acme members") rather
460
+ // than leaving an empty list to imply nobody. Null for a workspace-less
461
+ // artifact; optional so an older server still parses.
462
+ workspaceName: z8.string().nullable().optional()
463
+ });
464
+ var revokeGrantResponseSchema = z8.object({
465
+ revoked: z8.literal(true)
438
466
  });
439
- var acceptGrantSchema = z7.object({
440
- token: z7.string().min(1)
467
+ var acceptGrantSchema = z8.object({
468
+ token: z8.string().min(1)
441
469
  });
442
- var acceptGrantResponseSchema = z7.object({
443
- artifactId: z7.string(),
470
+ var acceptGrantResponseSchema = z8.object({
471
+ artifactId: z8.string(),
444
472
  // The review shell URL to land on, carrying ?invited=1 for the banner.
445
- artifactUrl: z7.string().url(),
446
- slug: z7.string(),
447
- title: z7.string()
448
- });
449
- var workspaceGuestSchema = z7.object({
450
- artifacts: z7.array(
451
- z7.object({
452
- artifactId: z7.string(),
453
- grantId: z7.string(),
454
- slug: z7.string(),
455
- status: z7.enum(["pending", "active"]),
456
- title: z7.string()
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()
457
485
  })
458
486
  ),
459
- email: z7.string().nullable(),
460
- image: z7.string().nullable(),
461
- name: z7.string().nullable(),
462
- userId: z7.string().nullable()
463
- });
464
- var workspaceGuestListResponseSchema = z7.object({
465
- guests: z7.array(workspaceGuestSchema)
466
- });
467
- var sharedWithMeArtifactSchema = z7.object({
468
- artifactId: z7.string(),
469
- slug: z7.string(),
470
- title: z7.string(),
471
- url: z7.string().url(),
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(),
472
500
  // Who shared it, for the "Shared with me" list.
473
- workspaceName: z7.string()
501
+ workspaceName: z8.string()
474
502
  });
475
- var sharedWithMeListResponseSchema = z7.object({
476
- artifacts: z7.array(sharedWithMeArtifactSchema)
503
+ var sharedWithMeListResponseSchema = z8.object({
504
+ artifacts: z8.array(sharedWithMeArtifactSchema)
477
505
  });
478
506
 
479
507
  // ../shared/src/me.ts
480
- import { z as z9 } from "zod";
508
+ import { z as z10 } from "zod";
481
509
 
482
510
  // ../shared/src/workspaces.ts
483
- import { z as z8 } from "zod";
511
+ import { z as z9 } from "zod";
484
512
  var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
485
- var workspaceHandleUnavailableReasonSchema = z8.enum([
513
+ var workspaceHandleUnavailableReasonSchema = z9.enum([
486
514
  "held",
487
515
  "invalid",
488
516
  "reserved",
489
517
  "taken"
490
518
  ]);
491
- var workspaceHandleAvailabilitySchema = z8.object({
492
- available: z8.boolean(),
493
- handle: z8.string(),
519
+ var workspaceHandleAvailabilitySchema = z9.object({
520
+ available: z9.boolean(),
521
+ handle: z9.string(),
494
522
  // Human-readable sentence for the field hint; null when available.
495
- message: z8.string().nullable(),
523
+ message: z9.string().nullable(),
496
524
  reason: workspaceHandleUnavailableReasonSchema.nullable()
497
525
  });
498
- var workspaceRoleSchema = z8.enum(["owner", "admin", "member"]);
499
- var workspaceInviteRoleSchema = z8.enum(["admin", "member"]);
500
- var workspaceHandleSchema = z8.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
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, {
501
529
  message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
502
530
  });
503
- var createWorkspaceSchema = z8.object({
531
+ var createWorkspaceSchema = z9.object({
504
532
  handle: workspaceHandleSchema.optional(),
505
- name: z8.string().trim().min(1).max(100)
533
+ name: z9.string().trim().min(1).max(100)
506
534
  });
507
- var updateWorkspaceSchema = z8.object({
535
+ var updateWorkspaceSchema = z9.object({
508
536
  handle: workspaceHandleSchema.optional(),
509
- name: z8.string().trim().min(1).max(100).optional()
537
+ name: z9.string().trim().min(1).max(100).optional()
510
538
  }).refine((body) => body.handle !== void 0 || body.name !== void 0, {
511
539
  message: "provide at least one of name or handle"
512
540
  });
513
- var workspaceSchema = z8.object({
514
- createdAt: z8.string(),
515
- handle: z8.string(),
541
+ var workspaceSchema = z9.object({
542
+ createdAt: z9.string(),
543
+ handle: z9.string(),
516
544
  // ISO timestamp when `{handle}.artifact.cafe` was provisioned, or null.
517
545
  // Set by the one-time, self-serve enable flow (§4.4); never auto at
518
546
  // create, and always null for personal workspaces.
519
- hostnameProvisionedAt: z8.string().nullable(),
520
- id: z8.string(),
547
+ hostnameProvisionedAt: z9.string().nullable(),
548
+ id: z9.string(),
521
549
  // Auto-provisioned solo workspace, one per user: non-deletable, takes
522
550
  // no members, renameable like any workspace.
523
- isPersonal: z8.boolean(),
551
+ isPersonal: z9.boolean(),
524
552
  // Uploaded logo URL, or null for the initials fallback. Set through the
525
553
  // dedicated /workspaces/:id/avatar route, never the generic update.
526
- logo: z8.string().nullable(),
527
- name: z8.string(),
554
+ logo: z9.string().nullable(),
555
+ name: z9.string(),
528
556
  role: workspaceRoleSchema.nullable(),
529
- updatedAt: z8.string()
557
+ updatedAt: z9.string()
530
558
  });
531
- var workspaceListResponseSchema = z8.object({
532
- workspaces: z8.array(workspaceSchema)
559
+ var workspaceListResponseSchema = z9.object({
560
+ workspaces: z9.array(workspaceSchema)
533
561
  });
534
- var deleteWorkspaceResponseSchema = z8.object({
535
- deleted: z8.literal(true)
562
+ var deleteWorkspaceResponseSchema = z9.object({
563
+ deleted: z9.literal(true)
536
564
  });
537
- var workspaceMemberSchema = z8.object({
538
- createdAt: z8.string(),
539
- email: z8.string().email(),
540
- image: z8.string().nullable(),
541
- name: z8.string(),
565
+ var workspaceMemberSchema = z9.object({
566
+ createdAt: z9.string(),
567
+ email: z9.string().email(),
568
+ image: z9.string().nullable(),
569
+ name: z9.string(),
542
570
  role: workspaceRoleSchema,
543
- userId: z8.string()
571
+ userId: z9.string()
544
572
  });
545
- var workspaceMemberListResponseSchema = z8.object({
546
- members: z8.array(workspaceMemberSchema)
573
+ var workspaceMemberListResponseSchema = z9.object({
574
+ members: z9.array(workspaceMemberSchema)
547
575
  });
548
- var updateWorkspaceMemberSchema = z8.object({
576
+ var updateWorkspaceMemberSchema = z9.object({
549
577
  role: workspaceRoleSchema
550
578
  });
551
- var removeWorkspaceMemberResponseSchema = z8.object({
552
- removed: z8.literal(true)
579
+ var removeWorkspaceMemberResponseSchema = z9.object({
580
+ removed: z9.literal(true)
553
581
  });
554
- var createWorkspaceInviteSchema = z8.object({
555
- email: z8.string().trim().toLowerCase().email().optional(),
556
- expiresAt: z8.string().datetime(),
557
- maxUses: z8.number().int().positive().optional(),
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(),
558
586
  role: workspaceInviteRoleSchema
559
587
  });
560
- var workspaceInviteSchema = z8.object({
561
- createdAt: z8.string(),
562
- email: z8.string().email().nullable(),
563
- expiresAt: z8.string(),
564
- id: z8.string(),
565
- maxUses: z8.number().int().positive().nullable(),
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(),
566
594
  role: workspaceInviteRoleSchema,
567
- tokenPrefix: z8.string(),
568
- useCount: z8.number().int().nonnegative()
595
+ tokenPrefix: z9.string(),
596
+ useCount: z9.number().int().nonnegative()
569
597
  });
570
598
  var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
571
- url: z8.string().url()
599
+ url: z9.string().url()
572
600
  });
573
- var workspaceInviteListResponseSchema = z8.object({
574
- invites: z8.array(workspaceInviteSchema)
601
+ var workspaceInviteListResponseSchema = z9.object({
602
+ invites: z9.array(workspaceInviteSchema)
575
603
  });
576
- var revokeWorkspaceInviteResponseSchema = z8.object({
577
- revoked: z8.literal(true)
604
+ var revokeWorkspaceInviteResponseSchema = z9.object({
605
+ revoked: z9.literal(true)
578
606
  });
579
- var acceptWorkspaceInviteSchema = z8.object({
580
- token: z8.string().min(1)
607
+ var acceptWorkspaceInviteSchema = z9.object({
608
+ token: z9.string().min(1)
581
609
  });
582
- var acceptWorkspaceInviteResponseSchema = z8.object({
610
+ var acceptWorkspaceInviteResponseSchema = z9.object({
583
611
  role: workspaceRoleSchema,
584
612
  workspace: workspaceSchema
585
613
  });
586
- var folderVisibilitySchema = z8.enum(["public", "workspace"]);
587
- var artifactVisibilitySchema = z8.enum([
614
+ var folderVisibilitySchema = z9.enum(["public", "workspace"]);
615
+ var artifactVisibilitySchema = z9.enum([
588
616
  "public",
589
617
  "unlisted",
590
618
  "workspace",
591
619
  "private"
592
620
  ]);
593
- var createFolderSchema = z8.object({
594
- name: z8.string().trim().min(1).max(100),
621
+ var createFolderSchema = z9.object({
622
+ name: z9.string().trim().min(1).max(100),
595
623
  visibility: folderVisibilitySchema
596
624
  });
597
- var updateFolderSchema = z8.object({
625
+ var updateFolderSchema = z9.object({
598
626
  // With `visibility`: bulk-update the folder's artifacts to match
599
627
  // (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
600
- cascade: z8.boolean().optional(),
601
- name: z8.string().trim().min(1).max(100).optional(),
602
- sortOrder: z8.number().int().min(0).optional(),
628
+ cascade: z9.boolean().optional(),
629
+ name: z9.string().trim().min(1).max(100).optional(),
630
+ sortOrder: z9.number().int().min(0).optional(),
603
631
  visibility: folderVisibilitySchema.optional()
604
632
  }).refine(
605
633
  (body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0,
@@ -607,256 +635,260 @@ var updateFolderSchema = z8.object({
607
635
  ).refine((body) => !body.cascade || body.visibility !== void 0, {
608
636
  message: "cascade requires visibility"
609
637
  });
610
- var folderSchema = z8.object({
611
- createdAt: z8.string(),
612
- id: z8.string(),
613
- name: z8.string(),
638
+ var folderSchema = z9.object({
639
+ createdAt: z9.string(),
640
+ id: z9.string(),
641
+ name: z9.string(),
614
642
  // Permalink segment for the public folder page (/w/{handle}/f/{slug}).
615
643
  // Server-derived from the name; renames regenerate it.
616
- slug: z8.string(),
617
- sortOrder: z8.number().int(),
618
- updatedAt: z8.string(),
644
+ slug: z9.string(),
645
+ sortOrder: z9.number().int(),
646
+ updatedAt: z9.string(),
619
647
  visibility: folderVisibilitySchema,
620
- workspaceId: z8.string()
648
+ workspaceId: z9.string()
621
649
  });
622
- var folderListResponseSchema = z8.object({
623
- folders: z8.array(folderSchema)
650
+ var folderListResponseSchema = z9.object({
651
+ folders: z9.array(folderSchema)
624
652
  });
625
- var deleteFolderResponseSchema = z8.object({
626
- deleted: z8.literal(true)
653
+ var deleteFolderResponseSchema = z9.object({
654
+ deleted: z9.literal(true)
627
655
  });
628
- var workspaceArtifactSchema = z8.object({
629
- createdAt: z8.string(),
630
- currentVersionNumber: z8.number().int().nullable(),
631
- folderId: z8.string().nullable(),
632
- id: z8.string(),
633
- openCommentCount: z8.number().int().nonnegative(),
634
- slug: z8.string(),
635
- title: z8.string(),
636
- updatedAt: z8.string(),
637
- url: z8.string().url(),
638
- versionCount: z8.number().int().nonnegative(),
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(),
662
+ 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(),
639
668
  visibility: artifactVisibilitySchema
640
669
  });
641
- var workspaceArtifactListResponseSchema = z8.object({
642
- artifacts: z8.array(workspaceArtifactSchema)
670
+ var workspaceArtifactListResponseSchema = z9.object({
671
+ artifacts: z9.array(workspaceArtifactSchema)
643
672
  });
644
- var workspaceArtifactSearchResultSchema = z8.object({
645
- id: z8.string(),
646
- slug: z8.string(),
647
- title: z8.string()
673
+ var workspaceArtifactSearchResultSchema = z9.object({
674
+ id: z9.string(),
675
+ slug: z9.string(),
676
+ title: z9.string()
648
677
  });
649
- var workspaceArtifactSearchResponseSchema = z8.object({
650
- results: z8.array(workspaceArtifactSearchResultSchema)
678
+ var workspaceArtifactSearchResponseSchema = z9.object({
679
+ results: z9.array(workspaceArtifactSearchResultSchema)
651
680
  });
652
681
 
653
682
  // ../shared/src/me.ts
654
- var ownArtifactSchema = z9.object({
655
- createdAt: z9.string(),
656
- currentVersionNumber: z9.number().nullable(),
683
+ var ownArtifactSchema = z10.object({
684
+ createdAt: z10.string(),
685
+ currentVersionNumber: z10.number().nullable(),
657
686
  // Owned artifacts normally never expire; non-null only while an
658
687
  // expiry is still set (e.g. an unclaimed publish surfaced here later).
659
- expiresAt: z9.string().nullable(),
688
+ expiresAt: z10.string().nullable(),
660
689
  // SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
661
690
  // both null for personal artifacts. `folderId` drives the dashboard's
662
691
  // move control and folder filter; `folderName` is the display label.
663
- folderId: z9.string().nullable(),
664
- folderName: z9.string().nullable(),
692
+ folderId: z10.string().nullable(),
693
+ folderName: z10.string().nullable(),
665
694
  // Whether a publish token is still active — revocable from the
666
695
  // dashboard (§6.18); the token itself is never returned.
667
- hasPublishToken: z9.boolean(),
668
- id: z9.string(),
669
- openThreads: z9.number(),
670
- protected: z9.boolean(),
696
+ hasPublishToken: z10.boolean(),
697
+ id: z10.string(),
698
+ openThreads: z10.number(),
699
+ protected: z10.boolean(),
671
700
  // Who published the current version (falling back to the claiming
672
701
  // owner) — the dashboard shows a name and avatar per artifact so a
673
702
  // shared workspace reads as people's work, not anonymous rows. Null
674
703
  // when the publish was token-only and the artifact is unclaimed.
675
- publishedBy: z9.object({ image: z9.string().nullable(), name: z9.string() }).nullable(),
676
- slug: z9.string(),
677
- title: z9.string(),
678
- totalViews: z9.number(),
704
+ publishedBy: z10.object({ image: z10.string().nullable(), name: z10.string() }).nullable(),
705
+ // SPEC-2 §5.8 — the review dial, so the dashboard's settings dialog can
706
+ // render current state without a second fetch.
707
+ reviewMode: reviewModeSchema,
708
+ slug: z10.string(),
709
+ title: z10.string(),
710
+ totalViews: z10.number(),
679
711
  // SPEC §6.20 — messages by others newer than the viewer's last-seen
680
712
  // watermark. 0 for signed-out/API-key-less contexts.
681
- unreadComments: z9.number(),
682
- updatedAt: z9.string(),
683
- url: z9.string(),
684
- versionCount: z9.number(),
713
+ unreadComments: z10.number(),
714
+ updatedAt: z10.string(),
715
+ url: z10.string(),
716
+ versionCount: z10.number(),
685
717
  // SPEC-2 §5.1 — whether the caller clears the manage bar for this
686
718
  // artifact (workspace admin, or the person who first published it).
687
719
  // Only a manager's folder move inherits the folder's visibility, so the
688
720
  // dashboard warns exactly the people the change can actually affect, and
689
721
  // only a manager is offered the Share entry (§5.7).
690
- viewerCanManage: z9.boolean(),
722
+ viewerCanManage: z10.boolean(),
691
723
  // SPEC-2 §5.2 — the artifact's own visibility, the single enforcement
692
724
  // source. The dashboard needs it to tell whether a folder move would
693
725
  // widen exposure before it sends the PATCH (§3.4), and to render the lock
694
726
  // glyph on `private` rows and cards (§5.7).
695
727
  visibility: artifactVisibilitySchema
696
728
  });
697
- var ownArtifactsResponseSchema = z9.object({
698
- artifacts: z9.array(ownArtifactSchema)
729
+ var ownArtifactsResponseSchema = z10.object({
730
+ artifacts: z10.array(ownArtifactSchema)
699
731
  });
700
- var revokePublishTokenResponseSchema = z9.object({
701
- revoked: z9.boolean()
732
+ var revokePublishTokenResponseSchema = z10.object({
733
+ revoked: z10.boolean()
702
734
  });
703
735
 
704
736
  // ../shared/src/owner.ts
705
- import { z as z10 } from "zod";
737
+ import { z as z11 } from "zod";
706
738
  var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
707
- var claimResponseSchema = z10.object({
708
- artifactId: z10.string(),
709
- slug: z10.string(),
710
- title: z10.string(),
711
- url: z10.string().url()
739
+ var claimResponseSchema = z11.object({
740
+ artifactId: z11.string(),
741
+ slug: z11.string(),
742
+ title: z11.string(),
743
+ url: z11.string().url()
712
744
  });
713
- var updateArtifactSchema = z10.object({
745
+ var updateArtifactSchema = z11.object({
714
746
  // SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
715
747
  // folder; `workspaceId: null` moves the artifact out of its workspace
716
748
  // (§5.5 ownership transfer).
717
- folderId: z10.string().min(1).nullable().optional(),
718
- slug: z10.string().min(3).max(63).regex(SLUG_PATTERN, {
749
+ folderId: z11.string().min(1).nullable().optional(),
750
+ slug: z11.string().min(3).max(63).regex(SLUG_PATTERN, {
719
751
  message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
720
752
  }).optional(),
721
- title: z10.string().min(1).max(200).optional(),
753
+ title: z11.string().min(1).max(200).optional(),
722
754
  visibility: artifactVisibilitySchema.optional(),
723
- workspaceId: z10.string().min(1).nullable().optional()
755
+ workspaceId: z11.string().min(1).nullable().optional()
724
756
  }).refine(
725
757
  (body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0,
726
758
  {
727
759
  message: "provide at least one of title, slug, visibility, folderId, workspaceId"
728
760
  }
729
761
  );
730
- var deleteArtifactResponseSchema = z10.object({
731
- deleted: z10.literal(true)
762
+ var deleteArtifactResponseSchema = z11.object({
763
+ deleted: z11.literal(true)
732
764
  });
733
765
 
734
766
  // ../shared/src/presence.ts
735
- import { z as z11 } from "zod";
767
+ import { z as z12 } from "zod";
736
768
  var AGENT_STATES = ["listening", "editing", "publishing"];
737
- var agentStateSchema = z11.enum(AGENT_STATES);
769
+ var agentStateSchema = z12.enum(AGENT_STATES);
738
770
  var AGENT_PRESENCE = {
739
771
  editingTtlSeconds: 300,
740
772
  heartbeatSeconds: 15,
741
773
  shellPollSeconds: 15,
742
774
  ttlSeconds: 45
743
775
  };
744
- var agentHeartbeatBodySchema = z11.object({
776
+ var agentHeartbeatBodySchema = z12.object({
745
777
  state: agentStateSchema.optional()
746
778
  });
747
- var agentHeartbeatResponseSchema = z11.object({
748
- listening: z11.literal(true),
779
+ var agentHeartbeatResponseSchema = z12.object({
780
+ listening: z12.literal(true),
749
781
  state: agentStateSchema.optional(),
750
- ttlSeconds: z11.number().int().positive()
782
+ ttlSeconds: z12.number().int().positive()
751
783
  });
752
- var agentPresenceSchema = z11.object({
753
- listening: z11.boolean(),
784
+ var agentPresenceSchema = z12.object({
785
+ listening: z12.boolean(),
754
786
  state: agentStateSchema.nullable().optional(),
755
- versionNumber: z11.number().int().positive().nullable()
787
+ versionNumber: z12.number().int().positive().nullable()
756
788
  });
757
789
 
758
790
  // ../shared/src/protection.ts
759
- import { z as z12 } from "zod";
791
+ import { z as z13 } from "zod";
760
792
  var PASSWORD_MIN = 6;
761
793
  var PASSWORD_MAX = 128;
762
- var gatePasswordSchema = z12.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
763
- var setProtectionSchema = z12.object({
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({
764
796
  password: gatePasswordSchema.nullable()
765
797
  });
766
- var protectionResponseSchema = z12.object({
767
- protected: z12.boolean()
798
+ var protectionResponseSchema = z13.object({
799
+ protected: z13.boolean()
768
800
  });
769
- var unlockSchema = z12.object({
770
- password: z12.string().min(1).max(1024)
801
+ var unlockSchema = z13.object({
802
+ password: z13.string().min(1).max(1024)
771
803
  });
772
- var unlockResponseSchema = z12.object({
773
- unlocked: z12.literal(true)
804
+ var unlockResponseSchema = z13.object({
805
+ unlocked: z13.literal(true)
774
806
  });
775
807
 
776
808
  // ../shared/src/report.ts
777
- import { z as z13 } from "zod";
778
- var reportRequestSchema = z13.object({
779
- reason: z13.string().max(1e3).optional()
809
+ import { z as z14 } from "zod";
810
+ var reportRequestSchema = z14.object({
811
+ reason: z14.string().max(1e3).optional()
780
812
  });
781
- var reportResponseSchema = z13.object({
782
- reported: z13.literal(true)
813
+ var reportResponseSchema = z14.object({
814
+ reported: z14.literal(true)
783
815
  });
784
816
 
785
817
  // ../shared/src/reviewers.ts
786
- import { z as z14 } from "zod";
787
- var artifactReviewerSchema = z14.object({
788
- avatarUrl: z14.string().nullable(),
789
- displayName: z14.string(),
818
+ import { z as z15 } from "zod";
819
+ var artifactReviewerSchema = z15.object({
820
+ avatarUrl: z15.string().nullable(),
821
+ displayName: z15.string(),
790
822
  // Exactly one of userId/guestId is set, per `kind`.
791
- guestId: z14.string().nullable(),
823
+ guestId: z15.string().nullable(),
792
824
  // "account", not commentAuthorSchema's "member" — the whole point here is
793
825
  // that these people need not be workspace members. A guest can be named in
794
826
  // a comment but never notified: guest_identities carries a display name and
795
827
  // no email, so there is nowhere to send anything.
796
- kind: z14.enum(["account", "guest"]),
797
- userId: z14.string().nullable()
828
+ kind: z15.enum(["account", "guest"]),
829
+ userId: z15.string().nullable()
798
830
  });
799
- var artifactReviewerListResponseSchema = z14.object({
800
- reviewers: z14.array(artifactReviewerSchema)
831
+ var artifactReviewerListResponseSchema = z15.object({
832
+ reviewers: z15.array(artifactReviewerSchema)
801
833
  });
802
834
 
803
835
  // ../shared/src/stats.ts
804
- import { z as z15 } from "zod";
805
- var avgViewSecondsSchema = z15.number().int().nonnegative().nullable();
806
- var versionViewStatsSchema = z15.object({
836
+ import { z as z16 } from "zod";
837
+ var avgViewSecondsSchema = z16.number().int().nonnegative().nullable();
838
+ var versionViewStatsSchema = z16.object({
807
839
  avgViewSeconds: avgViewSecondsSchema,
808
840
  // Views from inside the review shell's iframe vs. direct visits to
809
841
  // the render URL. views = embeddedViews + directViews.
810
- directViews: z15.number().int().nonnegative(),
811
- embeddedViews: z15.number().int().nonnegative(),
842
+ directViews: z16.number().int().nonnegative(),
843
+ embeddedViews: z16.number().int().nonnegative(),
812
844
  // Distinct hashed viewer IPs across the version's lifetime.
813
- uniqueViewers: z15.number().int().nonnegative(),
814
- versionNumber: z15.number().int().positive(),
815
- views: z15.number().int().nonnegative()
845
+ uniqueViewers: z16.number().int().nonnegative(),
846
+ versionNumber: z16.number().int().positive(),
847
+ views: z16.number().int().nonnegative()
816
848
  });
817
- var dailyViewsSchema = z15.object({
849
+ var dailyViewsSchema = z16.object({
818
850
  avgViewSeconds: avgViewSecondsSchema,
819
851
  // UTC day, YYYY-MM-DD.
820
- day: z15.string(),
821
- uniqueViewers: z15.number().int().nonnegative(),
822
- views: z15.number().int().nonnegative()
852
+ day: z16.string(),
853
+ uniqueViewers: z16.number().int().nonnegative(),
854
+ views: z16.number().int().nonnegative()
823
855
  });
824
- var artifactStatsResponseSchema = z15.object({
856
+ var artifactStatsResponseSchema = z16.object({
825
857
  avgViewSeconds: avgViewSecondsSchema,
826
- daily: z15.array(dailyViewsSchema),
858
+ daily: z16.array(dailyViewsSchema),
827
859
  // Distinct hashed viewer IPs across all versions and days.
828
- totalUniqueViewers: z15.number().int().nonnegative(),
829
- totalViews: z15.number().int().nonnegative(),
830
- versions: z15.array(versionViewStatsSchema)
860
+ totalUniqueViewers: z16.number().int().nonnegative(),
861
+ totalViews: z16.number().int().nonnegative(),
862
+ versions: z16.array(versionViewStatsSchema)
831
863
  });
832
864
 
833
865
  // ../shared/src/versions.ts
834
- import { z as z16 } from "zod";
835
- var versionSourceSchema = z16.object({
836
- agentName: z16.string().nullable(),
837
- type: z16.string()
866
+ import { z as z17 } from "zod";
867
+ var versionSourceSchema = z17.object({
868
+ agentName: z17.string().nullable(),
869
+ type: z17.string()
838
870
  });
839
- var versionChangesSchema = z16.object({
840
- added: z16.array(z16.string()),
841
- changed: z16.array(z16.string()),
842
- removed: z16.array(z16.string())
871
+ var versionChangesSchema = z17.object({
872
+ added: z17.array(z17.string()),
873
+ changed: z17.array(z17.string()),
874
+ removed: z17.array(z17.string())
843
875
  });
844
- var versionSummarySchema = z16.object({
876
+ var versionSummarySchema = z17.object({
845
877
  // Absent for the first version — there is no previous version to diff against.
846
878
  changes: versionChangesSchema.nullable(),
847
- createdAt: z16.string(),
848
- entryUrl: z16.string().url(),
849
- fileCount: z16.number().int().nonnegative(),
850
- id: z16.string(),
851
- isCurrent: z16.boolean(),
879
+ createdAt: z17.string(),
880
+ entryUrl: z17.string().url(),
881
+ fileCount: z17.number().int().nonnegative(),
882
+ id: z17.string(),
883
+ isCurrent: z17.boolean(),
852
884
  // Author-supplied bullet-point changelog; null when the publish omitted it.
853
- notes: z16.string().nullable(),
885
+ notes: z17.string().nullable(),
854
886
  source: versionSourceSchema,
855
- totalSizeBytes: z16.number().int().nonnegative(),
856
- versionNumber: z16.number().int().positive()
887
+ totalSizeBytes: z17.number().int().nonnegative(),
888
+ versionNumber: z17.number().int().positive()
857
889
  });
858
- var versionListResponseSchema = z16.object({
859
- versions: z16.array(versionSummarySchema)
890
+ var versionListResponseSchema = z17.object({
891
+ versions: z17.array(versionSummarySchema)
860
892
  });
861
893
 
862
894
  // src/api.ts
@@ -933,6 +965,15 @@ function createApiClient(apiUrl2, publishToken) {
933
965
  (d) => agentHeartbeatResponseSchema.parse(d)
934
966
  );
935
967
  },
968
+ // SPEC-2 §6.3 — create a folder in a workspace. Account key required
969
+ // (owner/admin; in a personal workspace that's always the user).
970
+ async createFolder(workspaceId, body) {
971
+ return request(
972
+ `${base}/api/v1/workspaces/${workspaceId}/folders`,
973
+ { body: JSON.stringify(body), headers, method: "POST" },
974
+ (d) => folderSchema.parse(d)
975
+ );
976
+ },
936
977
  // SPEC-2 §6.6 — share an artifact with a person (email or user id).
937
978
  // Authorized by this client's credential: the publish token (an agent
938
979
  // sharing a private draft) or an account key/session. The invitation
@@ -1031,6 +1072,15 @@ function createApiClient(apiUrl2, publishToken) {
1031
1072
  (d) => grantListResponseSchema.parse(d)
1032
1073
  );
1033
1074
  },
1075
+ // SPEC-2 §6.4 — every artifact in a workspace (members see all, with
1076
+ // each artifact's folderId). The only folder-aware artifact listing.
1077
+ async listWorkspaceArtifacts(workspaceId) {
1078
+ return request(
1079
+ `${base}/api/v1/workspaces/${workspaceId}/artifacts`,
1080
+ { headers, method: "GET" },
1081
+ (d) => workspaceArtifactListResponseSchema.parse(d)
1082
+ );
1083
+ },
1034
1084
  async listWorkspaces() {
1035
1085
  return request(
1036
1086
  `${base}/api/v1/workspaces`,
@@ -1091,6 +1141,26 @@ function createApiClient(apiUrl2, publishToken) {
1091
1141
  { body: JSON.stringify({ password }), headers, method: "PUT" },
1092
1142
  (d) => protectionResponseSchema.parse(d)
1093
1143
  );
1144
+ },
1145
+ // SPEC-2 §5.8 — what reviewers may do once they can open it. Same
1146
+ // dual-auth as protection, so the publish token this client holds is
1147
+ // enough (an agent can close review on its own artifact).
1148
+ async setReviewMode(artifactId, mode) {
1149
+ return request(
1150
+ `${base}/api/v1/artifacts/${artifactId}/review-mode`,
1151
+ { body: JSON.stringify({ mode }), headers, method: "PUT" },
1152
+ (d) => reviewModeResponseSchema.parse(d)
1153
+ );
1154
+ },
1155
+ // SPEC.md §6.11 / SPEC-2 §6.4 — update an artifact (folder placement,
1156
+ // title, slug, visibility). Workspace-member action: account key only,
1157
+ // a publish token can't re-file.
1158
+ async updateArtifact(artifactId, body) {
1159
+ return request(
1160
+ `${base}/api/v1/artifacts/${artifactId}`,
1161
+ { body: JSON.stringify(body), headers, method: "PATCH" },
1162
+ (d) => artifactResponseSchema.parse(d)
1163
+ );
1094
1164
  }
1095
1165
  };
1096
1166
  }
@@ -1119,17 +1189,17 @@ import {
1119
1189
  writeFileSync as writeFileSync2
1120
1190
  } from "fs";
1121
1191
  import path2 from "path";
1122
- import { z as z18 } from "zod";
1192
+ import { z as z19 } from "zod";
1123
1193
 
1124
1194
  // src/credentials.ts
1125
1195
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
1126
1196
  import { homedir } from "os";
1127
1197
  import path from "path";
1128
- import { z as z17 } from "zod";
1198
+ import { z as z18 } from "zod";
1129
1199
  var DEFAULT_API_URL = "https://artifact.cafe";
1130
- var credentialsSchema = z17.object({
1131
- keys: z17.record(z17.string(), z17.string()),
1132
- version: z17.literal(1)
1200
+ var credentialsSchema = z18.object({
1201
+ keys: z18.record(z18.string(), z18.string()),
1202
+ version: z18.literal(1)
1133
1203
  });
1134
1204
  function credentialsPath() {
1135
1205
  return path.join(homedir(), ".artifactcafe", "credentials");
@@ -1179,24 +1249,24 @@ function resolveApiKey(apiUrl2, flag) {
1179
1249
  }
1180
1250
 
1181
1251
  // src/config.ts
1182
- var configSchema = z18.object({
1183
- apiUrl: z18.string(),
1184
- artifactId: z18.string(),
1185
- folder: z18.string().optional(),
1186
- folderId: z18.string().optional(),
1252
+ var configSchema = z19.object({
1253
+ apiUrl: z19.string(),
1254
+ artifactId: z19.string(),
1255
+ folder: z19.string().optional(),
1256
+ folderId: z19.string().optional(),
1187
1257
  // Optional: a folder linked by `link` to an artifact you own carries no
1188
1258
  // token — owner actions authorize via your stored login instead. A first
1189
1259
  // publish still writes the anonymous-mode token here.
1190
- publishToken: z18.string().optional(),
1191
- slug: z18.string(),
1192
- url: z18.string(),
1193
- workspace: z18.string().optional(),
1194
- workspaceId: z18.string().optional(),
1195
- workspaceKind: z18.enum(["personal", "workspace"]).optional()
1196
- });
1197
- var projectConfigSchema = z18.object({
1198
- folder: z18.string().trim().min(1).optional(),
1199
- workspace: z18.string().trim().min(1)
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)
1200
1270
  }).strict();
1201
1271
  var CONFIG_DIR = ".artifactcafe";
1202
1272
  var CONFIG_FILE = "config.json";
@@ -1232,7 +1302,7 @@ function readProjectConfig(dir) {
1232
1302
  JSON.parse(readFileSync2(file, "utf8"))
1233
1303
  );
1234
1304
  } catch (error) {
1235
- const message = error instanceof z18.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1305
+ const message = error instanceof z19.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1236
1306
  throw new Error(`Invalid ${file}: ${message}.`);
1237
1307
  }
1238
1308
  }
@@ -1755,6 +1825,11 @@ function resolveEntry(files, explicit) {
1755
1825
 
1756
1826
  // src/publish.ts
1757
1827
  var UPLOAD_CONCURRENCY = 6;
1828
+ var REVIEW_LABEL = {
1829
+ off: "off \u2014 plain artifact, no review tools",
1830
+ open: "open \u2014 anyone with the link can comment",
1831
+ readonly: "read-only \u2014 existing comments only"
1832
+ };
1758
1833
  async function publish(opts) {
1759
1834
  const dir = path6.resolve(opts.dir);
1760
1835
  const config = readConfig(dir);
@@ -1764,6 +1839,14 @@ async function publish(opts) {
1764
1839
  throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
1765
1840
  }
1766
1841
  }
1842
+ let reviewMode;
1843
+ if (opts.review !== void 0) {
1844
+ const parsed = reviewModeSchema.safeParse(opts.review);
1845
+ if (!parsed.success) {
1846
+ throw new Error("--review must be one of: open, readonly, off.");
1847
+ }
1848
+ reviewMode = parsed.data;
1849
+ }
1767
1850
  const artifactId = opts.artifact ?? config?.artifactId ?? null;
1768
1851
  const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
1769
1852
  const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
@@ -1867,6 +1950,13 @@ async function publish(opts) {
1867
1950
  );
1868
1951
  await protectClient.setProtection(result.artifactId, opts.password);
1869
1952
  }
1953
+ if (reviewMode !== void 0) {
1954
+ const reviewClient = createApiClient(
1955
+ apiUrl2,
1956
+ credential ?? result.publishToken
1957
+ );
1958
+ await reviewClient.setReviewMode(result.artifactId, reviewMode);
1959
+ }
1870
1960
  if (opts.json) {
1871
1961
  console.log(
1872
1962
  JSON.stringify(
@@ -1879,10 +1969,12 @@ async function publish(opts) {
1879
1969
  folderId: placement.folderId ?? null,
1880
1970
  url: result.url,
1881
1971
  versionNumber: result.versionNumber,
1972
+ visibility: result.visibility ?? null,
1882
1973
  workspace: placement.workspace ?? null,
1883
1974
  workspaceId: placement.workspaceId ?? null,
1884
1975
  ...result.claimUrl ? { claimUrl: result.claimUrl } : {},
1885
1976
  ...opts.password === void 0 ? {} : { protected: true },
1977
+ ...reviewMode === void 0 ? {} : { reviewMode },
1886
1978
  filesUploaded: session.filesToUpload.map((f) => f.path),
1887
1979
  ignored: skipped,
1888
1980
  skippedFiles: session.skippedFiles
@@ -1902,6 +1994,11 @@ async function publish(opts) {
1902
1994
  console.log(`Review URL:
1903
1995
  ${result.url}
1904
1996
  `);
1997
+ const access = accessNote(result.visibility);
1998
+ if (access) {
1999
+ console.log(`${access}
2000
+ `);
2001
+ }
1905
2002
  if (result.claimUrl) {
1906
2003
  console.log(
1907
2004
  `Claim URL (shown once \u2014 claim to keep this artifact):
@@ -1914,6 +2011,10 @@ ${result.claimUrl}
1914
2011
  if (opts.password !== void 0) {
1915
2012
  console.log("Password protection: on\n");
1916
2013
  }
2014
+ if (reviewMode !== void 0) {
2015
+ console.log(`Review: ${REVIEW_LABEL[reviewMode]}
2016
+ `);
2017
+ }
1917
2018
  } else {
1918
2019
  console.log(`
1919
2020
  \u2713 Published v${result.versionNumber}
@@ -1926,14 +2027,32 @@ ${result.claimUrl}
1926
2027
  }
1927
2028
  console.log(`${result.url}
1928
2029
  `);
2030
+ const access = accessNote(result.visibility);
2031
+ if (access) {
2032
+ console.log(`${access}
2033
+ `);
2034
+ }
1929
2035
  if (opts.password !== void 0) {
1930
2036
  console.log("Password protection: on\n");
1931
2037
  }
2038
+ if (reviewMode !== void 0) {
2039
+ console.log(`Review: ${REVIEW_LABEL[reviewMode]}
2040
+ `);
2041
+ }
1932
2042
  }
1933
2043
  if (opts.open && !opts.json && process.stdout.isTTY) {
1934
2044
  openInBrowser(result.url);
1935
2045
  }
1936
2046
  }
2047
+ function accessNote(visibility) {
2048
+ if (visibility === "workspace") {
2049
+ return "Access: workspace members only \u2014 anyone else needs `artifact-cafe share <email>` to open it.";
2050
+ }
2051
+ if (visibility === "private") {
2052
+ return "Access: private \u2014 you, the workspace's admins, and anyone you `artifact-cafe share` it with.";
2053
+ }
2054
+ return null;
2055
+ }
1937
2056
  async function resolvePlacement(params) {
1938
2057
  const { apiKey, apiUrl: apiUrl2, config, isNewArtifact, opts } = params;
1939
2058
  const hasPlacementFlag = opts.workspace !== void 0 || opts.folder !== void 0;
@@ -2200,6 +2319,24 @@ function printCliError(err, json) {
2200
2319
 
2201
2320
  // src/folders.ts
2202
2321
  async function folders(opts) {
2322
+ if (opts.action !== void 0 && opts.action !== "create") {
2323
+ throw new Error(
2324
+ `Unknown folders action "${opts.action}" \u2014 run \`folders\` to list, or \`folders create <name>\` to create one.`
2325
+ );
2326
+ }
2327
+ if (opts.action === "create") {
2328
+ if (!opts.name?.trim()) {
2329
+ throw new Error(
2330
+ `Pass the folder name \u2014 e.g. \`artifact-cafe folders create "Design specs" --workspace ${opts.workspace}\`.`
2331
+ );
2332
+ }
2333
+ const visibility = opts.visibility ?? "workspace";
2334
+ if (visibility !== "workspace" && visibility !== "public") {
2335
+ throw new Error(
2336
+ `--visibility must be "workspace" or "public" (got "${opts.visibility}").`
2337
+ );
2338
+ }
2339
+ }
2203
2340
  const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
2204
2341
  /\/$/,
2205
2342
  ""
@@ -2207,7 +2344,7 @@ async function folders(opts) {
2207
2344
  const key = resolveApiKey(apiUrl2, opts.apiKey);
2208
2345
  if (!key) {
2209
2346
  throw new Error(
2210
- `Listing folders needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
2347
+ `Folder commands need an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
2211
2348
  );
2212
2349
  }
2213
2350
  const client = createApiClient(apiUrl2, key);
@@ -2220,6 +2357,10 @@ async function folders(opts) {
2220
2357
  `No workspace with handle "${opts.workspace}" belongs to this account.`
2221
2358
  );
2222
2359
  }
2360
+ if (opts.action === "create") {
2361
+ await createFolder(client, workspace, opts);
2362
+ return;
2363
+ }
2223
2364
  const data = await client.listFolders(workspace.id);
2224
2365
  if (opts.json) {
2225
2366
  console.log(JSON.stringify(data, null, 2));
@@ -2238,6 +2379,33 @@ async function folders(opts) {
2238
2379
  printFolder(folder);
2239
2380
  }
2240
2381
  }
2382
+ async function createFolder(client, workspace, opts) {
2383
+ const name = opts.name.trim();
2384
+ const visibility = opts.visibility ?? "workspace";
2385
+ const existing = await client.listFolders(workspace.id);
2386
+ const duplicate = existing.folders.find(
2387
+ (candidate) => candidate.name.toLowerCase() === name.toLowerCase()
2388
+ );
2389
+ if (duplicate) {
2390
+ throw new Error(
2391
+ `A folder named "${duplicate.name}" already exists in ${workspace.handle} (${duplicate.id}) \u2014 publish into it with --folder, or pick a different name.`
2392
+ );
2393
+ }
2394
+ const folder = await client.createFolder(workspace.id, { name, visibility });
2395
+ if (opts.json) {
2396
+ console.log(JSON.stringify(folder, null, 2));
2397
+ return;
2398
+ }
2399
+ console.log(
2400
+ `\u2713 Created folder "${folder.name}" (${folder.visibility}) in ${workspace.handle}`
2401
+ );
2402
+ console.log(` ${folder.id}`);
2403
+ if (folder.visibility === "public") {
2404
+ console.log(
2405
+ " Artifacts placed in it default to public \u2014 visible to anyone."
2406
+ );
2407
+ }
2408
+ }
2241
2409
  function printFolder(folder) {
2242
2410
  console.log(`${folder.name} (${folder.visibility})`);
2243
2411
  console.log(` ${folder.id}
@@ -2352,7 +2520,12 @@ async function list(opts) {
2352
2520
  `Listing your artifacts needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
2353
2521
  );
2354
2522
  }
2355
- const data = await createApiClient(apiUrl2, key).listArtifacts();
2523
+ const client = createApiClient(apiUrl2, key);
2524
+ if (opts.workspace !== void 0) {
2525
+ await listWorkspace(client, opts);
2526
+ return;
2527
+ }
2528
+ const data = await client.listArtifacts();
2356
2529
  if (opts.json) {
2357
2530
  console.log(JSON.stringify(data, null, 2));
2358
2531
  return;
@@ -2370,6 +2543,70 @@ async function list(opts) {
2370
2543
  printArtifact(artifact);
2371
2544
  }
2372
2545
  }
2546
+ async function listWorkspace(client, opts) {
2547
+ const workspaceData = await client.listWorkspaces();
2548
+ const workspace = workspaceData.workspaces.find(
2549
+ (candidate) => candidate.handle === opts.workspace
2550
+ );
2551
+ if (!workspace) {
2552
+ throw new Error(
2553
+ `No workspace with handle "${opts.workspace}" belongs to this account.`
2554
+ );
2555
+ }
2556
+ const [artifactData, folderData] = await Promise.all([
2557
+ client.listWorkspaceArtifacts(workspace.id),
2558
+ client.listFolders(workspace.id)
2559
+ ]);
2560
+ if (opts.json) {
2561
+ console.log(
2562
+ JSON.stringify(
2563
+ { artifacts: artifactData.artifacts, folders: folderData.folders },
2564
+ null,
2565
+ 2
2566
+ )
2567
+ );
2568
+ return;
2569
+ }
2570
+ if (artifactData.artifacts.length === 0) {
2571
+ console.log(`No artifacts in workspace ${workspace.handle}.`);
2572
+ return;
2573
+ }
2574
+ const plural = artifactData.artifacts.length === 1 ? "" : "s";
2575
+ console.log(
2576
+ `${artifactData.artifacts.length} artifact${plural} in ${workspace.handle}
2577
+ `
2578
+ );
2579
+ for (const folder of folderData.folders) {
2580
+ const inFolder = artifactData.artifacts.filter(
2581
+ (artifact) => artifact.folderId === folder.id
2582
+ );
2583
+ if (inFolder.length === 0) {
2584
+ continue;
2585
+ }
2586
+ console.log(`${folder.name} (${folder.visibility})`);
2587
+ for (const artifact of inFolder) {
2588
+ printWorkspaceArtifact(artifact);
2589
+ }
2590
+ }
2591
+ const unfiled = artifactData.artifacts.filter(
2592
+ (artifact) => artifact.folderId === null
2593
+ );
2594
+ if (unfiled.length > 0) {
2595
+ console.log("Unfiled");
2596
+ for (const artifact of unfiled) {
2597
+ printWorkspaceArtifact(artifact);
2598
+ }
2599
+ }
2600
+ }
2601
+ function printWorkspaceArtifact(a) {
2602
+ const version = a.currentVersionNumber === null ? "no version" : `v${a.currentVersionNumber}`;
2603
+ const open2 = a.openCommentCount === 0 ? "no open comments" : `${a.openCommentCount} open comment${a.openCommentCount === 1 ? "" : "s"}`;
2604
+ const marker = a.openCommentCount > 0 ? "\u25CF" : "\u25CB";
2605
+ console.log(
2606
+ ` ${marker} ${a.title} \u2014 ${version} \xB7 ${open2} \xB7 ${a.visibility}`
2607
+ );
2608
+ console.log(` ${a.url}`);
2609
+ }
2373
2610
  function printArtifact(a) {
2374
2611
  const version = a.currentVersionNumber === null ? "no version" : `v${a.currentVersionNumber}`;
2375
2612
  const open2 = a.openCommentCount === 0 ? "no open comments" : `${a.openCommentCount} open comment${a.openCommentCount === 1 ? "" : "s"}`;
@@ -2569,14 +2806,109 @@ async function deleteMessage(opts) {
2569
2806
  console.log(`\u2713 Deleted ${messageId}.`);
2570
2807
  }
2571
2808
 
2572
- // src/open.ts
2809
+ // src/move.ts
2573
2810
  import path10 from "path";
2574
- async function openArtifact(opts) {
2811
+ async function move(opts) {
2812
+ if (Boolean(opts.folder) === Boolean(opts.unfiled)) {
2813
+ throw new Error("Pass exactly one of --folder <name-or-id> or --unfiled.");
2814
+ }
2575
2815
  const dir = path10.resolve(opts.dir);
2576
2816
  const target = resolveArtifactTarget(dir, {
2577
2817
  apiKey: opts.apiKey,
2578
2818
  artifact: opts.artifact
2579
2819
  });
2820
+ const key = resolveApiKey(target.apiUrl, opts.apiKey);
2821
+ if (!key) {
2822
+ throw new Error(
2823
+ `Moving an artifact needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(target.apiUrl)}).`
2824
+ );
2825
+ }
2826
+ const client = createApiClient(target.apiUrl, key);
2827
+ const artifact = await client.getArtifact(target.artifactId);
2828
+ if (!artifact.workspaceId) {
2829
+ throw new Error(
2830
+ "This artifact isn't in a workspace \u2014 folders live in workspaces. Claim it (or publish it while logged in) first."
2831
+ );
2832
+ }
2833
+ let folderId = null;
2834
+ let folderName = null;
2835
+ if (opts.folder) {
2836
+ const data = await client.listFolders(artifact.workspaceId);
2837
+ const matches = data.folders.filter(
2838
+ (candidate) => candidate.name === opts.folder || candidate.id === opts.folder
2839
+ );
2840
+ if (matches.length === 0) {
2841
+ throw new Error(
2842
+ `No folder named "${opts.folder}" exists in this workspace \u2014 list them with \`artifact-cafe folders --workspace <handle>\`, or create it with \`folders create\`.`
2843
+ );
2844
+ }
2845
+ if (matches.length > 1) {
2846
+ throw new Error(
2847
+ `More than one folder is named "${opts.folder}"; pass the folder id instead.`
2848
+ );
2849
+ }
2850
+ folderId = matches[0].id;
2851
+ folderName = matches[0].name;
2852
+ }
2853
+ if (artifact.folderId === folderId) {
2854
+ const where = folderName ? `folder "${folderName}"` : "no folder";
2855
+ if (opts.json) {
2856
+ console.log(
2857
+ JSON.stringify(
2858
+ {
2859
+ artifactId: artifact.id,
2860
+ folder: folderName ? { id: folderId, name: folderName } : null,
2861
+ moved: false,
2862
+ title: artifact.title,
2863
+ visibility: artifact.visibility
2864
+ },
2865
+ null,
2866
+ 2
2867
+ )
2868
+ );
2869
+ return;
2870
+ }
2871
+ console.log(`"${artifact.title}" is already in ${where} \u2014 nothing to do.`);
2872
+ return;
2873
+ }
2874
+ const updated = await client.updateArtifact(artifact.id, { folderId });
2875
+ if (opts.json) {
2876
+ console.log(
2877
+ JSON.stringify(
2878
+ {
2879
+ artifactId: updated.id,
2880
+ folder: folderName ? { id: folderId, name: folderName } : null,
2881
+ moved: true,
2882
+ previousVisibility: artifact.visibility,
2883
+ title: updated.title,
2884
+ visibility: updated.visibility
2885
+ },
2886
+ null,
2887
+ 2
2888
+ )
2889
+ );
2890
+ return;
2891
+ }
2892
+ const destination = folderName ? `"${folderName}"` : "Unfiled";
2893
+ console.log(`\u2713 Moved "${updated.title}" \u2192 ${destination}`);
2894
+ if (updated.visibility !== artifact.visibility) {
2895
+ console.log(
2896
+ ` Visibility: ${artifact.visibility} \u2192 ${updated.visibility} (folder default)`
2897
+ );
2898
+ if (updated.visibility === "public") {
2899
+ console.log(" Now public \u2014 visible to anyone.");
2900
+ }
2901
+ }
2902
+ }
2903
+
2904
+ // src/open.ts
2905
+ import path11 from "path";
2906
+ async function openArtifact(opts) {
2907
+ const dir = path11.resolve(opts.dir);
2908
+ const target = resolveArtifactTarget(dir, {
2909
+ apiKey: opts.apiKey,
2910
+ artifact: opts.artifact
2911
+ });
2580
2912
  const url = target.url ?? reviewUrl(
2581
2913
  target.apiUrl,
2582
2914
  (await createApiClient(target.apiUrl, target.credential).getArtifact(
@@ -2590,7 +2922,7 @@ async function openArtifact(opts) {
2590
2922
  }
2591
2923
 
2592
2924
  // src/protect.ts
2593
- import path11 from "path";
2925
+ import path12 from "path";
2594
2926
  async function protect(opts) {
2595
2927
  if (opts.off && opts.password !== void 0) {
2596
2928
  throw new Error("Pass either --password or --off, not both.");
@@ -2607,7 +2939,7 @@ async function protect(opts) {
2607
2939
  throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
2608
2940
  }
2609
2941
  }
2610
- const dir = path11.resolve(opts.dir);
2942
+ const dir = path12.resolve(opts.dir);
2611
2943
  const target = resolveArtifactTarget(dir, {
2612
2944
  apiKey: opts.apiKey,
2613
2945
  artifact: opts.artifact
@@ -2632,7 +2964,7 @@ async function protect(opts) {
2632
2964
  }
2633
2965
 
2634
2966
  // src/reply.ts
2635
- import path12 from "path";
2967
+ import path13 from "path";
2636
2968
  async function readStdin3() {
2637
2969
  if (process.stdin.isTTY) {
2638
2970
  return "";
@@ -2656,7 +2988,7 @@ async function replyToThread(opts) {
2656
2988
  'Nothing to post. Pass the reply with --body "\u2026", or pipe it on stdin.'
2657
2989
  );
2658
2990
  }
2659
- const dir = path12.resolve(opts.dir);
2991
+ const dir = path13.resolve(opts.dir);
2660
2992
  const target = resolveArtifactTarget(dir, {
2661
2993
  apiKey: opts.apiKey,
2662
2994
  artifact: opts.artifact
@@ -2685,7 +3017,7 @@ async function replyToThread(opts) {
2685
3017
  }
2686
3018
 
2687
3019
  // src/resolve.ts
2688
- import path13 from "path";
3020
+ import path14 from "path";
2689
3021
  async function changeThreadStatus(opts) {
2690
3022
  const threadId = opts.thread?.trim();
2691
3023
  if (!threadId) {
@@ -2693,7 +3025,7 @@ async function changeThreadStatus(opts) {
2693
3025
  `Pass the thread id to ${opts.action} \u2014 get it from \`artifact-cafe comments --json\`.`
2694
3026
  );
2695
3027
  }
2696
- const dir = path13.resolve(opts.dir);
3028
+ const dir = path14.resolve(opts.dir);
2697
3029
  const target = resolveArtifactTarget(dir, {
2698
3030
  apiKey: opts.apiKey,
2699
3031
  artifact: opts.artifact
@@ -2729,11 +3061,47 @@ async function changeThreadStatus(opts) {
2729
3061
  console.log(`\u2713 ${verb} ${thread.id} \u2014 now ${thread.status}.`);
2730
3062
  }
2731
3063
 
3064
+ // src/review.ts
3065
+ import path15 from "path";
3066
+ var OUTCOME = {
3067
+ off: "Plain artifact \u2014 the review tools are hidden entirely",
3068
+ open: "Open for review \u2014 anyone who can open it can comment",
3069
+ readonly: "Read-only \u2014 earlier comments stay visible, no new ones"
3070
+ };
3071
+ async function review(opts) {
3072
+ const parsed = reviewModeSchema.safeParse(opts.mode);
3073
+ if (!parsed.success) {
3074
+ throw new Error(
3075
+ "Provide a mode: open, readonly, or off. `readonly` keeps existing comments readable and refuses new ones; `off` hides the review layer entirely."
3076
+ );
3077
+ }
3078
+ const mode = parsed.data;
3079
+ const dir = path15.resolve(opts.dir);
3080
+ const target = resolveArtifactTarget(dir, {
3081
+ apiKey: opts.apiKey,
3082
+ artifact: opts.artifact
3083
+ });
3084
+ const client = createApiClient(target.apiUrl, target.credential);
3085
+ const res = await client.setReviewMode(target.artifactId, mode);
3086
+ if (opts.json) {
3087
+ console.log(JSON.stringify({ reviewMode: res.mode }, null, 2));
3088
+ return;
3089
+ }
3090
+ const url = target.url ?? reviewUrl(
3091
+ target.apiUrl,
3092
+ (await client.getArtifact(target.artifactId)).slug
3093
+ );
3094
+ console.log(`
3095
+ \u2713 ${OUTCOME[res.mode]}
3096
+ ${url}
3097
+ `);
3098
+ }
3099
+
2732
3100
  // src/share.ts
2733
- import path14 from "path";
3101
+ import path16 from "path";
2734
3102
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2735
3103
  async function share(opts) {
2736
- const dir = path14.resolve(opts.dir);
3104
+ const dir = path16.resolve(opts.dir);
2737
3105
  const target = resolveArtifactTarget(dir, {
2738
3106
  apiKey: opts.apiKey,
2739
3107
  artifact: opts.artifact
@@ -2788,7 +3156,7 @@ General access: ${visibility}`);
2788
3156
  console.log("");
2789
3157
  }
2790
3158
  async function unshare(opts) {
2791
- const dir = path14.resolve(opts.dir);
3159
+ const dir = path16.resolve(opts.dir);
2792
3160
  const target = resolveArtifactTarget(dir, {
2793
3161
  apiKey: opts.apiKey,
2794
3162
  artifact: opts.artifact
@@ -2811,7 +3179,7 @@ async function unshare(opts) {
2811
3179
  }
2812
3180
 
2813
3181
  // src/status.ts
2814
- import path15 from "path";
3182
+ import path17 from "path";
2815
3183
  async function status(opts) {
2816
3184
  if (!AGENT_STATES.includes(opts.state)) {
2817
3185
  throw new Error(
@@ -2819,7 +3187,7 @@ async function status(opts) {
2819
3187
  );
2820
3188
  }
2821
3189
  const state = opts.state;
2822
- const dir = path15.resolve(opts.dir);
3190
+ const dir = path17.resolve(opts.dir);
2823
3191
  const target = resolveArtifactTarget(dir, {
2824
3192
  apiKey: opts.apiKey,
2825
3193
  artifact: opts.artifact
@@ -2848,26 +3216,26 @@ async function status(opts) {
2848
3216
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
2849
3217
  import { createRequire } from "module";
2850
3218
  import { homedir as homedir2 } from "os";
2851
- import path16 from "path";
2852
- import { z as z19 } from "zod";
3219
+ import path18 from "path";
3220
+ import { z as z20 } from "zod";
2853
3221
  var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
2854
3222
  var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
2855
3223
  var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
2856
3224
  var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
2857
3225
  var FETCH_TIMEOUT_MS = 2e3;
2858
3226
  var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
2859
- var cacheSchema = z19.object({
2860
- checkedAt: z19.number(),
2861
- cliLatest: z19.string().nullable(),
2862
- skillLatest: z19.string().nullable(),
2863
- version: z19.literal(1)
3227
+ var cacheSchema = z20.object({
3228
+ checkedAt: z20.number(),
3229
+ cliLatest: z20.string().nullable(),
3230
+ skillLatest: z20.string().nullable(),
3231
+ version: z20.literal(1)
2864
3232
  });
2865
3233
  function cliVersion() {
2866
3234
  const pkg = createRequire(import.meta.url)("../package.json");
2867
3235
  return pkg.version;
2868
3236
  }
2869
3237
  function cachePath() {
2870
- return path16.join(homedir2(), ".artifactcafe", "update-check.json");
3238
+ return path18.join(homedir2(), ".artifactcafe", "update-check.json");
2871
3239
  }
2872
3240
  function readCache() {
2873
3241
  try {
@@ -2878,7 +3246,7 @@ function readCache() {
2878
3246
  }
2879
3247
  function writeCache(cache) {
2880
3248
  try {
2881
- mkdirSync3(path16.dirname(cachePath()), { mode: 448, recursive: true });
3249
+ mkdirSync3(path18.dirname(cachePath()), { mode: 448, recursive: true });
2882
3250
  writeFileSync3(cachePath(), `${JSON.stringify(cache, null, 2)}
2883
3251
  `);
2884
3252
  } catch {
@@ -2915,7 +3283,7 @@ async function fetchText(url) {
2915
3283
  }
2916
3284
  async function fetchLatestVersions(previous) {
2917
3285
  const [cli, skill] = await Promise.all([
2918
- fetchJson(REGISTRY_LATEST_URL).then((body) => z19.object({ version: z19.string() }).parse(body).version).catch(() => null),
3286
+ fetchJson(REGISTRY_LATEST_URL).then((body) => z20.object({ version: z20.string() }).parse(body).version).catch(() => null),
2919
3287
  fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
2920
3288
  ]);
2921
3289
  return {
@@ -2931,7 +3299,7 @@ function installedSkillVersions() {
2931
3299
  const versions = [];
2932
3300
  for (const root of roots) {
2933
3301
  for (const dir of dirs) {
2934
- const file = path16.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
3302
+ const file = path18.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
2935
3303
  if (!existsSync3(file)) {
2936
3304
  continue;
2937
3305
  }
@@ -3072,6 +3440,10 @@ var publish2 = defineCommand({
3072
3440
  description: "Password-protect the artifact (min 6 chars)",
3073
3441
  type: "string"
3074
3442
  },
3443
+ review: {
3444
+ description: "Review mode: open (default), readonly, or off (plain artifact)",
3445
+ type: "string"
3446
+ },
3075
3447
  title: { description: "Artifact title", type: "string" },
3076
3448
  workspace: {
3077
3449
  description: "Publish into this workspace handle",
@@ -3091,6 +3463,7 @@ var publish2 = defineCommand({
3091
3463
  notes: readNotesArg(process.argv.slice(2)),
3092
3464
  open: !args["no-open"],
3093
3465
  password: args.password,
3466
+ review: args.review,
3094
3467
  title: args.title,
3095
3468
  workspace: args.workspace
3096
3469
  });
@@ -3161,7 +3534,11 @@ var list2 = defineCommand({
3161
3534
  description: "List with this API key (default: stored login)",
3162
3535
  type: "string"
3163
3536
  },
3164
- json: { description: "Machine-readable output", type: "boolean" }
3537
+ json: { description: "Machine-readable output", type: "boolean" },
3538
+ workspace: {
3539
+ description: "List this workspace's artifacts, grouped by folder",
3540
+ type: "string"
3541
+ }
3165
3542
  },
3166
3543
  meta: {
3167
3544
  description: "List the artifacts your account owns",
@@ -3169,7 +3546,11 @@ var list2 = defineCommand({
3169
3546
  },
3170
3547
  async run({ args }) {
3171
3548
  try {
3172
- await list({ apiKey: args["api-key"], json: Boolean(args.json) });
3549
+ await list({
3550
+ apiKey: args["api-key"],
3551
+ json: Boolean(args.json),
3552
+ workspace: args.workspace
3553
+ });
3173
3554
  } catch (err) {
3174
3555
  printCliError(err, Boolean(args.json));
3175
3556
  process.exit(1);
@@ -3178,26 +3559,43 @@ var list2 = defineCommand({
3178
3559
  });
3179
3560
  var folders2 = defineCommand({
3180
3561
  args: {
3562
+ action: {
3563
+ description: 'Optional action: "create"',
3564
+ required: false,
3565
+ type: "positional"
3566
+ },
3181
3567
  "api-key": {
3182
- description: "List with this API key (default: stored login)",
3568
+ description: "Authorize with an account API key (default: stored login)",
3183
3569
  type: "string"
3184
3570
  },
3185
3571
  json: { description: "Machine-readable output", type: "boolean" },
3572
+ name: {
3573
+ description: "Folder name (with create)",
3574
+ required: false,
3575
+ type: "positional"
3576
+ },
3577
+ visibility: {
3578
+ description: "With create: workspace (default) | public",
3579
+ type: "string"
3580
+ },
3186
3581
  workspace: {
3187
- description: "Workspace handle whose folders to list",
3582
+ description: "Workspace handle to list or create in",
3188
3583
  required: true,
3189
3584
  type: "string"
3190
3585
  }
3191
3586
  },
3192
3587
  meta: {
3193
- description: "List folders in a workspace",
3588
+ description: "List a workspace's folders, or create one (folders create)",
3194
3589
  name: "folders"
3195
3590
  },
3196
3591
  async run({ args }) {
3197
3592
  try {
3198
3593
  await folders({
3594
+ action: args.action,
3199
3595
  apiKey: args["api-key"],
3200
3596
  json: Boolean(args.json),
3597
+ name: args.name,
3598
+ visibility: args.visibility,
3201
3599
  workspace: args.workspace
3202
3600
  });
3203
3601
  } catch (err) {
@@ -3206,6 +3604,51 @@ var folders2 = defineCommand({
3206
3604
  }
3207
3605
  }
3208
3606
  });
3607
+ var move2 = defineCommand({
3608
+ args: {
3609
+ "api-key": {
3610
+ description: "Authorize with an account API key (default: stored login)",
3611
+ type: "string"
3612
+ },
3613
+ artifact: {
3614
+ description: "Move this artifact ID instead of the linked folder",
3615
+ type: "string"
3616
+ },
3617
+ dir: {
3618
+ default: ".",
3619
+ description: "Linked directory to resolve the artifact from (default: .)",
3620
+ type: "string"
3621
+ },
3622
+ folder: {
3623
+ description: "Target folder name (or id) in the artifact's workspace",
3624
+ type: "string"
3625
+ },
3626
+ json: { description: "Machine-readable output", type: "boolean" },
3627
+ unfiled: {
3628
+ description: "Move out of its folder (workspace root)",
3629
+ type: "boolean"
3630
+ }
3631
+ },
3632
+ meta: {
3633
+ description: "Move an artifact into a workspace folder (or --unfiled)",
3634
+ name: "move"
3635
+ },
3636
+ async run({ args }) {
3637
+ try {
3638
+ await move({
3639
+ apiKey: args["api-key"],
3640
+ artifact: args.artifact,
3641
+ dir: args.dir,
3642
+ folder: args.folder,
3643
+ json: Boolean(args.json),
3644
+ unfiled: Boolean(args.unfiled)
3645
+ });
3646
+ } catch (err) {
3647
+ printCliError(err, Boolean(args.json));
3648
+ process.exit(1);
3649
+ }
3650
+ }
3651
+ });
3209
3652
  var workspaces2 = defineCommand({
3210
3653
  args: {
3211
3654
  "api-key": {
@@ -3474,6 +3917,47 @@ var protect2 = defineCommand({
3474
3917
  }
3475
3918
  }
3476
3919
  });
3920
+ var review2 = defineCommand({
3921
+ args: {
3922
+ "api-key": {
3923
+ description: "Authorize with an account API key (default: stored login)",
3924
+ type: "string"
3925
+ },
3926
+ artifact: {
3927
+ description: "Act on an artifact ID instead of the linked folder",
3928
+ type: "string"
3929
+ },
3930
+ dir: {
3931
+ default: ".",
3932
+ description: "Published directory",
3933
+ type: "string"
3934
+ },
3935
+ json: { description: "Machine-readable output", type: "boolean" },
3936
+ mode: {
3937
+ description: "open, readonly, or off (plain artifact)",
3938
+ required: true,
3939
+ type: "positional"
3940
+ }
3941
+ },
3942
+ meta: {
3943
+ description: "Open, close, or freeze review on the linked artifact",
3944
+ name: "review"
3945
+ },
3946
+ async run({ args }) {
3947
+ try {
3948
+ await review({
3949
+ apiKey: args["api-key"],
3950
+ artifact: args.artifact,
3951
+ dir: args.dir,
3952
+ json: Boolean(args.json),
3953
+ mode: args.mode
3954
+ });
3955
+ } catch (err) {
3956
+ printCliError(err, Boolean(args.json));
3957
+ process.exit(1);
3958
+ }
3959
+ }
3960
+ });
3477
3961
  var threadStatusCommand = (action) => defineCommand({
3478
3962
  args: {
3479
3963
  "api-key": {
@@ -3884,12 +4368,14 @@ var main = defineCommand({
3884
4368
  listen,
3885
4369
  login: login2,
3886
4370
  logout: logout2,
4371
+ move: move2,
3887
4372
  open,
3888
4373
  protect: protect2,
3889
4374
  publish: publish2,
3890
4375
  reopen,
3891
4376
  reply,
3892
4377
  resolve,
4378
+ review: review2,
3893
4379
  share: share2,
3894
4380
  status: statusCmd,
3895
4381
  unshare: unshare2,