artifact-cafe 0.0.7 → 0.0.9
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 +1408 -427
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ function printBanner() {
|
|
|
30
30
|
console.log();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
// src/
|
|
33
|
+
// src/comment.ts
|
|
34
34
|
import path3 from "path";
|
|
35
35
|
|
|
36
36
|
// ../shared/src/account.ts
|
|
@@ -58,6 +58,9 @@ var apiKeyListResponseSchema = z.object({
|
|
|
58
58
|
var revokeApiKeyResponseSchema = z.object({
|
|
59
59
|
revoked: z.literal(true)
|
|
60
60
|
});
|
|
61
|
+
var notificationPrefsSchema = z.object({
|
|
62
|
+
commentEmailsEnabled: z.boolean()
|
|
63
|
+
});
|
|
61
64
|
var meResponseSchema = z.object({
|
|
62
65
|
// Best-effort profile field from our own user table; null when unknown.
|
|
63
66
|
email: z.string().nullable(),
|
|
@@ -81,106 +84,260 @@ var artifactListResponseSchema = z.object({
|
|
|
81
84
|
});
|
|
82
85
|
|
|
83
86
|
// ../shared/src/comments.ts
|
|
87
|
+
import { z as z3 } from "zod";
|
|
88
|
+
|
|
89
|
+
// ../shared/src/publish.ts
|
|
84
90
|
import { z as z2 } from "zod";
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
function isSafeArtifactPath(p) {
|
|
92
|
+
if (p.length === 0 || p.length > 1024) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (p.includes("\\") || p.includes("\0")) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (p.startsWith("/") || p.startsWith("~")) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
|
|
102
|
+
}
|
|
103
|
+
var manifestFileSchema = z2.object({
|
|
104
|
+
hash: z2.string().regex(/^sha256_[a-f0-9]{64}$/),
|
|
105
|
+
mimeType: z2.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
|
|
106
|
+
path: z2.string().min(1).max(1024).refine(isSafeArtifactPath, {
|
|
107
|
+
message: "path must be a safe relative path"
|
|
108
|
+
}),
|
|
109
|
+
size: z2.number().int().positive()
|
|
110
|
+
});
|
|
111
|
+
var publishSourceSchema = z2.object({
|
|
112
|
+
agent: z2.string().optional(),
|
|
113
|
+
runId: z2.string().optional(),
|
|
114
|
+
type: z2.literal("cli")
|
|
115
|
+
});
|
|
116
|
+
var createPublishSessionSchema = z2.object({
|
|
117
|
+
artifactId: z2.string().nullable(),
|
|
118
|
+
entryPath: z2.string().optional(),
|
|
119
|
+
files: z2.array(manifestFileSchema).min(1).max(1e3),
|
|
120
|
+
// SPEC-2 §6.4 — workspace placement is available only to authenticated
|
|
121
|
+
// members; the API enforces membership and folder ownership. folderId
|
|
122
|
+
// without workspaceId resolves against the account's personal workspace.
|
|
123
|
+
folderId: z2.string().min(1).optional(),
|
|
124
|
+
source: publishSourceSchema,
|
|
125
|
+
title: z2.string().max(200).optional(),
|
|
126
|
+
workspaceId: z2.string().min(1).optional()
|
|
127
|
+
});
|
|
128
|
+
var publishSessionResponseSchema = z2.object({
|
|
129
|
+
artifactId: z2.string(),
|
|
130
|
+
expiresAt: z2.string(),
|
|
131
|
+
filesToUpload: z2.array(
|
|
132
|
+
z2.object({
|
|
133
|
+
method: z2.literal("PUT"),
|
|
134
|
+
path: z2.string(),
|
|
135
|
+
uploadUrl: z2.string().url()
|
|
136
|
+
})
|
|
137
|
+
),
|
|
138
|
+
publishSessionId: z2.string(),
|
|
139
|
+
skippedFiles: z2.array(z2.string()),
|
|
140
|
+
versionId: z2.string()
|
|
141
|
+
});
|
|
142
|
+
var finalizeResponseSchema = z2.object({
|
|
143
|
+
artifactId: z2.string(),
|
|
144
|
+
// API-key publishes create the artifact already claimed (SPEC §5.4) —
|
|
145
|
+
// the CLI uses this to tell "claim URL pending" from "already yours".
|
|
146
|
+
claimed: z2.boolean(),
|
|
147
|
+
// Returned only on first publish of a new artifact (one-time).
|
|
148
|
+
claimUrl: z2.string().url().optional(),
|
|
149
|
+
publishToken: z2.string().optional(),
|
|
150
|
+
url: z2.string().url(),
|
|
151
|
+
versionId: z2.string(),
|
|
152
|
+
versionNumber: z2.number().int().positive()
|
|
153
|
+
});
|
|
154
|
+
var artifactResponseSchema = z2.object({
|
|
155
|
+
claimed: z2.boolean(),
|
|
156
|
+
currentVersion: z2.object({
|
|
157
|
+
entryUrl: z2.string().url(),
|
|
158
|
+
id: z2.string(),
|
|
159
|
+
versionNumber: z2.number().int().positive()
|
|
160
|
+
}).nullable(),
|
|
161
|
+
// SPEC-2 §6.4 — placement + visibility surface on the artifact.
|
|
162
|
+
folderId: z2.string().nullable(),
|
|
163
|
+
id: z2.string(),
|
|
164
|
+
slug: z2.string(),
|
|
165
|
+
title: z2.string(),
|
|
166
|
+
visibility: z2.enum(["public", "unlisted", "workspace"]),
|
|
167
|
+
workspaceId: z2.string().nullable()
|
|
90
168
|
});
|
|
91
|
-
|
|
169
|
+
|
|
170
|
+
// ../shared/src/comments.ts
|
|
171
|
+
var anchorPageSchema = z3.string().max(1024).refine(isSafeArtifactPath, {
|
|
172
|
+
message: "page must be a safe relative path"
|
|
173
|
+
}).optional();
|
|
174
|
+
var elementSnapshotSchema = z3.object({
|
|
175
|
+
classes: z3.string().max(256).optional(),
|
|
176
|
+
id: z3.string().max(256).optional(),
|
|
177
|
+
tag: z3.string().min(1).max(128),
|
|
178
|
+
text: z3.string().max(200).optional()
|
|
179
|
+
});
|
|
180
|
+
var pointAnchorSchema = z3.object({
|
|
92
181
|
element: elementSnapshotSchema.optional(),
|
|
93
182
|
// elementPath is the primary anchor; x/y are offsets within the
|
|
94
183
|
// element's box, normalized 0..1 (SPEC.md §8.1).
|
|
95
|
-
elementPath:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
184
|
+
elementPath: z3.string().max(2048),
|
|
185
|
+
page: anchorPageSchema,
|
|
186
|
+
type: z3.literal("point"),
|
|
187
|
+
viewportHeight: z3.number().int().positive().max(1e5),
|
|
188
|
+
viewportWidth: z3.number().int().positive().max(1e5),
|
|
189
|
+
x: z3.number().min(0).max(1),
|
|
190
|
+
y: z3.number().min(0).max(1)
|
|
191
|
+
});
|
|
192
|
+
var textAnchorSchema = z3.object({
|
|
193
|
+
elementPath: z3.string().max(2048),
|
|
194
|
+
endOffset: z3.number().int().nonnegative().max(1e8),
|
|
195
|
+
page: anchorPageSchema,
|
|
196
|
+
prefix: z3.string().max(200).optional(),
|
|
197
|
+
quote: z3.string().min(1).max(1e3),
|
|
198
|
+
startOffset: z3.number().int().nonnegative().max(1e8),
|
|
199
|
+
suffix: z3.string().max(200).optional(),
|
|
200
|
+
type: z3.literal("text")
|
|
201
|
+
});
|
|
202
|
+
var pageAnchorSchema = z3.object({
|
|
203
|
+
page: anchorPageSchema,
|
|
204
|
+
type: z3.literal("page")
|
|
205
|
+
});
|
|
206
|
+
var anchorSchema = z3.discriminatedUnion("type", [
|
|
112
207
|
pointAnchorSchema,
|
|
113
|
-
textAnchorSchema
|
|
208
|
+
textAnchorSchema,
|
|
209
|
+
pageAnchorSchema
|
|
114
210
|
]);
|
|
115
|
-
var
|
|
116
|
-
|
|
117
|
-
|
|
211
|
+
var displayNameSchema = z3.string().trim().min(1).max(64);
|
|
212
|
+
var guestSchema = z3.object({
|
|
213
|
+
displayName: displayNameSchema,
|
|
214
|
+
guestId: z3.string().min(16)
|
|
118
215
|
});
|
|
119
|
-
var
|
|
216
|
+
var viaLabelSchema = z3.string().trim().min(1).max(64);
|
|
217
|
+
var createCommentThreadSchema = z3.object({
|
|
120
218
|
anchor: anchorSchema,
|
|
121
|
-
body:
|
|
219
|
+
body: z3.string().min(1).max(1e4),
|
|
122
220
|
guest: guestSchema.optional(),
|
|
123
|
-
versionId:
|
|
221
|
+
versionId: z3.string(),
|
|
222
|
+
via: viaLabelSchema.optional()
|
|
223
|
+
});
|
|
224
|
+
var createCommentMessageSchema = z3.object({
|
|
225
|
+
body: z3.string().min(1).max(1e4),
|
|
226
|
+
guest: guestSchema.optional(),
|
|
227
|
+
via: viaLabelSchema.optional()
|
|
124
228
|
});
|
|
125
|
-
var
|
|
126
|
-
body:
|
|
229
|
+
var editCommentMessageSchema = z3.object({
|
|
230
|
+
body: z3.string().min(1).max(1e4),
|
|
127
231
|
guest: guestSchema.optional()
|
|
128
232
|
});
|
|
129
|
-
var threadActionSchema =
|
|
233
|
+
var threadActionSchema = z3.object({
|
|
130
234
|
guest: guestSchema.optional()
|
|
131
235
|
});
|
|
132
|
-
var
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
236
|
+
var REACTION_EMOJI = [
|
|
237
|
+
"\u{1F44D}",
|
|
238
|
+
"\u{1F44E}",
|
|
239
|
+
"\u2764\uFE0F",
|
|
240
|
+
"\u{1F389}",
|
|
241
|
+
"\u{1F4A1}",
|
|
242
|
+
"\u{1F440}",
|
|
243
|
+
"\u{1F680}",
|
|
244
|
+
"\u2615"
|
|
245
|
+
];
|
|
246
|
+
var reactionEmojiSchema = z3.enum(REACTION_EMOJI);
|
|
247
|
+
var toggleReactionSchema = z3.object({
|
|
248
|
+
emoji: reactionEmojiSchema,
|
|
249
|
+
guest: guestSchema.optional()
|
|
138
250
|
});
|
|
139
|
-
var
|
|
251
|
+
var commentAuthorSchema = z3.object({
|
|
252
|
+
avatarUrl: z3.string().nullable(),
|
|
253
|
+
displayName: z3.string(),
|
|
254
|
+
guestId: z3.string().nullable(),
|
|
255
|
+
isViewer: z3.boolean(),
|
|
256
|
+
kind: z3.enum(["member", "guest"]),
|
|
257
|
+
// Optional "via" attribution (§6.4). Null for plain comments; a bounded
|
|
258
|
+
// label the shell appends after the display name.
|
|
259
|
+
via: z3.string().nullable()
|
|
260
|
+
});
|
|
261
|
+
var messageReactionSchema = z3.object({
|
|
262
|
+
emoji: z3.string(),
|
|
263
|
+
reactors: z3.array(commentAuthorSchema)
|
|
264
|
+
});
|
|
265
|
+
var commentMessageSchema = z3.object({
|
|
140
266
|
author: commentAuthorSchema,
|
|
141
|
-
body
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
267
|
+
// Empty string for a deleted message — the body is stripped server-side
|
|
268
|
+
// (§6.7.1 tombstone), never sent to the client and hidden there.
|
|
269
|
+
body: z3.string(),
|
|
270
|
+
createdAt: z3.string(),
|
|
271
|
+
// Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
|
|
272
|
+
// the shell renders "Comment deleted" instead of the (absent) body.
|
|
273
|
+
deleted: z3.boolean(),
|
|
274
|
+
// Last edit time (§6.7.1), or null if never edited — drives the "edited"
|
|
275
|
+
// badge. No edit history is kept.
|
|
276
|
+
editedAt: z3.string().nullable(),
|
|
277
|
+
id: z3.string(),
|
|
278
|
+
// Defaulted, not required: a new CLI parsing an older server's
|
|
279
|
+
// response must not fail on the missing key.
|
|
280
|
+
reactions: z3.array(messageReactionSchema).default([])
|
|
281
|
+
});
|
|
282
|
+
var commentThreadSchema = z3.object({
|
|
146
283
|
anchor: anchorSchema,
|
|
147
|
-
artifactId:
|
|
148
|
-
createdAt:
|
|
284
|
+
artifactId: z3.string(),
|
|
285
|
+
createdAt: z3.string(),
|
|
149
286
|
createdBy: commentAuthorSchema,
|
|
150
|
-
id:
|
|
151
|
-
messages:
|
|
152
|
-
status:
|
|
153
|
-
versionId:
|
|
154
|
-
});
|
|
155
|
-
var commentThreadListSchema =
|
|
156
|
-
threads:
|
|
157
|
-
});
|
|
158
|
-
var commentExportQuerySchema =
|
|
159
|
-
status:
|
|
160
|
-
version:
|
|
161
|
-
});
|
|
162
|
-
var
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
287
|
+
id: z3.string(),
|
|
288
|
+
messages: z3.array(commentMessageSchema),
|
|
289
|
+
status: z3.enum(["open", "resolved"]),
|
|
290
|
+
versionId: z3.string()
|
|
291
|
+
});
|
|
292
|
+
var commentThreadListSchema = z3.object({
|
|
293
|
+
threads: z3.array(commentThreadSchema)
|
|
294
|
+
});
|
|
295
|
+
var commentExportQuerySchema = z3.object({
|
|
296
|
+
status: z3.enum(["open", "resolved", "all"]).default("open"),
|
|
297
|
+
version: z3.enum(["current", "all"]).default("current")
|
|
298
|
+
});
|
|
299
|
+
var exportReactionSchema = z3.object({
|
|
300
|
+
count: z3.number().int().positive(),
|
|
301
|
+
emoji: z3.string()
|
|
302
|
+
});
|
|
303
|
+
var commentExportReplySchema = z3.object({
|
|
304
|
+
author: z3.string(),
|
|
305
|
+
authorKind: z3.enum(["member", "guest"]),
|
|
306
|
+
body: z3.string(),
|
|
307
|
+
createdAt: z3.string(),
|
|
308
|
+
// Soft-delete tombstone (§6.7.1): body is "" when true.
|
|
309
|
+
deleted: z3.boolean(),
|
|
310
|
+
editedAt: z3.string().nullable(),
|
|
311
|
+
// The message id — what `edit`/`delete` target (§5.1).
|
|
312
|
+
id: z3.string(),
|
|
313
|
+
reactions: z3.array(exportReactionSchema).optional()
|
|
314
|
+
});
|
|
315
|
+
var commentExportThreadSchema = z3.object({
|
|
169
316
|
anchor: anchorSchema,
|
|
170
|
-
author:
|
|
171
|
-
authorKind:
|
|
172
|
-
body:
|
|
173
|
-
createdAt:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
317
|
+
author: z3.string(),
|
|
318
|
+
authorKind: z3.enum(["member", "guest"]),
|
|
319
|
+
body: z3.string(),
|
|
320
|
+
createdAt: z3.string(),
|
|
321
|
+
deleted: z3.boolean(),
|
|
322
|
+
editedAt: z3.string().nullable(),
|
|
323
|
+
id: z3.string(),
|
|
324
|
+
// The opening message's id (distinct from the thread `id`) — `edit`/
|
|
325
|
+
// `delete` operate on messages, so the CLI needs it to target the first
|
|
326
|
+
// comment, not just the replies.
|
|
327
|
+
messageId: z3.string(),
|
|
328
|
+
reactions: z3.array(exportReactionSchema).optional(),
|
|
329
|
+
replies: z3.array(commentExportReplySchema),
|
|
330
|
+
status: z3.enum(["open", "resolved"]),
|
|
331
|
+
versionNumber: z3.number().int().positive()
|
|
332
|
+
});
|
|
333
|
+
var commentExportSchema = z3.object({
|
|
334
|
+
artifactId: z3.string(),
|
|
335
|
+
comments: z3.array(commentExportThreadSchema),
|
|
182
336
|
// The artifact's current version (null before the first finalize).
|
|
183
|
-
versionNumber:
|
|
337
|
+
versionNumber: z3.number().int().positive().nullable()
|
|
338
|
+
});
|
|
339
|
+
var commentReadResponseSchema = z3.object({
|
|
340
|
+
lastSeenAt: z3.string()
|
|
184
341
|
});
|
|
185
342
|
function elementLabel(el) {
|
|
186
343
|
if (el.id) {
|
|
@@ -191,145 +348,163 @@ function elementLabel(el) {
|
|
|
191
348
|
}
|
|
192
349
|
|
|
193
350
|
// ../shared/src/errors.ts
|
|
194
|
-
import { z as
|
|
195
|
-
var apiErrorSchema =
|
|
196
|
-
code:
|
|
197
|
-
docsUrl:
|
|
198
|
-
message:
|
|
199
|
-
retryAfter:
|
|
351
|
+
import { z as z4 } from "zod";
|
|
352
|
+
var apiErrorSchema = z4.object({
|
|
353
|
+
code: z4.string(),
|
|
354
|
+
docsUrl: z4.string().url(),
|
|
355
|
+
message: z4.string(),
|
|
356
|
+
retryAfter: z4.number().int().positive().optional()
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// ../shared/src/feedback.ts
|
|
360
|
+
import { z as z5 } from "zod";
|
|
361
|
+
var feedbackRequestSchema = z5.object({
|
|
362
|
+
email: z5.string().trim().toLowerCase().email().optional().or(z5.literal("")),
|
|
363
|
+
message: z5.string().trim().min(1).max(4e3)
|
|
364
|
+
});
|
|
365
|
+
var feedbackResponseSchema = z5.object({
|
|
366
|
+
sent: z5.literal(true)
|
|
200
367
|
});
|
|
201
368
|
|
|
202
369
|
// ../shared/src/me.ts
|
|
203
|
-
import { z as
|
|
204
|
-
var ownArtifactSchema =
|
|
205
|
-
createdAt:
|
|
206
|
-
currentVersionNumber:
|
|
370
|
+
import { z as z6 } from "zod";
|
|
371
|
+
var ownArtifactSchema = z6.object({
|
|
372
|
+
createdAt: z6.string(),
|
|
373
|
+
currentVersionNumber: z6.number().nullable(),
|
|
207
374
|
// Owned artifacts normally never expire; non-null only while an
|
|
208
375
|
// expiry is still set (e.g. an unclaimed publish surfaced here later).
|
|
209
|
-
expiresAt:
|
|
210
|
-
// SPEC-2 §8.4 — the containing folder
|
|
211
|
-
//
|
|
212
|
-
folderName
|
|
376
|
+
expiresAt: z6.string().nullable(),
|
|
377
|
+
// SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
|
|
378
|
+
// both null for personal artifacts. `folderId` drives the dashboard's
|
|
379
|
+
// move control and folder filter; `folderName` is the display label.
|
|
380
|
+
folderId: z6.string().nullable(),
|
|
381
|
+
folderName: z6.string().nullable(),
|
|
213
382
|
// Whether a publish token is still active — revocable from the
|
|
214
383
|
// dashboard (§6.18); the token itself is never returned.
|
|
215
|
-
hasPublishToken:
|
|
216
|
-
id:
|
|
217
|
-
openThreads:
|
|
218
|
-
protected:
|
|
219
|
-
slug:
|
|
220
|
-
title:
|
|
221
|
-
totalViews:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
});
|
|
229
|
-
var
|
|
230
|
-
|
|
384
|
+
hasPublishToken: z6.boolean(),
|
|
385
|
+
id: z6.string(),
|
|
386
|
+
openThreads: z6.number(),
|
|
387
|
+
protected: z6.boolean(),
|
|
388
|
+
slug: z6.string(),
|
|
389
|
+
title: z6.string(),
|
|
390
|
+
totalViews: z6.number(),
|
|
391
|
+
// SPEC §6.20 — messages by others newer than the viewer's last-seen
|
|
392
|
+
// watermark. 0 for signed-out/API-key-less contexts.
|
|
393
|
+
unreadComments: z6.number(),
|
|
394
|
+
updatedAt: z6.string(),
|
|
395
|
+
url: z6.string(),
|
|
396
|
+
versionCount: z6.number()
|
|
397
|
+
});
|
|
398
|
+
var ownArtifactsResponseSchema = z6.object({
|
|
399
|
+
artifacts: z6.array(ownArtifactSchema)
|
|
400
|
+
});
|
|
401
|
+
var revokePublishTokenResponseSchema = z6.object({
|
|
402
|
+
revoked: z6.boolean()
|
|
231
403
|
});
|
|
232
404
|
|
|
233
405
|
// ../shared/src/owner.ts
|
|
234
|
-
import { z as
|
|
406
|
+
import { z as z8 } from "zod";
|
|
235
407
|
|
|
236
408
|
// ../shared/src/workspaces.ts
|
|
237
|
-
import { z as
|
|
409
|
+
import { z as z7 } from "zod";
|
|
238
410
|
var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
|
|
239
|
-
var workspaceRoleSchema =
|
|
240
|
-
var workspaceInviteRoleSchema =
|
|
241
|
-
var workspaceHandleSchema =
|
|
411
|
+
var workspaceRoleSchema = z7.enum(["owner", "admin", "member"]);
|
|
412
|
+
var workspaceInviteRoleSchema = z7.enum(["admin", "member"]);
|
|
413
|
+
var workspaceHandleSchema = z7.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
|
|
242
414
|
message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
243
415
|
});
|
|
244
|
-
var createWorkspaceSchema =
|
|
416
|
+
var createWorkspaceSchema = z7.object({
|
|
245
417
|
handle: workspaceHandleSchema.optional(),
|
|
246
|
-
name:
|
|
418
|
+
name: z7.string().trim().min(1).max(100)
|
|
247
419
|
});
|
|
248
|
-
var updateWorkspaceSchema =
|
|
420
|
+
var updateWorkspaceSchema = z7.object({
|
|
249
421
|
handle: workspaceHandleSchema.optional(),
|
|
250
|
-
name:
|
|
422
|
+
name: z7.string().trim().min(1).max(100).optional()
|
|
251
423
|
}).refine((body) => body.handle !== void 0 || body.name !== void 0, {
|
|
252
424
|
message: "provide at least one of name or handle"
|
|
253
425
|
});
|
|
254
|
-
var workspaceSchema =
|
|
255
|
-
createdAt:
|
|
256
|
-
handle:
|
|
257
|
-
id:
|
|
258
|
-
|
|
426
|
+
var workspaceSchema = z7.object({
|
|
427
|
+
createdAt: z7.string(),
|
|
428
|
+
handle: z7.string(),
|
|
429
|
+
id: z7.string(),
|
|
430
|
+
// Auto-provisioned solo workspace, one per user: non-deletable, takes
|
|
431
|
+
// no members, renameable like any workspace.
|
|
432
|
+
isPersonal: z7.boolean(),
|
|
433
|
+
name: z7.string(),
|
|
259
434
|
role: workspaceRoleSchema.nullable(),
|
|
260
|
-
updatedAt:
|
|
435
|
+
updatedAt: z7.string()
|
|
261
436
|
});
|
|
262
|
-
var workspaceListResponseSchema =
|
|
263
|
-
workspaces:
|
|
437
|
+
var workspaceListResponseSchema = z7.object({
|
|
438
|
+
workspaces: z7.array(workspaceSchema)
|
|
264
439
|
});
|
|
265
|
-
var deleteWorkspaceResponseSchema =
|
|
266
|
-
deleted:
|
|
440
|
+
var deleteWorkspaceResponseSchema = z7.object({
|
|
441
|
+
deleted: z7.literal(true)
|
|
267
442
|
});
|
|
268
|
-
var workspaceMemberSchema =
|
|
269
|
-
createdAt:
|
|
270
|
-
email:
|
|
271
|
-
image:
|
|
272
|
-
name:
|
|
443
|
+
var workspaceMemberSchema = z7.object({
|
|
444
|
+
createdAt: z7.string(),
|
|
445
|
+
email: z7.string().email(),
|
|
446
|
+
image: z7.string().nullable(),
|
|
447
|
+
name: z7.string(),
|
|
273
448
|
role: workspaceRoleSchema,
|
|
274
|
-
userId:
|
|
449
|
+
userId: z7.string()
|
|
275
450
|
});
|
|
276
|
-
var workspaceMemberListResponseSchema =
|
|
277
|
-
members:
|
|
451
|
+
var workspaceMemberListResponseSchema = z7.object({
|
|
452
|
+
members: z7.array(workspaceMemberSchema)
|
|
278
453
|
});
|
|
279
|
-
var updateWorkspaceMemberSchema =
|
|
454
|
+
var updateWorkspaceMemberSchema = z7.object({
|
|
280
455
|
role: workspaceRoleSchema
|
|
281
456
|
});
|
|
282
|
-
var removeWorkspaceMemberResponseSchema =
|
|
283
|
-
removed:
|
|
457
|
+
var removeWorkspaceMemberResponseSchema = z7.object({
|
|
458
|
+
removed: z7.literal(true)
|
|
284
459
|
});
|
|
285
|
-
var createWorkspaceInviteSchema =
|
|
286
|
-
email:
|
|
287
|
-
expiresAt:
|
|
288
|
-
maxUses:
|
|
460
|
+
var createWorkspaceInviteSchema = z7.object({
|
|
461
|
+
email: z7.string().trim().toLowerCase().email().optional(),
|
|
462
|
+
expiresAt: z7.string().datetime(),
|
|
463
|
+
maxUses: z7.number().int().positive().optional(),
|
|
289
464
|
role: workspaceInviteRoleSchema
|
|
290
465
|
});
|
|
291
|
-
var workspaceInviteSchema =
|
|
292
|
-
createdAt:
|
|
293
|
-
email:
|
|
294
|
-
expiresAt:
|
|
295
|
-
id:
|
|
296
|
-
maxUses:
|
|
466
|
+
var workspaceInviteSchema = z7.object({
|
|
467
|
+
createdAt: z7.string(),
|
|
468
|
+
email: z7.string().email().nullable(),
|
|
469
|
+
expiresAt: z7.string(),
|
|
470
|
+
id: z7.string(),
|
|
471
|
+
maxUses: z7.number().int().positive().nullable(),
|
|
297
472
|
role: workspaceInviteRoleSchema,
|
|
298
|
-
tokenPrefix:
|
|
299
|
-
useCount:
|
|
473
|
+
tokenPrefix: z7.string(),
|
|
474
|
+
useCount: z7.number().int().nonnegative()
|
|
300
475
|
});
|
|
301
476
|
var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
|
|
302
|
-
url:
|
|
477
|
+
url: z7.string().url()
|
|
303
478
|
});
|
|
304
|
-
var workspaceInviteListResponseSchema =
|
|
305
|
-
invites:
|
|
479
|
+
var workspaceInviteListResponseSchema = z7.object({
|
|
480
|
+
invites: z7.array(workspaceInviteSchema)
|
|
306
481
|
});
|
|
307
|
-
var revokeWorkspaceInviteResponseSchema =
|
|
308
|
-
revoked:
|
|
482
|
+
var revokeWorkspaceInviteResponseSchema = z7.object({
|
|
483
|
+
revoked: z7.literal(true)
|
|
309
484
|
});
|
|
310
|
-
var acceptWorkspaceInviteSchema =
|
|
311
|
-
token:
|
|
485
|
+
var acceptWorkspaceInviteSchema = z7.object({
|
|
486
|
+
token: z7.string().min(1)
|
|
312
487
|
});
|
|
313
|
-
var acceptWorkspaceInviteResponseSchema =
|
|
488
|
+
var acceptWorkspaceInviteResponseSchema = z7.object({
|
|
314
489
|
role: workspaceRoleSchema,
|
|
315
490
|
workspace: workspaceSchema
|
|
316
491
|
});
|
|
317
|
-
var folderVisibilitySchema =
|
|
318
|
-
var artifactVisibilitySchema =
|
|
492
|
+
var folderVisibilitySchema = z7.enum(["public", "workspace"]);
|
|
493
|
+
var artifactVisibilitySchema = z7.enum([
|
|
319
494
|
"public",
|
|
320
495
|
"unlisted",
|
|
321
496
|
"workspace"
|
|
322
497
|
]);
|
|
323
|
-
var createFolderSchema =
|
|
324
|
-
name:
|
|
498
|
+
var createFolderSchema = z7.object({
|
|
499
|
+
name: z7.string().trim().min(1).max(100),
|
|
325
500
|
visibility: folderVisibilitySchema
|
|
326
501
|
});
|
|
327
|
-
var updateFolderSchema =
|
|
502
|
+
var updateFolderSchema = z7.object({
|
|
328
503
|
// With `visibility`: bulk-update the folder's artifacts to match
|
|
329
504
|
// (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
|
|
330
|
-
cascade:
|
|
331
|
-
name:
|
|
332
|
-
sortOrder:
|
|
505
|
+
cascade: z7.boolean().optional(),
|
|
506
|
+
name: z7.string().trim().min(1).max(100).optional(),
|
|
507
|
+
sortOrder: z7.number().int().min(0).optional(),
|
|
333
508
|
visibility: folderVisibilitySchema.optional()
|
|
334
509
|
}).refine(
|
|
335
510
|
(body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0,
|
|
@@ -337,248 +512,168 @@ var updateFolderSchema = z5.object({
|
|
|
337
512
|
).refine((body) => !body.cascade || body.visibility !== void 0, {
|
|
338
513
|
message: "cascade requires visibility"
|
|
339
514
|
});
|
|
340
|
-
var folderSchema =
|
|
341
|
-
createdAt:
|
|
342
|
-
id:
|
|
343
|
-
name:
|
|
515
|
+
var folderSchema = z7.object({
|
|
516
|
+
createdAt: z7.string(),
|
|
517
|
+
id: z7.string(),
|
|
518
|
+
name: z7.string(),
|
|
344
519
|
// Permalink segment for the public folder page (/w/{handle}/f/{slug}).
|
|
345
520
|
// Server-derived from the name; renames regenerate it.
|
|
346
|
-
slug:
|
|
347
|
-
sortOrder:
|
|
348
|
-
updatedAt:
|
|
521
|
+
slug: z7.string(),
|
|
522
|
+
sortOrder: z7.number().int(),
|
|
523
|
+
updatedAt: z7.string(),
|
|
349
524
|
visibility: folderVisibilitySchema,
|
|
350
|
-
workspaceId:
|
|
351
|
-
});
|
|
352
|
-
var folderListResponseSchema =
|
|
353
|
-
folders:
|
|
354
|
-
});
|
|
355
|
-
var deleteFolderResponseSchema =
|
|
356
|
-
deleted:
|
|
357
|
-
});
|
|
358
|
-
var workspaceArtifactSchema =
|
|
359
|
-
createdAt:
|
|
360
|
-
currentVersionNumber:
|
|
361
|
-
folderId:
|
|
362
|
-
id:
|
|
363
|
-
openCommentCount:
|
|
364
|
-
slug:
|
|
365
|
-
title:
|
|
366
|
-
updatedAt:
|
|
367
|
-
url:
|
|
368
|
-
versionCount:
|
|
525
|
+
workspaceId: z7.string()
|
|
526
|
+
});
|
|
527
|
+
var folderListResponseSchema = z7.object({
|
|
528
|
+
folders: z7.array(folderSchema)
|
|
529
|
+
});
|
|
530
|
+
var deleteFolderResponseSchema = z7.object({
|
|
531
|
+
deleted: z7.literal(true)
|
|
532
|
+
});
|
|
533
|
+
var workspaceArtifactSchema = z7.object({
|
|
534
|
+
createdAt: z7.string(),
|
|
535
|
+
currentVersionNumber: z7.number().int().nullable(),
|
|
536
|
+
folderId: z7.string().nullable(),
|
|
537
|
+
id: z7.string(),
|
|
538
|
+
openCommentCount: z7.number().int().nonnegative(),
|
|
539
|
+
slug: z7.string(),
|
|
540
|
+
title: z7.string(),
|
|
541
|
+
updatedAt: z7.string(),
|
|
542
|
+
url: z7.string().url(),
|
|
543
|
+
versionCount: z7.number().int().nonnegative(),
|
|
369
544
|
visibility: artifactVisibilitySchema
|
|
370
545
|
});
|
|
371
|
-
var workspaceArtifactListResponseSchema =
|
|
372
|
-
artifacts:
|
|
546
|
+
var workspaceArtifactListResponseSchema = z7.object({
|
|
547
|
+
artifacts: z7.array(workspaceArtifactSchema)
|
|
373
548
|
});
|
|
374
549
|
|
|
375
550
|
// ../shared/src/owner.ts
|
|
376
551
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
|
|
377
|
-
var claimResponseSchema =
|
|
378
|
-
artifactId:
|
|
379
|
-
slug:
|
|
380
|
-
title:
|
|
381
|
-
url:
|
|
552
|
+
var claimResponseSchema = z8.object({
|
|
553
|
+
artifactId: z8.string(),
|
|
554
|
+
slug: z8.string(),
|
|
555
|
+
title: z8.string(),
|
|
556
|
+
url: z8.string().url()
|
|
382
557
|
});
|
|
383
|
-
var updateArtifactSchema =
|
|
558
|
+
var updateArtifactSchema = z8.object({
|
|
384
559
|
// SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
|
|
385
560
|
// folder; `workspaceId: null` moves the artifact out of its workspace
|
|
386
561
|
// (§5.5 ownership transfer).
|
|
387
|
-
folderId:
|
|
388
|
-
slug:
|
|
562
|
+
folderId: z8.string().min(1).nullable().optional(),
|
|
563
|
+
slug: z8.string().min(3).max(63).regex(SLUG_PATTERN, {
|
|
389
564
|
message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
390
565
|
}).optional(),
|
|
391
|
-
title:
|
|
566
|
+
title: z8.string().min(1).max(200).optional(),
|
|
392
567
|
visibility: artifactVisibilitySchema.optional(),
|
|
393
|
-
workspaceId:
|
|
568
|
+
workspaceId: z8.string().min(1).nullable().optional()
|
|
394
569
|
}).refine(
|
|
395
570
|
(body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0,
|
|
396
571
|
{
|
|
397
572
|
message: "provide at least one of title, slug, visibility, folderId, workspaceId"
|
|
398
573
|
}
|
|
399
574
|
);
|
|
400
|
-
var deleteArtifactResponseSchema =
|
|
401
|
-
deleted:
|
|
575
|
+
var deleteArtifactResponseSchema = z8.object({
|
|
576
|
+
deleted: z8.literal(true)
|
|
402
577
|
});
|
|
403
578
|
|
|
404
579
|
// ../shared/src/presence.ts
|
|
405
|
-
import { z as
|
|
580
|
+
import { z as z9 } from "zod";
|
|
406
581
|
var AGENT_PRESENCE = {
|
|
407
582
|
heartbeatSeconds: 15,
|
|
408
583
|
shellPollSeconds: 15,
|
|
409
584
|
ttlSeconds: 45
|
|
410
585
|
};
|
|
411
|
-
var agentHeartbeatResponseSchema =
|
|
412
|
-
listening:
|
|
413
|
-
ttlSeconds:
|
|
586
|
+
var agentHeartbeatResponseSchema = z9.object({
|
|
587
|
+
listening: z9.literal(true),
|
|
588
|
+
ttlSeconds: z9.number().int().positive()
|
|
414
589
|
});
|
|
415
|
-
var agentPresenceSchema =
|
|
416
|
-
listening:
|
|
417
|
-
versionNumber:
|
|
590
|
+
var agentPresenceSchema = z9.object({
|
|
591
|
+
listening: z9.boolean(),
|
|
592
|
+
versionNumber: z9.number().int().positive().nullable()
|
|
418
593
|
});
|
|
419
594
|
|
|
420
595
|
// ../shared/src/protection.ts
|
|
421
|
-
import { z as
|
|
596
|
+
import { z as z10 } from "zod";
|
|
422
597
|
var PASSWORD_MIN = 6;
|
|
423
598
|
var PASSWORD_MAX = 128;
|
|
424
|
-
var gatePasswordSchema =
|
|
425
|
-
var setProtectionSchema =
|
|
599
|
+
var gatePasswordSchema = z10.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
|
|
600
|
+
var setProtectionSchema = z10.object({
|
|
426
601
|
password: gatePasswordSchema.nullable()
|
|
427
602
|
});
|
|
428
|
-
var protectionResponseSchema =
|
|
429
|
-
protected:
|
|
430
|
-
});
|
|
431
|
-
var unlockSchema = z8.object({
|
|
432
|
-
password: z8.string().min(1).max(1024)
|
|
433
|
-
});
|
|
434
|
-
var unlockResponseSchema = z8.object({
|
|
435
|
-
unlocked: z8.literal(true)
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
// ../shared/src/publish.ts
|
|
439
|
-
import { z as z9 } from "zod";
|
|
440
|
-
function isSafeArtifactPath(p) {
|
|
441
|
-
if (p.length === 0 || p.length > 1024) {
|
|
442
|
-
return false;
|
|
443
|
-
}
|
|
444
|
-
if (p.includes("\\") || p.includes("\0")) {
|
|
445
|
-
return false;
|
|
446
|
-
}
|
|
447
|
-
if (p.startsWith("/") || p.startsWith("~")) {
|
|
448
|
-
return false;
|
|
449
|
-
}
|
|
450
|
-
return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
|
|
451
|
-
}
|
|
452
|
-
var manifestFileSchema = z9.object({
|
|
453
|
-
hash: z9.string().regex(/^sha256_[a-f0-9]{64}$/),
|
|
454
|
-
mimeType: z9.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
|
|
455
|
-
path: z9.string().min(1).max(1024).refine(isSafeArtifactPath, {
|
|
456
|
-
message: "path must be a safe relative path"
|
|
457
|
-
}),
|
|
458
|
-
size: z9.number().int().positive()
|
|
459
|
-
});
|
|
460
|
-
var publishSourceSchema = z9.object({
|
|
461
|
-
agent: z9.string().optional(),
|
|
462
|
-
runId: z9.string().optional(),
|
|
463
|
-
type: z9.literal("cli")
|
|
603
|
+
var protectionResponseSchema = z10.object({
|
|
604
|
+
protected: z10.boolean()
|
|
464
605
|
});
|
|
465
|
-
var
|
|
466
|
-
|
|
467
|
-
entryPath: z9.string().optional(),
|
|
468
|
-
files: z9.array(manifestFileSchema).min(1).max(1e3),
|
|
469
|
-
// SPEC-2 §6.4 — workspace placement is available only to authenticated
|
|
470
|
-
// members; the API enforces membership and folder ownership.
|
|
471
|
-
folderId: z9.string().min(1).optional(),
|
|
472
|
-
source: publishSourceSchema,
|
|
473
|
-
title: z9.string().max(200).optional(),
|
|
474
|
-
workspaceId: z9.string().min(1).optional()
|
|
475
|
-
});
|
|
476
|
-
var publishSessionResponseSchema = z9.object({
|
|
477
|
-
artifactId: z9.string(),
|
|
478
|
-
expiresAt: z9.string(),
|
|
479
|
-
filesToUpload: z9.array(
|
|
480
|
-
z9.object({
|
|
481
|
-
method: z9.literal("PUT"),
|
|
482
|
-
path: z9.string(),
|
|
483
|
-
uploadUrl: z9.string().url()
|
|
484
|
-
})
|
|
485
|
-
),
|
|
486
|
-
publishSessionId: z9.string(),
|
|
487
|
-
skippedFiles: z9.array(z9.string()),
|
|
488
|
-
versionId: z9.string()
|
|
606
|
+
var unlockSchema = z10.object({
|
|
607
|
+
password: z10.string().min(1).max(1024)
|
|
489
608
|
});
|
|
490
|
-
var
|
|
491
|
-
|
|
492
|
-
// API-key publishes create the artifact already claimed (SPEC §5.4) —
|
|
493
|
-
// the CLI uses this to tell "claim URL pending" from "already yours".
|
|
494
|
-
claimed: z9.boolean(),
|
|
495
|
-
// Returned only on first publish of a new artifact (one-time).
|
|
496
|
-
claimUrl: z9.string().url().optional(),
|
|
497
|
-
publishToken: z9.string().optional(),
|
|
498
|
-
url: z9.string().url(),
|
|
499
|
-
versionId: z9.string(),
|
|
500
|
-
versionNumber: z9.number().int().positive()
|
|
501
|
-
});
|
|
502
|
-
var artifactResponseSchema = z9.object({
|
|
503
|
-
claimed: z9.boolean(),
|
|
504
|
-
currentVersion: z9.object({
|
|
505
|
-
entryUrl: z9.string().url(),
|
|
506
|
-
id: z9.string(),
|
|
507
|
-
versionNumber: z9.number().int().positive()
|
|
508
|
-
}).nullable(),
|
|
509
|
-
// SPEC-2 §6.4 — placement + visibility surface on the artifact.
|
|
510
|
-
folderId: z9.string().nullable(),
|
|
511
|
-
id: z9.string(),
|
|
512
|
-
slug: z9.string(),
|
|
513
|
-
title: z9.string(),
|
|
514
|
-
visibility: z9.enum(["public", "unlisted", "workspace"]),
|
|
515
|
-
workspaceId: z9.string().nullable()
|
|
609
|
+
var unlockResponseSchema = z10.object({
|
|
610
|
+
unlocked: z10.literal(true)
|
|
516
611
|
});
|
|
517
612
|
|
|
518
613
|
// ../shared/src/report.ts
|
|
519
|
-
import { z as
|
|
520
|
-
var reportRequestSchema =
|
|
521
|
-
reason:
|
|
614
|
+
import { z as z11 } from "zod";
|
|
615
|
+
var reportRequestSchema = z11.object({
|
|
616
|
+
reason: z11.string().max(1e3).optional()
|
|
522
617
|
});
|
|
523
|
-
var reportResponseSchema =
|
|
524
|
-
reported:
|
|
618
|
+
var reportResponseSchema = z11.object({
|
|
619
|
+
reported: z11.literal(true)
|
|
525
620
|
});
|
|
526
621
|
|
|
527
622
|
// ../shared/src/stats.ts
|
|
528
|
-
import { z as
|
|
529
|
-
var avgViewSecondsSchema =
|
|
530
|
-
var versionViewStatsSchema =
|
|
623
|
+
import { z as z12 } from "zod";
|
|
624
|
+
var avgViewSecondsSchema = z12.number().int().nonnegative().nullable();
|
|
625
|
+
var versionViewStatsSchema = z12.object({
|
|
531
626
|
avgViewSeconds: avgViewSecondsSchema,
|
|
532
627
|
// Views from inside the review shell's iframe vs. direct visits to
|
|
533
628
|
// the render URL. views = embeddedViews + directViews.
|
|
534
|
-
directViews:
|
|
535
|
-
embeddedViews:
|
|
629
|
+
directViews: z12.number().int().nonnegative(),
|
|
630
|
+
embeddedViews: z12.number().int().nonnegative(),
|
|
536
631
|
// Distinct hashed viewer IPs across the version's lifetime.
|
|
537
|
-
uniqueViewers:
|
|
538
|
-
versionNumber:
|
|
539
|
-
views:
|
|
632
|
+
uniqueViewers: z12.number().int().nonnegative(),
|
|
633
|
+
versionNumber: z12.number().int().positive(),
|
|
634
|
+
views: z12.number().int().nonnegative()
|
|
540
635
|
});
|
|
541
|
-
var dailyViewsSchema =
|
|
636
|
+
var dailyViewsSchema = z12.object({
|
|
542
637
|
avgViewSeconds: avgViewSecondsSchema,
|
|
543
638
|
// UTC day, YYYY-MM-DD.
|
|
544
|
-
day:
|
|
545
|
-
uniqueViewers:
|
|
546
|
-
views:
|
|
639
|
+
day: z12.string(),
|
|
640
|
+
uniqueViewers: z12.number().int().nonnegative(),
|
|
641
|
+
views: z12.number().int().nonnegative()
|
|
547
642
|
});
|
|
548
|
-
var artifactStatsResponseSchema =
|
|
643
|
+
var artifactStatsResponseSchema = z12.object({
|
|
549
644
|
avgViewSeconds: avgViewSecondsSchema,
|
|
550
|
-
daily:
|
|
645
|
+
daily: z12.array(dailyViewsSchema),
|
|
551
646
|
// Distinct hashed viewer IPs across all versions and days.
|
|
552
|
-
totalUniqueViewers:
|
|
553
|
-
totalViews:
|
|
554
|
-
versions:
|
|
647
|
+
totalUniqueViewers: z12.number().int().nonnegative(),
|
|
648
|
+
totalViews: z12.number().int().nonnegative(),
|
|
649
|
+
versions: z12.array(versionViewStatsSchema)
|
|
555
650
|
});
|
|
556
651
|
|
|
557
652
|
// ../shared/src/versions.ts
|
|
558
|
-
import { z as
|
|
559
|
-
var versionSourceSchema =
|
|
560
|
-
agentName:
|
|
561
|
-
type:
|
|
653
|
+
import { z as z13 } from "zod";
|
|
654
|
+
var versionSourceSchema = z13.object({
|
|
655
|
+
agentName: z13.string().nullable(),
|
|
656
|
+
type: z13.string()
|
|
562
657
|
});
|
|
563
|
-
var versionChangesSchema =
|
|
564
|
-
added:
|
|
565
|
-
changed:
|
|
566
|
-
removed:
|
|
658
|
+
var versionChangesSchema = z13.object({
|
|
659
|
+
added: z13.array(z13.string()),
|
|
660
|
+
changed: z13.array(z13.string()),
|
|
661
|
+
removed: z13.array(z13.string())
|
|
567
662
|
});
|
|
568
|
-
var versionSummarySchema =
|
|
663
|
+
var versionSummarySchema = z13.object({
|
|
569
664
|
// Absent for the first version — there is no previous version to diff against.
|
|
570
665
|
changes: versionChangesSchema.nullable(),
|
|
571
|
-
createdAt:
|
|
572
|
-
entryUrl:
|
|
573
|
-
fileCount:
|
|
574
|
-
id:
|
|
575
|
-
isCurrent:
|
|
666
|
+
createdAt: z13.string(),
|
|
667
|
+
entryUrl: z13.string().url(),
|
|
668
|
+
fileCount: z13.number().int().nonnegative(),
|
|
669
|
+
id: z13.string(),
|
|
670
|
+
isCurrent: z13.boolean(),
|
|
576
671
|
source: versionSourceSchema,
|
|
577
|
-
totalSizeBytes:
|
|
578
|
-
versionNumber:
|
|
672
|
+
totalSizeBytes: z13.number().int().nonnegative(),
|
|
673
|
+
versionNumber: z13.number().int().positive()
|
|
579
674
|
});
|
|
580
|
-
var versionListResponseSchema =
|
|
581
|
-
versions:
|
|
675
|
+
var versionListResponseSchema = z13.object({
|
|
676
|
+
versions: z13.array(versionSummarySchema)
|
|
582
677
|
});
|
|
583
678
|
|
|
584
679
|
// src/api.ts
|
|
@@ -648,6 +743,36 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
648
743
|
(d) => publishSessionResponseSchema.parse(d)
|
|
649
744
|
);
|
|
650
745
|
},
|
|
746
|
+
// SPEC §6.4 — open a new comment thread as the account this client's
|
|
747
|
+
// key belongs to (agent-originated review, §5.1 `comment`). The create
|
|
748
|
+
// route rejects a bare publish token (agents respond, accounts
|
|
749
|
+
// originate), so this must carry an account credential.
|
|
750
|
+
async createThread(artifactId, body) {
|
|
751
|
+
return request(
|
|
752
|
+
`${base}/api/v1/artifacts/${artifactId}/comments`,
|
|
753
|
+
{ body: JSON.stringify(body), headers, method: "POST" },
|
|
754
|
+
(d) => commentThreadSchema.parse(d)
|
|
755
|
+
);
|
|
756
|
+
},
|
|
757
|
+
// SPEC §6.7.1 — soft-delete a message you authored. Authorized by this
|
|
758
|
+
// client's credential (account key, or the publish token for an agent
|
|
759
|
+
// message); a guest deletes from the browser, not the CLI.
|
|
760
|
+
async deleteMessage(messageId) {
|
|
761
|
+
await request(
|
|
762
|
+
`${base}/api/v1/comment-messages/${messageId}`,
|
|
763
|
+
{ body: "{}", headers, method: "DELETE" },
|
|
764
|
+
() => void 0
|
|
765
|
+
);
|
|
766
|
+
},
|
|
767
|
+
// SPEC §6.7.1 — edit a message you authored (new body + "edited" badge).
|
|
768
|
+
// Same credential rule as delete; returns the updated thread.
|
|
769
|
+
async editMessage(messageId, body) {
|
|
770
|
+
return request(
|
|
771
|
+
`${base}/api/v1/comment-messages/${messageId}`,
|
|
772
|
+
{ body: JSON.stringify({ body }), headers, method: "PATCH" },
|
|
773
|
+
(d) => commentThreadSchema.parse(d)
|
|
774
|
+
);
|
|
775
|
+
},
|
|
651
776
|
async exportComments(artifactId, query) {
|
|
652
777
|
const params = new URLSearchParams({
|
|
653
778
|
status: query.status,
|
|
@@ -655,7 +780,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
655
780
|
});
|
|
656
781
|
return request(
|
|
657
782
|
`${base}/api/v1/artifacts/${artifactId}/comments/export?${params}`,
|
|
658
|
-
{ method: "GET" },
|
|
783
|
+
{ headers, method: "GET" },
|
|
659
784
|
(d) => commentExportSchema.parse(d)
|
|
660
785
|
);
|
|
661
786
|
},
|
|
@@ -669,7 +794,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
669
794
|
async getArtifact(artifactId) {
|
|
670
795
|
return request(
|
|
671
796
|
`${base}/api/v1/artifacts/${artifactId}`,
|
|
672
|
-
{ method: "GET" },
|
|
797
|
+
{ headers, method: "GET" },
|
|
673
798
|
(d) => artifactResponseSchema.parse(d)
|
|
674
799
|
);
|
|
675
800
|
},
|
|
@@ -705,6 +830,34 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
705
830
|
(d) => meResponseSchema.parse(d)
|
|
706
831
|
);
|
|
707
832
|
},
|
|
833
|
+
// SPEC §6.7 — reopen a resolved thread. Same auth as resolve.
|
|
834
|
+
async reopenThread(threadId) {
|
|
835
|
+
return request(
|
|
836
|
+
`${base}/api/v1/comment-threads/${threadId}/reopen`,
|
|
837
|
+
{ body: "{}", headers, method: "POST" },
|
|
838
|
+
(d) => commentThreadSchema.parse(d)
|
|
839
|
+
);
|
|
840
|
+
},
|
|
841
|
+
// SPEC §6.5 — post a reply into a thread. Authorized by this client's
|
|
842
|
+
// credential: the artifact's publish token (the reply renders as the
|
|
843
|
+
// agent) or the owner's account key (renders as the owner). No guest
|
|
844
|
+
// payload — the credential is the identity.
|
|
845
|
+
async replyThread(threadId, body) {
|
|
846
|
+
return request(
|
|
847
|
+
`${base}/api/v1/comment-threads/${threadId}/messages`,
|
|
848
|
+
{ body: JSON.stringify({ body }), headers, method: "POST" },
|
|
849
|
+
(d) => commentThreadSchema.parse(d)
|
|
850
|
+
);
|
|
851
|
+
},
|
|
852
|
+
// SPEC §6.6 — mark a thread resolved. Authorized by this client's
|
|
853
|
+
// credential: the artifact's publish token, or the owner's account key.
|
|
854
|
+
async resolveThread(threadId) {
|
|
855
|
+
return request(
|
|
856
|
+
`${base}/api/v1/comment-threads/${threadId}/resolve`,
|
|
857
|
+
{ body: "{}", headers, method: "POST" },
|
|
858
|
+
(d) => commentThreadSchema.parse(d)
|
|
859
|
+
);
|
|
860
|
+
},
|
|
708
861
|
// SPEC §6.16 — set a password (string) or clear it (null). Authorized
|
|
709
862
|
// by the publish token this client was created with.
|
|
710
863
|
async setProtection(artifactId, password) {
|
|
@@ -741,17 +894,17 @@ import {
|
|
|
741
894
|
writeFileSync as writeFileSync2
|
|
742
895
|
} from "fs";
|
|
743
896
|
import path2 from "path";
|
|
744
|
-
import { z as
|
|
897
|
+
import { z as z15 } from "zod";
|
|
745
898
|
|
|
746
899
|
// src/credentials.ts
|
|
747
900
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
748
901
|
import { homedir } from "os";
|
|
749
902
|
import path from "path";
|
|
750
|
-
import { z as
|
|
903
|
+
import { z as z14 } from "zod";
|
|
751
904
|
var DEFAULT_API_URL = "https://artifact.cafe";
|
|
752
|
-
var credentialsSchema =
|
|
753
|
-
keys:
|
|
754
|
-
version:
|
|
905
|
+
var credentialsSchema = z14.object({
|
|
906
|
+
keys: z14.record(z14.string(), z14.string()),
|
|
907
|
+
version: z14.literal(1)
|
|
755
908
|
});
|
|
756
909
|
function credentialsPath() {
|
|
757
910
|
return path.join(homedir(), ".artifactcafe", "credentials");
|
|
@@ -801,22 +954,28 @@ function resolveApiKey(apiUrl2, flag) {
|
|
|
801
954
|
}
|
|
802
955
|
|
|
803
956
|
// src/config.ts
|
|
804
|
-
var configSchema =
|
|
805
|
-
apiUrl:
|
|
806
|
-
artifactId:
|
|
807
|
-
folder:
|
|
808
|
-
folderId:
|
|
957
|
+
var configSchema = z15.object({
|
|
958
|
+
apiUrl: z15.string(),
|
|
959
|
+
artifactId: z15.string(),
|
|
960
|
+
folder: z15.string().optional(),
|
|
961
|
+
folderId: z15.string().optional(),
|
|
809
962
|
// Optional: a folder linked by `link` to an artifact you own carries no
|
|
810
963
|
// token — owner actions authorize via your stored login instead. A first
|
|
811
964
|
// publish still writes the anonymous-mode token here.
|
|
812
|
-
publishToken:
|
|
813
|
-
slug:
|
|
814
|
-
url:
|
|
815
|
-
workspace:
|
|
816
|
-
workspaceId:
|
|
817
|
-
|
|
965
|
+
publishToken: z15.string().optional(),
|
|
966
|
+
slug: z15.string(),
|
|
967
|
+
url: z15.string(),
|
|
968
|
+
workspace: z15.string().optional(),
|
|
969
|
+
workspaceId: z15.string().optional(),
|
|
970
|
+
workspaceKind: z15.enum(["personal", "workspace"]).optional()
|
|
971
|
+
});
|
|
972
|
+
var projectConfigSchema = z15.object({
|
|
973
|
+
folder: z15.string().trim().min(1).optional(),
|
|
974
|
+
workspace: z15.string().trim().min(1)
|
|
975
|
+
}).strict();
|
|
818
976
|
var CONFIG_DIR = ".artifactcafe";
|
|
819
977
|
var CONFIG_FILE = "config.json";
|
|
978
|
+
var PROJECT_CONFIG_FILE = "artifact-cafe.json";
|
|
820
979
|
function readConfig(dir) {
|
|
821
980
|
const file = path2.join(dir, CONFIG_DIR, CONFIG_FILE);
|
|
822
981
|
if (!existsSync2(file)) {
|
|
@@ -838,6 +997,48 @@ function writeConfig(dir, config) {
|
|
|
838
997
|
);
|
|
839
998
|
ensureGitignored(dir);
|
|
840
999
|
}
|
|
1000
|
+
function readProjectConfig(dir) {
|
|
1001
|
+
let current = path2.resolve(dir);
|
|
1002
|
+
while (true) {
|
|
1003
|
+
const file = path2.join(current, PROJECT_CONFIG_FILE);
|
|
1004
|
+
if (existsSync2(file)) {
|
|
1005
|
+
try {
|
|
1006
|
+
return projectConfigSchema.parse(
|
|
1007
|
+
JSON.parse(readFileSync2(file, "utf8"))
|
|
1008
|
+
);
|
|
1009
|
+
} catch (error) {
|
|
1010
|
+
const message = error instanceof z15.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
|
|
1011
|
+
throw new Error(`Invalid ${file}: ${message}.`);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
if (existsSync2(path2.join(current, ".git"))) {
|
|
1015
|
+
return null;
|
|
1016
|
+
}
|
|
1017
|
+
const parent = path2.dirname(current);
|
|
1018
|
+
if (parent === current) {
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
current = parent;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
function writeProjectConfig(dir, config) {
|
|
1025
|
+
let root = path2.resolve(dir);
|
|
1026
|
+
while (!existsSync2(path2.join(root, ".git"))) {
|
|
1027
|
+
const parent = path2.dirname(root);
|
|
1028
|
+
if (parent === root) {
|
|
1029
|
+
root = path2.resolve(dir);
|
|
1030
|
+
break;
|
|
1031
|
+
}
|
|
1032
|
+
root = parent;
|
|
1033
|
+
}
|
|
1034
|
+
const file = path2.join(root, PROJECT_CONFIG_FILE);
|
|
1035
|
+
writeFileSync2(
|
|
1036
|
+
file,
|
|
1037
|
+
`${JSON.stringify(projectConfigSchema.parse(config), null, 2)}
|
|
1038
|
+
`
|
|
1039
|
+
);
|
|
1040
|
+
return file;
|
|
1041
|
+
}
|
|
841
1042
|
function ensureGitignored(dir) {
|
|
842
1043
|
const gitignore = path2.join(dir, ".gitignore");
|
|
843
1044
|
if (!existsSync2(gitignore)) {
|
|
@@ -876,7 +1077,107 @@ function resolveArtifactTarget(dir, opts = {}) {
|
|
|
876
1077
|
return { apiUrl: apiUrl2, artifactId, credential, url };
|
|
877
1078
|
}
|
|
878
1079
|
|
|
1080
|
+
// src/comment.ts
|
|
1081
|
+
async function readStdin() {
|
|
1082
|
+
if (process.stdin.isTTY) {
|
|
1083
|
+
return "";
|
|
1084
|
+
}
|
|
1085
|
+
const chunks = [];
|
|
1086
|
+
for await (const chunk of process.stdin) {
|
|
1087
|
+
chunks.push(chunk);
|
|
1088
|
+
}
|
|
1089
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
1090
|
+
}
|
|
1091
|
+
function buildAnchor(opts) {
|
|
1092
|
+
if (opts.quote !== void 0) {
|
|
1093
|
+
const quote = opts.quote.trim();
|
|
1094
|
+
if (!quote) {
|
|
1095
|
+
throw new Error(
|
|
1096
|
+
"--quote can't be empty. Omit it entirely for a page-level comment."
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
const anchor2 = {
|
|
1100
|
+
elementPath: "body",
|
|
1101
|
+
endOffset: 0,
|
|
1102
|
+
prefix: opts.prefix ?? "",
|
|
1103
|
+
quote,
|
|
1104
|
+
startOffset: 0,
|
|
1105
|
+
suffix: opts.suffix ?? "",
|
|
1106
|
+
type: "text"
|
|
1107
|
+
};
|
|
1108
|
+
if (opts.page) {
|
|
1109
|
+
anchor2.page = opts.page;
|
|
1110
|
+
}
|
|
1111
|
+
return anchor2;
|
|
1112
|
+
}
|
|
1113
|
+
if (opts.prefix || opts.suffix) {
|
|
1114
|
+
throw new Error("--prefix and --suffix only apply together with --quote.");
|
|
1115
|
+
}
|
|
1116
|
+
const anchor = { type: "page" };
|
|
1117
|
+
if (opts.page) {
|
|
1118
|
+
anchor.page = opts.page;
|
|
1119
|
+
}
|
|
1120
|
+
return anchor;
|
|
1121
|
+
}
|
|
1122
|
+
async function comment(opts) {
|
|
1123
|
+
const body = (opts.body ?? await readStdin()).trim();
|
|
1124
|
+
if (!body) {
|
|
1125
|
+
throw new Error(
|
|
1126
|
+
'Nothing to post. Pass the comment with --body "\u2026", or pipe it on stdin.'
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
const anchor = buildAnchor(opts);
|
|
1130
|
+
const dir = path3.resolve(opts.dir);
|
|
1131
|
+
const target = resolveArtifactTarget(dir, {
|
|
1132
|
+
apiKey: opts.apiKey,
|
|
1133
|
+
artifact: opts.artifact
|
|
1134
|
+
});
|
|
1135
|
+
const credential = resolveApiKey(target.apiUrl, opts.apiKey) ?? target.credential;
|
|
1136
|
+
const client = createApiClient(target.apiUrl, credential);
|
|
1137
|
+
const artifact = await client.getArtifact(target.artifactId);
|
|
1138
|
+
if (!artifact.currentVersion) {
|
|
1139
|
+
throw new Error(
|
|
1140
|
+
"This artifact has no published version yet \u2014 publish before commenting."
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
let thread;
|
|
1144
|
+
try {
|
|
1145
|
+
thread = await client.createThread(target.artifactId, {
|
|
1146
|
+
anchor,
|
|
1147
|
+
body,
|
|
1148
|
+
versionId: artifact.currentVersion.id,
|
|
1149
|
+
via: opts.via
|
|
1150
|
+
});
|
|
1151
|
+
} catch (err) {
|
|
1152
|
+
if (err instanceof ApiRequestError && err.status === 401) {
|
|
1153
|
+
throw new Error(
|
|
1154
|
+
"Publish tokens can't open threads. Run `artifact-cafe login` to comment as your account."
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
throw err;
|
|
1158
|
+
}
|
|
1159
|
+
if (opts.json) {
|
|
1160
|
+
console.log(
|
|
1161
|
+
JSON.stringify(
|
|
1162
|
+
{
|
|
1163
|
+
anchorType: thread.anchor.type,
|
|
1164
|
+
id: thread.id,
|
|
1165
|
+
messageId: thread.messages[0]?.id,
|
|
1166
|
+
versionId: thread.versionId
|
|
1167
|
+
},
|
|
1168
|
+
null,
|
|
1169
|
+
2
|
|
1170
|
+
)
|
|
1171
|
+
);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const url = target.url ?? reviewUrl(target.apiUrl, artifact.slug);
|
|
1175
|
+
const where = anchor.type === "text" ? ` on \u201C${anchor.quote}\u201D` : " on the page";
|
|
1176
|
+
console.log(`\u2713 Opened thread ${thread.id}${where} \u2014 ${url}`);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
879
1179
|
// src/comments.ts
|
|
1180
|
+
import path4 from "path";
|
|
880
1181
|
var STATUSES = ["open", "resolved", "all"];
|
|
881
1182
|
var VERSIONS = ["current", "all"];
|
|
882
1183
|
var pollSeconds = () => Number(process.env.AC_WAIT_POLL_SECONDS ?? 5);
|
|
@@ -895,7 +1196,7 @@ async function comments(opts) {
|
|
|
895
1196
|
}
|
|
896
1197
|
const status = opts.status;
|
|
897
1198
|
const version = opts.version;
|
|
898
|
-
const dir =
|
|
1199
|
+
const dir = path4.resolve(opts.dir);
|
|
899
1200
|
const target = resolveArtifactTarget(dir, {
|
|
900
1201
|
apiKey: opts.apiKey,
|
|
901
1202
|
artifact: opts.artifact
|
|
@@ -1054,21 +1355,30 @@ function printHuman(data, url, status, version) {
|
|
|
1054
1355
|
function printThread(t) {
|
|
1055
1356
|
const marker = t.status === "resolved" ? "\u2713" : "\u25CF";
|
|
1056
1357
|
const date = t.createdAt.slice(0, 10);
|
|
1358
|
+
const edited = t.editedAt && !t.deleted ? " \xB7 edited" : "";
|
|
1057
1359
|
console.log(
|
|
1058
|
-
`${marker} ${t.author} \xB7 v${t.versionNumber} \xB7 ${date} \xB7 ${t.id}`
|
|
1360
|
+
`${marker} ${t.author} \xB7 v${t.versionNumber} \xB7 ${date} \xB7 ${t.id}${edited}`
|
|
1059
1361
|
);
|
|
1060
1362
|
if (t.anchor.type === "text") {
|
|
1061
1363
|
console.log(` \u21B3 "${t.anchor.quote}"`);
|
|
1062
|
-
} else if (t.anchor.
|
|
1364
|
+
} else if (t.anchor.type === "point") {
|
|
1063
1365
|
const el = t.anchor.element;
|
|
1064
|
-
|
|
1065
|
-
|
|
1366
|
+
if (el) {
|
|
1367
|
+
const text = el.text ? ` "${el.text}"` : "";
|
|
1368
|
+
console.log(
|
|
1369
|
+
` \u21B3 at ${elementLabel(el)}${text} \u2014 ${t.anchor.elementPath}`
|
|
1370
|
+
);
|
|
1371
|
+
} else {
|
|
1372
|
+
console.log(` \u21B3 at ${t.anchor.elementPath}`);
|
|
1373
|
+
}
|
|
1066
1374
|
} else {
|
|
1067
|
-
console.log(` \u21B3
|
|
1375
|
+
console.log(t.anchor.page ? ` \u21B3 on ${t.anchor.page}` : " \u21B3 page comment");
|
|
1068
1376
|
}
|
|
1069
|
-
console.log(indent(t.body, " "));
|
|
1070
|
-
for (const
|
|
1071
|
-
|
|
1377
|
+
console.log(indent(t.deleted ? "[deleted]" : t.body, " "));
|
|
1378
|
+
for (const reply2 of t.replies) {
|
|
1379
|
+
const replyEdited = reply2.editedAt && !reply2.deleted ? " (edited)" : "";
|
|
1380
|
+
const replyBody = reply2.deleted ? "[deleted]" : reply2.body;
|
|
1381
|
+
console.log(indent(`${reply2.author}: ${replyBody}${replyEdited}`, " "));
|
|
1072
1382
|
}
|
|
1073
1383
|
console.log("");
|
|
1074
1384
|
}
|
|
@@ -1077,19 +1387,20 @@ function indent(text, pad) {
|
|
|
1077
1387
|
}
|
|
1078
1388
|
|
|
1079
1389
|
// src/dashboard.ts
|
|
1080
|
-
import
|
|
1390
|
+
import path7 from "path";
|
|
1081
1391
|
|
|
1082
1392
|
// src/publish.ts
|
|
1083
1393
|
import { exec } from "child_process";
|
|
1084
1394
|
import { readFileSync as readFileSync4 } from "fs";
|
|
1085
|
-
import
|
|
1395
|
+
import path6 from "path";
|
|
1396
|
+
import { createInterface } from "readline";
|
|
1086
1397
|
|
|
1087
1398
|
// src/manifest.ts
|
|
1088
1399
|
import { createHash } from "crypto";
|
|
1089
1400
|
import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
1090
|
-
import
|
|
1401
|
+
import path5 from "path";
|
|
1091
1402
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".artifactcafe"]);
|
|
1092
|
-
var IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store"]);
|
|
1403
|
+
var IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store", "artifact-cafe.json"]);
|
|
1093
1404
|
function isIgnoredFile(name) {
|
|
1094
1405
|
if (IGNORED_FILES.has(name)) {
|
|
1095
1406
|
return true;
|
|
@@ -1136,7 +1447,7 @@ var MIME_TYPES = {
|
|
|
1136
1447
|
".xml": "application/xml"
|
|
1137
1448
|
};
|
|
1138
1449
|
function mimeFor(filePath) {
|
|
1139
|
-
return MIME_TYPES[
|
|
1450
|
+
return MIME_TYPES[path5.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
1140
1451
|
}
|
|
1141
1452
|
function scanDirectory(dir) {
|
|
1142
1453
|
const files = [];
|
|
@@ -1144,7 +1455,7 @@ function scanDirectory(dir) {
|
|
|
1144
1455
|
function walk(current, relPrefix) {
|
|
1145
1456
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
1146
1457
|
const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
1147
|
-
const abs =
|
|
1458
|
+
const abs = path5.join(current, entry.name);
|
|
1148
1459
|
if (entry.isSymbolicLink()) {
|
|
1149
1460
|
skipped.push(rel);
|
|
1150
1461
|
continue;
|
|
@@ -1210,7 +1521,7 @@ function resolveEntry(files, explicit) {
|
|
|
1210
1521
|
// src/publish.ts
|
|
1211
1522
|
var UPLOAD_CONCURRENCY = 6;
|
|
1212
1523
|
async function publish(opts) {
|
|
1213
|
-
const dir =
|
|
1524
|
+
const dir = path6.resolve(opts.dir);
|
|
1214
1525
|
const config = readConfig(dir);
|
|
1215
1526
|
if (opts.password !== void 0) {
|
|
1216
1527
|
const parsed = gatePasswordSchema.safeParse(opts.password);
|
|
@@ -1222,7 +1533,13 @@ async function publish(opts) {
|
|
|
1222
1533
|
const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
|
|
1223
1534
|
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
|
|
1224
1535
|
const apiKey = resolveApiKey(apiUrl2, opts.apiKey);
|
|
1225
|
-
const placement = await resolvePlacement({
|
|
1536
|
+
const placement = await resolvePlacement({
|
|
1537
|
+
apiKey,
|
|
1538
|
+
apiUrl: apiUrl2,
|
|
1539
|
+
config,
|
|
1540
|
+
isNewArtifact: artifactId === null,
|
|
1541
|
+
opts
|
|
1542
|
+
});
|
|
1226
1543
|
const { files, skipped } = scanDirectory(dir);
|
|
1227
1544
|
if (files.length === 0) {
|
|
1228
1545
|
throw new Error(`Nothing to publish in ${dir} (after ignore rules).`);
|
|
@@ -1247,7 +1564,7 @@ async function publish(opts) {
|
|
|
1247
1564
|
type: "cli"
|
|
1248
1565
|
},
|
|
1249
1566
|
title: opts.title,
|
|
1250
|
-
...placement.
|
|
1567
|
+
...placement.publishWorkspaceId ? { workspaceId: placement.publishWorkspaceId } : {}
|
|
1251
1568
|
};
|
|
1252
1569
|
let credential = apiKey ?? token;
|
|
1253
1570
|
let client = createApiClient(apiUrl2, credential);
|
|
@@ -1279,8 +1596,9 @@ async function publish(opts) {
|
|
|
1279
1596
|
);
|
|
1280
1597
|
});
|
|
1281
1598
|
const result = await client.finalize(session.publishSessionId);
|
|
1599
|
+
const rememberedProjectFile = placement.rememberProjectDefault ? writeProjectConfig(dir, placement.rememberProjectDefault) : null;
|
|
1282
1600
|
const existingToken = config?.artifactId === result.artifactId ? config.publishToken : void 0;
|
|
1283
|
-
const shouldWriteConfig = result.publishToken !== void 0 || config?.artifactId === result.artifactId || opts.workspace !== void 0 || opts.folder !== void 0;
|
|
1601
|
+
const shouldWriteConfig = result.publishToken !== void 0 || config?.artifactId === result.artifactId || opts.workspace !== void 0 || opts.folder !== void 0 || placement.workspaceId !== void 0;
|
|
1284
1602
|
if (shouldWriteConfig) {
|
|
1285
1603
|
writeConfig(dir, {
|
|
1286
1604
|
apiUrl: apiUrl2,
|
|
@@ -1291,7 +1609,8 @@ async function publish(opts) {
|
|
|
1291
1609
|
slug: slugFromUrl(result.url),
|
|
1292
1610
|
url: result.url,
|
|
1293
1611
|
...placement.workspace ? { workspace: placement.workspace } : {},
|
|
1294
|
-
...placement.workspaceId ? { workspaceId: placement.workspaceId } : {}
|
|
1612
|
+
...placement.workspaceId ? { workspaceId: placement.workspaceId } : {},
|
|
1613
|
+
...placement.destination === "personal" || placement.destination === "workspace" ? { workspaceKind: placement.destination } : {}
|
|
1295
1614
|
});
|
|
1296
1615
|
}
|
|
1297
1616
|
if (opts.password !== void 0) {
|
|
@@ -1307,6 +1626,8 @@ async function publish(opts) {
|
|
|
1307
1626
|
{
|
|
1308
1627
|
artifactId: result.artifactId,
|
|
1309
1628
|
claimed: result.claimed,
|
|
1629
|
+
destination: placement.destination,
|
|
1630
|
+
destinationSource: placement.destinationSource,
|
|
1310
1631
|
folder: placement.folder ?? null,
|
|
1311
1632
|
folderId: placement.folderId ?? null,
|
|
1312
1633
|
url: result.url,
|
|
@@ -1325,6 +1646,12 @@ async function publish(opts) {
|
|
|
1325
1646
|
);
|
|
1326
1647
|
} else if (result.versionNumber === 1) {
|
|
1327
1648
|
console.log("\n\u2713 Published to artifact.cafe\n");
|
|
1649
|
+
console.log(`Destination: ${placement.destinationLabel}
|
|
1650
|
+
`);
|
|
1651
|
+
if (rememberedProjectFile) {
|
|
1652
|
+
console.log(`Project default: ${rememberedProjectFile}
|
|
1653
|
+
`);
|
|
1654
|
+
}
|
|
1328
1655
|
console.log(`Review URL:
|
|
1329
1656
|
${result.url}
|
|
1330
1657
|
`);
|
|
@@ -1344,6 +1671,12 @@ ${result.claimUrl}
|
|
|
1344
1671
|
console.log(`
|
|
1345
1672
|
\u2713 Published v${result.versionNumber}
|
|
1346
1673
|
`);
|
|
1674
|
+
console.log(`Destination: ${placement.destinationLabel}
|
|
1675
|
+
`);
|
|
1676
|
+
if (rememberedProjectFile) {
|
|
1677
|
+
console.log(`Project default: ${rememberedProjectFile}
|
|
1678
|
+
`);
|
|
1679
|
+
}
|
|
1347
1680
|
console.log(`${result.url}
|
|
1348
1681
|
`);
|
|
1349
1682
|
if (opts.password !== void 0) {
|
|
@@ -1355,75 +1688,181 @@ ${result.claimUrl}
|
|
|
1355
1688
|
}
|
|
1356
1689
|
}
|
|
1357
1690
|
async function resolvePlacement(params) {
|
|
1358
|
-
const { apiKey, apiUrl: apiUrl2, config, opts } = params;
|
|
1691
|
+
const { apiKey, apiUrl: apiUrl2, config, isNewArtifact, opts } = params;
|
|
1359
1692
|
const hasPlacementFlag = opts.workspace !== void 0 || opts.folder !== void 0;
|
|
1360
1693
|
if (!hasPlacementFlag && config?.workspaceId) {
|
|
1361
|
-
|
|
1694
|
+
const isPersonal = config.workspaceKind === "personal";
|
|
1695
|
+
if (!(apiKey || isPersonal)) {
|
|
1362
1696
|
throw new Error(
|
|
1363
1697
|
`Publishing into workspace ${config.workspace ?? config.workspaceId} needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
|
|
1364
1698
|
);
|
|
1365
1699
|
}
|
|
1366
1700
|
return {
|
|
1701
|
+
destination: isPersonal ? "personal" : "workspace",
|
|
1702
|
+
destinationLabel: isPersonal ? "Personal" : config.workspace ?? config.workspaceId,
|
|
1703
|
+
destinationSource: "artifact-config",
|
|
1367
1704
|
...config.folder ? { folder: config.folder } : {},
|
|
1368
1705
|
...config.folderId ? { folderId: config.folderId } : {},
|
|
1369
1706
|
...config.workspace ? { workspace: config.workspace } : {},
|
|
1707
|
+
...apiKey ? { publishWorkspaceId: config.workspaceId } : {},
|
|
1370
1708
|
workspaceId: config.workspaceId
|
|
1371
1709
|
};
|
|
1372
1710
|
}
|
|
1373
|
-
if (!hasPlacementFlag) {
|
|
1374
|
-
return {
|
|
1711
|
+
if (!(hasPlacementFlag || isNewArtifact)) {
|
|
1712
|
+
return {
|
|
1713
|
+
destination: "existing-artifact",
|
|
1714
|
+
destinationLabel: "Existing artifact",
|
|
1715
|
+
destinationSource: "artifact-config"
|
|
1716
|
+
};
|
|
1375
1717
|
}
|
|
1376
|
-
|
|
1718
|
+
const projectConfig = isNewArtifact ? readProjectConfig(opts.dir) : null;
|
|
1719
|
+
const wantsWorkspace = opts.workspace !== void 0 || opts.folder !== void 0 || projectConfig !== null;
|
|
1720
|
+
if (wantsWorkspace && !apiKey) {
|
|
1377
1721
|
throw new Error(
|
|
1378
1722
|
"Publishing into a workspace needs an account \u2014 run `artifact-cafe login` first."
|
|
1379
1723
|
);
|
|
1380
1724
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1725
|
+
if (!(hasPlacementFlag || projectConfig)) {
|
|
1726
|
+
if (!apiKey) {
|
|
1727
|
+
return {
|
|
1728
|
+
destination: "anonymous",
|
|
1729
|
+
destinationLabel: "Anonymous",
|
|
1730
|
+
destinationSource: "anonymous-default"
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
if (shouldPromptForWorkspace(opts)) {
|
|
1734
|
+
const data2 = await createApiClient(apiUrl2, apiKey).listWorkspaces();
|
|
1735
|
+
if (data2.workspaces.length > 1) {
|
|
1736
|
+
const choice = await pickWorkspace(data2.workspaces);
|
|
1737
|
+
return {
|
|
1738
|
+
destination: choice.workspace.isPersonal ? "personal" : "workspace",
|
|
1739
|
+
destinationLabel: choice.workspace.isPersonal ? "Personal" : choice.workspace.name,
|
|
1740
|
+
destinationSource: "interactive",
|
|
1741
|
+
publishWorkspaceId: choice.workspace.id,
|
|
1742
|
+
...choice.remember ? {
|
|
1743
|
+
rememberProjectDefault: {
|
|
1744
|
+
workspace: choice.workspace.handle
|
|
1745
|
+
}
|
|
1746
|
+
} : {},
|
|
1747
|
+
workspace: choice.workspace.handle,
|
|
1748
|
+
workspaceId: choice.workspace.id
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
return {
|
|
1753
|
+
destination: "personal",
|
|
1754
|
+
destinationLabel: "Personal",
|
|
1755
|
+
destinationSource: "personal-default"
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
if (!isNewArtifact && opts.folder !== void 0 && opts.workspace === void 0 && !(config?.workspace || config?.workspaceId)) {
|
|
1383
1759
|
throw new Error("--folder requires --workspace or a stored workspace.");
|
|
1384
1760
|
}
|
|
1385
1761
|
const client = createApiClient(apiUrl2, apiKey);
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
const workspace = data2.workspaces.find(
|
|
1762
|
+
const data = await client.listWorkspaces();
|
|
1763
|
+
const workspaceHandle = opts.workspace ?? config?.workspace ?? projectConfig?.workspace;
|
|
1764
|
+
let workspace;
|
|
1765
|
+
if (workspaceHandle) {
|
|
1766
|
+
workspace = data.workspaces.find(
|
|
1392
1767
|
(candidate) => candidate.handle === workspaceHandle
|
|
1393
1768
|
);
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1769
|
+
} else if (config?.workspaceId) {
|
|
1770
|
+
workspace = data.workspaces.find(
|
|
1771
|
+
(candidate) => candidate.id === config.workspaceId
|
|
1772
|
+
);
|
|
1773
|
+
} else {
|
|
1774
|
+
workspace = data.workspaces.find((candidate) => candidate.isPersonal);
|
|
1775
|
+
}
|
|
1776
|
+
if (!workspace) {
|
|
1777
|
+
const target = workspaceHandle ? `workspace with handle "${workspaceHandle}"` : "personal workspace";
|
|
1778
|
+
throw new Error(`No ${target} belongs to this account.`);
|
|
1400
1779
|
}
|
|
1401
|
-
const folderName = opts.workspace !== void 0 && opts.folder === void 0 ? void 0 : opts.folder ??
|
|
1780
|
+
const folderName = opts.workspace !== void 0 && opts.folder === void 0 ? void 0 : opts.folder ?? projectConfig?.folder;
|
|
1781
|
+
const destinationSource = hasPlacementFlag ? "explicit" : "project-default";
|
|
1402
1782
|
if (!folderName) {
|
|
1403
|
-
return {
|
|
1783
|
+
return {
|
|
1784
|
+
destination: workspace.isPersonal ? "personal" : "workspace",
|
|
1785
|
+
destinationLabel: workspace.isPersonal ? "Personal" : workspace.name,
|
|
1786
|
+
destinationSource,
|
|
1787
|
+
publishWorkspaceId: workspace.id,
|
|
1788
|
+
workspace: workspace.handle,
|
|
1789
|
+
workspaceId: workspace.id
|
|
1790
|
+
};
|
|
1404
1791
|
}
|
|
1405
|
-
const
|
|
1406
|
-
const matches =
|
|
1792
|
+
const folderData = await client.listFolders(workspace.id);
|
|
1793
|
+
const matches = folderData.folders.filter(
|
|
1407
1794
|
(candidate) => candidate.name === folderName || candidate.id === folderName
|
|
1408
1795
|
);
|
|
1409
1796
|
if (matches.length === 0) {
|
|
1410
1797
|
throw new Error(
|
|
1411
|
-
`No folder named "${folderName}" exists in workspace ${
|
|
1798
|
+
`No folder named "${folderName}" exists in workspace ${workspace.handle}.`
|
|
1412
1799
|
);
|
|
1413
1800
|
}
|
|
1414
1801
|
if (matches.length > 1) {
|
|
1415
1802
|
throw new Error(
|
|
1416
|
-
`More than one folder is named "${folderName}" in workspace ${
|
|
1803
|
+
`More than one folder is named "${folderName}" in workspace ${workspace.handle}; pass the folder id instead.`
|
|
1417
1804
|
);
|
|
1418
1805
|
}
|
|
1419
1806
|
const folder = matches[0];
|
|
1420
1807
|
return {
|
|
1808
|
+
destination: workspace.isPersonal ? "personal" : "workspace",
|
|
1809
|
+
destinationLabel: workspace.isPersonal ? "Personal" : workspace.name,
|
|
1810
|
+
destinationSource,
|
|
1421
1811
|
folder: folder.name,
|
|
1422
1812
|
folderId: folder.id,
|
|
1423
|
-
|
|
1424
|
-
|
|
1813
|
+
publishWorkspaceId: workspace.id,
|
|
1814
|
+
workspace: workspace.handle,
|
|
1815
|
+
workspaceId: workspace.id
|
|
1425
1816
|
};
|
|
1426
1817
|
}
|
|
1818
|
+
function shouldPromptForWorkspace(opts) {
|
|
1819
|
+
if (opts.json || process.env.ARTIFACT_CAFE_AGENT) {
|
|
1820
|
+
return false;
|
|
1821
|
+
}
|
|
1822
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
1823
|
+
}
|
|
1824
|
+
function pickWorkspace(workspaces3) {
|
|
1825
|
+
const choices = [...workspaces3].sort((a, b) => {
|
|
1826
|
+
if (a.isPersonal !== b.isPersonal) {
|
|
1827
|
+
return a.isPersonal ? -1 : 1;
|
|
1828
|
+
}
|
|
1829
|
+
return a.name.localeCompare(b.name);
|
|
1830
|
+
});
|
|
1831
|
+
console.log("Where should this artifact live?\n");
|
|
1832
|
+
choices.forEach((workspace, index) => {
|
|
1833
|
+
const label = workspace.isPersonal ? "Personal" : workspace.name;
|
|
1834
|
+
const handle = workspace.isPersonal ? "" : ` \xB7 ${workspace.handle}`;
|
|
1835
|
+
console.log(` ${String(index + 1).padStart(2)}. ${label}${handle}`);
|
|
1836
|
+
});
|
|
1837
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1838
|
+
return new Promise((resolve2, reject) => {
|
|
1839
|
+
rl.question(`
|
|
1840
|
+
Publish where? [1-${choices.length}]: `, (answer) => {
|
|
1841
|
+
const index = Number(answer.trim()) - 1;
|
|
1842
|
+
const workspace = choices[index];
|
|
1843
|
+
if (!(Number.isInteger(index) && workspace)) {
|
|
1844
|
+
rl.close();
|
|
1845
|
+
reject(new Error(`Enter a number between 1 and ${choices.length}.`));
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
if (workspace.isPersonal) {
|
|
1849
|
+
rl.close();
|
|
1850
|
+
resolve2({ remember: false, workspace });
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
rl.question(
|
|
1854
|
+
`Remember ${workspace.name} for new artifacts in this project? [y/N]: `,
|
|
1855
|
+
(rememberAnswer) => {
|
|
1856
|
+
rl.close();
|
|
1857
|
+
resolve2({
|
|
1858
|
+
remember: /^y(?:es)?$/i.test(rememberAnswer.trim()),
|
|
1859
|
+
workspace
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
);
|
|
1863
|
+
});
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1427
1866
|
async function inBatches(items, concurrency, fn) {
|
|
1428
1867
|
let next = 0;
|
|
1429
1868
|
const workers = Array.from(
|
|
@@ -1460,7 +1899,7 @@ function openInBrowser(url) {
|
|
|
1460
1899
|
|
|
1461
1900
|
// src/dashboard.ts
|
|
1462
1901
|
function openDashboard(opts) {
|
|
1463
|
-
const base = process.env.ARTIFACT_CAFE_URL ?? readConfig(
|
|
1902
|
+
const base = process.env.ARTIFACT_CAFE_URL ?? readConfig(path7.resolve(opts.dir))?.apiUrl ?? DEFAULT_API_URL;
|
|
1464
1903
|
const url = `${base.replace(/\/$/, "")}/dashboard`;
|
|
1465
1904
|
console.log(url);
|
|
1466
1905
|
if (process.stdout.isTTY) {
|
|
@@ -1486,7 +1925,12 @@ function printCliError(err, json) {
|
|
|
1486
1925
|
if (err.retryAfter) {
|
|
1487
1926
|
console.error(` Retry in ${err.retryAfter}s.`);
|
|
1488
1927
|
}
|
|
1489
|
-
|
|
1928
|
+
if (err.code === "password_required") {
|
|
1929
|
+
console.error(
|
|
1930
|
+
" Run `artifact-cafe login` as the owner, or run from the published folder (its publish token unlocks it)."
|
|
1931
|
+
);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1490
1934
|
return;
|
|
1491
1935
|
}
|
|
1492
1936
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1544,8 +1988,8 @@ function printFolder(folder) {
|
|
|
1544
1988
|
}
|
|
1545
1989
|
|
|
1546
1990
|
// src/link.ts
|
|
1547
|
-
import
|
|
1548
|
-
import { createInterface } from "readline";
|
|
1991
|
+
import path8 from "path";
|
|
1992
|
+
import { createInterface as createInterface2 } from "readline";
|
|
1549
1993
|
async function link(opts) {
|
|
1550
1994
|
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
1551
1995
|
/\/$/,
|
|
@@ -1587,7 +2031,7 @@ async function link(opts) {
|
|
|
1587
2031
|
}
|
|
1588
2032
|
chosen = await pickArtifact(artifacts);
|
|
1589
2033
|
}
|
|
1590
|
-
const dir =
|
|
2034
|
+
const dir = path8.resolve(opts.dir);
|
|
1591
2035
|
writeConfig(dir, {
|
|
1592
2036
|
apiUrl: apiUrl2,
|
|
1593
2037
|
artifactId: chosen.id,
|
|
@@ -1624,8 +2068,8 @@ function pickArtifact(artifacts) {
|
|
|
1624
2068
|
` ${String(i + 1).padStart(2)}. ${a.title} \xB7 ${v} \xB7 ${a.url}`
|
|
1625
2069
|
);
|
|
1626
2070
|
});
|
|
1627
|
-
const rl =
|
|
1628
|
-
return new Promise((
|
|
2071
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
2072
|
+
return new Promise((resolve2, reject) => {
|
|
1629
2073
|
rl.question(`
|
|
1630
2074
|
Link which? [1-${artifacts.length}]: `, (answer) => {
|
|
1631
2075
|
rl.close();
|
|
@@ -1634,7 +2078,7 @@ Link which? [1-${artifacts.length}]: `, (answer) => {
|
|
|
1634
2078
|
reject(new Error(`Enter a number between 1 and ${artifacts.length}.`));
|
|
1635
2079
|
return;
|
|
1636
2080
|
}
|
|
1637
|
-
|
|
2081
|
+
resolve2(artifacts[n - 1]);
|
|
1638
2082
|
});
|
|
1639
2083
|
});
|
|
1640
2084
|
}
|
|
@@ -1696,13 +2140,13 @@ function relativeTime(iso) {
|
|
|
1696
2140
|
}
|
|
1697
2141
|
|
|
1698
2142
|
// src/login.ts
|
|
1699
|
-
import { createInterface as
|
|
2143
|
+
import { createInterface as createInterface3 } from "readline";
|
|
1700
2144
|
function apiUrl() {
|
|
1701
2145
|
return (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
1702
2146
|
}
|
|
1703
2147
|
function promptSecret(question) {
|
|
1704
|
-
return new Promise((
|
|
1705
|
-
const rl =
|
|
2148
|
+
return new Promise((resolve2) => {
|
|
2149
|
+
const rl = createInterface3({
|
|
1706
2150
|
input: process.stdin,
|
|
1707
2151
|
output: process.stdout
|
|
1708
2152
|
});
|
|
@@ -1716,7 +2160,7 @@ function promptSecret(question) {
|
|
|
1716
2160
|
rl.question(question, (answer) => {
|
|
1717
2161
|
rl.close();
|
|
1718
2162
|
process.stdout.write("\n");
|
|
1719
|
-
|
|
2163
|
+
resolve2(answer);
|
|
1720
2164
|
});
|
|
1721
2165
|
muted = true;
|
|
1722
2166
|
});
|
|
@@ -1809,10 +2253,69 @@ function logout(opts) {
|
|
|
1809
2253
|
);
|
|
1810
2254
|
}
|
|
1811
2255
|
|
|
2256
|
+
// src/message.ts
|
|
2257
|
+
import path9 from "path";
|
|
2258
|
+
async function readStdin2() {
|
|
2259
|
+
if (process.stdin.isTTY) {
|
|
2260
|
+
return "";
|
|
2261
|
+
}
|
|
2262
|
+
const chunks = [];
|
|
2263
|
+
for await (const chunk of process.stdin) {
|
|
2264
|
+
chunks.push(chunk);
|
|
2265
|
+
}
|
|
2266
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
2267
|
+
}
|
|
2268
|
+
async function editMessage(opts) {
|
|
2269
|
+
const messageId = opts.message?.trim();
|
|
2270
|
+
if (!messageId) {
|
|
2271
|
+
throw new Error(
|
|
2272
|
+
"Pass the message id to edit \u2014 get it from `artifact-cafe comments --json`."
|
|
2273
|
+
);
|
|
2274
|
+
}
|
|
2275
|
+
const body = (opts.body ?? await readStdin2()).trim();
|
|
2276
|
+
if (!body) {
|
|
2277
|
+
throw new Error(
|
|
2278
|
+
'Nothing to save. Pass the new text with --body "\u2026", or pipe it on stdin.'
|
|
2279
|
+
);
|
|
2280
|
+
}
|
|
2281
|
+
const dir = path9.resolve(opts.dir);
|
|
2282
|
+
const target = resolveArtifactTarget(dir, {
|
|
2283
|
+
apiKey: opts.apiKey,
|
|
2284
|
+
artifact: opts.artifact
|
|
2285
|
+
});
|
|
2286
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2287
|
+
const thread = await client.editMessage(messageId, body);
|
|
2288
|
+
if (opts.json) {
|
|
2289
|
+
console.log(JSON.stringify({ id: thread.id, messageId }, null, 2));
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
console.log(`\u2713 Edited ${messageId}.`);
|
|
2293
|
+
}
|
|
2294
|
+
async function deleteMessage(opts) {
|
|
2295
|
+
const messageId = opts.message?.trim();
|
|
2296
|
+
if (!messageId) {
|
|
2297
|
+
throw new Error(
|
|
2298
|
+
"Pass the message id to delete \u2014 get it from `artifact-cafe comments --json`."
|
|
2299
|
+
);
|
|
2300
|
+
}
|
|
2301
|
+
const dir = path9.resolve(opts.dir);
|
|
2302
|
+
const target = resolveArtifactTarget(dir, {
|
|
2303
|
+
apiKey: opts.apiKey,
|
|
2304
|
+
artifact: opts.artifact
|
|
2305
|
+
});
|
|
2306
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2307
|
+
await client.deleteMessage(messageId);
|
|
2308
|
+
if (opts.json) {
|
|
2309
|
+
console.log(JSON.stringify({ deleted: true, messageId }, null, 2));
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
console.log(`\u2713 Deleted ${messageId}.`);
|
|
2313
|
+
}
|
|
2314
|
+
|
|
1812
2315
|
// src/open.ts
|
|
1813
|
-
import
|
|
2316
|
+
import path10 from "path";
|
|
1814
2317
|
async function openArtifact(opts) {
|
|
1815
|
-
const dir =
|
|
2318
|
+
const dir = path10.resolve(opts.dir);
|
|
1816
2319
|
const target = resolveArtifactTarget(dir, {
|
|
1817
2320
|
apiKey: opts.apiKey,
|
|
1818
2321
|
artifact: opts.artifact
|
|
@@ -1830,7 +2333,7 @@ async function openArtifact(opts) {
|
|
|
1830
2333
|
}
|
|
1831
2334
|
|
|
1832
2335
|
// src/protect.ts
|
|
1833
|
-
import
|
|
2336
|
+
import path11 from "path";
|
|
1834
2337
|
async function protect(opts) {
|
|
1835
2338
|
if (opts.off && opts.password !== void 0) {
|
|
1836
2339
|
throw new Error("Pass either --password or --off, not both.");
|
|
@@ -1847,7 +2350,7 @@ async function protect(opts) {
|
|
|
1847
2350
|
throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
|
|
1848
2351
|
}
|
|
1849
2352
|
}
|
|
1850
|
-
const dir =
|
|
2353
|
+
const dir = path11.resolve(opts.dir);
|
|
1851
2354
|
const target = resolveArtifactTarget(dir, {
|
|
1852
2355
|
apiKey: opts.apiKey,
|
|
1853
2356
|
artifact: opts.artifact
|
|
@@ -1871,6 +2374,234 @@ async function protect(opts) {
|
|
|
1871
2374
|
);
|
|
1872
2375
|
}
|
|
1873
2376
|
|
|
2377
|
+
// src/reply.ts
|
|
2378
|
+
import path12 from "path";
|
|
2379
|
+
async function readStdin3() {
|
|
2380
|
+
if (process.stdin.isTTY) {
|
|
2381
|
+
return "";
|
|
2382
|
+
}
|
|
2383
|
+
const chunks = [];
|
|
2384
|
+
for await (const chunk of process.stdin) {
|
|
2385
|
+
chunks.push(chunk);
|
|
2386
|
+
}
|
|
2387
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
2388
|
+
}
|
|
2389
|
+
async function replyToThread(opts) {
|
|
2390
|
+
const threadId = opts.thread?.trim();
|
|
2391
|
+
if (!threadId) {
|
|
2392
|
+
throw new Error(
|
|
2393
|
+
"Pass the thread id to reply to \u2014 get it from `artifact-cafe comments --json`."
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
2396
|
+
const body = (opts.body ?? await readStdin3()).trim();
|
|
2397
|
+
if (!body) {
|
|
2398
|
+
throw new Error(
|
|
2399
|
+
'Nothing to post. Pass the reply with --body "\u2026", or pipe it on stdin.'
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
const dir = path12.resolve(opts.dir);
|
|
2403
|
+
const target = resolveArtifactTarget(dir, {
|
|
2404
|
+
apiKey: opts.apiKey,
|
|
2405
|
+
artifact: opts.artifact
|
|
2406
|
+
});
|
|
2407
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2408
|
+
let thread = await client.replyThread(threadId, body);
|
|
2409
|
+
if (opts.resolve) {
|
|
2410
|
+
thread = await client.resolveThread(threadId);
|
|
2411
|
+
}
|
|
2412
|
+
if (opts.json) {
|
|
2413
|
+
console.log(
|
|
2414
|
+
JSON.stringify(
|
|
2415
|
+
{
|
|
2416
|
+
id: thread.id,
|
|
2417
|
+
messageCount: thread.messages.length,
|
|
2418
|
+
status: thread.status
|
|
2419
|
+
},
|
|
2420
|
+
null,
|
|
2421
|
+
2
|
|
2422
|
+
)
|
|
2423
|
+
);
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
const closed = opts.resolve ? " and resolved it" : "";
|
|
2427
|
+
console.log(`\u2713 Replied to ${thread.id}${closed} \u2014 now ${thread.status}.`);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
// src/resolve.ts
|
|
2431
|
+
import path13 from "path";
|
|
2432
|
+
async function changeThreadStatus(opts) {
|
|
2433
|
+
const threadId = opts.thread?.trim();
|
|
2434
|
+
if (!threadId) {
|
|
2435
|
+
throw new Error(
|
|
2436
|
+
`Pass the thread id to ${opts.action} \u2014 get it from \`artifact-cafe comments --json\`.`
|
|
2437
|
+
);
|
|
2438
|
+
}
|
|
2439
|
+
const dir = path13.resolve(opts.dir);
|
|
2440
|
+
const target = resolveArtifactTarget(dir, {
|
|
2441
|
+
apiKey: opts.apiKey,
|
|
2442
|
+
artifact: opts.artifact
|
|
2443
|
+
});
|
|
2444
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2445
|
+
const thread = opts.action === "resolve" ? await client.resolveThread(threadId) : await client.reopenThread(threadId);
|
|
2446
|
+
if (opts.json) {
|
|
2447
|
+
console.log(
|
|
2448
|
+
JSON.stringify({ id: thread.id, status: thread.status }, null, 2)
|
|
2449
|
+
);
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
const verb = opts.action === "resolve" ? "Resolved" : "Reopened";
|
|
2453
|
+
console.log(`\u2713 ${verb} ${thread.id} \u2014 now ${thread.status}.`);
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
// src/update.ts
|
|
2457
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
2458
|
+
import { createRequire } from "module";
|
|
2459
|
+
import { homedir as homedir2 } from "os";
|
|
2460
|
+
import path14 from "path";
|
|
2461
|
+
import { z as z16 } from "zod";
|
|
2462
|
+
var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
|
|
2463
|
+
var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
|
|
2464
|
+
var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
|
|
2465
|
+
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2466
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
2467
|
+
var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
|
|
2468
|
+
var cacheSchema = z16.object({
|
|
2469
|
+
checkedAt: z16.number(),
|
|
2470
|
+
cliLatest: z16.string().nullable(),
|
|
2471
|
+
skillLatest: z16.string().nullable(),
|
|
2472
|
+
version: z16.literal(1)
|
|
2473
|
+
});
|
|
2474
|
+
function cliVersion() {
|
|
2475
|
+
const pkg = createRequire(import.meta.url)("../package.json");
|
|
2476
|
+
return pkg.version;
|
|
2477
|
+
}
|
|
2478
|
+
function cachePath() {
|
|
2479
|
+
return path14.join(homedir2(), ".artifactcafe", "update-check.json");
|
|
2480
|
+
}
|
|
2481
|
+
function readCache() {
|
|
2482
|
+
try {
|
|
2483
|
+
return cacheSchema.parse(JSON.parse(readFileSync5(cachePath(), "utf8")));
|
|
2484
|
+
} catch {
|
|
2485
|
+
return null;
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
function writeCache(cache) {
|
|
2489
|
+
try {
|
|
2490
|
+
mkdirSync3(path14.dirname(cachePath()), { mode: 448, recursive: true });
|
|
2491
|
+
writeFileSync3(cachePath(), `${JSON.stringify(cache, null, 2)}
|
|
2492
|
+
`);
|
|
2493
|
+
} catch {
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
function isNewerVersion(candidate, current) {
|
|
2497
|
+
const parse = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
2498
|
+
const a = parse(candidate);
|
|
2499
|
+
const b = parse(current);
|
|
2500
|
+
for (let i = 0; i < 3; i++) {
|
|
2501
|
+
if ((a[i] ?? 0) !== (b[i] ?? 0)) {
|
|
2502
|
+
return (a[i] ?? 0) > (b[i] ?? 0);
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
return false;
|
|
2506
|
+
}
|
|
2507
|
+
async function fetchJson(url) {
|
|
2508
|
+
const res = await fetch(url, {
|
|
2509
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
2510
|
+
});
|
|
2511
|
+
if (!res.ok) {
|
|
2512
|
+
throw new Error(`GET ${url} \u2192 ${res.status}`);
|
|
2513
|
+
}
|
|
2514
|
+
return res.json();
|
|
2515
|
+
}
|
|
2516
|
+
async function fetchText(url) {
|
|
2517
|
+
const res = await fetch(url, {
|
|
2518
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
2519
|
+
});
|
|
2520
|
+
if (!res.ok) {
|
|
2521
|
+
throw new Error(`GET ${url} \u2192 ${res.status}`);
|
|
2522
|
+
}
|
|
2523
|
+
return res.text();
|
|
2524
|
+
}
|
|
2525
|
+
async function fetchLatestVersions(previous) {
|
|
2526
|
+
const [cli, skill] = await Promise.all([
|
|
2527
|
+
fetchJson(REGISTRY_LATEST_URL).then((body) => z16.object({ version: z16.string() }).parse(body).version).catch(() => null),
|
|
2528
|
+
fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
|
|
2529
|
+
]);
|
|
2530
|
+
return {
|
|
2531
|
+
checkedAt: Date.now(),
|
|
2532
|
+
cliLatest: cli ?? previous?.cliLatest ?? null,
|
|
2533
|
+
skillLatest: skill ?? previous?.skillLatest ?? null,
|
|
2534
|
+
version: 1
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
function installedSkillVersions() {
|
|
2538
|
+
const roots = [homedir2(), process.cwd()];
|
|
2539
|
+
const dirs = [".agents", ".claude"];
|
|
2540
|
+
const versions = [];
|
|
2541
|
+
for (const root of roots) {
|
|
2542
|
+
for (const dir of dirs) {
|
|
2543
|
+
const file = path14.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
|
|
2544
|
+
if (!existsSync3(file)) {
|
|
2545
|
+
continue;
|
|
2546
|
+
}
|
|
2547
|
+
try {
|
|
2548
|
+
const found = SKILL_VERSION_PATTERN.exec(readFileSync5(file, "utf8"));
|
|
2549
|
+
if (found?.[1]) {
|
|
2550
|
+
versions.push(found[1]);
|
|
2551
|
+
}
|
|
2552
|
+
} catch {
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
return versions;
|
|
2557
|
+
}
|
|
2558
|
+
function updateChecksDisabled() {
|
|
2559
|
+
return Boolean(
|
|
2560
|
+
process.env.ARTIFACT_CAFE_NO_UPDATE_CHECK ?? process.env.NO_UPDATE_NOTIFIER ?? process.env.CI
|
|
2561
|
+
);
|
|
2562
|
+
}
|
|
2563
|
+
function startUpdateCheck() {
|
|
2564
|
+
if (updateChecksDisabled()) {
|
|
2565
|
+
return () => Promise.resolve();
|
|
2566
|
+
}
|
|
2567
|
+
const pending = (async () => {
|
|
2568
|
+
const cached = readCache();
|
|
2569
|
+
if (cached && Date.now() - cached.checkedAt < CHECK_TTL_MS) {
|
|
2570
|
+
return cached;
|
|
2571
|
+
}
|
|
2572
|
+
const fresh = await fetchLatestVersions(cached);
|
|
2573
|
+
writeCache(fresh);
|
|
2574
|
+
return fresh;
|
|
2575
|
+
})().catch(() => null);
|
|
2576
|
+
return async () => {
|
|
2577
|
+
const latest = await pending;
|
|
2578
|
+
if (!latest) {
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2581
|
+
const notices = [];
|
|
2582
|
+
const current = cliVersion();
|
|
2583
|
+
if (latest.cliLatest && isNewerVersion(latest.cliLatest, current)) {
|
|
2584
|
+
notices.push(
|
|
2585
|
+
`[artifact-cafe] CLI update available: ${current} \u2192 ${latest.cliLatest}. Run commands via \`npx artifact-cafe@latest\`; update a global install with \`npm i -g artifact-cafe@latest\`.`
|
|
2586
|
+
);
|
|
2587
|
+
}
|
|
2588
|
+
if (latest.skillLatest) {
|
|
2589
|
+
const stale = installedSkillVersions().filter(
|
|
2590
|
+
(version) => isNewerVersion(latest.skillLatest, version)
|
|
2591
|
+
).sort((a, b) => isNewerVersion(a, b) ? 1 : -1);
|
|
2592
|
+
if (stale.length > 0) {
|
|
2593
|
+
notices.push(
|
|
2594
|
+
`[artifact-cafe] Skill update available: ${stale[0]} \u2192 ${latest.skillLatest}. Update: \`${SKILL_UPDATE_COMMAND}\` (drop -g for a repo-local copy), then re-read SKILL.md.`
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
if (notices.length > 0) {
|
|
2599
|
+
console.error(`
|
|
2600
|
+
${notices.join("\n")}`);
|
|
2601
|
+
}
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
|
|
1874
2605
|
// src/workspaces.ts
|
|
1875
2606
|
async function workspaces(opts) {
|
|
1876
2607
|
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
@@ -2292,6 +3023,247 @@ var protect2 = defineCommand({
|
|
|
2292
3023
|
}
|
|
2293
3024
|
}
|
|
2294
3025
|
});
|
|
3026
|
+
var threadStatusCommand = (action) => defineCommand({
|
|
3027
|
+
args: {
|
|
3028
|
+
"api-key": {
|
|
3029
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3030
|
+
type: "string"
|
|
3031
|
+
},
|
|
3032
|
+
artifact: {
|
|
3033
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3034
|
+
type: "string"
|
|
3035
|
+
},
|
|
3036
|
+
dir: {
|
|
3037
|
+
default: ".",
|
|
3038
|
+
description: "Linked directory to authorize from (default: .)",
|
|
3039
|
+
type: "string"
|
|
3040
|
+
},
|
|
3041
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
3042
|
+
thread: {
|
|
3043
|
+
description: "Thread id (from `comments --json`)",
|
|
3044
|
+
required: true,
|
|
3045
|
+
type: "positional"
|
|
3046
|
+
}
|
|
3047
|
+
},
|
|
3048
|
+
meta: {
|
|
3049
|
+
description: action === "resolve" ? "Mark a comment thread resolved" : "Reopen a resolved comment thread",
|
|
3050
|
+
name: action
|
|
3051
|
+
},
|
|
3052
|
+
async run({ args }) {
|
|
3053
|
+
try {
|
|
3054
|
+
await changeThreadStatus({
|
|
3055
|
+
action,
|
|
3056
|
+
apiKey: args["api-key"],
|
|
3057
|
+
artifact: args.artifact,
|
|
3058
|
+
dir: args.dir,
|
|
3059
|
+
json: Boolean(args.json),
|
|
3060
|
+
thread: args.thread
|
|
3061
|
+
});
|
|
3062
|
+
} catch (err) {
|
|
3063
|
+
printCliError(err, Boolean(args.json));
|
|
3064
|
+
process.exit(1);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
});
|
|
3068
|
+
var resolve = threadStatusCommand("resolve");
|
|
3069
|
+
var reopen = threadStatusCommand("reopen");
|
|
3070
|
+
var reply = defineCommand({
|
|
3071
|
+
args: {
|
|
3072
|
+
"api-key": {
|
|
3073
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3074
|
+
type: "string"
|
|
3075
|
+
},
|
|
3076
|
+
artifact: {
|
|
3077
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3078
|
+
type: "string"
|
|
3079
|
+
},
|
|
3080
|
+
body: {
|
|
3081
|
+
description: "Reply text (omit to read from stdin)",
|
|
3082
|
+
type: "string"
|
|
3083
|
+
},
|
|
3084
|
+
dir: {
|
|
3085
|
+
default: ".",
|
|
3086
|
+
description: "Linked directory to authorize from (default: .)",
|
|
3087
|
+
type: "string"
|
|
3088
|
+
},
|
|
3089
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
3090
|
+
resolve: {
|
|
3091
|
+
description: "Resolve the thread after replying",
|
|
3092
|
+
type: "boolean"
|
|
3093
|
+
},
|
|
3094
|
+
thread: {
|
|
3095
|
+
description: "Thread id (from `comments --json`)",
|
|
3096
|
+
required: true,
|
|
3097
|
+
type: "positional"
|
|
3098
|
+
}
|
|
3099
|
+
},
|
|
3100
|
+
meta: {
|
|
3101
|
+
description: "Reply to a comment thread (as the agent or owner)",
|
|
3102
|
+
name: "reply"
|
|
3103
|
+
},
|
|
3104
|
+
async run({ args }) {
|
|
3105
|
+
try {
|
|
3106
|
+
await replyToThread({
|
|
3107
|
+
apiKey: args["api-key"],
|
|
3108
|
+
artifact: args.artifact,
|
|
3109
|
+
body: args.body,
|
|
3110
|
+
dir: args.dir,
|
|
3111
|
+
json: Boolean(args.json),
|
|
3112
|
+
resolve: Boolean(args.resolve),
|
|
3113
|
+
thread: args.thread
|
|
3114
|
+
});
|
|
3115
|
+
} catch (err) {
|
|
3116
|
+
printCliError(err, Boolean(args.json));
|
|
3117
|
+
process.exit(1);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
});
|
|
3121
|
+
var comment2 = defineCommand({
|
|
3122
|
+
args: {
|
|
3123
|
+
"api-key": {
|
|
3124
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3125
|
+
type: "string"
|
|
3126
|
+
},
|
|
3127
|
+
artifact: {
|
|
3128
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3129
|
+
type: "string"
|
|
3130
|
+
},
|
|
3131
|
+
body: {
|
|
3132
|
+
description: "Comment text (omit to read from stdin)",
|
|
3133
|
+
type: "string"
|
|
3134
|
+
},
|
|
3135
|
+
dir: {
|
|
3136
|
+
default: ".",
|
|
3137
|
+
description: "Published/linked directory",
|
|
3138
|
+
type: "positional"
|
|
3139
|
+
},
|
|
3140
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
3141
|
+
page: {
|
|
3142
|
+
description: "Scope to one document (e.g. pages/notes.html)",
|
|
3143
|
+
type: "string"
|
|
3144
|
+
},
|
|
3145
|
+
prefix: {
|
|
3146
|
+
description: "Text just before --quote (disambiguates repeats)",
|
|
3147
|
+
type: "string"
|
|
3148
|
+
},
|
|
3149
|
+
quote: {
|
|
3150
|
+
description: "Anchor to this text (omit for a page-level comment)",
|
|
3151
|
+
type: "string"
|
|
3152
|
+
},
|
|
3153
|
+
suffix: {
|
|
3154
|
+
description: "Text just after --quote (disambiguates repeats)",
|
|
3155
|
+
type: "string"
|
|
3156
|
+
},
|
|
3157
|
+
via: {
|
|
3158
|
+
description: 'Attribution label, e.g. "Claude Code"',
|
|
3159
|
+
type: "string"
|
|
3160
|
+
}
|
|
3161
|
+
},
|
|
3162
|
+
meta: {
|
|
3163
|
+
description: "Open a comment thread (as your account)",
|
|
3164
|
+
name: "comment"
|
|
3165
|
+
},
|
|
3166
|
+
async run({ args }) {
|
|
3167
|
+
try {
|
|
3168
|
+
await comment({
|
|
3169
|
+
apiKey: args["api-key"],
|
|
3170
|
+
artifact: args.artifact,
|
|
3171
|
+
body: args.body,
|
|
3172
|
+
dir: args.dir,
|
|
3173
|
+
json: Boolean(args.json),
|
|
3174
|
+
page: args.page,
|
|
3175
|
+
prefix: args.prefix,
|
|
3176
|
+
quote: args.quote,
|
|
3177
|
+
suffix: args.suffix,
|
|
3178
|
+
via: args.via
|
|
3179
|
+
});
|
|
3180
|
+
} catch (err) {
|
|
3181
|
+
printCliError(err, Boolean(args.json));
|
|
3182
|
+
process.exit(1);
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
});
|
|
3186
|
+
var edit = defineCommand({
|
|
3187
|
+
args: {
|
|
3188
|
+
"api-key": {
|
|
3189
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3190
|
+
type: "string"
|
|
3191
|
+
},
|
|
3192
|
+
artifact: {
|
|
3193
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3194
|
+
type: "string"
|
|
3195
|
+
},
|
|
3196
|
+
body: {
|
|
3197
|
+
description: "New comment text (omit to read from stdin)",
|
|
3198
|
+
type: "string"
|
|
3199
|
+
},
|
|
3200
|
+
dir: {
|
|
3201
|
+
default: ".",
|
|
3202
|
+
description: "Linked directory to authorize from (default: .)",
|
|
3203
|
+
type: "string"
|
|
3204
|
+
},
|
|
3205
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
3206
|
+
message: {
|
|
3207
|
+
description: "Message id (from `comments --json`)",
|
|
3208
|
+
required: true,
|
|
3209
|
+
type: "positional"
|
|
3210
|
+
}
|
|
3211
|
+
},
|
|
3212
|
+
meta: { description: "Edit a comment you authored", name: "edit" },
|
|
3213
|
+
async run({ args }) {
|
|
3214
|
+
try {
|
|
3215
|
+
await editMessage({
|
|
3216
|
+
apiKey: args["api-key"],
|
|
3217
|
+
artifact: args.artifact,
|
|
3218
|
+
body: args.body,
|
|
3219
|
+
dir: args.dir,
|
|
3220
|
+
json: Boolean(args.json),
|
|
3221
|
+
message: args.message
|
|
3222
|
+
});
|
|
3223
|
+
} catch (err) {
|
|
3224
|
+
printCliError(err, Boolean(args.json));
|
|
3225
|
+
process.exit(1);
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
});
|
|
3229
|
+
var del = defineCommand({
|
|
3230
|
+
args: {
|
|
3231
|
+
"api-key": {
|
|
3232
|
+
description: "Authorize with an account API key (default: stored login)",
|
|
3233
|
+
type: "string"
|
|
3234
|
+
},
|
|
3235
|
+
artifact: {
|
|
3236
|
+
description: "Act on an artifact ID instead of the linked folder",
|
|
3237
|
+
type: "string"
|
|
3238
|
+
},
|
|
3239
|
+
dir: {
|
|
3240
|
+
default: ".",
|
|
3241
|
+
description: "Linked directory to authorize from (default: .)",
|
|
3242
|
+
type: "string"
|
|
3243
|
+
},
|
|
3244
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
3245
|
+
message: {
|
|
3246
|
+
description: "Message id (from `comments --json`)",
|
|
3247
|
+
required: true,
|
|
3248
|
+
type: "positional"
|
|
3249
|
+
}
|
|
3250
|
+
},
|
|
3251
|
+
meta: { description: "Delete a comment you authored", name: "delete" },
|
|
3252
|
+
async run({ args }) {
|
|
3253
|
+
try {
|
|
3254
|
+
await deleteMessage({
|
|
3255
|
+
apiKey: args["api-key"],
|
|
3256
|
+
artifact: args.artifact,
|
|
3257
|
+
dir: args.dir,
|
|
3258
|
+
json: Boolean(args.json),
|
|
3259
|
+
message: args.message
|
|
3260
|
+
});
|
|
3261
|
+
} catch (err) {
|
|
3262
|
+
printCliError(err, Boolean(args.json));
|
|
3263
|
+
process.exit(1);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
2295
3267
|
var open = defineCommand({
|
|
2296
3268
|
args: {
|
|
2297
3269
|
"api-key": {
|
|
@@ -2346,7 +3318,8 @@ var dashboard = defineCommand({
|
|
|
2346
3318
|
var main = defineCommand({
|
|
2347
3319
|
meta: {
|
|
2348
3320
|
description: "Publish AI-generated artifacts for review \u2014 https://artifact.cafe",
|
|
2349
|
-
name: "artifact-cafe"
|
|
3321
|
+
name: "artifact-cafe",
|
|
3322
|
+
version: cliVersion()
|
|
2350
3323
|
},
|
|
2351
3324
|
// Bare `artifact-cafe` (no subcommand) → brand banner + usage. citty
|
|
2352
3325
|
// still calls this parent run *after* a matched subcommand, so bail when
|
|
@@ -2360,8 +3333,11 @@ var main = defineCommand({
|
|
|
2360
3333
|
await showUsage(cmd);
|
|
2361
3334
|
},
|
|
2362
3335
|
subCommands: {
|
|
3336
|
+
comment: comment2,
|
|
2363
3337
|
comments: comments2,
|
|
2364
3338
|
dashboard,
|
|
3339
|
+
delete: del,
|
|
3340
|
+
edit,
|
|
2365
3341
|
folders: folders2,
|
|
2366
3342
|
link: link2,
|
|
2367
3343
|
list: list2,
|
|
@@ -2371,8 +3347,13 @@ var main = defineCommand({
|
|
|
2371
3347
|
open,
|
|
2372
3348
|
protect: protect2,
|
|
2373
3349
|
publish: publish2,
|
|
3350
|
+
reopen,
|
|
3351
|
+
reply,
|
|
3352
|
+
resolve,
|
|
2374
3353
|
whoami: whoami2,
|
|
2375
3354
|
workspaces: workspaces2
|
|
2376
3355
|
}
|
|
2377
3356
|
});
|
|
2378
|
-
|
|
3357
|
+
var finishUpdateCheck = startUpdateCheck();
|
|
3358
|
+
await runMain(main);
|
|
3359
|
+
await finishUpdateCheck();
|