artifact-cafe 0.0.16 → 0.0.17
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 +931 -615
- package/package.json +10 -9
package/dist/index.js
CHANGED
|
@@ -7,24 +7,17 @@ import { defineCommand, runMain, showUsage } from "citty";
|
|
|
7
7
|
var AMBER = "\x1B[38;2;214;138;57m";
|
|
8
8
|
var BOLD = "\x1B[1m";
|
|
9
9
|
var RESET = "\x1B[0m";
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E",
|
|
14
|
-
" \u2502 \u251C\u2500\u256E",
|
|
15
|
-
" \u2502 \u2502 \u2502",
|
|
16
|
-
" \u2502 \u251C\u2500\u256F",
|
|
17
|
-
" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
|
|
18
|
-
" \u203E\u203E\u203E\u203E\u203E\u203E\u203E\u203E"
|
|
19
|
-
];
|
|
10
|
+
var TICK_TOP = " \u250C \u2510";
|
|
11
|
+
var GLYPH = " \xE3";
|
|
12
|
+
var TICK_BOTTOM = " \u2514 \u2518";
|
|
20
13
|
var WORDMARK = " artifact.cafe";
|
|
21
14
|
function printBanner() {
|
|
22
15
|
const color = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
23
16
|
const paint = (line, codes) => color ? `${codes}${line}${RESET}` : line;
|
|
24
17
|
console.log();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
console.log(paint(TICK_TOP, AMBER));
|
|
19
|
+
console.log(paint(GLYPH, BOLD));
|
|
20
|
+
console.log(paint(TICK_BOTTOM, AMBER));
|
|
28
21
|
console.log();
|
|
29
22
|
console.log(paint(WORDMARK, AMBER + BOLD));
|
|
30
23
|
console.log();
|
|
@@ -69,8 +62,10 @@ var meResponseSchema = z.object({
|
|
|
69
62
|
userId: z.string()
|
|
70
63
|
});
|
|
71
64
|
var artifactListItemSchema = z.object({
|
|
65
|
+
autoDeleteAt: z.string().datetime().nullable().optional(),
|
|
72
66
|
createdAt: z.string(),
|
|
73
67
|
currentVersionNumber: z.number().int().positive().nullable(),
|
|
68
|
+
expiresAt: z.string().datetime().nullable().optional(),
|
|
74
69
|
id: z.string(),
|
|
75
70
|
openCommentCount: z.number().int().nonnegative(),
|
|
76
71
|
slug: z.string(),
|
|
@@ -181,7 +176,7 @@ var workspaceAvatarResponseSchema = z3.object({
|
|
|
181
176
|
});
|
|
182
177
|
|
|
183
178
|
// ../shared/src/comments.ts
|
|
184
|
-
import { z as
|
|
179
|
+
import { z as z8 } from "zod";
|
|
185
180
|
|
|
186
181
|
// ../shared/src/emoji.ts
|
|
187
182
|
import { z as z4 } from "zod";
|
|
@@ -193,15 +188,38 @@ function canonicalizeEmoji(s) {
|
|
|
193
188
|
var iconEmojiSchema = z4.string().max(64).transform(canonicalizeEmoji).refine((s) => SINGLE_EMOJI.test(s), { message: "Must be a single emoji." }).nullable().describe("A single emoji shown as the icon, or null for the default.");
|
|
194
189
|
|
|
195
190
|
// ../shared/src/publish.ts
|
|
196
|
-
import { z as
|
|
191
|
+
import { z as z7 } from "zod";
|
|
197
192
|
|
|
198
|
-
// ../shared/src/
|
|
193
|
+
// ../shared/src/expiration.ts
|
|
199
194
|
import { z as z5 } from "zod";
|
|
200
|
-
var
|
|
201
|
-
var
|
|
195
|
+
var MAX_EXPIRATION_SECONDS = 2147483647;
|
|
196
|
+
var expirationSecondsSchema = z5.number().int().positive().max(MAX_EXPIRATION_SECONDS);
|
|
197
|
+
var setExpirationSchema = z5.object({
|
|
198
|
+
expiresInSeconds: expirationSecondsSchema.nullable()
|
|
199
|
+
});
|
|
200
|
+
var expirationResponseSchema = z5.object({
|
|
201
|
+
autoDeleteAt: z5.string().datetime().nullable(),
|
|
202
|
+
expiresAt: z5.string().datetime().nullable()
|
|
203
|
+
});
|
|
204
|
+
function parseArtifactDuration(value) {
|
|
205
|
+
const match = /^([1-9]\d*)([mhd])$/.exec(value);
|
|
206
|
+
const units = { d: 86400, h: 3600, m: 60 };
|
|
207
|
+
const seconds = match ? Number(match[1]) * (units[match[2]] ?? 0) : 0;
|
|
208
|
+
if (!expirationSecondsSchema.safeParse(seconds).success) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`Duration must be a positive whole number followed by m, h, or d (for example 30m, 1h, 7d), up to ${MAX_EXPIRATION_SECONDS} seconds.`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return seconds;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ../shared/src/review-mode.ts
|
|
217
|
+
import { z as z6 } from "zod";
|
|
218
|
+
var reviewModeSchema = z6.enum(["open", "readonly", "off"]);
|
|
219
|
+
var setReviewModeSchema = z6.object({
|
|
202
220
|
mode: reviewModeSchema
|
|
203
221
|
});
|
|
204
|
-
var reviewModeResponseSchema =
|
|
222
|
+
var reviewModeResponseSchema = z6.object({
|
|
205
223
|
mode: reviewModeSchema
|
|
206
224
|
});
|
|
207
225
|
|
|
@@ -220,274 +238,300 @@ function isSafeArtifactPath(p) {
|
|
|
220
238
|
}
|
|
221
239
|
var CARD_DESCRIPTION_MAX = 300;
|
|
222
240
|
var CARD_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
223
|
-
var manifestFileSchema =
|
|
224
|
-
hash:
|
|
225
|
-
mimeType:
|
|
226
|
-
path:
|
|
241
|
+
var manifestFileSchema = z7.object({
|
|
242
|
+
hash: z7.string().regex(/^sha256_[a-f0-9]{64}$/),
|
|
243
|
+
mimeType: z7.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
|
|
244
|
+
path: z7.string().min(1).max(1024).refine(isSafeArtifactPath, {
|
|
227
245
|
message: "path must be a safe relative path"
|
|
228
246
|
}),
|
|
229
|
-
size:
|
|
247
|
+
size: z7.number().int().positive()
|
|
230
248
|
});
|
|
231
|
-
var publishSourceSchema =
|
|
232
|
-
agent:
|
|
233
|
-
runId:
|
|
234
|
-
type:
|
|
249
|
+
var publishSourceSchema = z7.object({
|
|
250
|
+
agent: z7.string().optional(),
|
|
251
|
+
runId: z7.string().optional(),
|
|
252
|
+
type: z7.literal("cli")
|
|
235
253
|
});
|
|
236
|
-
var createPublishSessionSchema =
|
|
237
|
-
artifactId:
|
|
254
|
+
var createPublishSessionSchema = z7.object({
|
|
255
|
+
artifactId: z7.string().nullable(),
|
|
238
256
|
// One-line share-card summary (`--description`). Overrides whatever the
|
|
239
257
|
// entry HTML declares; leave it off and the API reads the document's own
|
|
240
258
|
// `og:description`/`description` at finalize.
|
|
241
|
-
description:
|
|
242
|
-
entryPath:
|
|
243
|
-
|
|
259
|
+
description: z7.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
260
|
+
entryPath: z7.string().optional(),
|
|
261
|
+
expiresInSeconds: expirationSecondsSchema.optional(),
|
|
262
|
+
files: z7.array(manifestFileSchema).min(1).max(1e3),
|
|
244
263
|
// SPEC-2 §6.4 — workspace placement is available only to authenticated
|
|
245
264
|
// members; the API enforces membership and folder ownership. folderId
|
|
246
265
|
// without workspaceId resolves against the account's personal workspace.
|
|
247
|
-
folderId:
|
|
266
|
+
folderId: z7.string().min(1).optional(),
|
|
248
267
|
// Bullet-point changelog for this version, authored by the publisher.
|
|
249
268
|
// Optional on every publish (incl. v1); stored immutably on the version.
|
|
250
|
-
notes:
|
|
269
|
+
notes: z7.string().max(4e3).optional(),
|
|
251
270
|
source: publishSourceSchema,
|
|
252
|
-
title:
|
|
253
|
-
workspaceId:
|
|
254
|
-
});
|
|
255
|
-
var publishSessionResponseSchema =
|
|
256
|
-
artifactId:
|
|
257
|
-
expiresAt:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
271
|
+
title: z7.string().max(200).optional(),
|
|
272
|
+
workspaceId: z7.string().min(1).optional()
|
|
273
|
+
});
|
|
274
|
+
var publishSessionResponseSchema = z7.object({
|
|
275
|
+
artifactId: z7.string(),
|
|
276
|
+
expiresAt: z7.string(),
|
|
277
|
+
expiresInSeconds: expirationSecondsSchema.nullable().optional(),
|
|
278
|
+
filesToUpload: z7.array(
|
|
279
|
+
z7.object({
|
|
280
|
+
method: z7.literal("PUT"),
|
|
281
|
+
path: z7.string(),
|
|
282
|
+
uploadUrl: z7.string().url()
|
|
263
283
|
})
|
|
264
284
|
),
|
|
265
|
-
publishSessionId:
|
|
266
|
-
skippedFiles:
|
|
267
|
-
versionId:
|
|
285
|
+
publishSessionId: z7.string(),
|
|
286
|
+
skippedFiles: z7.array(z7.string()),
|
|
287
|
+
versionId: z7.string()
|
|
268
288
|
});
|
|
269
|
-
var finalizeResponseSchema =
|
|
270
|
-
artifactId:
|
|
289
|
+
var finalizeResponseSchema = z7.object({
|
|
290
|
+
artifactId: z7.string(),
|
|
291
|
+
autoDeleteAt: z7.string().datetime().nullable().optional(),
|
|
271
292
|
// API-key publishes create the artifact already claimed (SPEC §5.4) —
|
|
272
293
|
// the CLI uses this to tell "claim URL pending" from "already yours".
|
|
273
|
-
claimed:
|
|
294
|
+
claimed: z7.boolean(),
|
|
274
295
|
// Returned only on first publish of a new artifact (one-time).
|
|
275
|
-
claimUrl:
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
296
|
+
claimUrl: z7.string().url().optional(),
|
|
297
|
+
expiresAt: z7.string().datetime().nullable().optional(),
|
|
298
|
+
publishToken: z7.string().optional(),
|
|
299
|
+
url: z7.string().url(),
|
|
300
|
+
versionId: z7.string(),
|
|
301
|
+
versionNumber: z7.number().int().positive(),
|
|
280
302
|
// SPEC-2 §5.2 — who can open the review URL. A publish into a folder
|
|
281
303
|
// inherits that folder's visibility, so this is the only place the CLI
|
|
282
304
|
// learns that a link it just printed is members-only. Optional so a new
|
|
283
305
|
// CLI against an older API degrades to "unknown" instead of failing the
|
|
284
306
|
// publish over a field that only shapes the closing message.
|
|
285
|
-
visibility:
|
|
286
|
-
workspaceId:
|
|
287
|
-
});
|
|
288
|
-
var artifactResponseSchema =
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
307
|
+
visibility: z7.enum(["public", "unlisted", "workspace", "private"]).optional(),
|
|
308
|
+
workspaceId: z7.string().nullable().optional()
|
|
309
|
+
});
|
|
310
|
+
var artifactResponseSchema = z7.object({
|
|
311
|
+
autoDeleteAt: z7.string().datetime().nullable().optional(),
|
|
312
|
+
claimed: z7.boolean(),
|
|
313
|
+
currentVersion: z7.object({
|
|
314
|
+
entryUrl: z7.string().url(),
|
|
315
|
+
id: z7.string(),
|
|
316
|
+
versionNumber: z7.number().int().positive()
|
|
294
317
|
}).nullable(),
|
|
295
318
|
// Custom icon, or null to render the default document glyph.
|
|
296
|
-
emoji:
|
|
319
|
+
emoji: z7.string().nullable(),
|
|
320
|
+
expiresAt: z7.string().datetime().nullable().optional(),
|
|
297
321
|
// SPEC-2 §6.4 — placement + visibility surface on the artifact.
|
|
298
|
-
folderId:
|
|
299
|
-
id:
|
|
322
|
+
folderId: z7.string().nullable(),
|
|
323
|
+
id: z7.string(),
|
|
300
324
|
// SPEC-2 §5.8 — what a viewer may do once in (open / readonly / off).
|
|
301
325
|
reviewMode: reviewModeSchema,
|
|
302
|
-
slug:
|
|
303
|
-
title:
|
|
304
|
-
visibility:
|
|
305
|
-
workspaceId:
|
|
326
|
+
slug: z7.string(),
|
|
327
|
+
title: z7.string(),
|
|
328
|
+
visibility: z7.enum(["public", "unlisted", "workspace", "private"]),
|
|
329
|
+
workspaceId: z7.string().nullable()
|
|
306
330
|
});
|
|
331
|
+
function resolvePublishEntry(files, explicit) {
|
|
332
|
+
if (explicit !== void 0) {
|
|
333
|
+
if (!files.some((file) => file.path === explicit)) {
|
|
334
|
+
throw new Error(`Entry file "${explicit}" is not in the manifest.`);
|
|
335
|
+
}
|
|
336
|
+
return explicit;
|
|
337
|
+
}
|
|
338
|
+
const htmlFiles = files.filter(
|
|
339
|
+
(file) => file.mimeType === "text/html" && !file.path.includes("/")
|
|
340
|
+
);
|
|
341
|
+
if (htmlFiles.some((file) => file.path === "index.html")) {
|
|
342
|
+
return "index.html";
|
|
343
|
+
}
|
|
344
|
+
if (htmlFiles.length > 1) {
|
|
345
|
+
throw new Error(
|
|
346
|
+
`Multiple HTML files found (${htmlFiles.map((file) => file.path).join(", ")}). Specify an entry file.`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
return htmlFiles[0]?.path;
|
|
350
|
+
}
|
|
307
351
|
|
|
308
352
|
// ../shared/src/comments.ts
|
|
309
|
-
var anchorPageSchema =
|
|
353
|
+
var anchorPageSchema = z8.string().max(1024).refine(isSafeArtifactPath, {
|
|
310
354
|
message: "page must be a safe relative path"
|
|
311
355
|
}).optional();
|
|
312
|
-
var elementSnapshotSchema =
|
|
313
|
-
classes:
|
|
314
|
-
id:
|
|
315
|
-
tag:
|
|
316
|
-
text:
|
|
356
|
+
var elementSnapshotSchema = z8.object({
|
|
357
|
+
classes: z8.string().max(256).optional(),
|
|
358
|
+
id: z8.string().max(256).optional(),
|
|
359
|
+
tag: z8.string().min(1).max(128),
|
|
360
|
+
text: z8.string().max(200).optional()
|
|
317
361
|
});
|
|
318
|
-
var pointAnchorSchema =
|
|
362
|
+
var pointAnchorSchema = z8.object({
|
|
319
363
|
element: elementSnapshotSchema.optional(),
|
|
320
364
|
// elementPath is the primary anchor; x/y are offsets within the
|
|
321
365
|
// element's box, normalized 0..1 (SPEC.md §8.1).
|
|
322
|
-
elementPath:
|
|
366
|
+
elementPath: z8.string().max(2048),
|
|
323
367
|
page: anchorPageSchema,
|
|
324
|
-
type:
|
|
325
|
-
viewportHeight:
|
|
326
|
-
viewportWidth:
|
|
327
|
-
x:
|
|
328
|
-
y:
|
|
329
|
-
});
|
|
330
|
-
var textAnchorSchema =
|
|
331
|
-
elementPath:
|
|
332
|
-
endOffset:
|
|
368
|
+
type: z8.literal("point"),
|
|
369
|
+
viewportHeight: z8.number().int().positive().max(1e5),
|
|
370
|
+
viewportWidth: z8.number().int().positive().max(1e5),
|
|
371
|
+
x: z8.number().min(0).max(1),
|
|
372
|
+
y: z8.number().min(0).max(1)
|
|
373
|
+
});
|
|
374
|
+
var textAnchorSchema = z8.object({
|
|
375
|
+
elementPath: z8.string().max(2048),
|
|
376
|
+
endOffset: z8.number().int().nonnegative().max(1e8),
|
|
333
377
|
page: anchorPageSchema,
|
|
334
|
-
prefix:
|
|
335
|
-
quote:
|
|
336
|
-
startOffset:
|
|
337
|
-
suffix:
|
|
338
|
-
type:
|
|
378
|
+
prefix: z8.string().max(200).optional(),
|
|
379
|
+
quote: z8.string().min(1).max(1e3),
|
|
380
|
+
startOffset: z8.number().int().nonnegative().max(1e8),
|
|
381
|
+
suffix: z8.string().max(200).optional(),
|
|
382
|
+
type: z8.literal("text")
|
|
339
383
|
});
|
|
340
|
-
var pageAnchorSchema =
|
|
384
|
+
var pageAnchorSchema = z8.object({
|
|
341
385
|
page: anchorPageSchema,
|
|
342
|
-
type:
|
|
386
|
+
type: z8.literal("page")
|
|
343
387
|
});
|
|
344
|
-
var anchorSchema =
|
|
388
|
+
var anchorSchema = z8.discriminatedUnion("type", [
|
|
345
389
|
pointAnchorSchema,
|
|
346
390
|
textAnchorSchema,
|
|
347
391
|
pageAnchorSchema
|
|
348
392
|
]);
|
|
349
|
-
var displayNameSchema =
|
|
350
|
-
var guestSchema =
|
|
393
|
+
var displayNameSchema = z8.string().trim().min(1).max(64);
|
|
394
|
+
var guestSchema = z8.object({
|
|
351
395
|
displayName: displayNameSchema,
|
|
352
|
-
guestId:
|
|
396
|
+
guestId: z8.string().min(16)
|
|
353
397
|
});
|
|
354
|
-
var viaLabelSchema =
|
|
355
|
-
var createCommentThreadSchema =
|
|
398
|
+
var viaLabelSchema = z8.string().trim().min(1).max(64);
|
|
399
|
+
var createCommentThreadSchema = z8.object({
|
|
356
400
|
anchor: anchorSchema,
|
|
357
|
-
body:
|
|
401
|
+
body: z8.string().min(1).max(1e4),
|
|
358
402
|
guest: guestSchema.optional(),
|
|
359
|
-
versionId:
|
|
403
|
+
versionId: z8.string(),
|
|
360
404
|
via: viaLabelSchema.optional()
|
|
361
405
|
});
|
|
362
|
-
var createCommentMessageSchema =
|
|
363
|
-
body:
|
|
406
|
+
var createCommentMessageSchema = z8.object({
|
|
407
|
+
body: z8.string().min(1).max(1e4),
|
|
364
408
|
guest: guestSchema.optional(),
|
|
365
409
|
via: viaLabelSchema.optional()
|
|
366
410
|
});
|
|
367
|
-
var editCommentMessageSchema =
|
|
368
|
-
body:
|
|
411
|
+
var editCommentMessageSchema = z8.object({
|
|
412
|
+
body: z8.string().min(1).max(1e4),
|
|
369
413
|
guest: guestSchema.optional()
|
|
370
414
|
});
|
|
371
|
-
var threadActionSchema =
|
|
415
|
+
var threadActionSchema = z8.object({
|
|
372
416
|
guest: guestSchema.optional()
|
|
373
417
|
});
|
|
374
|
-
var reactionEmojiSchema =
|
|
375
|
-
var toggleReactionSchema =
|
|
418
|
+
var reactionEmojiSchema = z8.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}");
|
|
419
|
+
var toggleReactionSchema = z8.object({
|
|
376
420
|
emoji: reactionEmojiSchema,
|
|
377
421
|
guest: guestSchema.optional()
|
|
378
422
|
});
|
|
379
|
-
var commentAgentSchema =
|
|
380
|
-
id:
|
|
381
|
-
label:
|
|
423
|
+
var commentAgentSchema = z8.object({
|
|
424
|
+
id: z8.string().nullable(),
|
|
425
|
+
label: z8.string()
|
|
382
426
|
});
|
|
383
|
-
var commentAuthorSchema =
|
|
427
|
+
var commentAuthorSchema = z8.object({
|
|
384
428
|
agent: commentAgentSchema.nullable().default(null),
|
|
385
|
-
avatarUrl:
|
|
386
|
-
displayName:
|
|
387
|
-
guestId:
|
|
388
|
-
isViewer:
|
|
389
|
-
kind:
|
|
429
|
+
avatarUrl: z8.string().nullable(),
|
|
430
|
+
displayName: z8.string(),
|
|
431
|
+
guestId: z8.string().nullable(),
|
|
432
|
+
isViewer: z8.boolean(),
|
|
433
|
+
kind: z8.enum(["member", "guest"]),
|
|
390
434
|
// Optional "via" attribution (§6.4). Null for plain comments; a bounded
|
|
391
435
|
// label the shell appends after the display name. Defaulted for the same
|
|
392
436
|
// reason as `agent`: a server predating §6.4 omits the key entirely.
|
|
393
|
-
via:
|
|
437
|
+
via: z8.string().nullable().default(null)
|
|
394
438
|
});
|
|
395
|
-
var messageReactionSchema =
|
|
396
|
-
emoji:
|
|
397
|
-
reactors:
|
|
439
|
+
var messageReactionSchema = z8.object({
|
|
440
|
+
emoji: z8.string(),
|
|
441
|
+
reactors: z8.array(commentAuthorSchema)
|
|
398
442
|
});
|
|
399
|
-
var commentMessageSchema =
|
|
443
|
+
var commentMessageSchema = z8.object({
|
|
400
444
|
author: commentAuthorSchema,
|
|
401
445
|
// Empty string for a deleted message — the body is stripped server-side
|
|
402
446
|
// (§6.7.1 tombstone), never sent to the client and hidden there.
|
|
403
|
-
body:
|
|
404
|
-
createdAt:
|
|
447
|
+
body: z8.string(),
|
|
448
|
+
createdAt: z8.string(),
|
|
405
449
|
// Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
|
|
406
450
|
// the shell renders "Comment deleted" instead of the (absent) body.
|
|
407
|
-
deleted:
|
|
451
|
+
deleted: z8.boolean(),
|
|
408
452
|
// Last edit time (§6.7.1), or null if never edited — drives the "edited"
|
|
409
453
|
// badge. No edit history is kept.
|
|
410
|
-
editedAt:
|
|
411
|
-
id:
|
|
454
|
+
editedAt: z8.string().nullable(),
|
|
455
|
+
id: z8.string(),
|
|
412
456
|
// Defaulted, not required: a new CLI parsing an older server's
|
|
413
457
|
// response must not fail on the missing key.
|
|
414
|
-
reactions:
|
|
458
|
+
reactions: z8.array(messageReactionSchema).default([])
|
|
415
459
|
});
|
|
416
|
-
var threadActorSchema =
|
|
417
|
-
displayName:
|
|
418
|
-
kind:
|
|
460
|
+
var threadActorSchema = z8.object({
|
|
461
|
+
displayName: z8.string(),
|
|
462
|
+
kind: z8.enum(["member", "guest", "agent"])
|
|
419
463
|
});
|
|
420
|
-
var commentThreadSchema =
|
|
464
|
+
var commentThreadSchema = z8.object({
|
|
421
465
|
// §6.6.1 — set while open to signal "seen, working on it"; cleared on
|
|
422
466
|
// reopen, kept (but superseded in display) once resolved. Defaulted so
|
|
423
467
|
// clients parsing an older server's response don't fail.
|
|
424
|
-
acknowledgedAt:
|
|
468
|
+
acknowledgedAt: z8.string().nullable().default(null),
|
|
425
469
|
acknowledgedBy: threadActorSchema.nullable().default(null),
|
|
426
470
|
anchor: anchorSchema,
|
|
427
|
-
artifactId:
|
|
428
|
-
createdAt:
|
|
471
|
+
artifactId: z8.string(),
|
|
472
|
+
createdAt: z8.string(),
|
|
429
473
|
createdBy: commentAuthorSchema,
|
|
430
|
-
id:
|
|
431
|
-
messages:
|
|
474
|
+
id: z8.string(),
|
|
475
|
+
messages: z8.array(commentMessageSchema),
|
|
432
476
|
// Null on threads resolved before actor recording existed, and always
|
|
433
477
|
// null while open.
|
|
434
478
|
resolvedBy: threadActorSchema.nullable().default(null),
|
|
435
|
-
status:
|
|
436
|
-
versionId:
|
|
479
|
+
status: z8.enum(["open", "resolved"]),
|
|
480
|
+
versionId: z8.string()
|
|
437
481
|
});
|
|
438
|
-
var commentThreadListSchema =
|
|
439
|
-
threads:
|
|
482
|
+
var commentThreadListSchema = z8.object({
|
|
483
|
+
threads: z8.array(commentThreadSchema)
|
|
440
484
|
});
|
|
441
|
-
var commentExportQuerySchema =
|
|
442
|
-
status:
|
|
443
|
-
version:
|
|
485
|
+
var commentExportQuerySchema = z8.object({
|
|
486
|
+
status: z8.enum(["open", "resolved", "all"]).default("open"),
|
|
487
|
+
version: z8.enum(["current", "all"]).default("current")
|
|
444
488
|
});
|
|
445
|
-
var exportReactionSchema =
|
|
446
|
-
count:
|
|
447
|
-
emoji:
|
|
489
|
+
var exportReactionSchema = z8.object({
|
|
490
|
+
count: z8.number().int().positive(),
|
|
491
|
+
emoji: z8.string()
|
|
448
492
|
});
|
|
449
|
-
var commentExportReplySchema =
|
|
450
|
-
author:
|
|
451
|
-
authorKind:
|
|
452
|
-
body:
|
|
453
|
-
createdAt:
|
|
493
|
+
var commentExportReplySchema = z8.object({
|
|
494
|
+
author: z8.string(),
|
|
495
|
+
authorKind: z8.enum(["member", "guest"]),
|
|
496
|
+
body: z8.string(),
|
|
497
|
+
createdAt: z8.string(),
|
|
454
498
|
// Soft-delete tombstone (§6.7.1): body is "" when true.
|
|
455
|
-
deleted:
|
|
456
|
-
editedAt:
|
|
499
|
+
deleted: z8.boolean(),
|
|
500
|
+
editedAt: z8.string().nullable(),
|
|
457
501
|
// The message id — what `edit`/`delete` target (§5.1).
|
|
458
|
-
id:
|
|
459
|
-
reactions:
|
|
502
|
+
id: z8.string(),
|
|
503
|
+
reactions: z8.array(exportReactionSchema).optional()
|
|
460
504
|
});
|
|
461
|
-
var commentExportThreadSchema =
|
|
505
|
+
var commentExportThreadSchema = z8.object({
|
|
462
506
|
// §6.6.1 — lets a polling agent skip threads it already acknowledged
|
|
463
507
|
// (acknowledged threads stay in status=open exports on purpose).
|
|
464
|
-
acknowledgedAt:
|
|
508
|
+
acknowledgedAt: z8.string().nullable().default(null),
|
|
465
509
|
anchor: anchorSchema,
|
|
466
|
-
author:
|
|
467
|
-
authorKind:
|
|
468
|
-
body:
|
|
469
|
-
createdAt:
|
|
470
|
-
deleted:
|
|
471
|
-
editedAt:
|
|
472
|
-
id:
|
|
510
|
+
author: z8.string(),
|
|
511
|
+
authorKind: z8.enum(["member", "guest"]),
|
|
512
|
+
body: z8.string(),
|
|
513
|
+
createdAt: z8.string(),
|
|
514
|
+
deleted: z8.boolean(),
|
|
515
|
+
editedAt: z8.string().nullable(),
|
|
516
|
+
id: z8.string(),
|
|
473
517
|
// The opening message's id (distinct from the thread `id`) — `edit`/
|
|
474
518
|
// `delete` operate on messages, so the CLI needs it to target the first
|
|
475
519
|
// comment, not just the replies.
|
|
476
|
-
messageId:
|
|
477
|
-
reactions:
|
|
478
|
-
replies:
|
|
520
|
+
messageId: z8.string(),
|
|
521
|
+
reactions: z8.array(exportReactionSchema).optional(),
|
|
522
|
+
replies: z8.array(commentExportReplySchema),
|
|
479
523
|
resolvedBy: threadActorSchema.nullable().default(null),
|
|
480
|
-
status:
|
|
481
|
-
versionNumber:
|
|
524
|
+
status: z8.enum(["open", "resolved"]),
|
|
525
|
+
versionNumber: z8.number().int().positive()
|
|
482
526
|
});
|
|
483
|
-
var commentExportSchema =
|
|
484
|
-
artifactId:
|
|
485
|
-
comments:
|
|
527
|
+
var commentExportSchema = z8.object({
|
|
528
|
+
artifactId: z8.string(),
|
|
529
|
+
comments: z8.array(commentExportThreadSchema),
|
|
486
530
|
// The artifact's current version (null before the first finalize).
|
|
487
|
-
versionNumber:
|
|
531
|
+
versionNumber: z8.number().int().positive().nullable()
|
|
488
532
|
});
|
|
489
|
-
var commentReadResponseSchema =
|
|
490
|
-
lastSeenAt:
|
|
533
|
+
var commentReadResponseSchema = z8.object({
|
|
534
|
+
lastSeenAt: z8.string()
|
|
491
535
|
});
|
|
492
536
|
function elementLabel(el) {
|
|
493
537
|
if (el.id) {
|
|
@@ -516,6 +560,8 @@ var DATA_LIMITS = {
|
|
|
516
560
|
defaultWriteRate: "10/minute/ip",
|
|
517
561
|
maxCollections: 10,
|
|
518
562
|
maxFieldsPerCollection: 50,
|
|
563
|
+
/** Owner-plane batch insert: records per request. */
|
|
564
|
+
maxInsertBatch: 100,
|
|
519
565
|
maxListLimit: 200,
|
|
520
566
|
maxManifestBytes: 64 * 1024,
|
|
521
567
|
maxRecordBytes: 16 * 1024,
|
|
@@ -663,46 +709,50 @@ function validateRecordInput(collection, input, opts = {}) {
|
|
|
663
709
|
}
|
|
664
710
|
|
|
665
711
|
// ../shared/src/data-api.ts
|
|
666
|
-
import { z as
|
|
667
|
-
var dataRecordSchema =
|
|
668
|
-
created_at:
|
|
669
|
-
created_by:
|
|
670
|
-
id:
|
|
671
|
-
updated_at:
|
|
672
|
-
}).catchall(
|
|
673
|
-
var dataPublicRecordSchema =
|
|
674
|
-
created_at:
|
|
675
|
-
created_by:
|
|
676
|
-
id:
|
|
677
|
-
mine:
|
|
678
|
-
updated_at:
|
|
679
|
-
}).catchall(
|
|
680
|
-
var dataPublicListResponseSchema =
|
|
681
|
-
nextCursor:
|
|
682
|
-
records:
|
|
683
|
-
});
|
|
684
|
-
var dataListResponseSchema =
|
|
685
|
-
nextCursor:
|
|
686
|
-
records:
|
|
687
|
-
});
|
|
688
|
-
var
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
});
|
|
698
|
-
var
|
|
699
|
-
|
|
712
|
+
import { z as z9 } from "zod";
|
|
713
|
+
var dataRecordSchema = z9.object({
|
|
714
|
+
created_at: z9.string(),
|
|
715
|
+
created_by: z9.string().nullable(),
|
|
716
|
+
id: z9.string(),
|
|
717
|
+
updated_at: z9.string()
|
|
718
|
+
}).catchall(z9.unknown());
|
|
719
|
+
var dataPublicRecordSchema = z9.object({
|
|
720
|
+
created_at: z9.string(),
|
|
721
|
+
created_by: z9.never().optional(),
|
|
722
|
+
id: z9.string(),
|
|
723
|
+
mine: z9.boolean(),
|
|
724
|
+
updated_at: z9.string()
|
|
725
|
+
}).catchall(z9.unknown());
|
|
726
|
+
var dataPublicListResponseSchema = z9.object({
|
|
727
|
+
nextCursor: z9.string().nullable(),
|
|
728
|
+
records: z9.array(dataPublicRecordSchema)
|
|
729
|
+
});
|
|
730
|
+
var dataListResponseSchema = z9.object({
|
|
731
|
+
nextCursor: z9.string().nullable(),
|
|
732
|
+
records: z9.array(dataRecordSchema)
|
|
733
|
+
});
|
|
734
|
+
var dataRecordResponseSchema = z9.object({ record: dataRecordSchema });
|
|
735
|
+
var dataBatchInsertResponseSchema = z9.object({
|
|
736
|
+
records: z9.array(dataRecordSchema)
|
|
737
|
+
});
|
|
738
|
+
var dataCollectionSummarySchema = z9.object({
|
|
739
|
+
access: z9.record(z9.string(), z9.string()).optional(),
|
|
740
|
+
count: z9.number().int().nonnegative().optional(),
|
|
741
|
+
name: z9.string(),
|
|
742
|
+
rateLimit: z9.string().optional()
|
|
743
|
+
});
|
|
744
|
+
var dataSummaryResponseSchema = z9.object({
|
|
745
|
+
collections: z9.array(dataCollectionSummarySchema),
|
|
746
|
+
enabled: z9.boolean()
|
|
747
|
+
});
|
|
748
|
+
var dataClearResponseSchema = z9.object({
|
|
749
|
+
cleared: z9.number().int().nonnegative()
|
|
700
750
|
});
|
|
701
751
|
|
|
702
752
|
// ../shared/src/data-manifest.ts
|
|
703
|
-
import { z as
|
|
704
|
-
var nameSchema =
|
|
705
|
-
var fieldTypeSchema =
|
|
753
|
+
import { z as z10 } from "zod";
|
|
754
|
+
var nameSchema = z10.string().max(DATA_NAME_MAX).regex(DATA_NAME_RE, "must match ^[a-z][a-z0-9_]*$");
|
|
755
|
+
var fieldTypeSchema = z10.enum([
|
|
706
756
|
"string",
|
|
707
757
|
"number",
|
|
708
758
|
"integer",
|
|
@@ -713,13 +763,13 @@ var fieldTypeSchema = z9.enum([
|
|
|
713
763
|
"array",
|
|
714
764
|
"object"
|
|
715
765
|
]);
|
|
716
|
-
var fieldSchema =
|
|
717
|
-
default:
|
|
718
|
-
maximum:
|
|
719
|
-
maxLength:
|
|
720
|
-
minimum:
|
|
721
|
-
required:
|
|
722
|
-
trim:
|
|
766
|
+
var fieldSchema = z10.strictObject({
|
|
767
|
+
default: z10.unknown().optional(),
|
|
768
|
+
maximum: z10.number().optional(),
|
|
769
|
+
maxLength: z10.number().int().positive().optional(),
|
|
770
|
+
minimum: z10.number().optional(),
|
|
771
|
+
required: z10.boolean().optional(),
|
|
772
|
+
trim: z10.boolean().optional(),
|
|
723
773
|
type: fieldTypeSchema
|
|
724
774
|
}).superRefine((field, ctx) => {
|
|
725
775
|
if (field.default === void 0) {
|
|
@@ -745,18 +795,18 @@ function defaultMatchesType(field) {
|
|
|
745
795
|
{ value: field.default }
|
|
746
796
|
).ok;
|
|
747
797
|
}
|
|
748
|
-
var accessLevelSchema =
|
|
749
|
-
var accessSchema =
|
|
798
|
+
var accessLevelSchema = z10.enum(["public", "creator", "owner", "none"]);
|
|
799
|
+
var accessSchema = z10.strictObject({
|
|
750
800
|
delete: accessLevelSchema.default("owner"),
|
|
751
801
|
insert: accessLevelSchema.default("owner"),
|
|
752
802
|
read: accessLevelSchema.default("public"),
|
|
753
803
|
update: accessLevelSchema.default("owner")
|
|
754
804
|
}).prefault({});
|
|
755
|
-
var collectionSchema =
|
|
805
|
+
var collectionSchema = z10.strictObject({
|
|
756
806
|
access: accessSchema,
|
|
757
|
-
fields:
|
|
758
|
-
publicMutation:
|
|
759
|
-
rateLimit:
|
|
807
|
+
fields: z10.record(nameSchema, fieldSchema),
|
|
808
|
+
publicMutation: z10.literal("open").optional(),
|
|
809
|
+
rateLimit: z10.string().regex(DATA_RATE_LIMIT_RE, 'expected "N/hour/ip" or "N/minute/ip"').optional()
|
|
760
810
|
}).superRefine((collection, ctx) => {
|
|
761
811
|
const fieldNames = Object.keys(collection.fields);
|
|
762
812
|
if (fieldNames.length > DATA_LIMITS.maxFieldsPerCollection) {
|
|
@@ -785,8 +835,8 @@ var collectionSchema = z9.strictObject({
|
|
|
785
835
|
}
|
|
786
836
|
}
|
|
787
837
|
});
|
|
788
|
-
var dataManifestSchema =
|
|
789
|
-
collections:
|
|
838
|
+
var dataManifestSchema = z10.strictObject({
|
|
839
|
+
collections: z10.record(nameSchema, collectionSchema)
|
|
790
840
|
}).superRefine((manifest, ctx) => {
|
|
791
841
|
if (Object.keys(manifest.collections).length > DATA_LIMITS.maxCollections) {
|
|
792
842
|
ctx.addIssue({
|
|
@@ -825,245 +875,245 @@ function parseDataManifest(text) {
|
|
|
825
875
|
}
|
|
826
876
|
|
|
827
877
|
// ../shared/src/errors.ts
|
|
828
|
-
import { z as
|
|
829
|
-
var apiErrorSchema =
|
|
830
|
-
code:
|
|
831
|
-
docsUrl:
|
|
832
|
-
message:
|
|
833
|
-
retryAfter:
|
|
834
|
-
scope:
|
|
878
|
+
import { z as z11 } from "zod";
|
|
879
|
+
var apiErrorSchema = z11.object({
|
|
880
|
+
code: z11.string(),
|
|
881
|
+
docsUrl: z11.string().url(),
|
|
882
|
+
message: z11.string(),
|
|
883
|
+
retryAfter: z11.number().int().positive().optional(),
|
|
884
|
+
scope: z11.enum(["ip", "artifact"]).optional()
|
|
835
885
|
});
|
|
836
886
|
|
|
837
887
|
// ../shared/src/feedback.ts
|
|
838
|
-
import { z as
|
|
839
|
-
var feedbackRequestSchema =
|
|
840
|
-
email:
|
|
841
|
-
message:
|
|
888
|
+
import { z as z12 } from "zod";
|
|
889
|
+
var feedbackRequestSchema = z12.object({
|
|
890
|
+
email: z12.string().trim().toLowerCase().email().optional().or(z12.literal("")),
|
|
891
|
+
message: z12.string().trim().min(1).max(4e3)
|
|
842
892
|
});
|
|
843
|
-
var feedbackResponseSchema =
|
|
844
|
-
sent:
|
|
893
|
+
var feedbackResponseSchema = z12.object({
|
|
894
|
+
sent: z12.literal(true)
|
|
845
895
|
});
|
|
846
896
|
|
|
847
897
|
// ../shared/src/grants.ts
|
|
848
|
-
import { z as
|
|
849
|
-
var grantRoleSchema =
|
|
850
|
-
var createGrantSchema =
|
|
851
|
-
email:
|
|
898
|
+
import { z as z13 } from "zod";
|
|
899
|
+
var grantRoleSchema = z13.enum(["viewer"]);
|
|
900
|
+
var createGrantSchema = z13.object({
|
|
901
|
+
email: z13.string().trim().toLowerCase().email().optional(),
|
|
852
902
|
role: grantRoleSchema.default("viewer"),
|
|
853
|
-
userId:
|
|
903
|
+
userId: z13.string().min(1).optional()
|
|
854
904
|
}).refine((body) => Boolean(body.email) !== Boolean(body.userId), {
|
|
855
905
|
message: "provide exactly one of email or userId"
|
|
856
906
|
});
|
|
857
|
-
var grantSchema =
|
|
858
|
-
createdAt:
|
|
859
|
-
email:
|
|
860
|
-
expiresAt:
|
|
861
|
-
id:
|
|
907
|
+
var grantSchema = z13.object({
|
|
908
|
+
createdAt: z13.string(),
|
|
909
|
+
email: z13.string().email().nullable(),
|
|
910
|
+
expiresAt: z13.string().nullable(),
|
|
911
|
+
id: z13.string(),
|
|
862
912
|
// The subject's avatar once they have an account; null otherwise.
|
|
863
|
-
image:
|
|
913
|
+
image: z13.string().nullable(),
|
|
864
914
|
// Present on the invited account after redemption / when granting an
|
|
865
915
|
// existing user; null for a still-pending email invite.
|
|
866
|
-
name:
|
|
867
|
-
redeemedAt:
|
|
916
|
+
name: z13.string().nullable(),
|
|
917
|
+
redeemedAt: z13.string().nullable(),
|
|
868
918
|
role: grantRoleSchema,
|
|
869
919
|
// Shown as "· pending" beside the role until the link is redeemed.
|
|
870
|
-
status:
|
|
920
|
+
status: z13.enum(["pending", "active"]),
|
|
871
921
|
// Display fragment of the live invitation token, or null once redeemed.
|
|
872
|
-
tokenPrefix:
|
|
873
|
-
userId:
|
|
922
|
+
tokenPrefix: z13.string().nullable(),
|
|
923
|
+
userId: z13.string().nullable()
|
|
874
924
|
});
|
|
875
925
|
var createdGrantSchema = grantSchema.extend({
|
|
876
|
-
url:
|
|
926
|
+
url: z13.string().url()
|
|
877
927
|
});
|
|
878
|
-
var grantListResponseSchema =
|
|
879
|
-
grants:
|
|
928
|
+
var grantListResponseSchema = z13.object({
|
|
929
|
+
grants: z13.array(grantSchema),
|
|
880
930
|
// The artifact's current general-access value, so the share surface can
|
|
881
931
|
// render the "General access" line without a second read.
|
|
882
|
-
visibility:
|
|
932
|
+
visibility: z13.enum(["public", "unlisted", "workspace", "private"]),
|
|
883
933
|
// The owning workspace's logo, so the share modal can draw the workspace
|
|
884
934
|
// itself rather than a generic glyph. Null when none was uploaded.
|
|
885
|
-
workspaceLogo:
|
|
935
|
+
workspaceLogo: z13.string().nullable().optional(),
|
|
886
936
|
// The owning workspace's display name, so "People with access" can name the
|
|
887
937
|
// audience `workspace` visibility actually admits ("All Acme members") rather
|
|
888
938
|
// than leaving an empty list to imply nobody. Null for a workspace-less
|
|
889
939
|
// artifact; optional so an older server still parses.
|
|
890
|
-
workspaceName:
|
|
940
|
+
workspaceName: z13.string().nullable().optional()
|
|
891
941
|
});
|
|
892
|
-
var revokeGrantResponseSchema =
|
|
893
|
-
revoked:
|
|
942
|
+
var revokeGrantResponseSchema = z13.object({
|
|
943
|
+
revoked: z13.literal(true)
|
|
894
944
|
});
|
|
895
|
-
var acceptGrantSchema =
|
|
896
|
-
token:
|
|
945
|
+
var acceptGrantSchema = z13.object({
|
|
946
|
+
token: z13.string().min(1)
|
|
897
947
|
});
|
|
898
|
-
var acceptGrantResponseSchema =
|
|
899
|
-
artifactId:
|
|
948
|
+
var acceptGrantResponseSchema = z13.object({
|
|
949
|
+
artifactId: z13.string(),
|
|
900
950
|
// The review shell URL to land on, carrying ?invited=1 for the banner.
|
|
901
|
-
artifactUrl:
|
|
902
|
-
slug:
|
|
903
|
-
title:
|
|
904
|
-
});
|
|
905
|
-
var workspaceGuestSchema =
|
|
906
|
-
artifacts:
|
|
907
|
-
|
|
908
|
-
artifactId:
|
|
909
|
-
grantId:
|
|
910
|
-
slug:
|
|
911
|
-
status:
|
|
912
|
-
title:
|
|
951
|
+
artifactUrl: z13.string().url(),
|
|
952
|
+
slug: z13.string(),
|
|
953
|
+
title: z13.string()
|
|
954
|
+
});
|
|
955
|
+
var workspaceGuestSchema = z13.object({
|
|
956
|
+
artifacts: z13.array(
|
|
957
|
+
z13.object({
|
|
958
|
+
artifactId: z13.string(),
|
|
959
|
+
grantId: z13.string(),
|
|
960
|
+
slug: z13.string(),
|
|
961
|
+
status: z13.enum(["pending", "active"]),
|
|
962
|
+
title: z13.string()
|
|
913
963
|
})
|
|
914
964
|
),
|
|
915
|
-
email:
|
|
916
|
-
image:
|
|
917
|
-
name:
|
|
918
|
-
userId:
|
|
919
|
-
});
|
|
920
|
-
var workspaceGuestListResponseSchema =
|
|
921
|
-
guests:
|
|
922
|
-
});
|
|
923
|
-
var sharedWithMeArtifactSchema =
|
|
924
|
-
artifactId:
|
|
925
|
-
slug:
|
|
926
|
-
title:
|
|
927
|
-
url:
|
|
965
|
+
email: z13.string().nullable(),
|
|
966
|
+
image: z13.string().nullable(),
|
|
967
|
+
name: z13.string().nullable(),
|
|
968
|
+
userId: z13.string().nullable()
|
|
969
|
+
});
|
|
970
|
+
var workspaceGuestListResponseSchema = z13.object({
|
|
971
|
+
guests: z13.array(workspaceGuestSchema)
|
|
972
|
+
});
|
|
973
|
+
var sharedWithMeArtifactSchema = z13.object({
|
|
974
|
+
artifactId: z13.string(),
|
|
975
|
+
slug: z13.string(),
|
|
976
|
+
title: z13.string(),
|
|
977
|
+
url: z13.string().url(),
|
|
928
978
|
// Who shared it, for the "Shared with me" list.
|
|
929
|
-
workspaceName:
|
|
979
|
+
workspaceName: z13.string()
|
|
930
980
|
});
|
|
931
|
-
var sharedWithMeListResponseSchema =
|
|
932
|
-
artifacts:
|
|
981
|
+
var sharedWithMeListResponseSchema = z13.object({
|
|
982
|
+
artifacts: z13.array(sharedWithMeArtifactSchema)
|
|
933
983
|
});
|
|
934
984
|
|
|
935
985
|
// ../shared/src/html-meta.ts
|
|
936
986
|
var HEAD_SCAN_CHARS = 64 * 1024;
|
|
937
987
|
|
|
938
988
|
// ../shared/src/me.ts
|
|
939
|
-
import { z as
|
|
989
|
+
import { z as z15 } from "zod";
|
|
940
990
|
|
|
941
991
|
// ../shared/src/workspaces.ts
|
|
942
|
-
import { z as
|
|
992
|
+
import { z as z14 } from "zod";
|
|
943
993
|
var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
|
|
944
|
-
var workspaceHandleUnavailableReasonSchema =
|
|
994
|
+
var workspaceHandleUnavailableReasonSchema = z14.enum([
|
|
945
995
|
"held",
|
|
946
996
|
"invalid",
|
|
947
997
|
"reserved",
|
|
948
998
|
"taken"
|
|
949
999
|
]);
|
|
950
|
-
var workspaceHandleAvailabilitySchema =
|
|
951
|
-
available:
|
|
952
|
-
handle:
|
|
1000
|
+
var workspaceHandleAvailabilitySchema = z14.object({
|
|
1001
|
+
available: z14.boolean(),
|
|
1002
|
+
handle: z14.string(),
|
|
953
1003
|
// Human-readable sentence for the field hint; null when available.
|
|
954
|
-
message:
|
|
1004
|
+
message: z14.string().nullable(),
|
|
955
1005
|
reason: workspaceHandleUnavailableReasonSchema.nullable()
|
|
956
1006
|
});
|
|
957
|
-
var workspaceRoleSchema =
|
|
958
|
-
var workspaceInviteRoleSchema =
|
|
959
|
-
var workspaceHandleSchema =
|
|
1007
|
+
var workspaceRoleSchema = z14.enum(["owner", "admin", "member"]);
|
|
1008
|
+
var workspaceInviteRoleSchema = z14.enum(["admin", "member"]);
|
|
1009
|
+
var workspaceHandleSchema = z14.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
|
|
960
1010
|
message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
961
1011
|
});
|
|
962
|
-
var createWorkspaceSchema =
|
|
1012
|
+
var createWorkspaceSchema = z14.object({
|
|
963
1013
|
handle: workspaceHandleSchema.optional(),
|
|
964
|
-
name:
|
|
1014
|
+
name: z14.string().trim().min(1).max(100)
|
|
965
1015
|
});
|
|
966
|
-
var updateWorkspaceSchema =
|
|
1016
|
+
var updateWorkspaceSchema = z14.object({
|
|
967
1017
|
handle: workspaceHandleSchema.optional(),
|
|
968
|
-
name:
|
|
1018
|
+
name: z14.string().trim().min(1).max(100).optional()
|
|
969
1019
|
}).refine((body) => body.handle !== void 0 || body.name !== void 0, {
|
|
970
1020
|
message: "provide at least one of name or handle"
|
|
971
1021
|
});
|
|
972
|
-
var workspaceSchema =
|
|
973
|
-
createdAt:
|
|
974
|
-
handle:
|
|
1022
|
+
var workspaceSchema = z14.object({
|
|
1023
|
+
createdAt: z14.string(),
|
|
1024
|
+
handle: z14.string(),
|
|
975
1025
|
// ISO timestamp when `{handle}.artifact.cafe` was provisioned, or null.
|
|
976
1026
|
// Set by the one-time, self-serve enable flow (§4.4); never auto at
|
|
977
1027
|
// create, and always null for personal workspaces.
|
|
978
|
-
hostnameProvisionedAt:
|
|
979
|
-
id:
|
|
1028
|
+
hostnameProvisionedAt: z14.string().nullable(),
|
|
1029
|
+
id: z14.string(),
|
|
980
1030
|
// Auto-provisioned solo workspace, one per user: non-deletable, takes
|
|
981
1031
|
// no members, renameable like any workspace.
|
|
982
|
-
isPersonal:
|
|
1032
|
+
isPersonal: z14.boolean(),
|
|
983
1033
|
// Uploaded logo URL, or null for the initials fallback. Set through the
|
|
984
1034
|
// dedicated /workspaces/:id/avatar route, never the generic update.
|
|
985
|
-
logo:
|
|
986
|
-
name:
|
|
1035
|
+
logo: z14.string().nullable(),
|
|
1036
|
+
name: z14.string(),
|
|
987
1037
|
role: workspaceRoleSchema.nullable(),
|
|
988
|
-
updatedAt:
|
|
1038
|
+
updatedAt: z14.string()
|
|
989
1039
|
});
|
|
990
|
-
var workspaceListResponseSchema =
|
|
991
|
-
workspaces:
|
|
1040
|
+
var workspaceListResponseSchema = z14.object({
|
|
1041
|
+
workspaces: z14.array(workspaceSchema)
|
|
992
1042
|
});
|
|
993
|
-
var deleteWorkspaceResponseSchema =
|
|
994
|
-
deleted:
|
|
1043
|
+
var deleteWorkspaceResponseSchema = z14.object({
|
|
1044
|
+
deleted: z14.literal(true)
|
|
995
1045
|
});
|
|
996
|
-
var workspaceMemberSchema =
|
|
997
|
-
createdAt:
|
|
998
|
-
email:
|
|
999
|
-
image:
|
|
1000
|
-
name:
|
|
1046
|
+
var workspaceMemberSchema = z14.object({
|
|
1047
|
+
createdAt: z14.string(),
|
|
1048
|
+
email: z14.string().email(),
|
|
1049
|
+
image: z14.string().nullable(),
|
|
1050
|
+
name: z14.string(),
|
|
1001
1051
|
role: workspaceRoleSchema,
|
|
1002
|
-
userId:
|
|
1052
|
+
userId: z14.string()
|
|
1003
1053
|
});
|
|
1004
|
-
var workspaceMemberListResponseSchema =
|
|
1005
|
-
members:
|
|
1054
|
+
var workspaceMemberListResponseSchema = z14.object({
|
|
1055
|
+
members: z14.array(workspaceMemberSchema)
|
|
1006
1056
|
});
|
|
1007
|
-
var updateWorkspaceMemberSchema =
|
|
1057
|
+
var updateWorkspaceMemberSchema = z14.object({
|
|
1008
1058
|
role: workspaceRoleSchema
|
|
1009
1059
|
});
|
|
1010
|
-
var removeWorkspaceMemberResponseSchema =
|
|
1011
|
-
removed:
|
|
1060
|
+
var removeWorkspaceMemberResponseSchema = z14.object({
|
|
1061
|
+
removed: z14.literal(true)
|
|
1012
1062
|
});
|
|
1013
|
-
var createWorkspaceInviteSchema =
|
|
1014
|
-
email:
|
|
1015
|
-
expiresAt:
|
|
1016
|
-
maxUses:
|
|
1063
|
+
var createWorkspaceInviteSchema = z14.object({
|
|
1064
|
+
email: z14.string().trim().toLowerCase().email().optional(),
|
|
1065
|
+
expiresAt: z14.string().datetime(),
|
|
1066
|
+
maxUses: z14.number().int().positive().optional(),
|
|
1017
1067
|
role: workspaceInviteRoleSchema
|
|
1018
1068
|
});
|
|
1019
|
-
var workspaceInviteSchema =
|
|
1020
|
-
createdAt:
|
|
1021
|
-
email:
|
|
1022
|
-
expiresAt:
|
|
1023
|
-
id:
|
|
1024
|
-
maxUses:
|
|
1069
|
+
var workspaceInviteSchema = z14.object({
|
|
1070
|
+
createdAt: z14.string(),
|
|
1071
|
+
email: z14.string().email().nullable(),
|
|
1072
|
+
expiresAt: z14.string(),
|
|
1073
|
+
id: z14.string(),
|
|
1074
|
+
maxUses: z14.number().int().positive().nullable(),
|
|
1025
1075
|
role: workspaceInviteRoleSchema,
|
|
1026
|
-
tokenPrefix:
|
|
1027
|
-
useCount:
|
|
1076
|
+
tokenPrefix: z14.string(),
|
|
1077
|
+
useCount: z14.number().int().nonnegative()
|
|
1028
1078
|
});
|
|
1029
1079
|
var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
|
|
1030
|
-
url:
|
|
1080
|
+
url: z14.string().url()
|
|
1031
1081
|
});
|
|
1032
|
-
var workspaceInviteListResponseSchema =
|
|
1033
|
-
invites:
|
|
1082
|
+
var workspaceInviteListResponseSchema = z14.object({
|
|
1083
|
+
invites: z14.array(workspaceInviteSchema)
|
|
1034
1084
|
});
|
|
1035
|
-
var revokeWorkspaceInviteResponseSchema =
|
|
1036
|
-
revoked:
|
|
1085
|
+
var revokeWorkspaceInviteResponseSchema = z14.object({
|
|
1086
|
+
revoked: z14.literal(true)
|
|
1037
1087
|
});
|
|
1038
|
-
var acceptWorkspaceInviteSchema =
|
|
1039
|
-
token:
|
|
1088
|
+
var acceptWorkspaceInviteSchema = z14.object({
|
|
1089
|
+
token: z14.string().min(1)
|
|
1040
1090
|
});
|
|
1041
|
-
var acceptWorkspaceInviteResponseSchema =
|
|
1091
|
+
var acceptWorkspaceInviteResponseSchema = z14.object({
|
|
1042
1092
|
role: workspaceRoleSchema,
|
|
1043
1093
|
workspace: workspaceSchema
|
|
1044
1094
|
});
|
|
1045
|
-
var folderVisibilitySchema =
|
|
1046
|
-
var artifactVisibilitySchema =
|
|
1095
|
+
var folderVisibilitySchema = z14.enum(["public", "workspace"]);
|
|
1096
|
+
var artifactVisibilitySchema = z14.enum([
|
|
1047
1097
|
"public",
|
|
1048
1098
|
"unlisted",
|
|
1049
1099
|
"workspace",
|
|
1050
1100
|
"private"
|
|
1051
1101
|
]);
|
|
1052
|
-
var createFolderSchema =
|
|
1102
|
+
var createFolderSchema = z14.object({
|
|
1053
1103
|
// Optional custom icon; omitted (or null) leaves the default glyph.
|
|
1054
1104
|
emoji: iconEmojiSchema.optional(),
|
|
1055
|
-
name:
|
|
1105
|
+
name: z14.string().trim().min(1).max(100),
|
|
1056
1106
|
visibility: folderVisibilitySchema
|
|
1057
1107
|
});
|
|
1058
|
-
var updateFolderSchema =
|
|
1108
|
+
var updateFolderSchema = z14.object({
|
|
1059
1109
|
// With `visibility`: bulk-update the folder's artifacts to match
|
|
1060
1110
|
// (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
|
|
1061
|
-
cascade:
|
|
1111
|
+
cascade: z14.boolean().optional(),
|
|
1062
1112
|
// Explicit null clears the icon back to the default glyph, so this is
|
|
1063
1113
|
// a field where "sent as null" and "omitted" mean different things.
|
|
1064
1114
|
emoji: iconEmojiSchema.optional(),
|
|
1065
|
-
name:
|
|
1066
|
-
sortOrder:
|
|
1115
|
+
name: z14.string().trim().min(1).max(100).optional(),
|
|
1116
|
+
sortOrder: z14.number().int().min(0).optional(),
|
|
1067
1117
|
visibility: folderVisibilitySchema.optional()
|
|
1068
1118
|
}).refine(
|
|
1069
1119
|
(body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0 || body.emoji !== void 0,
|
|
@@ -1071,93 +1121,95 @@ var updateFolderSchema = z13.object({
|
|
|
1071
1121
|
).refine((body) => !body.cascade || body.visibility !== void 0, {
|
|
1072
1122
|
message: "cascade requires visibility"
|
|
1073
1123
|
});
|
|
1074
|
-
var folderSchema =
|
|
1075
|
-
createdAt:
|
|
1124
|
+
var folderSchema = z14.object({
|
|
1125
|
+
createdAt: z14.string(),
|
|
1076
1126
|
// Custom icon, or null to render the default folder glyph.
|
|
1077
|
-
emoji:
|
|
1078
|
-
id:
|
|
1079
|
-
name:
|
|
1127
|
+
emoji: z14.string().nullable(),
|
|
1128
|
+
id: z14.string(),
|
|
1129
|
+
name: z14.string(),
|
|
1080
1130
|
// Permalink segment for the public folder page (/w/{handle}/f/{slug}).
|
|
1081
1131
|
// Server-derived from the name; renames regenerate it.
|
|
1082
|
-
slug:
|
|
1083
|
-
sortOrder:
|
|
1084
|
-
updatedAt:
|
|
1132
|
+
slug: z14.string(),
|
|
1133
|
+
sortOrder: z14.number().int(),
|
|
1134
|
+
updatedAt: z14.string(),
|
|
1085
1135
|
visibility: folderVisibilitySchema,
|
|
1086
|
-
workspaceId:
|
|
1136
|
+
workspaceId: z14.string()
|
|
1087
1137
|
});
|
|
1088
|
-
var folderListResponseSchema =
|
|
1089
|
-
folders:
|
|
1138
|
+
var folderListResponseSchema = z14.object({
|
|
1139
|
+
folders: z14.array(folderSchema)
|
|
1090
1140
|
});
|
|
1091
|
-
var deleteFolderResponseSchema =
|
|
1092
|
-
deleted:
|
|
1141
|
+
var deleteFolderResponseSchema = z14.object({
|
|
1142
|
+
deleted: z14.literal(true)
|
|
1093
1143
|
});
|
|
1094
|
-
var workspaceArtifactSchema =
|
|
1095
|
-
|
|
1096
|
-
|
|
1144
|
+
var workspaceArtifactSchema = z14.object({
|
|
1145
|
+
autoDeleteAt: z14.string().datetime().nullable().optional(),
|
|
1146
|
+
createdAt: z14.string(),
|
|
1147
|
+
currentVersionNumber: z14.number().int().nullable(),
|
|
1097
1148
|
// Custom icon, or null to render the default document glyph.
|
|
1098
|
-
emoji:
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1149
|
+
emoji: z14.string().nullable(),
|
|
1150
|
+
expiresAt: z14.string().datetime().nullable().optional(),
|
|
1151
|
+
folderId: z14.string().nullable(),
|
|
1152
|
+
id: z14.string(),
|
|
1153
|
+
openCommentCount: z14.number().int().nonnegative(),
|
|
1102
1154
|
reviewMode: reviewModeSchema,
|
|
1103
|
-
slug:
|
|
1104
|
-
title:
|
|
1105
|
-
updatedAt:
|
|
1106
|
-
url:
|
|
1107
|
-
versionCount:
|
|
1155
|
+
slug: z14.string(),
|
|
1156
|
+
title: z14.string(),
|
|
1157
|
+
updatedAt: z14.string(),
|
|
1158
|
+
url: z14.string().url(),
|
|
1159
|
+
versionCount: z14.number().int().nonnegative(),
|
|
1108
1160
|
visibility: artifactVisibilitySchema
|
|
1109
1161
|
});
|
|
1110
|
-
var workspaceArtifactListResponseSchema =
|
|
1111
|
-
artifacts:
|
|
1162
|
+
var workspaceArtifactListResponseSchema = z14.object({
|
|
1163
|
+
artifacts: z14.array(workspaceArtifactSchema)
|
|
1112
1164
|
});
|
|
1113
|
-
var workspaceArtifactSearchResultSchema =
|
|
1114
|
-
id:
|
|
1115
|
-
slug:
|
|
1116
|
-
title:
|
|
1165
|
+
var workspaceArtifactSearchResultSchema = z14.object({
|
|
1166
|
+
id: z14.string(),
|
|
1167
|
+
slug: z14.string(),
|
|
1168
|
+
title: z14.string()
|
|
1117
1169
|
});
|
|
1118
|
-
var workspaceArtifactSearchResponseSchema =
|
|
1119
|
-
results:
|
|
1170
|
+
var workspaceArtifactSearchResponseSchema = z14.object({
|
|
1171
|
+
results: z14.array(workspaceArtifactSearchResultSchema)
|
|
1120
1172
|
});
|
|
1121
1173
|
|
|
1122
1174
|
// ../shared/src/me.ts
|
|
1123
|
-
var personSchema =
|
|
1124
|
-
image:
|
|
1125
|
-
name:
|
|
1175
|
+
var personSchema = z15.object({
|
|
1176
|
+
image: z15.string().nullable(),
|
|
1177
|
+
name: z15.string()
|
|
1126
1178
|
});
|
|
1127
|
-
var ownArtifactSchema =
|
|
1179
|
+
var ownArtifactSchema = z15.object({
|
|
1180
|
+
autoDeleteAt: z15.string().datetime().nullable().optional(),
|
|
1128
1181
|
// Everyone else who has published a version, oldest first. Any member may
|
|
1129
1182
|
// publish a new one (SPEC-2 §5.1), so an artifact's byline is often more
|
|
1130
1183
|
// than one person and the card stacks their faces rather than crediting
|
|
1131
1184
|
// whoever shipped last. Excludes `publishedBy`; empty on a solo artifact.
|
|
1132
|
-
coPublishers:
|
|
1133
|
-
createdAt:
|
|
1185
|
+
coPublishers: z15.array(personSchema),
|
|
1186
|
+
createdAt: z15.string(),
|
|
1134
1187
|
// SPEC-2 §5.1 — who *made* the artifact: the author of its first
|
|
1135
1188
|
// finalized version, the same row `viewerCanManage` is decided on. Not
|
|
1136
1189
|
// the same person as `publishedBy` once an agent ships v2, which is why
|
|
1137
1190
|
// the dashboard filters on both. Null for a token-only publish.
|
|
1138
|
-
createdBy:
|
|
1139
|
-
currentVersionNumber:
|
|
1191
|
+
createdBy: z15.object({ image: z15.string().nullable(), name: z15.string() }).nullable(),
|
|
1192
|
+
currentVersionNumber: z15.number().nullable(),
|
|
1140
1193
|
// Custom icon, or null to render the default document glyph.
|
|
1141
|
-
emoji:
|
|
1142
|
-
//
|
|
1143
|
-
|
|
1144
|
-
expiresAt: z14.string().nullable(),
|
|
1194
|
+
emoji: z15.string().nullable(),
|
|
1195
|
+
// Effective expiry: explicit temporary lifetime or anonymous retention.
|
|
1196
|
+
expiresAt: z15.string().nullable(),
|
|
1145
1197
|
// SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
|
|
1146
1198
|
// both null for personal artifacts. `folderId` drives the dashboard's
|
|
1147
1199
|
// move control and folder filter; `folderName` is the display label,
|
|
1148
1200
|
// `folderEmoji` its icon (null = the default folder glyph).
|
|
1149
|
-
folderEmoji:
|
|
1150
|
-
folderId:
|
|
1151
|
-
folderName:
|
|
1201
|
+
folderEmoji: z15.string().nullable(),
|
|
1202
|
+
folderId: z15.string().nullable(),
|
|
1203
|
+
folderName: z15.string().nullable(),
|
|
1152
1204
|
// Whether a publish token is still active — revocable from the
|
|
1153
1205
|
// dashboard (§6.18); the token itself is never returned.
|
|
1154
|
-
hasPublishToken:
|
|
1206
|
+
hasPublishToken: z15.boolean(),
|
|
1155
1207
|
// Owner preference: hide the version list from reviewers in the review
|
|
1156
1208
|
// shell. Managers still see it.
|
|
1157
|
-
hideVersions:
|
|
1158
|
-
id:
|
|
1159
|
-
openThreads:
|
|
1160
|
-
protected:
|
|
1209
|
+
hideVersions: z15.boolean(),
|
|
1210
|
+
id: z15.string(),
|
|
1211
|
+
openThreads: z15.number(),
|
|
1212
|
+
protected: z15.boolean(),
|
|
1161
1213
|
// Who published the current version (falling back to the claiming
|
|
1162
1214
|
// owner) — the dashboard shows a name and avatar per artifact so a
|
|
1163
1215
|
// shared workspace reads as people's work, not anonymous rows. Null
|
|
@@ -1166,44 +1218,44 @@ var ownArtifactSchema = z14.object({
|
|
|
1166
1218
|
// SPEC-2 §5.8 — the review dial, so the dashboard's settings dialog can
|
|
1167
1219
|
// render current state without a second fetch.
|
|
1168
1220
|
reviewMode: reviewModeSchema,
|
|
1169
|
-
slug:
|
|
1170
|
-
title:
|
|
1171
|
-
totalViews:
|
|
1221
|
+
slug: z15.string(),
|
|
1222
|
+
title: z15.string(),
|
|
1223
|
+
totalViews: z15.number(),
|
|
1172
1224
|
// SPEC §6.20 — messages by others newer than the viewer's last-seen
|
|
1173
1225
|
// watermark. 0 for signed-out/API-key-less contexts.
|
|
1174
|
-
unreadComments:
|
|
1175
|
-
updatedAt:
|
|
1176
|
-
url:
|
|
1177
|
-
versionCount:
|
|
1226
|
+
unreadComments: z15.number(),
|
|
1227
|
+
updatedAt: z15.string(),
|
|
1228
|
+
url: z15.string(),
|
|
1229
|
+
versionCount: z15.number(),
|
|
1178
1230
|
// SPEC-2 §5.1 — whether the caller clears the manage bar for this
|
|
1179
1231
|
// artifact (workspace admin, or the person who first published it).
|
|
1180
1232
|
// Only a manager's folder move inherits the folder's visibility, so the
|
|
1181
1233
|
// dashboard warns exactly the people the change can actually affect, and
|
|
1182
1234
|
// only a manager is offered the Share entry (§5.7).
|
|
1183
|
-
viewerCanManage:
|
|
1235
|
+
viewerCanManage: z15.boolean(),
|
|
1184
1236
|
// SPEC-2 §5.2 — the artifact's own visibility, the single enforcement
|
|
1185
1237
|
// source. The dashboard needs it to tell whether a folder move would
|
|
1186
1238
|
// widen exposure before it sends the PATCH (§3.4), and to render the
|
|
1187
1239
|
// audience glyph — one per level — on rows and cards (§5.7).
|
|
1188
1240
|
visibility: artifactVisibilitySchema
|
|
1189
1241
|
});
|
|
1190
|
-
var ownArtifactsResponseSchema =
|
|
1191
|
-
artifacts:
|
|
1242
|
+
var ownArtifactsResponseSchema = z15.object({
|
|
1243
|
+
artifacts: z15.array(ownArtifactSchema)
|
|
1192
1244
|
});
|
|
1193
|
-
var revokePublishTokenResponseSchema =
|
|
1194
|
-
revoked:
|
|
1245
|
+
var revokePublishTokenResponseSchema = z15.object({
|
|
1246
|
+
revoked: z15.boolean()
|
|
1195
1247
|
});
|
|
1196
1248
|
|
|
1197
1249
|
// ../shared/src/owner.ts
|
|
1198
|
-
import { z as
|
|
1250
|
+
import { z as z16 } from "zod";
|
|
1199
1251
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
|
|
1200
|
-
var claimResponseSchema =
|
|
1201
|
-
artifactId:
|
|
1202
|
-
slug:
|
|
1203
|
-
title:
|
|
1204
|
-
url:
|
|
1252
|
+
var claimResponseSchema = z16.object({
|
|
1253
|
+
artifactId: z16.string(),
|
|
1254
|
+
slug: z16.string(),
|
|
1255
|
+
title: z16.string(),
|
|
1256
|
+
url: z16.string().url()
|
|
1205
1257
|
});
|
|
1206
|
-
var updateArtifactSchema =
|
|
1258
|
+
var updateArtifactSchema = z16.object({
|
|
1207
1259
|
// Custom icon; explicit null clears it back to the default glyph.
|
|
1208
1260
|
// Part of what the artifact *is* (like the title), so it takes the
|
|
1209
1261
|
// same manage rights as title/slug — not the member-level folder move.
|
|
@@ -1211,159 +1263,159 @@ var updateArtifactSchema = z15.object({
|
|
|
1211
1263
|
// SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
|
|
1212
1264
|
// folder; `workspaceId: null` moves the artifact out of its workspace
|
|
1213
1265
|
// (§5.5 ownership transfer).
|
|
1214
|
-
folderId:
|
|
1266
|
+
folderId: z16.string().min(1).nullable().optional(),
|
|
1215
1267
|
// Keep the version list out of the review shell. Presentation only:
|
|
1216
1268
|
// versions stay immutable, and a `/vN/` URL still resolves.
|
|
1217
|
-
hideVersions:
|
|
1218
|
-
slug:
|
|
1269
|
+
hideVersions: z16.boolean().optional(),
|
|
1270
|
+
slug: z16.string().min(3).max(63).regex(SLUG_PATTERN, {
|
|
1219
1271
|
message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
1220
1272
|
}).optional(),
|
|
1221
|
-
title:
|
|
1273
|
+
title: z16.string().min(1).max(200).optional(),
|
|
1222
1274
|
visibility: artifactVisibilitySchema.optional(),
|
|
1223
|
-
workspaceId:
|
|
1275
|
+
workspaceId: z16.string().min(1).nullable().optional()
|
|
1224
1276
|
}).refine(
|
|
1225
1277
|
(body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0 || body.emoji !== void 0 || body.hideVersions !== void 0,
|
|
1226
1278
|
{
|
|
1227
1279
|
message: "provide at least one of title, slug, visibility, folderId, workspaceId, emoji, hideVersions"
|
|
1228
1280
|
}
|
|
1229
1281
|
);
|
|
1230
|
-
var deleteArtifactResponseSchema =
|
|
1231
|
-
deleted:
|
|
1282
|
+
var deleteArtifactResponseSchema = z16.object({
|
|
1283
|
+
deleted: z16.literal(true)
|
|
1232
1284
|
});
|
|
1233
1285
|
|
|
1234
1286
|
// ../shared/src/presence.ts
|
|
1235
|
-
import { z as
|
|
1287
|
+
import { z as z17 } from "zod";
|
|
1236
1288
|
var AGENT_STATES = ["listening", "editing", "publishing"];
|
|
1237
|
-
var agentStateSchema =
|
|
1289
|
+
var agentStateSchema = z17.enum(AGENT_STATES);
|
|
1238
1290
|
var AGENT_PRESENCE = {
|
|
1239
1291
|
editingTtlSeconds: 300,
|
|
1240
1292
|
heartbeatSeconds: 15,
|
|
1241
1293
|
shellPollSeconds: 15,
|
|
1242
1294
|
ttlSeconds: 45
|
|
1243
1295
|
};
|
|
1244
|
-
var agentHeartbeatBodySchema =
|
|
1296
|
+
var agentHeartbeatBodySchema = z17.object({
|
|
1245
1297
|
state: agentStateSchema.optional()
|
|
1246
1298
|
});
|
|
1247
|
-
var agentHeartbeatResponseSchema =
|
|
1248
|
-
listening:
|
|
1299
|
+
var agentHeartbeatResponseSchema = z17.object({
|
|
1300
|
+
listening: z17.literal(true),
|
|
1249
1301
|
state: agentStateSchema.optional(),
|
|
1250
|
-
ttlSeconds:
|
|
1302
|
+
ttlSeconds: z17.number().int().positive()
|
|
1251
1303
|
});
|
|
1252
|
-
var agentPresenceSchema =
|
|
1253
|
-
agent:
|
|
1254
|
-
listening:
|
|
1304
|
+
var agentPresenceSchema = z17.object({
|
|
1305
|
+
agent: z17.object({ id: z17.string().nullable(), label: z17.string() }).nullable().default(null),
|
|
1306
|
+
listening: z17.boolean(),
|
|
1255
1307
|
state: agentStateSchema.nullable().optional(),
|
|
1256
|
-
versionNumber:
|
|
1308
|
+
versionNumber: z17.number().int().positive().nullable()
|
|
1257
1309
|
});
|
|
1258
1310
|
|
|
1259
1311
|
// ../shared/src/protection.ts
|
|
1260
|
-
import { z as
|
|
1312
|
+
import { z as z18 } from "zod";
|
|
1261
1313
|
var PASSWORD_MIN = 6;
|
|
1262
1314
|
var PASSWORD_MAX = 128;
|
|
1263
|
-
var gatePasswordSchema =
|
|
1264
|
-
var setProtectionSchema =
|
|
1315
|
+
var gatePasswordSchema = z18.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
|
|
1316
|
+
var setProtectionSchema = z18.object({
|
|
1265
1317
|
password: gatePasswordSchema.nullable()
|
|
1266
1318
|
});
|
|
1267
|
-
var protectionResponseSchema =
|
|
1268
|
-
protected:
|
|
1319
|
+
var protectionResponseSchema = z18.object({
|
|
1320
|
+
protected: z18.boolean()
|
|
1269
1321
|
});
|
|
1270
|
-
var unlockSchema =
|
|
1271
|
-
password:
|
|
1322
|
+
var unlockSchema = z18.object({
|
|
1323
|
+
password: z18.string().min(1).max(1024)
|
|
1272
1324
|
});
|
|
1273
|
-
var unlockResponseSchema =
|
|
1274
|
-
unlocked:
|
|
1325
|
+
var unlockResponseSchema = z18.object({
|
|
1326
|
+
unlocked: z18.literal(true)
|
|
1275
1327
|
});
|
|
1276
1328
|
|
|
1277
1329
|
// ../shared/src/report.ts
|
|
1278
|
-
import { z as
|
|
1279
|
-
var reportRequestSchema =
|
|
1280
|
-
reason:
|
|
1330
|
+
import { z as z19 } from "zod";
|
|
1331
|
+
var reportRequestSchema = z19.object({
|
|
1332
|
+
reason: z19.string().max(1e3).optional()
|
|
1281
1333
|
});
|
|
1282
|
-
var reportResponseSchema =
|
|
1283
|
-
reported:
|
|
1334
|
+
var reportResponseSchema = z19.object({
|
|
1335
|
+
reported: z19.literal(true)
|
|
1284
1336
|
});
|
|
1285
1337
|
|
|
1286
1338
|
// ../shared/src/reviewers.ts
|
|
1287
|
-
import { z as
|
|
1288
|
-
var artifactReviewerSchema =
|
|
1289
|
-
avatarUrl:
|
|
1290
|
-
displayName:
|
|
1339
|
+
import { z as z20 } from "zod";
|
|
1340
|
+
var artifactReviewerSchema = z20.object({
|
|
1341
|
+
avatarUrl: z20.string().nullable(),
|
|
1342
|
+
displayName: z20.string(),
|
|
1291
1343
|
// Exactly one of userId/guestId is set, per `kind`.
|
|
1292
|
-
guestId:
|
|
1344
|
+
guestId: z20.string().nullable(),
|
|
1293
1345
|
// "account", not commentAuthorSchema's "member" — the whole point here is
|
|
1294
1346
|
// that these people need not be workspace members. A guest can be named in
|
|
1295
1347
|
// a comment but never notified: guest_identities carries a display name and
|
|
1296
1348
|
// no email, so there is nowhere to send anything.
|
|
1297
|
-
kind:
|
|
1298
|
-
userId:
|
|
1349
|
+
kind: z20.enum(["account", "guest"]),
|
|
1350
|
+
userId: z20.string().nullable()
|
|
1299
1351
|
});
|
|
1300
|
-
var artifactReviewerListResponseSchema =
|
|
1301
|
-
reviewers:
|
|
1352
|
+
var artifactReviewerListResponseSchema = z20.object({
|
|
1353
|
+
reviewers: z20.array(artifactReviewerSchema)
|
|
1302
1354
|
});
|
|
1303
1355
|
|
|
1304
1356
|
// ../shared/src/stats.ts
|
|
1305
|
-
import { z as
|
|
1306
|
-
var avgViewSecondsSchema =
|
|
1307
|
-
var versionViewStatsSchema =
|
|
1357
|
+
import { z as z21 } from "zod";
|
|
1358
|
+
var avgViewSecondsSchema = z21.number().int().nonnegative().nullable();
|
|
1359
|
+
var versionViewStatsSchema = z21.object({
|
|
1308
1360
|
avgViewSeconds: avgViewSecondsSchema,
|
|
1309
1361
|
// Views from inside the review shell's iframe vs. direct visits to
|
|
1310
1362
|
// the render URL. views = embeddedViews + directViews.
|
|
1311
|
-
directViews:
|
|
1312
|
-
embeddedViews:
|
|
1363
|
+
directViews: z21.number().int().nonnegative(),
|
|
1364
|
+
embeddedViews: z21.number().int().nonnegative(),
|
|
1313
1365
|
// Distinct hashed viewer IPs across the version's lifetime.
|
|
1314
|
-
uniqueViewers:
|
|
1315
|
-
versionNumber:
|
|
1316
|
-
views:
|
|
1366
|
+
uniqueViewers: z21.number().int().nonnegative(),
|
|
1367
|
+
versionNumber: z21.number().int().positive(),
|
|
1368
|
+
views: z21.number().int().nonnegative()
|
|
1317
1369
|
});
|
|
1318
|
-
var dailyViewsSchema =
|
|
1370
|
+
var dailyViewsSchema = z21.object({
|
|
1319
1371
|
avgViewSeconds: avgViewSecondsSchema,
|
|
1320
1372
|
// UTC day, YYYY-MM-DD.
|
|
1321
|
-
day:
|
|
1322
|
-
uniqueViewers:
|
|
1323
|
-
views:
|
|
1373
|
+
day: z21.string(),
|
|
1374
|
+
uniqueViewers: z21.number().int().nonnegative(),
|
|
1375
|
+
views: z21.number().int().nonnegative()
|
|
1324
1376
|
});
|
|
1325
|
-
var artifactStatsResponseSchema =
|
|
1377
|
+
var artifactStatsResponseSchema = z21.object({
|
|
1326
1378
|
avgViewSeconds: avgViewSecondsSchema,
|
|
1327
|
-
daily:
|
|
1379
|
+
daily: z21.array(dailyViewsSchema),
|
|
1328
1380
|
// Distinct hashed viewer IPs across all versions and days.
|
|
1329
|
-
totalUniqueViewers:
|
|
1330
|
-
totalViews:
|
|
1331
|
-
versions:
|
|
1381
|
+
totalUniqueViewers: z21.number().int().nonnegative(),
|
|
1382
|
+
totalViews: z21.number().int().nonnegative(),
|
|
1383
|
+
versions: z21.array(versionViewStatsSchema)
|
|
1332
1384
|
});
|
|
1333
1385
|
|
|
1334
1386
|
// ../shared/src/versions.ts
|
|
1335
|
-
import { z as
|
|
1336
|
-
var versionSourceSchema =
|
|
1337
|
-
agentName:
|
|
1338
|
-
type:
|
|
1387
|
+
import { z as z22 } from "zod";
|
|
1388
|
+
var versionSourceSchema = z22.object({
|
|
1389
|
+
agentName: z22.string().nullable(),
|
|
1390
|
+
type: z22.string()
|
|
1339
1391
|
});
|
|
1340
|
-
var versionChangesSchema =
|
|
1341
|
-
added:
|
|
1342
|
-
changed:
|
|
1343
|
-
removed:
|
|
1392
|
+
var versionChangesSchema = z22.object({
|
|
1393
|
+
added: z22.array(z22.string()),
|
|
1394
|
+
changed: z22.array(z22.string()),
|
|
1395
|
+
removed: z22.array(z22.string())
|
|
1344
1396
|
});
|
|
1345
|
-
var versionSummarySchema =
|
|
1397
|
+
var versionSummarySchema = z22.object({
|
|
1346
1398
|
// Absent for the first version — there is no previous version to diff against.
|
|
1347
1399
|
changes: versionChangesSchema.nullable(),
|
|
1348
|
-
createdAt:
|
|
1349
|
-
entryUrl:
|
|
1350
|
-
fileCount:
|
|
1351
|
-
id:
|
|
1352
|
-
isCurrent:
|
|
1400
|
+
createdAt: z22.string(),
|
|
1401
|
+
entryUrl: z22.string().url(),
|
|
1402
|
+
fileCount: z22.number().int().nonnegative(),
|
|
1403
|
+
id: z22.string(),
|
|
1404
|
+
isCurrent: z22.boolean(),
|
|
1353
1405
|
// Author-supplied bullet-point changelog; null when the publish omitted it.
|
|
1354
|
-
notes:
|
|
1406
|
+
notes: z22.string().nullable(),
|
|
1355
1407
|
// The account that published this version, with their own avatar where
|
|
1356
1408
|
// they've set one. Any workspace member can publish a new version
|
|
1357
1409
|
// (SPEC-2 §5.1), so "which teammate" outranks "which agent" — the agent
|
|
1358
1410
|
// is the tool, this is the person. Null for token-only publishes, which
|
|
1359
1411
|
// carry no account.
|
|
1360
|
-
publishedBy:
|
|
1412
|
+
publishedBy: z22.object({ image: z22.string().nullable(), name: z22.string() }).nullable(),
|
|
1361
1413
|
source: versionSourceSchema,
|
|
1362
|
-
totalSizeBytes:
|
|
1363
|
-
versionNumber:
|
|
1414
|
+
totalSizeBytes: z22.number().int().nonnegative(),
|
|
1415
|
+
versionNumber: z22.number().int().positive()
|
|
1364
1416
|
});
|
|
1365
|
-
var versionListResponseSchema =
|
|
1366
|
-
versions:
|
|
1417
|
+
var versionListResponseSchema = z22.object({
|
|
1418
|
+
versions: z22.array(versionSummarySchema)
|
|
1367
1419
|
});
|
|
1368
1420
|
|
|
1369
1421
|
// src/agent-identity.ts
|
|
@@ -1544,6 +1596,21 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1544
1596
|
(d) => dataSummaryResponseSchema.parse(d)
|
|
1545
1597
|
);
|
|
1546
1598
|
},
|
|
1599
|
+
async insertData(artifactId, collection, fields) {
|
|
1600
|
+
return request(
|
|
1601
|
+
`${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}`,
|
|
1602
|
+
{ body: JSON.stringify(fields), headers, method: "POST" },
|
|
1603
|
+
(d) => dataRecordResponseSchema.parse(d).record
|
|
1604
|
+
);
|
|
1605
|
+
},
|
|
1606
|
+
/** Owner-plane batch: up to DATA_LIMITS.maxInsertBatch, all-or-nothing. */
|
|
1607
|
+
async insertDataBatch(artifactId, collection, records) {
|
|
1608
|
+
return request(
|
|
1609
|
+
`${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}`,
|
|
1610
|
+
{ body: JSON.stringify(records), headers, method: "POST" },
|
|
1611
|
+
(d) => dataBatchInsertResponseSchema.parse(d)
|
|
1612
|
+
);
|
|
1613
|
+
},
|
|
1547
1614
|
// SPEC §6.18 — everything owned by the account this client's API
|
|
1548
1615
|
// key (or session) belongs to.
|
|
1549
1616
|
async listArtifacts() {
|
|
@@ -1647,6 +1714,13 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1647
1714
|
() => void 0
|
|
1648
1715
|
);
|
|
1649
1716
|
},
|
|
1717
|
+
async setExpiration(artifactId, expiresInSeconds) {
|
|
1718
|
+
return request(
|
|
1719
|
+
`${base}/api/v1/artifacts/${artifactId}/expiration`,
|
|
1720
|
+
{ body: JSON.stringify({ expiresInSeconds }), headers, method: "PUT" },
|
|
1721
|
+
(d) => expirationResponseSchema.parse(d)
|
|
1722
|
+
);
|
|
1723
|
+
},
|
|
1650
1724
|
// SPEC §6.16 — set a password (string) or clear it (null). Authorized
|
|
1651
1725
|
// by the publish token this client was created with.
|
|
1652
1726
|
async setProtection(artifactId, password) {
|
|
@@ -1703,17 +1777,17 @@ import {
|
|
|
1703
1777
|
writeFileSync as writeFileSync2
|
|
1704
1778
|
} from "fs";
|
|
1705
1779
|
import path2 from "path";
|
|
1706
|
-
import { z as
|
|
1780
|
+
import { z as z24 } from "zod";
|
|
1707
1781
|
|
|
1708
1782
|
// src/credentials.ts
|
|
1709
1783
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
1710
1784
|
import { homedir } from "os";
|
|
1711
1785
|
import path from "path";
|
|
1712
|
-
import { z as
|
|
1786
|
+
import { z as z23 } from "zod";
|
|
1713
1787
|
var DEFAULT_API_URL = "https://artifact.cafe";
|
|
1714
|
-
var credentialsSchema =
|
|
1715
|
-
keys:
|
|
1716
|
-
version:
|
|
1788
|
+
var credentialsSchema = z23.object({
|
|
1789
|
+
keys: z23.record(z23.string(), z23.string()),
|
|
1790
|
+
version: z23.literal(1)
|
|
1717
1791
|
});
|
|
1718
1792
|
function credentialsPath() {
|
|
1719
1793
|
return path.join(homedir(), ".artifactcafe", "credentials");
|
|
@@ -1763,24 +1837,24 @@ function resolveApiKey(apiUrl2, flag) {
|
|
|
1763
1837
|
}
|
|
1764
1838
|
|
|
1765
1839
|
// src/config.ts
|
|
1766
|
-
var configSchema =
|
|
1767
|
-
apiUrl:
|
|
1768
|
-
artifactId:
|
|
1769
|
-
folder:
|
|
1770
|
-
folderId:
|
|
1840
|
+
var configSchema = z24.object({
|
|
1841
|
+
apiUrl: z24.string(),
|
|
1842
|
+
artifactId: z24.string(),
|
|
1843
|
+
folder: z24.string().optional(),
|
|
1844
|
+
folderId: z24.string().optional(),
|
|
1771
1845
|
// Optional: a folder linked by `link` to an artifact you own carries no
|
|
1772
1846
|
// token — owner actions authorize via your stored login instead. A first
|
|
1773
1847
|
// publish still writes the anonymous-mode token here.
|
|
1774
|
-
publishToken:
|
|
1775
|
-
slug:
|
|
1776
|
-
url:
|
|
1777
|
-
workspace:
|
|
1778
|
-
workspaceId:
|
|
1779
|
-
workspaceKind:
|
|
1780
|
-
});
|
|
1781
|
-
var projectConfigSchema =
|
|
1782
|
-
folder:
|
|
1783
|
-
workspace:
|
|
1848
|
+
publishToken: z24.string().optional(),
|
|
1849
|
+
slug: z24.string(),
|
|
1850
|
+
url: z24.string(),
|
|
1851
|
+
workspace: z24.string().optional(),
|
|
1852
|
+
workspaceId: z24.string().optional(),
|
|
1853
|
+
workspaceKind: z24.enum(["personal", "workspace"]).optional()
|
|
1854
|
+
});
|
|
1855
|
+
var projectConfigSchema = z24.object({
|
|
1856
|
+
folder: z24.string().trim().min(1).optional(),
|
|
1857
|
+
workspace: z24.string().trim().min(1)
|
|
1784
1858
|
}).strict();
|
|
1785
1859
|
var CONFIG_DIR = ".artifactcafe";
|
|
1786
1860
|
var CONFIG_FILE = "config.json";
|
|
@@ -1816,7 +1890,7 @@ function readProjectConfig(dir) {
|
|
|
1816
1890
|
JSON.parse(readFileSync2(file, "utf8"))
|
|
1817
1891
|
);
|
|
1818
1892
|
} catch (error) {
|
|
1819
|
-
const message = error instanceof
|
|
1893
|
+
const message = error instanceof z24.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
|
|
1820
1894
|
throw new Error(`Invalid ${file}: ${message}.`);
|
|
1821
1895
|
}
|
|
1822
1896
|
}
|
|
@@ -2214,7 +2288,7 @@ import path8 from "path";
|
|
|
2214
2288
|
|
|
2215
2289
|
// src/publish.ts
|
|
2216
2290
|
import { exec } from "child_process";
|
|
2217
|
-
import { readFileSync as readFileSync5 } from "fs";
|
|
2291
|
+
import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
2218
2292
|
import path7 from "path";
|
|
2219
2293
|
import { createInterface } from "readline";
|
|
2220
2294
|
|
|
@@ -2312,6 +2386,75 @@ async function dataLs(opts) {
|
|
|
2312
2386
|
console.log(` ${JSON.stringify(fields)}`);
|
|
2313
2387
|
}
|
|
2314
2388
|
}
|
|
2389
|
+
var RECORD_SHAPE_HINT = `a JSON object or array of objects, e.g. '{"name":"Ada"}'`;
|
|
2390
|
+
function isRecord(value) {
|
|
2391
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2392
|
+
}
|
|
2393
|
+
async function dataInsert(opts) {
|
|
2394
|
+
if (opts.record === void 0 === (opts.file === void 0)) {
|
|
2395
|
+
throw new Error("Pass exactly one of --record '<json>' or --file <path>.");
|
|
2396
|
+
}
|
|
2397
|
+
const text = opts.record ?? readFileSync3(opts.file, "utf8");
|
|
2398
|
+
let parsed;
|
|
2399
|
+
try {
|
|
2400
|
+
parsed = JSON.parse(text);
|
|
2401
|
+
} catch {
|
|
2402
|
+
throw new Error(`Records must be ${RECORD_SHAPE_HINT}.`);
|
|
2403
|
+
}
|
|
2404
|
+
const linked = requireLinkedArtifact(opts.dir);
|
|
2405
|
+
const client = createApiClient(linked.apiUrl, linked.publishToken);
|
|
2406
|
+
if (isRecord(parsed)) {
|
|
2407
|
+
const record = await client.insertData(
|
|
2408
|
+
linked.artifactId,
|
|
2409
|
+
opts.collection,
|
|
2410
|
+
parsed
|
|
2411
|
+
);
|
|
2412
|
+
if (opts.json) {
|
|
2413
|
+
console.log(JSON.stringify({ collection: opts.collection, record }));
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
console.log(`Inserted ${record.id} into "${opts.collection}".`);
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
if (!(Array.isArray(parsed) && parsed.length > 0 && parsed.every(isRecord))) {
|
|
2420
|
+
throw new Error(`Records must be ${RECORD_SHAPE_HINT}.`);
|
|
2421
|
+
}
|
|
2422
|
+
const records = [];
|
|
2423
|
+
for (let i = 0; i < parsed.length; i += DATA_LIMITS.maxInsertBatch) {
|
|
2424
|
+
try {
|
|
2425
|
+
const page = await client.insertDataBatch(
|
|
2426
|
+
linked.artifactId,
|
|
2427
|
+
opts.collection,
|
|
2428
|
+
parsed.slice(i, i + DATA_LIMITS.maxInsertBatch)
|
|
2429
|
+
);
|
|
2430
|
+
records.push(...page.records);
|
|
2431
|
+
} catch (err) {
|
|
2432
|
+
if (records.length === 0) {
|
|
2433
|
+
throw err;
|
|
2434
|
+
}
|
|
2435
|
+
const note = ` (the first ${records.length} records were inserted; records ${i}+ may not have been)`;
|
|
2436
|
+
if (err instanceof ApiRequestError) {
|
|
2437
|
+
throw new ApiRequestError(
|
|
2438
|
+
err.status,
|
|
2439
|
+
err.code,
|
|
2440
|
+
`${err.message}${note}`,
|
|
2441
|
+
err.retryAfter
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
throw new Error(
|
|
2445
|
+
`${err instanceof Error ? err.message : String(err)}${note}`
|
|
2446
|
+
);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
if (opts.json) {
|
|
2450
|
+
console.log(JSON.stringify({ collection: opts.collection, records }));
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
const plural = records.length === 1 ? "" : "s";
|
|
2454
|
+
console.log(
|
|
2455
|
+
`Inserted ${records.length} record${plural} into "${opts.collection}".`
|
|
2456
|
+
);
|
|
2457
|
+
}
|
|
2315
2458
|
async function dataExport(opts) {
|
|
2316
2459
|
const linked = requireLinkedArtifact(opts.dir);
|
|
2317
2460
|
const client = createApiClient(linked.apiUrl, linked.publishToken);
|
|
@@ -2363,7 +2506,7 @@ async function dataClear(opts) {
|
|
|
2363
2506
|
|
|
2364
2507
|
// src/manifest.ts
|
|
2365
2508
|
import { createHash } from "crypto";
|
|
2366
|
-
import { readdirSync, readFileSync as readFileSync4, statSync } from "fs";
|
|
2509
|
+
import { lstatSync, readdirSync, readFileSync as readFileSync4, statSync } from "fs";
|
|
2367
2510
|
import path6 from "path";
|
|
2368
2511
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".artifactcafe"]);
|
|
2369
2512
|
var IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store", "artifact-cafe.json"]);
|
|
@@ -2385,6 +2528,7 @@ function isIgnoredFile(name) {
|
|
|
2385
2528
|
var MIME_TYPES = {
|
|
2386
2529
|
".avif": "image/avif",
|
|
2387
2530
|
".css": "text/css",
|
|
2531
|
+
".csv": "text/csv",
|
|
2388
2532
|
".gif": "image/gif",
|
|
2389
2533
|
".htm": "text/html",
|
|
2390
2534
|
".html": "text/html",
|
|
@@ -2394,6 +2538,7 @@ var MIME_TYPES = {
|
|
|
2394
2538
|
".js": "text/javascript",
|
|
2395
2539
|
".json": "application/json",
|
|
2396
2540
|
".map": "application/json",
|
|
2541
|
+
".markdown": "text/markdown",
|
|
2397
2542
|
".md": "text/markdown",
|
|
2398
2543
|
".mjs": "text/javascript",
|
|
2399
2544
|
".mp3": "audio/mpeg",
|
|
@@ -2415,6 +2560,31 @@ var MIME_TYPES = {
|
|
|
2415
2560
|
function mimeFor(filePath) {
|
|
2416
2561
|
return MIME_TYPES[path6.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
2417
2562
|
}
|
|
2563
|
+
function localFile(absPath, rel) {
|
|
2564
|
+
if (isIgnoredFile(path6.basename(rel)) || !isSafeArtifactPath(rel)) {
|
|
2565
|
+
return null;
|
|
2566
|
+
}
|
|
2567
|
+
const { size } = statSync(absPath);
|
|
2568
|
+
if (size === 0) {
|
|
2569
|
+
return null;
|
|
2570
|
+
}
|
|
2571
|
+
return {
|
|
2572
|
+
absPath,
|
|
2573
|
+
hash: `sha256_${createHash("sha256").update(readFileSync4(absPath)).digest("hex")}`,
|
|
2574
|
+
mimeType: mimeFor(rel),
|
|
2575
|
+
path: rel,
|
|
2576
|
+
size
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
function scanPath(target) {
|
|
2580
|
+
const stat = lstatSync(target);
|
|
2581
|
+
if (stat.isDirectory()) {
|
|
2582
|
+
return scanDirectory(target);
|
|
2583
|
+
}
|
|
2584
|
+
const rel = path6.basename(target);
|
|
2585
|
+
const file = stat.isFile() ? localFile(target, rel) : null;
|
|
2586
|
+
return file ? { files: [file], skipped: [] } : { files: [], skipped: [rel] };
|
|
2587
|
+
}
|
|
2418
2588
|
function scanDirectory(dir) {
|
|
2419
2589
|
const files = [];
|
|
2420
2590
|
const skipped = [];
|
|
@@ -2437,23 +2607,12 @@ function scanDirectory(dir) {
|
|
|
2437
2607
|
if (!entry.isFile()) {
|
|
2438
2608
|
continue;
|
|
2439
2609
|
}
|
|
2440
|
-
|
|
2610
|
+
const file = localFile(abs, rel);
|
|
2611
|
+
if (!file) {
|
|
2441
2612
|
skipped.push(rel);
|
|
2442
2613
|
continue;
|
|
2443
2614
|
}
|
|
2444
|
-
|
|
2445
|
-
if (size === 0) {
|
|
2446
|
-
skipped.push(rel);
|
|
2447
|
-
continue;
|
|
2448
|
-
}
|
|
2449
|
-
const content = readFileSync4(abs);
|
|
2450
|
-
files.push({
|
|
2451
|
-
absPath: abs,
|
|
2452
|
-
hash: `sha256_${createHash("sha256").update(content).digest("hex")}`,
|
|
2453
|
-
mimeType: mimeFor(rel),
|
|
2454
|
-
path: rel,
|
|
2455
|
-
size
|
|
2456
|
-
});
|
|
2615
|
+
files.push(file);
|
|
2457
2616
|
}
|
|
2458
2617
|
}
|
|
2459
2618
|
walk(dir, "");
|
|
@@ -2461,27 +2620,37 @@ function scanDirectory(dir) {
|
|
|
2461
2620
|
return { files, skipped };
|
|
2462
2621
|
}
|
|
2463
2622
|
function resolveEntry(files, explicit) {
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2623
|
+
return resolvePublishEntry(files, explicit);
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
// src/spinner.ts
|
|
2627
|
+
var FRAMES = ["\xE3", "\xE4", "\xE5"];
|
|
2628
|
+
var INTERVAL_MS = 420;
|
|
2629
|
+
var AMBER2 = "\x1B[38;2;214;138;57m";
|
|
2630
|
+
var RESET2 = "\x1B[0m";
|
|
2631
|
+
var CLEAR_LINE = "\r\x1B[2K";
|
|
2632
|
+
function startBrewing(label, enabled) {
|
|
2633
|
+
const stream = process.stderr;
|
|
2634
|
+
if (!(enabled && stream.isTTY) || process.env.CI) {
|
|
2635
|
+
return { stop: () => void 0 };
|
|
2636
|
+
}
|
|
2637
|
+
const color = !process.env.NO_COLOR;
|
|
2638
|
+
let frame = 0;
|
|
2639
|
+
const draw = () => {
|
|
2640
|
+
const glyph = FRAMES[frame % FRAMES.length];
|
|
2641
|
+
stream.write(
|
|
2642
|
+
`${CLEAR_LINE} ${color ? `${AMBER2}${glyph}${RESET2}` : glyph} ${label}`
|
|
2643
|
+
);
|
|
2644
|
+
frame += 1;
|
|
2645
|
+
};
|
|
2646
|
+
draw();
|
|
2647
|
+
const timer = setInterval(draw, INTERVAL_MS);
|
|
2648
|
+
return {
|
|
2649
|
+
stop() {
|
|
2650
|
+
clearInterval(timer);
|
|
2651
|
+
stream.write(CLEAR_LINE);
|
|
2469
2652
|
}
|
|
2470
|
-
|
|
2471
|
-
}
|
|
2472
|
-
if (files.some((f) => f.path === "index.html")) {
|
|
2473
|
-
return "index.html";
|
|
2474
|
-
}
|
|
2475
|
-
const htmlFiles = files.filter(
|
|
2476
|
-
(f) => f.path.endsWith(".html") && !f.path.includes("/")
|
|
2477
|
-
);
|
|
2478
|
-
const [onlyHtmlFile] = htmlFiles;
|
|
2479
|
-
if (onlyHtmlFile && htmlFiles.length === 1) {
|
|
2480
|
-
return onlyHtmlFile.path;
|
|
2481
|
-
}
|
|
2482
|
-
throw new Error(
|
|
2483
|
-
htmlFiles.length === 0 ? "No entry file found. Add an index.html or pass --entry <file>." : `Multiple HTML files found (${htmlFiles.map((f) => f.path).join(", ")}). Pass --entry <file>.`
|
|
2484
|
-
);
|
|
2653
|
+
};
|
|
2485
2654
|
}
|
|
2486
2655
|
|
|
2487
2656
|
// src/publish.ts
|
|
@@ -2492,8 +2661,10 @@ var REVIEW_LABEL = {
|
|
|
2492
2661
|
readonly: "read-only \u2014 existing comments only"
|
|
2493
2662
|
};
|
|
2494
2663
|
async function publish(opts) {
|
|
2495
|
-
const
|
|
2664
|
+
const target = path7.resolve(opts.dir);
|
|
2665
|
+
const dir = statSync2(target).isFile() ? path7.dirname(target) : target;
|
|
2496
2666
|
const config = readConfig(dir);
|
|
2667
|
+
const expiresInSeconds = opts.expiresIn === void 0 ? void 0 : parseArtifactDuration(opts.expiresIn);
|
|
2497
2668
|
if (opts.password !== void 0) {
|
|
2498
2669
|
const parsed = gatePasswordSchema.safeParse(opts.password);
|
|
2499
2670
|
if (!parsed.success) {
|
|
@@ -2518,11 +2689,11 @@ async function publish(opts) {
|
|
|
2518
2689
|
apiUrl: apiUrl2,
|
|
2519
2690
|
config,
|
|
2520
2691
|
isNewArtifact: artifactId === null,
|
|
2521
|
-
opts
|
|
2692
|
+
opts: { ...opts, dir }
|
|
2522
2693
|
});
|
|
2523
|
-
const { files, skipped } =
|
|
2694
|
+
const { files, skipped } = scanPath(target);
|
|
2524
2695
|
if (files.length === 0) {
|
|
2525
|
-
throw new Error(`Nothing to publish
|
|
2696
|
+
throw new Error(`Nothing to publish at ${target} (after ignore rules).`);
|
|
2526
2697
|
}
|
|
2527
2698
|
const entryPath = resolveEntry(files, opts.entry);
|
|
2528
2699
|
const dataInfo = inspectDataManifest(files);
|
|
@@ -2536,6 +2707,7 @@ async function publish(opts) {
|
|
|
2536
2707
|
}
|
|
2537
2708
|
const sessionBody = {
|
|
2538
2709
|
artifactId,
|
|
2710
|
+
...expiresInSeconds === void 0 ? {} : { expiresInSeconds },
|
|
2539
2711
|
// Overrides the entry HTML's own meta description on the share card.
|
|
2540
2712
|
// Omitted entirely when absent, so the document keeps speaking for itself.
|
|
2541
2713
|
...opts.description?.trim() ? { description: opts.description.trim() } : {},
|
|
@@ -2570,11 +2742,17 @@ async function publish(opts) {
|
|
|
2570
2742
|
client = createApiClient(apiUrl2, credential);
|
|
2571
2743
|
session = await client.createSession(sessionBody);
|
|
2572
2744
|
}
|
|
2745
|
+
if (expiresInSeconds !== void 0 && session.expiresInSeconds !== expiresInSeconds) {
|
|
2746
|
+
throw new Error(
|
|
2747
|
+
"This API does not support temporary publishing. Nothing was uploaded or finalized; upgrade the API before using --expires-in."
|
|
2748
|
+
);
|
|
2749
|
+
}
|
|
2573
2750
|
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
2574
2751
|
log(
|
|
2575
2752
|
` uploading ${session.filesToUpload.length} file(s)` + (session.skippedFiles.length ? `, ${session.skippedFiles.length} unchanged` : "")
|
|
2576
2753
|
);
|
|
2577
2754
|
const presence = startPublishingPresence(client, session.artifactId);
|
|
2755
|
+
const brewing = startBrewing("brewing\u2026", !opts.json);
|
|
2578
2756
|
try {
|
|
2579
2757
|
await inBatches(
|
|
2580
2758
|
session.filesToUpload,
|
|
@@ -2591,10 +2769,18 @@ async function publish(opts) {
|
|
|
2591
2769
|
);
|
|
2592
2770
|
}
|
|
2593
2771
|
);
|
|
2772
|
+
} catch (error) {
|
|
2773
|
+
brewing.stop();
|
|
2774
|
+
throw error;
|
|
2594
2775
|
} finally {
|
|
2595
2776
|
presence.stop();
|
|
2596
2777
|
}
|
|
2597
|
-
|
|
2778
|
+
let result;
|
|
2779
|
+
try {
|
|
2780
|
+
result = await client.finalize(session.publishSessionId);
|
|
2781
|
+
} finally {
|
|
2782
|
+
brewing.stop();
|
|
2783
|
+
}
|
|
2598
2784
|
if (usingAgentToken && result.workspaceId) {
|
|
2599
2785
|
placement.workspaceId = result.workspaceId;
|
|
2600
2786
|
}
|
|
@@ -2634,6 +2820,8 @@ async function publish(opts) {
|
|
|
2634
2820
|
JSON.stringify(
|
|
2635
2821
|
{
|
|
2636
2822
|
artifactId: result.artifactId,
|
|
2823
|
+
autoDeleteAt: result.autoDeleteAt ?? null,
|
|
2824
|
+
expiresAt: result.expiresAt ?? null,
|
|
2637
2825
|
...dataInfo.config ? { data: dataInfo.config } : {},
|
|
2638
2826
|
claimed: result.claimed,
|
|
2639
2827
|
destination: placement.destination,
|
|
@@ -2678,7 +2866,7 @@ ${result.url}
|
|
|
2678
2866
|
${result.claimUrl}
|
|
2679
2867
|
`
|
|
2680
2868
|
);
|
|
2681
|
-
} else if (result.claimed) {
|
|
2869
|
+
} else if (result.claimed && !result.expiresAt) {
|
|
2682
2870
|
console.log("Claimed by your account \u2014 no expiry.\n");
|
|
2683
2871
|
}
|
|
2684
2872
|
if (opts.password !== void 0) {
|
|
@@ -2714,6 +2902,12 @@ ${result.claimUrl}
|
|
|
2714
2902
|
}
|
|
2715
2903
|
}
|
|
2716
2904
|
if (!opts.json) {
|
|
2905
|
+
if (result.expiresAt) {
|
|
2906
|
+
console.log(
|
|
2907
|
+
`${result.autoDeleteAt ? "Temporary artifact" : "Anonymous artifact"} \u2014 expires at ${result.expiresAt}. All versions, comments, files, and artifact data will be automatically deleted.
|
|
2908
|
+
`
|
|
2909
|
+
);
|
|
2910
|
+
}
|
|
2717
2911
|
for (const announcement of dataInfo.announcements) {
|
|
2718
2912
|
console.log(announcement);
|
|
2719
2913
|
}
|
|
@@ -3007,6 +3201,35 @@ function printCliError(err, json) {
|
|
|
3007
3201
|
}
|
|
3008
3202
|
}
|
|
3009
3203
|
|
|
3204
|
+
// src/expiration.ts
|
|
3205
|
+
import path9 from "path";
|
|
3206
|
+
async function expiration(opts) {
|
|
3207
|
+
if (opts.duration !== void 0 === opts.keep) {
|
|
3208
|
+
throw new Error("Provide exactly one of --in <duration> or --keep.");
|
|
3209
|
+
}
|
|
3210
|
+
const seconds = opts.keep ? null : parseArtifactDuration(opts.duration);
|
|
3211
|
+
const target = resolveArtifactTarget(path9.resolve(opts.dir), opts);
|
|
3212
|
+
const result = await createApiClient(
|
|
3213
|
+
target.apiUrl,
|
|
3214
|
+
target.credential
|
|
3215
|
+
).setExpiration(target.artifactId, seconds);
|
|
3216
|
+
if (opts.json) {
|
|
3217
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
if (result.autoDeleteAt) {
|
|
3221
|
+
console.log(`Temporary artifact \u2014 expires at ${result.expiresAt}.`);
|
|
3222
|
+
} else if (result.expiresAt) {
|
|
3223
|
+
console.log(
|
|
3224
|
+
`Temporary deadline cleared. Anonymous retention still expires at ${result.expiresAt}; claim the artifact to keep it.`
|
|
3225
|
+
);
|
|
3226
|
+
} else {
|
|
3227
|
+
console.log(
|
|
3228
|
+
"Artifact kept \u2014 no automatic expiry. Its link, versions, and comments are preserved."
|
|
3229
|
+
);
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3010
3233
|
// src/folders.ts
|
|
3011
3234
|
async function folders(opts) {
|
|
3012
3235
|
if (opts.action !== void 0 && opts.action !== "create") {
|
|
@@ -3103,7 +3326,7 @@ function printFolder(folder) {
|
|
|
3103
3326
|
}
|
|
3104
3327
|
|
|
3105
3328
|
// src/link.ts
|
|
3106
|
-
import
|
|
3329
|
+
import path10 from "path";
|
|
3107
3330
|
import { createInterface as createInterface2 } from "readline";
|
|
3108
3331
|
|
|
3109
3332
|
// src/list.ts
|
|
@@ -3332,7 +3555,7 @@ async function link(opts) {
|
|
|
3332
3555
|
}
|
|
3333
3556
|
chosen = await pickArtifact(artifacts);
|
|
3334
3557
|
}
|
|
3335
|
-
const dir =
|
|
3558
|
+
const dir = path10.resolve(opts.dir);
|
|
3336
3559
|
writeConfig(dir, {
|
|
3337
3560
|
apiUrl: apiUrl2,
|
|
3338
3561
|
artifactId: chosen.id,
|
|
@@ -3500,7 +3723,7 @@ function logout(opts) {
|
|
|
3500
3723
|
}
|
|
3501
3724
|
|
|
3502
3725
|
// src/message.ts
|
|
3503
|
-
import
|
|
3726
|
+
import path11 from "path";
|
|
3504
3727
|
async function readStdin2() {
|
|
3505
3728
|
if (process.stdin.isTTY) {
|
|
3506
3729
|
return "";
|
|
@@ -3524,7 +3747,7 @@ async function editMessage(opts) {
|
|
|
3524
3747
|
'Nothing to save. Pass the new text with --body "\u2026", or pipe it on stdin.'
|
|
3525
3748
|
);
|
|
3526
3749
|
}
|
|
3527
|
-
const dir =
|
|
3750
|
+
const dir = path11.resolve(opts.dir);
|
|
3528
3751
|
const target = resolveArtifactTarget(dir, {
|
|
3529
3752
|
apiKey: opts.apiKey,
|
|
3530
3753
|
artifact: opts.artifact
|
|
@@ -3544,7 +3767,7 @@ async function deleteMessage(opts) {
|
|
|
3544
3767
|
"Pass the message id to delete \u2014 get it from `artifact-cafe comments --json`."
|
|
3545
3768
|
);
|
|
3546
3769
|
}
|
|
3547
|
-
const dir =
|
|
3770
|
+
const dir = path11.resolve(opts.dir);
|
|
3548
3771
|
const target = resolveArtifactTarget(dir, {
|
|
3549
3772
|
apiKey: opts.apiKey,
|
|
3550
3773
|
artifact: opts.artifact
|
|
@@ -3559,12 +3782,12 @@ async function deleteMessage(opts) {
|
|
|
3559
3782
|
}
|
|
3560
3783
|
|
|
3561
3784
|
// src/move.ts
|
|
3562
|
-
import
|
|
3785
|
+
import path12 from "path";
|
|
3563
3786
|
async function move(opts) {
|
|
3564
3787
|
if (Boolean(opts.folder) === Boolean(opts.unfiled)) {
|
|
3565
3788
|
throw new Error("Pass exactly one of --folder <name-or-id> or --unfiled.");
|
|
3566
3789
|
}
|
|
3567
|
-
const dir =
|
|
3790
|
+
const dir = path12.resolve(opts.dir);
|
|
3568
3791
|
const target = resolveArtifactTarget(dir, {
|
|
3569
3792
|
apiKey: opts.apiKey,
|
|
3570
3793
|
artifact: opts.artifact
|
|
@@ -3654,9 +3877,9 @@ async function move(opts) {
|
|
|
3654
3877
|
}
|
|
3655
3878
|
|
|
3656
3879
|
// src/open.ts
|
|
3657
|
-
import
|
|
3880
|
+
import path13 from "path";
|
|
3658
3881
|
async function openArtifact(opts) {
|
|
3659
|
-
const dir =
|
|
3882
|
+
const dir = path13.resolve(opts.dir);
|
|
3660
3883
|
const target = resolveArtifactTarget(dir, {
|
|
3661
3884
|
apiKey: opts.apiKey,
|
|
3662
3885
|
artifact: opts.artifact
|
|
@@ -3674,7 +3897,7 @@ async function openArtifact(opts) {
|
|
|
3674
3897
|
}
|
|
3675
3898
|
|
|
3676
3899
|
// src/protect.ts
|
|
3677
|
-
import
|
|
3900
|
+
import path14 from "path";
|
|
3678
3901
|
async function protect(opts) {
|
|
3679
3902
|
if (opts.off && opts.password !== void 0) {
|
|
3680
3903
|
throw new Error("Pass either --password or --off, not both.");
|
|
@@ -3691,7 +3914,7 @@ async function protect(opts) {
|
|
|
3691
3914
|
throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
|
|
3692
3915
|
}
|
|
3693
3916
|
}
|
|
3694
|
-
const dir =
|
|
3917
|
+
const dir = path14.resolve(opts.dir);
|
|
3695
3918
|
const target = resolveArtifactTarget(dir, {
|
|
3696
3919
|
apiKey: opts.apiKey,
|
|
3697
3920
|
artifact: opts.artifact
|
|
@@ -3716,7 +3939,7 @@ async function protect(opts) {
|
|
|
3716
3939
|
}
|
|
3717
3940
|
|
|
3718
3941
|
// src/reply.ts
|
|
3719
|
-
import
|
|
3942
|
+
import path15 from "path";
|
|
3720
3943
|
async function readStdin3() {
|
|
3721
3944
|
if (process.stdin.isTTY) {
|
|
3722
3945
|
return "";
|
|
@@ -3740,7 +3963,7 @@ async function replyToThread(opts) {
|
|
|
3740
3963
|
'Nothing to post. Pass the reply with --body "\u2026", or pipe it on stdin.'
|
|
3741
3964
|
);
|
|
3742
3965
|
}
|
|
3743
|
-
const dir =
|
|
3966
|
+
const dir = path15.resolve(opts.dir);
|
|
3744
3967
|
const target = resolveArtifactTarget(dir, {
|
|
3745
3968
|
apiKey: opts.apiKey,
|
|
3746
3969
|
artifact: opts.artifact
|
|
@@ -3769,7 +3992,7 @@ async function replyToThread(opts) {
|
|
|
3769
3992
|
}
|
|
3770
3993
|
|
|
3771
3994
|
// src/resolve.ts
|
|
3772
|
-
import
|
|
3995
|
+
import path16 from "path";
|
|
3773
3996
|
async function changeThreadStatus(opts) {
|
|
3774
3997
|
const threadId = opts.thread?.trim();
|
|
3775
3998
|
if (!threadId) {
|
|
@@ -3777,7 +4000,7 @@ async function changeThreadStatus(opts) {
|
|
|
3777
4000
|
`Pass the thread id to ${opts.action} \u2014 get it from \`artifact-cafe comments --json\`.`
|
|
3778
4001
|
);
|
|
3779
4002
|
}
|
|
3780
|
-
const dir =
|
|
4003
|
+
const dir = path16.resolve(opts.dir);
|
|
3781
4004
|
const target = resolveArtifactTarget(dir, {
|
|
3782
4005
|
apiKey: opts.apiKey,
|
|
3783
4006
|
artifact: opts.artifact
|
|
@@ -3814,7 +4037,7 @@ async function changeThreadStatus(opts) {
|
|
|
3814
4037
|
}
|
|
3815
4038
|
|
|
3816
4039
|
// src/review.ts
|
|
3817
|
-
import
|
|
4040
|
+
import path17 from "path";
|
|
3818
4041
|
var OUTCOME = {
|
|
3819
4042
|
off: "Plain artifact \u2014 the review tools are hidden entirely",
|
|
3820
4043
|
open: "Open for review \u2014 anyone who can open it can comment",
|
|
@@ -3828,7 +4051,7 @@ async function review(opts) {
|
|
|
3828
4051
|
);
|
|
3829
4052
|
}
|
|
3830
4053
|
const mode = parsed.data;
|
|
3831
|
-
const dir =
|
|
4054
|
+
const dir = path17.resolve(opts.dir);
|
|
3832
4055
|
const target = resolveArtifactTarget(dir, {
|
|
3833
4056
|
apiKey: opts.apiKey,
|
|
3834
4057
|
artifact: opts.artifact
|
|
@@ -3850,10 +4073,10 @@ async function review(opts) {
|
|
|
3850
4073
|
}
|
|
3851
4074
|
|
|
3852
4075
|
// src/share.ts
|
|
3853
|
-
import
|
|
4076
|
+
import path18 from "path";
|
|
3854
4077
|
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3855
4078
|
async function share(opts) {
|
|
3856
|
-
const dir =
|
|
4079
|
+
const dir = path18.resolve(opts.dir);
|
|
3857
4080
|
const target = resolveArtifactTarget(dir, {
|
|
3858
4081
|
apiKey: opts.apiKey,
|
|
3859
4082
|
artifact: opts.artifact
|
|
@@ -3908,7 +4131,7 @@ General access: ${visibility}`);
|
|
|
3908
4131
|
console.log("");
|
|
3909
4132
|
}
|
|
3910
4133
|
async function unshare(opts) {
|
|
3911
|
-
const dir =
|
|
4134
|
+
const dir = path18.resolve(opts.dir);
|
|
3912
4135
|
const target = resolveArtifactTarget(dir, {
|
|
3913
4136
|
apiKey: opts.apiKey,
|
|
3914
4137
|
artifact: opts.artifact
|
|
@@ -3931,7 +4154,7 @@ async function unshare(opts) {
|
|
|
3931
4154
|
}
|
|
3932
4155
|
|
|
3933
4156
|
// src/status.ts
|
|
3934
|
-
import
|
|
4157
|
+
import path19 from "path";
|
|
3935
4158
|
async function status(opts) {
|
|
3936
4159
|
if (!AGENT_STATES.includes(opts.state)) {
|
|
3937
4160
|
throw new Error(
|
|
@@ -3939,7 +4162,7 @@ async function status(opts) {
|
|
|
3939
4162
|
);
|
|
3940
4163
|
}
|
|
3941
4164
|
const state = opts.state;
|
|
3942
|
-
const dir =
|
|
4165
|
+
const dir = path19.resolve(opts.dir);
|
|
3943
4166
|
const target = resolveArtifactTarget(dir, {
|
|
3944
4167
|
apiKey: opts.apiKey,
|
|
3945
4168
|
artifact: opts.artifact
|
|
@@ -3968,26 +4191,26 @@ async function status(opts) {
|
|
|
3968
4191
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
3969
4192
|
import { createRequire } from "module";
|
|
3970
4193
|
import { homedir as homedir2 } from "os";
|
|
3971
|
-
import
|
|
3972
|
-
import { z as
|
|
4194
|
+
import path20 from "path";
|
|
4195
|
+
import { z as z25 } from "zod";
|
|
3973
4196
|
var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
|
|
3974
4197
|
var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
|
|
3975
4198
|
var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
|
|
3976
4199
|
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3977
4200
|
var FETCH_TIMEOUT_MS = 2e3;
|
|
3978
4201
|
var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
|
|
3979
|
-
var cacheSchema =
|
|
3980
|
-
checkedAt:
|
|
3981
|
-
cliLatest:
|
|
3982
|
-
skillLatest:
|
|
3983
|
-
version:
|
|
4202
|
+
var cacheSchema = z25.object({
|
|
4203
|
+
checkedAt: z25.number(),
|
|
4204
|
+
cliLatest: z25.string().nullable(),
|
|
4205
|
+
skillLatest: z25.string().nullable(),
|
|
4206
|
+
version: z25.literal(1)
|
|
3984
4207
|
});
|
|
3985
4208
|
function cliVersion() {
|
|
3986
4209
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
3987
4210
|
return pkg.version;
|
|
3988
4211
|
}
|
|
3989
4212
|
function cachePath() {
|
|
3990
|
-
return
|
|
4213
|
+
return path20.join(homedir2(), ".artifactcafe", "update-check.json");
|
|
3991
4214
|
}
|
|
3992
4215
|
function readCache() {
|
|
3993
4216
|
try {
|
|
@@ -3998,7 +4221,7 @@ function readCache() {
|
|
|
3998
4221
|
}
|
|
3999
4222
|
function writeCache(cache) {
|
|
4000
4223
|
try {
|
|
4001
|
-
mkdirSync3(
|
|
4224
|
+
mkdirSync3(path20.dirname(cachePath()), { mode: 448, recursive: true });
|
|
4002
4225
|
writeFileSync3(cachePath(), `${JSON.stringify(cache, null, 2)}
|
|
4003
4226
|
`);
|
|
4004
4227
|
} catch {
|
|
@@ -4035,7 +4258,7 @@ async function fetchText(url) {
|
|
|
4035
4258
|
}
|
|
4036
4259
|
async function fetchLatestVersions(previous) {
|
|
4037
4260
|
const [cli, skill] = await Promise.all([
|
|
4038
|
-
fetchJson(REGISTRY_LATEST_URL).then((body) =>
|
|
4261
|
+
fetchJson(REGISTRY_LATEST_URL).then((body) => z25.object({ version: z25.string() }).parse(body).version).catch(() => null),
|
|
4039
4262
|
fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
|
|
4040
4263
|
]);
|
|
4041
4264
|
return {
|
|
@@ -4051,7 +4274,7 @@ function installedSkillVersions() {
|
|
|
4051
4274
|
const versions = [];
|
|
4052
4275
|
for (const root of roots) {
|
|
4053
4276
|
for (const dir of dirs) {
|
|
4054
|
-
const file =
|
|
4277
|
+
const file = path20.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
|
|
4055
4278
|
if (!existsSync3(file)) {
|
|
4056
4279
|
continue;
|
|
4057
4280
|
}
|
|
@@ -4179,10 +4402,14 @@ var publish2 = defineCommand({
|
|
|
4179
4402
|
},
|
|
4180
4403
|
dir: {
|
|
4181
4404
|
default: ".",
|
|
4182
|
-
description: "
|
|
4405
|
+
description: "File or directory to publish",
|
|
4183
4406
|
type: "positional"
|
|
4184
4407
|
},
|
|
4185
4408
|
entry: { description: "Entry file (default: auto-detect)", type: "string" },
|
|
4409
|
+
"expires-in": {
|
|
4410
|
+
description: "Temporary lifetime: 30m, 1h, 7d (ordinary republishes preserve it)",
|
|
4411
|
+
type: "string"
|
|
4412
|
+
},
|
|
4186
4413
|
folder: {
|
|
4187
4414
|
description: "Publish into this workspace folder name (or id)",
|
|
4188
4415
|
type: "string"
|
|
@@ -4207,7 +4434,10 @@ var publish2 = defineCommand({
|
|
|
4207
4434
|
type: "string"
|
|
4208
4435
|
}
|
|
4209
4436
|
},
|
|
4210
|
-
meta: {
|
|
4437
|
+
meta: {
|
|
4438
|
+
description: "Publish a file or folder as an artifact",
|
|
4439
|
+
name: "publish"
|
|
4440
|
+
},
|
|
4211
4441
|
async run({ args }) {
|
|
4212
4442
|
try {
|
|
4213
4443
|
await publish({
|
|
@@ -4216,6 +4446,7 @@ var publish2 = defineCommand({
|
|
|
4216
4446
|
description: readStringArg(process.argv.slice(2), "--description"),
|
|
4217
4447
|
dir: args.dir,
|
|
4218
4448
|
entry: args.entry,
|
|
4449
|
+
expiresIn: args["expires-in"],
|
|
4219
4450
|
folder: args.folder,
|
|
4220
4451
|
json: Boolean(args.json),
|
|
4221
4452
|
notes: readStringArg(process.argv.slice(2), "--notes"),
|
|
@@ -4471,6 +4702,48 @@ var link2 = defineCommand({
|
|
|
4471
4702
|
}
|
|
4472
4703
|
}
|
|
4473
4704
|
});
|
|
4705
|
+
var expiration2 = defineCommand({
|
|
4706
|
+
args: {
|
|
4707
|
+
"api-key": {
|
|
4708
|
+
description: "Account or agent token (default: env or login)",
|
|
4709
|
+
type: "string"
|
|
4710
|
+
},
|
|
4711
|
+
artifact: {
|
|
4712
|
+
description: "Artifact ID instead of the linked folder",
|
|
4713
|
+
type: "string"
|
|
4714
|
+
},
|
|
4715
|
+
dir: {
|
|
4716
|
+
default: ".",
|
|
4717
|
+
description: "Published directory",
|
|
4718
|
+
type: "positional"
|
|
4719
|
+
},
|
|
4720
|
+
in: { description: "Reset lifetime from now: 30m, 1h, 7d", type: "string" },
|
|
4721
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
4722
|
+
keep: {
|
|
4723
|
+
description: "Clear the explicit temporary deadline",
|
|
4724
|
+
type: "boolean"
|
|
4725
|
+
}
|
|
4726
|
+
},
|
|
4727
|
+
meta: {
|
|
4728
|
+
description: "Extend a temporary artifact or keep it",
|
|
4729
|
+
name: "expiration"
|
|
4730
|
+
},
|
|
4731
|
+
async run({ args }) {
|
|
4732
|
+
try {
|
|
4733
|
+
await expiration({
|
|
4734
|
+
apiKey: args["api-key"],
|
|
4735
|
+
artifact: args.artifact,
|
|
4736
|
+
dir: args.dir,
|
|
4737
|
+
duration: args.in,
|
|
4738
|
+
json: Boolean(args.json),
|
|
4739
|
+
keep: Boolean(args.keep)
|
|
4740
|
+
});
|
|
4741
|
+
} catch (err) {
|
|
4742
|
+
printCliError(err, Boolean(args.json));
|
|
4743
|
+
process.exitCode = 1;
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
});
|
|
4474
4747
|
var comments2 = defineCommand({
|
|
4475
4748
|
args: {
|
|
4476
4749
|
"api-key": {
|
|
@@ -5139,6 +5412,47 @@ var dataLsCommand = defineCommand({
|
|
|
5139
5412
|
}
|
|
5140
5413
|
}
|
|
5141
5414
|
});
|
|
5415
|
+
var dataInsertCommand = defineCommand({
|
|
5416
|
+
args: {
|
|
5417
|
+
collection: {
|
|
5418
|
+
description: "Collection name",
|
|
5419
|
+
required: true,
|
|
5420
|
+
type: "positional"
|
|
5421
|
+
},
|
|
5422
|
+
dir: {
|
|
5423
|
+
default: ".",
|
|
5424
|
+
description: "Published directory",
|
|
5425
|
+
type: "positional"
|
|
5426
|
+
},
|
|
5427
|
+
file: {
|
|
5428
|
+
description: "Read the record(s) from a JSON file instead of --record",
|
|
5429
|
+
type: "string"
|
|
5430
|
+
},
|
|
5431
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
5432
|
+
record: {
|
|
5433
|
+
description: "A JSON object, or an array of objects for a batch",
|
|
5434
|
+
type: "string"
|
|
5435
|
+
}
|
|
5436
|
+
},
|
|
5437
|
+
meta: {
|
|
5438
|
+
description: "Insert one or many records into a collection",
|
|
5439
|
+
name: "insert"
|
|
5440
|
+
},
|
|
5441
|
+
async run({ args }) {
|
|
5442
|
+
try {
|
|
5443
|
+
await dataInsert({
|
|
5444
|
+
collection: args.collection,
|
|
5445
|
+
dir: args.dir,
|
|
5446
|
+
file: args.file,
|
|
5447
|
+
json: Boolean(args.json),
|
|
5448
|
+
record: args.record
|
|
5449
|
+
});
|
|
5450
|
+
} catch (err) {
|
|
5451
|
+
printCliError(err, Boolean(args.json));
|
|
5452
|
+
process.exit(1);
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
});
|
|
5142
5456
|
var dataExportCommand = defineCommand({
|
|
5143
5457
|
args: {
|
|
5144
5458
|
dir: {
|
|
@@ -5201,12 +5515,13 @@ var dataClearCommand = defineCommand({
|
|
|
5201
5515
|
});
|
|
5202
5516
|
var data = defineCommand({
|
|
5203
5517
|
meta: {
|
|
5204
|
-
description: "Read or clear the artifact's shared records",
|
|
5518
|
+
description: "Read, insert, or clear the artifact's shared records",
|
|
5205
5519
|
name: "data"
|
|
5206
5520
|
},
|
|
5207
5521
|
subCommands: {
|
|
5208
5522
|
clear: dataClearCommand,
|
|
5209
5523
|
export: dataExportCommand,
|
|
5524
|
+
insert: dataInsertCommand,
|
|
5210
5525
|
ls: dataLsCommand
|
|
5211
5526
|
}
|
|
5212
5527
|
});
|
|
@@ -5235,6 +5550,7 @@ var main = defineCommand({
|
|
|
5235
5550
|
data,
|
|
5236
5551
|
delete: del,
|
|
5237
5552
|
edit,
|
|
5553
|
+
expiration: expiration2,
|
|
5238
5554
|
folders: folders2,
|
|
5239
5555
|
link: link2,
|
|
5240
5556
|
list: list2,
|