artifact-cafe 0.0.5 → 0.0.8
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.
- package/dist/index.js +993 -145
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -112,8 +112,9 @@ var anchorSchema = z2.discriminatedUnion("type", [
|
|
|
112
112
|
pointAnchorSchema,
|
|
113
113
|
textAnchorSchema
|
|
114
114
|
]);
|
|
115
|
+
var displayNameSchema = z2.string().trim().min(1).max(64);
|
|
115
116
|
var guestSchema = z2.object({
|
|
116
|
-
displayName:
|
|
117
|
+
displayName: displayNameSchema,
|
|
117
118
|
guestId: z2.string().min(16)
|
|
118
119
|
});
|
|
119
120
|
var createCommentThreadSchema = z2.object({
|
|
@@ -130,8 +131,11 @@ var threadActionSchema = z2.object({
|
|
|
130
131
|
guest: guestSchema.optional()
|
|
131
132
|
});
|
|
132
133
|
var commentAuthorSchema = z2.object({
|
|
134
|
+
avatarUrl: z2.string().nullable(),
|
|
133
135
|
displayName: z2.string(),
|
|
134
|
-
guestId: z2.string().nullable()
|
|
136
|
+
guestId: z2.string().nullable(),
|
|
137
|
+
isViewer: z2.boolean(),
|
|
138
|
+
kind: z2.enum(["member", "guest"])
|
|
135
139
|
});
|
|
136
140
|
var commentMessageSchema = z2.object({
|
|
137
141
|
author: commentAuthorSchema,
|
|
@@ -158,12 +162,14 @@ var commentExportQuerySchema = z2.object({
|
|
|
158
162
|
});
|
|
159
163
|
var commentExportReplySchema = z2.object({
|
|
160
164
|
author: z2.string(),
|
|
165
|
+
authorKind: z2.enum(["member", "guest"]),
|
|
161
166
|
body: z2.string(),
|
|
162
167
|
createdAt: z2.string()
|
|
163
168
|
});
|
|
164
169
|
var commentExportThreadSchema = z2.object({
|
|
165
170
|
anchor: anchorSchema,
|
|
166
171
|
author: z2.string(),
|
|
172
|
+
authorKind: z2.enum(["member", "guest"]),
|
|
167
173
|
body: z2.string(),
|
|
168
174
|
createdAt: z2.string(),
|
|
169
175
|
id: z2.string(),
|
|
@@ -199,6 +205,17 @@ import { z as z4 } from "zod";
|
|
|
199
205
|
var ownArtifactSchema = z4.object({
|
|
200
206
|
createdAt: z4.string(),
|
|
201
207
|
currentVersionNumber: z4.number().nullable(),
|
|
208
|
+
// Owned artifacts normally never expire; non-null only while an
|
|
209
|
+
// expiry is still set (e.g. an unclaimed publish surfaced here later).
|
|
210
|
+
expiresAt: z4.string().nullable(),
|
|
211
|
+
// SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
|
|
212
|
+
// both null for personal artifacts. `folderId` drives the dashboard's
|
|
213
|
+
// move control and folder filter; `folderName` is the display label.
|
|
214
|
+
folderId: z4.string().nullable(),
|
|
215
|
+
folderName: z4.string().nullable(),
|
|
216
|
+
// Whether a publish token is still active — revocable from the
|
|
217
|
+
// dashboard (§6.18); the token itself is never returned.
|
|
218
|
+
hasPublishToken: z4.boolean(),
|
|
202
219
|
id: z4.string(),
|
|
203
220
|
openThreads: z4.number(),
|
|
204
221
|
protected: z4.boolean(),
|
|
@@ -212,64 +229,217 @@ var ownArtifactSchema = z4.object({
|
|
|
212
229
|
var ownArtifactsResponseSchema = z4.object({
|
|
213
230
|
artifacts: z4.array(ownArtifactSchema)
|
|
214
231
|
});
|
|
232
|
+
var revokePublishTokenResponseSchema = z4.object({
|
|
233
|
+
revoked: z4.boolean()
|
|
234
|
+
});
|
|
215
235
|
|
|
216
236
|
// ../shared/src/owner.ts
|
|
237
|
+
import { z as z6 } from "zod";
|
|
238
|
+
|
|
239
|
+
// ../shared/src/workspaces.ts
|
|
217
240
|
import { z as z5 } from "zod";
|
|
218
|
-
var
|
|
219
|
-
var
|
|
220
|
-
|
|
241
|
+
var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
|
|
242
|
+
var workspaceRoleSchema = z5.enum(["owner", "admin", "member"]);
|
|
243
|
+
var workspaceInviteRoleSchema = z5.enum(["admin", "member"]);
|
|
244
|
+
var workspaceHandleSchema = z5.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
|
|
245
|
+
message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
246
|
+
});
|
|
247
|
+
var createWorkspaceSchema = z5.object({
|
|
248
|
+
handle: workspaceHandleSchema.optional(),
|
|
249
|
+
name: z5.string().trim().min(1).max(100)
|
|
250
|
+
});
|
|
251
|
+
var updateWorkspaceSchema = z5.object({
|
|
252
|
+
handle: workspaceHandleSchema.optional(),
|
|
253
|
+
name: z5.string().trim().min(1).max(100).optional()
|
|
254
|
+
}).refine((body) => body.handle !== void 0 || body.name !== void 0, {
|
|
255
|
+
message: "provide at least one of name or handle"
|
|
256
|
+
});
|
|
257
|
+
var workspaceSchema = z5.object({
|
|
258
|
+
createdAt: z5.string(),
|
|
259
|
+
handle: z5.string(),
|
|
260
|
+
id: z5.string(),
|
|
261
|
+
name: z5.string(),
|
|
262
|
+
role: workspaceRoleSchema.nullable(),
|
|
263
|
+
updatedAt: z5.string()
|
|
264
|
+
});
|
|
265
|
+
var workspaceListResponseSchema = z5.object({
|
|
266
|
+
workspaces: z5.array(workspaceSchema)
|
|
267
|
+
});
|
|
268
|
+
var deleteWorkspaceResponseSchema = z5.object({
|
|
269
|
+
deleted: z5.literal(true)
|
|
270
|
+
});
|
|
271
|
+
var workspaceMemberSchema = z5.object({
|
|
272
|
+
createdAt: z5.string(),
|
|
273
|
+
email: z5.string().email(),
|
|
274
|
+
image: z5.string().nullable(),
|
|
275
|
+
name: z5.string(),
|
|
276
|
+
role: workspaceRoleSchema,
|
|
277
|
+
userId: z5.string()
|
|
278
|
+
});
|
|
279
|
+
var workspaceMemberListResponseSchema = z5.object({
|
|
280
|
+
members: z5.array(workspaceMemberSchema)
|
|
281
|
+
});
|
|
282
|
+
var updateWorkspaceMemberSchema = z5.object({
|
|
283
|
+
role: workspaceRoleSchema
|
|
284
|
+
});
|
|
285
|
+
var removeWorkspaceMemberResponseSchema = z5.object({
|
|
286
|
+
removed: z5.literal(true)
|
|
287
|
+
});
|
|
288
|
+
var createWorkspaceInviteSchema = z5.object({
|
|
289
|
+
email: z5.string().trim().toLowerCase().email().optional(),
|
|
290
|
+
expiresAt: z5.string().datetime(),
|
|
291
|
+
maxUses: z5.number().int().positive().optional(),
|
|
292
|
+
role: workspaceInviteRoleSchema
|
|
293
|
+
});
|
|
294
|
+
var workspaceInviteSchema = z5.object({
|
|
295
|
+
createdAt: z5.string(),
|
|
296
|
+
email: z5.string().email().nullable(),
|
|
297
|
+
expiresAt: z5.string(),
|
|
298
|
+
id: z5.string(),
|
|
299
|
+
maxUses: z5.number().int().positive().nullable(),
|
|
300
|
+
role: workspaceInviteRoleSchema,
|
|
301
|
+
tokenPrefix: z5.string(),
|
|
302
|
+
useCount: z5.number().int().nonnegative()
|
|
303
|
+
});
|
|
304
|
+
var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
|
|
305
|
+
url: z5.string().url()
|
|
306
|
+
});
|
|
307
|
+
var workspaceInviteListResponseSchema = z5.object({
|
|
308
|
+
invites: z5.array(workspaceInviteSchema)
|
|
309
|
+
});
|
|
310
|
+
var revokeWorkspaceInviteResponseSchema = z5.object({
|
|
311
|
+
revoked: z5.literal(true)
|
|
312
|
+
});
|
|
313
|
+
var acceptWorkspaceInviteSchema = z5.object({
|
|
314
|
+
token: z5.string().min(1)
|
|
315
|
+
});
|
|
316
|
+
var acceptWorkspaceInviteResponseSchema = z5.object({
|
|
317
|
+
role: workspaceRoleSchema,
|
|
318
|
+
workspace: workspaceSchema
|
|
319
|
+
});
|
|
320
|
+
var folderVisibilitySchema = z5.enum(["public", "workspace"]);
|
|
321
|
+
var artifactVisibilitySchema = z5.enum([
|
|
322
|
+
"public",
|
|
323
|
+
"unlisted",
|
|
324
|
+
"workspace"
|
|
325
|
+
]);
|
|
326
|
+
var createFolderSchema = z5.object({
|
|
327
|
+
name: z5.string().trim().min(1).max(100),
|
|
328
|
+
visibility: folderVisibilitySchema
|
|
329
|
+
});
|
|
330
|
+
var updateFolderSchema = z5.object({
|
|
331
|
+
// With `visibility`: bulk-update the folder's artifacts to match
|
|
332
|
+
// (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
|
|
333
|
+
cascade: z5.boolean().optional(),
|
|
334
|
+
name: z5.string().trim().min(1).max(100).optional(),
|
|
335
|
+
sortOrder: z5.number().int().min(0).optional(),
|
|
336
|
+
visibility: folderVisibilitySchema.optional()
|
|
337
|
+
}).refine(
|
|
338
|
+
(body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0,
|
|
339
|
+
{ message: "provide at least one of name, visibility, sortOrder" }
|
|
340
|
+
).refine((body) => !body.cascade || body.visibility !== void 0, {
|
|
341
|
+
message: "cascade requires visibility"
|
|
342
|
+
});
|
|
343
|
+
var folderSchema = z5.object({
|
|
344
|
+
createdAt: z5.string(),
|
|
345
|
+
id: z5.string(),
|
|
346
|
+
name: z5.string(),
|
|
347
|
+
// Permalink segment for the public folder page (/w/{handle}/f/{slug}).
|
|
348
|
+
// Server-derived from the name; renames regenerate it.
|
|
349
|
+
slug: z5.string(),
|
|
350
|
+
sortOrder: z5.number().int(),
|
|
351
|
+
updatedAt: z5.string(),
|
|
352
|
+
visibility: folderVisibilitySchema,
|
|
353
|
+
workspaceId: z5.string()
|
|
354
|
+
});
|
|
355
|
+
var folderListResponseSchema = z5.object({
|
|
356
|
+
folders: z5.array(folderSchema)
|
|
357
|
+
});
|
|
358
|
+
var deleteFolderResponseSchema = z5.object({
|
|
359
|
+
deleted: z5.literal(true)
|
|
360
|
+
});
|
|
361
|
+
var workspaceArtifactSchema = z5.object({
|
|
362
|
+
createdAt: z5.string(),
|
|
363
|
+
currentVersionNumber: z5.number().int().nullable(),
|
|
364
|
+
folderId: z5.string().nullable(),
|
|
365
|
+
id: z5.string(),
|
|
366
|
+
openCommentCount: z5.number().int().nonnegative(),
|
|
221
367
|
slug: z5.string(),
|
|
222
368
|
title: z5.string(),
|
|
223
|
-
|
|
369
|
+
updatedAt: z5.string(),
|
|
370
|
+
url: z5.string().url(),
|
|
371
|
+
versionCount: z5.number().int().nonnegative(),
|
|
372
|
+
visibility: artifactVisibilitySchema
|
|
373
|
+
});
|
|
374
|
+
var workspaceArtifactListResponseSchema = z5.object({
|
|
375
|
+
artifacts: z5.array(workspaceArtifactSchema)
|
|
224
376
|
});
|
|
225
|
-
|
|
226
|
-
|
|
377
|
+
|
|
378
|
+
// ../shared/src/owner.ts
|
|
379
|
+
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
|
|
380
|
+
var claimResponseSchema = z6.object({
|
|
381
|
+
artifactId: z6.string(),
|
|
382
|
+
slug: z6.string(),
|
|
383
|
+
title: z6.string(),
|
|
384
|
+
url: z6.string().url()
|
|
385
|
+
});
|
|
386
|
+
var updateArtifactSchema = z6.object({
|
|
387
|
+
// SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
|
|
388
|
+
// folder; `workspaceId: null` moves the artifact out of its workspace
|
|
389
|
+
// (§5.5 ownership transfer).
|
|
390
|
+
folderId: z6.string().min(1).nullable().optional(),
|
|
391
|
+
slug: z6.string().min(3).max(63).regex(SLUG_PATTERN, {
|
|
227
392
|
message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
228
393
|
}).optional(),
|
|
229
|
-
title:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
|
|
394
|
+
title: z6.string().min(1).max(200).optional(),
|
|
395
|
+
visibility: artifactVisibilitySchema.optional(),
|
|
396
|
+
workspaceId: z6.string().min(1).nullable().optional()
|
|
397
|
+
}).refine(
|
|
398
|
+
(body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0,
|
|
399
|
+
{
|
|
400
|
+
message: "provide at least one of title, slug, visibility, folderId, workspaceId"
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
var deleteArtifactResponseSchema = z6.object({
|
|
404
|
+
deleted: z6.literal(true)
|
|
235
405
|
});
|
|
236
406
|
|
|
237
407
|
// ../shared/src/presence.ts
|
|
238
|
-
import { z as
|
|
408
|
+
import { z as z7 } from "zod";
|
|
239
409
|
var AGENT_PRESENCE = {
|
|
240
410
|
heartbeatSeconds: 15,
|
|
241
411
|
shellPollSeconds: 15,
|
|
242
412
|
ttlSeconds: 45
|
|
243
413
|
};
|
|
244
|
-
var agentHeartbeatResponseSchema =
|
|
245
|
-
listening:
|
|
246
|
-
ttlSeconds:
|
|
414
|
+
var agentHeartbeatResponseSchema = z7.object({
|
|
415
|
+
listening: z7.literal(true),
|
|
416
|
+
ttlSeconds: z7.number().int().positive()
|
|
247
417
|
});
|
|
248
|
-
var agentPresenceSchema =
|
|
249
|
-
listening:
|
|
250
|
-
versionNumber:
|
|
418
|
+
var agentPresenceSchema = z7.object({
|
|
419
|
+
listening: z7.boolean(),
|
|
420
|
+
versionNumber: z7.number().int().positive().nullable()
|
|
251
421
|
});
|
|
252
422
|
|
|
253
423
|
// ../shared/src/protection.ts
|
|
254
|
-
import { z as
|
|
424
|
+
import { z as z8 } from "zod";
|
|
255
425
|
var PASSWORD_MIN = 6;
|
|
256
426
|
var PASSWORD_MAX = 128;
|
|
257
|
-
var gatePasswordSchema =
|
|
258
|
-
var setProtectionSchema =
|
|
427
|
+
var gatePasswordSchema = z8.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
|
|
428
|
+
var setProtectionSchema = z8.object({
|
|
259
429
|
password: gatePasswordSchema.nullable()
|
|
260
430
|
});
|
|
261
|
-
var protectionResponseSchema =
|
|
262
|
-
protected:
|
|
431
|
+
var protectionResponseSchema = z8.object({
|
|
432
|
+
protected: z8.boolean()
|
|
263
433
|
});
|
|
264
|
-
var unlockSchema =
|
|
265
|
-
password:
|
|
434
|
+
var unlockSchema = z8.object({
|
|
435
|
+
password: z8.string().min(1).max(1024)
|
|
266
436
|
});
|
|
267
|
-
var unlockResponseSchema =
|
|
268
|
-
unlocked:
|
|
437
|
+
var unlockResponseSchema = z8.object({
|
|
438
|
+
unlocked: z8.literal(true)
|
|
269
439
|
});
|
|
270
440
|
|
|
271
441
|
// ../shared/src/publish.ts
|
|
272
|
-
import { z as
|
|
442
|
+
import { z as z9 } from "zod";
|
|
273
443
|
function isSafeArtifactPath(p) {
|
|
274
444
|
if (p.length === 0 || p.length > 1024) {
|
|
275
445
|
return false;
|
|
@@ -282,128 +452,136 @@ function isSafeArtifactPath(p) {
|
|
|
282
452
|
}
|
|
283
453
|
return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
|
|
284
454
|
}
|
|
285
|
-
var manifestFileSchema =
|
|
286
|
-
hash:
|
|
287
|
-
mimeType:
|
|
288
|
-
path:
|
|
455
|
+
var manifestFileSchema = z9.object({
|
|
456
|
+
hash: z9.string().regex(/^sha256_[a-f0-9]{64}$/),
|
|
457
|
+
mimeType: z9.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
|
|
458
|
+
path: z9.string().min(1).max(1024).refine(isSafeArtifactPath, {
|
|
289
459
|
message: "path must be a safe relative path"
|
|
290
460
|
}),
|
|
291
|
-
size:
|
|
461
|
+
size: z9.number().int().positive()
|
|
292
462
|
});
|
|
293
|
-
var publishSourceSchema =
|
|
294
|
-
agent:
|
|
295
|
-
runId:
|
|
296
|
-
type:
|
|
463
|
+
var publishSourceSchema = z9.object({
|
|
464
|
+
agent: z9.string().optional(),
|
|
465
|
+
runId: z9.string().optional(),
|
|
466
|
+
type: z9.literal("cli")
|
|
297
467
|
});
|
|
298
|
-
var createPublishSessionSchema =
|
|
299
|
-
artifactId:
|
|
300
|
-
entryPath:
|
|
301
|
-
files:
|
|
468
|
+
var createPublishSessionSchema = z9.object({
|
|
469
|
+
artifactId: z9.string().nullable(),
|
|
470
|
+
entryPath: z9.string().optional(),
|
|
471
|
+
files: z9.array(manifestFileSchema).min(1).max(1e3),
|
|
472
|
+
// SPEC-2 §6.4 — workspace placement is available only to authenticated
|
|
473
|
+
// members; the API enforces membership and folder ownership.
|
|
474
|
+
folderId: z9.string().min(1).optional(),
|
|
302
475
|
source: publishSourceSchema,
|
|
303
|
-
title:
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
476
|
+
title: z9.string().max(200).optional(),
|
|
477
|
+
workspaceId: z9.string().min(1).optional()
|
|
478
|
+
});
|
|
479
|
+
var publishSessionResponseSchema = z9.object({
|
|
480
|
+
artifactId: z9.string(),
|
|
481
|
+
expiresAt: z9.string(),
|
|
482
|
+
filesToUpload: z9.array(
|
|
483
|
+
z9.object({
|
|
484
|
+
method: z9.literal("PUT"),
|
|
485
|
+
path: z9.string(),
|
|
486
|
+
uploadUrl: z9.string().url()
|
|
313
487
|
})
|
|
314
488
|
),
|
|
315
|
-
publishSessionId:
|
|
316
|
-
skippedFiles:
|
|
317
|
-
versionId:
|
|
489
|
+
publishSessionId: z9.string(),
|
|
490
|
+
skippedFiles: z9.array(z9.string()),
|
|
491
|
+
versionId: z9.string()
|
|
318
492
|
});
|
|
319
|
-
var finalizeResponseSchema =
|
|
320
|
-
artifactId:
|
|
493
|
+
var finalizeResponseSchema = z9.object({
|
|
494
|
+
artifactId: z9.string(),
|
|
321
495
|
// API-key publishes create the artifact already claimed (SPEC §5.4) —
|
|
322
496
|
// the CLI uses this to tell "claim URL pending" from "already yours".
|
|
323
|
-
claimed:
|
|
497
|
+
claimed: z9.boolean(),
|
|
324
498
|
// Returned only on first publish of a new artifact (one-time).
|
|
325
|
-
claimUrl:
|
|
326
|
-
publishToken:
|
|
327
|
-
url:
|
|
328
|
-
versionId:
|
|
329
|
-
versionNumber:
|
|
330
|
-
});
|
|
331
|
-
var artifactResponseSchema =
|
|
332
|
-
claimed:
|
|
333
|
-
currentVersion:
|
|
334
|
-
entryUrl:
|
|
335
|
-
id:
|
|
336
|
-
versionNumber:
|
|
499
|
+
claimUrl: z9.string().url().optional(),
|
|
500
|
+
publishToken: z9.string().optional(),
|
|
501
|
+
url: z9.string().url(),
|
|
502
|
+
versionId: z9.string(),
|
|
503
|
+
versionNumber: z9.number().int().positive()
|
|
504
|
+
});
|
|
505
|
+
var artifactResponseSchema = z9.object({
|
|
506
|
+
claimed: z9.boolean(),
|
|
507
|
+
currentVersion: z9.object({
|
|
508
|
+
entryUrl: z9.string().url(),
|
|
509
|
+
id: z9.string(),
|
|
510
|
+
versionNumber: z9.number().int().positive()
|
|
337
511
|
}).nullable(),
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
512
|
+
// SPEC-2 §6.4 — placement + visibility surface on the artifact.
|
|
513
|
+
folderId: z9.string().nullable(),
|
|
514
|
+
id: z9.string(),
|
|
515
|
+
slug: z9.string(),
|
|
516
|
+
title: z9.string(),
|
|
517
|
+
visibility: z9.enum(["public", "unlisted", "workspace"]),
|
|
518
|
+
workspaceId: z9.string().nullable()
|
|
341
519
|
});
|
|
342
520
|
|
|
343
521
|
// ../shared/src/report.ts
|
|
344
|
-
import { z as
|
|
345
|
-
var reportRequestSchema =
|
|
346
|
-
reason:
|
|
522
|
+
import { z as z10 } from "zod";
|
|
523
|
+
var reportRequestSchema = z10.object({
|
|
524
|
+
reason: z10.string().max(1e3).optional()
|
|
347
525
|
});
|
|
348
|
-
var reportResponseSchema =
|
|
349
|
-
reported:
|
|
526
|
+
var reportResponseSchema = z10.object({
|
|
527
|
+
reported: z10.literal(true)
|
|
350
528
|
});
|
|
351
529
|
|
|
352
530
|
// ../shared/src/stats.ts
|
|
353
|
-
import { z as
|
|
354
|
-
var avgViewSecondsSchema =
|
|
355
|
-
var versionViewStatsSchema =
|
|
531
|
+
import { z as z11 } from "zod";
|
|
532
|
+
var avgViewSecondsSchema = z11.number().int().nonnegative().nullable();
|
|
533
|
+
var versionViewStatsSchema = z11.object({
|
|
356
534
|
avgViewSeconds: avgViewSecondsSchema,
|
|
357
535
|
// Views from inside the review shell's iframe vs. direct visits to
|
|
358
536
|
// the render URL. views = embeddedViews + directViews.
|
|
359
|
-
directViews:
|
|
360
|
-
embeddedViews:
|
|
537
|
+
directViews: z11.number().int().nonnegative(),
|
|
538
|
+
embeddedViews: z11.number().int().nonnegative(),
|
|
361
539
|
// Distinct hashed viewer IPs across the version's lifetime.
|
|
362
|
-
uniqueViewers:
|
|
363
|
-
versionNumber:
|
|
364
|
-
views:
|
|
540
|
+
uniqueViewers: z11.number().int().nonnegative(),
|
|
541
|
+
versionNumber: z11.number().int().positive(),
|
|
542
|
+
views: z11.number().int().nonnegative()
|
|
365
543
|
});
|
|
366
|
-
var dailyViewsSchema =
|
|
544
|
+
var dailyViewsSchema = z11.object({
|
|
367
545
|
avgViewSeconds: avgViewSecondsSchema,
|
|
368
546
|
// UTC day, YYYY-MM-DD.
|
|
369
|
-
day:
|
|
370
|
-
uniqueViewers:
|
|
371
|
-
views:
|
|
547
|
+
day: z11.string(),
|
|
548
|
+
uniqueViewers: z11.number().int().nonnegative(),
|
|
549
|
+
views: z11.number().int().nonnegative()
|
|
372
550
|
});
|
|
373
|
-
var artifactStatsResponseSchema =
|
|
551
|
+
var artifactStatsResponseSchema = z11.object({
|
|
374
552
|
avgViewSeconds: avgViewSecondsSchema,
|
|
375
|
-
daily:
|
|
553
|
+
daily: z11.array(dailyViewsSchema),
|
|
376
554
|
// Distinct hashed viewer IPs across all versions and days.
|
|
377
|
-
totalUniqueViewers:
|
|
378
|
-
totalViews:
|
|
379
|
-
versions:
|
|
555
|
+
totalUniqueViewers: z11.number().int().nonnegative(),
|
|
556
|
+
totalViews: z11.number().int().nonnegative(),
|
|
557
|
+
versions: z11.array(versionViewStatsSchema)
|
|
380
558
|
});
|
|
381
559
|
|
|
382
560
|
// ../shared/src/versions.ts
|
|
383
|
-
import { z as
|
|
384
|
-
var versionSourceSchema =
|
|
385
|
-
agentName:
|
|
386
|
-
type:
|
|
561
|
+
import { z as z12 } from "zod";
|
|
562
|
+
var versionSourceSchema = z12.object({
|
|
563
|
+
agentName: z12.string().nullable(),
|
|
564
|
+
type: z12.string()
|
|
387
565
|
});
|
|
388
|
-
var versionChangesSchema =
|
|
389
|
-
added:
|
|
390
|
-
changed:
|
|
391
|
-
removed:
|
|
566
|
+
var versionChangesSchema = z12.object({
|
|
567
|
+
added: z12.array(z12.string()),
|
|
568
|
+
changed: z12.array(z12.string()),
|
|
569
|
+
removed: z12.array(z12.string())
|
|
392
570
|
});
|
|
393
|
-
var versionSummarySchema =
|
|
571
|
+
var versionSummarySchema = z12.object({
|
|
394
572
|
// Absent for the first version — there is no previous version to diff against.
|
|
395
573
|
changes: versionChangesSchema.nullable(),
|
|
396
|
-
createdAt:
|
|
397
|
-
entryUrl:
|
|
398
|
-
fileCount:
|
|
399
|
-
id:
|
|
400
|
-
isCurrent:
|
|
574
|
+
createdAt: z12.string(),
|
|
575
|
+
entryUrl: z12.string().url(),
|
|
576
|
+
fileCount: z12.number().int().nonnegative(),
|
|
577
|
+
id: z12.string(),
|
|
578
|
+
isCurrent: z12.boolean(),
|
|
401
579
|
source: versionSourceSchema,
|
|
402
|
-
totalSizeBytes:
|
|
403
|
-
versionNumber:
|
|
580
|
+
totalSizeBytes: z12.number().int().nonnegative(),
|
|
581
|
+
versionNumber: z12.number().int().positive()
|
|
404
582
|
});
|
|
405
|
-
var versionListResponseSchema =
|
|
406
|
-
versions:
|
|
583
|
+
var versionListResponseSchema = z12.object({
|
|
584
|
+
versions: z12.array(versionSummarySchema)
|
|
407
585
|
});
|
|
408
586
|
|
|
409
587
|
// src/api.ts
|
|
@@ -480,7 +658,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
480
658
|
});
|
|
481
659
|
return request(
|
|
482
660
|
`${base}/api/v1/artifacts/${artifactId}/comments/export?${params}`,
|
|
483
|
-
{ method: "GET" },
|
|
661
|
+
{ headers, method: "GET" },
|
|
484
662
|
(d) => commentExportSchema.parse(d)
|
|
485
663
|
);
|
|
486
664
|
},
|
|
@@ -494,7 +672,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
494
672
|
async getArtifact(artifactId) {
|
|
495
673
|
return request(
|
|
496
674
|
`${base}/api/v1/artifacts/${artifactId}`,
|
|
497
|
-
{ method: "GET" },
|
|
675
|
+
{ headers, method: "GET" },
|
|
498
676
|
(d) => artifactResponseSchema.parse(d)
|
|
499
677
|
);
|
|
500
678
|
},
|
|
@@ -507,6 +685,20 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
507
685
|
(d) => artifactListResponseSchema.parse(d)
|
|
508
686
|
);
|
|
509
687
|
},
|
|
688
|
+
async listFolders(workspaceId) {
|
|
689
|
+
return request(
|
|
690
|
+
`${base}/api/v1/workspaces/${workspaceId}/folders`,
|
|
691
|
+
{ headers, method: "GET" },
|
|
692
|
+
(d) => folderListResponseSchema.parse(d)
|
|
693
|
+
);
|
|
694
|
+
},
|
|
695
|
+
async listWorkspaces() {
|
|
696
|
+
return request(
|
|
697
|
+
`${base}/api/v1/workspaces`,
|
|
698
|
+
{ headers, method: "GET" },
|
|
699
|
+
(d) => workspaceListResponseSchema.parse(d)
|
|
700
|
+
);
|
|
701
|
+
},
|
|
510
702
|
// SPEC §6.14 — who the presented credential belongs to. Used by
|
|
511
703
|
// `login` to verify a pasted key and by `whoami`.
|
|
512
704
|
async me() {
|
|
@@ -516,6 +708,34 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
516
708
|
(d) => meResponseSchema.parse(d)
|
|
517
709
|
);
|
|
518
710
|
},
|
|
711
|
+
// SPEC §6.7 — reopen a resolved thread. Same auth as resolve.
|
|
712
|
+
async reopenThread(threadId) {
|
|
713
|
+
return request(
|
|
714
|
+
`${base}/api/v1/comment-threads/${threadId}/reopen`,
|
|
715
|
+
{ body: "{}", headers, method: "POST" },
|
|
716
|
+
(d) => commentThreadSchema.parse(d)
|
|
717
|
+
);
|
|
718
|
+
},
|
|
719
|
+
// SPEC §6.5 — post a reply into a thread. Authorized by this client's
|
|
720
|
+
// credential: the artifact's publish token (the reply renders as the
|
|
721
|
+
// agent) or the owner's account key (renders as the owner). No guest
|
|
722
|
+
// payload — the credential is the identity.
|
|
723
|
+
async replyThread(threadId, body) {
|
|
724
|
+
return request(
|
|
725
|
+
`${base}/api/v1/comment-threads/${threadId}/messages`,
|
|
726
|
+
{ body: JSON.stringify({ body }), headers, method: "POST" },
|
|
727
|
+
(d) => commentThreadSchema.parse(d)
|
|
728
|
+
);
|
|
729
|
+
},
|
|
730
|
+
// SPEC §6.6 — mark a thread resolved. Authorized by this client's
|
|
731
|
+
// credential: the artifact's publish token, or the owner's account key.
|
|
732
|
+
async resolveThread(threadId) {
|
|
733
|
+
return request(
|
|
734
|
+
`${base}/api/v1/comment-threads/${threadId}/resolve`,
|
|
735
|
+
{ body: "{}", headers, method: "POST" },
|
|
736
|
+
(d) => commentThreadSchema.parse(d)
|
|
737
|
+
);
|
|
738
|
+
},
|
|
519
739
|
// SPEC §6.16 — set a password (string) or clear it (null). Authorized
|
|
520
740
|
// by the publish token this client was created with.
|
|
521
741
|
async setProtection(artifactId, password) {
|
|
@@ -552,17 +772,17 @@ import {
|
|
|
552
772
|
writeFileSync as writeFileSync2
|
|
553
773
|
} from "fs";
|
|
554
774
|
import path2 from "path";
|
|
555
|
-
import { z as
|
|
775
|
+
import { z as z14 } from "zod";
|
|
556
776
|
|
|
557
777
|
// src/credentials.ts
|
|
558
778
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
559
779
|
import { homedir } from "os";
|
|
560
780
|
import path from "path";
|
|
561
|
-
import { z as
|
|
781
|
+
import { z as z13 } from "zod";
|
|
562
782
|
var DEFAULT_API_URL = "https://artifact.cafe";
|
|
563
|
-
var credentialsSchema =
|
|
564
|
-
keys:
|
|
565
|
-
version:
|
|
783
|
+
var credentialsSchema = z13.object({
|
|
784
|
+
keys: z13.record(z13.string(), z13.string()),
|
|
785
|
+
version: z13.literal(1)
|
|
566
786
|
});
|
|
567
787
|
function credentialsPath() {
|
|
568
788
|
return path.join(homedir(), ".artifactcafe", "credentials");
|
|
@@ -612,15 +832,19 @@ function resolveApiKey(apiUrl2, flag) {
|
|
|
612
832
|
}
|
|
613
833
|
|
|
614
834
|
// src/config.ts
|
|
615
|
-
var configSchema =
|
|
616
|
-
apiUrl:
|
|
617
|
-
artifactId:
|
|
835
|
+
var configSchema = z14.object({
|
|
836
|
+
apiUrl: z14.string(),
|
|
837
|
+
artifactId: z14.string(),
|
|
838
|
+
folder: z14.string().optional(),
|
|
839
|
+
folderId: z14.string().optional(),
|
|
618
840
|
// Optional: a folder linked by `link` to an artifact you own carries no
|
|
619
841
|
// token — owner actions authorize via your stored login instead. A first
|
|
620
842
|
// publish still writes the anonymous-mode token here.
|
|
621
|
-
publishToken:
|
|
622
|
-
slug:
|
|
623
|
-
url:
|
|
843
|
+
publishToken: z14.string().optional(),
|
|
844
|
+
slug: z14.string(),
|
|
845
|
+
url: z14.string(),
|
|
846
|
+
workspace: z14.string().optional(),
|
|
847
|
+
workspaceId: z14.string().optional()
|
|
624
848
|
});
|
|
625
849
|
var CONFIG_DIR = ".artifactcafe";
|
|
626
850
|
var CONFIG_FILE = "config.json";
|
|
@@ -874,8 +1098,8 @@ function printThread(t) {
|
|
|
874
1098
|
console.log(` \u21B3 at ${t.anchor.elementPath}`);
|
|
875
1099
|
}
|
|
876
1100
|
console.log(indent(t.body, " "));
|
|
877
|
-
for (const
|
|
878
|
-
console.log(indent(`${
|
|
1101
|
+
for (const reply2 of t.replies) {
|
|
1102
|
+
console.log(indent(`${reply2.author}: ${reply2.body}`, " "));
|
|
879
1103
|
}
|
|
880
1104
|
console.log("");
|
|
881
1105
|
}
|
|
@@ -1029,6 +1253,7 @@ async function publish(opts) {
|
|
|
1029
1253
|
const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
|
|
1030
1254
|
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
|
|
1031
1255
|
const apiKey = resolveApiKey(apiUrl2, opts.apiKey);
|
|
1256
|
+
const placement = await resolvePlacement({ apiKey, apiUrl: apiUrl2, config, opts });
|
|
1032
1257
|
const { files, skipped } = scanDirectory(dir);
|
|
1033
1258
|
if (files.length === 0) {
|
|
1034
1259
|
throw new Error(`Nothing to publish in ${dir} (after ignore rules).`);
|
|
@@ -1046,12 +1271,14 @@ async function publish(opts) {
|
|
|
1046
1271
|
artifactId,
|
|
1047
1272
|
entryPath,
|
|
1048
1273
|
files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
|
|
1274
|
+
...placement.folderId ? { folderId: placement.folderId } : {},
|
|
1049
1275
|
source: {
|
|
1050
1276
|
agent: process.env.ARTIFACT_CAFE_AGENT,
|
|
1051
1277
|
runId: process.env.ARTIFACT_CAFE_RUN_ID,
|
|
1052
1278
|
type: "cli"
|
|
1053
1279
|
},
|
|
1054
|
-
title: opts.title
|
|
1280
|
+
title: opts.title,
|
|
1281
|
+
...placement.workspaceId ? { workspaceId: placement.workspaceId } : {}
|
|
1055
1282
|
};
|
|
1056
1283
|
let credential = apiKey ?? token;
|
|
1057
1284
|
let client = createApiClient(apiUrl2, credential);
|
|
@@ -1083,13 +1310,19 @@ async function publish(opts) {
|
|
|
1083
1310
|
);
|
|
1084
1311
|
});
|
|
1085
1312
|
const result = await client.finalize(session.publishSessionId);
|
|
1086
|
-
|
|
1313
|
+
const existingToken = config?.artifactId === result.artifactId ? config.publishToken : void 0;
|
|
1314
|
+
const shouldWriteConfig = result.publishToken !== void 0 || config?.artifactId === result.artifactId || opts.workspace !== void 0 || opts.folder !== void 0;
|
|
1315
|
+
if (shouldWriteConfig) {
|
|
1087
1316
|
writeConfig(dir, {
|
|
1088
1317
|
apiUrl: apiUrl2,
|
|
1089
1318
|
artifactId: result.artifactId,
|
|
1090
|
-
|
|
1319
|
+
...placement.folder ? { folder: placement.folder } : {},
|
|
1320
|
+
...placement.folderId ? { folderId: placement.folderId } : {},
|
|
1321
|
+
...result.publishToken ?? existingToken ? { publishToken: result.publishToken ?? existingToken } : {},
|
|
1091
1322
|
slug: slugFromUrl(result.url),
|
|
1092
|
-
url: result.url
|
|
1323
|
+
url: result.url,
|
|
1324
|
+
...placement.workspace ? { workspace: placement.workspace } : {},
|
|
1325
|
+
...placement.workspaceId ? { workspaceId: placement.workspaceId } : {}
|
|
1093
1326
|
});
|
|
1094
1327
|
}
|
|
1095
1328
|
if (opts.password !== void 0) {
|
|
@@ -1105,8 +1338,12 @@ async function publish(opts) {
|
|
|
1105
1338
|
{
|
|
1106
1339
|
artifactId: result.artifactId,
|
|
1107
1340
|
claimed: result.claimed,
|
|
1341
|
+
folder: placement.folder ?? null,
|
|
1342
|
+
folderId: placement.folderId ?? null,
|
|
1108
1343
|
url: result.url,
|
|
1109
1344
|
versionNumber: result.versionNumber,
|
|
1345
|
+
workspace: placement.workspace ?? null,
|
|
1346
|
+
workspaceId: placement.workspaceId ?? null,
|
|
1110
1347
|
...result.claimUrl ? { claimUrl: result.claimUrl } : {},
|
|
1111
1348
|
...opts.password === void 0 ? {} : { protected: true },
|
|
1112
1349
|
filesUploaded: session.filesToUpload.map((f) => f.path),
|
|
@@ -1148,6 +1385,76 @@ ${result.claimUrl}
|
|
|
1148
1385
|
openInBrowser(result.url);
|
|
1149
1386
|
}
|
|
1150
1387
|
}
|
|
1388
|
+
async function resolvePlacement(params) {
|
|
1389
|
+
const { apiKey, apiUrl: apiUrl2, config, opts } = params;
|
|
1390
|
+
const hasPlacementFlag = opts.workspace !== void 0 || opts.folder !== void 0;
|
|
1391
|
+
if (!hasPlacementFlag && config?.workspaceId) {
|
|
1392
|
+
if (!apiKey) {
|
|
1393
|
+
throw new Error(
|
|
1394
|
+
`Publishing into workspace ${config.workspace ?? config.workspaceId} needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
return {
|
|
1398
|
+
...config.folder ? { folder: config.folder } : {},
|
|
1399
|
+
...config.folderId ? { folderId: config.folderId } : {},
|
|
1400
|
+
...config.workspace ? { workspace: config.workspace } : {},
|
|
1401
|
+
workspaceId: config.workspaceId
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
if (!hasPlacementFlag) {
|
|
1405
|
+
return {};
|
|
1406
|
+
}
|
|
1407
|
+
if (!apiKey) {
|
|
1408
|
+
throw new Error(
|
|
1409
|
+
"Publishing into a workspace needs an account \u2014 run `artifact-cafe login` first."
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
const workspaceHandle = opts.workspace ?? config?.workspace;
|
|
1413
|
+
if (!workspaceHandle) {
|
|
1414
|
+
throw new Error("--folder requires --workspace or a stored workspace.");
|
|
1415
|
+
}
|
|
1416
|
+
const client = createApiClient(apiUrl2, apiKey);
|
|
1417
|
+
let workspaceId;
|
|
1418
|
+
if (opts.workspace === void 0 && config?.workspaceId) {
|
|
1419
|
+
({ workspaceId } = config);
|
|
1420
|
+
} else {
|
|
1421
|
+
const data2 = await client.listWorkspaces();
|
|
1422
|
+
const workspace = data2.workspaces.find(
|
|
1423
|
+
(candidate) => candidate.handle === workspaceHandle
|
|
1424
|
+
);
|
|
1425
|
+
if (!workspace) {
|
|
1426
|
+
throw new Error(
|
|
1427
|
+
`No workspace with handle "${workspaceHandle}" belongs to this account.`
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
workspaceId = workspace.id;
|
|
1431
|
+
}
|
|
1432
|
+
const folderName = opts.workspace !== void 0 && opts.folder === void 0 ? void 0 : opts.folder ?? config?.folder;
|
|
1433
|
+
if (!folderName) {
|
|
1434
|
+
return { workspace: workspaceHandle, workspaceId };
|
|
1435
|
+
}
|
|
1436
|
+
const data = await client.listFolders(workspaceId);
|
|
1437
|
+
const matches = data.folders.filter(
|
|
1438
|
+
(candidate) => candidate.name === folderName || candidate.id === folderName
|
|
1439
|
+
);
|
|
1440
|
+
if (matches.length === 0) {
|
|
1441
|
+
throw new Error(
|
|
1442
|
+
`No folder named "${folderName}" exists in workspace ${workspaceHandle}.`
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
if (matches.length > 1) {
|
|
1446
|
+
throw new Error(
|
|
1447
|
+
`More than one folder is named "${folderName}" in workspace ${workspaceHandle}; pass the folder id instead.`
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
const folder = matches[0];
|
|
1451
|
+
return {
|
|
1452
|
+
folder: folder.name,
|
|
1453
|
+
folderId: folder.id,
|
|
1454
|
+
workspace: workspaceHandle,
|
|
1455
|
+
workspaceId
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1151
1458
|
async function inBatches(items, concurrency, fn) {
|
|
1152
1459
|
let next = 0;
|
|
1153
1460
|
const workers = Array.from(
|
|
@@ -1210,6 +1517,11 @@ function printCliError(err, json) {
|
|
|
1210
1517
|
if (err.retryAfter) {
|
|
1211
1518
|
console.error(` Retry in ${err.retryAfter}s.`);
|
|
1212
1519
|
}
|
|
1520
|
+
if (err.code === "password_required") {
|
|
1521
|
+
console.error(
|
|
1522
|
+
" Run `artifact-cafe login` as the owner, or run from the published folder (its publish token unlocks it)."
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1213
1525
|
}
|
|
1214
1526
|
return;
|
|
1215
1527
|
}
|
|
@@ -1221,6 +1533,52 @@ function printCliError(err, json) {
|
|
|
1221
1533
|
}
|
|
1222
1534
|
}
|
|
1223
1535
|
|
|
1536
|
+
// src/folders.ts
|
|
1537
|
+
async function folders(opts) {
|
|
1538
|
+
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
1539
|
+
/\/$/,
|
|
1540
|
+
""
|
|
1541
|
+
);
|
|
1542
|
+
const key = resolveApiKey(apiUrl2, opts.apiKey);
|
|
1543
|
+
if (!key) {
|
|
1544
|
+
throw new Error(
|
|
1545
|
+
`Listing folders needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
const client = createApiClient(apiUrl2, key);
|
|
1549
|
+
const workspaceData = await client.listWorkspaces();
|
|
1550
|
+
const workspace = workspaceData.workspaces.find(
|
|
1551
|
+
(candidate) => candidate.handle === opts.workspace
|
|
1552
|
+
);
|
|
1553
|
+
if (!workspace) {
|
|
1554
|
+
throw new Error(
|
|
1555
|
+
`No workspace with handle "${opts.workspace}" belongs to this account.`
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
const data = await client.listFolders(workspace.id);
|
|
1559
|
+
if (opts.json) {
|
|
1560
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
if (data.folders.length === 0) {
|
|
1564
|
+
console.log(`No folders in workspace ${workspace.handle}.`);
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
const plural = data.folders.length === 1 ? "" : "s";
|
|
1568
|
+
console.log(
|
|
1569
|
+
`${data.folders.length} folder${plural} in ${workspace.handle}
|
|
1570
|
+
`
|
|
1571
|
+
);
|
|
1572
|
+
for (const folder of data.folders) {
|
|
1573
|
+
printFolder(folder);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
function printFolder(folder) {
|
|
1577
|
+
console.log(`${folder.name} (${folder.visibility})`);
|
|
1578
|
+
console.log(` ${folder.id}
|
|
1579
|
+
`);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1224
1582
|
// src/link.ts
|
|
1225
1583
|
import path7 from "path";
|
|
1226
1584
|
import { createInterface } from "readline";
|
|
@@ -1303,7 +1661,7 @@ function pickArtifact(artifacts) {
|
|
|
1303
1661
|
);
|
|
1304
1662
|
});
|
|
1305
1663
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1306
|
-
return new Promise((
|
|
1664
|
+
return new Promise((resolve2, reject) => {
|
|
1307
1665
|
rl.question(`
|
|
1308
1666
|
Link which? [1-${artifacts.length}]: `, (answer) => {
|
|
1309
1667
|
rl.close();
|
|
@@ -1312,7 +1670,7 @@ Link which? [1-${artifacts.length}]: `, (answer) => {
|
|
|
1312
1670
|
reject(new Error(`Enter a number between 1 and ${artifacts.length}.`));
|
|
1313
1671
|
return;
|
|
1314
1672
|
}
|
|
1315
|
-
|
|
1673
|
+
resolve2(artifacts[n - 1]);
|
|
1316
1674
|
});
|
|
1317
1675
|
});
|
|
1318
1676
|
}
|
|
@@ -1379,7 +1737,7 @@ function apiUrl() {
|
|
|
1379
1737
|
return (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
1380
1738
|
}
|
|
1381
1739
|
function promptSecret(question) {
|
|
1382
|
-
return new Promise((
|
|
1740
|
+
return new Promise((resolve2) => {
|
|
1383
1741
|
const rl = createInterface2({
|
|
1384
1742
|
input: process.stdin,
|
|
1385
1743
|
output: process.stdout
|
|
@@ -1394,14 +1752,14 @@ function promptSecret(question) {
|
|
|
1394
1752
|
rl.question(question, (answer) => {
|
|
1395
1753
|
rl.close();
|
|
1396
1754
|
process.stdout.write("\n");
|
|
1397
|
-
|
|
1755
|
+
resolve2(answer);
|
|
1398
1756
|
});
|
|
1399
1757
|
muted = true;
|
|
1400
1758
|
});
|
|
1401
1759
|
}
|
|
1402
1760
|
async function login(opts) {
|
|
1403
1761
|
const base = apiUrl();
|
|
1404
|
-
const keysUrl = `${base}/
|
|
1762
|
+
const keysUrl = `${base}/dashboard?view=api-keys`;
|
|
1405
1763
|
let key = opts.apiKey?.trim();
|
|
1406
1764
|
if (!key) {
|
|
1407
1765
|
if (!opts.json) {
|
|
@@ -1483,7 +1841,7 @@ function logout(opts) {
|
|
|
1483
1841
|
}
|
|
1484
1842
|
console.log(`\u2713 Logged out of ${apiOrigin(base)} on this machine.`);
|
|
1485
1843
|
console.log(
|
|
1486
|
-
`The key itself is still active \u2014 revoke it at ${base}/
|
|
1844
|
+
`The key itself is still active \u2014 revoke it at ${base}/dashboard?view=api-keys if this machine is shared.`
|
|
1487
1845
|
);
|
|
1488
1846
|
}
|
|
1489
1847
|
|
|
@@ -1549,6 +1907,268 @@ async function protect(opts) {
|
|
|
1549
1907
|
);
|
|
1550
1908
|
}
|
|
1551
1909
|
|
|
1910
|
+
// src/reply.ts
|
|
1911
|
+
import path10 from "path";
|
|
1912
|
+
async function readStdin() {
|
|
1913
|
+
if (process.stdin.isTTY) {
|
|
1914
|
+
return "";
|
|
1915
|
+
}
|
|
1916
|
+
const chunks = [];
|
|
1917
|
+
for await (const chunk of process.stdin) {
|
|
1918
|
+
chunks.push(chunk);
|
|
1919
|
+
}
|
|
1920
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
1921
|
+
}
|
|
1922
|
+
async function replyToThread(opts) {
|
|
1923
|
+
const threadId = opts.thread?.trim();
|
|
1924
|
+
if (!threadId) {
|
|
1925
|
+
throw new Error(
|
|
1926
|
+
"Pass the thread id to reply to \u2014 get it from `artifact-cafe comments --json`."
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1929
|
+
const body = (opts.body ?? await readStdin()).trim();
|
|
1930
|
+
if (!body) {
|
|
1931
|
+
throw new Error(
|
|
1932
|
+
'Nothing to post. Pass the reply with --body "\u2026", or pipe it on stdin.'
|
|
1933
|
+
);
|
|
1934
|
+
}
|
|
1935
|
+
const dir = path10.resolve(opts.dir);
|
|
1936
|
+
const target = resolveArtifactTarget(dir, {
|
|
1937
|
+
apiKey: opts.apiKey,
|
|
1938
|
+
artifact: opts.artifact
|
|
1939
|
+
});
|
|
1940
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
1941
|
+
let thread = await client.replyThread(threadId, body);
|
|
1942
|
+
if (opts.resolve) {
|
|
1943
|
+
thread = await client.resolveThread(threadId);
|
|
1944
|
+
}
|
|
1945
|
+
if (opts.json) {
|
|
1946
|
+
console.log(
|
|
1947
|
+
JSON.stringify(
|
|
1948
|
+
{
|
|
1949
|
+
id: thread.id,
|
|
1950
|
+
messageCount: thread.messages.length,
|
|
1951
|
+
status: thread.status
|
|
1952
|
+
},
|
|
1953
|
+
null,
|
|
1954
|
+
2
|
|
1955
|
+
)
|
|
1956
|
+
);
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
const closed = opts.resolve ? " and resolved it" : "";
|
|
1960
|
+
console.log(`\u2713 Replied to ${thread.id}${closed} \u2014 now ${thread.status}.`);
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
// src/resolve.ts
|
|
1964
|
+
import path11 from "path";
|
|
1965
|
+
async function changeThreadStatus(opts) {
|
|
1966
|
+
const threadId = opts.thread?.trim();
|
|
1967
|
+
if (!threadId) {
|
|
1968
|
+
throw new Error(
|
|
1969
|
+
`Pass the thread id to ${opts.action} \u2014 get it from \`artifact-cafe comments --json\`.`
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
const dir = path11.resolve(opts.dir);
|
|
1973
|
+
const target = resolveArtifactTarget(dir, {
|
|
1974
|
+
apiKey: opts.apiKey,
|
|
1975
|
+
artifact: opts.artifact
|
|
1976
|
+
});
|
|
1977
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
1978
|
+
const thread = opts.action === "resolve" ? await client.resolveThread(threadId) : await client.reopenThread(threadId);
|
|
1979
|
+
if (opts.json) {
|
|
1980
|
+
console.log(
|
|
1981
|
+
JSON.stringify({ id: thread.id, status: thread.status }, null, 2)
|
|
1982
|
+
);
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
const verb = opts.action === "resolve" ? "Resolved" : "Reopened";
|
|
1986
|
+
console.log(`\u2713 ${verb} ${thread.id} \u2014 now ${thread.status}.`);
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
// src/update.ts
|
|
1990
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
1991
|
+
import { createRequire } from "module";
|
|
1992
|
+
import { homedir as homedir2 } from "os";
|
|
1993
|
+
import path12 from "path";
|
|
1994
|
+
import { z as z15 } from "zod";
|
|
1995
|
+
var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
|
|
1996
|
+
var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
|
|
1997
|
+
var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
|
|
1998
|
+
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1999
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
2000
|
+
var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
|
|
2001
|
+
var cacheSchema = z15.object({
|
|
2002
|
+
checkedAt: z15.number(),
|
|
2003
|
+
cliLatest: z15.string().nullable(),
|
|
2004
|
+
skillLatest: z15.string().nullable(),
|
|
2005
|
+
version: z15.literal(1)
|
|
2006
|
+
});
|
|
2007
|
+
function cliVersion() {
|
|
2008
|
+
const pkg = createRequire(import.meta.url)("../package.json");
|
|
2009
|
+
return pkg.version;
|
|
2010
|
+
}
|
|
2011
|
+
function cachePath() {
|
|
2012
|
+
return path12.join(homedir2(), ".artifactcafe", "update-check.json");
|
|
2013
|
+
}
|
|
2014
|
+
function readCache() {
|
|
2015
|
+
try {
|
|
2016
|
+
return cacheSchema.parse(JSON.parse(readFileSync5(cachePath(), "utf8")));
|
|
2017
|
+
} catch {
|
|
2018
|
+
return null;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
function writeCache(cache) {
|
|
2022
|
+
try {
|
|
2023
|
+
mkdirSync3(path12.dirname(cachePath()), { mode: 448, recursive: true });
|
|
2024
|
+
writeFileSync3(cachePath(), `${JSON.stringify(cache, null, 2)}
|
|
2025
|
+
`);
|
|
2026
|
+
} catch {
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
function isNewerVersion(candidate, current) {
|
|
2030
|
+
const parse = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
2031
|
+
const a = parse(candidate);
|
|
2032
|
+
const b = parse(current);
|
|
2033
|
+
for (let i = 0; i < 3; i++) {
|
|
2034
|
+
if ((a[i] ?? 0) !== (b[i] ?? 0)) {
|
|
2035
|
+
return (a[i] ?? 0) > (b[i] ?? 0);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
return false;
|
|
2039
|
+
}
|
|
2040
|
+
async function fetchJson(url) {
|
|
2041
|
+
const res = await fetch(url, {
|
|
2042
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
2043
|
+
});
|
|
2044
|
+
if (!res.ok) {
|
|
2045
|
+
throw new Error(`GET ${url} \u2192 ${res.status}`);
|
|
2046
|
+
}
|
|
2047
|
+
return res.json();
|
|
2048
|
+
}
|
|
2049
|
+
async function fetchText(url) {
|
|
2050
|
+
const res = await fetch(url, {
|
|
2051
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
2052
|
+
});
|
|
2053
|
+
if (!res.ok) {
|
|
2054
|
+
throw new Error(`GET ${url} \u2192 ${res.status}`);
|
|
2055
|
+
}
|
|
2056
|
+
return res.text();
|
|
2057
|
+
}
|
|
2058
|
+
async function fetchLatestVersions(previous) {
|
|
2059
|
+
const [cli, skill] = await Promise.all([
|
|
2060
|
+
fetchJson(REGISTRY_LATEST_URL).then((body) => z15.object({ version: z15.string() }).parse(body).version).catch(() => null),
|
|
2061
|
+
fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
|
|
2062
|
+
]);
|
|
2063
|
+
return {
|
|
2064
|
+
checkedAt: Date.now(),
|
|
2065
|
+
cliLatest: cli ?? previous?.cliLatest ?? null,
|
|
2066
|
+
skillLatest: skill ?? previous?.skillLatest ?? null,
|
|
2067
|
+
version: 1
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
function installedSkillVersions() {
|
|
2071
|
+
const roots = [homedir2(), process.cwd()];
|
|
2072
|
+
const dirs = [".agents", ".claude"];
|
|
2073
|
+
const versions = [];
|
|
2074
|
+
for (const root of roots) {
|
|
2075
|
+
for (const dir of dirs) {
|
|
2076
|
+
const file = path12.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
|
|
2077
|
+
if (!existsSync3(file)) {
|
|
2078
|
+
continue;
|
|
2079
|
+
}
|
|
2080
|
+
try {
|
|
2081
|
+
const found = SKILL_VERSION_PATTERN.exec(readFileSync5(file, "utf8"));
|
|
2082
|
+
if (found?.[1]) {
|
|
2083
|
+
versions.push(found[1]);
|
|
2084
|
+
}
|
|
2085
|
+
} catch {
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
return versions;
|
|
2090
|
+
}
|
|
2091
|
+
function updateChecksDisabled() {
|
|
2092
|
+
return Boolean(
|
|
2093
|
+
process.env.ARTIFACT_CAFE_NO_UPDATE_CHECK ?? process.env.NO_UPDATE_NOTIFIER ?? process.env.CI
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
function startUpdateCheck() {
|
|
2097
|
+
if (updateChecksDisabled()) {
|
|
2098
|
+
return () => Promise.resolve();
|
|
2099
|
+
}
|
|
2100
|
+
const pending = (async () => {
|
|
2101
|
+
const cached = readCache();
|
|
2102
|
+
if (cached && Date.now() - cached.checkedAt < CHECK_TTL_MS) {
|
|
2103
|
+
return cached;
|
|
2104
|
+
}
|
|
2105
|
+
const fresh = await fetchLatestVersions(cached);
|
|
2106
|
+
writeCache(fresh);
|
|
2107
|
+
return fresh;
|
|
2108
|
+
})().catch(() => null);
|
|
2109
|
+
return async () => {
|
|
2110
|
+
const latest = await pending;
|
|
2111
|
+
if (!latest) {
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
const notices = [];
|
|
2115
|
+
const current = cliVersion();
|
|
2116
|
+
if (latest.cliLatest && isNewerVersion(latest.cliLatest, current)) {
|
|
2117
|
+
notices.push(
|
|
2118
|
+
`[artifact-cafe] CLI update available: ${current} \u2192 ${latest.cliLatest}. Run commands via \`npx artifact-cafe@latest\`; update a global install with \`npm i -g artifact-cafe@latest\`.`
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
if (latest.skillLatest) {
|
|
2122
|
+
const stale = installedSkillVersions().filter(
|
|
2123
|
+
(version) => isNewerVersion(latest.skillLatest, version)
|
|
2124
|
+
).sort((a, b) => isNewerVersion(a, b) ? 1 : -1);
|
|
2125
|
+
if (stale.length > 0) {
|
|
2126
|
+
notices.push(
|
|
2127
|
+
`[artifact-cafe] Skill update available: ${stale[0]} \u2192 ${latest.skillLatest}. Update: \`${SKILL_UPDATE_COMMAND}\` (drop -g for a repo-local copy), then re-read SKILL.md.`
|
|
2128
|
+
);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
if (notices.length > 0) {
|
|
2132
|
+
console.error(`
|
|
2133
|
+
${notices.join("\n")}`);
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// src/workspaces.ts
|
|
2139
|
+
async function workspaces(opts) {
|
|
2140
|
+
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
2141
|
+
/\/$/,
|
|
2142
|
+
""
|
|
2143
|
+
);
|
|
2144
|
+
const key = resolveApiKey(apiUrl2, opts.apiKey);
|
|
2145
|
+
if (!key) {
|
|
2146
|
+
throw new Error(
|
|
2147
|
+
`Listing workspaces needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
const data = await createApiClient(apiUrl2, key).listWorkspaces();
|
|
2151
|
+
if (opts.json) {
|
|
2152
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
if (data.workspaces.length === 0) {
|
|
2156
|
+
console.log("No workspaces yet \u2014 create one from the dashboard.");
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
const plural = data.workspaces.length === 1 ? "" : "s";
|
|
2160
|
+
console.log(`${data.workspaces.length} workspace${plural}
|
|
2161
|
+
`);
|
|
2162
|
+
for (const workspace of data.workspaces) {
|
|
2163
|
+
printWorkspace(workspace);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
function printWorkspace(workspace) {
|
|
2167
|
+
console.log(`${workspace.name} (${workspace.role ?? "non-member"})`);
|
|
2168
|
+
console.log(` ${workspace.handle} \xB7 ${workspace.id}
|
|
2169
|
+
`);
|
|
2170
|
+
}
|
|
2171
|
+
|
|
1552
2172
|
// src/index.ts
|
|
1553
2173
|
var publish2 = defineCommand({
|
|
1554
2174
|
args: {
|
|
@@ -1566,13 +2186,21 @@ var publish2 = defineCommand({
|
|
|
1566
2186
|
type: "positional"
|
|
1567
2187
|
},
|
|
1568
2188
|
entry: { description: "Entry file (default: auto-detect)", type: "string" },
|
|
2189
|
+
folder: {
|
|
2190
|
+
description: "Publish into this workspace folder name (or id)",
|
|
2191
|
+
type: "string"
|
|
2192
|
+
},
|
|
1569
2193
|
json: { description: "Machine-readable output", type: "boolean" },
|
|
1570
2194
|
"no-open": { description: "Don't open the review URL", type: "boolean" },
|
|
1571
2195
|
password: {
|
|
1572
2196
|
description: "Password-protect the artifact (min 6 chars)",
|
|
1573
2197
|
type: "string"
|
|
1574
2198
|
},
|
|
1575
|
-
title: { description: "Artifact title", type: "string" }
|
|
2199
|
+
title: { description: "Artifact title", type: "string" },
|
|
2200
|
+
workspace: {
|
|
2201
|
+
description: "Publish into this workspace handle",
|
|
2202
|
+
type: "string"
|
|
2203
|
+
}
|
|
1576
2204
|
},
|
|
1577
2205
|
meta: { description: "Publish a folder as an artifact", name: "publish" },
|
|
1578
2206
|
async run({ args }) {
|
|
@@ -1582,10 +2210,12 @@ var publish2 = defineCommand({
|
|
|
1582
2210
|
artifact: args.artifact,
|
|
1583
2211
|
dir: args.dir,
|
|
1584
2212
|
entry: args.entry,
|
|
2213
|
+
folder: args.folder,
|
|
1585
2214
|
json: Boolean(args.json),
|
|
1586
2215
|
open: !args["no-open"],
|
|
1587
2216
|
password: args.password,
|
|
1588
|
-
title: args.title
|
|
2217
|
+
title: args.title,
|
|
2218
|
+
workspace: args.workspace
|
|
1589
2219
|
});
|
|
1590
2220
|
} catch (err) {
|
|
1591
2221
|
printCliError(err, Boolean(args.json));
|
|
@@ -1669,6 +2299,60 @@ var list2 = defineCommand({
|
|
|
1669
2299
|
}
|
|
1670
2300
|
}
|
|
1671
2301
|
});
|
|
2302
|
+
var folders2 = defineCommand({
|
|
2303
|
+
args: {
|
|
2304
|
+
"api-key": {
|
|
2305
|
+
description: "List with this API key (default: stored login)",
|
|
2306
|
+
type: "string"
|
|
2307
|
+
},
|
|
2308
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
2309
|
+
workspace: {
|
|
2310
|
+
description: "Workspace handle whose folders to list",
|
|
2311
|
+
required: true,
|
|
2312
|
+
type: "string"
|
|
2313
|
+
}
|
|
2314
|
+
},
|
|
2315
|
+
meta: {
|
|
2316
|
+
description: "List folders in a workspace",
|
|
2317
|
+
name: "folders"
|
|
2318
|
+
},
|
|
2319
|
+
async run({ args }) {
|
|
2320
|
+
try {
|
|
2321
|
+
await folders({
|
|
2322
|
+
apiKey: args["api-key"],
|
|
2323
|
+
json: Boolean(args.json),
|
|
2324
|
+
workspace: args.workspace
|
|
2325
|
+
});
|
|
2326
|
+
} catch (err) {
|
|
2327
|
+
printCliError(err, Boolean(args.json));
|
|
2328
|
+
process.exit(1);
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
});
|
|
2332
|
+
var workspaces2 = defineCommand({
|
|
2333
|
+
args: {
|
|
2334
|
+
"api-key": {
|
|
2335
|
+
description: "List with this API key (default: stored login)",
|
|
2336
|
+
type: "string"
|
|
2337
|
+
},
|
|
2338
|
+
json: { description: "Machine-readable output", type: "boolean" }
|
|
2339
|
+
},
|
|
2340
|
+
meta: {
|
|
2341
|
+
description: "List the workspaces your account belongs to",
|
|
2342
|
+
name: "workspaces"
|
|
2343
|
+
},
|
|
2344
|
+
async run({ args }) {
|
|
2345
|
+
try {
|
|
2346
|
+
await workspaces({
|
|
2347
|
+
apiKey: args["api-key"],
|
|
2348
|
+
json: Boolean(args.json)
|
|
2349
|
+
});
|
|
2350
|
+
} catch (err) {
|
|
2351
|
+
printCliError(err, Boolean(args.json));
|
|
2352
|
+
process.exit(1);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
});
|
|
1672
2356
|
var link2 = defineCommand({
|
|
1673
2357
|
args: {
|
|
1674
2358
|
"api-key": {
|
|
@@ -1773,6 +2457,66 @@ var comments2 = defineCommand({
|
|
|
1773
2457
|
}
|
|
1774
2458
|
}
|
|
1775
2459
|
});
|
|
2460
|
+
var listen = defineCommand({
|
|
2461
|
+
args: {
|
|
2462
|
+
"api-key": {
|
|
2463
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
2464
|
+
type: "string"
|
|
2465
|
+
},
|
|
2466
|
+
artifact: {
|
|
2467
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
2468
|
+
type: "string"
|
|
2469
|
+
},
|
|
2470
|
+
dir: {
|
|
2471
|
+
default: ".",
|
|
2472
|
+
description: "Published directory",
|
|
2473
|
+
type: "positional"
|
|
2474
|
+
},
|
|
2475
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
2476
|
+
status: {
|
|
2477
|
+
default: "open",
|
|
2478
|
+
description: "Filter: open | resolved | all",
|
|
2479
|
+
type: "string"
|
|
2480
|
+
},
|
|
2481
|
+
timeout: {
|
|
2482
|
+
default: "900",
|
|
2483
|
+
description: "Give up after this many seconds (exit 2)",
|
|
2484
|
+
type: "string"
|
|
2485
|
+
},
|
|
2486
|
+
version: {
|
|
2487
|
+
default: "current",
|
|
2488
|
+
description: "Filter: current | all",
|
|
2489
|
+
type: "string"
|
|
2490
|
+
}
|
|
2491
|
+
},
|
|
2492
|
+
meta: {
|
|
2493
|
+
description: "Wait for new reviewer feedback (alias for comments --wait)",
|
|
2494
|
+
name: "listen"
|
|
2495
|
+
},
|
|
2496
|
+
async run({ args }) {
|
|
2497
|
+
try {
|
|
2498
|
+
const timeout = Number(args.timeout);
|
|
2499
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
2500
|
+
throw new Error(
|
|
2501
|
+
`--timeout must be a positive number of seconds (got "${args.timeout}").`
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2504
|
+
await comments({
|
|
2505
|
+
apiKey: args["api-key"],
|
|
2506
|
+
artifact: args.artifact,
|
|
2507
|
+
dir: args.dir,
|
|
2508
|
+
json: Boolean(args.json),
|
|
2509
|
+
status: args.status,
|
|
2510
|
+
timeout,
|
|
2511
|
+
version: args.version,
|
|
2512
|
+
wait: true
|
|
2513
|
+
});
|
|
2514
|
+
} catch (err) {
|
|
2515
|
+
printCliError(err, Boolean(args.json));
|
|
2516
|
+
process.exit(1);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
});
|
|
1776
2520
|
var protect2 = defineCommand({
|
|
1777
2521
|
args: {
|
|
1778
2522
|
"api-key": {
|
|
@@ -1812,6 +2556,101 @@ var protect2 = defineCommand({
|
|
|
1812
2556
|
}
|
|
1813
2557
|
}
|
|
1814
2558
|
});
|
|
2559
|
+
var threadStatusCommand = (action) => defineCommand({
|
|
2560
|
+
args: {
|
|
2561
|
+
"api-key": {
|
|
2562
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
2563
|
+
type: "string"
|
|
2564
|
+
},
|
|
2565
|
+
artifact: {
|
|
2566
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
2567
|
+
type: "string"
|
|
2568
|
+
},
|
|
2569
|
+
dir: {
|
|
2570
|
+
default: ".",
|
|
2571
|
+
description: "Linked directory to authorize from (default: .)",
|
|
2572
|
+
type: "string"
|
|
2573
|
+
},
|
|
2574
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
2575
|
+
thread: {
|
|
2576
|
+
description: "Thread id (from `comments --json`)",
|
|
2577
|
+
required: true,
|
|
2578
|
+
type: "positional"
|
|
2579
|
+
}
|
|
2580
|
+
},
|
|
2581
|
+
meta: {
|
|
2582
|
+
description: action === "resolve" ? "Mark a comment thread resolved" : "Reopen a resolved comment thread",
|
|
2583
|
+
name: action
|
|
2584
|
+
},
|
|
2585
|
+
async run({ args }) {
|
|
2586
|
+
try {
|
|
2587
|
+
await changeThreadStatus({
|
|
2588
|
+
action,
|
|
2589
|
+
apiKey: args["api-key"],
|
|
2590
|
+
artifact: args.artifact,
|
|
2591
|
+
dir: args.dir,
|
|
2592
|
+
json: Boolean(args.json),
|
|
2593
|
+
thread: args.thread
|
|
2594
|
+
});
|
|
2595
|
+
} catch (err) {
|
|
2596
|
+
printCliError(err, Boolean(args.json));
|
|
2597
|
+
process.exit(1);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
var resolve = threadStatusCommand("resolve");
|
|
2602
|
+
var reopen = threadStatusCommand("reopen");
|
|
2603
|
+
var reply = defineCommand({
|
|
2604
|
+
args: {
|
|
2605
|
+
"api-key": {
|
|
2606
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
2607
|
+
type: "string"
|
|
2608
|
+
},
|
|
2609
|
+
artifact: {
|
|
2610
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
2611
|
+
type: "string"
|
|
2612
|
+
},
|
|
2613
|
+
body: {
|
|
2614
|
+
description: "Reply text (omit to read from stdin)",
|
|
2615
|
+
type: "string"
|
|
2616
|
+
},
|
|
2617
|
+
dir: {
|
|
2618
|
+
default: ".",
|
|
2619
|
+
description: "Linked directory to authorize from (default: .)",
|
|
2620
|
+
type: "string"
|
|
2621
|
+
},
|
|
2622
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
2623
|
+
resolve: {
|
|
2624
|
+
description: "Resolve the thread after replying",
|
|
2625
|
+
type: "boolean"
|
|
2626
|
+
},
|
|
2627
|
+
thread: {
|
|
2628
|
+
description: "Thread id (from `comments --json`)",
|
|
2629
|
+
required: true,
|
|
2630
|
+
type: "positional"
|
|
2631
|
+
}
|
|
2632
|
+
},
|
|
2633
|
+
meta: {
|
|
2634
|
+
description: "Reply to a comment thread (as the agent or owner)",
|
|
2635
|
+
name: "reply"
|
|
2636
|
+
},
|
|
2637
|
+
async run({ args }) {
|
|
2638
|
+
try {
|
|
2639
|
+
await replyToThread({
|
|
2640
|
+
apiKey: args["api-key"],
|
|
2641
|
+
artifact: args.artifact,
|
|
2642
|
+
body: args.body,
|
|
2643
|
+
dir: args.dir,
|
|
2644
|
+
json: Boolean(args.json),
|
|
2645
|
+
resolve: Boolean(args.resolve),
|
|
2646
|
+
thread: args.thread
|
|
2647
|
+
});
|
|
2648
|
+
} catch (err) {
|
|
2649
|
+
printCliError(err, Boolean(args.json));
|
|
2650
|
+
process.exit(1);
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
});
|
|
1815
2654
|
var open = defineCommand({
|
|
1816
2655
|
args: {
|
|
1817
2656
|
"api-key": {
|
|
@@ -1866,7 +2705,8 @@ var dashboard = defineCommand({
|
|
|
1866
2705
|
var main = defineCommand({
|
|
1867
2706
|
meta: {
|
|
1868
2707
|
description: "Publish AI-generated artifacts for review \u2014 https://artifact.cafe",
|
|
1869
|
-
name: "artifact-cafe"
|
|
2708
|
+
name: "artifact-cafe",
|
|
2709
|
+
version: cliVersion()
|
|
1870
2710
|
},
|
|
1871
2711
|
// Bare `artifact-cafe` (no subcommand) → brand banner + usage. citty
|
|
1872
2712
|
// still calls this parent run *after* a matched subcommand, so bail when
|
|
@@ -1882,14 +2722,22 @@ var main = defineCommand({
|
|
|
1882
2722
|
subCommands: {
|
|
1883
2723
|
comments: comments2,
|
|
1884
2724
|
dashboard,
|
|
2725
|
+
folders: folders2,
|
|
1885
2726
|
link: link2,
|
|
1886
2727
|
list: list2,
|
|
2728
|
+
listen,
|
|
1887
2729
|
login: login2,
|
|
1888
2730
|
logout: logout2,
|
|
1889
2731
|
open,
|
|
1890
2732
|
protect: protect2,
|
|
1891
2733
|
publish: publish2,
|
|
1892
|
-
|
|
2734
|
+
reopen,
|
|
2735
|
+
reply,
|
|
2736
|
+
resolve,
|
|
2737
|
+
whoami: whoami2,
|
|
2738
|
+
workspaces: workspaces2
|
|
1893
2739
|
}
|
|
1894
2740
|
});
|
|
1895
|
-
|
|
2741
|
+
var finishUpdateCheck = startUpdateCheck();
|
|
2742
|
+
await runMain(main);
|
|
2743
|
+
await finishUpdateCheck();
|