artifact-cafe 0.0.10 → 0.0.12
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 +992 -407
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -100,10 +100,22 @@ var workspaceAvatarResponseSchema = z2.object({
|
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
// ../shared/src/comments.ts
|
|
103
|
-
import { z as
|
|
103
|
+
import { z as z5 } from "zod";
|
|
104
104
|
|
|
105
105
|
// ../shared/src/publish.ts
|
|
106
|
+
import { z as z4 } from "zod";
|
|
107
|
+
|
|
108
|
+
// ../shared/src/review-mode.ts
|
|
106
109
|
import { z as z3 } from "zod";
|
|
110
|
+
var reviewModeSchema = z3.enum(["open", "readonly", "off"]);
|
|
111
|
+
var setReviewModeSchema = z3.object({
|
|
112
|
+
mode: reviewModeSchema
|
|
113
|
+
});
|
|
114
|
+
var reviewModeResponseSchema = z3.object({
|
|
115
|
+
mode: reviewModeSchema
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ../shared/src/publish.ts
|
|
107
119
|
function isSafeArtifactPath(p) {
|
|
108
120
|
if (p.length === 0 || p.length > 1024) {
|
|
109
121
|
return false;
|
|
@@ -116,140 +128,142 @@ function isSafeArtifactPath(p) {
|
|
|
116
128
|
}
|
|
117
129
|
return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
|
|
118
130
|
}
|
|
119
|
-
var manifestFileSchema =
|
|
120
|
-
hash:
|
|
121
|
-
mimeType:
|
|
122
|
-
path:
|
|
131
|
+
var manifestFileSchema = z4.object({
|
|
132
|
+
hash: z4.string().regex(/^sha256_[a-f0-9]{64}$/),
|
|
133
|
+
mimeType: z4.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
|
|
134
|
+
path: z4.string().min(1).max(1024).refine(isSafeArtifactPath, {
|
|
123
135
|
message: "path must be a safe relative path"
|
|
124
136
|
}),
|
|
125
|
-
size:
|
|
137
|
+
size: z4.number().int().positive()
|
|
126
138
|
});
|
|
127
|
-
var publishSourceSchema =
|
|
128
|
-
agent:
|
|
129
|
-
runId:
|
|
130
|
-
type:
|
|
139
|
+
var publishSourceSchema = z4.object({
|
|
140
|
+
agent: z4.string().optional(),
|
|
141
|
+
runId: z4.string().optional(),
|
|
142
|
+
type: z4.literal("cli")
|
|
131
143
|
});
|
|
132
|
-
var createPublishSessionSchema =
|
|
133
|
-
artifactId:
|
|
134
|
-
entryPath:
|
|
135
|
-
files:
|
|
144
|
+
var createPublishSessionSchema = z4.object({
|
|
145
|
+
artifactId: z4.string().nullable(),
|
|
146
|
+
entryPath: z4.string().optional(),
|
|
147
|
+
files: z4.array(manifestFileSchema).min(1).max(1e3),
|
|
136
148
|
// SPEC-2 §6.4 — workspace placement is available only to authenticated
|
|
137
149
|
// members; the API enforces membership and folder ownership. folderId
|
|
138
150
|
// without workspaceId resolves against the account's personal workspace.
|
|
139
|
-
folderId:
|
|
151
|
+
folderId: z4.string().min(1).optional(),
|
|
140
152
|
// Bullet-point changelog for this version, authored by the publisher.
|
|
141
153
|
// Optional on every publish (incl. v1); stored immutably on the version.
|
|
142
|
-
notes:
|
|
154
|
+
notes: z4.string().max(4e3).optional(),
|
|
143
155
|
source: publishSourceSchema,
|
|
144
|
-
title:
|
|
145
|
-
workspaceId:
|
|
146
|
-
});
|
|
147
|
-
var publishSessionResponseSchema =
|
|
148
|
-
artifactId:
|
|
149
|
-
expiresAt:
|
|
150
|
-
filesToUpload:
|
|
151
|
-
|
|
152
|
-
method:
|
|
153
|
-
path:
|
|
154
|
-
uploadUrl:
|
|
156
|
+
title: z4.string().max(200).optional(),
|
|
157
|
+
workspaceId: z4.string().min(1).optional()
|
|
158
|
+
});
|
|
159
|
+
var publishSessionResponseSchema = z4.object({
|
|
160
|
+
artifactId: z4.string(),
|
|
161
|
+
expiresAt: z4.string(),
|
|
162
|
+
filesToUpload: z4.array(
|
|
163
|
+
z4.object({
|
|
164
|
+
method: z4.literal("PUT"),
|
|
165
|
+
path: z4.string(),
|
|
166
|
+
uploadUrl: z4.string().url()
|
|
155
167
|
})
|
|
156
168
|
),
|
|
157
|
-
publishSessionId:
|
|
158
|
-
skippedFiles:
|
|
159
|
-
versionId:
|
|
169
|
+
publishSessionId: z4.string(),
|
|
170
|
+
skippedFiles: z4.array(z4.string()),
|
|
171
|
+
versionId: z4.string()
|
|
160
172
|
});
|
|
161
|
-
var finalizeResponseSchema =
|
|
162
|
-
artifactId:
|
|
173
|
+
var finalizeResponseSchema = z4.object({
|
|
174
|
+
artifactId: z4.string(),
|
|
163
175
|
// API-key publishes create the artifact already claimed (SPEC §5.4) —
|
|
164
176
|
// the CLI uses this to tell "claim URL pending" from "already yours".
|
|
165
|
-
claimed:
|
|
177
|
+
claimed: z4.boolean(),
|
|
166
178
|
// Returned only on first publish of a new artifact (one-time).
|
|
167
|
-
claimUrl:
|
|
168
|
-
publishToken:
|
|
169
|
-
url:
|
|
170
|
-
versionId:
|
|
171
|
-
versionNumber:
|
|
172
|
-
});
|
|
173
|
-
var artifactResponseSchema =
|
|
174
|
-
claimed:
|
|
175
|
-
currentVersion:
|
|
176
|
-
entryUrl:
|
|
177
|
-
id:
|
|
178
|
-
versionNumber:
|
|
179
|
+
claimUrl: z4.string().url().optional(),
|
|
180
|
+
publishToken: z4.string().optional(),
|
|
181
|
+
url: z4.string().url(),
|
|
182
|
+
versionId: z4.string(),
|
|
183
|
+
versionNumber: z4.number().int().positive()
|
|
184
|
+
});
|
|
185
|
+
var artifactResponseSchema = z4.object({
|
|
186
|
+
claimed: z4.boolean(),
|
|
187
|
+
currentVersion: z4.object({
|
|
188
|
+
entryUrl: z4.string().url(),
|
|
189
|
+
id: z4.string(),
|
|
190
|
+
versionNumber: z4.number().int().positive()
|
|
179
191
|
}).nullable(),
|
|
180
192
|
// SPEC-2 §6.4 — placement + visibility surface on the artifact.
|
|
181
|
-
folderId:
|
|
182
|
-
id:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
193
|
+
folderId: z4.string().nullable(),
|
|
194
|
+
id: z4.string(),
|
|
195
|
+
// SPEC-2 §5.8 — what a viewer may do once in (open / readonly / off).
|
|
196
|
+
reviewMode: reviewModeSchema,
|
|
197
|
+
slug: z4.string(),
|
|
198
|
+
title: z4.string(),
|
|
199
|
+
visibility: z4.enum(["public", "unlisted", "workspace", "private"]),
|
|
200
|
+
workspaceId: z4.string().nullable()
|
|
187
201
|
});
|
|
188
202
|
|
|
189
203
|
// ../shared/src/comments.ts
|
|
190
|
-
var anchorPageSchema =
|
|
204
|
+
var anchorPageSchema = z5.string().max(1024).refine(isSafeArtifactPath, {
|
|
191
205
|
message: "page must be a safe relative path"
|
|
192
206
|
}).optional();
|
|
193
|
-
var elementSnapshotSchema =
|
|
194
|
-
classes:
|
|
195
|
-
id:
|
|
196
|
-
tag:
|
|
197
|
-
text:
|
|
207
|
+
var elementSnapshotSchema = z5.object({
|
|
208
|
+
classes: z5.string().max(256).optional(),
|
|
209
|
+
id: z5.string().max(256).optional(),
|
|
210
|
+
tag: z5.string().min(1).max(128),
|
|
211
|
+
text: z5.string().max(200).optional()
|
|
198
212
|
});
|
|
199
|
-
var pointAnchorSchema =
|
|
213
|
+
var pointAnchorSchema = z5.object({
|
|
200
214
|
element: elementSnapshotSchema.optional(),
|
|
201
215
|
// elementPath is the primary anchor; x/y are offsets within the
|
|
202
216
|
// element's box, normalized 0..1 (SPEC.md §8.1).
|
|
203
|
-
elementPath:
|
|
217
|
+
elementPath: z5.string().max(2048),
|
|
204
218
|
page: anchorPageSchema,
|
|
205
|
-
type:
|
|
206
|
-
viewportHeight:
|
|
207
|
-
viewportWidth:
|
|
208
|
-
x:
|
|
209
|
-
y:
|
|
210
|
-
});
|
|
211
|
-
var textAnchorSchema =
|
|
212
|
-
elementPath:
|
|
213
|
-
endOffset:
|
|
219
|
+
type: z5.literal("point"),
|
|
220
|
+
viewportHeight: z5.number().int().positive().max(1e5),
|
|
221
|
+
viewportWidth: z5.number().int().positive().max(1e5),
|
|
222
|
+
x: z5.number().min(0).max(1),
|
|
223
|
+
y: z5.number().min(0).max(1)
|
|
224
|
+
});
|
|
225
|
+
var textAnchorSchema = z5.object({
|
|
226
|
+
elementPath: z5.string().max(2048),
|
|
227
|
+
endOffset: z5.number().int().nonnegative().max(1e8),
|
|
214
228
|
page: anchorPageSchema,
|
|
215
|
-
prefix:
|
|
216
|
-
quote:
|
|
217
|
-
startOffset:
|
|
218
|
-
suffix:
|
|
219
|
-
type:
|
|
229
|
+
prefix: z5.string().max(200).optional(),
|
|
230
|
+
quote: z5.string().min(1).max(1e3),
|
|
231
|
+
startOffset: z5.number().int().nonnegative().max(1e8),
|
|
232
|
+
suffix: z5.string().max(200).optional(),
|
|
233
|
+
type: z5.literal("text")
|
|
220
234
|
});
|
|
221
|
-
var pageAnchorSchema =
|
|
235
|
+
var pageAnchorSchema = z5.object({
|
|
222
236
|
page: anchorPageSchema,
|
|
223
|
-
type:
|
|
237
|
+
type: z5.literal("page")
|
|
224
238
|
});
|
|
225
|
-
var anchorSchema =
|
|
239
|
+
var anchorSchema = z5.discriminatedUnion("type", [
|
|
226
240
|
pointAnchorSchema,
|
|
227
241
|
textAnchorSchema,
|
|
228
242
|
pageAnchorSchema
|
|
229
243
|
]);
|
|
230
|
-
var displayNameSchema =
|
|
231
|
-
var guestSchema =
|
|
244
|
+
var displayNameSchema = z5.string().trim().min(1).max(64);
|
|
245
|
+
var guestSchema = z5.object({
|
|
232
246
|
displayName: displayNameSchema,
|
|
233
|
-
guestId:
|
|
247
|
+
guestId: z5.string().min(16)
|
|
234
248
|
});
|
|
235
|
-
var viaLabelSchema =
|
|
236
|
-
var createCommentThreadSchema =
|
|
249
|
+
var viaLabelSchema = z5.string().trim().min(1).max(64);
|
|
250
|
+
var createCommentThreadSchema = z5.object({
|
|
237
251
|
anchor: anchorSchema,
|
|
238
|
-
body:
|
|
252
|
+
body: z5.string().min(1).max(1e4),
|
|
239
253
|
guest: guestSchema.optional(),
|
|
240
|
-
versionId:
|
|
254
|
+
versionId: z5.string(),
|
|
241
255
|
via: viaLabelSchema.optional()
|
|
242
256
|
});
|
|
243
|
-
var createCommentMessageSchema =
|
|
244
|
-
body:
|
|
257
|
+
var createCommentMessageSchema = z5.object({
|
|
258
|
+
body: z5.string().min(1).max(1e4),
|
|
245
259
|
guest: guestSchema.optional(),
|
|
246
260
|
via: viaLabelSchema.optional()
|
|
247
261
|
});
|
|
248
|
-
var editCommentMessageSchema =
|
|
249
|
-
body:
|
|
262
|
+
var editCommentMessageSchema = z5.object({
|
|
263
|
+
body: z5.string().min(1).max(1e4),
|
|
250
264
|
guest: guestSchema.optional()
|
|
251
265
|
});
|
|
252
|
-
var threadActionSchema =
|
|
266
|
+
var threadActionSchema = z5.object({
|
|
253
267
|
guest: guestSchema.optional()
|
|
254
268
|
});
|
|
255
269
|
var SINGLE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
|
|
@@ -257,117 +271,117 @@ var REDUNDANT_VS16 = new RegExp("(\\p{Emoji_Presentation})\\uFE0F", "gu");
|
|
|
257
271
|
function canonicalizeEmoji(s) {
|
|
258
272
|
return s.replace(REDUNDANT_VS16, "$1");
|
|
259
273
|
}
|
|
260
|
-
var reactionEmojiSchema =
|
|
261
|
-
var toggleReactionSchema =
|
|
274
|
+
var reactionEmojiSchema = z5.string().max(64).transform(canonicalizeEmoji).refine((s) => SINGLE_EMOJI.test(s), { message: "Must be a single emoji." }).describe("A single emoji, e.g. \u{1F44D}");
|
|
275
|
+
var toggleReactionSchema = z5.object({
|
|
262
276
|
emoji: reactionEmojiSchema,
|
|
263
277
|
guest: guestSchema.optional()
|
|
264
278
|
});
|
|
265
|
-
var commentAuthorSchema =
|
|
266
|
-
avatarUrl:
|
|
267
|
-
displayName:
|
|
268
|
-
guestId:
|
|
269
|
-
isViewer:
|
|
270
|
-
kind:
|
|
279
|
+
var commentAuthorSchema = z5.object({
|
|
280
|
+
avatarUrl: z5.string().nullable(),
|
|
281
|
+
displayName: z5.string(),
|
|
282
|
+
guestId: z5.string().nullable(),
|
|
283
|
+
isViewer: z5.boolean(),
|
|
284
|
+
kind: z5.enum(["member", "guest"]),
|
|
271
285
|
// Optional "via" attribution (§6.4). Null for plain comments; a bounded
|
|
272
286
|
// label the shell appends after the display name.
|
|
273
|
-
via:
|
|
287
|
+
via: z5.string().nullable()
|
|
274
288
|
});
|
|
275
|
-
var messageReactionSchema =
|
|
276
|
-
emoji:
|
|
277
|
-
reactors:
|
|
289
|
+
var messageReactionSchema = z5.object({
|
|
290
|
+
emoji: z5.string(),
|
|
291
|
+
reactors: z5.array(commentAuthorSchema)
|
|
278
292
|
});
|
|
279
|
-
var commentMessageSchema =
|
|
293
|
+
var commentMessageSchema = z5.object({
|
|
280
294
|
author: commentAuthorSchema,
|
|
281
295
|
// Empty string for a deleted message — the body is stripped server-side
|
|
282
296
|
// (§6.7.1 tombstone), never sent to the client and hidden there.
|
|
283
|
-
body:
|
|
284
|
-
createdAt:
|
|
297
|
+
body: z5.string(),
|
|
298
|
+
createdAt: z5.string(),
|
|
285
299
|
// Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
|
|
286
300
|
// the shell renders "Comment deleted" instead of the (absent) body.
|
|
287
|
-
deleted:
|
|
301
|
+
deleted: z5.boolean(),
|
|
288
302
|
// Last edit time (§6.7.1), or null if never edited — drives the "edited"
|
|
289
303
|
// badge. No edit history is kept.
|
|
290
|
-
editedAt:
|
|
291
|
-
id:
|
|
304
|
+
editedAt: z5.string().nullable(),
|
|
305
|
+
id: z5.string(),
|
|
292
306
|
// Defaulted, not required: a new CLI parsing an older server's
|
|
293
307
|
// response must not fail on the missing key.
|
|
294
|
-
reactions:
|
|
308
|
+
reactions: z5.array(messageReactionSchema).default([])
|
|
295
309
|
});
|
|
296
|
-
var threadActorSchema =
|
|
297
|
-
displayName:
|
|
298
|
-
kind:
|
|
310
|
+
var threadActorSchema = z5.object({
|
|
311
|
+
displayName: z5.string(),
|
|
312
|
+
kind: z5.enum(["member", "guest", "agent"])
|
|
299
313
|
});
|
|
300
|
-
var commentThreadSchema =
|
|
314
|
+
var commentThreadSchema = z5.object({
|
|
301
315
|
// §6.6.1 — set while open to signal "seen, working on it"; cleared on
|
|
302
316
|
// reopen, kept (but superseded in display) once resolved. Defaulted so
|
|
303
317
|
// clients parsing an older server's response don't fail.
|
|
304
|
-
acknowledgedAt:
|
|
318
|
+
acknowledgedAt: z5.string().nullable().default(null),
|
|
305
319
|
acknowledgedBy: threadActorSchema.nullable().default(null),
|
|
306
320
|
anchor: anchorSchema,
|
|
307
|
-
artifactId:
|
|
308
|
-
createdAt:
|
|
321
|
+
artifactId: z5.string(),
|
|
322
|
+
createdAt: z5.string(),
|
|
309
323
|
createdBy: commentAuthorSchema,
|
|
310
|
-
id:
|
|
311
|
-
messages:
|
|
324
|
+
id: z5.string(),
|
|
325
|
+
messages: z5.array(commentMessageSchema),
|
|
312
326
|
// Null on threads resolved before actor recording existed, and always
|
|
313
327
|
// null while open.
|
|
314
328
|
resolvedBy: threadActorSchema.nullable().default(null),
|
|
315
|
-
status:
|
|
316
|
-
versionId:
|
|
329
|
+
status: z5.enum(["open", "resolved"]),
|
|
330
|
+
versionId: z5.string()
|
|
317
331
|
});
|
|
318
|
-
var commentThreadListSchema =
|
|
319
|
-
threads:
|
|
332
|
+
var commentThreadListSchema = z5.object({
|
|
333
|
+
threads: z5.array(commentThreadSchema)
|
|
320
334
|
});
|
|
321
|
-
var commentExportQuerySchema =
|
|
322
|
-
status:
|
|
323
|
-
version:
|
|
335
|
+
var commentExportQuerySchema = z5.object({
|
|
336
|
+
status: z5.enum(["open", "resolved", "all"]).default("open"),
|
|
337
|
+
version: z5.enum(["current", "all"]).default("current")
|
|
324
338
|
});
|
|
325
|
-
var exportReactionSchema =
|
|
326
|
-
count:
|
|
327
|
-
emoji:
|
|
339
|
+
var exportReactionSchema = z5.object({
|
|
340
|
+
count: z5.number().int().positive(),
|
|
341
|
+
emoji: z5.string()
|
|
328
342
|
});
|
|
329
|
-
var commentExportReplySchema =
|
|
330
|
-
author:
|
|
331
|
-
authorKind:
|
|
332
|
-
body:
|
|
333
|
-
createdAt:
|
|
343
|
+
var commentExportReplySchema = z5.object({
|
|
344
|
+
author: z5.string(),
|
|
345
|
+
authorKind: z5.enum(["member", "guest"]),
|
|
346
|
+
body: z5.string(),
|
|
347
|
+
createdAt: z5.string(),
|
|
334
348
|
// Soft-delete tombstone (§6.7.1): body is "" when true.
|
|
335
|
-
deleted:
|
|
336
|
-
editedAt:
|
|
349
|
+
deleted: z5.boolean(),
|
|
350
|
+
editedAt: z5.string().nullable(),
|
|
337
351
|
// The message id — what `edit`/`delete` target (§5.1).
|
|
338
|
-
id:
|
|
339
|
-
reactions:
|
|
352
|
+
id: z5.string(),
|
|
353
|
+
reactions: z5.array(exportReactionSchema).optional()
|
|
340
354
|
});
|
|
341
|
-
var commentExportThreadSchema =
|
|
355
|
+
var commentExportThreadSchema = z5.object({
|
|
342
356
|
// §6.6.1 — lets a polling agent skip threads it already acknowledged
|
|
343
357
|
// (acknowledged threads stay in status=open exports on purpose).
|
|
344
|
-
acknowledgedAt:
|
|
358
|
+
acknowledgedAt: z5.string().nullable().default(null),
|
|
345
359
|
anchor: anchorSchema,
|
|
346
|
-
author:
|
|
347
|
-
authorKind:
|
|
348
|
-
body:
|
|
349
|
-
createdAt:
|
|
350
|
-
deleted:
|
|
351
|
-
editedAt:
|
|
352
|
-
id:
|
|
360
|
+
author: z5.string(),
|
|
361
|
+
authorKind: z5.enum(["member", "guest"]),
|
|
362
|
+
body: z5.string(),
|
|
363
|
+
createdAt: z5.string(),
|
|
364
|
+
deleted: z5.boolean(),
|
|
365
|
+
editedAt: z5.string().nullable(),
|
|
366
|
+
id: z5.string(),
|
|
353
367
|
// The opening message's id (distinct from the thread `id`) — `edit`/
|
|
354
368
|
// `delete` operate on messages, so the CLI needs it to target the first
|
|
355
369
|
// comment, not just the replies.
|
|
356
|
-
messageId:
|
|
357
|
-
reactions:
|
|
358
|
-
replies:
|
|
370
|
+
messageId: z5.string(),
|
|
371
|
+
reactions: z5.array(exportReactionSchema).optional(),
|
|
372
|
+
replies: z5.array(commentExportReplySchema),
|
|
359
373
|
resolvedBy: threadActorSchema.nullable().default(null),
|
|
360
|
-
status:
|
|
361
|
-
versionNumber:
|
|
374
|
+
status: z5.enum(["open", "resolved"]),
|
|
375
|
+
versionNumber: z5.number().int().positive()
|
|
362
376
|
});
|
|
363
|
-
var commentExportSchema =
|
|
364
|
-
artifactId:
|
|
365
|
-
comments:
|
|
377
|
+
var commentExportSchema = z5.object({
|
|
378
|
+
artifactId: z5.string(),
|
|
379
|
+
comments: z5.array(commentExportThreadSchema),
|
|
366
380
|
// The artifact's current version (null before the first finalize).
|
|
367
|
-
versionNumber:
|
|
381
|
+
versionNumber: z5.number().int().positive().nullable()
|
|
368
382
|
});
|
|
369
|
-
var commentReadResponseSchema =
|
|
370
|
-
lastSeenAt:
|
|
383
|
+
var commentReadResponseSchema = z5.object({
|
|
384
|
+
lastSeenAt: z5.string()
|
|
371
385
|
});
|
|
372
386
|
function elementLabel(el) {
|
|
373
387
|
if (el.id) {
|
|
@@ -378,166 +392,228 @@ function elementLabel(el) {
|
|
|
378
392
|
}
|
|
379
393
|
|
|
380
394
|
// ../shared/src/errors.ts
|
|
381
|
-
import { z as
|
|
382
|
-
var apiErrorSchema =
|
|
383
|
-
code:
|
|
384
|
-
docsUrl:
|
|
385
|
-
message:
|
|
386
|
-
retryAfter:
|
|
395
|
+
import { z as z6 } from "zod";
|
|
396
|
+
var apiErrorSchema = z6.object({
|
|
397
|
+
code: z6.string(),
|
|
398
|
+
docsUrl: z6.string().url(),
|
|
399
|
+
message: z6.string(),
|
|
400
|
+
retryAfter: z6.number().int().positive().optional()
|
|
387
401
|
});
|
|
388
402
|
|
|
389
403
|
// ../shared/src/feedback.ts
|
|
390
|
-
import { z as
|
|
391
|
-
var feedbackRequestSchema =
|
|
392
|
-
email:
|
|
393
|
-
message:
|
|
404
|
+
import { z as z7 } from "zod";
|
|
405
|
+
var feedbackRequestSchema = z7.object({
|
|
406
|
+
email: z7.string().trim().toLowerCase().email().optional().or(z7.literal("")),
|
|
407
|
+
message: z7.string().trim().min(1).max(4e3)
|
|
394
408
|
});
|
|
395
|
-
var feedbackResponseSchema =
|
|
396
|
-
sent:
|
|
409
|
+
var feedbackResponseSchema = z7.object({
|
|
410
|
+
sent: z7.literal(true)
|
|
397
411
|
});
|
|
398
412
|
|
|
399
|
-
// ../shared/src/
|
|
400
|
-
import { z as
|
|
401
|
-
var
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
413
|
+
// ../shared/src/grants.ts
|
|
414
|
+
import { z as z8 } from "zod";
|
|
415
|
+
var grantRoleSchema = z8.enum(["viewer"]);
|
|
416
|
+
var createGrantSchema = z8.object({
|
|
417
|
+
email: z8.string().trim().toLowerCase().email().optional(),
|
|
418
|
+
role: grantRoleSchema.default("viewer"),
|
|
419
|
+
userId: z8.string().min(1).optional()
|
|
420
|
+
}).refine((body) => Boolean(body.email) !== Boolean(body.userId), {
|
|
421
|
+
message: "provide exactly one of email or userId"
|
|
422
|
+
});
|
|
423
|
+
var grantSchema = z8.object({
|
|
424
|
+
createdAt: z8.string(),
|
|
425
|
+
email: z8.string().email().nullable(),
|
|
426
|
+
expiresAt: z8.string().nullable(),
|
|
427
|
+
id: z8.string(),
|
|
428
|
+
// The subject's avatar once they have an account; null otherwise.
|
|
429
|
+
image: z8.string().nullable(),
|
|
430
|
+
// Present on the invited account after redemption / when granting an
|
|
431
|
+
// existing user; null for a still-pending email invite.
|
|
432
|
+
name: z8.string().nullable(),
|
|
433
|
+
redeemedAt: z8.string().nullable(),
|
|
434
|
+
role: grantRoleSchema,
|
|
435
|
+
// Shown as "· pending" beside the role until the link is redeemed.
|
|
436
|
+
status: z8.enum(["pending", "active"]),
|
|
437
|
+
// Display fragment of the live invitation token, or null once redeemed.
|
|
438
|
+
tokenPrefix: z8.string().nullable(),
|
|
439
|
+
userId: z8.string().nullable()
|
|
440
|
+
});
|
|
441
|
+
var createdGrantSchema = grantSchema.extend({
|
|
442
|
+
url: z8.string().url()
|
|
427
443
|
});
|
|
428
|
-
var
|
|
429
|
-
|
|
444
|
+
var grantListResponseSchema = z8.object({
|
|
445
|
+
grants: z8.array(grantSchema),
|
|
446
|
+
// The artifact's current general-access value, so the share surface can
|
|
447
|
+
// render the "General access" line without a second read.
|
|
448
|
+
visibility: z8.enum(["public", "unlisted", "workspace", "private"])
|
|
430
449
|
});
|
|
431
|
-
var
|
|
432
|
-
revoked:
|
|
450
|
+
var revokeGrantResponseSchema = z8.object({
|
|
451
|
+
revoked: z8.literal(true)
|
|
452
|
+
});
|
|
453
|
+
var acceptGrantSchema = z8.object({
|
|
454
|
+
token: z8.string().min(1)
|
|
455
|
+
});
|
|
456
|
+
var acceptGrantResponseSchema = z8.object({
|
|
457
|
+
artifactId: z8.string(),
|
|
458
|
+
// The review shell URL to land on, carrying ?invited=1 for the banner.
|
|
459
|
+
artifactUrl: z8.string().url(),
|
|
460
|
+
slug: z8.string(),
|
|
461
|
+
title: z8.string()
|
|
462
|
+
});
|
|
463
|
+
var workspaceGuestSchema = z8.object({
|
|
464
|
+
artifacts: z8.array(
|
|
465
|
+
z8.object({
|
|
466
|
+
artifactId: z8.string(),
|
|
467
|
+
grantId: z8.string(),
|
|
468
|
+
slug: z8.string(),
|
|
469
|
+
status: z8.enum(["pending", "active"]),
|
|
470
|
+
title: z8.string()
|
|
471
|
+
})
|
|
472
|
+
),
|
|
473
|
+
email: z8.string().nullable(),
|
|
474
|
+
image: z8.string().nullable(),
|
|
475
|
+
name: z8.string().nullable(),
|
|
476
|
+
userId: z8.string().nullable()
|
|
477
|
+
});
|
|
478
|
+
var workspaceGuestListResponseSchema = z8.object({
|
|
479
|
+
guests: z8.array(workspaceGuestSchema)
|
|
480
|
+
});
|
|
481
|
+
var sharedWithMeArtifactSchema = z8.object({
|
|
482
|
+
artifactId: z8.string(),
|
|
483
|
+
slug: z8.string(),
|
|
484
|
+
title: z8.string(),
|
|
485
|
+
url: z8.string().url(),
|
|
486
|
+
// Who shared it, for the "Shared with me" list.
|
|
487
|
+
workspaceName: z8.string()
|
|
488
|
+
});
|
|
489
|
+
var sharedWithMeListResponseSchema = z8.object({
|
|
490
|
+
artifacts: z8.array(sharedWithMeArtifactSchema)
|
|
433
491
|
});
|
|
434
492
|
|
|
435
|
-
// ../shared/src/
|
|
436
|
-
import { z as
|
|
493
|
+
// ../shared/src/me.ts
|
|
494
|
+
import { z as z10 } from "zod";
|
|
437
495
|
|
|
438
496
|
// ../shared/src/workspaces.ts
|
|
439
|
-
import { z as
|
|
497
|
+
import { z as z9 } from "zod";
|
|
440
498
|
var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
|
|
441
|
-
var
|
|
442
|
-
|
|
443
|
-
|
|
499
|
+
var workspaceHandleUnavailableReasonSchema = z9.enum([
|
|
500
|
+
"held",
|
|
501
|
+
"invalid",
|
|
502
|
+
"reserved",
|
|
503
|
+
"taken"
|
|
504
|
+
]);
|
|
505
|
+
var workspaceHandleAvailabilitySchema = z9.object({
|
|
506
|
+
available: z9.boolean(),
|
|
507
|
+
handle: z9.string(),
|
|
508
|
+
// Human-readable sentence for the field hint; null when available.
|
|
509
|
+
message: z9.string().nullable(),
|
|
510
|
+
reason: workspaceHandleUnavailableReasonSchema.nullable()
|
|
511
|
+
});
|
|
512
|
+
var workspaceRoleSchema = z9.enum(["owner", "admin", "member"]);
|
|
513
|
+
var workspaceInviteRoleSchema = z9.enum(["admin", "member"]);
|
|
514
|
+
var workspaceHandleSchema = z9.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
|
|
444
515
|
message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
445
516
|
});
|
|
446
|
-
var createWorkspaceSchema =
|
|
517
|
+
var createWorkspaceSchema = z9.object({
|
|
447
518
|
handle: workspaceHandleSchema.optional(),
|
|
448
|
-
name:
|
|
519
|
+
name: z9.string().trim().min(1).max(100)
|
|
449
520
|
});
|
|
450
|
-
var updateWorkspaceSchema =
|
|
521
|
+
var updateWorkspaceSchema = z9.object({
|
|
451
522
|
handle: workspaceHandleSchema.optional(),
|
|
452
|
-
name:
|
|
523
|
+
name: z9.string().trim().min(1).max(100).optional()
|
|
453
524
|
}).refine((body) => body.handle !== void 0 || body.name !== void 0, {
|
|
454
525
|
message: "provide at least one of name or handle"
|
|
455
526
|
});
|
|
456
|
-
var workspaceSchema =
|
|
457
|
-
createdAt:
|
|
458
|
-
handle:
|
|
459
|
-
|
|
527
|
+
var workspaceSchema = z9.object({
|
|
528
|
+
createdAt: z9.string(),
|
|
529
|
+
handle: z9.string(),
|
|
530
|
+
// ISO timestamp when `{handle}.artifact.cafe` was provisioned, or null.
|
|
531
|
+
// Set by the one-time, self-serve enable flow (§4.4); never auto at
|
|
532
|
+
// create, and always null for personal workspaces.
|
|
533
|
+
hostnameProvisionedAt: z9.string().nullable(),
|
|
534
|
+
id: z9.string(),
|
|
460
535
|
// Auto-provisioned solo workspace, one per user: non-deletable, takes
|
|
461
536
|
// no members, renameable like any workspace.
|
|
462
|
-
isPersonal:
|
|
537
|
+
isPersonal: z9.boolean(),
|
|
463
538
|
// Uploaded logo URL, or null for the initials fallback. Set through the
|
|
464
539
|
// dedicated /workspaces/:id/avatar route, never the generic update.
|
|
465
|
-
logo:
|
|
466
|
-
name:
|
|
540
|
+
logo: z9.string().nullable(),
|
|
541
|
+
name: z9.string(),
|
|
467
542
|
role: workspaceRoleSchema.nullable(),
|
|
468
|
-
updatedAt:
|
|
543
|
+
updatedAt: z9.string()
|
|
469
544
|
});
|
|
470
|
-
var workspaceListResponseSchema =
|
|
471
|
-
workspaces:
|
|
545
|
+
var workspaceListResponseSchema = z9.object({
|
|
546
|
+
workspaces: z9.array(workspaceSchema)
|
|
472
547
|
});
|
|
473
|
-
var deleteWorkspaceResponseSchema =
|
|
474
|
-
deleted:
|
|
548
|
+
var deleteWorkspaceResponseSchema = z9.object({
|
|
549
|
+
deleted: z9.literal(true)
|
|
475
550
|
});
|
|
476
|
-
var workspaceMemberSchema =
|
|
477
|
-
createdAt:
|
|
478
|
-
email:
|
|
479
|
-
image:
|
|
480
|
-
name:
|
|
551
|
+
var workspaceMemberSchema = z9.object({
|
|
552
|
+
createdAt: z9.string(),
|
|
553
|
+
email: z9.string().email(),
|
|
554
|
+
image: z9.string().nullable(),
|
|
555
|
+
name: z9.string(),
|
|
481
556
|
role: workspaceRoleSchema,
|
|
482
|
-
userId:
|
|
557
|
+
userId: z9.string()
|
|
483
558
|
});
|
|
484
|
-
var workspaceMemberListResponseSchema =
|
|
485
|
-
members:
|
|
559
|
+
var workspaceMemberListResponseSchema = z9.object({
|
|
560
|
+
members: z9.array(workspaceMemberSchema)
|
|
486
561
|
});
|
|
487
|
-
var updateWorkspaceMemberSchema =
|
|
562
|
+
var updateWorkspaceMemberSchema = z9.object({
|
|
488
563
|
role: workspaceRoleSchema
|
|
489
564
|
});
|
|
490
|
-
var removeWorkspaceMemberResponseSchema =
|
|
491
|
-
removed:
|
|
565
|
+
var removeWorkspaceMemberResponseSchema = z9.object({
|
|
566
|
+
removed: z9.literal(true)
|
|
492
567
|
});
|
|
493
|
-
var createWorkspaceInviteSchema =
|
|
494
|
-
email:
|
|
495
|
-
expiresAt:
|
|
496
|
-
maxUses:
|
|
568
|
+
var createWorkspaceInviteSchema = z9.object({
|
|
569
|
+
email: z9.string().trim().toLowerCase().email().optional(),
|
|
570
|
+
expiresAt: z9.string().datetime(),
|
|
571
|
+
maxUses: z9.number().int().positive().optional(),
|
|
497
572
|
role: workspaceInviteRoleSchema
|
|
498
573
|
});
|
|
499
|
-
var workspaceInviteSchema =
|
|
500
|
-
createdAt:
|
|
501
|
-
email:
|
|
502
|
-
expiresAt:
|
|
503
|
-
id:
|
|
504
|
-
maxUses:
|
|
574
|
+
var workspaceInviteSchema = z9.object({
|
|
575
|
+
createdAt: z9.string(),
|
|
576
|
+
email: z9.string().email().nullable(),
|
|
577
|
+
expiresAt: z9.string(),
|
|
578
|
+
id: z9.string(),
|
|
579
|
+
maxUses: z9.number().int().positive().nullable(),
|
|
505
580
|
role: workspaceInviteRoleSchema,
|
|
506
|
-
tokenPrefix:
|
|
507
|
-
useCount:
|
|
581
|
+
tokenPrefix: z9.string(),
|
|
582
|
+
useCount: z9.number().int().nonnegative()
|
|
508
583
|
});
|
|
509
584
|
var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
|
|
510
|
-
url:
|
|
585
|
+
url: z9.string().url()
|
|
511
586
|
});
|
|
512
|
-
var workspaceInviteListResponseSchema =
|
|
513
|
-
invites:
|
|
587
|
+
var workspaceInviteListResponseSchema = z9.object({
|
|
588
|
+
invites: z9.array(workspaceInviteSchema)
|
|
514
589
|
});
|
|
515
|
-
var revokeWorkspaceInviteResponseSchema =
|
|
516
|
-
revoked:
|
|
590
|
+
var revokeWorkspaceInviteResponseSchema = z9.object({
|
|
591
|
+
revoked: z9.literal(true)
|
|
517
592
|
});
|
|
518
|
-
var acceptWorkspaceInviteSchema =
|
|
519
|
-
token:
|
|
593
|
+
var acceptWorkspaceInviteSchema = z9.object({
|
|
594
|
+
token: z9.string().min(1)
|
|
520
595
|
});
|
|
521
|
-
var acceptWorkspaceInviteResponseSchema =
|
|
596
|
+
var acceptWorkspaceInviteResponseSchema = z9.object({
|
|
522
597
|
role: workspaceRoleSchema,
|
|
523
598
|
workspace: workspaceSchema
|
|
524
599
|
});
|
|
525
|
-
var folderVisibilitySchema =
|
|
526
|
-
var artifactVisibilitySchema =
|
|
600
|
+
var folderVisibilitySchema = z9.enum(["public", "workspace"]);
|
|
601
|
+
var artifactVisibilitySchema = z9.enum([
|
|
527
602
|
"public",
|
|
528
603
|
"unlisted",
|
|
529
|
-
"workspace"
|
|
604
|
+
"workspace",
|
|
605
|
+
"private"
|
|
530
606
|
]);
|
|
531
|
-
var createFolderSchema =
|
|
532
|
-
name:
|
|
607
|
+
var createFolderSchema = z9.object({
|
|
608
|
+
name: z9.string().trim().min(1).max(100),
|
|
533
609
|
visibility: folderVisibilitySchema
|
|
534
610
|
});
|
|
535
|
-
var updateFolderSchema =
|
|
611
|
+
var updateFolderSchema = z9.object({
|
|
536
612
|
// With `visibility`: bulk-update the folder's artifacts to match
|
|
537
613
|
// (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
|
|
538
|
-
cascade:
|
|
539
|
-
name:
|
|
540
|
-
sortOrder:
|
|
614
|
+
cascade: z9.boolean().optional(),
|
|
615
|
+
name: z9.string().trim().min(1).max(100).optional(),
|
|
616
|
+
sortOrder: z9.number().int().min(0).optional(),
|
|
541
617
|
visibility: folderVisibilitySchema.optional()
|
|
542
618
|
}).refine(
|
|
543
619
|
(body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0,
|
|
@@ -545,177 +621,267 @@ var updateFolderSchema = z8.object({
|
|
|
545
621
|
).refine((body) => !body.cascade || body.visibility !== void 0, {
|
|
546
622
|
message: "cascade requires visibility"
|
|
547
623
|
});
|
|
548
|
-
var folderSchema =
|
|
549
|
-
createdAt:
|
|
550
|
-
id:
|
|
551
|
-
name:
|
|
624
|
+
var folderSchema = z9.object({
|
|
625
|
+
createdAt: z9.string(),
|
|
626
|
+
id: z9.string(),
|
|
627
|
+
name: z9.string(),
|
|
552
628
|
// Permalink segment for the public folder page (/w/{handle}/f/{slug}).
|
|
553
629
|
// Server-derived from the name; renames regenerate it.
|
|
554
|
-
slug:
|
|
555
|
-
sortOrder:
|
|
556
|
-
updatedAt:
|
|
630
|
+
slug: z9.string(),
|
|
631
|
+
sortOrder: z9.number().int(),
|
|
632
|
+
updatedAt: z9.string(),
|
|
557
633
|
visibility: folderVisibilitySchema,
|
|
558
|
-
workspaceId:
|
|
634
|
+
workspaceId: z9.string()
|
|
635
|
+
});
|
|
636
|
+
var folderListResponseSchema = z9.object({
|
|
637
|
+
folders: z9.array(folderSchema)
|
|
559
638
|
});
|
|
560
|
-
var
|
|
561
|
-
|
|
639
|
+
var deleteFolderResponseSchema = z9.object({
|
|
640
|
+
deleted: z9.literal(true)
|
|
562
641
|
});
|
|
563
|
-
var
|
|
564
|
-
|
|
642
|
+
var workspaceArtifactSchema = z9.object({
|
|
643
|
+
createdAt: z9.string(),
|
|
644
|
+
currentVersionNumber: z9.number().int().nullable(),
|
|
645
|
+
folderId: z9.string().nullable(),
|
|
646
|
+
id: z9.string(),
|
|
647
|
+
openCommentCount: z9.number().int().nonnegative(),
|
|
648
|
+
reviewMode: reviewModeSchema,
|
|
649
|
+
slug: z9.string(),
|
|
650
|
+
title: z9.string(),
|
|
651
|
+
updatedAt: z9.string(),
|
|
652
|
+
url: z9.string().url(),
|
|
653
|
+
versionCount: z9.number().int().nonnegative(),
|
|
654
|
+
visibility: artifactVisibilitySchema
|
|
565
655
|
});
|
|
566
|
-
var
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
id:
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
656
|
+
var workspaceArtifactListResponseSchema = z9.object({
|
|
657
|
+
artifacts: z9.array(workspaceArtifactSchema)
|
|
658
|
+
});
|
|
659
|
+
var workspaceArtifactSearchResultSchema = z9.object({
|
|
660
|
+
id: z9.string(),
|
|
661
|
+
slug: z9.string(),
|
|
662
|
+
title: z9.string()
|
|
663
|
+
});
|
|
664
|
+
var workspaceArtifactSearchResponseSchema = z9.object({
|
|
665
|
+
results: z9.array(workspaceArtifactSearchResultSchema)
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
// ../shared/src/me.ts
|
|
669
|
+
var ownArtifactSchema = z10.object({
|
|
670
|
+
createdAt: z10.string(),
|
|
671
|
+
currentVersionNumber: z10.number().nullable(),
|
|
672
|
+
// Owned artifacts normally never expire; non-null only while an
|
|
673
|
+
// expiry is still set (e.g. an unclaimed publish surfaced here later).
|
|
674
|
+
expiresAt: z10.string().nullable(),
|
|
675
|
+
// SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
|
|
676
|
+
// both null for personal artifacts. `folderId` drives the dashboard's
|
|
677
|
+
// move control and folder filter; `folderName` is the display label.
|
|
678
|
+
folderId: z10.string().nullable(),
|
|
679
|
+
folderName: z10.string().nullable(),
|
|
680
|
+
// Whether a publish token is still active — revocable from the
|
|
681
|
+
// dashboard (§6.18); the token itself is never returned.
|
|
682
|
+
hasPublishToken: z10.boolean(),
|
|
683
|
+
id: z10.string(),
|
|
684
|
+
openThreads: z10.number(),
|
|
685
|
+
protected: z10.boolean(),
|
|
686
|
+
// Who published the current version (falling back to the claiming
|
|
687
|
+
// owner) — the dashboard shows a name and avatar per artifact so a
|
|
688
|
+
// shared workspace reads as people's work, not anonymous rows. Null
|
|
689
|
+
// when the publish was token-only and the artifact is unclaimed.
|
|
690
|
+
publishedBy: z10.object({ image: z10.string().nullable(), name: z10.string() }).nullable(),
|
|
691
|
+
// SPEC-2 §5.8 — the review dial, so the dashboard's settings dialog can
|
|
692
|
+
// render current state without a second fetch.
|
|
693
|
+
reviewMode: reviewModeSchema,
|
|
694
|
+
slug: z10.string(),
|
|
695
|
+
title: z10.string(),
|
|
696
|
+
totalViews: z10.number(),
|
|
697
|
+
// SPEC §6.20 — messages by others newer than the viewer's last-seen
|
|
698
|
+
// watermark. 0 for signed-out/API-key-less contexts.
|
|
699
|
+
unreadComments: z10.number(),
|
|
700
|
+
updatedAt: z10.string(),
|
|
701
|
+
url: z10.string(),
|
|
702
|
+
versionCount: z10.number(),
|
|
703
|
+
// SPEC-2 §5.1 — whether the caller clears the manage bar for this
|
|
704
|
+
// artifact (workspace admin, or the person who first published it).
|
|
705
|
+
// Only a manager's folder move inherits the folder's visibility, so the
|
|
706
|
+
// dashboard warns exactly the people the change can actually affect, and
|
|
707
|
+
// only a manager is offered the Share entry (§5.7).
|
|
708
|
+
viewerCanManage: z10.boolean(),
|
|
709
|
+
// SPEC-2 §5.2 — the artifact's own visibility, the single enforcement
|
|
710
|
+
// source. The dashboard needs it to tell whether a folder move would
|
|
711
|
+
// widen exposure before it sends the PATCH (§3.4), and to render the lock
|
|
712
|
+
// glyph on `private` rows and cards (§5.7).
|
|
577
713
|
visibility: artifactVisibilitySchema
|
|
578
714
|
});
|
|
579
|
-
var
|
|
580
|
-
artifacts:
|
|
715
|
+
var ownArtifactsResponseSchema = z10.object({
|
|
716
|
+
artifacts: z10.array(ownArtifactSchema)
|
|
717
|
+
});
|
|
718
|
+
var revokePublishTokenResponseSchema = z10.object({
|
|
719
|
+
revoked: z10.boolean()
|
|
581
720
|
});
|
|
582
721
|
|
|
583
722
|
// ../shared/src/owner.ts
|
|
723
|
+
import { z as z11 } from "zod";
|
|
584
724
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
|
|
585
|
-
var claimResponseSchema =
|
|
586
|
-
artifactId:
|
|
587
|
-
slug:
|
|
588
|
-
title:
|
|
589
|
-
url:
|
|
725
|
+
var claimResponseSchema = z11.object({
|
|
726
|
+
artifactId: z11.string(),
|
|
727
|
+
slug: z11.string(),
|
|
728
|
+
title: z11.string(),
|
|
729
|
+
url: z11.string().url()
|
|
590
730
|
});
|
|
591
|
-
var updateArtifactSchema =
|
|
731
|
+
var updateArtifactSchema = z11.object({
|
|
592
732
|
// SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
|
|
593
733
|
// folder; `workspaceId: null` moves the artifact out of its workspace
|
|
594
734
|
// (§5.5 ownership transfer).
|
|
595
|
-
folderId:
|
|
596
|
-
slug:
|
|
735
|
+
folderId: z11.string().min(1).nullable().optional(),
|
|
736
|
+
slug: z11.string().min(3).max(63).regex(SLUG_PATTERN, {
|
|
597
737
|
message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
598
738
|
}).optional(),
|
|
599
|
-
title:
|
|
739
|
+
title: z11.string().min(1).max(200).optional(),
|
|
600
740
|
visibility: artifactVisibilitySchema.optional(),
|
|
601
|
-
workspaceId:
|
|
741
|
+
workspaceId: z11.string().min(1).nullable().optional()
|
|
602
742
|
}).refine(
|
|
603
743
|
(body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0,
|
|
604
744
|
{
|
|
605
745
|
message: "provide at least one of title, slug, visibility, folderId, workspaceId"
|
|
606
746
|
}
|
|
607
747
|
);
|
|
608
|
-
var deleteArtifactResponseSchema =
|
|
609
|
-
deleted:
|
|
748
|
+
var deleteArtifactResponseSchema = z11.object({
|
|
749
|
+
deleted: z11.literal(true)
|
|
610
750
|
});
|
|
611
751
|
|
|
612
752
|
// ../shared/src/presence.ts
|
|
613
|
-
import { z as
|
|
753
|
+
import { z as z12 } from "zod";
|
|
754
|
+
var AGENT_STATES = ["listening", "editing", "publishing"];
|
|
755
|
+
var agentStateSchema = z12.enum(AGENT_STATES);
|
|
614
756
|
var AGENT_PRESENCE = {
|
|
757
|
+
editingTtlSeconds: 300,
|
|
615
758
|
heartbeatSeconds: 15,
|
|
616
759
|
shellPollSeconds: 15,
|
|
617
760
|
ttlSeconds: 45
|
|
618
761
|
};
|
|
619
|
-
var
|
|
620
|
-
|
|
621
|
-
|
|
762
|
+
var agentHeartbeatBodySchema = z12.object({
|
|
763
|
+
state: agentStateSchema.optional()
|
|
764
|
+
});
|
|
765
|
+
var agentHeartbeatResponseSchema = z12.object({
|
|
766
|
+
listening: z12.literal(true),
|
|
767
|
+
state: agentStateSchema.optional(),
|
|
768
|
+
ttlSeconds: z12.number().int().positive()
|
|
622
769
|
});
|
|
623
|
-
var agentPresenceSchema =
|
|
624
|
-
listening:
|
|
625
|
-
|
|
770
|
+
var agentPresenceSchema = z12.object({
|
|
771
|
+
listening: z12.boolean(),
|
|
772
|
+
state: agentStateSchema.nullable().optional(),
|
|
773
|
+
versionNumber: z12.number().int().positive().nullable()
|
|
626
774
|
});
|
|
627
775
|
|
|
628
776
|
// ../shared/src/protection.ts
|
|
629
|
-
import { z as
|
|
777
|
+
import { z as z13 } from "zod";
|
|
630
778
|
var PASSWORD_MIN = 6;
|
|
631
779
|
var PASSWORD_MAX = 128;
|
|
632
|
-
var gatePasswordSchema =
|
|
633
|
-
var setProtectionSchema =
|
|
780
|
+
var gatePasswordSchema = z13.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
|
|
781
|
+
var setProtectionSchema = z13.object({
|
|
634
782
|
password: gatePasswordSchema.nullable()
|
|
635
783
|
});
|
|
636
|
-
var protectionResponseSchema =
|
|
637
|
-
protected:
|
|
784
|
+
var protectionResponseSchema = z13.object({
|
|
785
|
+
protected: z13.boolean()
|
|
638
786
|
});
|
|
639
|
-
var unlockSchema =
|
|
640
|
-
password:
|
|
787
|
+
var unlockSchema = z13.object({
|
|
788
|
+
password: z13.string().min(1).max(1024)
|
|
641
789
|
});
|
|
642
|
-
var unlockResponseSchema =
|
|
643
|
-
unlocked:
|
|
790
|
+
var unlockResponseSchema = z13.object({
|
|
791
|
+
unlocked: z13.literal(true)
|
|
644
792
|
});
|
|
645
793
|
|
|
646
794
|
// ../shared/src/report.ts
|
|
647
|
-
import { z as
|
|
648
|
-
var reportRequestSchema =
|
|
649
|
-
reason:
|
|
795
|
+
import { z as z14 } from "zod";
|
|
796
|
+
var reportRequestSchema = z14.object({
|
|
797
|
+
reason: z14.string().max(1e3).optional()
|
|
650
798
|
});
|
|
651
|
-
var reportResponseSchema =
|
|
652
|
-
reported:
|
|
799
|
+
var reportResponseSchema = z14.object({
|
|
800
|
+
reported: z14.literal(true)
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
// ../shared/src/reviewers.ts
|
|
804
|
+
import { z as z15 } from "zod";
|
|
805
|
+
var artifactReviewerSchema = z15.object({
|
|
806
|
+
avatarUrl: z15.string().nullable(),
|
|
807
|
+
displayName: z15.string(),
|
|
808
|
+
// Exactly one of userId/guestId is set, per `kind`.
|
|
809
|
+
guestId: z15.string().nullable(),
|
|
810
|
+
// "account", not commentAuthorSchema's "member" — the whole point here is
|
|
811
|
+
// that these people need not be workspace members. A guest can be named in
|
|
812
|
+
// a comment but never notified: guest_identities carries a display name and
|
|
813
|
+
// no email, so there is nowhere to send anything.
|
|
814
|
+
kind: z15.enum(["account", "guest"]),
|
|
815
|
+
userId: z15.string().nullable()
|
|
816
|
+
});
|
|
817
|
+
var artifactReviewerListResponseSchema = z15.object({
|
|
818
|
+
reviewers: z15.array(artifactReviewerSchema)
|
|
653
819
|
});
|
|
654
820
|
|
|
655
821
|
// ../shared/src/stats.ts
|
|
656
|
-
import { z as
|
|
657
|
-
var avgViewSecondsSchema =
|
|
658
|
-
var versionViewStatsSchema =
|
|
822
|
+
import { z as z16 } from "zod";
|
|
823
|
+
var avgViewSecondsSchema = z16.number().int().nonnegative().nullable();
|
|
824
|
+
var versionViewStatsSchema = z16.object({
|
|
659
825
|
avgViewSeconds: avgViewSecondsSchema,
|
|
660
826
|
// Views from inside the review shell's iframe vs. direct visits to
|
|
661
827
|
// the render URL. views = embeddedViews + directViews.
|
|
662
|
-
directViews:
|
|
663
|
-
embeddedViews:
|
|
828
|
+
directViews: z16.number().int().nonnegative(),
|
|
829
|
+
embeddedViews: z16.number().int().nonnegative(),
|
|
664
830
|
// Distinct hashed viewer IPs across the version's lifetime.
|
|
665
|
-
uniqueViewers:
|
|
666
|
-
versionNumber:
|
|
667
|
-
views:
|
|
831
|
+
uniqueViewers: z16.number().int().nonnegative(),
|
|
832
|
+
versionNumber: z16.number().int().positive(),
|
|
833
|
+
views: z16.number().int().nonnegative()
|
|
668
834
|
});
|
|
669
|
-
var dailyViewsSchema =
|
|
835
|
+
var dailyViewsSchema = z16.object({
|
|
670
836
|
avgViewSeconds: avgViewSecondsSchema,
|
|
671
837
|
// UTC day, YYYY-MM-DD.
|
|
672
|
-
day:
|
|
673
|
-
uniqueViewers:
|
|
674
|
-
views:
|
|
838
|
+
day: z16.string(),
|
|
839
|
+
uniqueViewers: z16.number().int().nonnegative(),
|
|
840
|
+
views: z16.number().int().nonnegative()
|
|
675
841
|
});
|
|
676
|
-
var artifactStatsResponseSchema =
|
|
842
|
+
var artifactStatsResponseSchema = z16.object({
|
|
677
843
|
avgViewSeconds: avgViewSecondsSchema,
|
|
678
|
-
daily:
|
|
844
|
+
daily: z16.array(dailyViewsSchema),
|
|
679
845
|
// Distinct hashed viewer IPs across all versions and days.
|
|
680
|
-
totalUniqueViewers:
|
|
681
|
-
totalViews:
|
|
682
|
-
versions:
|
|
846
|
+
totalUniqueViewers: z16.number().int().nonnegative(),
|
|
847
|
+
totalViews: z16.number().int().nonnegative(),
|
|
848
|
+
versions: z16.array(versionViewStatsSchema)
|
|
683
849
|
});
|
|
684
850
|
|
|
685
851
|
// ../shared/src/versions.ts
|
|
686
|
-
import { z as
|
|
687
|
-
var versionSourceSchema =
|
|
688
|
-
agentName:
|
|
689
|
-
type:
|
|
852
|
+
import { z as z17 } from "zod";
|
|
853
|
+
var versionSourceSchema = z17.object({
|
|
854
|
+
agentName: z17.string().nullable(),
|
|
855
|
+
type: z17.string()
|
|
690
856
|
});
|
|
691
|
-
var versionChangesSchema =
|
|
692
|
-
added:
|
|
693
|
-
changed:
|
|
694
|
-
removed:
|
|
857
|
+
var versionChangesSchema = z17.object({
|
|
858
|
+
added: z17.array(z17.string()),
|
|
859
|
+
changed: z17.array(z17.string()),
|
|
860
|
+
removed: z17.array(z17.string())
|
|
695
861
|
});
|
|
696
|
-
var versionSummarySchema =
|
|
862
|
+
var versionSummarySchema = z17.object({
|
|
697
863
|
// Absent for the first version — there is no previous version to diff against.
|
|
698
864
|
changes: versionChangesSchema.nullable(),
|
|
699
|
-
createdAt:
|
|
700
|
-
entryUrl:
|
|
701
|
-
fileCount:
|
|
702
|
-
id:
|
|
703
|
-
isCurrent:
|
|
865
|
+
createdAt: z17.string(),
|
|
866
|
+
entryUrl: z17.string().url(),
|
|
867
|
+
fileCount: z17.number().int().nonnegative(),
|
|
868
|
+
id: z17.string(),
|
|
869
|
+
isCurrent: z17.boolean(),
|
|
704
870
|
// Author-supplied bullet-point changelog; null when the publish omitted it.
|
|
705
|
-
notes:
|
|
871
|
+
notes: z17.string().nullable(),
|
|
706
872
|
source: versionSourceSchema,
|
|
707
|
-
totalSizeBytes:
|
|
708
|
-
versionNumber:
|
|
873
|
+
totalSizeBytes: z17.number().int().nonnegative(),
|
|
874
|
+
versionNumber: z17.number().int().positive()
|
|
709
875
|
});
|
|
710
|
-
var versionListResponseSchema =
|
|
711
|
-
versions:
|
|
876
|
+
var versionListResponseSchema = z17.object({
|
|
877
|
+
versions: z17.array(versionSummarySchema)
|
|
712
878
|
});
|
|
713
879
|
|
|
714
880
|
// src/api.ts
|
|
715
881
|
var ApiRequestError = class extends Error {
|
|
716
|
-
constructor(
|
|
882
|
+
constructor(status2, code, message, retryAfter) {
|
|
717
883
|
super(message);
|
|
718
|
-
this.status =
|
|
884
|
+
this.status = status2;
|
|
719
885
|
this.code = code;
|
|
720
886
|
this.retryAfter = retryAfter;
|
|
721
887
|
this.name = "ApiRequestError";
|
|
@@ -774,13 +940,28 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
774
940
|
(d) => commentThreadSchema.parse(d)
|
|
775
941
|
);
|
|
776
942
|
},
|
|
777
|
-
async agentHeartbeat(artifactId) {
|
|
943
|
+
async agentHeartbeat(artifactId, state) {
|
|
778
944
|
return request(
|
|
779
945
|
`${base}/api/v1/artifacts/${artifactId}/agent-presence`,
|
|
780
|
-
{
|
|
946
|
+
{
|
|
947
|
+
...state ? { body: JSON.stringify({ state }) } : {},
|
|
948
|
+
headers,
|
|
949
|
+
method: "POST"
|
|
950
|
+
},
|
|
781
951
|
(d) => agentHeartbeatResponseSchema.parse(d)
|
|
782
952
|
);
|
|
783
953
|
},
|
|
954
|
+
// SPEC-2 §6.6 — share an artifact with a person (email or user id).
|
|
955
|
+
// Authorized by this client's credential: the publish token (an agent
|
|
956
|
+
// sharing a private draft) or an account key/session. The invitation
|
|
957
|
+
// URL is returned exactly once, here.
|
|
958
|
+
async createGrant(artifactId, body) {
|
|
959
|
+
return request(
|
|
960
|
+
`${base}/api/v1/artifacts/${artifactId}/grants`,
|
|
961
|
+
{ body: JSON.stringify(body), headers, method: "POST" },
|
|
962
|
+
(d) => createdGrantSchema.parse(d)
|
|
963
|
+
);
|
|
964
|
+
},
|
|
784
965
|
async createSession(body) {
|
|
785
966
|
return request(
|
|
786
967
|
`${base}/api/v1/publish`,
|
|
@@ -859,6 +1040,15 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
859
1040
|
(d) => folderListResponseSchema.parse(d)
|
|
860
1041
|
);
|
|
861
1042
|
},
|
|
1043
|
+
// SPEC-2 §6.6 — the artifact's grants (no plaintext tokens). Used by
|
|
1044
|
+
// `share --list` and to resolve an email → grant id for `unshare`.
|
|
1045
|
+
async listGrants(artifactId) {
|
|
1046
|
+
return request(
|
|
1047
|
+
`${base}/api/v1/artifacts/${artifactId}/grants`,
|
|
1048
|
+
{ headers, method: "GET" },
|
|
1049
|
+
(d) => grantListResponseSchema.parse(d)
|
|
1050
|
+
);
|
|
1051
|
+
},
|
|
862
1052
|
async listWorkspaces() {
|
|
863
1053
|
return request(
|
|
864
1054
|
`${base}/api/v1/workspaces`,
|
|
@@ -903,6 +1093,14 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
903
1093
|
(d) => commentThreadSchema.parse(d)
|
|
904
1094
|
);
|
|
905
1095
|
},
|
|
1096
|
+
// SPEC-2 §6.6 — revoke a grant; access is gone within the cookie TTL.
|
|
1097
|
+
async revokeGrant(artifactId, grantId) {
|
|
1098
|
+
await request(
|
|
1099
|
+
`${base}/api/v1/artifacts/${artifactId}/grants/${grantId}`,
|
|
1100
|
+
{ headers, method: "DELETE" },
|
|
1101
|
+
() => void 0
|
|
1102
|
+
);
|
|
1103
|
+
},
|
|
906
1104
|
// SPEC §6.16 — set a password (string) or clear it (null). Authorized
|
|
907
1105
|
// by the publish token this client was created with.
|
|
908
1106
|
async setProtection(artifactId, password) {
|
|
@@ -911,6 +1109,16 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
911
1109
|
{ body: JSON.stringify({ password }), headers, method: "PUT" },
|
|
912
1110
|
(d) => protectionResponseSchema.parse(d)
|
|
913
1111
|
);
|
|
1112
|
+
},
|
|
1113
|
+
// SPEC-2 §5.8 — what reviewers may do once they can open it. Same
|
|
1114
|
+
// dual-auth as protection, so the publish token this client holds is
|
|
1115
|
+
// enough (an agent can close review on its own artifact).
|
|
1116
|
+
async setReviewMode(artifactId, mode) {
|
|
1117
|
+
return request(
|
|
1118
|
+
`${base}/api/v1/artifacts/${artifactId}/review-mode`,
|
|
1119
|
+
{ body: JSON.stringify({ mode }), headers, method: "PUT" },
|
|
1120
|
+
(d) => reviewModeResponseSchema.parse(d)
|
|
1121
|
+
);
|
|
914
1122
|
}
|
|
915
1123
|
};
|
|
916
1124
|
}
|
|
@@ -939,17 +1147,17 @@ import {
|
|
|
939
1147
|
writeFileSync as writeFileSync2
|
|
940
1148
|
} from "fs";
|
|
941
1149
|
import path2 from "path";
|
|
942
|
-
import { z as
|
|
1150
|
+
import { z as z19 } from "zod";
|
|
943
1151
|
|
|
944
1152
|
// src/credentials.ts
|
|
945
1153
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
946
1154
|
import { homedir } from "os";
|
|
947
1155
|
import path from "path";
|
|
948
|
-
import { z as
|
|
1156
|
+
import { z as z18 } from "zod";
|
|
949
1157
|
var DEFAULT_API_URL = "https://artifact.cafe";
|
|
950
|
-
var credentialsSchema =
|
|
951
|
-
keys:
|
|
952
|
-
version:
|
|
1158
|
+
var credentialsSchema = z18.object({
|
|
1159
|
+
keys: z18.record(z18.string(), z18.string()),
|
|
1160
|
+
version: z18.literal(1)
|
|
953
1161
|
});
|
|
954
1162
|
function credentialsPath() {
|
|
955
1163
|
return path.join(homedir(), ".artifactcafe", "credentials");
|
|
@@ -999,24 +1207,24 @@ function resolveApiKey(apiUrl2, flag) {
|
|
|
999
1207
|
}
|
|
1000
1208
|
|
|
1001
1209
|
// src/config.ts
|
|
1002
|
-
var configSchema =
|
|
1003
|
-
apiUrl:
|
|
1004
|
-
artifactId:
|
|
1005
|
-
folder:
|
|
1006
|
-
folderId:
|
|
1210
|
+
var configSchema = z19.object({
|
|
1211
|
+
apiUrl: z19.string(),
|
|
1212
|
+
artifactId: z19.string(),
|
|
1213
|
+
folder: z19.string().optional(),
|
|
1214
|
+
folderId: z19.string().optional(),
|
|
1007
1215
|
// Optional: a folder linked by `link` to an artifact you own carries no
|
|
1008
1216
|
// token — owner actions authorize via your stored login instead. A first
|
|
1009
1217
|
// publish still writes the anonymous-mode token here.
|
|
1010
|
-
publishToken:
|
|
1011
|
-
slug:
|
|
1012
|
-
url:
|
|
1013
|
-
workspace:
|
|
1014
|
-
workspaceId:
|
|
1015
|
-
workspaceKind:
|
|
1016
|
-
});
|
|
1017
|
-
var projectConfigSchema =
|
|
1018
|
-
folder:
|
|
1019
|
-
workspace:
|
|
1218
|
+
publishToken: z19.string().optional(),
|
|
1219
|
+
slug: z19.string(),
|
|
1220
|
+
url: z19.string(),
|
|
1221
|
+
workspace: z19.string().optional(),
|
|
1222
|
+
workspaceId: z19.string().optional(),
|
|
1223
|
+
workspaceKind: z19.enum(["personal", "workspace"]).optional()
|
|
1224
|
+
});
|
|
1225
|
+
var projectConfigSchema = z19.object({
|
|
1226
|
+
folder: z19.string().trim().min(1).optional(),
|
|
1227
|
+
workspace: z19.string().trim().min(1)
|
|
1020
1228
|
}).strict();
|
|
1021
1229
|
var CONFIG_DIR = ".artifactcafe";
|
|
1022
1230
|
var CONFIG_FILE = "config.json";
|
|
@@ -1052,7 +1260,7 @@ function readProjectConfig(dir) {
|
|
|
1052
1260
|
JSON.parse(readFileSync2(file, "utf8"))
|
|
1053
1261
|
);
|
|
1054
1262
|
} catch (error) {
|
|
1055
|
-
const message = error instanceof
|
|
1263
|
+
const message = error instanceof z19.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
|
|
1056
1264
|
throw new Error(`Invalid ${file}: ${message}.`);
|
|
1057
1265
|
}
|
|
1058
1266
|
}
|
|
@@ -1239,7 +1447,7 @@ async function comments(opts) {
|
|
|
1239
1447
|
`--version must be one of: ${VERSIONS.join(", ")} (got "${opts.version}").`
|
|
1240
1448
|
);
|
|
1241
1449
|
}
|
|
1242
|
-
const
|
|
1450
|
+
const status2 = opts.status;
|
|
1243
1451
|
const version = opts.version;
|
|
1244
1452
|
const dir = path4.resolve(opts.dir);
|
|
1245
1453
|
const target = resolveArtifactTarget(dir, {
|
|
@@ -1252,21 +1460,21 @@ async function comments(opts) {
|
|
|
1252
1460
|
if (opts.wait) {
|
|
1253
1461
|
await waitForFeedback(client, ref, {
|
|
1254
1462
|
json: opts.json,
|
|
1255
|
-
status,
|
|
1463
|
+
status: status2,
|
|
1256
1464
|
timeoutSeconds: opts.timeout,
|
|
1257
1465
|
version
|
|
1258
1466
|
});
|
|
1259
1467
|
return;
|
|
1260
1468
|
}
|
|
1261
1469
|
const data = await client.exportComments(target.artifactId, {
|
|
1262
|
-
status,
|
|
1470
|
+
status: status2,
|
|
1263
1471
|
version
|
|
1264
1472
|
});
|
|
1265
1473
|
if (opts.json) {
|
|
1266
1474
|
console.log(JSON.stringify(data, null, 2));
|
|
1267
1475
|
return;
|
|
1268
1476
|
}
|
|
1269
|
-
printHuman(data, url,
|
|
1477
|
+
printHuman(data, url, status2, version);
|
|
1270
1478
|
}
|
|
1271
1479
|
async function deriveReviewUrl(client, target) {
|
|
1272
1480
|
const artifact = await client.getArtifact(target.artifactId);
|
|
@@ -1328,6 +1536,10 @@ async function waitForFeedback(client, config, opts) {
|
|
|
1328
1536
|
await sleep(settleSeconds());
|
|
1329
1537
|
const settled = await client.exportComments(config.artifactId, query);
|
|
1330
1538
|
const changed = diffThreads(baseline, settled);
|
|
1539
|
+
try {
|
|
1540
|
+
await client.agentHeartbeat(config.artifactId, "editing");
|
|
1541
|
+
} catch {
|
|
1542
|
+
}
|
|
1331
1543
|
printWaitResult(config, settled, changed, opts.json);
|
|
1332
1544
|
return;
|
|
1333
1545
|
}
|
|
@@ -1378,8 +1590,8 @@ ${changed.length} thread${plural} with new feedback \u2014 ${config.url}
|
|
|
1378
1590
|
printThread(thread);
|
|
1379
1591
|
}
|
|
1380
1592
|
}
|
|
1381
|
-
function printHuman(data, url,
|
|
1382
|
-
const statusLabel =
|
|
1593
|
+
function printHuman(data, url, status2, version) {
|
|
1594
|
+
const statusLabel = status2 === "all" ? "" : `${status2} `;
|
|
1383
1595
|
let scope = "across all versions";
|
|
1384
1596
|
if (version === "current") {
|
|
1385
1597
|
scope = data.versionNumber === null ? "on the current version" : `on v${data.versionNumber}`;
|
|
@@ -1571,6 +1783,11 @@ function resolveEntry(files, explicit) {
|
|
|
1571
1783
|
|
|
1572
1784
|
// src/publish.ts
|
|
1573
1785
|
var UPLOAD_CONCURRENCY = 6;
|
|
1786
|
+
var REVIEW_LABEL = {
|
|
1787
|
+
off: "off \u2014 plain artifact, no review tools",
|
|
1788
|
+
open: "open \u2014 anyone with the link can comment",
|
|
1789
|
+
readonly: "read-only \u2014 existing comments only"
|
|
1790
|
+
};
|
|
1574
1791
|
async function publish(opts) {
|
|
1575
1792
|
const dir = path6.resolve(opts.dir);
|
|
1576
1793
|
const config = readConfig(dir);
|
|
@@ -1580,6 +1797,14 @@ async function publish(opts) {
|
|
|
1580
1797
|
throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
|
|
1581
1798
|
}
|
|
1582
1799
|
}
|
|
1800
|
+
let reviewMode;
|
|
1801
|
+
if (opts.review !== void 0) {
|
|
1802
|
+
const parsed = reviewModeSchema.safeParse(opts.review);
|
|
1803
|
+
if (!parsed.success) {
|
|
1804
|
+
throw new Error("--review must be one of: open, readonly, off.");
|
|
1805
|
+
}
|
|
1806
|
+
reviewMode = parsed.data;
|
|
1807
|
+
}
|
|
1583
1808
|
const artifactId = opts.artifact ?? config?.artifactId ?? null;
|
|
1584
1809
|
const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
|
|
1585
1810
|
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
|
|
@@ -1638,17 +1863,26 @@ async function publish(opts) {
|
|
|
1638
1863
|
log(
|
|
1639
1864
|
` uploading ${session.filesToUpload.length} file(s)` + (session.skippedFiles.length ? `, ${session.skippedFiles.length} unchanged` : "")
|
|
1640
1865
|
);
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1866
|
+
const presence = startPublishingPresence(client, session.artifactId);
|
|
1867
|
+
try {
|
|
1868
|
+
await inBatches(
|
|
1869
|
+
session.filesToUpload,
|
|
1870
|
+
UPLOAD_CONCURRENCY,
|
|
1871
|
+
async (upload) => {
|
|
1872
|
+
const file = byPath.get(upload.path);
|
|
1873
|
+
if (!file) {
|
|
1874
|
+
throw new Error(`Server asked for unknown file ${upload.path}`);
|
|
1875
|
+
}
|
|
1876
|
+
await uploadFile(
|
|
1877
|
+
upload.uploadUrl,
|
|
1878
|
+
readFileSync4(file.absPath),
|
|
1879
|
+
file.mimeType
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1650
1882
|
);
|
|
1651
|
-
}
|
|
1883
|
+
} finally {
|
|
1884
|
+
presence.stop();
|
|
1885
|
+
}
|
|
1652
1886
|
const result = await client.finalize(session.publishSessionId);
|
|
1653
1887
|
const rememberedProjectFile = placement.rememberProjectDefault ? writeProjectConfig(dir, placement.rememberProjectDefault) : null;
|
|
1654
1888
|
const existingToken = config?.artifactId === result.artifactId ? config.publishToken : void 0;
|
|
@@ -1674,6 +1908,13 @@ async function publish(opts) {
|
|
|
1674
1908
|
);
|
|
1675
1909
|
await protectClient.setProtection(result.artifactId, opts.password);
|
|
1676
1910
|
}
|
|
1911
|
+
if (reviewMode !== void 0) {
|
|
1912
|
+
const reviewClient = createApiClient(
|
|
1913
|
+
apiUrl2,
|
|
1914
|
+
credential ?? result.publishToken
|
|
1915
|
+
);
|
|
1916
|
+
await reviewClient.setReviewMode(result.artifactId, reviewMode);
|
|
1917
|
+
}
|
|
1677
1918
|
if (opts.json) {
|
|
1678
1919
|
console.log(
|
|
1679
1920
|
JSON.stringify(
|
|
@@ -1690,6 +1931,7 @@ async function publish(opts) {
|
|
|
1690
1931
|
workspaceId: placement.workspaceId ?? null,
|
|
1691
1932
|
...result.claimUrl ? { claimUrl: result.claimUrl } : {},
|
|
1692
1933
|
...opts.password === void 0 ? {} : { protected: true },
|
|
1934
|
+
...reviewMode === void 0 ? {} : { reviewMode },
|
|
1693
1935
|
filesUploaded: session.filesToUpload.map((f) => f.path),
|
|
1694
1936
|
ignored: skipped,
|
|
1695
1937
|
skippedFiles: session.skippedFiles
|
|
@@ -1721,6 +1963,10 @@ ${result.claimUrl}
|
|
|
1721
1963
|
if (opts.password !== void 0) {
|
|
1722
1964
|
console.log("Password protection: on\n");
|
|
1723
1965
|
}
|
|
1966
|
+
if (reviewMode !== void 0) {
|
|
1967
|
+
console.log(`Review: ${REVIEW_LABEL[reviewMode]}
|
|
1968
|
+
`);
|
|
1969
|
+
}
|
|
1724
1970
|
} else {
|
|
1725
1971
|
console.log(`
|
|
1726
1972
|
\u2713 Published v${result.versionNumber}
|
|
@@ -1736,6 +1982,10 @@ ${result.claimUrl}
|
|
|
1736
1982
|
if (opts.password !== void 0) {
|
|
1737
1983
|
console.log("Password protection: on\n");
|
|
1738
1984
|
}
|
|
1985
|
+
if (reviewMode !== void 0) {
|
|
1986
|
+
console.log(`Review: ${REVIEW_LABEL[reviewMode]}
|
|
1987
|
+
`);
|
|
1988
|
+
}
|
|
1739
1989
|
}
|
|
1740
1990
|
if (opts.open && !opts.json && process.stdout.isTTY) {
|
|
1741
1991
|
openInBrowser(result.url);
|
|
@@ -1917,6 +2167,16 @@ Publish where? [1-${choices.length}]: `, (answer) => {
|
|
|
1917
2167
|
});
|
|
1918
2168
|
});
|
|
1919
2169
|
}
|
|
2170
|
+
function startPublishingPresence(client, artifactId) {
|
|
2171
|
+
const beat = () => {
|
|
2172
|
+
client.agentHeartbeat(artifactId, "publishing").catch(() => {
|
|
2173
|
+
});
|
|
2174
|
+
};
|
|
2175
|
+
beat();
|
|
2176
|
+
const timer = setInterval(beat, AGENT_PRESENCE.heartbeatSeconds * 1e3);
|
|
2177
|
+
timer.unref();
|
|
2178
|
+
return { stop: () => clearInterval(timer) };
|
|
2179
|
+
}
|
|
1920
2180
|
async function inBatches(items, concurrency, fn) {
|
|
1921
2181
|
let next = 0;
|
|
1922
2182
|
const workers = Array.from(
|
|
@@ -2526,30 +2786,181 @@ async function changeThreadStatus(opts) {
|
|
|
2526
2786
|
console.log(`\u2713 ${verb} ${thread.id} \u2014 now ${thread.status}.`);
|
|
2527
2787
|
}
|
|
2528
2788
|
|
|
2789
|
+
// src/review.ts
|
|
2790
|
+
import path14 from "path";
|
|
2791
|
+
var OUTCOME = {
|
|
2792
|
+
off: "Plain artifact \u2014 the review tools are hidden entirely",
|
|
2793
|
+
open: "Open for review \u2014 anyone who can open it can comment",
|
|
2794
|
+
readonly: "Read-only \u2014 earlier comments stay visible, no new ones"
|
|
2795
|
+
};
|
|
2796
|
+
async function review(opts) {
|
|
2797
|
+
const parsed = reviewModeSchema.safeParse(opts.mode);
|
|
2798
|
+
if (!parsed.success) {
|
|
2799
|
+
throw new Error(
|
|
2800
|
+
"Provide a mode: open, readonly, or off. `readonly` keeps existing comments readable and refuses new ones; `off` hides the review layer entirely."
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
const mode = parsed.data;
|
|
2804
|
+
const dir = path14.resolve(opts.dir);
|
|
2805
|
+
const target = resolveArtifactTarget(dir, {
|
|
2806
|
+
apiKey: opts.apiKey,
|
|
2807
|
+
artifact: opts.artifact
|
|
2808
|
+
});
|
|
2809
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2810
|
+
const res = await client.setReviewMode(target.artifactId, mode);
|
|
2811
|
+
if (opts.json) {
|
|
2812
|
+
console.log(JSON.stringify({ reviewMode: res.mode }, null, 2));
|
|
2813
|
+
return;
|
|
2814
|
+
}
|
|
2815
|
+
const url = target.url ?? reviewUrl(
|
|
2816
|
+
target.apiUrl,
|
|
2817
|
+
(await client.getArtifact(target.artifactId)).slug
|
|
2818
|
+
);
|
|
2819
|
+
console.log(`
|
|
2820
|
+
\u2713 ${OUTCOME[res.mode]}
|
|
2821
|
+
${url}
|
|
2822
|
+
`);
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
// src/share.ts
|
|
2826
|
+
import path15 from "path";
|
|
2827
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2828
|
+
async function share(opts) {
|
|
2829
|
+
const dir = path15.resolve(opts.dir);
|
|
2830
|
+
const target = resolveArtifactTarget(dir, {
|
|
2831
|
+
apiKey: opts.apiKey,
|
|
2832
|
+
artifact: opts.artifact
|
|
2833
|
+
});
|
|
2834
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2835
|
+
if (opts.list) {
|
|
2836
|
+
const { grants, visibility } = await client.listGrants(target.artifactId);
|
|
2837
|
+
if (opts.json) {
|
|
2838
|
+
console.log(JSON.stringify({ grants, visibility }, null, 2));
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
console.log(`
|
|
2842
|
+
General access: ${visibility}`);
|
|
2843
|
+
if (grants.length === 0) {
|
|
2844
|
+
console.log("No one has been invited yet.\n");
|
|
2845
|
+
return;
|
|
2846
|
+
}
|
|
2847
|
+
const label = grants.length === 1 ? "person" : "people";
|
|
2848
|
+
console.log(`${grants.length} ${label} with a grant:
|
|
2849
|
+
`);
|
|
2850
|
+
for (const grant of grants) {
|
|
2851
|
+
const who = grant.name && grant.email ? `${grant.name} <${grant.email}>` : grant.name ?? grant.email ?? grant.userId ?? "unknown";
|
|
2852
|
+
console.log(` ${who} \xB7 ${grant.role} \xB7 ${grant.status}`);
|
|
2853
|
+
}
|
|
2854
|
+
console.log("");
|
|
2855
|
+
return;
|
|
2856
|
+
}
|
|
2857
|
+
if (opts.emails.length === 0) {
|
|
2858
|
+
throw new Error(
|
|
2859
|
+
"Provide at least one email to invite, or --list to see current grants."
|
|
2860
|
+
);
|
|
2861
|
+
}
|
|
2862
|
+
for (const email of opts.emails) {
|
|
2863
|
+
if (!EMAIL_RE.test(email)) {
|
|
2864
|
+
throw new Error(`"${email}" is not a valid email address.`);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
const results = [];
|
|
2868
|
+
for (const email of opts.emails) {
|
|
2869
|
+
const grant = await client.createGrant(target.artifactId, { email });
|
|
2870
|
+
results.push({ email, url: grant.url });
|
|
2871
|
+
}
|
|
2872
|
+
if (opts.json) {
|
|
2873
|
+
console.log(JSON.stringify({ invited: results }, null, 2));
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
console.log("");
|
|
2877
|
+
for (const { email, url } of results) {
|
|
2878
|
+
console.log(`\u2713 Invited ${email}`);
|
|
2879
|
+
console.log(` ${url}`);
|
|
2880
|
+
}
|
|
2881
|
+
console.log("");
|
|
2882
|
+
}
|
|
2883
|
+
async function unshare(opts) {
|
|
2884
|
+
const dir = path15.resolve(opts.dir);
|
|
2885
|
+
const target = resolveArtifactTarget(dir, {
|
|
2886
|
+
apiKey: opts.apiKey,
|
|
2887
|
+
artifact: opts.artifact
|
|
2888
|
+
});
|
|
2889
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2890
|
+
const email = opts.email.toLowerCase();
|
|
2891
|
+
const { grants } = await client.listGrants(target.artifactId);
|
|
2892
|
+
const grant = grants.find((g) => g.email?.toLowerCase() === email);
|
|
2893
|
+
if (!grant) {
|
|
2894
|
+
throw new Error(`No active grant for ${opts.email} on this artifact.`);
|
|
2895
|
+
}
|
|
2896
|
+
await client.revokeGrant(target.artifactId, grant.id);
|
|
2897
|
+
if (opts.json) {
|
|
2898
|
+
console.log(JSON.stringify({ email: opts.email, revoked: true }, null, 2));
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2901
|
+
console.log(`
|
|
2902
|
+
\u2713 Revoked access for ${opts.email}
|
|
2903
|
+
`);
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
// src/status.ts
|
|
2907
|
+
import path16 from "path";
|
|
2908
|
+
async function status(opts) {
|
|
2909
|
+
if (!AGENT_STATES.includes(opts.state)) {
|
|
2910
|
+
throw new Error(
|
|
2911
|
+
`State must be one of: ${AGENT_STATES.join(", ")} (got "${opts.state}").`
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
const state = opts.state;
|
|
2915
|
+
const dir = path16.resolve(opts.dir);
|
|
2916
|
+
const target = resolveArtifactTarget(dir, {
|
|
2917
|
+
apiKey: opts.apiKey,
|
|
2918
|
+
artifact: opts.artifact
|
|
2919
|
+
});
|
|
2920
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2921
|
+
const res = await client.agentHeartbeat(target.artifactId, state);
|
|
2922
|
+
if (opts.json) {
|
|
2923
|
+
console.log(
|
|
2924
|
+
JSON.stringify(
|
|
2925
|
+
{
|
|
2926
|
+
artifactId: target.artifactId,
|
|
2927
|
+
state,
|
|
2928
|
+
ttlSeconds: res.ttlSeconds
|
|
2929
|
+
},
|
|
2930
|
+
null,
|
|
2931
|
+
2
|
|
2932
|
+
)
|
|
2933
|
+
);
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2936
|
+
const visible = res.ttlSeconds >= 120 ? `~${Math.round(res.ttlSeconds / 60)}m` : `~${res.ttlSeconds}s`;
|
|
2937
|
+
console.log(`\u2713 Presence: ${state} (visible ${visible})`);
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2529
2940
|
// src/update.ts
|
|
2530
2941
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
2531
2942
|
import { createRequire } from "module";
|
|
2532
2943
|
import { homedir as homedir2 } from "os";
|
|
2533
|
-
import
|
|
2534
|
-
import { z as
|
|
2944
|
+
import path17 from "path";
|
|
2945
|
+
import { z as z20 } from "zod";
|
|
2535
2946
|
var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
|
|
2536
2947
|
var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
|
|
2537
2948
|
var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
|
|
2538
2949
|
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2539
2950
|
var FETCH_TIMEOUT_MS = 2e3;
|
|
2540
2951
|
var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
|
|
2541
|
-
var cacheSchema =
|
|
2542
|
-
checkedAt:
|
|
2543
|
-
cliLatest:
|
|
2544
|
-
skillLatest:
|
|
2545
|
-
version:
|
|
2952
|
+
var cacheSchema = z20.object({
|
|
2953
|
+
checkedAt: z20.number(),
|
|
2954
|
+
cliLatest: z20.string().nullable(),
|
|
2955
|
+
skillLatest: z20.string().nullable(),
|
|
2956
|
+
version: z20.literal(1)
|
|
2546
2957
|
});
|
|
2547
2958
|
function cliVersion() {
|
|
2548
2959
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
2549
2960
|
return pkg.version;
|
|
2550
2961
|
}
|
|
2551
2962
|
function cachePath() {
|
|
2552
|
-
return
|
|
2963
|
+
return path17.join(homedir2(), ".artifactcafe", "update-check.json");
|
|
2553
2964
|
}
|
|
2554
2965
|
function readCache() {
|
|
2555
2966
|
try {
|
|
@@ -2560,7 +2971,7 @@ function readCache() {
|
|
|
2560
2971
|
}
|
|
2561
2972
|
function writeCache(cache) {
|
|
2562
2973
|
try {
|
|
2563
|
-
mkdirSync3(
|
|
2974
|
+
mkdirSync3(path17.dirname(cachePath()), { mode: 448, recursive: true });
|
|
2564
2975
|
writeFileSync3(cachePath(), `${JSON.stringify(cache, null, 2)}
|
|
2565
2976
|
`);
|
|
2566
2977
|
} catch {
|
|
@@ -2597,7 +3008,7 @@ async function fetchText(url) {
|
|
|
2597
3008
|
}
|
|
2598
3009
|
async function fetchLatestVersions(previous) {
|
|
2599
3010
|
const [cli, skill] = await Promise.all([
|
|
2600
|
-
fetchJson(REGISTRY_LATEST_URL).then((body) =>
|
|
3011
|
+
fetchJson(REGISTRY_LATEST_URL).then((body) => z20.object({ version: z20.string() }).parse(body).version).catch(() => null),
|
|
2601
3012
|
fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
|
|
2602
3013
|
]);
|
|
2603
3014
|
return {
|
|
@@ -2613,7 +3024,7 @@ function installedSkillVersions() {
|
|
|
2613
3024
|
const versions = [];
|
|
2614
3025
|
for (const root of roots) {
|
|
2615
3026
|
for (const dir of dirs) {
|
|
2616
|
-
const file =
|
|
3027
|
+
const file = path17.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
|
|
2617
3028
|
if (!existsSync3(file)) {
|
|
2618
3029
|
continue;
|
|
2619
3030
|
}
|
|
@@ -2754,6 +3165,10 @@ var publish2 = defineCommand({
|
|
|
2754
3165
|
description: "Password-protect the artifact (min 6 chars)",
|
|
2755
3166
|
type: "string"
|
|
2756
3167
|
},
|
|
3168
|
+
review: {
|
|
3169
|
+
description: "Review mode: open (default), readonly, or off (plain artifact)",
|
|
3170
|
+
type: "string"
|
|
3171
|
+
},
|
|
2757
3172
|
title: { description: "Artifact title", type: "string" },
|
|
2758
3173
|
workspace: {
|
|
2759
3174
|
description: "Publish into this workspace handle",
|
|
@@ -2773,6 +3188,7 @@ var publish2 = defineCommand({
|
|
|
2773
3188
|
notes: readNotesArg(process.argv.slice(2)),
|
|
2774
3189
|
open: !args["no-open"],
|
|
2775
3190
|
password: args.password,
|
|
3191
|
+
review: args.review,
|
|
2776
3192
|
title: args.title,
|
|
2777
3193
|
workspace: args.workspace
|
|
2778
3194
|
});
|
|
@@ -3076,6 +3492,47 @@ var listen = defineCommand({
|
|
|
3076
3492
|
}
|
|
3077
3493
|
}
|
|
3078
3494
|
});
|
|
3495
|
+
var statusCmd = defineCommand({
|
|
3496
|
+
args: {
|
|
3497
|
+
"api-key": {
|
|
3498
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3499
|
+
type: "string"
|
|
3500
|
+
},
|
|
3501
|
+
artifact: {
|
|
3502
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3503
|
+
type: "string"
|
|
3504
|
+
},
|
|
3505
|
+
dir: {
|
|
3506
|
+
default: ".",
|
|
3507
|
+
description: "Linked directory to authorize from (default: .)",
|
|
3508
|
+
type: "string"
|
|
3509
|
+
},
|
|
3510
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
3511
|
+
state: {
|
|
3512
|
+
description: "What the agent is doing: editing | publishing | listening",
|
|
3513
|
+
required: true,
|
|
3514
|
+
type: "positional"
|
|
3515
|
+
}
|
|
3516
|
+
},
|
|
3517
|
+
meta: {
|
|
3518
|
+
description: "Mark what the agent is doing on the linked artifact",
|
|
3519
|
+
name: "status"
|
|
3520
|
+
},
|
|
3521
|
+
async run({ args }) {
|
|
3522
|
+
try {
|
|
3523
|
+
await status({
|
|
3524
|
+
apiKey: args["api-key"],
|
|
3525
|
+
artifact: args.artifact,
|
|
3526
|
+
dir: args.dir,
|
|
3527
|
+
json: Boolean(args.json),
|
|
3528
|
+
state: args.state
|
|
3529
|
+
});
|
|
3530
|
+
} catch (err) {
|
|
3531
|
+
printCliError(err, Boolean(args.json));
|
|
3532
|
+
process.exit(1);
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3079
3536
|
var protect2 = defineCommand({
|
|
3080
3537
|
args: {
|
|
3081
3538
|
"api-key": {
|
|
@@ -3115,6 +3572,47 @@ var protect2 = defineCommand({
|
|
|
3115
3572
|
}
|
|
3116
3573
|
}
|
|
3117
3574
|
});
|
|
3575
|
+
var review2 = defineCommand({
|
|
3576
|
+
args: {
|
|
3577
|
+
"api-key": {
|
|
3578
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3579
|
+
type: "string"
|
|
3580
|
+
},
|
|
3581
|
+
artifact: {
|
|
3582
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3583
|
+
type: "string"
|
|
3584
|
+
},
|
|
3585
|
+
dir: {
|
|
3586
|
+
default: ".",
|
|
3587
|
+
description: "Published directory",
|
|
3588
|
+
type: "string"
|
|
3589
|
+
},
|
|
3590
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
3591
|
+
mode: {
|
|
3592
|
+
description: "open, readonly, or off (plain artifact)",
|
|
3593
|
+
required: true,
|
|
3594
|
+
type: "positional"
|
|
3595
|
+
}
|
|
3596
|
+
},
|
|
3597
|
+
meta: {
|
|
3598
|
+
description: "Open, close, or freeze review on the linked artifact",
|
|
3599
|
+
name: "review"
|
|
3600
|
+
},
|
|
3601
|
+
async run({ args }) {
|
|
3602
|
+
try {
|
|
3603
|
+
await review({
|
|
3604
|
+
apiKey: args["api-key"],
|
|
3605
|
+
artifact: args.artifact,
|
|
3606
|
+
dir: args.dir,
|
|
3607
|
+
json: Boolean(args.json),
|
|
3608
|
+
mode: args.mode
|
|
3609
|
+
});
|
|
3610
|
+
} catch (err) {
|
|
3611
|
+
printCliError(err, Boolean(args.json));
|
|
3612
|
+
process.exit(1);
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
});
|
|
3118
3616
|
var threadStatusCommand = (action) => defineCommand({
|
|
3119
3617
|
args: {
|
|
3120
3618
|
"api-key": {
|
|
@@ -3361,6 +3859,89 @@ var del = defineCommand({
|
|
|
3361
3859
|
}
|
|
3362
3860
|
}
|
|
3363
3861
|
});
|
|
3862
|
+
var share2 = defineCommand({
|
|
3863
|
+
args: {
|
|
3864
|
+
"api-key": {
|
|
3865
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3866
|
+
type: "string"
|
|
3867
|
+
},
|
|
3868
|
+
artifact: {
|
|
3869
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3870
|
+
type: "string"
|
|
3871
|
+
},
|
|
3872
|
+
dir: {
|
|
3873
|
+
default: ".",
|
|
3874
|
+
description: "Linked directory to authorize from (default: .)",
|
|
3875
|
+
type: "string"
|
|
3876
|
+
},
|
|
3877
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
3878
|
+
list: {
|
|
3879
|
+
description: "List current grants instead of inviting",
|
|
3880
|
+
type: "boolean"
|
|
3881
|
+
}
|
|
3882
|
+
},
|
|
3883
|
+
meta: {
|
|
3884
|
+
description: "Share the linked artifact with people by email",
|
|
3885
|
+
name: "share"
|
|
3886
|
+
},
|
|
3887
|
+
async run({ args }) {
|
|
3888
|
+
try {
|
|
3889
|
+
await share({
|
|
3890
|
+
apiKey: args["api-key"],
|
|
3891
|
+
artifact: args.artifact,
|
|
3892
|
+
dir: args.dir,
|
|
3893
|
+
// citty collects bare positionals (the invited emails) in args._.
|
|
3894
|
+
emails: (args._ ?? []).map(String),
|
|
3895
|
+
json: Boolean(args.json),
|
|
3896
|
+
list: Boolean(args.list)
|
|
3897
|
+
});
|
|
3898
|
+
} catch (err) {
|
|
3899
|
+
printCliError(err, Boolean(args.json));
|
|
3900
|
+
process.exit(1);
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
});
|
|
3904
|
+
var unshare2 = defineCommand({
|
|
3905
|
+
args: {
|
|
3906
|
+
"api-key": {
|
|
3907
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3908
|
+
type: "string"
|
|
3909
|
+
},
|
|
3910
|
+
artifact: {
|
|
3911
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3912
|
+
type: "string"
|
|
3913
|
+
},
|
|
3914
|
+
dir: {
|
|
3915
|
+
default: ".",
|
|
3916
|
+
description: "Linked directory to authorize from (default: .)",
|
|
3917
|
+
type: "string"
|
|
3918
|
+
},
|
|
3919
|
+
email: {
|
|
3920
|
+
description: "Email address to revoke",
|
|
3921
|
+
required: true,
|
|
3922
|
+
type: "positional"
|
|
3923
|
+
},
|
|
3924
|
+
json: { description: "Machine-readable output", type: "boolean" }
|
|
3925
|
+
},
|
|
3926
|
+
meta: {
|
|
3927
|
+
description: "Revoke a person's access to the linked artifact",
|
|
3928
|
+
name: "unshare"
|
|
3929
|
+
},
|
|
3930
|
+
async run({ args }) {
|
|
3931
|
+
try {
|
|
3932
|
+
await unshare({
|
|
3933
|
+
apiKey: args["api-key"],
|
|
3934
|
+
artifact: args.artifact,
|
|
3935
|
+
dir: args.dir,
|
|
3936
|
+
email: args.email,
|
|
3937
|
+
json: Boolean(args.json)
|
|
3938
|
+
});
|
|
3939
|
+
} catch (err) {
|
|
3940
|
+
printCliError(err, Boolean(args.json));
|
|
3941
|
+
process.exit(1);
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
});
|
|
3364
3945
|
var open = defineCommand({
|
|
3365
3946
|
args: {
|
|
3366
3947
|
"api-key": {
|
|
@@ -3448,6 +4029,10 @@ var main = defineCommand({
|
|
|
3448
4029
|
reopen,
|
|
3449
4030
|
reply,
|
|
3450
4031
|
resolve,
|
|
4032
|
+
review: review2,
|
|
4033
|
+
share: share2,
|
|
4034
|
+
status: statusCmd,
|
|
4035
|
+
unshare: unshare2,
|
|
3451
4036
|
whoami: whoami2,
|
|
3452
4037
|
workspaces: workspaces2
|
|
3453
4038
|
}
|