artifact-cafe 0.0.17 → 0.1.0
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 +1866 -1571
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ var TICK_TOP = " \u250C \u2510";
|
|
|
11
11
|
var GLYPH = " \xE3";
|
|
12
12
|
var TICK_BOTTOM = " \u2514 \u2518";
|
|
13
13
|
var WORDMARK = " artifact.cafe";
|
|
14
|
-
|
|
14
|
+
var printBanner = () => {
|
|
15
15
|
const color = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
16
16
|
const paint = (line, codes) => color ? `${codes}${line}${RESET}` : line;
|
|
17
17
|
console.log();
|
|
@@ -21,10 +21,10 @@ function printBanner() {
|
|
|
21
21
|
console.log();
|
|
22
22
|
console.log(paint(WORDMARK, AMBER + BOLD));
|
|
23
23
|
console.log();
|
|
24
|
-
}
|
|
24
|
+
};
|
|
25
25
|
|
|
26
26
|
// src/comment.ts
|
|
27
|
-
import
|
|
27
|
+
import path5 from "path";
|
|
28
28
|
|
|
29
29
|
// ../shared/src/account.ts
|
|
30
30
|
import { z } from "zod";
|
|
@@ -61,17 +61,37 @@ var meResponseSchema = z.object({
|
|
|
61
61
|
keyName: z.string().optional(),
|
|
62
62
|
userId: z.string()
|
|
63
63
|
});
|
|
64
|
+
var sessionSummarySchema = z.object({
|
|
65
|
+
createdAt: z.string(),
|
|
66
|
+
current: z.boolean(),
|
|
67
|
+
id: z.string(),
|
|
68
|
+
ipAddress: z.string().nullable(),
|
|
69
|
+
lastActiveAt: z.string(),
|
|
70
|
+
userAgent: z.string().nullable()
|
|
71
|
+
});
|
|
72
|
+
var sessionListResponseSchema = z.object({
|
|
73
|
+
sessions: z.array(sessionSummarySchema)
|
|
74
|
+
});
|
|
75
|
+
var revokeSessionsResponseSchema = z.object({
|
|
76
|
+
revoked: z.number().int().nonnegative()
|
|
77
|
+
});
|
|
78
|
+
var deleteAccountSchema = z.object({
|
|
79
|
+
confirmEmail: z.string().trim().min(1).max(320)
|
|
80
|
+
});
|
|
81
|
+
var deleteAccountResponseSchema = z.object({
|
|
82
|
+
deleted: z.literal(true)
|
|
83
|
+
});
|
|
64
84
|
var artifactListItemSchema = z.object({
|
|
65
|
-
autoDeleteAt: z.
|
|
85
|
+
autoDeleteAt: z.iso.datetime().nullable().optional(),
|
|
66
86
|
createdAt: z.string(),
|
|
67
87
|
currentVersionNumber: z.number().int().positive().nullable(),
|
|
68
|
-
expiresAt: z.
|
|
88
|
+
expiresAt: z.iso.datetime().nullable().optional(),
|
|
69
89
|
id: z.string(),
|
|
70
90
|
openCommentCount: z.number().int().nonnegative(),
|
|
71
91
|
slug: z.string(),
|
|
72
92
|
title: z.string(),
|
|
73
93
|
updatedAt: z.string(),
|
|
74
|
-
url: z.
|
|
94
|
+
url: z.url(),
|
|
75
95
|
versionCount: z.number().int().nonnegative()
|
|
76
96
|
});
|
|
77
97
|
var artifactListResponseSchema = z.object({
|
|
@@ -86,7 +106,7 @@ var createAgentTokenSchema = z2.object({
|
|
|
86
106
|
ttlSeconds: z2.number().int().positive().max(3600)
|
|
87
107
|
});
|
|
88
108
|
var agentTokenResponseSchema = z2.object({
|
|
89
|
-
expiresAt: z2.
|
|
109
|
+
expiresAt: z2.iso.datetime(),
|
|
90
110
|
token: z2.string(),
|
|
91
111
|
workspaceId: z2.string()
|
|
92
112
|
});
|
|
@@ -127,13 +147,13 @@ var AGENT_BRANDS = [
|
|
|
127
147
|
},
|
|
128
148
|
{ id: "hermes", label: "Hermes", match: ["hermes"], short: "Hermes" }
|
|
129
149
|
];
|
|
130
|
-
|
|
150
|
+
var normalizeAgent = (raw) => {
|
|
131
151
|
const value = raw?.trim().toLowerCase();
|
|
132
152
|
if (!value) {
|
|
133
153
|
return null;
|
|
134
154
|
}
|
|
135
155
|
return AGENT_BRANDS.find((brand) => brand.id === value) ?? AGENT_BRANDS.find((brand) => brand.match.some((m) => value.includes(m))) ?? null;
|
|
136
|
-
}
|
|
156
|
+
};
|
|
137
157
|
var ENV_FINGERPRINTS = [
|
|
138
158
|
{ id: "claude-code", prefixes: ["CLAUDECODE", "CLAUDE_CODE_"] },
|
|
139
159
|
{ id: "codex", prefixes: ["CODEX_"] },
|
|
@@ -142,30 +162,24 @@ var ENV_FINGERPRINTS = [
|
|
|
142
162
|
{ id: "gemini-cli", prefixes: ["GEMINI_CLI", "GEMINI_SANDBOX"] },
|
|
143
163
|
{ id: "opencode", prefixes: ["OPENCODE"] }
|
|
144
164
|
];
|
|
145
|
-
|
|
165
|
+
var detectAgentFromEnv = (env) => {
|
|
146
166
|
const explicit = env.ARTIFACT_CAFE_AGENT?.trim();
|
|
147
167
|
if (explicit) {
|
|
148
168
|
return explicit;
|
|
149
169
|
}
|
|
150
170
|
const keys = Object.keys(env).filter((key) => env[key]);
|
|
151
171
|
for (const { id, prefixes } of ENV_FINGERPRINTS) {
|
|
152
|
-
const hit = keys.some(
|
|
153
|
-
(key) => prefixes.some((prefix) => key.startsWith(prefix))
|
|
154
|
-
);
|
|
172
|
+
const hit = keys.some((key) => prefixes.some((prefix) => key.startsWith(prefix)));
|
|
155
173
|
if (hit) {
|
|
156
174
|
return AGENT_BRANDS.find((brand) => brand.id === id)?.label;
|
|
157
175
|
}
|
|
158
176
|
}
|
|
159
177
|
return normalizeAgent(env.AI_AGENT)?.label;
|
|
160
|
-
}
|
|
178
|
+
};
|
|
161
179
|
|
|
162
180
|
// ../shared/src/avatars.ts
|
|
163
181
|
import { z as z3 } from "zod";
|
|
164
|
-
var AVATAR_CONTENT_TYPES = [
|
|
165
|
-
"image/png",
|
|
166
|
-
"image/jpeg",
|
|
167
|
-
"image/webp"
|
|
168
|
-
];
|
|
182
|
+
var AVATAR_CONTENT_TYPES = ["image/png", "image/jpeg", "image/webp"];
|
|
169
183
|
var AVATAR_MAX_BYTES = 2 * 1024 * 1024;
|
|
170
184
|
var avatarContentTypeSchema = z3.enum(AVATAR_CONTENT_TYPES);
|
|
171
185
|
var userAvatarResponseSchema = z3.object({
|
|
@@ -181,10 +195,8 @@ import { z as z8 } from "zod";
|
|
|
181
195
|
// ../shared/src/emoji.ts
|
|
182
196
|
import { z as z4 } from "zod";
|
|
183
197
|
var SINGLE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
|
|
184
|
-
var REDUNDANT_VS16 = new RegExp("(
|
|
185
|
-
|
|
186
|
-
return s.replace(REDUNDANT_VS16, "$1");
|
|
187
|
-
}
|
|
198
|
+
var REDUNDANT_VS16 = new RegExp("(?<emoji>\\p{Emoji_Presentation})\\uFE0F", "gu");
|
|
199
|
+
var canonicalizeEmoji = (s) => s.replace(REDUNDANT_VS16, "$1");
|
|
188
200
|
var iconEmojiSchema = z4.string().max(64).transform(canonicalizeEmoji).refine((s) => SINGLE_EMOJI.test(s), { message: "Must be a single emoji." }).nullable().describe("A single emoji shown as the icon, or null for the default.");
|
|
189
201
|
|
|
190
202
|
// ../shared/src/publish.ts
|
|
@@ -198,20 +210,24 @@ var setExpirationSchema = z5.object({
|
|
|
198
210
|
expiresInSeconds: expirationSecondsSchema.nullable()
|
|
199
211
|
});
|
|
200
212
|
var expirationResponseSchema = z5.object({
|
|
201
|
-
autoDeleteAt: z5.
|
|
202
|
-
expiresAt: z5.
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
const match = /^([1-9]\d*)([mhd])
|
|
206
|
-
const units =
|
|
207
|
-
|
|
213
|
+
autoDeleteAt: z5.iso.datetime().nullable(),
|
|
214
|
+
expiresAt: z5.iso.datetime().nullable()
|
|
215
|
+
});
|
|
216
|
+
var parseArtifactDuration = (value) => {
|
|
217
|
+
const match = /^(?<amount>[1-9]\d*)(?<unit>[mhd])$/u.exec(value);
|
|
218
|
+
const units = /* @__PURE__ */ new Map([
|
|
219
|
+
["d", 86400],
|
|
220
|
+
["h", 3600],
|
|
221
|
+
["m", 60]
|
|
222
|
+
]);
|
|
223
|
+
const seconds = match ? Number(match[1]) * (units.get(match[2] ?? "") ?? 0) : 0;
|
|
208
224
|
if (!expirationSecondsSchema.safeParse(seconds).success) {
|
|
209
225
|
throw new Error(
|
|
210
226
|
`Duration must be a positive whole number followed by m, h, or d (for example 30m, 1h, 7d), up to ${MAX_EXPIRATION_SECONDS} seconds.`
|
|
211
227
|
);
|
|
212
228
|
}
|
|
213
229
|
return seconds;
|
|
214
|
-
}
|
|
230
|
+
};
|
|
215
231
|
|
|
216
232
|
// ../shared/src/review-mode.ts
|
|
217
233
|
import { z as z6 } from "zod";
|
|
@@ -224,7 +240,7 @@ var reviewModeResponseSchema = z6.object({
|
|
|
224
240
|
});
|
|
225
241
|
|
|
226
242
|
// ../shared/src/publish.ts
|
|
227
|
-
|
|
243
|
+
var isSafeArtifactPath = (p) => {
|
|
228
244
|
if (p.length === 0 || p.length > 1024) {
|
|
229
245
|
return false;
|
|
230
246
|
}
|
|
@@ -235,12 +251,12 @@ function isSafeArtifactPath(p) {
|
|
|
235
251
|
return false;
|
|
236
252
|
}
|
|
237
253
|
return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
|
|
238
|
-
}
|
|
254
|
+
};
|
|
239
255
|
var CARD_DESCRIPTION_MAX = 300;
|
|
240
256
|
var CARD_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
241
257
|
var manifestFileSchema = z7.object({
|
|
242
|
-
hash: z7.string().regex(/^sha256_[a-f0-9]{64}$/),
|
|
243
|
-
mimeType: z7.string().regex(/^[-\w.+]+\/[-\w.+]
|
|
258
|
+
hash: z7.string().regex(/^sha256_[a-f0-9]{64}$/u),
|
|
259
|
+
mimeType: z7.string().regex(/^[-\w.+]+\/[-\w.+]+$/u, { message: "invalid MIME type" }),
|
|
244
260
|
path: z7.string().min(1).max(1024).refine(isSafeArtifactPath, {
|
|
245
261
|
message: "path must be a safe relative path"
|
|
246
262
|
}),
|
|
@@ -253,6 +269,7 @@ var publishSourceSchema = z7.object({
|
|
|
253
269
|
});
|
|
254
270
|
var createPublishSessionSchema = z7.object({
|
|
255
271
|
artifactId: z7.string().nullable(),
|
|
272
|
+
baseVersion: z7.number().int().positive().optional(),
|
|
256
273
|
// One-line share-card summary (`--description`). Overrides whatever the
|
|
257
274
|
// entry HTML declares; leave it off and the API reads the document's own
|
|
258
275
|
// `og:description`/`description` at finalize.
|
|
@@ -264,6 +281,7 @@ var createPublishSessionSchema = z7.object({
|
|
|
264
281
|
// members; the API enforces membership and folder ownership. folderId
|
|
265
282
|
// without workspaceId resolves against the account's personal workspace.
|
|
266
283
|
folderId: z7.string().min(1).optional(),
|
|
284
|
+
force: z7.boolean().optional(),
|
|
267
285
|
// Bullet-point changelog for this version, authored by the publisher.
|
|
268
286
|
// Optional on every publish (incl. v1); stored immutably on the version.
|
|
269
287
|
notes: z7.string().max(4e3).optional(),
|
|
@@ -279,7 +297,7 @@ var publishSessionResponseSchema = z7.object({
|
|
|
279
297
|
z7.object({
|
|
280
298
|
method: z7.literal("PUT"),
|
|
281
299
|
path: z7.string(),
|
|
282
|
-
uploadUrl: z7.
|
|
300
|
+
uploadUrl: z7.url()
|
|
283
301
|
})
|
|
284
302
|
),
|
|
285
303
|
publishSessionId: z7.string(),
|
|
@@ -288,15 +306,15 @@ var publishSessionResponseSchema = z7.object({
|
|
|
288
306
|
});
|
|
289
307
|
var finalizeResponseSchema = z7.object({
|
|
290
308
|
artifactId: z7.string(),
|
|
291
|
-
autoDeleteAt: z7.
|
|
309
|
+
autoDeleteAt: z7.iso.datetime().nullable().optional(),
|
|
310
|
+
// Returned only on first publish of a new artifact (one-time).
|
|
311
|
+
claimUrl: z7.url().optional(),
|
|
292
312
|
// API-key publishes create the artifact already claimed (SPEC §5.4) —
|
|
293
313
|
// the CLI uses this to tell "claim URL pending" from "already yours".
|
|
294
314
|
claimed: z7.boolean(),
|
|
295
|
-
|
|
296
|
-
claimUrl: z7.string().url().optional(),
|
|
297
|
-
expiresAt: z7.string().datetime().nullable().optional(),
|
|
315
|
+
expiresAt: z7.iso.datetime().nullable().optional(),
|
|
298
316
|
publishToken: z7.string().optional(),
|
|
299
|
-
url: z7.
|
|
317
|
+
url: z7.url(),
|
|
300
318
|
versionId: z7.string(),
|
|
301
319
|
versionNumber: z7.number().int().positive(),
|
|
302
320
|
// SPEC-2 §5.2 — who can open the review URL. A publish into a folder
|
|
@@ -308,16 +326,16 @@ var finalizeResponseSchema = z7.object({
|
|
|
308
326
|
workspaceId: z7.string().nullable().optional()
|
|
309
327
|
});
|
|
310
328
|
var artifactResponseSchema = z7.object({
|
|
311
|
-
autoDeleteAt: z7.
|
|
329
|
+
autoDeleteAt: z7.iso.datetime().nullable().optional(),
|
|
312
330
|
claimed: z7.boolean(),
|
|
313
331
|
currentVersion: z7.object({
|
|
314
|
-
entryUrl: z7.
|
|
332
|
+
entryUrl: z7.url(),
|
|
315
333
|
id: z7.string(),
|
|
316
334
|
versionNumber: z7.number().int().positive()
|
|
317
335
|
}).nullable(),
|
|
318
336
|
// Custom icon, or null to render the default document glyph.
|
|
319
337
|
emoji: z7.string().nullable(),
|
|
320
|
-
expiresAt: z7.
|
|
338
|
+
expiresAt: z7.iso.datetime().nullable().optional(),
|
|
321
339
|
// SPEC-2 §6.4 — placement + visibility surface on the artifact.
|
|
322
340
|
folderId: z7.string().nullable(),
|
|
323
341
|
id: z7.string(),
|
|
@@ -328,7 +346,7 @@ var artifactResponseSchema = z7.object({
|
|
|
328
346
|
visibility: z7.enum(["public", "unlisted", "workspace", "private"]),
|
|
329
347
|
workspaceId: z7.string().nullable()
|
|
330
348
|
});
|
|
331
|
-
|
|
349
|
+
var resolvePublishEntry = (files, explicit) => {
|
|
332
350
|
if (explicit !== void 0) {
|
|
333
351
|
if (!files.some((file) => file.path === explicit)) {
|
|
334
352
|
throw new Error(`Entry file "${explicit}" is not in the manifest.`);
|
|
@@ -347,7 +365,7 @@ function resolvePublishEntry(files, explicit) {
|
|
|
347
365
|
);
|
|
348
366
|
}
|
|
349
367
|
return htmlFiles[0]?.path;
|
|
350
|
-
}
|
|
368
|
+
};
|
|
351
369
|
|
|
352
370
|
// ../shared/src/comments.ts
|
|
353
371
|
var anchorPageSchema = z8.string().max(1024).refine(isSafeArtifactPath, {
|
|
@@ -533,17 +551,24 @@ var commentExportSchema = z8.object({
|
|
|
533
551
|
var commentReadResponseSchema = z8.object({
|
|
534
552
|
lastSeenAt: z8.string()
|
|
535
553
|
});
|
|
536
|
-
|
|
554
|
+
var elementLabel = (el) => {
|
|
537
555
|
if (el.id) {
|
|
538
556
|
return `<${el.tag}#${el.id}>`;
|
|
539
557
|
}
|
|
540
558
|
const classes = el.classes ? el.classes.split(" ").slice(0, 2) : [];
|
|
541
559
|
return classes.length ? `<${el.tag}.${classes.join(".")}>` : `<${el.tag}>`;
|
|
542
|
-
}
|
|
560
|
+
};
|
|
543
561
|
|
|
544
562
|
// ../shared/src/data.ts
|
|
563
|
+
var canParseUrl = (value) => {
|
|
564
|
+
try {
|
|
565
|
+
return Boolean(new URL(value));
|
|
566
|
+
} catch {
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
545
570
|
var DATA_MANIFEST_PATH = ".artifact/data.json";
|
|
546
|
-
var DATA_NAME_RE = /^[a-z][a-z0-9_]
|
|
571
|
+
var DATA_NAME_RE = /^[a-z][a-z0-9_]*$/u;
|
|
547
572
|
var DATA_NAME_MAX = 64;
|
|
548
573
|
var DATA_RESERVED_FIELDS = /* @__PURE__ */ new Set([
|
|
549
574
|
"id",
|
|
@@ -573,37 +598,53 @@ var DATA_ACCESS_DEFAULTS = {
|
|
|
573
598
|
read: "public",
|
|
574
599
|
update: "owner"
|
|
575
600
|
};
|
|
576
|
-
var DATA_RATE_LIMIT_RE = /^([1-9]\d{0,5})\/(hour|minute)\/ip
|
|
601
|
+
var DATA_RATE_LIMIT_RE = /^(?<count>[1-9]\d{0,5})\/(?<period>hour|minute)\/ip$/u;
|
|
577
602
|
var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
578
|
-
|
|
603
|
+
var validateStringField = (name, field, value, errors) => {
|
|
604
|
+
if (typeof value !== "string") {
|
|
605
|
+
errors.push(`${name}: expected a string`);
|
|
606
|
+
return value;
|
|
607
|
+
}
|
|
608
|
+
const s = field.trim ? value.trim() : value;
|
|
609
|
+
if (field.maxLength !== void 0 && s.length > field.maxLength) {
|
|
610
|
+
errors.push(`${name}: longer than maxLength ${field.maxLength}`);
|
|
611
|
+
}
|
|
612
|
+
return s;
|
|
613
|
+
};
|
|
614
|
+
var validateNumberField = (name, field, value, errors) => {
|
|
615
|
+
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
616
|
+
errors.push(`${name}: expected a ${field.type}`);
|
|
617
|
+
return value;
|
|
618
|
+
}
|
|
619
|
+
if (field.type === "integer" && !Number.isInteger(value)) {
|
|
620
|
+
errors.push(`${name}: expected an integer`);
|
|
621
|
+
}
|
|
622
|
+
if (field.minimum !== void 0 && value < field.minimum) {
|
|
623
|
+
errors.push(`${name}: below minimum ${field.minimum}`);
|
|
624
|
+
}
|
|
625
|
+
if (field.maximum !== void 0 && value > field.maximum) {
|
|
626
|
+
errors.push(`${name}: above maximum ${field.maximum}`);
|
|
627
|
+
}
|
|
628
|
+
return value;
|
|
629
|
+
};
|
|
630
|
+
var validateArrayField = (name, field, value, errors) => {
|
|
631
|
+
if (!Array.isArray(value)) {
|
|
632
|
+
errors.push(`${name}: expected an array`);
|
|
633
|
+
return value;
|
|
634
|
+
}
|
|
635
|
+
if (field.maxLength !== void 0 && value.length > field.maxLength) {
|
|
636
|
+
errors.push(`${name}: more than maxLength ${field.maxLength} items`);
|
|
637
|
+
}
|
|
638
|
+
return value;
|
|
639
|
+
};
|
|
640
|
+
var validateFieldValue = (name, field, value, errors) => {
|
|
579
641
|
switch (field.type) {
|
|
580
642
|
case "string": {
|
|
581
|
-
|
|
582
|
-
errors.push(`${name}: expected a string`);
|
|
583
|
-
return value;
|
|
584
|
-
}
|
|
585
|
-
const s = field.trim ? value.trim() : value;
|
|
586
|
-
if (field.maxLength !== void 0 && s.length > field.maxLength) {
|
|
587
|
-
errors.push(`${name}: longer than maxLength ${field.maxLength}`);
|
|
588
|
-
}
|
|
589
|
-
return s;
|
|
643
|
+
return validateStringField(name, field, value, errors);
|
|
590
644
|
}
|
|
591
645
|
case "number":
|
|
592
646
|
case "integer": {
|
|
593
|
-
|
|
594
|
-
errors.push(`${name}: expected a ${field.type}`);
|
|
595
|
-
return value;
|
|
596
|
-
}
|
|
597
|
-
if (field.type === "integer" && !Number.isInteger(value)) {
|
|
598
|
-
errors.push(`${name}: expected an integer`);
|
|
599
|
-
}
|
|
600
|
-
if (field.minimum !== void 0 && value < field.minimum) {
|
|
601
|
-
errors.push(`${name}: below minimum ${field.minimum}`);
|
|
602
|
-
}
|
|
603
|
-
if (field.maximum !== void 0 && value > field.maximum) {
|
|
604
|
-
errors.push(`${name}: above maximum ${field.maximum}`);
|
|
605
|
-
}
|
|
606
|
-
return value;
|
|
647
|
+
return validateNumberField(name, field, value, errors);
|
|
607
648
|
}
|
|
608
649
|
case "boolean": {
|
|
609
650
|
if (typeof value !== "boolean") {
|
|
@@ -624,20 +665,13 @@ function validateFieldValue(name, field, value, errors) {
|
|
|
624
665
|
return value;
|
|
625
666
|
}
|
|
626
667
|
case "email": {
|
|
627
|
-
if (typeof value !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]
|
|
668
|
+
if (typeof value !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(value)) {
|
|
628
669
|
errors.push(`${name}: expected an email address`);
|
|
629
670
|
}
|
|
630
671
|
return value;
|
|
631
672
|
}
|
|
632
673
|
case "array": {
|
|
633
|
-
|
|
634
|
-
errors.push(`${name}: expected an array`);
|
|
635
|
-
return value;
|
|
636
|
-
}
|
|
637
|
-
if (field.maxLength !== void 0 && value.length > field.maxLength) {
|
|
638
|
-
errors.push(`${name}: more than maxLength ${field.maxLength} items`);
|
|
639
|
-
}
|
|
640
|
-
return value;
|
|
674
|
+
return validateArrayField(name, field, value, errors);
|
|
641
675
|
}
|
|
642
676
|
case "object": {
|
|
643
677
|
if (!isPlainObject(value)) {
|
|
@@ -650,15 +684,8 @@ function validateFieldValue(name, field, value, errors) {
|
|
|
650
684
|
return value;
|
|
651
685
|
}
|
|
652
686
|
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
try {
|
|
656
|
-
return Boolean(new URL(value));
|
|
657
|
-
} catch {
|
|
658
|
-
return false;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
function validateRecordInput(collection, input, opts = {}) {
|
|
687
|
+
};
|
|
688
|
+
var validateRecordInput = (collection, input, opts = {}) => {
|
|
662
689
|
if (!isPlainObject(input)) {
|
|
663
690
|
return { errors: ["record body must be a JSON object"], ok: false };
|
|
664
691
|
}
|
|
@@ -706,7 +733,7 @@ function validateRecordInput(collection, input, opts = {}) {
|
|
|
706
733
|
return { errors, ok: false };
|
|
707
734
|
}
|
|
708
735
|
return { data: data2, ok: true };
|
|
709
|
-
}
|
|
736
|
+
};
|
|
710
737
|
|
|
711
738
|
// ../shared/src/data-api.ts
|
|
712
739
|
import { z as z9 } from "zod";
|
|
@@ -751,6 +778,18 @@ var dataClearResponseSchema = z9.object({
|
|
|
751
778
|
|
|
752
779
|
// ../shared/src/data-manifest.ts
|
|
753
780
|
import { z as z10 } from "zod";
|
|
781
|
+
var defaultMatchesType = (field) => {
|
|
782
|
+
if (field.required && field.default === null) {
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
return validateRecordInput(
|
|
786
|
+
{
|
|
787
|
+
access: DATA_ACCESS_DEFAULTS,
|
|
788
|
+
fields: { value: { ...field, default: void 0 } }
|
|
789
|
+
},
|
|
790
|
+
{ value: field.default }
|
|
791
|
+
).ok;
|
|
792
|
+
};
|
|
754
793
|
var nameSchema = z10.string().max(DATA_NAME_MAX).regex(DATA_NAME_RE, "must match ^[a-z][a-z0-9_]*$");
|
|
755
794
|
var fieldTypeSchema = z10.enum([
|
|
756
795
|
"string",
|
|
@@ -765,8 +804,8 @@ var fieldTypeSchema = z10.enum([
|
|
|
765
804
|
]);
|
|
766
805
|
var fieldSchema = z10.strictObject({
|
|
767
806
|
default: z10.unknown().optional(),
|
|
768
|
-
maximum: z10.number().optional(),
|
|
769
807
|
maxLength: z10.number().int().positive().optional(),
|
|
808
|
+
maximum: z10.number().optional(),
|
|
770
809
|
minimum: z10.number().optional(),
|
|
771
810
|
required: z10.boolean().optional(),
|
|
772
811
|
trim: z10.boolean().optional(),
|
|
@@ -783,18 +822,6 @@ var fieldSchema = z10.strictObject({
|
|
|
783
822
|
});
|
|
784
823
|
}
|
|
785
824
|
});
|
|
786
|
-
function defaultMatchesType(field) {
|
|
787
|
-
if (field.required && field.default === null) {
|
|
788
|
-
return false;
|
|
789
|
-
}
|
|
790
|
-
return validateRecordInput(
|
|
791
|
-
{
|
|
792
|
-
access: DATA_ACCESS_DEFAULTS,
|
|
793
|
-
fields: { value: { ...field, default: void 0 } }
|
|
794
|
-
},
|
|
795
|
-
{ value: field.default }
|
|
796
|
-
).ok;
|
|
797
|
-
}
|
|
798
825
|
var accessLevelSchema = z10.enum(["public", "creator", "owner", "none"]);
|
|
799
826
|
var accessSchema = z10.strictObject({
|
|
800
827
|
delete: accessLevelSchema.default("owner"),
|
|
@@ -846,7 +873,7 @@ var dataManifestSchema = z10.strictObject({
|
|
|
846
873
|
});
|
|
847
874
|
}
|
|
848
875
|
});
|
|
849
|
-
|
|
876
|
+
var parseDataManifest = (text) => {
|
|
850
877
|
if (new TextEncoder().encode(text).length > DATA_LIMITS.maxManifestBytes) {
|
|
851
878
|
return {
|
|
852
879
|
errors: [`manifest larger than ${DATA_LIMITS.maxManifestBytes} bytes`],
|
|
@@ -856,9 +883,9 @@ function parseDataManifest(text) {
|
|
|
856
883
|
let raw;
|
|
857
884
|
try {
|
|
858
885
|
raw = JSON.parse(text);
|
|
859
|
-
} catch (
|
|
886
|
+
} catch (error) {
|
|
860
887
|
return {
|
|
861
|
-
errors: [`not valid JSON: ${
|
|
888
|
+
errors: [`not valid JSON: ${error instanceof Error ? error.message : error}`],
|
|
862
889
|
ok: false
|
|
863
890
|
};
|
|
864
891
|
}
|
|
@@ -872,13 +899,14 @@ function parseDataManifest(text) {
|
|
|
872
899
|
};
|
|
873
900
|
}
|
|
874
901
|
return { config: parsed.data, ok: true };
|
|
875
|
-
}
|
|
902
|
+
};
|
|
876
903
|
|
|
877
904
|
// ../shared/src/errors.ts
|
|
878
905
|
import { z as z11 } from "zod";
|
|
879
906
|
var apiErrorSchema = z11.object({
|
|
880
907
|
code: z11.string(),
|
|
881
|
-
|
|
908
|
+
currentVersion: z11.number().int().positive().nullable().optional(),
|
|
909
|
+
docsUrl: z11.url(),
|
|
882
910
|
message: z11.string(),
|
|
883
911
|
retryAfter: z11.number().int().positive().optional(),
|
|
884
912
|
scope: z11.enum(["ip", "artifact"]).optional()
|
|
@@ -906,7 +934,7 @@ var createGrantSchema = z13.object({
|
|
|
906
934
|
});
|
|
907
935
|
var grantSchema = z13.object({
|
|
908
936
|
createdAt: z13.string(),
|
|
909
|
-
email: z13.
|
|
937
|
+
email: z13.email().nullable(),
|
|
910
938
|
expiresAt: z13.string().nullable(),
|
|
911
939
|
id: z13.string(),
|
|
912
940
|
// The subject's avatar once they have an account; null otherwise.
|
|
@@ -923,7 +951,7 @@ var grantSchema = z13.object({
|
|
|
923
951
|
userId: z13.string().nullable()
|
|
924
952
|
});
|
|
925
953
|
var createdGrantSchema = grantSchema.extend({
|
|
926
|
-
url: z13.
|
|
954
|
+
url: z13.url()
|
|
927
955
|
});
|
|
928
956
|
var grantListResponseSchema = z13.object({
|
|
929
957
|
grants: z13.array(grantSchema),
|
|
@@ -948,7 +976,7 @@ var acceptGrantSchema = z13.object({
|
|
|
948
976
|
var acceptGrantResponseSchema = z13.object({
|
|
949
977
|
artifactId: z13.string(),
|
|
950
978
|
// The review shell URL to land on, carrying ?invited=1 for the banner.
|
|
951
|
-
artifactUrl: z13.
|
|
979
|
+
artifactUrl: z13.url(),
|
|
952
980
|
slug: z13.string(),
|
|
953
981
|
title: z13.string()
|
|
954
982
|
});
|
|
@@ -974,7 +1002,7 @@ var sharedWithMeArtifactSchema = z13.object({
|
|
|
974
1002
|
artifactId: z13.string(),
|
|
975
1003
|
slug: z13.string(),
|
|
976
1004
|
title: z13.string(),
|
|
977
|
-
url: z13.
|
|
1005
|
+
url: z13.url(),
|
|
978
1006
|
// Who shared it, for the "Shared with me" list.
|
|
979
1007
|
workspaceName: z13.string()
|
|
980
1008
|
});
|
|
@@ -984,13 +1012,105 @@ var sharedWithMeListResponseSchema = z13.object({
|
|
|
984
1012
|
|
|
985
1013
|
// ../shared/src/html-meta.ts
|
|
986
1014
|
var HEAD_SCAN_CHARS = 64 * 1024;
|
|
1015
|
+
var NAMED_ENTITIES = new Map(
|
|
1016
|
+
Object.entries({
|
|
1017
|
+
amp: "&",
|
|
1018
|
+
apos: "'",
|
|
1019
|
+
gt: ">",
|
|
1020
|
+
lt: "<",
|
|
1021
|
+
nbsp: " ",
|
|
1022
|
+
quot: '"'
|
|
1023
|
+
})
|
|
1024
|
+
);
|
|
987
1025
|
|
|
988
1026
|
// ../shared/src/me.ts
|
|
989
1027
|
import { z as z15 } from "zod";
|
|
990
1028
|
|
|
991
1029
|
// ../shared/src/workspaces.ts
|
|
992
1030
|
import { z as z14 } from "zod";
|
|
993
|
-
|
|
1031
|
+
|
|
1032
|
+
// ../shared/src/reserved.ts
|
|
1033
|
+
var VANITY_REDIRECTS = /* @__PURE__ */ new Map([
|
|
1034
|
+
["bharath", "https://bharath-sd6csg.artifactcafe.dev/"],
|
|
1035
|
+
["demo", "https://artifact-cafe-demo-vsuadu.artifactcafe.dev/"],
|
|
1036
|
+
["wilson", "https://wilson-nxzuc4.artifactcafe.dev/"],
|
|
1037
|
+
["yulong", "https://yulong-hoae20.artifactcafe.dev/"]
|
|
1038
|
+
]);
|
|
1039
|
+
var RESERVED_SLUGS = /* @__PURE__ */ new Set([
|
|
1040
|
+
...VANITY_REDIRECTS.keys(),
|
|
1041
|
+
"www",
|
|
1042
|
+
"api",
|
|
1043
|
+
"render",
|
|
1044
|
+
// Former auth-provider subdomains; kept reserved so a custom slug can't
|
|
1045
|
+
// shadow a future auth host.
|
|
1046
|
+
"clerk",
|
|
1047
|
+
"accounts",
|
|
1048
|
+
"claim",
|
|
1049
|
+
// The CLI key-management page lives at /cli (SPEC §6.14).
|
|
1050
|
+
"cli",
|
|
1051
|
+
"docs",
|
|
1052
|
+
"admin",
|
|
1053
|
+
"app",
|
|
1054
|
+
"auth",
|
|
1055
|
+
"login",
|
|
1056
|
+
"signin",
|
|
1057
|
+
"signup",
|
|
1058
|
+
"sign-in",
|
|
1059
|
+
"sign-up",
|
|
1060
|
+
"account",
|
|
1061
|
+
"settings",
|
|
1062
|
+
"dashboard",
|
|
1063
|
+
"workspace",
|
|
1064
|
+
"static",
|
|
1065
|
+
"assets",
|
|
1066
|
+
"cdn",
|
|
1067
|
+
"mail",
|
|
1068
|
+
"email",
|
|
1069
|
+
"status",
|
|
1070
|
+
"blog",
|
|
1071
|
+
"help",
|
|
1072
|
+
"support",
|
|
1073
|
+
"about",
|
|
1074
|
+
"pricing",
|
|
1075
|
+
"terms",
|
|
1076
|
+
"privacy",
|
|
1077
|
+
"examples",
|
|
1078
|
+
"new",
|
|
1079
|
+
"artifact",
|
|
1080
|
+
"artifacts",
|
|
1081
|
+
// Mail and DNS labels: a workspace holding one of these could receive
|
|
1082
|
+
// provider verification mail or shadow a record on the apex zone.
|
|
1083
|
+
"abuse",
|
|
1084
|
+
"autodiscover",
|
|
1085
|
+
"autoconfig",
|
|
1086
|
+
"ftp",
|
|
1087
|
+
"imap",
|
|
1088
|
+
"mx",
|
|
1089
|
+
"ns",
|
|
1090
|
+
"ns1",
|
|
1091
|
+
"ns2",
|
|
1092
|
+
"postmaster",
|
|
1093
|
+
"smtp",
|
|
1094
|
+
"webmail",
|
|
1095
|
+
// Security and legal contacts people expect to reach us at.
|
|
1096
|
+
"legal",
|
|
1097
|
+
"security",
|
|
1098
|
+
// Product routes and environment names a subdomain shouldn't shadow.
|
|
1099
|
+
"billing",
|
|
1100
|
+
"dev",
|
|
1101
|
+
"invite",
|
|
1102
|
+
"invites",
|
|
1103
|
+
"preview",
|
|
1104
|
+
"staging",
|
|
1105
|
+
"team",
|
|
1106
|
+
"teams",
|
|
1107
|
+
"test",
|
|
1108
|
+
"user",
|
|
1109
|
+
"users"
|
|
1110
|
+
]);
|
|
1111
|
+
|
|
1112
|
+
// ../shared/src/workspaces.ts
|
|
1113
|
+
var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/u;
|
|
994
1114
|
var workspaceHandleUnavailableReasonSchema = z14.enum([
|
|
995
1115
|
"held",
|
|
996
1116
|
"invalid",
|
|
@@ -1045,7 +1165,7 @@ var deleteWorkspaceResponseSchema = z14.object({
|
|
|
1045
1165
|
});
|
|
1046
1166
|
var workspaceMemberSchema = z14.object({
|
|
1047
1167
|
createdAt: z14.string(),
|
|
1048
|
-
email: z14.
|
|
1168
|
+
email: z14.email(),
|
|
1049
1169
|
image: z14.string().nullable(),
|
|
1050
1170
|
name: z14.string(),
|
|
1051
1171
|
role: workspaceRoleSchema,
|
|
@@ -1062,13 +1182,13 @@ var removeWorkspaceMemberResponseSchema = z14.object({
|
|
|
1062
1182
|
});
|
|
1063
1183
|
var createWorkspaceInviteSchema = z14.object({
|
|
1064
1184
|
email: z14.string().trim().toLowerCase().email().optional(),
|
|
1065
|
-
expiresAt: z14.
|
|
1185
|
+
expiresAt: z14.iso.datetime(),
|
|
1066
1186
|
maxUses: z14.number().int().positive().optional(),
|
|
1067
1187
|
role: workspaceInviteRoleSchema
|
|
1068
1188
|
});
|
|
1069
1189
|
var workspaceInviteSchema = z14.object({
|
|
1070
1190
|
createdAt: z14.string(),
|
|
1071
|
-
email: z14.
|
|
1191
|
+
email: z14.email().nullable(),
|
|
1072
1192
|
expiresAt: z14.string(),
|
|
1073
1193
|
id: z14.string(),
|
|
1074
1194
|
maxUses: z14.number().int().positive().nullable(),
|
|
@@ -1077,7 +1197,7 @@ var workspaceInviteSchema = z14.object({
|
|
|
1077
1197
|
useCount: z14.number().int().nonnegative()
|
|
1078
1198
|
});
|
|
1079
1199
|
var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
|
|
1080
|
-
url: z14.
|
|
1200
|
+
url: z14.url()
|
|
1081
1201
|
});
|
|
1082
1202
|
var workspaceInviteListResponseSchema = z14.object({
|
|
1083
1203
|
invites: z14.array(workspaceInviteSchema)
|
|
@@ -1093,12 +1213,7 @@ var acceptWorkspaceInviteResponseSchema = z14.object({
|
|
|
1093
1213
|
workspace: workspaceSchema
|
|
1094
1214
|
});
|
|
1095
1215
|
var folderVisibilitySchema = z14.enum(["public", "workspace"]);
|
|
1096
|
-
var artifactVisibilitySchema = z14.enum([
|
|
1097
|
-
"public",
|
|
1098
|
-
"unlisted",
|
|
1099
|
-
"workspace",
|
|
1100
|
-
"private"
|
|
1101
|
-
]);
|
|
1216
|
+
var artifactVisibilitySchema = z14.enum(["public", "unlisted", "workspace", "private"]);
|
|
1102
1217
|
var createFolderSchema = z14.object({
|
|
1103
1218
|
// Optional custom icon; omitted (or null) leaves the default glyph.
|
|
1104
1219
|
emoji: iconEmojiSchema.optional(),
|
|
@@ -1142,12 +1257,12 @@ var deleteFolderResponseSchema = z14.object({
|
|
|
1142
1257
|
deleted: z14.literal(true)
|
|
1143
1258
|
});
|
|
1144
1259
|
var workspaceArtifactSchema = z14.object({
|
|
1145
|
-
autoDeleteAt: z14.
|
|
1260
|
+
autoDeleteAt: z14.iso.datetime().nullable().optional(),
|
|
1146
1261
|
createdAt: z14.string(),
|
|
1147
1262
|
currentVersionNumber: z14.number().int().nullable(),
|
|
1148
1263
|
// Custom icon, or null to render the default document glyph.
|
|
1149
1264
|
emoji: z14.string().nullable(),
|
|
1150
|
-
expiresAt: z14.
|
|
1265
|
+
expiresAt: z14.iso.datetime().nullable().optional(),
|
|
1151
1266
|
folderId: z14.string().nullable(),
|
|
1152
1267
|
id: z14.string(),
|
|
1153
1268
|
openCommentCount: z14.number().int().nonnegative(),
|
|
@@ -1155,7 +1270,7 @@ var workspaceArtifactSchema = z14.object({
|
|
|
1155
1270
|
slug: z14.string(),
|
|
1156
1271
|
title: z14.string(),
|
|
1157
1272
|
updatedAt: z14.string(),
|
|
1158
|
-
url: z14.
|
|
1273
|
+
url: z14.url(),
|
|
1159
1274
|
versionCount: z14.number().int().nonnegative(),
|
|
1160
1275
|
visibility: artifactVisibilitySchema
|
|
1161
1276
|
});
|
|
@@ -1177,7 +1292,7 @@ var personSchema = z15.object({
|
|
|
1177
1292
|
name: z15.string()
|
|
1178
1293
|
});
|
|
1179
1294
|
var ownArtifactSchema = z15.object({
|
|
1180
|
-
autoDeleteAt: z15.
|
|
1295
|
+
autoDeleteAt: z15.iso.datetime().nullable().optional(),
|
|
1181
1296
|
// Everyone else who has published a version, oldest first. Any member may
|
|
1182
1297
|
// publish a new one (SPEC-2 §5.1), so an artifact's byline is often more
|
|
1183
1298
|
// than one person and the card stacks their faces rather than crediting
|
|
@@ -1248,12 +1363,12 @@ var revokePublishTokenResponseSchema = z15.object({
|
|
|
1248
1363
|
|
|
1249
1364
|
// ../shared/src/owner.ts
|
|
1250
1365
|
import { z as z16 } from "zod";
|
|
1251
|
-
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])
|
|
1366
|
+
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/u;
|
|
1252
1367
|
var claimResponseSchema = z16.object({
|
|
1253
1368
|
artifactId: z16.string(),
|
|
1254
1369
|
slug: z16.string(),
|
|
1255
1370
|
title: z16.string(),
|
|
1256
|
-
url: z16.
|
|
1371
|
+
url: z16.url()
|
|
1257
1372
|
});
|
|
1258
1373
|
var updateArtifactSchema = z16.object({
|
|
1259
1374
|
// Custom icon; explicit null clears it back to the default glyph.
|
|
@@ -1385,6 +1500,11 @@ var artifactStatsResponseSchema = z21.object({
|
|
|
1385
1500
|
|
|
1386
1501
|
// ../shared/src/versions.ts
|
|
1387
1502
|
import { z as z22 } from "zod";
|
|
1503
|
+
var versionFilesResponseSchema = z22.object({
|
|
1504
|
+
files: z22.array(manifestFileSchema.extend({ downloadUrl: z22.url() })),
|
|
1505
|
+
slug: z22.string(),
|
|
1506
|
+
versionNumber: z22.number().int().positive()
|
|
1507
|
+
});
|
|
1388
1508
|
var versionSourceSchema = z22.object({
|
|
1389
1509
|
agentName: z22.string().nullable(),
|
|
1390
1510
|
type: z22.string()
|
|
@@ -1398,7 +1518,7 @@ var versionSummarySchema = z22.object({
|
|
|
1398
1518
|
// Absent for the first version — there is no previous version to diff against.
|
|
1399
1519
|
changes: versionChangesSchema.nullable(),
|
|
1400
1520
|
createdAt: z22.string(),
|
|
1401
|
-
entryUrl: z22.
|
|
1521
|
+
entryUrl: z22.url(),
|
|
1402
1522
|
fileCount: z22.number().int().nonnegative(),
|
|
1403
1523
|
id: z22.string(),
|
|
1404
1524
|
isCurrent: z22.boolean(),
|
|
@@ -1419,34 +1539,237 @@ var versionListResponseSchema = z22.object({
|
|
|
1419
1539
|
});
|
|
1420
1540
|
|
|
1421
1541
|
// src/agent-identity.ts
|
|
1422
|
-
|
|
1542
|
+
var agentLabel = (explicit) => {
|
|
1423
1543
|
if (explicit !== void 0) {
|
|
1424
1544
|
return explicit.trim() || void 0;
|
|
1425
1545
|
}
|
|
1426
1546
|
return detectAgentFromEnv(process.env);
|
|
1427
|
-
}
|
|
1547
|
+
};
|
|
1548
|
+
|
|
1549
|
+
// src/install-id.ts
|
|
1550
|
+
import { randomUUID } from "crypto";
|
|
1551
|
+
import { mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
|
|
1552
|
+
import { homedir } from "os";
|
|
1553
|
+
import path from "path";
|
|
1554
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
1555
|
+
var resetInstallId = (file = path.join(homedir(), ".artifactcafe", "install-id")) => {
|
|
1556
|
+
const temporary = `${file}.${randomUUID()}`;
|
|
1557
|
+
try {
|
|
1558
|
+
mkdirSync(path.dirname(file), { mode: 448, recursive: true });
|
|
1559
|
+
writeFileSync(temporary, `${randomUUID()}
|
|
1560
|
+
`, { flag: "wx", mode: 384 });
|
|
1561
|
+
renameSync(temporary, file);
|
|
1562
|
+
} catch {
|
|
1563
|
+
} finally {
|
|
1564
|
+
try {
|
|
1565
|
+
unlinkSync(temporary);
|
|
1566
|
+
} catch {
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
};
|
|
1570
|
+
var readInstallId = (file = path.join(homedir(), ".artifactcafe", "install-id")) => {
|
|
1571
|
+
try {
|
|
1572
|
+
mkdirSync(path.dirname(file), { mode: 448, recursive: true });
|
|
1573
|
+
try {
|
|
1574
|
+
writeFileSync(file, `${randomUUID()}
|
|
1575
|
+
`, { flag: "wx", mode: 384 });
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) {
|
|
1578
|
+
return null;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
const id = readFileSync(file, "utf-8").trim();
|
|
1582
|
+
return UUID.test(id) ? id.toLowerCase() : null;
|
|
1583
|
+
} catch {
|
|
1584
|
+
return null;
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
// src/update.ts
|
|
1589
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1590
|
+
import { createRequire } from "module";
|
|
1591
|
+
import { homedir as homedir2 } from "os";
|
|
1592
|
+
import path2 from "path";
|
|
1593
|
+
import { z as z23 } from "zod";
|
|
1594
|
+
var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
|
|
1595
|
+
var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
|
|
1596
|
+
var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
|
|
1597
|
+
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1598
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
1599
|
+
var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(?<version>\d+\.\d+\.\d+)\*\*/u;
|
|
1600
|
+
var cacheSchema = z23.object({
|
|
1601
|
+
checkedAt: z23.number(),
|
|
1602
|
+
cliLatest: z23.string().nullable(),
|
|
1603
|
+
skillLatest: z23.string().nullable(),
|
|
1604
|
+
version: z23.literal(1)
|
|
1605
|
+
});
|
|
1606
|
+
var cliVersion = () => {
|
|
1607
|
+
const pkg = z23.object({ version: z23.string() }).parse(createRequire(import.meta.url)("../package.json"));
|
|
1608
|
+
return pkg.version;
|
|
1609
|
+
};
|
|
1610
|
+
var cachePath = () => path2.join(homedir2(), ".artifactcafe", "update-check.json");
|
|
1611
|
+
var readCache = () => {
|
|
1612
|
+
try {
|
|
1613
|
+
return cacheSchema.parse(JSON.parse(readFileSync2(cachePath(), "utf-8")));
|
|
1614
|
+
} catch {
|
|
1615
|
+
return null;
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
var writeCache = (cache) => {
|
|
1619
|
+
try {
|
|
1620
|
+
mkdirSync2(path2.dirname(cachePath()), { mode: 448, recursive: true });
|
|
1621
|
+
writeFileSync2(cachePath(), `${JSON.stringify(cache, null, 2)}
|
|
1622
|
+
`);
|
|
1623
|
+
} catch {
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
var parse = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
1627
|
+
var isNewerVersion = (candidate, current) => {
|
|
1628
|
+
const a = parse(candidate);
|
|
1629
|
+
const b = parse(current);
|
|
1630
|
+
for (let i = 0; i < 3; i += 1) {
|
|
1631
|
+
if ((a[i] ?? 0) !== (b[i] ?? 0)) {
|
|
1632
|
+
return (a[i] ?? 0) > (b[i] ?? 0);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
return false;
|
|
1636
|
+
};
|
|
1637
|
+
var fetchCliVersion = async () => {
|
|
1638
|
+
const url = REGISTRY_LATEST_URL;
|
|
1639
|
+
const res = await fetch(url, {
|
|
1640
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
1641
|
+
});
|
|
1642
|
+
if (!res.ok) {
|
|
1643
|
+
throw new Error(`GET ${url} \u2192 ${res.status}`);
|
|
1644
|
+
}
|
|
1645
|
+
return z23.object({ version: z23.string() }).parse(await res.json()).version;
|
|
1646
|
+
};
|
|
1647
|
+
var fetchText = async (url) => {
|
|
1648
|
+
const res = await fetch(url, {
|
|
1649
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
1650
|
+
});
|
|
1651
|
+
if (!res.ok) {
|
|
1652
|
+
throw new Error(`GET ${url} \u2192 ${res.status}`);
|
|
1653
|
+
}
|
|
1654
|
+
return res.text();
|
|
1655
|
+
};
|
|
1656
|
+
var fetchLatestVersions = async (previous) => {
|
|
1657
|
+
const [cli, skill] = await Promise.all([
|
|
1658
|
+
fetchCliVersion().catch(() => null),
|
|
1659
|
+
fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
|
|
1660
|
+
]);
|
|
1661
|
+
return {
|
|
1662
|
+
checkedAt: Date.now(),
|
|
1663
|
+
cliLatest: cli ?? previous?.cliLatest ?? null,
|
|
1664
|
+
skillLatest: skill ?? previous?.skillLatest ?? null,
|
|
1665
|
+
version: 1
|
|
1666
|
+
};
|
|
1667
|
+
};
|
|
1668
|
+
var installedSkillVersions = () => {
|
|
1669
|
+
const roots = [homedir2(), process.cwd()];
|
|
1670
|
+
const dirs = [".agents", ".claude"];
|
|
1671
|
+
const versions = [];
|
|
1672
|
+
for (const root of roots) {
|
|
1673
|
+
for (const dir of dirs) {
|
|
1674
|
+
const file = path2.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
|
|
1675
|
+
if (!existsSync(file)) {
|
|
1676
|
+
continue;
|
|
1677
|
+
}
|
|
1678
|
+
try {
|
|
1679
|
+
const found = SKILL_VERSION_PATTERN.exec(readFileSync2(file, "utf-8"));
|
|
1680
|
+
if (found?.[1]) {
|
|
1681
|
+
versions.push(found[1]);
|
|
1682
|
+
}
|
|
1683
|
+
} catch {
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return versions;
|
|
1688
|
+
};
|
|
1689
|
+
var updateChecksDisabled = () => Boolean(
|
|
1690
|
+
process.env.ARTIFACT_CAFE_NO_UPDATE_CHECK ?? process.env.NO_UPDATE_NOTIFIER ?? process.env.CI
|
|
1691
|
+
);
|
|
1692
|
+
var startUpdateCheck = () => {
|
|
1693
|
+
if (updateChecksDisabled()) {
|
|
1694
|
+
return () => Promise.resolve();
|
|
1695
|
+
}
|
|
1696
|
+
const pending = (async () => {
|
|
1697
|
+
try {
|
|
1698
|
+
const cached = readCache();
|
|
1699
|
+
if (cached && Date.now() - cached.checkedAt < CHECK_TTL_MS) {
|
|
1700
|
+
return cached;
|
|
1701
|
+
}
|
|
1702
|
+
const fresh = await fetchLatestVersions(cached);
|
|
1703
|
+
writeCache(fresh);
|
|
1704
|
+
return fresh;
|
|
1705
|
+
} catch {
|
|
1706
|
+
return null;
|
|
1707
|
+
}
|
|
1708
|
+
})();
|
|
1709
|
+
return async () => {
|
|
1710
|
+
const latest = await pending;
|
|
1711
|
+
if (!latest) {
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1714
|
+
const notices = [];
|
|
1715
|
+
const current = cliVersion();
|
|
1716
|
+
if (latest.cliLatest && isNewerVersion(latest.cliLatest, current)) {
|
|
1717
|
+
notices.push(
|
|
1718
|
+
`[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\`.`
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
const { skillLatest } = latest;
|
|
1722
|
+
if (skillLatest) {
|
|
1723
|
+
const stale = installedSkillVersions().filter((version) => isNewerVersion(skillLatest, version)).sort((a, b) => isNewerVersion(a, b) ? 1 : -1);
|
|
1724
|
+
if (stale.length > 0) {
|
|
1725
|
+
notices.push(
|
|
1726
|
+
`[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.`
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
if (notices.length > 0) {
|
|
1731
|
+
console.error(`
|
|
1732
|
+
${notices.join("\n")}`);
|
|
1733
|
+
}
|
|
1734
|
+
};
|
|
1735
|
+
};
|
|
1428
1736
|
|
|
1429
1737
|
// src/api.ts
|
|
1430
1738
|
var ApiRequestError = class extends Error {
|
|
1431
|
-
|
|
1739
|
+
status;
|
|
1740
|
+
code;
|
|
1741
|
+
retryAfter;
|
|
1742
|
+
currentVersion;
|
|
1743
|
+
docsUrl;
|
|
1744
|
+
constructor(status2, code, message, retryAfter, details = void 0) {
|
|
1432
1745
|
super(message);
|
|
1433
1746
|
this.status = status2;
|
|
1434
1747
|
this.code = code;
|
|
1435
1748
|
this.retryAfter = retryAfter;
|
|
1749
|
+
this.currentVersion = details?.currentVersion;
|
|
1750
|
+
this.docsUrl = details?.docsUrl;
|
|
1436
1751
|
this.name = "ApiRequestError";
|
|
1437
1752
|
}
|
|
1438
|
-
status;
|
|
1439
|
-
code;
|
|
1440
|
-
retryAfter;
|
|
1441
1753
|
};
|
|
1442
|
-
|
|
1754
|
+
var accessHeaders = (env = process.env) => {
|
|
1755
|
+
const id = env.CF_ACCESS_CLIENT_ID;
|
|
1756
|
+
const secret = env.CF_ACCESS_CLIENT_SECRET;
|
|
1757
|
+
return id && secret ? { "cf-access-client-id": id, "cf-access-client-secret": secret } : {};
|
|
1758
|
+
};
|
|
1759
|
+
var request = async (url, init, parse2) => {
|
|
1443
1760
|
let res;
|
|
1444
1761
|
try {
|
|
1445
|
-
res = await fetch(url,
|
|
1446
|
-
|
|
1762
|
+
res = await fetch(url, {
|
|
1763
|
+
...init,
|
|
1764
|
+
headers: {
|
|
1765
|
+
...Object.fromEntries(new Headers(init.headers)),
|
|
1766
|
+
...accessHeaders()
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
} catch (error) {
|
|
1447
1770
|
throw new Error(
|
|
1448
|
-
`Could not reach ${new URL(url).origin} \u2014 is the API up? (${
|
|
1449
|
-
{ cause:
|
|
1771
|
+
`Could not reach ${new URL(url).origin} \u2014 is the API up? (${error instanceof Error ? error.message : error})`,
|
|
1772
|
+
{ cause: error }
|
|
1450
1773
|
);
|
|
1451
1774
|
}
|
|
1452
1775
|
const body = await res.json().catch(() => null);
|
|
@@ -1457,24 +1780,26 @@ async function request(url, init, parse) {
|
|
|
1457
1780
|
res.status,
|
|
1458
1781
|
parsed.data.code,
|
|
1459
1782
|
parsed.data.message,
|
|
1460
|
-
parsed.data.retryAfter
|
|
1783
|
+
parsed.data.retryAfter,
|
|
1784
|
+
parsed.data
|
|
1461
1785
|
);
|
|
1462
1786
|
}
|
|
1463
|
-
throw new ApiRequestError(
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1787
|
+
throw new ApiRequestError(res.status, "unknown_error", `API returned ${res.status}`);
|
|
1788
|
+
}
|
|
1789
|
+
return parse2(body);
|
|
1790
|
+
};
|
|
1791
|
+
var createApiClient = (apiUrl2, publishToken) => {
|
|
1792
|
+
const base = apiUrl2.replace(/\/$/u, "");
|
|
1793
|
+
const headers = new Headers({
|
|
1794
|
+
"content-type": "application/json",
|
|
1795
|
+
"x-artifact-cafe-cli-version": cliVersion()
|
|
1796
|
+
});
|
|
1797
|
+
const installId = readInstallId();
|
|
1798
|
+
if (installId) {
|
|
1799
|
+
headers.set("x-artifact-cafe-install-id", installId);
|
|
1468
1800
|
}
|
|
1469
|
-
return parse(body);
|
|
1470
|
-
}
|
|
1471
|
-
function createApiClient(apiUrl2, publishToken) {
|
|
1472
|
-
const base = apiUrl2.replace(/\/$/, "");
|
|
1473
|
-
const headers = {
|
|
1474
|
-
"content-type": "application/json"
|
|
1475
|
-
};
|
|
1476
1801
|
if (publishToken) {
|
|
1477
|
-
headers.authorization
|
|
1802
|
+
headers.set("authorization", `Bearer ${publishToken}`);
|
|
1478
1803
|
}
|
|
1479
1804
|
return {
|
|
1480
1805
|
// SPEC §6.19 — mark the agent as actively waiting for feedback.
|
|
@@ -1482,25 +1807,25 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1482
1807
|
// SPEC §6.6.1 — mark an open thread acknowledged ("seen, working on
|
|
1483
1808
|
// it") without resolving it. Publish token or account key only —
|
|
1484
1809
|
// the server rejects guests.
|
|
1485
|
-
|
|
1810
|
+
acknowledgeThread(threadId) {
|
|
1486
1811
|
return request(
|
|
1487
1812
|
`${base}/api/v1/comment-threads/${threadId}/acknowledge`,
|
|
1488
1813
|
{ body: "{}", headers, method: "POST" },
|
|
1489
1814
|
(d) => commentThreadSchema.parse(d)
|
|
1490
1815
|
);
|
|
1491
1816
|
},
|
|
1492
|
-
|
|
1817
|
+
agentHeartbeat(artifactId, state) {
|
|
1493
1818
|
return request(
|
|
1494
1819
|
`${base}/api/v1/artifacts/${artifactId}/agent-presence`,
|
|
1495
1820
|
{
|
|
1496
|
-
...state ? { body: JSON.stringify({ state }) } :
|
|
1821
|
+
...state ? { body: JSON.stringify({ state }) } : void 0,
|
|
1497
1822
|
headers,
|
|
1498
1823
|
method: "POST"
|
|
1499
1824
|
},
|
|
1500
1825
|
(d) => agentHeartbeatResponseSchema.parse(d)
|
|
1501
1826
|
);
|
|
1502
1827
|
},
|
|
1503
|
-
|
|
1828
|
+
clearData(artifactId, collection) {
|
|
1504
1829
|
return request(
|
|
1505
1830
|
`${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}`,
|
|
1506
1831
|
{ headers, method: "DELETE" },
|
|
@@ -1509,7 +1834,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1509
1834
|
},
|
|
1510
1835
|
// SPEC-2 §6.3 — create a folder in a workspace. Account key required
|
|
1511
1836
|
// (owner/admin; in a personal workspace that's always the user).
|
|
1512
|
-
|
|
1837
|
+
createFolder(workspaceId, body) {
|
|
1513
1838
|
return request(
|
|
1514
1839
|
`${base}/api/v1/workspaces/${workspaceId}/folders`,
|
|
1515
1840
|
{ body: JSON.stringify(body), headers, method: "POST" },
|
|
@@ -1520,14 +1845,14 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1520
1845
|
// Authorized by this client's credential: the publish token (an agent
|
|
1521
1846
|
// sharing a private draft) or an account key/session. The invitation
|
|
1522
1847
|
// URL is returned exactly once, here.
|
|
1523
|
-
|
|
1848
|
+
createGrant(artifactId, body) {
|
|
1524
1849
|
return request(
|
|
1525
1850
|
`${base}/api/v1/artifacts/${artifactId}/grants`,
|
|
1526
1851
|
{ body: JSON.stringify(body), headers, method: "POST" },
|
|
1527
1852
|
(d) => createdGrantSchema.parse(d)
|
|
1528
1853
|
);
|
|
1529
1854
|
},
|
|
1530
|
-
|
|
1855
|
+
createSession(body) {
|
|
1531
1856
|
return request(
|
|
1532
1857
|
`${base}/api/v1/publish`,
|
|
1533
1858
|
{ body: JSON.stringify(body), headers, method: "POST" },
|
|
@@ -1538,7 +1863,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1538
1863
|
// key belongs to (agent-originated review, §5.1 `comment`). The create
|
|
1539
1864
|
// route rejects a bare publish token (agents respond, accounts
|
|
1540
1865
|
// originate), so this must carry an account credential.
|
|
1541
|
-
|
|
1866
|
+
createThread(artifactId, body) {
|
|
1542
1867
|
return request(
|
|
1543
1868
|
`${base}/api/v1/artifacts/${artifactId}/comments`,
|
|
1544
1869
|
{ body: JSON.stringify(body), headers, method: "POST" },
|
|
@@ -1552,51 +1877,56 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1552
1877
|
await request(
|
|
1553
1878
|
`${base}/api/v1/comment-messages/${messageId}`,
|
|
1554
1879
|
{ body: "{}", headers, method: "DELETE" },
|
|
1555
|
-
() =>
|
|
1880
|
+
() => {
|
|
1881
|
+
}
|
|
1556
1882
|
);
|
|
1557
1883
|
},
|
|
1558
1884
|
// SPEC §6.7.1 — edit a message you authored (new body + "edited" badge).
|
|
1559
1885
|
// Same credential rule as delete; returns the updated thread.
|
|
1560
|
-
|
|
1886
|
+
editMessage(messageId, body) {
|
|
1561
1887
|
return request(
|
|
1562
1888
|
`${base}/api/v1/comment-messages/${messageId}`,
|
|
1563
1889
|
{ body: JSON.stringify({ body }), headers, method: "PATCH" },
|
|
1564
1890
|
(d) => commentThreadSchema.parse(d)
|
|
1565
1891
|
);
|
|
1566
1892
|
},
|
|
1567
|
-
|
|
1893
|
+
exportComments(artifactId, query, { poll = false } = {}) {
|
|
1568
1894
|
const params = new URLSearchParams({
|
|
1569
1895
|
status: query.status,
|
|
1570
1896
|
version: query.version
|
|
1571
1897
|
});
|
|
1898
|
+
const exportHeaders = new Headers(headers);
|
|
1899
|
+
if (poll) {
|
|
1900
|
+
exportHeaders.set("x-artifact-cafe-poll", "1");
|
|
1901
|
+
}
|
|
1572
1902
|
return request(
|
|
1573
1903
|
`${base}/api/v1/artifacts/${artifactId}/comments/export?${params}`,
|
|
1574
|
-
{ headers, method: "GET" },
|
|
1904
|
+
{ headers: exportHeaders, method: "GET" },
|
|
1575
1905
|
(d) => commentExportSchema.parse(d)
|
|
1576
1906
|
);
|
|
1577
1907
|
},
|
|
1578
|
-
|
|
1908
|
+
finalize(publishSessionId) {
|
|
1579
1909
|
return request(
|
|
1580
1910
|
`${base}/api/v1/publish/${publishSessionId}/finalize`,
|
|
1581
1911
|
{ headers, method: "POST" },
|
|
1582
1912
|
(d) => finalizeResponseSchema.parse(d)
|
|
1583
1913
|
);
|
|
1584
1914
|
},
|
|
1585
|
-
|
|
1915
|
+
getArtifact(artifactId) {
|
|
1586
1916
|
return request(
|
|
1587
1917
|
`${base}/api/v1/artifacts/${artifactId}`,
|
|
1588
1918
|
{ headers, method: "GET" },
|
|
1589
1919
|
(d) => artifactResponseSchema.parse(d)
|
|
1590
1920
|
);
|
|
1591
1921
|
},
|
|
1592
|
-
|
|
1922
|
+
getDataSummary(artifactId) {
|
|
1593
1923
|
return request(
|
|
1594
1924
|
`${base}/api/v1/artifacts/${artifactId}/data`,
|
|
1595
1925
|
{ headers, method: "GET" },
|
|
1596
1926
|
(d) => dataSummaryResponseSchema.parse(d)
|
|
1597
1927
|
);
|
|
1598
1928
|
},
|
|
1599
|
-
|
|
1929
|
+
insertData(artifactId, collection, fields) {
|
|
1600
1930
|
return request(
|
|
1601
1931
|
`${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}`,
|
|
1602
1932
|
{ body: JSON.stringify(fields), headers, method: "POST" },
|
|
@@ -1604,7 +1934,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1604
1934
|
);
|
|
1605
1935
|
},
|
|
1606
1936
|
/** Owner-plane batch: up to DATA_LIMITS.maxInsertBatch, all-or-nothing. */
|
|
1607
|
-
|
|
1937
|
+
insertDataBatch(artifactId, collection, records) {
|
|
1608
1938
|
return request(
|
|
1609
1939
|
`${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}`,
|
|
1610
1940
|
{ body: JSON.stringify(records), headers, method: "POST" },
|
|
@@ -1613,14 +1943,14 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1613
1943
|
},
|
|
1614
1944
|
// SPEC §6.18 — everything owned by the account this client's API
|
|
1615
1945
|
// key (or session) belongs to.
|
|
1616
|
-
|
|
1946
|
+
listArtifacts() {
|
|
1617
1947
|
return request(
|
|
1618
1948
|
`${base}/api/v1/artifacts`,
|
|
1619
1949
|
{ headers, method: "GET" },
|
|
1620
1950
|
(d) => artifactListResponseSchema.parse(d)
|
|
1621
1951
|
);
|
|
1622
1952
|
},
|
|
1623
|
-
|
|
1953
|
+
listData(artifactId, collection, query = {}) {
|
|
1624
1954
|
const params = new URLSearchParams();
|
|
1625
1955
|
if (query.limit !== void 0) {
|
|
1626
1956
|
params.set("limit", String(query.limit));
|
|
@@ -1635,7 +1965,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1635
1965
|
(d) => dataListResponseSchema.parse(d)
|
|
1636
1966
|
);
|
|
1637
1967
|
},
|
|
1638
|
-
|
|
1968
|
+
listFolders(workspaceId) {
|
|
1639
1969
|
return request(
|
|
1640
1970
|
`${base}/api/v1/workspaces/${workspaceId}/folders`,
|
|
1641
1971
|
{ headers, method: "GET" },
|
|
@@ -1644,7 +1974,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1644
1974
|
},
|
|
1645
1975
|
// SPEC-2 §6.6 — the artifact's grants (no plaintext tokens). Used by
|
|
1646
1976
|
// `share --list` and to resolve an email → grant id for `unshare`.
|
|
1647
|
-
|
|
1977
|
+
listGrants(artifactId) {
|
|
1648
1978
|
return request(
|
|
1649
1979
|
`${base}/api/v1/artifacts/${artifactId}/grants`,
|
|
1650
1980
|
{ headers, method: "GET" },
|
|
@@ -1653,14 +1983,14 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1653
1983
|
},
|
|
1654
1984
|
// SPEC-2 §6.4 — every artifact in a workspace (members see all, with
|
|
1655
1985
|
// each artifact's folderId). The only folder-aware artifact listing.
|
|
1656
|
-
|
|
1986
|
+
listWorkspaceArtifacts(workspaceId) {
|
|
1657
1987
|
return request(
|
|
1658
1988
|
`${base}/api/v1/workspaces/${workspaceId}/artifacts`,
|
|
1659
1989
|
{ headers, method: "GET" },
|
|
1660
1990
|
(d) => workspaceArtifactListResponseSchema.parse(d)
|
|
1661
1991
|
);
|
|
1662
1992
|
},
|
|
1663
|
-
|
|
1993
|
+
listWorkspaces() {
|
|
1664
1994
|
return request(
|
|
1665
1995
|
`${base}/api/v1/workspaces`,
|
|
1666
1996
|
{ headers, method: "GET" },
|
|
@@ -1669,7 +1999,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1669
1999
|
},
|
|
1670
2000
|
// SPEC §6.14 — who the presented credential belongs to. Used by
|
|
1671
2001
|
// `login` to verify a pasted key and by `whoami`.
|
|
1672
|
-
|
|
2002
|
+
me() {
|
|
1673
2003
|
return request(
|
|
1674
2004
|
`${base}/api/v1/me`,
|
|
1675
2005
|
{ headers, method: "GET" },
|
|
@@ -1677,7 +2007,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1677
2007
|
);
|
|
1678
2008
|
},
|
|
1679
2009
|
// SPEC §6.7 — reopen a resolved thread. Same auth as resolve.
|
|
1680
|
-
|
|
2010
|
+
reopenThread(threadId) {
|
|
1681
2011
|
return request(
|
|
1682
2012
|
`${base}/api/v1/comment-threads/${threadId}/reopen`,
|
|
1683
2013
|
{ body: "{}", headers, method: "POST" },
|
|
@@ -1690,7 +2020,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1690
2020
|
// payload — the credential is the identity. `via` names the tool: it
|
|
1691
2021
|
// becomes the agent's byline on a token reply, a "via" label on an
|
|
1692
2022
|
// account one.
|
|
1693
|
-
|
|
2023
|
+
replyThread(threadId, body, via) {
|
|
1694
2024
|
return request(
|
|
1695
2025
|
`${base}/api/v1/comment-threads/${threadId}/messages`,
|
|
1696
2026
|
{ body: JSON.stringify({ body, via }), headers, method: "POST" },
|
|
@@ -1699,7 +2029,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1699
2029
|
},
|
|
1700
2030
|
// SPEC §6.6 — mark a thread resolved. Authorized by this client's
|
|
1701
2031
|
// credential: the artifact's publish token, or the owner's account key.
|
|
1702
|
-
|
|
2032
|
+
resolveThread(threadId) {
|
|
1703
2033
|
return request(
|
|
1704
2034
|
`${base}/api/v1/comment-threads/${threadId}/resolve`,
|
|
1705
2035
|
{ body: "{}", headers, method: "POST" },
|
|
@@ -1711,10 +2041,11 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1711
2041
|
await request(
|
|
1712
2042
|
`${base}/api/v1/artifacts/${artifactId}/grants/${grantId}`,
|
|
1713
2043
|
{ headers, method: "DELETE" },
|
|
1714
|
-
() =>
|
|
2044
|
+
() => {
|
|
2045
|
+
}
|
|
1715
2046
|
);
|
|
1716
2047
|
},
|
|
1717
|
-
|
|
2048
|
+
setExpiration(artifactId, expiresInSeconds) {
|
|
1718
2049
|
return request(
|
|
1719
2050
|
`${base}/api/v1/artifacts/${artifactId}/expiration`,
|
|
1720
2051
|
{ body: JSON.stringify({ expiresInSeconds }), headers, method: "PUT" },
|
|
@@ -1723,7 +2054,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1723
2054
|
},
|
|
1724
2055
|
// SPEC §6.16 — set a password (string) or clear it (null). Authorized
|
|
1725
2056
|
// by the publish token this client was created with.
|
|
1726
|
-
|
|
2057
|
+
setProtection(artifactId, password) {
|
|
1727
2058
|
return request(
|
|
1728
2059
|
`${base}/api/v1/artifacts/${artifactId}/protection`,
|
|
1729
2060
|
{ body: JSON.stringify({ password }), headers, method: "PUT" },
|
|
@@ -1733,7 +2064,7 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1733
2064
|
// SPEC-2 §5.8 — what reviewers may do once they can open it. Same
|
|
1734
2065
|
// dual-auth as protection, so the publish token this client holds is
|
|
1735
2066
|
// enough (an agent can close review on its own artifact).
|
|
1736
|
-
|
|
2067
|
+
setReviewMode(artifactId, mode) {
|
|
1737
2068
|
return request(
|
|
1738
2069
|
`${base}/api/v1/artifacts/${artifactId}/review-mode`,
|
|
1739
2070
|
{ body: JSON.stringify({ mode }), headers, method: "PUT" },
|
|
@@ -1743,16 +2074,23 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1743
2074
|
// SPEC.md §6.11 / SPEC-2 §6.4 — update an artifact (folder placement,
|
|
1744
2075
|
// title, slug, visibility). Workspace-member action: account key only,
|
|
1745
2076
|
// a publish token can't re-file.
|
|
1746
|
-
|
|
2077
|
+
updateArtifact(artifactId, body) {
|
|
1747
2078
|
return request(
|
|
1748
2079
|
`${base}/api/v1/artifacts/${artifactId}`,
|
|
1749
2080
|
{ body: JSON.stringify(body), headers, method: "PATCH" },
|
|
1750
2081
|
(d) => artifactResponseSchema.parse(d)
|
|
1751
2082
|
);
|
|
2083
|
+
},
|
|
2084
|
+
versionFiles(artifactId, version) {
|
|
2085
|
+
return request(
|
|
2086
|
+
`${base}/api/v1/artifacts/${encodeURIComponent(artifactId)}/versions/${version}/files`,
|
|
2087
|
+
{ headers, method: "GET" },
|
|
2088
|
+
(data2) => versionFilesResponseSchema.parse(data2)
|
|
2089
|
+
);
|
|
1752
2090
|
}
|
|
1753
2091
|
};
|
|
1754
|
-
}
|
|
1755
|
-
async
|
|
2092
|
+
};
|
|
2093
|
+
var uploadFile = async (uploadUrl, content, mimeType) => {
|
|
1756
2094
|
const res = await fetch(uploadUrl, {
|
|
1757
2095
|
body: new Uint8Array(content),
|
|
1758
2096
|
headers: {
|
|
@@ -1762,190 +2100,166 @@ async function uploadFile(uploadUrl, content, mimeType) {
|
|
|
1762
2100
|
method: "PUT"
|
|
1763
2101
|
});
|
|
1764
2102
|
if (!res.ok) {
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
);
|
|
2103
|
+
const responseBody = await res.text();
|
|
2104
|
+
throw new Error(`Upload failed (${res.status}): ${responseBody.slice(0, 200)}`);
|
|
1768
2105
|
}
|
|
1769
|
-
}
|
|
2106
|
+
};
|
|
1770
2107
|
|
|
1771
2108
|
// src/config.ts
|
|
1772
|
-
import {
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
mkdirSync as mkdirSync2,
|
|
1776
|
-
readFileSync as readFileSync2,
|
|
1777
|
-
writeFileSync as writeFileSync2
|
|
1778
|
-
} from "fs";
|
|
1779
|
-
import path2 from "path";
|
|
1780
|
-
import { z as z24 } from "zod";
|
|
2109
|
+
import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2110
|
+
import path4 from "path";
|
|
2111
|
+
import { z as z25 } from "zod";
|
|
1781
2112
|
|
|
1782
2113
|
// src/credentials.ts
|
|
1783
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
1784
|
-
import { homedir } from "os";
|
|
1785
|
-
import
|
|
1786
|
-
import { z as
|
|
2114
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2115
|
+
import { homedir as homedir3 } from "os";
|
|
2116
|
+
import path3 from "path";
|
|
2117
|
+
import { z as z24 } from "zod";
|
|
1787
2118
|
var DEFAULT_API_URL = "https://artifact.cafe";
|
|
1788
|
-
var credentialsSchema =
|
|
1789
|
-
keys:
|
|
1790
|
-
version:
|
|
2119
|
+
var credentialsSchema = z24.object({
|
|
2120
|
+
keys: z24.record(z24.string(), z24.string()),
|
|
2121
|
+
version: z24.literal(1)
|
|
1791
2122
|
});
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
function apiOrigin(apiUrl2) {
|
|
1796
|
-
return new URL(apiUrl2).origin;
|
|
1797
|
-
}
|
|
1798
|
-
function readCredentials() {
|
|
2123
|
+
var credentialsPath = () => path3.join(homedir3(), ".artifactcafe", "credentials");
|
|
2124
|
+
var apiOrigin = (apiUrl2) => new URL(apiUrl2).origin;
|
|
2125
|
+
var readCredentials = () => {
|
|
1799
2126
|
const file = credentialsPath();
|
|
1800
|
-
if (!
|
|
2127
|
+
if (!existsSync2(file)) {
|
|
1801
2128
|
return { keys: {}, version: 1 };
|
|
1802
2129
|
}
|
|
1803
2130
|
try {
|
|
1804
|
-
return credentialsSchema.parse(JSON.parse(
|
|
2131
|
+
return credentialsSchema.parse(JSON.parse(readFileSync3(file, "utf-8")));
|
|
1805
2132
|
} catch {
|
|
1806
2133
|
return { keys: {}, version: 1 };
|
|
1807
2134
|
}
|
|
1808
|
-
}
|
|
1809
|
-
|
|
2135
|
+
};
|
|
2136
|
+
var writeCredentials = (credentials) => {
|
|
1810
2137
|
const file = credentialsPath();
|
|
1811
|
-
|
|
1812
|
-
|
|
2138
|
+
mkdirSync3(path3.dirname(file), { mode: 448, recursive: true });
|
|
2139
|
+
writeFileSync3(file, `${JSON.stringify(credentials, null, 2)}
|
|
1813
2140
|
`, {
|
|
1814
2141
|
mode: 384
|
|
1815
2142
|
});
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
}
|
|
1820
|
-
function storeApiKey(apiUrl2, key) {
|
|
2143
|
+
};
|
|
2144
|
+
var storedApiKey = (apiUrl2) => readCredentials().keys[apiOrigin(apiUrl2)] ?? null;
|
|
2145
|
+
var storeApiKey = (apiUrl2, key) => {
|
|
1821
2146
|
const credentials = readCredentials();
|
|
1822
2147
|
credentials.keys[apiOrigin(apiUrl2)] = key;
|
|
1823
2148
|
writeCredentials(credentials);
|
|
1824
|
-
}
|
|
1825
|
-
|
|
2149
|
+
};
|
|
2150
|
+
var removeApiKey = (apiUrl2) => {
|
|
1826
2151
|
const credentials = readCredentials();
|
|
1827
2152
|
const origin = apiOrigin(apiUrl2);
|
|
1828
2153
|
if (!(origin in credentials.keys)) {
|
|
1829
2154
|
return false;
|
|
1830
2155
|
}
|
|
1831
|
-
|
|
2156
|
+
credentials.keys = Object.fromEntries(
|
|
2157
|
+
Object.entries(credentials.keys).filter(([key]) => key !== origin)
|
|
2158
|
+
);
|
|
1832
2159
|
writeCredentials(credentials);
|
|
1833
2160
|
return true;
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
return flag ?? process.env.ARTIFACT_CAFE_API_KEY ?? storedApiKey(apiUrl2) ?? void 0;
|
|
1837
|
-
}
|
|
2161
|
+
};
|
|
2162
|
+
var resolveApiKey = (apiUrl2, flag) => flag ?? process.env.ARTIFACT_CAFE_API_KEY ?? storedApiKey(apiUrl2) ?? void 0;
|
|
1838
2163
|
|
|
1839
2164
|
// src/config.ts
|
|
1840
|
-
var
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2165
|
+
var CONFIG_DIR = ".artifactcafe";
|
|
2166
|
+
var ensureGitignored = (dir) => {
|
|
2167
|
+
const gitignore = path4.join(dir, ".gitignore");
|
|
2168
|
+
if (!existsSync3(gitignore)) {
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
const lines = readFileSync4(gitignore, "utf-8").split(/\r?\n/u);
|
|
2172
|
+
const present = lines.some((l) => {
|
|
2173
|
+
const t = l.trim();
|
|
2174
|
+
return t === CONFIG_DIR || t === `${CONFIG_DIR}/` || t === `/${CONFIG_DIR}` || t === `/${CONFIG_DIR}/`;
|
|
2175
|
+
});
|
|
2176
|
+
if (!present) {
|
|
2177
|
+
appendFileSync(gitignore, `
|
|
2178
|
+
# artifact.cafe publish token
|
|
2179
|
+
${CONFIG_DIR}/
|
|
2180
|
+
`);
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
var configSchema = z25.object({
|
|
2184
|
+
apiUrl: z25.string(),
|
|
2185
|
+
artifactId: z25.string(),
|
|
2186
|
+
baseVersion: z25.number().int().positive().optional(),
|
|
2187
|
+
folder: z25.string().optional(),
|
|
2188
|
+
folderId: z25.string().optional(),
|
|
1845
2189
|
// Optional: a folder linked by `link` to an artifact you own carries no
|
|
1846
2190
|
// token — owner actions authorize via your stored login instead. A first
|
|
1847
2191
|
// publish still writes the anonymous-mode token here.
|
|
1848
|
-
publishToken:
|
|
1849
|
-
slug:
|
|
1850
|
-
url:
|
|
1851
|
-
workspace:
|
|
1852
|
-
workspaceId:
|
|
1853
|
-
workspaceKind:
|
|
1854
|
-
});
|
|
1855
|
-
var projectConfigSchema =
|
|
1856
|
-
folder:
|
|
1857
|
-
workspace:
|
|
1858
|
-
})
|
|
1859
|
-
var CONFIG_DIR = ".artifactcafe";
|
|
2192
|
+
publishToken: z25.string().optional(),
|
|
2193
|
+
slug: z25.string(),
|
|
2194
|
+
url: z25.string(),
|
|
2195
|
+
workspace: z25.string().optional(),
|
|
2196
|
+
workspaceId: z25.string().optional(),
|
|
2197
|
+
workspaceKind: z25.enum(["personal", "workspace"]).optional()
|
|
2198
|
+
});
|
|
2199
|
+
var projectConfigSchema = z25.strictObject({
|
|
2200
|
+
folder: z25.string().trim().min(1).optional(),
|
|
2201
|
+
workspace: z25.string().trim().min(1)
|
|
2202
|
+
});
|
|
1860
2203
|
var CONFIG_FILE = "config.json";
|
|
1861
2204
|
var PROJECT_CONFIG_FILE = "artifact-cafe.json";
|
|
1862
|
-
|
|
1863
|
-
const file =
|
|
1864
|
-
if (!
|
|
2205
|
+
var readConfig = (dir) => {
|
|
2206
|
+
const file = path4.join(dir, CONFIG_DIR, CONFIG_FILE);
|
|
2207
|
+
if (!existsSync3(file)) {
|
|
1865
2208
|
return null;
|
|
1866
2209
|
}
|
|
1867
2210
|
try {
|
|
1868
|
-
return configSchema.parse(JSON.parse(
|
|
2211
|
+
return configSchema.parse(JSON.parse(readFileSync4(file, "utf-8")));
|
|
1869
2212
|
} catch {
|
|
1870
2213
|
return null;
|
|
1871
2214
|
}
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
{ mode: 384 }
|
|
1880
|
-
);
|
|
2215
|
+
};
|
|
2216
|
+
var writeConfig = (dir, config) => {
|
|
2217
|
+
mkdirSync4(path4.join(dir, CONFIG_DIR), { recursive: true });
|
|
2218
|
+
writeFileSync4(path4.join(dir, CONFIG_DIR, CONFIG_FILE), `${JSON.stringify(config, null, 2)}
|
|
2219
|
+
`, {
|
|
2220
|
+
mode: 384
|
|
2221
|
+
});
|
|
1881
2222
|
ensureGitignored(dir);
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
let current =
|
|
2223
|
+
};
|
|
2224
|
+
var readProjectConfig = (dir) => {
|
|
2225
|
+
let current = path4.resolve(dir);
|
|
1885
2226
|
while (true) {
|
|
1886
|
-
const file =
|
|
1887
|
-
if (
|
|
2227
|
+
const file = path4.join(current, PROJECT_CONFIG_FILE);
|
|
2228
|
+
if (existsSync3(file)) {
|
|
1888
2229
|
try {
|
|
1889
|
-
return projectConfigSchema.parse(
|
|
1890
|
-
JSON.parse(readFileSync2(file, "utf8"))
|
|
1891
|
-
);
|
|
2230
|
+
return projectConfigSchema.parse(JSON.parse(readFileSync4(file, "utf-8")));
|
|
1892
2231
|
} catch (error) {
|
|
1893
|
-
const message = error instanceof
|
|
1894
|
-
throw new Error(`Invalid ${file}: ${message}
|
|
2232
|
+
const message = error instanceof z25.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
|
|
2233
|
+
throw new Error(`Invalid ${file}: ${message}.`, { cause: error });
|
|
1895
2234
|
}
|
|
1896
2235
|
}
|
|
1897
|
-
if (
|
|
2236
|
+
if (existsSync3(path4.join(current, ".git"))) {
|
|
1898
2237
|
return null;
|
|
1899
2238
|
}
|
|
1900
|
-
const parent =
|
|
2239
|
+
const parent = path4.dirname(current);
|
|
1901
2240
|
if (parent === current) {
|
|
1902
2241
|
return null;
|
|
1903
2242
|
}
|
|
1904
2243
|
current = parent;
|
|
1905
2244
|
}
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
let root =
|
|
1909
|
-
while (!
|
|
1910
|
-
const parent =
|
|
2245
|
+
};
|
|
2246
|
+
var writeProjectConfig = (dir, config) => {
|
|
2247
|
+
let root = path4.resolve(dir);
|
|
2248
|
+
while (!existsSync3(path4.join(root, ".git"))) {
|
|
2249
|
+
const parent = path4.dirname(root);
|
|
1911
2250
|
if (parent === root) {
|
|
1912
|
-
root =
|
|
2251
|
+
root = path4.resolve(dir);
|
|
1913
2252
|
break;
|
|
1914
2253
|
}
|
|
1915
2254
|
root = parent;
|
|
1916
2255
|
}
|
|
1917
|
-
const file =
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
`${JSON.stringify(projectConfigSchema.parse(config), null, 2)}
|
|
1921
|
-
`
|
|
1922
|
-
);
|
|
2256
|
+
const file = path4.join(root, PROJECT_CONFIG_FILE);
|
|
2257
|
+
writeFileSync4(file, `${JSON.stringify(projectConfigSchema.parse(config), null, 2)}
|
|
2258
|
+
`);
|
|
1923
2259
|
return file;
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
if (!existsSync2(gitignore)) {
|
|
1928
|
-
return;
|
|
1929
|
-
}
|
|
1930
|
-
const lines = readFileSync2(gitignore, "utf8").split(/\r?\n/);
|
|
1931
|
-
const present = lines.some((l) => {
|
|
1932
|
-
const t = l.trim();
|
|
1933
|
-
return t === CONFIG_DIR || t === `${CONFIG_DIR}/` || t === `/${CONFIG_DIR}` || t === `/${CONFIG_DIR}/`;
|
|
1934
|
-
});
|
|
1935
|
-
if (!present) {
|
|
1936
|
-
appendFileSync(
|
|
1937
|
-
gitignore,
|
|
1938
|
-
`
|
|
1939
|
-
# artifact.cafe publish token
|
|
1940
|
-
${CONFIG_DIR}/
|
|
1941
|
-
`
|
|
1942
|
-
);
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
function reviewUrl(apiUrl2, slug) {
|
|
1946
|
-
return `${apiUrl2.replace(/\/$/, "")}/a/${slug}`;
|
|
1947
|
-
}
|
|
1948
|
-
function resolveArtifactTarget(dir, opts = {}) {
|
|
2260
|
+
};
|
|
2261
|
+
var reviewUrl = (apiUrl2, slug) => `${apiUrl2.replace(/\/$/u, "")}/a/${slug}`;
|
|
2262
|
+
var resolveArtifactTarget = (dir, opts = {}) => {
|
|
1949
2263
|
const config = readConfig(dir);
|
|
1950
2264
|
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
|
|
1951
2265
|
const artifactId = opts.artifact ?? process.env.ARTIFACT_CAFE_ARTIFACT_ID ?? config?.artifactId ?? null;
|
|
@@ -1959,26 +2273,24 @@ function resolveArtifactTarget(dir, opts = {}) {
|
|
|
1959
2273
|
const credential = (apiKey?.startsWith("aat_") ? apiKey : void 0) ?? folderToken ?? process.env.ARTIFACT_CAFE_TOKEN ?? apiKey;
|
|
1960
2274
|
const url = artifactId === config?.artifactId ? config.url : void 0;
|
|
1961
2275
|
return { apiUrl: apiUrl2, artifactId, credential, url };
|
|
1962
|
-
}
|
|
2276
|
+
};
|
|
1963
2277
|
|
|
1964
2278
|
// src/comment.ts
|
|
1965
|
-
async
|
|
2279
|
+
var readStdin = async () => {
|
|
1966
2280
|
if (process.stdin.isTTY) {
|
|
1967
2281
|
return "";
|
|
1968
2282
|
}
|
|
1969
2283
|
const chunks = [];
|
|
1970
2284
|
for await (const chunk of process.stdin) {
|
|
1971
|
-
chunks.push(chunk);
|
|
2285
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
1972
2286
|
}
|
|
1973
|
-
return Buffer.concat(chunks).toString("
|
|
1974
|
-
}
|
|
1975
|
-
|
|
2287
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
2288
|
+
};
|
|
2289
|
+
var buildAnchor = (opts) => {
|
|
1976
2290
|
if (opts.quote !== void 0) {
|
|
1977
2291
|
const quote = opts.quote.trim();
|
|
1978
2292
|
if (!quote) {
|
|
1979
|
-
throw new Error(
|
|
1980
|
-
"--quote can't be empty. Omit it entirely for a page-level comment."
|
|
1981
|
-
);
|
|
2293
|
+
throw new Error("--quote can't be empty. Omit it entirely for a page-level comment.");
|
|
1982
2294
|
}
|
|
1983
2295
|
const anchor2 = {
|
|
1984
2296
|
elementPath: "body",
|
|
@@ -2002,16 +2314,14 @@ function buildAnchor(opts) {
|
|
|
2002
2314
|
anchor.page = opts.page;
|
|
2003
2315
|
}
|
|
2004
2316
|
return anchor;
|
|
2005
|
-
}
|
|
2006
|
-
async
|
|
2317
|
+
};
|
|
2318
|
+
var comment = async (opts) => {
|
|
2007
2319
|
const body = (opts.body ?? await readStdin()).trim();
|
|
2008
2320
|
if (!body) {
|
|
2009
|
-
throw new Error(
|
|
2010
|
-
'Nothing to post. Pass the comment with --body "\u2026", or pipe it on stdin.'
|
|
2011
|
-
);
|
|
2321
|
+
throw new Error('Nothing to post. Pass the comment with --body "\u2026", or pipe it on stdin.');
|
|
2012
2322
|
}
|
|
2013
2323
|
const anchor = buildAnchor(opts);
|
|
2014
|
-
const dir =
|
|
2324
|
+
const dir = path5.resolve(opts.dir);
|
|
2015
2325
|
const target = resolveArtifactTarget(dir, {
|
|
2016
2326
|
apiKey: opts.apiKey,
|
|
2017
2327
|
artifact: opts.artifact
|
|
@@ -2020,9 +2330,7 @@ async function comment(opts) {
|
|
|
2020
2330
|
const client = createApiClient(target.apiUrl, credential);
|
|
2021
2331
|
const artifact = await client.getArtifact(target.artifactId);
|
|
2022
2332
|
if (!artifact.currentVersion) {
|
|
2023
|
-
throw new Error(
|
|
2024
|
-
"This artifact has no published version yet \u2014 publish before commenting."
|
|
2025
|
-
);
|
|
2333
|
+
throw new Error("This artifact has no published version yet \u2014 publish before commenting.");
|
|
2026
2334
|
}
|
|
2027
2335
|
let thread;
|
|
2028
2336
|
try {
|
|
@@ -2035,13 +2343,14 @@ async function comment(opts) {
|
|
|
2035
2343
|
// commenting with your key still says which tool typed it.
|
|
2036
2344
|
via: agentLabel(opts.as ?? opts.via)
|
|
2037
2345
|
});
|
|
2038
|
-
} catch (
|
|
2039
|
-
if (
|
|
2346
|
+
} catch (error) {
|
|
2347
|
+
if (error instanceof ApiRequestError && error.status === 401) {
|
|
2040
2348
|
throw new Error(
|
|
2041
|
-
"Publish tokens can't open threads. Run `artifact-cafe login` to comment as your account."
|
|
2349
|
+
"Publish tokens can't open threads. Run `artifact-cafe login` to comment as your account.",
|
|
2350
|
+
{ cause: error }
|
|
2042
2351
|
);
|
|
2043
2352
|
}
|
|
2044
|
-
throw
|
|
2353
|
+
throw error;
|
|
2045
2354
|
}
|
|
2046
2355
|
if (opts.json) {
|
|
2047
2356
|
console.log(
|
|
@@ -2061,64 +2370,19 @@ async function comment(opts) {
|
|
|
2061
2370
|
const url = target.url ?? reviewUrl(target.apiUrl, artifact.slug);
|
|
2062
2371
|
const where = anchor.type === "text" ? ` on \u201C${anchor.quote}\u201D` : " on the page";
|
|
2063
2372
|
console.log(`\u2713 Opened thread ${thread.id}${where} \u2014 ${url}`);
|
|
2064
|
-
}
|
|
2373
|
+
};
|
|
2065
2374
|
|
|
2066
2375
|
// src/comments.ts
|
|
2067
|
-
import
|
|
2068
|
-
|
|
2069
|
-
var
|
|
2070
|
-
var pollSeconds = () => Number(process.env.AC_WAIT_POLL_SECONDS ?? 5);
|
|
2071
|
-
var settleSeconds = () => Number(process.env.AC_WAIT_SETTLE_SECONDS ?? 20);
|
|
2072
|
-
var WAIT_TIMEOUT_EXIT = 2;
|
|
2073
|
-
async function comments(opts) {
|
|
2074
|
-
if (!STATUSES.includes(opts.status)) {
|
|
2075
|
-
throw new Error(
|
|
2076
|
-
`--status must be one of: ${STATUSES.join(", ")} (got "${opts.status}").`
|
|
2077
|
-
);
|
|
2078
|
-
}
|
|
2079
|
-
if (!VERSIONS.includes(opts.version)) {
|
|
2080
|
-
throw new Error(
|
|
2081
|
-
`--version must be one of: ${VERSIONS.join(", ")} (got "${opts.version}").`
|
|
2082
|
-
);
|
|
2083
|
-
}
|
|
2084
|
-
const status2 = opts.status;
|
|
2085
|
-
const version = opts.version;
|
|
2086
|
-
const dir = path4.resolve(opts.dir);
|
|
2087
|
-
const target = resolveArtifactTarget(dir, {
|
|
2088
|
-
apiKey: opts.apiKey,
|
|
2089
|
-
artifact: opts.artifact
|
|
2090
|
-
});
|
|
2091
|
-
const client = createApiClient(target.apiUrl, target.credential);
|
|
2092
|
-
const url = target.url ?? (opts.json ? "" : await deriveReviewUrl(client, target));
|
|
2093
|
-
const ref = { artifactId: target.artifactId, url };
|
|
2094
|
-
if (opts.wait) {
|
|
2095
|
-
await waitForFeedback(client, ref, {
|
|
2096
|
-
json: opts.json,
|
|
2097
|
-
status: status2,
|
|
2098
|
-
timeoutSeconds: opts.timeout,
|
|
2099
|
-
version
|
|
2100
|
-
});
|
|
2101
|
-
return;
|
|
2102
|
-
}
|
|
2103
|
-
const data2 = await client.exportComments(target.artifactId, {
|
|
2104
|
-
status: status2,
|
|
2105
|
-
version
|
|
2106
|
-
});
|
|
2107
|
-
if (opts.json) {
|
|
2108
|
-
console.log(JSON.stringify(data2, null, 2));
|
|
2109
|
-
return;
|
|
2110
|
-
}
|
|
2111
|
-
printHuman(data2, url, status2, version);
|
|
2112
|
-
}
|
|
2113
|
-
async function deriveReviewUrl(client, target) {
|
|
2376
|
+
import path6 from "path";
|
|
2377
|
+
import { setTimeout as waitForTimeout } from "timers/promises";
|
|
2378
|
+
var deriveReviewUrl = async (client, target) => {
|
|
2114
2379
|
const artifact = await client.getArtifact(target.artifactId);
|
|
2115
2380
|
return reviewUrl(target.apiUrl, artifact.slug);
|
|
2116
|
-
}
|
|
2117
|
-
var
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
function diffThreads(baseline, data2) {
|
|
2381
|
+
};
|
|
2382
|
+
var messageCounts = (data2) => new Map(data2.comments.map((t) => [t.id, 1 + t.replies.length]));
|
|
2383
|
+
var sleep = (s) => waitForTimeout(s * 1e3);
|
|
2384
|
+
var pollSeconds = () => Number(process.env.AC_WAIT_POLL_SECONDS ?? 5);
|
|
2385
|
+
var diffThreads = (baseline, data2) => {
|
|
2122
2386
|
const changed = [];
|
|
2123
2387
|
for (const t of data2.comments) {
|
|
2124
2388
|
const before = baseline.get(t.id);
|
|
@@ -2129,8 +2393,68 @@ function diffThreads(baseline, data2) {
|
|
|
2129
2393
|
}
|
|
2130
2394
|
}
|
|
2131
2395
|
return changed;
|
|
2132
|
-
}
|
|
2133
|
-
|
|
2396
|
+
};
|
|
2397
|
+
var settleSeconds = () => Number(process.env.AC_WAIT_SETTLE_SECONDS ?? 20);
|
|
2398
|
+
var indent = (text, pad) => text.split("\n").map((line) => pad + line).join("\n");
|
|
2399
|
+
var printThread = (t) => {
|
|
2400
|
+
const marker = t.status === "resolved" ? "\u2713" : "\u25CF";
|
|
2401
|
+
const date = t.createdAt.slice(0, 10);
|
|
2402
|
+
const edited = t.editedAt && !t.deleted ? " \xB7 edited" : "";
|
|
2403
|
+
let state = "";
|
|
2404
|
+
if (t.status === "resolved" && t.resolvedBy) {
|
|
2405
|
+
state = ` \xB7 resolved by ${t.resolvedBy.displayName}`;
|
|
2406
|
+
} else if (t.status === "open" && t.acknowledgedAt) {
|
|
2407
|
+
state = " \xB7 acknowledged";
|
|
2408
|
+
}
|
|
2409
|
+
console.log(`${marker} ${t.author} \xB7 v${t.versionNumber} \xB7 ${date} \xB7 ${t.id}${edited}${state}`);
|
|
2410
|
+
if (t.anchor.type === "text") {
|
|
2411
|
+
console.log(` \u21B3 "${t.anchor.quote}"`);
|
|
2412
|
+
} else if (t.anchor.type === "point") {
|
|
2413
|
+
const el = t.anchor.element;
|
|
2414
|
+
if (el) {
|
|
2415
|
+
const text = el.text ? ` "${el.text}"` : "";
|
|
2416
|
+
console.log(` \u21B3 at ${elementLabel(el)}${text} \u2014 ${t.anchor.elementPath}`);
|
|
2417
|
+
} else {
|
|
2418
|
+
console.log(` \u21B3 at ${t.anchor.elementPath}`);
|
|
2419
|
+
}
|
|
2420
|
+
} else {
|
|
2421
|
+
console.log(t.anchor.page ? ` \u21B3 on ${t.anchor.page}` : " \u21B3 page comment");
|
|
2422
|
+
}
|
|
2423
|
+
console.log(indent(t.deleted ? "[deleted]" : t.body, " "));
|
|
2424
|
+
for (const reply2 of t.replies) {
|
|
2425
|
+
const replyEdited = reply2.editedAt && !reply2.deleted ? " (edited)" : "";
|
|
2426
|
+
const replyBody = reply2.deleted ? "[deleted]" : reply2.body;
|
|
2427
|
+
console.log(indent(`${reply2.author}: ${replyBody}${replyEdited}`, " "));
|
|
2428
|
+
}
|
|
2429
|
+
console.log("");
|
|
2430
|
+
};
|
|
2431
|
+
var printWaitResult = (config, data2, changed, json) => {
|
|
2432
|
+
if (json) {
|
|
2433
|
+
console.log(
|
|
2434
|
+
JSON.stringify(
|
|
2435
|
+
{
|
|
2436
|
+
artifactId: config.artifactId,
|
|
2437
|
+
threads: changed,
|
|
2438
|
+
timedOut: false,
|
|
2439
|
+
versionNumber: data2.versionNumber
|
|
2440
|
+
},
|
|
2441
|
+
null,
|
|
2442
|
+
2
|
|
2443
|
+
)
|
|
2444
|
+
);
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
const plural = changed.length === 1 ? "" : "s";
|
|
2448
|
+
console.log(`
|
|
2449
|
+
${changed.length} thread${plural} with new feedback \u2014 ${config.url}
|
|
2450
|
+
`);
|
|
2451
|
+
for (const thread of changed) {
|
|
2452
|
+
console.log(thread.change === "new" ? "NEW" : "UPDATED");
|
|
2453
|
+
printThread(thread);
|
|
2454
|
+
}
|
|
2455
|
+
};
|
|
2456
|
+
var WAIT_TIMEOUT_EXIT = 2;
|
|
2457
|
+
var waitForFeedback = async (client, config, opts) => {
|
|
2134
2458
|
const query = { status: opts.status, version: opts.version };
|
|
2135
2459
|
const baselineExport = await client.exportComments(config.artifactId, query);
|
|
2136
2460
|
const baseline = messageCounts(baselineExport);
|
|
@@ -2150,11 +2474,11 @@ async function waitForFeedback(client, config, opts) {
|
|
|
2150
2474
|
lastHeartbeat = Date.now();
|
|
2151
2475
|
try {
|
|
2152
2476
|
await client.agentHeartbeat(config.artifactId);
|
|
2153
|
-
} catch (
|
|
2477
|
+
} catch (error) {
|
|
2154
2478
|
if (!(opts.json || heartbeatWarned)) {
|
|
2155
2479
|
heartbeatWarned = true;
|
|
2156
2480
|
console.error(
|
|
2157
|
-
`(presence heartbeat failed \u2014 reviewers won't see "Agent is listening": ${
|
|
2481
|
+
`(presence heartbeat failed \u2014 reviewers won't see "Agent is listening": ${error instanceof Error ? error.message : error})`
|
|
2158
2482
|
);
|
|
2159
2483
|
}
|
|
2160
2484
|
}
|
|
@@ -2163,7 +2487,7 @@ async function waitForFeedback(client, config, opts) {
|
|
|
2163
2487
|
while (Date.now() < deadline) {
|
|
2164
2488
|
await sleep(Math.min(pollSeconds(), (deadline - Date.now()) / 1e3));
|
|
2165
2489
|
await heartbeat();
|
|
2166
|
-
const data2 = await client.exportComments(config.artifactId, query);
|
|
2490
|
+
const data2 = await client.exportComments(config.artifactId, query, { poll: true });
|
|
2167
2491
|
if (diffThreads(baseline, data2).length === 0) {
|
|
2168
2492
|
continue;
|
|
2169
2493
|
}
|
|
@@ -2196,35 +2520,8 @@ async function waitForFeedback(client, config, opts) {
|
|
|
2196
2520
|
);
|
|
2197
2521
|
}
|
|
2198
2522
|
process.exit(WAIT_TIMEOUT_EXIT);
|
|
2199
|
-
}
|
|
2200
|
-
|
|
2201
|
-
if (json) {
|
|
2202
|
-
console.log(
|
|
2203
|
-
JSON.stringify(
|
|
2204
|
-
{
|
|
2205
|
-
artifactId: config.artifactId,
|
|
2206
|
-
threads: changed,
|
|
2207
|
-
timedOut: false,
|
|
2208
|
-
versionNumber: data2.versionNumber
|
|
2209
|
-
},
|
|
2210
|
-
null,
|
|
2211
|
-
2
|
|
2212
|
-
)
|
|
2213
|
-
);
|
|
2214
|
-
return;
|
|
2215
|
-
}
|
|
2216
|
-
const plural = changed.length === 1 ? "" : "s";
|
|
2217
|
-
console.log(
|
|
2218
|
-
`
|
|
2219
|
-
${changed.length} thread${plural} with new feedback \u2014 ${config.url}
|
|
2220
|
-
`
|
|
2221
|
-
);
|
|
2222
|
-
for (const thread of changed) {
|
|
2223
|
-
console.log(thread.change === "new" ? "NEW" : "UPDATED");
|
|
2224
|
-
printThread(thread);
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
function printHuman(data2, url, status2, version) {
|
|
2523
|
+
};
|
|
2524
|
+
var printHuman = (data2, url, status2, version) => {
|
|
2228
2525
|
const statusLabel = status2 === "all" ? "" : `${status2} `;
|
|
2229
2526
|
let scope = "across all versions";
|
|
2230
2527
|
if (version === "current") {
|
|
@@ -2235,73 +2532,68 @@ function printHuman(data2, url, status2, version) {
|
|
|
2235
2532
|
return;
|
|
2236
2533
|
}
|
|
2237
2534
|
const plural = data2.comments.length === 1 ? "" : "s";
|
|
2238
|
-
console.log(
|
|
2239
|
-
|
|
2240
|
-
`
|
|
2241
|
-
);
|
|
2535
|
+
console.log(`${data2.comments.length} ${statusLabel}thread${plural} ${scope} \u2014 ${url}
|
|
2536
|
+
`);
|
|
2242
2537
|
for (const thread of data2.comments) {
|
|
2243
2538
|
printThread(thread);
|
|
2244
2539
|
}
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
const
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
state = ` \xB7 resolved by ${t.resolvedBy.displayName}`;
|
|
2253
|
-
} else if (t.status === "open" && t.acknowledgedAt) {
|
|
2254
|
-
state = " \xB7 acknowledged";
|
|
2540
|
+
};
|
|
2541
|
+
var STATUSES = ["open", "resolved", "all"];
|
|
2542
|
+
var VERSIONS = ["current", "all"];
|
|
2543
|
+
var comments = async (opts) => {
|
|
2544
|
+
const status2 = STATUSES.find((value) => value === opts.status);
|
|
2545
|
+
if (!status2) {
|
|
2546
|
+
throw new Error(`--status must be one of: ${STATUSES.join(", ")} (got "${opts.status}").`);
|
|
2255
2547
|
}
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
if (t.anchor.type === "text") {
|
|
2260
|
-
console.log(` \u21B3 "${t.anchor.quote}"`);
|
|
2261
|
-
} else if (t.anchor.type === "point") {
|
|
2262
|
-
const el = t.anchor.element;
|
|
2263
|
-
if (el) {
|
|
2264
|
-
const text = el.text ? ` "${el.text}"` : "";
|
|
2265
|
-
console.log(
|
|
2266
|
-
` \u21B3 at ${elementLabel(el)}${text} \u2014 ${t.anchor.elementPath}`
|
|
2267
|
-
);
|
|
2268
|
-
} else {
|
|
2269
|
-
console.log(` \u21B3 at ${t.anchor.elementPath}`);
|
|
2270
|
-
}
|
|
2271
|
-
} else {
|
|
2272
|
-
console.log(t.anchor.page ? ` \u21B3 on ${t.anchor.page}` : " \u21B3 page comment");
|
|
2548
|
+
const version = VERSIONS.find((value) => value === opts.version);
|
|
2549
|
+
if (!version) {
|
|
2550
|
+
throw new Error(`--version must be one of: ${VERSIONS.join(", ")} (got "${opts.version}").`);
|
|
2273
2551
|
}
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2552
|
+
const dir = path6.resolve(opts.dir);
|
|
2553
|
+
const target = resolveArtifactTarget(dir, {
|
|
2554
|
+
apiKey: opts.apiKey,
|
|
2555
|
+
artifact: opts.artifact
|
|
2556
|
+
});
|
|
2557
|
+
const client = createApiClient(target.apiUrl, target.credential);
|
|
2558
|
+
const url = target.url ?? (opts.json ? "" : await deriveReviewUrl(client, target));
|
|
2559
|
+
const ref = { artifactId: target.artifactId, url };
|
|
2560
|
+
if (opts.wait) {
|
|
2561
|
+
await waitForFeedback(client, ref, {
|
|
2562
|
+
json: opts.json,
|
|
2563
|
+
status: status2,
|
|
2564
|
+
timeoutSeconds: opts.timeout,
|
|
2565
|
+
version
|
|
2566
|
+
});
|
|
2567
|
+
return;
|
|
2279
2568
|
}
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2569
|
+
const data2 = await client.exportComments(target.artifactId, {
|
|
2570
|
+
status: status2,
|
|
2571
|
+
version
|
|
2572
|
+
});
|
|
2573
|
+
if (opts.json) {
|
|
2574
|
+
console.log(JSON.stringify(data2, null, 2));
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2577
|
+
printHuman(data2, url, status2, version);
|
|
2578
|
+
};
|
|
2285
2579
|
|
|
2286
2580
|
// src/dashboard.ts
|
|
2287
|
-
import
|
|
2581
|
+
import path10 from "path";
|
|
2288
2582
|
|
|
2289
2583
|
// src/publish.ts
|
|
2290
2584
|
import { exec } from "child_process";
|
|
2291
|
-
import { readFileSync as
|
|
2292
|
-
import
|
|
2585
|
+
import { readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
2586
|
+
import path9 from "path";
|
|
2293
2587
|
import { createInterface } from "readline";
|
|
2294
2588
|
|
|
2295
2589
|
// src/data.ts
|
|
2296
|
-
import { readFileSync as
|
|
2297
|
-
import
|
|
2298
|
-
|
|
2590
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
2591
|
+
import path7 from "path";
|
|
2592
|
+
var inspectDataManifest = (files) => {
|
|
2299
2593
|
const info = { announcements: [] };
|
|
2300
2594
|
const manifestFile = files.find((f) => f.path === DATA_MANIFEST_PATH);
|
|
2301
2595
|
if (manifestFile) {
|
|
2302
|
-
const parsed = parseDataManifest(
|
|
2303
|
-
readFileSync3(manifestFile.absPath, "utf8")
|
|
2304
|
-
);
|
|
2596
|
+
const parsed = parseDataManifest(readFileSync5(manifestFile.absPath, "utf-8"));
|
|
2305
2597
|
if (!parsed.ok) {
|
|
2306
2598
|
throw new Error(
|
|
2307
2599
|
`${DATA_MANIFEST_PATH} is invalid:
|
|
@@ -2309,13 +2601,8 @@ ${(parsed.errors ?? []).map((e) => ` - ${e}`).join("\n")}`
|
|
|
2309
2601
|
);
|
|
2310
2602
|
}
|
|
2311
2603
|
info.config = parsed.config;
|
|
2312
|
-
for (const [name, collection] of Object.entries(
|
|
2313
|
-
|
|
2314
|
-
)) {
|
|
2315
|
-
const bits = [
|
|
2316
|
-
`read: ${collection.access.read}`,
|
|
2317
|
-
`insert: ${collection.access.insert}`
|
|
2318
|
-
];
|
|
2604
|
+
for (const [name, collection] of Object.entries(parsed.config?.collections ?? {})) {
|
|
2605
|
+
const bits = [`read: ${collection.access.read}`, `insert: ${collection.access.insert}`];
|
|
2319
2606
|
if (collection.access.update !== "owner") {
|
|
2320
2607
|
bits.push(`update: ${collection.access.update}`);
|
|
2321
2608
|
}
|
|
@@ -2328,15 +2615,13 @@ ${(parsed.errors ?? []).map((e) => ` - ${e}`).join("\n")}`
|
|
|
2328
2615
|
info.announcements.push(`\u26C1 Data enabled: ${name} (${bits.join(", ")})`);
|
|
2329
2616
|
}
|
|
2330
2617
|
if (info.announcements.length === 0) {
|
|
2331
|
-
info.announcements.push(
|
|
2332
|
-
"\u26C1 Data disabled (empty collections in .artifact/data.json)"
|
|
2333
|
-
);
|
|
2618
|
+
info.announcements.push("\u26C1 Data disabled (empty collections in .artifact/data.json)");
|
|
2334
2619
|
}
|
|
2335
2620
|
}
|
|
2336
2621
|
return info;
|
|
2337
|
-
}
|
|
2338
|
-
|
|
2339
|
-
const resolved =
|
|
2622
|
+
};
|
|
2623
|
+
var requireLinkedArtifact = (dir) => {
|
|
2624
|
+
const resolved = path7.resolve(dir);
|
|
2340
2625
|
const config = readConfig(resolved);
|
|
2341
2626
|
if (!config) {
|
|
2342
2627
|
throw new Error(
|
|
@@ -2349,8 +2634,8 @@ function requireLinkedArtifact(dir) {
|
|
|
2349
2634
|
publishToken: resolveApiKey(process.env.ARTIFACT_CAFE_URL ?? config.apiUrl) ?? config.publishToken ?? process.env.ARTIFACT_CAFE_TOKEN,
|
|
2350
2635
|
url: config.url
|
|
2351
2636
|
};
|
|
2352
|
-
}
|
|
2353
|
-
async
|
|
2637
|
+
};
|
|
2638
|
+
var dataLs = async (opts) => {
|
|
2354
2639
|
const linked = requireLinkedArtifact(opts.dir);
|
|
2355
2640
|
const client = createApiClient(linked.apiUrl, linked.publishToken);
|
|
2356
2641
|
const records = [];
|
|
@@ -2365,9 +2650,7 @@ async function dataLs(opts) {
|
|
|
2365
2650
|
} while (cursor && (opts.limit === void 0 || records.length < opts.limit));
|
|
2366
2651
|
const shown = opts.limit === void 0 ? records : records.slice(0, opts.limit);
|
|
2367
2652
|
if (opts.json) {
|
|
2368
|
-
console.log(
|
|
2369
|
-
JSON.stringify({ collection: opts.collection, records: shown }, null, 2)
|
|
2370
|
-
);
|
|
2653
|
+
console.log(JSON.stringify({ collection: opts.collection, records: shown }, null, 2));
|
|
2371
2654
|
return;
|
|
2372
2655
|
}
|
|
2373
2656
|
if (shown.length === 0) {
|
|
@@ -2375,40 +2658,40 @@ async function dataLs(opts) {
|
|
|
2375
2658
|
return;
|
|
2376
2659
|
}
|
|
2377
2660
|
const plural = shown.length === 1 ? "" : "s";
|
|
2378
|
-
console.log(
|
|
2379
|
-
|
|
2380
|
-
`
|
|
2381
|
-
);
|
|
2661
|
+
console.log(`${shown.length} record${plural} in "${opts.collection}" \u2014 ${linked.url}
|
|
2662
|
+
`);
|
|
2382
2663
|
for (const record of shown) {
|
|
2383
2664
|
const { created_at, created_by, id, updated_at: _u, ...fields } = record;
|
|
2384
2665
|
const author = created_by ?? "anonymous";
|
|
2385
2666
|
console.log(`\u25CF ${id} \xB7 ${String(created_at).slice(0, 10)} \xB7 ${author}`);
|
|
2386
2667
|
console.log(` ${JSON.stringify(fields)}`);
|
|
2387
2668
|
}
|
|
2388
|
-
}
|
|
2389
|
-
var
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2669
|
+
};
|
|
2670
|
+
var RECORD_INPUT_HINT = `a JSON object or array of objects, e.g. '{"name":"Ada"}'`;
|
|
2671
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2672
|
+
var dataInsert = async (opts) => {
|
|
2673
|
+
let text;
|
|
2674
|
+
if (opts.record === void 0) {
|
|
2675
|
+
if (opts.file === void 0) {
|
|
2676
|
+
throw new Error("Pass exactly one of --record '<json>' or --file <path>.");
|
|
2677
|
+
}
|
|
2678
|
+
text = readFileSync5(opts.file, "utf-8");
|
|
2679
|
+
} else {
|
|
2680
|
+
if (opts.file !== void 0) {
|
|
2681
|
+
throw new Error("Pass exactly one of --record '<json>' or --file <path>.");
|
|
2682
|
+
}
|
|
2683
|
+
text = opts.record;
|
|
2396
2684
|
}
|
|
2397
|
-
const text = opts.record ?? readFileSync3(opts.file, "utf8");
|
|
2398
2685
|
let parsed;
|
|
2399
2686
|
try {
|
|
2400
2687
|
parsed = JSON.parse(text);
|
|
2401
2688
|
} catch {
|
|
2402
|
-
throw new Error(`Records must be ${
|
|
2689
|
+
throw new Error(`Records must be ${RECORD_INPUT_HINT}.`);
|
|
2403
2690
|
}
|
|
2404
2691
|
const linked = requireLinkedArtifact(opts.dir);
|
|
2405
2692
|
const client = createApiClient(linked.apiUrl, linked.publishToken);
|
|
2406
2693
|
if (isRecord(parsed)) {
|
|
2407
|
-
const record = await client.insertData(
|
|
2408
|
-
linked.artifactId,
|
|
2409
|
-
opts.collection,
|
|
2410
|
-
parsed
|
|
2411
|
-
);
|
|
2694
|
+
const record = await client.insertData(linked.artifactId, opts.collection, parsed);
|
|
2412
2695
|
if (opts.json) {
|
|
2413
2696
|
console.log(JSON.stringify({ collection: opts.collection, record }));
|
|
2414
2697
|
return;
|
|
@@ -2417,7 +2700,7 @@ async function dataInsert(opts) {
|
|
|
2417
2700
|
return;
|
|
2418
2701
|
}
|
|
2419
2702
|
if (!(Array.isArray(parsed) && parsed.length > 0 && parsed.every(isRecord))) {
|
|
2420
|
-
throw new Error(`Records must be ${
|
|
2703
|
+
throw new Error(`Records must be ${RECORD_INPUT_HINT}.`);
|
|
2421
2704
|
}
|
|
2422
2705
|
const records = [];
|
|
2423
2706
|
for (let i = 0; i < parsed.length; i += DATA_LIMITS.maxInsertBatch) {
|
|
@@ -2428,22 +2711,22 @@ async function dataInsert(opts) {
|
|
|
2428
2711
|
parsed.slice(i, i + DATA_LIMITS.maxInsertBatch)
|
|
2429
2712
|
);
|
|
2430
2713
|
records.push(...page.records);
|
|
2431
|
-
} catch (
|
|
2714
|
+
} catch (error) {
|
|
2432
2715
|
if (records.length === 0) {
|
|
2433
|
-
throw
|
|
2716
|
+
throw error;
|
|
2434
2717
|
}
|
|
2435
2718
|
const note = ` (the first ${records.length} records were inserted; records ${i}+ may not have been)`;
|
|
2436
|
-
if (
|
|
2719
|
+
if (error instanceof ApiRequestError) {
|
|
2437
2720
|
throw new ApiRequestError(
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
`${
|
|
2441
|
-
|
|
2721
|
+
error.status,
|
|
2722
|
+
error.code,
|
|
2723
|
+
`${error.message}${note}`,
|
|
2724
|
+
error.retryAfter
|
|
2442
2725
|
);
|
|
2443
2726
|
}
|
|
2444
|
-
throw new Error(
|
|
2445
|
-
|
|
2446
|
-
);
|
|
2727
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)}${note}`, {
|
|
2728
|
+
cause: error
|
|
2729
|
+
});
|
|
2447
2730
|
}
|
|
2448
2731
|
}
|
|
2449
2732
|
if (opts.json) {
|
|
@@ -2451,11 +2734,9 @@ async function dataInsert(opts) {
|
|
|
2451
2734
|
return;
|
|
2452
2735
|
}
|
|
2453
2736
|
const plural = records.length === 1 ? "" : "s";
|
|
2454
|
-
console.log(
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
}
|
|
2458
|
-
async function dataExport(opts) {
|
|
2737
|
+
console.log(`Inserted ${records.length} record${plural} into "${opts.collection}".`);
|
|
2738
|
+
};
|
|
2739
|
+
var dataExport = async (opts) => {
|
|
2459
2740
|
const linked = requireLinkedArtifact(opts.dir);
|
|
2460
2741
|
const client = createApiClient(linked.apiUrl, linked.publishToken);
|
|
2461
2742
|
const summary = await client.getDataSummary(linked.artifactId);
|
|
@@ -2485,8 +2766,8 @@ async function dataExport(opts) {
|
|
|
2485
2766
|
2
|
|
2486
2767
|
)
|
|
2487
2768
|
);
|
|
2488
|
-
}
|
|
2489
|
-
async
|
|
2769
|
+
};
|
|
2770
|
+
var dataClear = async (opts) => {
|
|
2490
2771
|
if (!opts.yes) {
|
|
2491
2772
|
throw new Error(
|
|
2492
2773
|
`Clearing "${opts.collection}" deletes every record in it. Re-run with --yes to confirm.`
|
|
@@ -2496,21 +2777,19 @@ async function dataClear(opts) {
|
|
|
2496
2777
|
const client = createApiClient(linked.apiUrl, linked.publishToken);
|
|
2497
2778
|
const result = await client.clearData(linked.artifactId, opts.collection);
|
|
2498
2779
|
if (opts.json) {
|
|
2499
|
-
console.log(
|
|
2500
|
-
JSON.stringify({ cleared: result.cleared, collection: opts.collection })
|
|
2501
|
-
);
|
|
2780
|
+
console.log(JSON.stringify({ cleared: result.cleared, collection: opts.collection }));
|
|
2502
2781
|
return;
|
|
2503
2782
|
}
|
|
2504
2783
|
console.log(`Cleared ${result.cleared} record(s) from "${opts.collection}".`);
|
|
2505
|
-
}
|
|
2784
|
+
};
|
|
2506
2785
|
|
|
2507
2786
|
// src/manifest.ts
|
|
2508
2787
|
import { createHash } from "crypto";
|
|
2509
|
-
import { lstatSync, readdirSync, readFileSync as
|
|
2510
|
-
import
|
|
2788
|
+
import { lstatSync, readdirSync, readFileSync as readFileSync6, statSync } from "fs";
|
|
2789
|
+
import path8 from "path";
|
|
2511
2790
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".artifactcafe"]);
|
|
2512
2791
|
var IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store", "artifact-cafe.json"]);
|
|
2513
|
-
|
|
2792
|
+
var isIgnoredFile = (name) => {
|
|
2514
2793
|
if (IGNORED_FILES.has(name)) {
|
|
2515
2794
|
return true;
|
|
2516
2795
|
}
|
|
@@ -2524,44 +2803,44 @@ function isIgnoredFile(name) {
|
|
|
2524
2803
|
return true;
|
|
2525
2804
|
}
|
|
2526
2805
|
return false;
|
|
2527
|
-
}
|
|
2528
|
-
var MIME_TYPES = {
|
|
2529
|
-
".avif": "image/avif",
|
|
2530
|
-
".css": "text/css",
|
|
2531
|
-
".csv": "text/csv",
|
|
2532
|
-
".gif": "image/gif",
|
|
2533
|
-
".htm": "text/html",
|
|
2534
|
-
".html": "text/html",
|
|
2535
|
-
".ico": "image/x-icon",
|
|
2536
|
-
".jpeg": "image/jpeg",
|
|
2537
|
-
".jpg": "image/jpeg",
|
|
2538
|
-
".js": "text/javascript",
|
|
2539
|
-
".json": "application/json",
|
|
2540
|
-
".map": "application/json",
|
|
2541
|
-
".markdown": "text/markdown",
|
|
2542
|
-
".md": "text/markdown",
|
|
2543
|
-
".mjs": "text/javascript",
|
|
2544
|
-
".mp3": "audio/mpeg",
|
|
2545
|
-
".mp4": "video/mp4",
|
|
2546
|
-
".otf": "font/otf",
|
|
2547
|
-
".pdf": "application/pdf",
|
|
2548
|
-
".png": "image/png",
|
|
2549
|
-
".svg": "image/svg+xml",
|
|
2550
|
-
".ttf": "font/ttf",
|
|
2551
|
-
".txt": "text/plain",
|
|
2552
|
-
".wasm": "application/wasm",
|
|
2553
|
-
".wav": "audio/wav",
|
|
2554
|
-
".webm": "video/webm",
|
|
2555
|
-
".webp": "image/webp",
|
|
2556
|
-
".woff": "font/woff",
|
|
2557
|
-
".woff2": "font/woff2",
|
|
2558
|
-
".xml": "application/xml"
|
|
2559
2806
|
};
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2807
|
+
var MIME_TYPES = new Map(
|
|
2808
|
+
Object.entries({
|
|
2809
|
+
".avif": "image/avif",
|
|
2810
|
+
".css": "text/css",
|
|
2811
|
+
".csv": "text/csv",
|
|
2812
|
+
".gif": "image/gif",
|
|
2813
|
+
".htm": "text/html",
|
|
2814
|
+
".html": "text/html",
|
|
2815
|
+
".ico": "image/x-icon",
|
|
2816
|
+
".jpeg": "image/jpeg",
|
|
2817
|
+
".jpg": "image/jpeg",
|
|
2818
|
+
".js": "text/javascript",
|
|
2819
|
+
".json": "application/json",
|
|
2820
|
+
".map": "application/json",
|
|
2821
|
+
".markdown": "text/markdown",
|
|
2822
|
+
".md": "text/markdown",
|
|
2823
|
+
".mjs": "text/javascript",
|
|
2824
|
+
".mp3": "audio/mpeg",
|
|
2825
|
+
".mp4": "video/mp4",
|
|
2826
|
+
".otf": "font/otf",
|
|
2827
|
+
".pdf": "application/pdf",
|
|
2828
|
+
".png": "image/png",
|
|
2829
|
+
".svg": "image/svg+xml",
|
|
2830
|
+
".ttf": "font/ttf",
|
|
2831
|
+
".txt": "text/plain",
|
|
2832
|
+
".wasm": "application/wasm",
|
|
2833
|
+
".wav": "audio/wav",
|
|
2834
|
+
".webm": "video/webm",
|
|
2835
|
+
".webp": "image/webp",
|
|
2836
|
+
".woff": "font/woff",
|
|
2837
|
+
".woff2": "font/woff2",
|
|
2838
|
+
".xml": "application/xml"
|
|
2839
|
+
})
|
|
2840
|
+
);
|
|
2841
|
+
var mimeFor = (filePath) => MIME_TYPES.get(path8.extname(filePath).toLowerCase()) ?? "application/octet-stream";
|
|
2842
|
+
var localFile = (absPath, rel) => {
|
|
2843
|
+
if (isIgnoredFile(path8.basename(rel)) || !isSafeArtifactPath(rel)) {
|
|
2565
2844
|
return null;
|
|
2566
2845
|
}
|
|
2567
2846
|
const { size } = statSync(absPath);
|
|
@@ -2570,28 +2849,19 @@ function localFile(absPath, rel) {
|
|
|
2570
2849
|
}
|
|
2571
2850
|
return {
|
|
2572
2851
|
absPath,
|
|
2573
|
-
hash: `sha256_${createHash("sha256").update(
|
|
2852
|
+
hash: `sha256_${createHash("sha256").update(readFileSync6(absPath)).digest("hex")}`,
|
|
2574
2853
|
mimeType: mimeFor(rel),
|
|
2575
2854
|
path: rel,
|
|
2576
2855
|
size
|
|
2577
2856
|
};
|
|
2578
|
-
}
|
|
2579
|
-
|
|
2580
|
-
const stat = lstatSync(target);
|
|
2581
|
-
if (stat.isDirectory()) {
|
|
2582
|
-
return scanDirectory(target);
|
|
2583
|
-
}
|
|
2584
|
-
const rel = path6.basename(target);
|
|
2585
|
-
const file = stat.isFile() ? localFile(target, rel) : null;
|
|
2586
|
-
return file ? { files: [file], skipped: [] } : { files: [], skipped: [rel] };
|
|
2587
|
-
}
|
|
2588
|
-
function scanDirectory(dir) {
|
|
2857
|
+
};
|
|
2858
|
+
var scanDirectory = (dir) => {
|
|
2589
2859
|
const files = [];
|
|
2590
2860
|
const skipped = [];
|
|
2591
|
-
|
|
2861
|
+
const walk = (current, relPrefix) => {
|
|
2592
2862
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
2593
2863
|
const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
2594
|
-
const abs =
|
|
2864
|
+
const abs = path8.join(current, entry.name);
|
|
2595
2865
|
if (entry.isSymbolicLink()) {
|
|
2596
2866
|
skipped.push(rel);
|
|
2597
2867
|
continue;
|
|
@@ -2614,14 +2884,21 @@ function scanDirectory(dir) {
|
|
|
2614
2884
|
}
|
|
2615
2885
|
files.push(file);
|
|
2616
2886
|
}
|
|
2617
|
-
}
|
|
2887
|
+
};
|
|
2618
2888
|
walk(dir, "");
|
|
2619
2889
|
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
2620
2890
|
return { files, skipped };
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2891
|
+
};
|
|
2892
|
+
var scanPath = (target) => {
|
|
2893
|
+
const stat = lstatSync(target);
|
|
2894
|
+
if (stat.isDirectory()) {
|
|
2895
|
+
return scanDirectory(target);
|
|
2896
|
+
}
|
|
2897
|
+
const rel = path8.basename(target);
|
|
2898
|
+
const file = stat.isFile() ? localFile(target, rel) : null;
|
|
2899
|
+
return file ? { files: [file], skipped: [] } : { files: [], skipped: [rel] };
|
|
2900
|
+
};
|
|
2901
|
+
var resolveEntry = (files, explicit) => resolvePublishEntry(files, explicit);
|
|
2625
2902
|
|
|
2626
2903
|
// src/spinner.ts
|
|
2627
2904
|
var FRAMES = ["\xE3", "\xE4", "\xE5"];
|
|
@@ -2629,18 +2906,19 @@ var INTERVAL_MS = 420;
|
|
|
2629
2906
|
var AMBER2 = "\x1B[38;2;214;138;57m";
|
|
2630
2907
|
var RESET2 = "\x1B[0m";
|
|
2631
2908
|
var CLEAR_LINE = "\r\x1B[2K";
|
|
2632
|
-
|
|
2909
|
+
var startBrewing = (label, enabled) => {
|
|
2633
2910
|
const stream = process.stderr;
|
|
2634
2911
|
if (!(enabled && stream.isTTY) || process.env.CI) {
|
|
2635
|
-
return {
|
|
2912
|
+
return {
|
|
2913
|
+
stop: () => {
|
|
2914
|
+
}
|
|
2915
|
+
};
|
|
2636
2916
|
}
|
|
2637
2917
|
const color = !process.env.NO_COLOR;
|
|
2638
2918
|
let frame = 0;
|
|
2639
2919
|
const draw = () => {
|
|
2640
2920
|
const glyph = FRAMES[frame % FRAMES.length];
|
|
2641
|
-
stream.write(
|
|
2642
|
-
`${CLEAR_LINE} ${color ? `${AMBER2}${glyph}${RESET2}` : glyph} ${label}`
|
|
2643
|
-
);
|
|
2921
|
+
stream.write(`${CLEAR_LINE} ${color ? `${AMBER2}${glyph}${RESET2}` : glyph} ${label}`);
|
|
2644
2922
|
frame += 1;
|
|
2645
2923
|
};
|
|
2646
2924
|
draw();
|
|
@@ -2651,199 +2929,392 @@ function startBrewing(label, enabled) {
|
|
|
2651
2929
|
stream.write(CLEAR_LINE);
|
|
2652
2930
|
}
|
|
2653
2931
|
};
|
|
2654
|
-
}
|
|
2932
|
+
};
|
|
2655
2933
|
|
|
2656
2934
|
// src/publish.ts
|
|
2657
|
-
var
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2935
|
+
var accessNote = (visibility) => {
|
|
2936
|
+
if (visibility === "workspace") {
|
|
2937
|
+
return "Access: workspace members only \u2014 anyone else needs `artifact-cafe share <email>` to open it.";
|
|
2938
|
+
}
|
|
2939
|
+
if (visibility === "private") {
|
|
2940
|
+
return "Access: private \u2014 you, the workspace's admins, and anyone you `artifact-cafe share` it with.";
|
|
2941
|
+
}
|
|
2942
|
+
return null;
|
|
2662
2943
|
};
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2944
|
+
var shouldPromptForWorkspace = (opts) => {
|
|
2945
|
+
if (opts.json || process.env.ARTIFACT_CAFE_AGENT) {
|
|
2946
|
+
return false;
|
|
2947
|
+
}
|
|
2948
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
2949
|
+
};
|
|
2950
|
+
var pickWorkspace = (workspaces3) => {
|
|
2951
|
+
const choices = [...workspaces3].sort((a, b) => {
|
|
2952
|
+
if (a.isPersonal !== b.isPersonal) {
|
|
2953
|
+
return a.isPersonal ? -1 : 1;
|
|
2672
2954
|
}
|
|
2955
|
+
return a.name.localeCompare(b.name);
|
|
2956
|
+
});
|
|
2957
|
+
console.log("Where should this artifact live?\n");
|
|
2958
|
+
for (const [index, workspace] of choices.entries()) {
|
|
2959
|
+
const label = workspace.isPersonal ? "Personal" : workspace.name;
|
|
2960
|
+
const handle = workspace.isPersonal ? "" : ` \xB7 ${workspace.handle}`;
|
|
2961
|
+
console.log(` ${String(index + 1).padStart(2)}. ${label}${handle}`);
|
|
2673
2962
|
}
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2963
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2964
|
+
return new Promise((resolve2, reject) => {
|
|
2965
|
+
rl.question(`
|
|
2966
|
+
Publish where? [1-${choices.length}]: `, (answer) => {
|
|
2967
|
+
const index = Number(answer.trim()) - 1;
|
|
2968
|
+
const workspace = choices[index];
|
|
2969
|
+
if (!(Number.isInteger(index) && workspace)) {
|
|
2970
|
+
rl.close();
|
|
2971
|
+
reject(new Error(`Enter a number between 1 and ${choices.length}.`));
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
if (workspace.isPersonal) {
|
|
2975
|
+
rl.close();
|
|
2976
|
+
resolve2({ remember: false, workspace });
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
rl.question(
|
|
2980
|
+
`Remember ${workspace.name} for new artifacts in this project? [y/N]: `,
|
|
2981
|
+
(rememberAnswer) => {
|
|
2982
|
+
rl.close();
|
|
2983
|
+
resolve2({
|
|
2984
|
+
remember: /^y(?:es)?$/iu.test(rememberAnswer.trim()),
|
|
2985
|
+
workspace
|
|
2986
|
+
});
|
|
2987
|
+
}
|
|
2988
|
+
);
|
|
2989
|
+
});
|
|
2990
|
+
});
|
|
2991
|
+
};
|
|
2992
|
+
var agentPlacement = (apiKey, opts) => {
|
|
2993
|
+
if (apiKey?.startsWith("aat_")) {
|
|
2994
|
+
if (opts.workspace !== void 0 || opts.folder !== void 0) {
|
|
2995
|
+
throw new Error("An agent token selects its own workspace; omit --workspace and --folder.");
|
|
2679
2996
|
}
|
|
2680
|
-
|
|
2997
|
+
return {
|
|
2998
|
+
destination: "workspace",
|
|
2999
|
+
destinationLabel: "Agent token's workspace",
|
|
3000
|
+
destinationSource: "agent-token"
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
return null;
|
|
3004
|
+
};
|
|
3005
|
+
var storedPlacement = (config, workspaceId, apiKey, apiUrl2) => {
|
|
3006
|
+
const isPersonal = config.workspaceKind === "personal";
|
|
3007
|
+
if (!(apiKey || isPersonal)) {
|
|
3008
|
+
throw new Error(
|
|
3009
|
+
`Publishing into workspace ${config.workspace ?? workspaceId} needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
|
|
3010
|
+
);
|
|
3011
|
+
}
|
|
3012
|
+
return {
|
|
3013
|
+
destination: isPersonal ? "personal" : "workspace",
|
|
3014
|
+
destinationLabel: isPersonal ? "Personal" : config.workspace ?? workspaceId,
|
|
3015
|
+
destinationSource: "artifact-config",
|
|
3016
|
+
...config.folder ? { folder: config.folder } : void 0,
|
|
3017
|
+
...config.folderId ? { folderId: config.folderId } : void 0,
|
|
3018
|
+
...config.workspace ? { workspace: config.workspace } : void 0,
|
|
3019
|
+
...apiKey ? { publishWorkspaceId: workspaceId } : void 0,
|
|
3020
|
+
workspaceId
|
|
3021
|
+
};
|
|
3022
|
+
};
|
|
3023
|
+
var defaultPlacement = async (apiKey, apiUrl2, opts) => {
|
|
3024
|
+
if (!apiKey) {
|
|
3025
|
+
return {
|
|
3026
|
+
destination: "anonymous",
|
|
3027
|
+
destinationLabel: "Anonymous",
|
|
3028
|
+
destinationSource: "anonymous-default"
|
|
3029
|
+
};
|
|
3030
|
+
}
|
|
3031
|
+
if (shouldPromptForWorkspace(opts)) {
|
|
3032
|
+
const data2 = await createApiClient(apiUrl2, apiKey).listWorkspaces();
|
|
3033
|
+
if (data2.workspaces.length > 1) {
|
|
3034
|
+
const choice = await pickWorkspace(data2.workspaces);
|
|
3035
|
+
return {
|
|
3036
|
+
destination: choice.workspace.isPersonal ? "personal" : "workspace",
|
|
3037
|
+
destinationLabel: choice.workspace.isPersonal ? "Personal" : choice.workspace.name,
|
|
3038
|
+
destinationSource: "interactive",
|
|
3039
|
+
publishWorkspaceId: choice.workspace.id,
|
|
3040
|
+
...choice.remember ? {
|
|
3041
|
+
rememberProjectDefault: {
|
|
3042
|
+
workspace: choice.workspace.handle
|
|
3043
|
+
}
|
|
3044
|
+
} : void 0,
|
|
3045
|
+
workspace: choice.workspace.handle,
|
|
3046
|
+
workspaceId: choice.workspace.id
|
|
3047
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
return {
|
|
3051
|
+
destination: "personal",
|
|
3052
|
+
destinationLabel: "Personal",
|
|
3053
|
+
destinationSource: "personal-default"
|
|
3054
|
+
};
|
|
3055
|
+
};
|
|
3056
|
+
var getPublishWorkspace = (workspaces3, opts, config, projectConfig) => {
|
|
3057
|
+
const workspaceHandle = opts.workspace ?? config?.workspace ?? projectConfig?.workspace;
|
|
3058
|
+
let workspace;
|
|
3059
|
+
if (workspaceHandle) {
|
|
3060
|
+
workspace = workspaces3.find((candidate) => candidate.handle === workspaceHandle);
|
|
3061
|
+
} else if (config?.workspaceId) {
|
|
3062
|
+
workspace = workspaces3.find((candidate) => candidate.id === config.workspaceId);
|
|
3063
|
+
} else {
|
|
3064
|
+
workspace = workspaces3.find((candidate) => candidate.isPersonal);
|
|
3065
|
+
}
|
|
3066
|
+
if (!workspace) {
|
|
3067
|
+
const target = workspaceHandle ? `workspace with handle "${workspaceHandle}"` : "personal workspace";
|
|
3068
|
+
throw new Error(`No ${target} belongs to this account.`);
|
|
2681
3069
|
}
|
|
3070
|
+
return workspace;
|
|
3071
|
+
};
|
|
3072
|
+
var publishingIdentity = (opts, config) => {
|
|
2682
3073
|
const artifactId = opts.artifact ?? config?.artifactId ?? null;
|
|
2683
3074
|
const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
|
|
2684
3075
|
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
|
|
2685
3076
|
const apiKey = resolveApiKey(apiUrl2, opts.apiKey);
|
|
2686
3077
|
const usingAgentToken = apiKey?.startsWith("aat_") === true;
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
3078
|
+
return { apiKey, apiUrl: apiUrl2, artifactId, token, usingAgentToken };
|
|
3079
|
+
};
|
|
3080
|
+
var resolveRequestedPlacement = async ({
|
|
3081
|
+
apiKey,
|
|
3082
|
+
apiUrl: apiUrl2,
|
|
3083
|
+
config,
|
|
3084
|
+
isNewArtifact,
|
|
3085
|
+
opts,
|
|
3086
|
+
projectConfig,
|
|
3087
|
+
hasPlacementFlag
|
|
3088
|
+
}) => {
|
|
3089
|
+
if (!isNewArtifact && opts.folder !== void 0 && opts.workspace === void 0 && !(config?.workspace || config?.workspaceId)) {
|
|
3090
|
+
throw new Error("--folder requires --workspace or a stored workspace.");
|
|
2697
3091
|
}
|
|
2698
|
-
const
|
|
2699
|
-
const
|
|
2700
|
-
const
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
3092
|
+
const client = createApiClient(apiUrl2, apiKey);
|
|
3093
|
+
const data2 = await client.listWorkspaces();
|
|
3094
|
+
const workspace = getPublishWorkspace(data2.workspaces, opts, config, projectConfig);
|
|
3095
|
+
const folderName = opts.workspace !== void 0 && opts.folder === void 0 ? void 0 : opts.folder ?? projectConfig?.folder;
|
|
3096
|
+
const destinationSource = hasPlacementFlag ? "explicit" : "project-default";
|
|
3097
|
+
const placement = {
|
|
3098
|
+
destination: workspace.isPersonal ? "personal" : "workspace",
|
|
3099
|
+
destinationLabel: workspace.isPersonal ? "Personal" : workspace.name,
|
|
3100
|
+
destinationSource,
|
|
3101
|
+
publishWorkspaceId: workspace.id,
|
|
3102
|
+
workspace: workspace.handle,
|
|
3103
|
+
workspaceId: workspace.id
|
|
2704
3104
|
};
|
|
2705
|
-
if (
|
|
2706
|
-
|
|
3105
|
+
if (!folderName) {
|
|
3106
|
+
return placement;
|
|
2707
3107
|
}
|
|
2708
|
-
const
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
entryPath,
|
|
2715
|
-
files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
|
|
2716
|
-
...placement.folderId ? { folderId: placement.folderId } : {},
|
|
2717
|
-
// Agents often pass a double-quoted string where "\n" is a literal
|
|
2718
|
-
// backslash-n; turn those back into real newlines so bullets survive.
|
|
2719
|
-
...opts.notes?.trim() ? { notes: opts.notes.replace(/\\n/g, "\n").trim() } : {},
|
|
2720
|
-
source: {
|
|
2721
|
-
// ARTIFACT_CAFE_AGENT if it's set, else whichever agent's environment
|
|
2722
|
-
// we recognize — this is what names the agent on every reply to a
|
|
2723
|
-
// thread opened against this version (§6.5).
|
|
2724
|
-
agent: agentLabel(),
|
|
2725
|
-
runId: process.env.ARTIFACT_CAFE_RUN_ID,
|
|
2726
|
-
type: "cli"
|
|
2727
|
-
},
|
|
2728
|
-
title: opts.title,
|
|
2729
|
-
...placement.publishWorkspaceId ? { workspaceId: placement.publishWorkspaceId } : {}
|
|
2730
|
-
};
|
|
2731
|
-
let credential = apiKey ?? token;
|
|
2732
|
-
let client = createApiClient(apiUrl2, credential);
|
|
2733
|
-
let session;
|
|
2734
|
-
try {
|
|
2735
|
-
session = await client.createSession(sessionBody);
|
|
2736
|
-
} catch (err) {
|
|
2737
|
-
const keyRefused = apiKey !== void 0 && !usingAgentToken && token !== void 0 && apiKey !== token && artifactId !== null && err instanceof ApiRequestError && (err.status === 401 || err.status === 403);
|
|
2738
|
-
if (!keyRefused) {
|
|
2739
|
-
throw err;
|
|
2740
|
-
}
|
|
2741
|
-
credential = token;
|
|
2742
|
-
client = createApiClient(apiUrl2, credential);
|
|
2743
|
-
session = await client.createSession(sessionBody);
|
|
3108
|
+
const folderData = await client.listFolders(workspace.id);
|
|
3109
|
+
const matches = folderData.folders.filter(
|
|
3110
|
+
(candidate) => candidate.name === folderName || candidate.id === folderName
|
|
3111
|
+
);
|
|
3112
|
+
if (matches.length === 0) {
|
|
3113
|
+
throw new Error(`No folder named "${folderName}" exists in workspace ${workspace.handle}.`);
|
|
2744
3114
|
}
|
|
2745
|
-
if (
|
|
3115
|
+
if (matches.length > 1) {
|
|
2746
3116
|
throw new Error(
|
|
2747
|
-
|
|
3117
|
+
`More than one folder is named "${folderName}" in workspace ${workspace.handle}; pass the folder id instead.`
|
|
2748
3118
|
);
|
|
2749
3119
|
}
|
|
2750
|
-
const
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
);
|
|
2754
|
-
const presence = startPublishingPresence(client, session.artifactId);
|
|
2755
|
-
const brewing = startBrewing("brewing\u2026", !opts.json);
|
|
2756
|
-
try {
|
|
2757
|
-
await inBatches(
|
|
2758
|
-
session.filesToUpload,
|
|
2759
|
-
UPLOAD_CONCURRENCY,
|
|
2760
|
-
async (upload) => {
|
|
2761
|
-
const file = byPath.get(upload.path);
|
|
2762
|
-
if (!file) {
|
|
2763
|
-
throw new Error(`Server asked for unknown file ${upload.path}`);
|
|
2764
|
-
}
|
|
2765
|
-
await uploadFile(
|
|
2766
|
-
upload.uploadUrl,
|
|
2767
|
-
readFileSync5(file.absPath),
|
|
2768
|
-
file.mimeType
|
|
2769
|
-
);
|
|
2770
|
-
}
|
|
2771
|
-
);
|
|
2772
|
-
} catch (error) {
|
|
2773
|
-
brewing.stop();
|
|
2774
|
-
throw error;
|
|
2775
|
-
} finally {
|
|
2776
|
-
presence.stop();
|
|
3120
|
+
const [folder] = matches;
|
|
3121
|
+
if (!folder) {
|
|
3122
|
+
throw new Error("The selected folder is unavailable.");
|
|
2777
3123
|
}
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
}
|
|
2782
|
-
|
|
3124
|
+
return { ...placement, folder: folder.name, folderId: folder.id };
|
|
3125
|
+
};
|
|
3126
|
+
var resolvePlacement = async (params) => {
|
|
3127
|
+
const { apiKey, apiUrl: apiUrl2, config, isNewArtifact, opts } = params;
|
|
3128
|
+
const agent = agentPlacement(apiKey, opts);
|
|
3129
|
+
if (agent) {
|
|
3130
|
+
return agent;
|
|
2783
3131
|
}
|
|
2784
|
-
|
|
2785
|
-
|
|
3132
|
+
const hasPlacementFlag = opts.workspace !== void 0 || opts.folder !== void 0;
|
|
3133
|
+
if (!hasPlacementFlag && config?.workspaceId) {
|
|
3134
|
+
return storedPlacement(config, config.workspaceId, apiKey, apiUrl2);
|
|
2786
3135
|
}
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
artifactId: result.artifactId,
|
|
2794
|
-
...placement.folder ? { folder: placement.folder } : {},
|
|
2795
|
-
...placement.folderId ? { folderId: placement.folderId } : {},
|
|
2796
|
-
...!usingAgentToken && (result.publishToken ?? existingToken) ? { publishToken: result.publishToken ?? existingToken } : {},
|
|
2797
|
-
slug: slugFromUrl(result.url),
|
|
2798
|
-
url: result.url,
|
|
2799
|
-
...placement.workspace ? { workspace: placement.workspace } : {},
|
|
2800
|
-
...placement.workspaceId ? { workspaceId: placement.workspaceId } : {},
|
|
2801
|
-
...placement.destination === "personal" || placement.destination === "workspace" ? { workspaceKind: placement.destination } : {}
|
|
2802
|
-
});
|
|
3136
|
+
if (!(hasPlacementFlag || isNewArtifact)) {
|
|
3137
|
+
return {
|
|
3138
|
+
destination: "existing-artifact",
|
|
3139
|
+
destinationLabel: "Existing artifact",
|
|
3140
|
+
destinationSource: "artifact-config"
|
|
3141
|
+
};
|
|
2803
3142
|
}
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
3143
|
+
const projectConfig = isNewArtifact ? readProjectConfig(opts.dir) : null;
|
|
3144
|
+
const wantsWorkspace = opts.workspace !== void 0 || opts.folder !== void 0 || projectConfig !== null;
|
|
3145
|
+
if (wantsWorkspace && !apiKey) {
|
|
3146
|
+
throw new Error(
|
|
3147
|
+
"Publishing into a workspace needs an account \u2014 run `artifact-cafe login` first."
|
|
2808
3148
|
);
|
|
2809
|
-
await protectClient.setProtection(result.artifactId, opts.password);
|
|
2810
3149
|
}
|
|
2811
|
-
if (
|
|
2812
|
-
|
|
2813
|
-
apiUrl2,
|
|
2814
|
-
credential ?? result.publishToken
|
|
2815
|
-
);
|
|
2816
|
-
await reviewClient.setReviewMode(result.artifactId, reviewMode);
|
|
3150
|
+
if (!(hasPlacementFlag || projectConfig)) {
|
|
3151
|
+
return defaultPlacement(apiKey, apiUrl2, opts);
|
|
2817
3152
|
}
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
3153
|
+
return await resolveRequestedPlacement({ ...params, hasPlacementFlag, projectConfig });
|
|
3154
|
+
};
|
|
3155
|
+
var startPublishingPresence = (client, artifactId) => {
|
|
3156
|
+
const beat = async () => {
|
|
3157
|
+
try {
|
|
3158
|
+
await client.agentHeartbeat(artifactId, "publishing");
|
|
3159
|
+
} catch {
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
beat();
|
|
3163
|
+
const timer = setInterval(beat, AGENT_PRESENCE.heartbeatSeconds * 1e3);
|
|
3164
|
+
timer.unref();
|
|
3165
|
+
return { stop: () => clearInterval(timer) };
|
|
3166
|
+
};
|
|
3167
|
+
var inBatches = async (items, concurrency, fn) => {
|
|
3168
|
+
let next = 0;
|
|
3169
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
3170
|
+
while (next < items.length) {
|
|
3171
|
+
const item = items[next];
|
|
3172
|
+
next += 1;
|
|
3173
|
+
if (item === void 0) {
|
|
3174
|
+
continue;
|
|
3175
|
+
}
|
|
3176
|
+
await fn(item);
|
|
3177
|
+
}
|
|
3178
|
+
});
|
|
3179
|
+
await Promise.all(workers);
|
|
3180
|
+
};
|
|
3181
|
+
var slugFromUrl = (url) => url.split("/a/")[1] ?? "";
|
|
3182
|
+
var preview = (items) => items.slice(0, 5).join(", ") + (items.length > 5 ? ", \u2026" : "");
|
|
3183
|
+
var openInBrowser = (url) => {
|
|
3184
|
+
let cmd = "xdg-open";
|
|
3185
|
+
if (process.platform === "darwin") {
|
|
3186
|
+
cmd = "open";
|
|
3187
|
+
} else if (process.platform === "win32") {
|
|
3188
|
+
cmd = "start";
|
|
3189
|
+
}
|
|
3190
|
+
exec(`${cmd} ${JSON.stringify(url)}`, () => {
|
|
3191
|
+
});
|
|
3192
|
+
};
|
|
3193
|
+
var UPLOAD_CONCURRENCY = 6;
|
|
3194
|
+
var REVIEW_LABEL = {
|
|
3195
|
+
off: "off \u2014 plain artifact, no review tools",
|
|
3196
|
+
open: "open \u2014 anyone with the link can comment",
|
|
3197
|
+
readonly: "read-only \u2014 existing comments only"
|
|
3198
|
+
};
|
|
3199
|
+
var createSessionBody = ({
|
|
3200
|
+
artifactId,
|
|
3201
|
+
baseVersion,
|
|
3202
|
+
entryPath,
|
|
3203
|
+
expiresInSeconds,
|
|
3204
|
+
files,
|
|
3205
|
+
opts,
|
|
3206
|
+
placement
|
|
3207
|
+
}) => ({
|
|
3208
|
+
artifactId,
|
|
3209
|
+
...baseVersion === void 0 ? void 0 : { baseVersion },
|
|
3210
|
+
...opts.force ? { force: true } : void 0,
|
|
3211
|
+
...expiresInSeconds === void 0 ? void 0 : { expiresInSeconds },
|
|
3212
|
+
// Overrides the entry HTML's own meta description on the share card.
|
|
3213
|
+
// Omitted entirely when absent, so the document keeps speaking for itself.
|
|
3214
|
+
...opts.description?.trim() ? { description: opts.description.trim() } : void 0,
|
|
3215
|
+
entryPath,
|
|
3216
|
+
files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
|
|
3217
|
+
...placement.folderId ? { folderId: placement.folderId } : void 0,
|
|
3218
|
+
// Agents often pass a double-quoted string where "\n" is a literal
|
|
3219
|
+
// backslash-n; turn those back into real newlines so bullets survive.
|
|
3220
|
+
...opts.notes?.trim() ? { notes: opts.notes.replaceAll("\\n", "\n").trim() } : void 0,
|
|
3221
|
+
source: {
|
|
3222
|
+
// ARTIFACT_CAFE_AGENT if it's set, else whichever agent's environment
|
|
3223
|
+
// we recognize — this is what names the agent on every reply to a
|
|
3224
|
+
// thread opened against this version (§6.5).
|
|
3225
|
+
agent: agentLabel(),
|
|
3226
|
+
runId: process.env.ARTIFACT_CAFE_RUN_ID,
|
|
3227
|
+
type: "cli"
|
|
3228
|
+
},
|
|
3229
|
+
title: opts.title,
|
|
3230
|
+
...placement.publishWorkspaceId ? { workspaceId: placement.publishWorkspaceId } : void 0
|
|
3231
|
+
});
|
|
3232
|
+
var savePublishedConfig = ({
|
|
3233
|
+
apiUrl: apiUrl2,
|
|
3234
|
+
config,
|
|
3235
|
+
dir,
|
|
3236
|
+
opts,
|
|
3237
|
+
placement,
|
|
3238
|
+
result,
|
|
3239
|
+
usingAgentToken
|
|
3240
|
+
}) => {
|
|
3241
|
+
const publishToken = usingAgentToken ? void 0 : result.publishToken ?? (config?.artifactId === result.artifactId ? config.publishToken : void 0);
|
|
3242
|
+
const shouldWriteConfig = usingAgentToken || result.publishToken !== void 0 || config?.artifactId === result.artifactId || opts.workspace !== void 0 || opts.folder !== void 0 || placement.workspaceId !== void 0;
|
|
3243
|
+
if (shouldWriteConfig) {
|
|
3244
|
+
writeConfig(dir, {
|
|
3245
|
+
apiUrl: apiUrl2,
|
|
3246
|
+
artifactId: result.artifactId,
|
|
3247
|
+
baseVersion: result.versionNumber,
|
|
3248
|
+
...placement.folder ? { folder: placement.folder } : void 0,
|
|
3249
|
+
...placement.folderId ? { folderId: placement.folderId } : void 0,
|
|
3250
|
+
...publishToken ? { publishToken } : void 0,
|
|
3251
|
+
slug: slugFromUrl(result.url),
|
|
3252
|
+
url: result.url,
|
|
3253
|
+
...placement.workspace ? { workspace: placement.workspace } : void 0,
|
|
3254
|
+
...placement.workspaceId ? { workspaceId: placement.workspaceId } : void 0,
|
|
3255
|
+
...placement.destination === "personal" || placement.destination === "workspace" ? { workspaceKind: placement.destination } : void 0
|
|
3256
|
+
});
|
|
3257
|
+
}
|
|
3258
|
+
};
|
|
3259
|
+
var printPublishJson = ({
|
|
3260
|
+
reviewMode,
|
|
3261
|
+
dataInfo,
|
|
3262
|
+
opts,
|
|
3263
|
+
placement,
|
|
3264
|
+
result,
|
|
3265
|
+
session,
|
|
3266
|
+
skipped
|
|
3267
|
+
}) => {
|
|
3268
|
+
console.log(
|
|
3269
|
+
JSON.stringify(
|
|
3270
|
+
{
|
|
3271
|
+
artifactId: result.artifactId,
|
|
3272
|
+
autoDeleteAt: result.autoDeleteAt ?? null,
|
|
3273
|
+
expiresAt: result.expiresAt ?? null,
|
|
3274
|
+
...dataInfo.config ? { data: dataInfo.config } : void 0,
|
|
3275
|
+
claimed: result.claimed,
|
|
3276
|
+
destination: placement.destination,
|
|
3277
|
+
destinationSource: placement.destinationSource,
|
|
3278
|
+
folder: placement.folder ?? null,
|
|
3279
|
+
folderId: placement.folderId ?? null,
|
|
3280
|
+
url: result.url,
|
|
3281
|
+
versionNumber: result.versionNumber,
|
|
3282
|
+
visibility: result.visibility ?? null,
|
|
3283
|
+
workspace: placement.workspace ?? null,
|
|
3284
|
+
workspaceId: placement.workspaceId ?? null,
|
|
3285
|
+
...result.claimUrl ? { claimUrl: result.claimUrl } : void 0,
|
|
3286
|
+
...opts.password === void 0 ? void 0 : { protected: true },
|
|
3287
|
+
...reviewMode === void 0 ? void 0 : { reviewMode },
|
|
3288
|
+
filesUploaded: session.filesToUpload.map((f) => f.path),
|
|
3289
|
+
ignored: skipped,
|
|
3290
|
+
skippedFiles: session.skippedFiles
|
|
3291
|
+
},
|
|
3292
|
+
null,
|
|
3293
|
+
2
|
|
3294
|
+
)
|
|
3295
|
+
);
|
|
3296
|
+
};
|
|
3297
|
+
var printPublishResult = ({
|
|
3298
|
+
reviewMode,
|
|
3299
|
+
dataInfo,
|
|
3300
|
+
opts,
|
|
3301
|
+
placement,
|
|
3302
|
+
rememberedProjectFile,
|
|
3303
|
+
result,
|
|
3304
|
+
session,
|
|
3305
|
+
skipped
|
|
3306
|
+
}) => {
|
|
3307
|
+
if (opts.json) {
|
|
3308
|
+
printPublishJson({
|
|
3309
|
+
dataInfo,
|
|
3310
|
+
opts,
|
|
3311
|
+
placement,
|
|
3312
|
+
rememberedProjectFile,
|
|
3313
|
+
result,
|
|
3314
|
+
reviewMode,
|
|
3315
|
+
session,
|
|
3316
|
+
skipped
|
|
3317
|
+
});
|
|
2847
3318
|
} else if (result.versionNumber === 1) {
|
|
2848
3319
|
console.log("\n\u2713 Published to artifact.cafe\n");
|
|
2849
3320
|
console.log(`Destination: ${placement.destinationLabel}
|
|
@@ -2861,11 +3332,9 @@ ${result.url}
|
|
|
2861
3332
|
`);
|
|
2862
3333
|
}
|
|
2863
3334
|
if (result.claimUrl) {
|
|
2864
|
-
console.log(
|
|
2865
|
-
`Claim URL (shown once \u2014 claim to keep this artifact):
|
|
3335
|
+
console.log(`Claim URL (shown once \u2014 claim to keep this artifact):
|
|
2866
3336
|
${result.claimUrl}
|
|
2867
|
-
`
|
|
2868
|
-
);
|
|
3337
|
+
`);
|
|
2869
3338
|
} else if (result.claimed && !result.expiresAt) {
|
|
2870
3339
|
console.log("Claimed by your account \u2014 no expiry.\n");
|
|
2871
3340
|
}
|
|
@@ -2912,269 +3381,172 @@ ${result.claimUrl}
|
|
|
2912
3381
|
console.log(announcement);
|
|
2913
3382
|
}
|
|
2914
3383
|
}
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
if (
|
|
2932
|
-
throw
|
|
2933
|
-
"An agent token selects its own workspace; omit --workspace and --folder."
|
|
2934
|
-
);
|
|
3384
|
+
};
|
|
3385
|
+
var openPublishSession = async ({
|
|
3386
|
+
apiKey,
|
|
3387
|
+
token,
|
|
3388
|
+
apiUrl: apiUrl2,
|
|
3389
|
+
usingAgentToken,
|
|
3390
|
+
artifactId,
|
|
3391
|
+
sessionBody
|
|
3392
|
+
}) => {
|
|
3393
|
+
let credential = apiKey ?? token;
|
|
3394
|
+
let client = createApiClient(apiUrl2, credential);
|
|
3395
|
+
let session;
|
|
3396
|
+
try {
|
|
3397
|
+
session = await client.createSession(sessionBody);
|
|
3398
|
+
} catch (error) {
|
|
3399
|
+
const keyRefused = apiKey !== void 0 && !usingAgentToken && token !== void 0 && apiKey !== token && artifactId !== null && error instanceof ApiRequestError && (error.status === 401 || error.status === 403);
|
|
3400
|
+
if (!keyRefused) {
|
|
3401
|
+
throw error;
|
|
2935
3402
|
}
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
destinationSource: "agent-token"
|
|
2940
|
-
};
|
|
3403
|
+
credential = token;
|
|
3404
|
+
client = createApiClient(apiUrl2, credential);
|
|
3405
|
+
session = await client.createSession(sessionBody);
|
|
2941
3406
|
}
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
3407
|
+
return { client, credential, session };
|
|
3408
|
+
};
|
|
3409
|
+
var validatePublishOptions = (opts) => {
|
|
3410
|
+
const expiresInSeconds = opts.expiresIn === void 0 ? void 0 : parseArtifactDuration(opts.expiresIn);
|
|
3411
|
+
if (opts.password !== void 0) {
|
|
3412
|
+
const parsed = gatePasswordSchema.safeParse(opts.password);
|
|
3413
|
+
if (!parsed.success) {
|
|
3414
|
+
throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
|
|
2949
3415
|
}
|
|
2950
|
-
return {
|
|
2951
|
-
destination: isPersonal ? "personal" : "workspace",
|
|
2952
|
-
destinationLabel: isPersonal ? "Personal" : config.workspace ?? config.workspaceId,
|
|
2953
|
-
destinationSource: "artifact-config",
|
|
2954
|
-
...config.folder ? { folder: config.folder } : {},
|
|
2955
|
-
...config.folderId ? { folderId: config.folderId } : {},
|
|
2956
|
-
...config.workspace ? { workspace: config.workspace } : {},
|
|
2957
|
-
...apiKey ? { publishWorkspaceId: config.workspaceId } : {},
|
|
2958
|
-
workspaceId: config.workspaceId
|
|
2959
|
-
};
|
|
2960
3416
|
}
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
}
|
|
3417
|
+
let reviewMode;
|
|
3418
|
+
if (opts.review !== void 0) {
|
|
3419
|
+
const parsed = reviewModeSchema.safeParse(opts.review);
|
|
3420
|
+
if (!parsed.success) {
|
|
3421
|
+
throw new Error("--review must be one of: open, readonly, off.");
|
|
3422
|
+
}
|
|
3423
|
+
reviewMode = parsed.data;
|
|
2967
3424
|
}
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
3425
|
+
return { expiresInSeconds, reviewMode };
|
|
3426
|
+
};
|
|
3427
|
+
var publish = async (opts) => {
|
|
3428
|
+
const target = path9.resolve(opts.dir);
|
|
3429
|
+
const dir = statSync2(target).isFile() ? path9.dirname(target) : target;
|
|
3430
|
+
const config = readConfig(dir);
|
|
3431
|
+
const { expiresInSeconds, reviewMode } = validatePublishOptions(opts);
|
|
3432
|
+
const { artifactId, token, apiUrl: apiUrl2, apiKey, usingAgentToken } = publishingIdentity(opts, config);
|
|
3433
|
+
const placement = await resolvePlacement({
|
|
3434
|
+
apiKey,
|
|
3435
|
+
apiUrl: apiUrl2,
|
|
3436
|
+
config,
|
|
3437
|
+
isNewArtifact: artifactId === null,
|
|
3438
|
+
opts: { ...opts, dir }
|
|
3439
|
+
});
|
|
3440
|
+
const { files, skipped } = scanPath(target);
|
|
3441
|
+
if (files.length === 0) {
|
|
3442
|
+
throw new Error(`Nothing to publish at ${target} (after ignore rules).`);
|
|
2974
3443
|
}
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
destinationSource: "anonymous-default"
|
|
2981
|
-
};
|
|
2982
|
-
}
|
|
2983
|
-
if (shouldPromptForWorkspace(opts)) {
|
|
2984
|
-
const data3 = await createApiClient(apiUrl2, apiKey).listWorkspaces();
|
|
2985
|
-
if (data3.workspaces.length > 1) {
|
|
2986
|
-
const choice = await pickWorkspace(data3.workspaces);
|
|
2987
|
-
return {
|
|
2988
|
-
destination: choice.workspace.isPersonal ? "personal" : "workspace",
|
|
2989
|
-
destinationLabel: choice.workspace.isPersonal ? "Personal" : choice.workspace.name,
|
|
2990
|
-
destinationSource: "interactive",
|
|
2991
|
-
publishWorkspaceId: choice.workspace.id,
|
|
2992
|
-
...choice.remember ? {
|
|
2993
|
-
rememberProjectDefault: {
|
|
2994
|
-
workspace: choice.workspace.handle
|
|
2995
|
-
}
|
|
2996
|
-
} : {},
|
|
2997
|
-
workspace: choice.workspace.handle,
|
|
2998
|
-
workspaceId: choice.workspace.id
|
|
2999
|
-
};
|
|
3000
|
-
}
|
|
3444
|
+
const entryPath = resolveEntry(files, opts.entry);
|
|
3445
|
+
const dataInfo = inspectDataManifest(files);
|
|
3446
|
+
const log = (msg) => {
|
|
3447
|
+
if (!opts.json) {
|
|
3448
|
+
console.log(msg);
|
|
3001
3449
|
}
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
destinationSource: "personal-default"
|
|
3006
|
-
};
|
|
3007
|
-
}
|
|
3008
|
-
if (!isNewArtifact && opts.folder !== void 0 && opts.workspace === void 0 && !(config?.workspace || config?.workspaceId)) {
|
|
3009
|
-
throw new Error("--folder requires --workspace or a stored workspace.");
|
|
3450
|
+
};
|
|
3451
|
+
if (skipped.length) {
|
|
3452
|
+
log(` ignoring ${skipped.length} item(s): ${preview(skipped)}`);
|
|
3010
3453
|
}
|
|
3011
|
-
const
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
}
|
|
3020
|
-
|
|
3021
|
-
|
|
3454
|
+
const sessionBody = createSessionBody({
|
|
3455
|
+
artifactId,
|
|
3456
|
+
baseVersion: artifactId === config?.artifactId ? config.baseVersion : void 0,
|
|
3457
|
+
entryPath,
|
|
3458
|
+
expiresInSeconds,
|
|
3459
|
+
files,
|
|
3460
|
+
opts,
|
|
3461
|
+
placement
|
|
3462
|
+
});
|
|
3463
|
+
const { client, credential, session } = await openPublishSession({
|
|
3464
|
+
apiKey,
|
|
3465
|
+
apiUrl: apiUrl2,
|
|
3466
|
+
artifactId,
|
|
3467
|
+
sessionBody,
|
|
3468
|
+
token,
|
|
3469
|
+
usingAgentToken
|
|
3470
|
+
});
|
|
3471
|
+
if (expiresInSeconds !== void 0 && session.expiresInSeconds !== expiresInSeconds) {
|
|
3472
|
+
throw new Error(
|
|
3473
|
+
"This API does not support temporary publishing. Nothing was uploaded or finalized; upgrade the API before using --expires-in."
|
|
3022
3474
|
);
|
|
3023
|
-
} else {
|
|
3024
|
-
workspace = data2.workspaces.find((candidate) => candidate.isPersonal);
|
|
3025
3475
|
}
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3476
|
+
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
3477
|
+
log(
|
|
3478
|
+
` uploading ${session.filesToUpload.length} file(s)${session.skippedFiles.length ? `, ${session.skippedFiles.length} unchanged` : ""}`
|
|
3479
|
+
);
|
|
3480
|
+
const presence = startPublishingPresence(client, session.artifactId);
|
|
3481
|
+
const brewing = startBrewing("brewing\u2026", !opts.json);
|
|
3482
|
+
try {
|
|
3483
|
+
await inBatches(session.filesToUpload, UPLOAD_CONCURRENCY, async (upload) => {
|
|
3484
|
+
const file = byPath.get(upload.path);
|
|
3485
|
+
if (!file) {
|
|
3486
|
+
throw new Error(`Server asked for unknown file ${upload.path}`);
|
|
3487
|
+
}
|
|
3488
|
+
await uploadFile(upload.uploadUrl, readFileSync7(file.absPath), file.mimeType);
|
|
3489
|
+
});
|
|
3490
|
+
} catch (error) {
|
|
3491
|
+
brewing.stop();
|
|
3492
|
+
throw error;
|
|
3493
|
+
} finally {
|
|
3494
|
+
presence.stop();
|
|
3029
3495
|
}
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
destinationLabel: workspace.isPersonal ? "Personal" : workspace.name,
|
|
3036
|
-
destinationSource,
|
|
3037
|
-
publishWorkspaceId: workspace.id,
|
|
3038
|
-
workspace: workspace.handle,
|
|
3039
|
-
workspaceId: workspace.id
|
|
3040
|
-
};
|
|
3496
|
+
let result;
|
|
3497
|
+
try {
|
|
3498
|
+
result = await client.finalize(session.publishSessionId);
|
|
3499
|
+
} finally {
|
|
3500
|
+
brewing.stop();
|
|
3041
3501
|
}
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
(candidate) => candidate.name === folderName || candidate.id === folderName
|
|
3045
|
-
);
|
|
3046
|
-
if (matches.length === 0) {
|
|
3047
|
-
throw new Error(
|
|
3048
|
-
`No folder named "${folderName}" exists in workspace ${workspace.handle}.`
|
|
3049
|
-
);
|
|
3502
|
+
if (usingAgentToken && result.workspaceId) {
|
|
3503
|
+
placement.workspaceId = result.workspaceId;
|
|
3050
3504
|
}
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
);
|
|
3505
|
+
const rememberedProjectFile = placement.rememberProjectDefault ? writeProjectConfig(dir, placement.rememberProjectDefault) : null;
|
|
3506
|
+
savePublishedConfig({ apiUrl: apiUrl2, config, dir, opts, placement, result, usingAgentToken });
|
|
3507
|
+
if (opts.password !== void 0) {
|
|
3508
|
+
const protectClient = createApiClient(apiUrl2, credential ?? result.publishToken);
|
|
3509
|
+
await protectClient.setProtection(result.artifactId, opts.password);
|
|
3055
3510
|
}
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
destinationLabel: workspace.isPersonal ? "Personal" : workspace.name,
|
|
3060
|
-
destinationSource,
|
|
3061
|
-
folder: folder.name,
|
|
3062
|
-
folderId: folder.id,
|
|
3063
|
-
publishWorkspaceId: workspace.id,
|
|
3064
|
-
workspace: workspace.handle,
|
|
3065
|
-
workspaceId: workspace.id
|
|
3066
|
-
};
|
|
3067
|
-
}
|
|
3068
|
-
function shouldPromptForWorkspace(opts) {
|
|
3069
|
-
if (opts.json || process.env.ARTIFACT_CAFE_AGENT) {
|
|
3070
|
-
return false;
|
|
3511
|
+
if (reviewMode !== void 0) {
|
|
3512
|
+
const reviewClient = createApiClient(apiUrl2, credential ?? result.publishToken);
|
|
3513
|
+
await reviewClient.setReviewMode(result.artifactId, reviewMode);
|
|
3071
3514
|
}
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
console.log("Where should this artifact live?\n");
|
|
3082
|
-
choices.forEach((workspace, index) => {
|
|
3083
|
-
const label = workspace.isPersonal ? "Personal" : workspace.name;
|
|
3084
|
-
const handle = workspace.isPersonal ? "" : ` \xB7 ${workspace.handle}`;
|
|
3085
|
-
console.log(` ${String(index + 1).padStart(2)}. ${label}${handle}`);
|
|
3515
|
+
printPublishResult({
|
|
3516
|
+
dataInfo,
|
|
3517
|
+
opts,
|
|
3518
|
+
placement,
|
|
3519
|
+
rememberedProjectFile,
|
|
3520
|
+
result,
|
|
3521
|
+
reviewMode,
|
|
3522
|
+
session,
|
|
3523
|
+
skipped
|
|
3086
3524
|
});
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
rl.question(`
|
|
3090
|
-
Publish where? [1-${choices.length}]: `, (answer) => {
|
|
3091
|
-
const index = Number(answer.trim()) - 1;
|
|
3092
|
-
const workspace = choices[index];
|
|
3093
|
-
if (!(Number.isInteger(index) && workspace)) {
|
|
3094
|
-
rl.close();
|
|
3095
|
-
reject(new Error(`Enter a number between 1 and ${choices.length}.`));
|
|
3096
|
-
return;
|
|
3097
|
-
}
|
|
3098
|
-
if (workspace.isPersonal) {
|
|
3099
|
-
rl.close();
|
|
3100
|
-
resolve2({ remember: false, workspace });
|
|
3101
|
-
return;
|
|
3102
|
-
}
|
|
3103
|
-
rl.question(
|
|
3104
|
-
`Remember ${workspace.name} for new artifacts in this project? [y/N]: `,
|
|
3105
|
-
(rememberAnswer) => {
|
|
3106
|
-
rl.close();
|
|
3107
|
-
resolve2({
|
|
3108
|
-
remember: /^y(?:es)?$/i.test(rememberAnswer.trim()),
|
|
3109
|
-
workspace
|
|
3110
|
-
});
|
|
3111
|
-
}
|
|
3112
|
-
);
|
|
3113
|
-
});
|
|
3114
|
-
});
|
|
3115
|
-
}
|
|
3116
|
-
function startPublishingPresence(client, artifactId) {
|
|
3117
|
-
const beat = () => {
|
|
3118
|
-
client.agentHeartbeat(artifactId, "publishing").catch(() => {
|
|
3119
|
-
});
|
|
3120
|
-
};
|
|
3121
|
-
beat();
|
|
3122
|
-
const timer = setInterval(beat, AGENT_PRESENCE.heartbeatSeconds * 1e3);
|
|
3123
|
-
timer.unref();
|
|
3124
|
-
return { stop: () => clearInterval(timer) };
|
|
3125
|
-
}
|
|
3126
|
-
async function inBatches(items, concurrency, fn) {
|
|
3127
|
-
let next = 0;
|
|
3128
|
-
const workers = Array.from(
|
|
3129
|
-
{ length: Math.min(concurrency, items.length) },
|
|
3130
|
-
async () => {
|
|
3131
|
-
while (next < items.length) {
|
|
3132
|
-
const item = items[next];
|
|
3133
|
-
next += 1;
|
|
3134
|
-
if (item === void 0) {
|
|
3135
|
-
continue;
|
|
3136
|
-
}
|
|
3137
|
-
await fn(item);
|
|
3138
|
-
}
|
|
3139
|
-
}
|
|
3140
|
-
);
|
|
3141
|
-
await Promise.all(workers);
|
|
3142
|
-
}
|
|
3143
|
-
function slugFromUrl(url) {
|
|
3144
|
-
return url.split("/a/")[1] ?? "";
|
|
3145
|
-
}
|
|
3146
|
-
function preview(items) {
|
|
3147
|
-
return items.slice(0, 5).join(", ") + (items.length > 5 ? ", \u2026" : "");
|
|
3148
|
-
}
|
|
3149
|
-
function openInBrowser(url) {
|
|
3150
|
-
let cmd = "xdg-open";
|
|
3151
|
-
if (process.platform === "darwin") {
|
|
3152
|
-
cmd = "open";
|
|
3153
|
-
} else if (process.platform === "win32") {
|
|
3154
|
-
cmd = "start";
|
|
3525
|
+
if (opts.open && !opts.json && process.stdout.isTTY) {
|
|
3526
|
+
openInBrowser(result.url);
|
|
3155
3527
|
}
|
|
3156
|
-
|
|
3157
|
-
});
|
|
3158
|
-
}
|
|
3528
|
+
};
|
|
3159
3529
|
|
|
3160
3530
|
// src/dashboard.ts
|
|
3161
|
-
|
|
3162
|
-
const base = process.env.ARTIFACT_CAFE_URL ?? readConfig(
|
|
3163
|
-
const url = `${base.replace(
|
|
3531
|
+
var openDashboard = (opts) => {
|
|
3532
|
+
const base = process.env.ARTIFACT_CAFE_URL ?? readConfig(path10.resolve(opts.dir))?.apiUrl ?? DEFAULT_API_URL;
|
|
3533
|
+
const url = `${base.replace(/\/$/u, "")}/dashboard`;
|
|
3164
3534
|
console.log(url);
|
|
3165
3535
|
if (process.stdout.isTTY) {
|
|
3166
3536
|
openInBrowser(url);
|
|
3167
3537
|
}
|
|
3168
|
-
}
|
|
3538
|
+
};
|
|
3169
3539
|
|
|
3170
3540
|
// src/errors.ts
|
|
3171
|
-
|
|
3541
|
+
var printCliError = (err, json) => {
|
|
3172
3542
|
if (err instanceof ApiRequestError) {
|
|
3173
3543
|
if (json) {
|
|
3174
3544
|
console.log(
|
|
3175
3545
|
JSON.stringify({
|
|
3176
3546
|
error: {
|
|
3177
3547
|
code: err.code,
|
|
3548
|
+
currentVersion: err.currentVersion,
|
|
3549
|
+
docsUrl: err.docsUrl,
|
|
3178
3550
|
message: err.message,
|
|
3179
3551
|
retryAfter: err.retryAfter
|
|
3180
3552
|
}
|
|
@@ -3199,20 +3571,20 @@ function printCliError(err, json) {
|
|
|
3199
3571
|
} else {
|
|
3200
3572
|
console.error(`\u2717 ${message}`);
|
|
3201
3573
|
}
|
|
3202
|
-
}
|
|
3574
|
+
};
|
|
3203
3575
|
|
|
3204
3576
|
// src/expiration.ts
|
|
3205
|
-
import
|
|
3206
|
-
async
|
|
3577
|
+
import path11 from "path";
|
|
3578
|
+
var expiration = async (opts) => {
|
|
3207
3579
|
if (opts.duration !== void 0 === opts.keep) {
|
|
3208
3580
|
throw new Error("Provide exactly one of --in <duration> or --keep.");
|
|
3209
3581
|
}
|
|
3210
|
-
const seconds = opts.
|
|
3211
|
-
const target = resolveArtifactTarget(
|
|
3212
|
-
const result = await createApiClient(
|
|
3213
|
-
target.
|
|
3214
|
-
|
|
3215
|
-
)
|
|
3582
|
+
const seconds = opts.duration === void 0 ? null : parseArtifactDuration(opts.duration);
|
|
3583
|
+
const target = resolveArtifactTarget(path11.resolve(opts.dir), opts);
|
|
3584
|
+
const result = await createApiClient(target.apiUrl, target.credential).setExpiration(
|
|
3585
|
+
target.artifactId,
|
|
3586
|
+
seconds
|
|
3587
|
+
);
|
|
3216
3588
|
if (opts.json) {
|
|
3217
3589
|
console.log(JSON.stringify(result, null, 2));
|
|
3218
3590
|
return;
|
|
@@ -3228,10 +3600,41 @@ async function expiration(opts) {
|
|
|
3228
3600
|
"Artifact kept \u2014 no automatic expiry. Its link, versions, and comments are preserved."
|
|
3229
3601
|
);
|
|
3230
3602
|
}
|
|
3231
|
-
}
|
|
3603
|
+
};
|
|
3232
3604
|
|
|
3233
3605
|
// src/folders.ts
|
|
3234
|
-
async
|
|
3606
|
+
var createFolder = async (client, workspace, opts) => {
|
|
3607
|
+
const name = opts.name?.trim();
|
|
3608
|
+
if (!name) {
|
|
3609
|
+
throw new Error("--name is required when creating a folder.");
|
|
3610
|
+
}
|
|
3611
|
+
const visibility = folderVisibilitySchema.parse(opts.visibility ?? "workspace");
|
|
3612
|
+
const existing = await client.listFolders(workspace.id);
|
|
3613
|
+
const duplicate = existing.folders.find(
|
|
3614
|
+
(candidate) => candidate.name.toLowerCase() === name.toLowerCase()
|
|
3615
|
+
);
|
|
3616
|
+
if (duplicate) {
|
|
3617
|
+
throw new Error(
|
|
3618
|
+
`A folder named "${duplicate.name}" already exists in ${workspace.handle} (${duplicate.id}) \u2014 publish into it with --folder, or pick a different name.`
|
|
3619
|
+
);
|
|
3620
|
+
}
|
|
3621
|
+
const folder = await client.createFolder(workspace.id, { name, visibility });
|
|
3622
|
+
if (opts.json) {
|
|
3623
|
+
console.log(JSON.stringify(folder, null, 2));
|
|
3624
|
+
return;
|
|
3625
|
+
}
|
|
3626
|
+
console.log(`\u2713 Created folder "${folder.name}" (${folder.visibility}) in ${workspace.handle}`);
|
|
3627
|
+
console.log(` ${folder.id}`);
|
|
3628
|
+
if (folder.visibility === "public") {
|
|
3629
|
+
console.log(" Artifacts placed in it default to public \u2014 visible to anyone.");
|
|
3630
|
+
}
|
|
3631
|
+
};
|
|
3632
|
+
var printFolder = (folder) => {
|
|
3633
|
+
console.log(`${folder.name} (${folder.visibility})`);
|
|
3634
|
+
console.log(` ${folder.id}
|
|
3635
|
+
`);
|
|
3636
|
+
};
|
|
3637
|
+
var folders = async (opts) => {
|
|
3235
3638
|
if (opts.action !== void 0 && opts.action !== "create") {
|
|
3236
3639
|
throw new Error(
|
|
3237
3640
|
`Unknown folders action "${opts.action}" \u2014 run \`folders\` to list, or \`folders create <name>\` to create one.`
|
|
@@ -3245,15 +3648,10 @@ async function folders(opts) {
|
|
|
3245
3648
|
}
|
|
3246
3649
|
const visibility = opts.visibility ?? "workspace";
|
|
3247
3650
|
if (visibility !== "workspace" && visibility !== "public") {
|
|
3248
|
-
throw new Error(
|
|
3249
|
-
`--visibility must be "workspace" or "public" (got "${opts.visibility}").`
|
|
3250
|
-
);
|
|
3651
|
+
throw new Error(`--visibility must be "workspace" or "public" (got "${opts.visibility}").`);
|
|
3251
3652
|
}
|
|
3252
3653
|
}
|
|
3253
|
-
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
3254
|
-
/\/$/,
|
|
3255
|
-
""
|
|
3256
|
-
);
|
|
3654
|
+
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(/\/$/u, "");
|
|
3257
3655
|
const key = resolveApiKey(apiUrl2, opts.apiKey);
|
|
3258
3656
|
if (!key) {
|
|
3259
3657
|
throw new Error(
|
|
@@ -3266,9 +3664,7 @@ async function folders(opts) {
|
|
|
3266
3664
|
(candidate) => candidate.handle === opts.workspace
|
|
3267
3665
|
);
|
|
3268
3666
|
if (!workspace) {
|
|
3269
|
-
throw new Error(
|
|
3270
|
-
`No workspace with handle "${opts.workspace}" belongs to this account.`
|
|
3271
|
-
);
|
|
3667
|
+
throw new Error(`No workspace with handle "${opts.workspace}" belongs to this account.`);
|
|
3272
3668
|
}
|
|
3273
3669
|
if (opts.action === "create") {
|
|
3274
3670
|
await createFolder(client, workspace, opts);
|
|
@@ -3284,68 +3680,107 @@ async function folders(opts) {
|
|
|
3284
3680
|
return;
|
|
3285
3681
|
}
|
|
3286
3682
|
const plural = data2.folders.length === 1 ? "" : "s";
|
|
3287
|
-
console.log(
|
|
3288
|
-
|
|
3289
|
-
`
|
|
3290
|
-
);
|
|
3683
|
+
console.log(`${data2.folders.length} folder${plural} in ${workspace.handle}
|
|
3684
|
+
`);
|
|
3291
3685
|
for (const folder of data2.folders) {
|
|
3292
3686
|
printFolder(folder);
|
|
3293
3687
|
}
|
|
3294
|
-
}
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3688
|
+
};
|
|
3689
|
+
|
|
3690
|
+
// src/link.ts
|
|
3691
|
+
import path12 from "path";
|
|
3692
|
+
import { createInterface as createInterface2 } from "readline";
|
|
3693
|
+
|
|
3694
|
+
// src/list.ts
|
|
3695
|
+
var printWorkspaceArtifact = (a) => {
|
|
3696
|
+
const version = a.currentVersionNumber === null ? "no version" : `v${a.currentVersionNumber}`;
|
|
3697
|
+
const open2 = a.openCommentCount === 0 ? "no open comments" : `${a.openCommentCount} open comment${a.openCommentCount === 1 ? "" : "s"}`;
|
|
3698
|
+
const marker = a.openCommentCount > 0 ? "\u25CF" : "\u25CB";
|
|
3699
|
+
console.log(` ${marker} ${a.title} \u2014 ${version} \xB7 ${open2} \xB7 ${a.visibility}`);
|
|
3700
|
+
console.log(` ${a.url}`);
|
|
3701
|
+
};
|
|
3702
|
+
var listWorkspace = async (client, opts) => {
|
|
3703
|
+
const workspaceData = await client.listWorkspaces();
|
|
3704
|
+
const workspace = workspaceData.workspaces.find(
|
|
3705
|
+
(candidate) => candidate.handle === opts.workspace
|
|
3301
3706
|
);
|
|
3302
|
-
if (
|
|
3303
|
-
throw new Error(
|
|
3304
|
-
`A folder named "${duplicate.name}" already exists in ${workspace.handle} (${duplicate.id}) \u2014 publish into it with --folder, or pick a different name.`
|
|
3305
|
-
);
|
|
3707
|
+
if (!workspace) {
|
|
3708
|
+
throw new Error(`No workspace with handle "${opts.workspace}" belongs to this account.`);
|
|
3306
3709
|
}
|
|
3307
|
-
const
|
|
3710
|
+
const [artifactData, folderData] = await Promise.all([
|
|
3711
|
+
client.listWorkspaceArtifacts(workspace.id),
|
|
3712
|
+
client.listFolders(workspace.id)
|
|
3713
|
+
]);
|
|
3308
3714
|
if (opts.json) {
|
|
3309
|
-
console.log(JSON.stringify(folder, null, 2));
|
|
3310
|
-
return;
|
|
3311
|
-
}
|
|
3312
|
-
console.log(
|
|
3313
|
-
`\u2713 Created folder "${folder.name}" (${folder.visibility}) in ${workspace.handle}`
|
|
3314
|
-
);
|
|
3315
|
-
console.log(` ${folder.id}`);
|
|
3316
|
-
if (folder.visibility === "public") {
|
|
3317
3715
|
console.log(
|
|
3318
|
-
|
|
3716
|
+
JSON.stringify({ artifacts: artifactData.artifacts, folders: folderData.folders }, null, 2)
|
|
3319
3717
|
);
|
|
3718
|
+
return;
|
|
3320
3719
|
}
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3720
|
+
if (artifactData.artifacts.length === 0) {
|
|
3721
|
+
console.log(`No artifacts in workspace ${workspace.handle}.`);
|
|
3722
|
+
return;
|
|
3723
|
+
}
|
|
3724
|
+
const plural = artifactData.artifacts.length === 1 ? "" : "s";
|
|
3725
|
+
console.log(`${artifactData.artifacts.length} artifact${plural} in ${workspace.handle}
|
|
3325
3726
|
`);
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3727
|
+
for (const folder of folderData.folders) {
|
|
3728
|
+
const inFolder = artifactData.artifacts.filter((artifact) => artifact.folderId === folder.id);
|
|
3729
|
+
if (inFolder.length === 0) {
|
|
3730
|
+
continue;
|
|
3731
|
+
}
|
|
3732
|
+
console.log(`${folder.name} (${folder.visibility})`);
|
|
3733
|
+
for (const artifact of inFolder) {
|
|
3734
|
+
printWorkspaceArtifact(artifact);
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
const unfiled = artifactData.artifacts.filter((artifact) => artifact.folderId === null);
|
|
3738
|
+
if (unfiled.length > 0) {
|
|
3739
|
+
console.log("Unfiled");
|
|
3740
|
+
for (const artifact of unfiled) {
|
|
3741
|
+
printWorkspaceArtifact(artifact);
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
};
|
|
3745
|
+
var relativeTime = (iso) => {
|
|
3746
|
+
const seconds = Math.max(0, (Date.now() - new Date(iso).getTime()) / 1e3);
|
|
3747
|
+
if (seconds < 60) {
|
|
3748
|
+
return "just now";
|
|
3749
|
+
}
|
|
3750
|
+
if (seconds < 3600) {
|
|
3751
|
+
return `${Math.floor(seconds / 60)}m ago`;
|
|
3752
|
+
}
|
|
3753
|
+
if (seconds < 86400) {
|
|
3754
|
+
return `${Math.floor(seconds / 3600)}h ago`;
|
|
3755
|
+
}
|
|
3756
|
+
if (seconds < 30 * 86400) {
|
|
3757
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
3758
|
+
}
|
|
3759
|
+
return iso.slice(0, 10);
|
|
3760
|
+
};
|
|
3761
|
+
var printArtifact = (a) => {
|
|
3762
|
+
const version = a.currentVersionNumber === null ? "no version" : `v${a.currentVersionNumber}`;
|
|
3763
|
+
const open2 = a.openCommentCount === 0 ? "no open comments" : `${a.openCommentCount} open comment${a.openCommentCount === 1 ? "" : "s"}`;
|
|
3764
|
+
const marker = a.openCommentCount > 0 ? "\u25CF" : "\u25CB";
|
|
3765
|
+
console.log(`${marker} ${a.title}`);
|
|
3766
|
+
console.log(` ${version} \xB7 ${open2} \xB7 updated ${relativeTime(a.updatedAt)}`);
|
|
3767
|
+
console.log(` ${a.url}
|
|
3768
|
+
`);
|
|
3769
|
+
};
|
|
3770
|
+
var getWorkspaceGroup = async (client, workspace) => {
|
|
3771
|
+
const { artifacts } = await client.listWorkspaceArtifacts(workspace.id);
|
|
3772
|
+
return { artifacts, handle: workspace.handle, id: workspace.id, name: workspace.name };
|
|
3773
|
+
};
|
|
3774
|
+
var teamGroups = async (client) => {
|
|
3334
3775
|
const { workspaces: workspaces3 } = await client.listWorkspaces();
|
|
3335
3776
|
return await Promise.all(
|
|
3336
|
-
workspaces3.
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
id: workspace.id,
|
|
3340
|
-
name: workspace.name
|
|
3341
|
-
}))
|
|
3777
|
+
workspaces3.flatMap(
|
|
3778
|
+
(workspace) => workspace.isPersonal ? [] : [getWorkspaceGroup(client, workspace)]
|
|
3779
|
+
)
|
|
3342
3780
|
);
|
|
3343
|
-
}
|
|
3344
|
-
async
|
|
3345
|
-
const [own, groups] = await Promise.all([
|
|
3346
|
-
client.listArtifacts(),
|
|
3347
|
-
teamGroups(client)
|
|
3348
|
-
]);
|
|
3781
|
+
};
|
|
3782
|
+
var reachableArtifacts = async (client) => {
|
|
3783
|
+
const [own, groups] = await Promise.all([client.listArtifacts(), teamGroups(client)]);
|
|
3349
3784
|
return [
|
|
3350
3785
|
...own.artifacts.map((artifact) => ({
|
|
3351
3786
|
currentVersionNumber: artifact.currentVersionNumber,
|
|
@@ -3366,12 +3801,9 @@ async function reachableArtifacts(client) {
|
|
|
3366
3801
|
}))
|
|
3367
3802
|
)
|
|
3368
3803
|
];
|
|
3369
|
-
}
|
|
3370
|
-
async
|
|
3371
|
-
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
3372
|
-
/\/$/,
|
|
3373
|
-
""
|
|
3374
|
-
);
|
|
3804
|
+
};
|
|
3805
|
+
var list = async (opts) => {
|
|
3806
|
+
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(/\/$/u, "");
|
|
3375
3807
|
const key = resolveApiKey(apiUrl2, opts.apiKey);
|
|
3376
3808
|
if (!key) {
|
|
3377
3809
|
throw new Error(
|
|
@@ -3383,14 +3815,9 @@ async function list(opts) {
|
|
|
3383
3815
|
await listWorkspace(client, opts);
|
|
3384
3816
|
return;
|
|
3385
3817
|
}
|
|
3386
|
-
const [data2, groups] = await Promise.all([
|
|
3387
|
-
client.listArtifacts(),
|
|
3388
|
-
teamGroups(client)
|
|
3389
|
-
]);
|
|
3818
|
+
const [data2, groups] = await Promise.all([client.listArtifacts(), teamGroups(client)]);
|
|
3390
3819
|
if (opts.json) {
|
|
3391
|
-
console.log(
|
|
3392
|
-
JSON.stringify({ artifacts: data2.artifacts, workspaces: groups }, null, 2)
|
|
3393
|
-
);
|
|
3820
|
+
console.log(JSON.stringify({ artifacts: data2.artifacts, workspaces: groups }, null, 2));
|
|
3394
3821
|
return;
|
|
3395
3822
|
}
|
|
3396
3823
|
const total = data2.artifacts.length + groups.reduce((sum, group) => sum + group.artifacts.length, 0);
|
|
@@ -3413,112 +3840,44 @@ async function list(opts) {
|
|
|
3413
3840
|
continue;
|
|
3414
3841
|
}
|
|
3415
3842
|
const plural = group.artifacts.length === 1 ? "" : "s";
|
|
3416
|
-
console.log(
|
|
3417
|
-
|
|
3418
|
-
`
|
|
3419
|
-
);
|
|
3843
|
+
console.log(`${group.artifacts.length} artifact${plural} in ${group.handle}
|
|
3844
|
+
`);
|
|
3420
3845
|
for (const artifact of group.artifacts) {
|
|
3421
3846
|
printWorkspaceArtifact(artifact);
|
|
3422
3847
|
}
|
|
3423
3848
|
console.log("");
|
|
3424
3849
|
}
|
|
3425
|
-
}
|
|
3426
|
-
async function listWorkspace(client, opts) {
|
|
3427
|
-
const workspaceData = await client.listWorkspaces();
|
|
3428
|
-
const workspace = workspaceData.workspaces.find(
|
|
3429
|
-
(candidate) => candidate.handle === opts.workspace
|
|
3430
|
-
);
|
|
3431
|
-
if (!workspace) {
|
|
3432
|
-
throw new Error(
|
|
3433
|
-
`No workspace with handle "${opts.workspace}" belongs to this account.`
|
|
3434
|
-
);
|
|
3435
|
-
}
|
|
3436
|
-
const [artifactData, folderData] = await Promise.all([
|
|
3437
|
-
client.listWorkspaceArtifacts(workspace.id),
|
|
3438
|
-
client.listFolders(workspace.id)
|
|
3439
|
-
]);
|
|
3440
|
-
if (opts.json) {
|
|
3441
|
-
console.log(
|
|
3442
|
-
JSON.stringify(
|
|
3443
|
-
{ artifacts: artifactData.artifacts, folders: folderData.folders },
|
|
3444
|
-
null,
|
|
3445
|
-
2
|
|
3446
|
-
)
|
|
3447
|
-
);
|
|
3448
|
-
return;
|
|
3449
|
-
}
|
|
3450
|
-
if (artifactData.artifacts.length === 0) {
|
|
3451
|
-
console.log(`No artifacts in workspace ${workspace.handle}.`);
|
|
3452
|
-
return;
|
|
3453
|
-
}
|
|
3454
|
-
const plural = artifactData.artifacts.length === 1 ? "" : "s";
|
|
3455
|
-
console.log(
|
|
3456
|
-
`${artifactData.artifacts.length} artifact${plural} in ${workspace.handle}
|
|
3457
|
-
`
|
|
3458
|
-
);
|
|
3459
|
-
for (const folder of folderData.folders) {
|
|
3460
|
-
const inFolder = artifactData.artifacts.filter(
|
|
3461
|
-
(artifact) => artifact.folderId === folder.id
|
|
3462
|
-
);
|
|
3463
|
-
if (inFolder.length === 0) {
|
|
3464
|
-
continue;
|
|
3465
|
-
}
|
|
3466
|
-
console.log(`${folder.name} (${folder.visibility})`);
|
|
3467
|
-
for (const artifact of inFolder) {
|
|
3468
|
-
printWorkspaceArtifact(artifact);
|
|
3469
|
-
}
|
|
3470
|
-
}
|
|
3471
|
-
const unfiled = artifactData.artifacts.filter(
|
|
3472
|
-
(artifact) => artifact.folderId === null
|
|
3473
|
-
);
|
|
3474
|
-
if (unfiled.length > 0) {
|
|
3475
|
-
console.log("Unfiled");
|
|
3476
|
-
for (const artifact of unfiled) {
|
|
3477
|
-
printWorkspaceArtifact(artifact);
|
|
3478
|
-
}
|
|
3479
|
-
}
|
|
3480
|
-
}
|
|
3481
|
-
function printWorkspaceArtifact(a) {
|
|
3482
|
-
const version = a.currentVersionNumber === null ? "no version" : `v${a.currentVersionNumber}`;
|
|
3483
|
-
const open2 = a.openCommentCount === 0 ? "no open comments" : `${a.openCommentCount} open comment${a.openCommentCount === 1 ? "" : "s"}`;
|
|
3484
|
-
const marker = a.openCommentCount > 0 ? "\u25CF" : "\u25CB";
|
|
3485
|
-
console.log(
|
|
3486
|
-
` ${marker} ${a.title} \u2014 ${version} \xB7 ${open2} \xB7 ${a.visibility}`
|
|
3487
|
-
);
|
|
3488
|
-
console.log(` ${a.url}`);
|
|
3489
|
-
}
|
|
3490
|
-
function printArtifact(a) {
|
|
3491
|
-
const version = a.currentVersionNumber === null ? "no version" : `v${a.currentVersionNumber}`;
|
|
3492
|
-
const open2 = a.openCommentCount === 0 ? "no open comments" : `${a.openCommentCount} open comment${a.openCommentCount === 1 ? "" : "s"}`;
|
|
3493
|
-
const marker = a.openCommentCount > 0 ? "\u25CF" : "\u25CB";
|
|
3494
|
-
console.log(`${marker} ${a.title}`);
|
|
3495
|
-
console.log(` ${version} \xB7 ${open2} \xB7 updated ${relativeTime(a.updatedAt)}`);
|
|
3496
|
-
console.log(` ${a.url}
|
|
3497
|
-
`);
|
|
3498
|
-
}
|
|
3499
|
-
function relativeTime(iso) {
|
|
3500
|
-
const seconds = Math.max(0, (Date.now() - new Date(iso).getTime()) / 1e3);
|
|
3501
|
-
if (seconds < 60) {
|
|
3502
|
-
return "just now";
|
|
3503
|
-
}
|
|
3504
|
-
if (seconds < 3600) {
|
|
3505
|
-
return `${Math.floor(seconds / 60)}m ago`;
|
|
3506
|
-
}
|
|
3507
|
-
if (seconds < 86400) {
|
|
3508
|
-
return `${Math.floor(seconds / 3600)}h ago`;
|
|
3509
|
-
}
|
|
3510
|
-
if (seconds < 30 * 86400) {
|
|
3511
|
-
return `${Math.floor(seconds / 86400)}d ago`;
|
|
3512
|
-
}
|
|
3513
|
-
return iso.slice(0, 10);
|
|
3514
|
-
}
|
|
3850
|
+
};
|
|
3515
3851
|
|
|
3516
3852
|
// src/link.ts
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
""
|
|
3521
|
-
|
|
3853
|
+
var pickArtifact = (artifacts) => {
|
|
3854
|
+
console.log("Artifacts you can publish to:\n");
|
|
3855
|
+
for (const [i, a] of artifacts.entries()) {
|
|
3856
|
+
const v = a.currentVersionNumber ? `v${a.currentVersionNumber}` : "\u2014";
|
|
3857
|
+
const where = a.workspaceHandle ? ` \xB7 ${a.workspaceHandle}` : "";
|
|
3858
|
+
console.log(` ${String(i + 1).padStart(2)}. ${a.title} \xB7 ${v}${where} \xB7 ${a.url}`);
|
|
3859
|
+
}
|
|
3860
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
3861
|
+
return new Promise((resolve2, reject) => {
|
|
3862
|
+
rl.question(`
|
|
3863
|
+
Link which? [1-${artifacts.length}]: `, (answer) => {
|
|
3864
|
+
rl.close();
|
|
3865
|
+
const n = Number(answer.trim());
|
|
3866
|
+
if (!(Number.isInteger(n) && n >= 1 && n <= artifacts.length)) {
|
|
3867
|
+
reject(new Error(`Enter a number between 1 and ${artifacts.length}.`));
|
|
3868
|
+
return;
|
|
3869
|
+
}
|
|
3870
|
+
const artifact = artifacts[n - 1];
|
|
3871
|
+
if (!artifact) {
|
|
3872
|
+
reject(new Error("No artifact at that selection."));
|
|
3873
|
+
return;
|
|
3874
|
+
}
|
|
3875
|
+
resolve2(artifact);
|
|
3876
|
+
});
|
|
3877
|
+
});
|
|
3878
|
+
};
|
|
3879
|
+
var link = async (opts) => {
|
|
3880
|
+
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(/\/$/u, "");
|
|
3522
3881
|
const key = resolveApiKey(apiUrl2, opts.apiKey);
|
|
3523
3882
|
if (!key) {
|
|
3524
3883
|
throw new Error(
|
|
@@ -3555,10 +3914,11 @@ async function link(opts) {
|
|
|
3555
3914
|
}
|
|
3556
3915
|
chosen = await pickArtifact(artifacts);
|
|
3557
3916
|
}
|
|
3558
|
-
const dir =
|
|
3917
|
+
const dir = path12.resolve(opts.dir);
|
|
3559
3918
|
writeConfig(dir, {
|
|
3560
3919
|
apiUrl: apiUrl2,
|
|
3561
3920
|
artifactId: chosen.id,
|
|
3921
|
+
baseVersion: chosen.currentVersionNumber ?? void 0,
|
|
3562
3922
|
slug: chosen.slug,
|
|
3563
3923
|
url: chosen.url
|
|
3564
3924
|
// No publishToken — this folder authorizes via your login, not a secret.
|
|
@@ -3573,68 +3933,43 @@ async function link(opts) {
|
|
|
3573
3933
|
url: chosen.url
|
|
3574
3934
|
},
|
|
3575
3935
|
null,
|
|
3576
|
-
2
|
|
3577
|
-
)
|
|
3578
|
-
);
|
|
3579
|
-
return;
|
|
3580
|
-
}
|
|
3581
|
-
console.log(`
|
|
3582
|
-
\u2713 Linked ${dir} \u2192 "${chosen.title}" \u2014 ${chosen.url}`);
|
|
3583
|
-
console.log(
|
|
3584
|
-
|
|
3585
|
-
);
|
|
3586
|
-
}
|
|
3587
|
-
function pickArtifact(artifacts) {
|
|
3588
|
-
console.log("Artifacts you can publish to:\n");
|
|
3589
|
-
artifacts.forEach((a, i) => {
|
|
3590
|
-
const v = a.currentVersionNumber ? `v${a.currentVersionNumber}` : "\u2014";
|
|
3591
|
-
const where = a.workspaceHandle ? ` \xB7 ${a.workspaceHandle}` : "";
|
|
3592
|
-
console.log(
|
|
3593
|
-
` ${String(i + 1).padStart(2)}. ${a.title} \xB7 ${v}${where} \xB7 ${a.url}`
|
|
3594
|
-
);
|
|
3595
|
-
});
|
|
3596
|
-
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
3597
|
-
return new Promise((resolve2, reject) => {
|
|
3598
|
-
rl.question(`
|
|
3599
|
-
Link which? [1-${artifacts.length}]: `, (answer) => {
|
|
3600
|
-
rl.close();
|
|
3601
|
-
const n = Number(answer.trim());
|
|
3602
|
-
if (!(Number.isInteger(n) && n >= 1 && n <= artifacts.length)) {
|
|
3603
|
-
reject(new Error(`Enter a number between 1 and ${artifacts.length}.`));
|
|
3604
|
-
return;
|
|
3605
|
-
}
|
|
3606
|
-
resolve2(artifacts[n - 1]);
|
|
3607
|
-
});
|
|
3608
|
-
});
|
|
3609
|
-
}
|
|
3936
|
+
2
|
|
3937
|
+
)
|
|
3938
|
+
);
|
|
3939
|
+
return;
|
|
3940
|
+
}
|
|
3941
|
+
console.log(`
|
|
3942
|
+
\u2713 Linked ${dir} \u2192 "${chosen.title}" \u2014 ${chosen.url}`);
|
|
3943
|
+
console.log(" Owner actions (publish, comments --wait, protect) use your login.\n");
|
|
3944
|
+
};
|
|
3610
3945
|
|
|
3611
3946
|
// src/login.ts
|
|
3612
3947
|
import { createInterface as createInterface3 } from "readline";
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
return new Promise((resolve2) => {
|
|
3948
|
+
var apiUrl = () => (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(/\/$/u, "");
|
|
3949
|
+
var promptSecret = (question) => (
|
|
3950
|
+
// oxlint-disable-next-line promise/avoid-new -- The readline callback owns terminal cleanup and cancellation.
|
|
3951
|
+
new Promise((resolve2) => {
|
|
3618
3952
|
const rl = createInterface3({
|
|
3619
3953
|
input: process.stdin,
|
|
3620
3954
|
output: process.stdout
|
|
3621
3955
|
});
|
|
3622
3956
|
let muted = false;
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3957
|
+
Object.defineProperty(rl, "_writeToOutput", {
|
|
3958
|
+
value: (s) => {
|
|
3959
|
+
if (!muted) {
|
|
3960
|
+
process.stdout.write(s);
|
|
3961
|
+
}
|
|
3627
3962
|
}
|
|
3628
|
-
};
|
|
3963
|
+
});
|
|
3629
3964
|
rl.question(question, (answer) => {
|
|
3630
3965
|
rl.close();
|
|
3631
3966
|
process.stdout.write("\n");
|
|
3632
3967
|
resolve2(answer);
|
|
3633
3968
|
});
|
|
3634
3969
|
muted = true;
|
|
3635
|
-
})
|
|
3636
|
-
|
|
3637
|
-
async
|
|
3970
|
+
})
|
|
3971
|
+
);
|
|
3972
|
+
var login = async (opts) => {
|
|
3638
3973
|
const base = apiUrl();
|
|
3639
3974
|
const keysUrl = `${base}/dashboard?view=api-keys`;
|
|
3640
3975
|
let key = opts.apiKey?.trim();
|
|
@@ -3647,26 +3982,25 @@ Opening ${keysUrl}`);
|
|
|
3647
3982
|
if (process.stdout.isTTY) {
|
|
3648
3983
|
openInBrowser(keysUrl);
|
|
3649
3984
|
}
|
|
3650
|
-
|
|
3985
|
+
const enteredKey = await promptSecret("API key: ");
|
|
3986
|
+
key = enteredKey.trim();
|
|
3651
3987
|
}
|
|
3652
3988
|
if (!key) {
|
|
3653
3989
|
throw new Error("No key entered \u2014 nothing was saved.");
|
|
3654
3990
|
}
|
|
3655
3991
|
if (!key.startsWith("ack_")) {
|
|
3656
|
-
throw new Error(
|
|
3657
|
-
|
|
3658
|
-
|
|
3992
|
+
throw new Error(`That doesn't look like an artifact.cafe API key (they start with "ack_").`);
|
|
3993
|
+
}
|
|
3994
|
+
const previous = resolveApiKey(base);
|
|
3995
|
+
if (previous && previous !== key) {
|
|
3996
|
+
resetInstallId();
|
|
3659
3997
|
}
|
|
3660
3998
|
const me = await createApiClient(base, key).me();
|
|
3661
3999
|
storeApiKey(base, key);
|
|
3662
4000
|
const who = me.email ?? me.userId;
|
|
3663
4001
|
if (opts.json) {
|
|
3664
4002
|
console.log(
|
|
3665
|
-
JSON.stringify(
|
|
3666
|
-
{ apiUrl: base, email: me.email, loggedIn: true, userId: me.userId },
|
|
3667
|
-
null,
|
|
3668
|
-
2
|
|
3669
|
-
)
|
|
4003
|
+
JSON.stringify({ apiUrl: base, email: me.email, loggedIn: true, userId: me.userId }, null, 2)
|
|
3670
4004
|
);
|
|
3671
4005
|
return;
|
|
3672
4006
|
}
|
|
@@ -3675,14 +4009,12 @@ Opening ${keysUrl}`);
|
|
|
3675
4009
|
console.log(
|
|
3676
4010
|
"New publishes are claimed by your account automatically. Try `artifact-cafe list`.\n"
|
|
3677
4011
|
);
|
|
3678
|
-
}
|
|
3679
|
-
async
|
|
4012
|
+
};
|
|
4013
|
+
var whoami = async (opts) => {
|
|
3680
4014
|
const base = apiUrl();
|
|
3681
4015
|
const key = resolveApiKey(base);
|
|
3682
4016
|
if (!key) {
|
|
3683
|
-
throw new Error(
|
|
3684
|
-
`Not logged in to ${apiOrigin(base)} \u2014 run \`artifact-cafe login\` first.`
|
|
3685
|
-
);
|
|
4017
|
+
throw new Error(`Not logged in to ${apiOrigin(base)} \u2014 run \`artifact-cafe login\` first.`);
|
|
3686
4018
|
}
|
|
3687
4019
|
const me = await createApiClient(base, key).me();
|
|
3688
4020
|
if (opts.json) {
|
|
@@ -3701,13 +4033,14 @@ async function whoami(opts) {
|
|
|
3701
4033
|
return;
|
|
3702
4034
|
}
|
|
3703
4035
|
const keyLabel = me.keyName ? ` (key: ${me.keyName})` : "";
|
|
3704
|
-
console.log(
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
}
|
|
3708
|
-
function logout(opts) {
|
|
4036
|
+
console.log(`Logged in to ${apiOrigin(base)} as ${me.email ?? me.userId}${keyLabel}`);
|
|
4037
|
+
};
|
|
4038
|
+
var logout = (opts) => {
|
|
3709
4039
|
const base = apiUrl();
|
|
3710
4040
|
const removed = removeApiKey(base);
|
|
4041
|
+
if (removed) {
|
|
4042
|
+
resetInstallId();
|
|
4043
|
+
}
|
|
3711
4044
|
if (opts.json) {
|
|
3712
4045
|
console.log(JSON.stringify({ apiUrl: base, loggedOut: removed }, null, 2));
|
|
3713
4046
|
return;
|
|
@@ -3720,34 +4053,30 @@ function logout(opts) {
|
|
|
3720
4053
|
console.log(
|
|
3721
4054
|
`The key itself is still active \u2014 revoke it at ${base}/dashboard?view=api-keys if this machine is shared.`
|
|
3722
4055
|
);
|
|
3723
|
-
}
|
|
4056
|
+
};
|
|
3724
4057
|
|
|
3725
4058
|
// src/message.ts
|
|
3726
|
-
import
|
|
3727
|
-
async
|
|
4059
|
+
import path13 from "path";
|
|
4060
|
+
var readStdin2 = async () => {
|
|
3728
4061
|
if (process.stdin.isTTY) {
|
|
3729
4062
|
return "";
|
|
3730
4063
|
}
|
|
3731
4064
|
const chunks = [];
|
|
3732
4065
|
for await (const chunk of process.stdin) {
|
|
3733
|
-
chunks.push(chunk);
|
|
4066
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
3734
4067
|
}
|
|
3735
|
-
return Buffer.concat(chunks).toString("
|
|
3736
|
-
}
|
|
3737
|
-
async
|
|
4068
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
4069
|
+
};
|
|
4070
|
+
var editMessage = async (opts) => {
|
|
3738
4071
|
const messageId = opts.message?.trim();
|
|
3739
4072
|
if (!messageId) {
|
|
3740
|
-
throw new Error(
|
|
3741
|
-
"Pass the message id to edit \u2014 get it from `artifact-cafe comments --json`."
|
|
3742
|
-
);
|
|
4073
|
+
throw new Error("Pass the message id to edit \u2014 get it from `artifact-cafe comments --json`.");
|
|
3743
4074
|
}
|
|
3744
4075
|
const body = (opts.body ?? await readStdin2()).trim();
|
|
3745
4076
|
if (!body) {
|
|
3746
|
-
throw new Error(
|
|
3747
|
-
'Nothing to save. Pass the new text with --body "\u2026", or pipe it on stdin.'
|
|
3748
|
-
);
|
|
4077
|
+
throw new Error('Nothing to save. Pass the new text with --body "\u2026", or pipe it on stdin.');
|
|
3749
4078
|
}
|
|
3750
|
-
const dir =
|
|
4079
|
+
const dir = path13.resolve(opts.dir);
|
|
3751
4080
|
const target = resolveArtifactTarget(dir, {
|
|
3752
4081
|
apiKey: opts.apiKey,
|
|
3753
4082
|
artifact: opts.artifact
|
|
@@ -3759,15 +4088,13 @@ async function editMessage(opts) {
|
|
|
3759
4088
|
return;
|
|
3760
4089
|
}
|
|
3761
4090
|
console.log(`\u2713 Edited ${messageId}.`);
|
|
3762
|
-
}
|
|
3763
|
-
async
|
|
4091
|
+
};
|
|
4092
|
+
var deleteMessage = async (opts) => {
|
|
3764
4093
|
const messageId = opts.message?.trim();
|
|
3765
4094
|
if (!messageId) {
|
|
3766
|
-
throw new Error(
|
|
3767
|
-
"Pass the message id to delete \u2014 get it from `artifact-cafe comments --json`."
|
|
3768
|
-
);
|
|
4095
|
+
throw new Error("Pass the message id to delete \u2014 get it from `artifact-cafe comments --json`.");
|
|
3769
4096
|
}
|
|
3770
|
-
const dir =
|
|
4097
|
+
const dir = path13.resolve(opts.dir);
|
|
3771
4098
|
const target = resolveArtifactTarget(dir, {
|
|
3772
4099
|
apiKey: opts.apiKey,
|
|
3773
4100
|
artifact: opts.artifact
|
|
@@ -3779,15 +4106,15 @@ async function deleteMessage(opts) {
|
|
|
3779
4106
|
return;
|
|
3780
4107
|
}
|
|
3781
4108
|
console.log(`\u2713 Deleted ${messageId}.`);
|
|
3782
|
-
}
|
|
4109
|
+
};
|
|
3783
4110
|
|
|
3784
4111
|
// src/move.ts
|
|
3785
|
-
import
|
|
3786
|
-
async
|
|
4112
|
+
import path14 from "path";
|
|
4113
|
+
var move = async (opts) => {
|
|
3787
4114
|
if (Boolean(opts.folder) === Boolean(opts.unfiled)) {
|
|
3788
4115
|
throw new Error("Pass exactly one of --folder <name-or-id> or --unfiled.");
|
|
3789
4116
|
}
|
|
3790
|
-
const dir =
|
|
4117
|
+
const dir = path14.resolve(opts.dir);
|
|
3791
4118
|
const target = resolveArtifactTarget(dir, {
|
|
3792
4119
|
apiKey: opts.apiKey,
|
|
3793
4120
|
artifact: opts.artifact
|
|
@@ -3822,6 +4149,9 @@ async function move(opts) {
|
|
|
3822
4149
|
`More than one folder is named "${opts.folder}"; pass the folder id instead.`
|
|
3823
4150
|
);
|
|
3824
4151
|
}
|
|
4152
|
+
if (matches[0] === null || matches[0] === void 0) {
|
|
4153
|
+
throw new Error("Expected matches[0] to be present.");
|
|
4154
|
+
}
|
|
3825
4155
|
folderId = matches[0].id;
|
|
3826
4156
|
folderName = matches[0].name;
|
|
3827
4157
|
}
|
|
@@ -3867,54 +4197,51 @@ async function move(opts) {
|
|
|
3867
4197
|
const destination = folderName ? `"${folderName}"` : "Unfiled";
|
|
3868
4198
|
console.log(`\u2713 Moved "${updated.title}" \u2192 ${destination}`);
|
|
3869
4199
|
if (updated.visibility !== artifact.visibility) {
|
|
3870
|
-
console.log(
|
|
3871
|
-
` Visibility: ${artifact.visibility} \u2192 ${updated.visibility} (folder default)`
|
|
3872
|
-
);
|
|
4200
|
+
console.log(` Visibility: ${artifact.visibility} \u2192 ${updated.visibility} (folder default)`);
|
|
3873
4201
|
if (updated.visibility === "public") {
|
|
3874
4202
|
console.log(" Now public \u2014 visible to anyone.");
|
|
3875
4203
|
}
|
|
3876
4204
|
}
|
|
3877
|
-
}
|
|
4205
|
+
};
|
|
3878
4206
|
|
|
3879
4207
|
// src/open.ts
|
|
3880
|
-
import
|
|
3881
|
-
async
|
|
3882
|
-
const dir =
|
|
4208
|
+
import path15 from "path";
|
|
4209
|
+
var openArtifact = async (opts) => {
|
|
4210
|
+
const dir = path15.resolve(opts.dir);
|
|
3883
4211
|
const target = resolveArtifactTarget(dir, {
|
|
3884
4212
|
apiKey: opts.apiKey,
|
|
3885
4213
|
artifact: opts.artifact
|
|
3886
4214
|
});
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
4215
|
+
let { url } = target;
|
|
4216
|
+
if (!url) {
|
|
4217
|
+
const artifact = await createApiClient(target.apiUrl, target.credential).getArtifact(
|
|
3890
4218
|
target.artifactId
|
|
3891
|
-
)
|
|
3892
|
-
|
|
4219
|
+
);
|
|
4220
|
+
url = reviewUrl(target.apiUrl, artifact.slug);
|
|
4221
|
+
}
|
|
3893
4222
|
console.log(url);
|
|
3894
4223
|
if (process.stdout.isTTY) {
|
|
3895
4224
|
openInBrowser(url);
|
|
3896
4225
|
}
|
|
3897
|
-
}
|
|
4226
|
+
};
|
|
3898
4227
|
|
|
3899
4228
|
// src/protect.ts
|
|
3900
|
-
import
|
|
3901
|
-
async
|
|
4229
|
+
import path16 from "path";
|
|
4230
|
+
var protect = async (opts) => {
|
|
3902
4231
|
if (opts.off && opts.password !== void 0) {
|
|
3903
4232
|
throw new Error("Pass either --password or --off, not both.");
|
|
3904
4233
|
}
|
|
3905
4234
|
if (!opts.off && opts.password === void 0) {
|
|
3906
|
-
throw new Error(
|
|
3907
|
-
"Provide --password <password> to set a gate, or --off to remove it."
|
|
3908
|
-
);
|
|
4235
|
+
throw new Error("Provide --password <password> to set a gate, or --off to remove it.");
|
|
3909
4236
|
}
|
|
3910
|
-
const password = opts.
|
|
4237
|
+
const password = opts.password === void 0 ? null : opts.password;
|
|
3911
4238
|
if (password !== null) {
|
|
3912
4239
|
const parsed = gatePasswordSchema.safeParse(password);
|
|
3913
4240
|
if (!parsed.success) {
|
|
3914
4241
|
throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
|
|
3915
4242
|
}
|
|
3916
4243
|
}
|
|
3917
|
-
const dir =
|
|
4244
|
+
const dir = path16.resolve(opts.dir);
|
|
3918
4245
|
const target = resolveArtifactTarget(dir, {
|
|
3919
4246
|
apiKey: opts.apiKey,
|
|
3920
4247
|
artifact: opts.artifact
|
|
@@ -3925,10 +4252,11 @@ async function protect(opts) {
|
|
|
3925
4252
|
console.log(JSON.stringify({ protected: res.protected }, null, 2));
|
|
3926
4253
|
return;
|
|
3927
4254
|
}
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
4255
|
+
let { url } = target;
|
|
4256
|
+
if (!url) {
|
|
4257
|
+
const artifact = await client.getArtifact(target.artifactId);
|
|
4258
|
+
url = reviewUrl(target.apiUrl, artifact.slug);
|
|
4259
|
+
}
|
|
3932
4260
|
console.log(
|
|
3933
4261
|
res.protected ? `
|
|
3934
4262
|
\u2713 Password protection on \u2014 ${url}
|
|
@@ -3936,21 +4264,119 @@ async function protect(opts) {
|
|
|
3936
4264
|
\u2713 Password protection removed \u2014 ${url}
|
|
3937
4265
|
`
|
|
3938
4266
|
);
|
|
3939
|
-
}
|
|
4267
|
+
};
|
|
4268
|
+
|
|
4269
|
+
// src/pull.ts
|
|
4270
|
+
import { createHash as createHash2 } from "crypto";
|
|
4271
|
+
import { existsSync as existsSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
4272
|
+
import path17 from "path";
|
|
4273
|
+
var rejectSymlinks = (dir, relative) => {
|
|
4274
|
+
let current = dir;
|
|
4275
|
+
for (const segment of ["", ...relative.split("/")]) {
|
|
4276
|
+
current = path17.join(current, segment);
|
|
4277
|
+
try {
|
|
4278
|
+
if (lstatSync2(current).isSymbolicLink()) {
|
|
4279
|
+
throw new Error(`Refusing to pull through a symbolic link: ${relative}`);
|
|
4280
|
+
}
|
|
4281
|
+
} catch (error) {
|
|
4282
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
|
|
4283
|
+
throw error;
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
};
|
|
4288
|
+
var warnExtraFiles = (extraFiles, versionNumber) => {
|
|
4289
|
+
if (extraFiles.length > 0) {
|
|
4290
|
+
console.warn(
|
|
4291
|
+
`Warning: Local files not in v${versionNumber} will be published unless removed: ${extraFiles.join(", ")}`
|
|
4292
|
+
);
|
|
4293
|
+
}
|
|
4294
|
+
};
|
|
4295
|
+
var pull = async (opts) => {
|
|
4296
|
+
const dir = path17.resolve(opts.dir);
|
|
4297
|
+
rejectSymlinks(dir, ".artifactcafe/config.json");
|
|
4298
|
+
const config = readConfig(dir);
|
|
4299
|
+
const target = resolveArtifactTarget(dir, opts);
|
|
4300
|
+
if (config && config.artifactId !== target.artifactId) {
|
|
4301
|
+
throw new Error("This directory is linked to another artifact. Pull into another directory.");
|
|
4302
|
+
}
|
|
4303
|
+
if (existsSync4(dir) && readdirSync2(dir).length > 0 && !opts.force) {
|
|
4304
|
+
throw new Error("Pull requires an empty directory. Use --force to overwrite matching files.");
|
|
4305
|
+
}
|
|
4306
|
+
let version = "current";
|
|
4307
|
+
if (opts.version !== void 0 && opts.version !== "current") {
|
|
4308
|
+
if (!/^[1-9]\d*$/u.test(opts.version) || !Number.isSafeInteger(Number(opts.version))) {
|
|
4309
|
+
throw new Error("--version must be a positive integer or current.");
|
|
4310
|
+
}
|
|
4311
|
+
version = Number(opts.version);
|
|
4312
|
+
}
|
|
4313
|
+
const result = await createApiClient(target.apiUrl, target.credential).versionFiles(
|
|
4314
|
+
target.artifactId,
|
|
4315
|
+
version
|
|
4316
|
+
);
|
|
4317
|
+
for (const file of result.files) {
|
|
4318
|
+
if (!isSafeArtifactPath(file.path) || path17.win32.isAbsolute(file.path) || file.path.split("/")[0] === ".artifactcafe") {
|
|
4319
|
+
throw new Error(`Unsafe download path: ${file.path}`);
|
|
4320
|
+
}
|
|
4321
|
+
rejectSymlinks(dir, file.path);
|
|
4322
|
+
}
|
|
4323
|
+
for (const file of result.files) {
|
|
4324
|
+
const response = await fetch(file.downloadUrl);
|
|
4325
|
+
if (!response.ok) {
|
|
4326
|
+
throw new Error(`Download failed for ${file.path} (HTTP ${response.status}).`);
|
|
4327
|
+
}
|
|
4328
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
4329
|
+
const hash = `sha256_${createHash2("sha256").update(bytes).digest("hex")}`;
|
|
4330
|
+
if (hash !== file.hash) {
|
|
4331
|
+
throw new Error(`Hash mismatch for ${file.path}.`);
|
|
4332
|
+
}
|
|
4333
|
+
if (bytes.length !== file.size) {
|
|
4334
|
+
throw new Error(`Size mismatch for ${file.path}.`);
|
|
4335
|
+
}
|
|
4336
|
+
const destination = path17.join(dir, file.path);
|
|
4337
|
+
mkdirSync5(path17.dirname(destination), { recursive: true });
|
|
4338
|
+
writeFileSync5(destination, bytes);
|
|
4339
|
+
}
|
|
4340
|
+
writeConfig(dir, {
|
|
4341
|
+
...config ?? { apiUrl: target.apiUrl, artifactId: target.artifactId },
|
|
4342
|
+
baseVersion: result.versionNumber,
|
|
4343
|
+
slug: result.slug,
|
|
4344
|
+
url: reviewUrl(target.apiUrl, result.slug)
|
|
4345
|
+
});
|
|
4346
|
+
const files = result.files.map(({ downloadUrl: _downloadUrl, ...file }) => file);
|
|
4347
|
+
const pulledPaths = new Set(files.map((file) => file.path));
|
|
4348
|
+
const extraFiles = scanDirectory(dir).files.flatMap(
|
|
4349
|
+
(file) => pulledPaths.has(file.path) ? [] : [file.path]
|
|
4350
|
+
);
|
|
4351
|
+
if (opts.json) {
|
|
4352
|
+
console.log(
|
|
4353
|
+
JSON.stringify({
|
|
4354
|
+
artifactId: target.artifactId,
|
|
4355
|
+
dir,
|
|
4356
|
+
extraFiles,
|
|
4357
|
+
files,
|
|
4358
|
+
versionNumber: result.versionNumber
|
|
4359
|
+
})
|
|
4360
|
+
);
|
|
4361
|
+
return;
|
|
4362
|
+
}
|
|
4363
|
+
console.log(`\u2713 Pulled v${result.versionNumber} to ${dir} (${files.length} files).`);
|
|
4364
|
+
warnExtraFiles(extraFiles, result.versionNumber);
|
|
4365
|
+
};
|
|
3940
4366
|
|
|
3941
4367
|
// src/reply.ts
|
|
3942
|
-
import
|
|
3943
|
-
async
|
|
4368
|
+
import path18 from "path";
|
|
4369
|
+
var readStdin3 = async () => {
|
|
3944
4370
|
if (process.stdin.isTTY) {
|
|
3945
4371
|
return "";
|
|
3946
4372
|
}
|
|
3947
4373
|
const chunks = [];
|
|
3948
4374
|
for await (const chunk of process.stdin) {
|
|
3949
|
-
chunks.push(chunk);
|
|
4375
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
3950
4376
|
}
|
|
3951
|
-
return Buffer.concat(chunks).toString("
|
|
3952
|
-
}
|
|
3953
|
-
async
|
|
4377
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
4378
|
+
};
|
|
4379
|
+
var replyToThread = async (opts) => {
|
|
3954
4380
|
const threadId = opts.thread?.trim();
|
|
3955
4381
|
if (!threadId) {
|
|
3956
4382
|
throw new Error(
|
|
@@ -3959,11 +4385,9 @@ async function replyToThread(opts) {
|
|
|
3959
4385
|
}
|
|
3960
4386
|
const body = (opts.body ?? await readStdin3()).trim();
|
|
3961
4387
|
if (!body) {
|
|
3962
|
-
throw new Error(
|
|
3963
|
-
'Nothing to post. Pass the reply with --body "\u2026", or pipe it on stdin.'
|
|
3964
|
-
);
|
|
4388
|
+
throw new Error('Nothing to post. Pass the reply with --body "\u2026", or pipe it on stdin.');
|
|
3965
4389
|
}
|
|
3966
|
-
const dir =
|
|
4390
|
+
const dir = path18.resolve(opts.dir);
|
|
3967
4391
|
const target = resolveArtifactTarget(dir, {
|
|
3968
4392
|
apiKey: opts.apiKey,
|
|
3969
4393
|
artifact: opts.artifact
|
|
@@ -3989,18 +4413,18 @@ async function replyToThread(opts) {
|
|
|
3989
4413
|
}
|
|
3990
4414
|
const closed = opts.resolve ? " and resolved it" : "";
|
|
3991
4415
|
console.log(`\u2713 Replied to ${thread.id}${closed} \u2014 now ${thread.status}.`);
|
|
3992
|
-
}
|
|
4416
|
+
};
|
|
3993
4417
|
|
|
3994
4418
|
// src/resolve.ts
|
|
3995
|
-
import
|
|
3996
|
-
async
|
|
4419
|
+
import path19 from "path";
|
|
4420
|
+
var changeThreadStatus = async (opts) => {
|
|
3997
4421
|
const threadId = opts.thread?.trim();
|
|
3998
4422
|
if (!threadId) {
|
|
3999
4423
|
throw new Error(
|
|
4000
4424
|
`Pass the thread id to ${opts.action} \u2014 get it from \`artifact-cafe comments --json\`.`
|
|
4001
4425
|
);
|
|
4002
4426
|
}
|
|
4003
|
-
const dir =
|
|
4427
|
+
const dir = path19.resolve(opts.dir);
|
|
4004
4428
|
const target = resolveArtifactTarget(dir, {
|
|
4005
4429
|
apiKey: opts.apiKey,
|
|
4006
4430
|
artifact: opts.artifact
|
|
@@ -4027,23 +4451,21 @@ async function changeThreadStatus(opts) {
|
|
|
4027
4451
|
return;
|
|
4028
4452
|
}
|
|
4029
4453
|
if (opts.action === "ack") {
|
|
4030
|
-
console.log(
|
|
4031
|
-
`\u2713 Acknowledged ${thread.id} \u2014 reviewers now see it as being handled.`
|
|
4032
|
-
);
|
|
4454
|
+
console.log(`\u2713 Acknowledged ${thread.id} \u2014 reviewers now see it as being handled.`);
|
|
4033
4455
|
return;
|
|
4034
4456
|
}
|
|
4035
4457
|
const verb = opts.action === "resolve" ? "Resolved" : "Reopened";
|
|
4036
4458
|
console.log(`\u2713 ${verb} ${thread.id} \u2014 now ${thread.status}.`);
|
|
4037
|
-
}
|
|
4459
|
+
};
|
|
4038
4460
|
|
|
4039
4461
|
// src/review.ts
|
|
4040
|
-
import
|
|
4462
|
+
import path20 from "path";
|
|
4041
4463
|
var OUTCOME = {
|
|
4042
4464
|
off: "Plain artifact \u2014 the review tools are hidden entirely",
|
|
4043
4465
|
open: "Open for review \u2014 anyone who can open it can comment",
|
|
4044
4466
|
readonly: "Read-only \u2014 earlier comments stay visible, no new ones"
|
|
4045
4467
|
};
|
|
4046
|
-
async
|
|
4468
|
+
var review = async (opts) => {
|
|
4047
4469
|
const parsed = reviewModeSchema.safeParse(opts.mode);
|
|
4048
4470
|
if (!parsed.success) {
|
|
4049
4471
|
throw new Error(
|
|
@@ -4051,7 +4473,7 @@ async function review(opts) {
|
|
|
4051
4473
|
);
|
|
4052
4474
|
}
|
|
4053
4475
|
const mode = parsed.data;
|
|
4054
|
-
const dir =
|
|
4476
|
+
const dir = path20.resolve(opts.dir);
|
|
4055
4477
|
const target = resolveArtifactTarget(dir, {
|
|
4056
4478
|
apiKey: opts.apiKey,
|
|
4057
4479
|
artifact: opts.artifact
|
|
@@ -4062,21 +4484,22 @@ async function review(opts) {
|
|
|
4062
4484
|
console.log(JSON.stringify({ reviewMode: res.mode }, null, 2));
|
|
4063
4485
|
return;
|
|
4064
4486
|
}
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4487
|
+
let { url } = target;
|
|
4488
|
+
if (!url) {
|
|
4489
|
+
const artifact = await client.getArtifact(target.artifactId);
|
|
4490
|
+
url = reviewUrl(target.apiUrl, artifact.slug);
|
|
4491
|
+
}
|
|
4069
4492
|
console.log(`
|
|
4070
4493
|
\u2713 ${OUTCOME[res.mode]}
|
|
4071
4494
|
${url}
|
|
4072
4495
|
`);
|
|
4073
|
-
}
|
|
4496
|
+
};
|
|
4074
4497
|
|
|
4075
4498
|
// src/share.ts
|
|
4076
|
-
import
|
|
4077
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]
|
|
4078
|
-
async
|
|
4079
|
-
const dir =
|
|
4499
|
+
import path21 from "path";
|
|
4500
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
|
|
4501
|
+
var share = async (opts) => {
|
|
4502
|
+
const dir = path21.resolve(opts.dir);
|
|
4080
4503
|
const target = resolveArtifactTarget(dir, {
|
|
4081
4504
|
apiKey: opts.apiKey,
|
|
4082
4505
|
artifact: opts.artifact
|
|
@@ -4105,9 +4528,7 @@ General access: ${visibility}`);
|
|
|
4105
4528
|
return;
|
|
4106
4529
|
}
|
|
4107
4530
|
if (opts.emails.length === 0) {
|
|
4108
|
-
throw new Error(
|
|
4109
|
-
"Provide at least one email to invite, or --list to see current grants."
|
|
4110
|
-
);
|
|
4531
|
+
throw new Error("Provide at least one email to invite, or --list to see current grants.");
|
|
4111
4532
|
}
|
|
4112
4533
|
for (const email of opts.emails) {
|
|
4113
4534
|
if (!EMAIL_RE.test(email)) {
|
|
@@ -4129,9 +4550,9 @@ General access: ${visibility}`);
|
|
|
4129
4550
|
console.log(` ${url}`);
|
|
4130
4551
|
}
|
|
4131
4552
|
console.log("");
|
|
4132
|
-
}
|
|
4133
|
-
async
|
|
4134
|
-
const dir =
|
|
4553
|
+
};
|
|
4554
|
+
var unshare = async (opts) => {
|
|
4555
|
+
const dir = path21.resolve(opts.dir);
|
|
4135
4556
|
const target = resolveArtifactTarget(dir, {
|
|
4136
4557
|
apiKey: opts.apiKey,
|
|
4137
4558
|
artifact: opts.artifact
|
|
@@ -4151,18 +4572,16 @@ async function unshare(opts) {
|
|
|
4151
4572
|
console.log(`
|
|
4152
4573
|
\u2713 Revoked access for ${opts.email}
|
|
4153
4574
|
`);
|
|
4154
|
-
}
|
|
4575
|
+
};
|
|
4155
4576
|
|
|
4156
4577
|
// src/status.ts
|
|
4157
|
-
import
|
|
4158
|
-
async
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
);
|
|
4578
|
+
import path22 from "path";
|
|
4579
|
+
var status = async (opts) => {
|
|
4580
|
+
const state = AGENT_STATES.find((candidate) => candidate === opts.state);
|
|
4581
|
+
if (!state) {
|
|
4582
|
+
throw new Error(`State must be one of: ${AGENT_STATES.join(", ")} (got "${opts.state}").`);
|
|
4163
4583
|
}
|
|
4164
|
-
const
|
|
4165
|
-
const dir = path19.resolve(opts.dir);
|
|
4584
|
+
const dir = path22.resolve(opts.dir);
|
|
4166
4585
|
const target = resolveArtifactTarget(dir, {
|
|
4167
4586
|
apiKey: opts.apiKey,
|
|
4168
4587
|
artifact: opts.artifact
|
|
@@ -4185,163 +4604,16 @@ async function status(opts) {
|
|
|
4185
4604
|
}
|
|
4186
4605
|
const visible = res.ttlSeconds >= 120 ? `~${Math.round(res.ttlSeconds / 60)}m` : `~${res.ttlSeconds}s`;
|
|
4187
4606
|
console.log(`\u2713 Presence: ${state} (visible ${visible})`);
|
|
4188
|
-
}
|
|
4189
|
-
|
|
4190
|
-
// src/update.ts
|
|
4191
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
4192
|
-
import { createRequire } from "module";
|
|
4193
|
-
import { homedir as homedir2 } from "os";
|
|
4194
|
-
import path20 from "path";
|
|
4195
|
-
import { z as z25 } from "zod";
|
|
4196
|
-
var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
|
|
4197
|
-
var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
|
|
4198
|
-
var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
|
|
4199
|
-
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4200
|
-
var FETCH_TIMEOUT_MS = 2e3;
|
|
4201
|
-
var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
|
|
4202
|
-
var cacheSchema = z25.object({
|
|
4203
|
-
checkedAt: z25.number(),
|
|
4204
|
-
cliLatest: z25.string().nullable(),
|
|
4205
|
-
skillLatest: z25.string().nullable(),
|
|
4206
|
-
version: z25.literal(1)
|
|
4207
|
-
});
|
|
4208
|
-
function cliVersion() {
|
|
4209
|
-
const pkg = createRequire(import.meta.url)("../package.json");
|
|
4210
|
-
return pkg.version;
|
|
4211
|
-
}
|
|
4212
|
-
function cachePath() {
|
|
4213
|
-
return path20.join(homedir2(), ".artifactcafe", "update-check.json");
|
|
4214
|
-
}
|
|
4215
|
-
function readCache() {
|
|
4216
|
-
try {
|
|
4217
|
-
return cacheSchema.parse(JSON.parse(readFileSync6(cachePath(), "utf8")));
|
|
4218
|
-
} catch {
|
|
4219
|
-
return null;
|
|
4220
|
-
}
|
|
4221
|
-
}
|
|
4222
|
-
function writeCache(cache) {
|
|
4223
|
-
try {
|
|
4224
|
-
mkdirSync3(path20.dirname(cachePath()), { mode: 448, recursive: true });
|
|
4225
|
-
writeFileSync3(cachePath(), `${JSON.stringify(cache, null, 2)}
|
|
4226
|
-
`);
|
|
4227
|
-
} catch {
|
|
4228
|
-
}
|
|
4229
|
-
}
|
|
4230
|
-
function isNewerVersion(candidate, current) {
|
|
4231
|
-
const parse = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
4232
|
-
const a = parse(candidate);
|
|
4233
|
-
const b = parse(current);
|
|
4234
|
-
for (let i = 0; i < 3; i++) {
|
|
4235
|
-
if ((a[i] ?? 0) !== (b[i] ?? 0)) {
|
|
4236
|
-
return (a[i] ?? 0) > (b[i] ?? 0);
|
|
4237
|
-
}
|
|
4238
|
-
}
|
|
4239
|
-
return false;
|
|
4240
|
-
}
|
|
4241
|
-
async function fetchJson(url) {
|
|
4242
|
-
const res = await fetch(url, {
|
|
4243
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
4244
|
-
});
|
|
4245
|
-
if (!res.ok) {
|
|
4246
|
-
throw new Error(`GET ${url} \u2192 ${res.status}`);
|
|
4247
|
-
}
|
|
4248
|
-
return res.json();
|
|
4249
|
-
}
|
|
4250
|
-
async function fetchText(url) {
|
|
4251
|
-
const res = await fetch(url, {
|
|
4252
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
4253
|
-
});
|
|
4254
|
-
if (!res.ok) {
|
|
4255
|
-
throw new Error(`GET ${url} \u2192 ${res.status}`);
|
|
4256
|
-
}
|
|
4257
|
-
return res.text();
|
|
4258
|
-
}
|
|
4259
|
-
async function fetchLatestVersions(previous) {
|
|
4260
|
-
const [cli, skill] = await Promise.all([
|
|
4261
|
-
fetchJson(REGISTRY_LATEST_URL).then((body) => z25.object({ version: z25.string() }).parse(body).version).catch(() => null),
|
|
4262
|
-
fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
|
|
4263
|
-
]);
|
|
4264
|
-
return {
|
|
4265
|
-
checkedAt: Date.now(),
|
|
4266
|
-
cliLatest: cli ?? previous?.cliLatest ?? null,
|
|
4267
|
-
skillLatest: skill ?? previous?.skillLatest ?? null,
|
|
4268
|
-
version: 1
|
|
4269
|
-
};
|
|
4270
|
-
}
|
|
4271
|
-
function installedSkillVersions() {
|
|
4272
|
-
const roots = [homedir2(), process.cwd()];
|
|
4273
|
-
const dirs = [".agents", ".claude"];
|
|
4274
|
-
const versions = [];
|
|
4275
|
-
for (const root of roots) {
|
|
4276
|
-
for (const dir of dirs) {
|
|
4277
|
-
const file = path20.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
|
|
4278
|
-
if (!existsSync3(file)) {
|
|
4279
|
-
continue;
|
|
4280
|
-
}
|
|
4281
|
-
try {
|
|
4282
|
-
const found = SKILL_VERSION_PATTERN.exec(readFileSync6(file, "utf8"));
|
|
4283
|
-
if (found?.[1]) {
|
|
4284
|
-
versions.push(found[1]);
|
|
4285
|
-
}
|
|
4286
|
-
} catch {
|
|
4287
|
-
}
|
|
4288
|
-
}
|
|
4289
|
-
}
|
|
4290
|
-
return versions;
|
|
4291
|
-
}
|
|
4292
|
-
function updateChecksDisabled() {
|
|
4293
|
-
return Boolean(
|
|
4294
|
-
process.env.ARTIFACT_CAFE_NO_UPDATE_CHECK ?? process.env.NO_UPDATE_NOTIFIER ?? process.env.CI
|
|
4295
|
-
);
|
|
4296
|
-
}
|
|
4297
|
-
function startUpdateCheck() {
|
|
4298
|
-
if (updateChecksDisabled()) {
|
|
4299
|
-
return () => Promise.resolve();
|
|
4300
|
-
}
|
|
4301
|
-
const pending = (async () => {
|
|
4302
|
-
const cached = readCache();
|
|
4303
|
-
if (cached && Date.now() - cached.checkedAt < CHECK_TTL_MS) {
|
|
4304
|
-
return cached;
|
|
4305
|
-
}
|
|
4306
|
-
const fresh = await fetchLatestVersions(cached);
|
|
4307
|
-
writeCache(fresh);
|
|
4308
|
-
return fresh;
|
|
4309
|
-
})().catch(() => null);
|
|
4310
|
-
return async () => {
|
|
4311
|
-
const latest = await pending;
|
|
4312
|
-
if (!latest) {
|
|
4313
|
-
return;
|
|
4314
|
-
}
|
|
4315
|
-
const notices = [];
|
|
4316
|
-
const current = cliVersion();
|
|
4317
|
-
if (latest.cliLatest && isNewerVersion(latest.cliLatest, current)) {
|
|
4318
|
-
notices.push(
|
|
4319
|
-
`[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\`.`
|
|
4320
|
-
);
|
|
4321
|
-
}
|
|
4322
|
-
if (latest.skillLatest) {
|
|
4323
|
-
const stale = installedSkillVersions().filter(
|
|
4324
|
-
(version) => isNewerVersion(latest.skillLatest, version)
|
|
4325
|
-
).sort((a, b) => isNewerVersion(a, b) ? 1 : -1);
|
|
4326
|
-
if (stale.length > 0) {
|
|
4327
|
-
notices.push(
|
|
4328
|
-
`[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.`
|
|
4329
|
-
);
|
|
4330
|
-
}
|
|
4331
|
-
}
|
|
4332
|
-
if (notices.length > 0) {
|
|
4333
|
-
console.error(`
|
|
4334
|
-
${notices.join("\n")}`);
|
|
4335
|
-
}
|
|
4336
|
-
};
|
|
4337
|
-
}
|
|
4607
|
+
};
|
|
4338
4608
|
|
|
4339
4609
|
// src/workspaces.ts
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4610
|
+
var printWorkspace = (workspace) => {
|
|
4611
|
+
console.log(`${workspace.name} (${workspace.role ?? "non-member"})`);
|
|
4612
|
+
console.log(` ${workspace.handle} \xB7 ${workspace.id}
|
|
4613
|
+
`);
|
|
4614
|
+
};
|
|
4615
|
+
var workspaces = async (opts) => {
|
|
4616
|
+
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(/\/$/u, "");
|
|
4345
4617
|
const key = resolveApiKey(apiUrl2, opts.apiKey);
|
|
4346
4618
|
if (!key) {
|
|
4347
4619
|
throw new Error(
|
|
@@ -4363,17 +4635,12 @@ async function workspaces(opts) {
|
|
|
4363
4635
|
for (const workspace of data2.workspaces) {
|
|
4364
4636
|
printWorkspace(workspace);
|
|
4365
4637
|
}
|
|
4366
|
-
}
|
|
4367
|
-
function printWorkspace(workspace) {
|
|
4368
|
-
console.log(`${workspace.name} (${workspace.role ?? "non-member"})`);
|
|
4369
|
-
console.log(` ${workspace.handle} \xB7 ${workspace.id}
|
|
4370
|
-
`);
|
|
4371
|
-
}
|
|
4638
|
+
};
|
|
4372
4639
|
|
|
4373
4640
|
// src/index.ts
|
|
4374
|
-
|
|
4641
|
+
var readStringArg = (argv, flag) => {
|
|
4375
4642
|
const prefix = `${flag}=`;
|
|
4376
|
-
for (let i = 0; i < argv.length; i
|
|
4643
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
4377
4644
|
const token = argv[i];
|
|
4378
4645
|
if (token === void 0) {
|
|
4379
4646
|
continue;
|
|
@@ -4385,7 +4652,36 @@ function readStringArg(argv, flag) {
|
|
|
4385
4652
|
return token.slice(prefix.length);
|
|
4386
4653
|
}
|
|
4387
4654
|
}
|
|
4388
|
-
}
|
|
4655
|
+
};
|
|
4656
|
+
var pull2 = defineCommand({
|
|
4657
|
+
args: {
|
|
4658
|
+
"api-key": {
|
|
4659
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
4660
|
+
type: "string"
|
|
4661
|
+
},
|
|
4662
|
+
artifact: { description: "Artifact ID (default: env or folder config)", type: "string" },
|
|
4663
|
+
dir: { default: ".", description: "Directory to download into", type: "positional" },
|
|
4664
|
+
force: { description: "Overwrite matching files in a non-empty directory", type: "boolean" },
|
|
4665
|
+
json: { description: "Machine-readable output", type: "boolean" },
|
|
4666
|
+
version: { description: "Version number (default: current)", type: "string" }
|
|
4667
|
+
},
|
|
4668
|
+
meta: { description: "Download all files of an artifact version", name: "pull" },
|
|
4669
|
+
async run({ args }) {
|
|
4670
|
+
try {
|
|
4671
|
+
await pull({
|
|
4672
|
+
apiKey: args["api-key"],
|
|
4673
|
+
artifact: args.artifact,
|
|
4674
|
+
dir: args.dir,
|
|
4675
|
+
force: Boolean(args.force),
|
|
4676
|
+
json: Boolean(args.json),
|
|
4677
|
+
version: args.version
|
|
4678
|
+
});
|
|
4679
|
+
} catch (error) {
|
|
4680
|
+
printCliError(error, Boolean(args.json));
|
|
4681
|
+
process.exit(1);
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
});
|
|
4389
4685
|
var publish2 = defineCommand({
|
|
4390
4686
|
args: {
|
|
4391
4687
|
"api-key": {
|
|
@@ -4414,6 +4710,7 @@ var publish2 = defineCommand({
|
|
|
4414
4710
|
description: "Publish into this workspace folder name (or id)",
|
|
4415
4711
|
type: "string"
|
|
4416
4712
|
},
|
|
4713
|
+
force: { description: "Publish even if a newer version exists", type: "boolean" },
|
|
4417
4714
|
json: { description: "Machine-readable output", type: "boolean" },
|
|
4418
4715
|
"no-open": { description: "Don't open the review URL", type: "boolean" },
|
|
4419
4716
|
notes: {
|
|
@@ -4448,6 +4745,7 @@ var publish2 = defineCommand({
|
|
|
4448
4745
|
entry: args.entry,
|
|
4449
4746
|
expiresIn: args["expires-in"],
|
|
4450
4747
|
folder: args.folder,
|
|
4748
|
+
force: Boolean(args.force),
|
|
4451
4749
|
json: Boolean(args.json),
|
|
4452
4750
|
notes: readStringArg(process.argv.slice(2), "--notes"),
|
|
4453
4751
|
open: !args["no-open"],
|
|
@@ -4456,8 +4754,8 @@ var publish2 = defineCommand({
|
|
|
4456
4754
|
title: args.title,
|
|
4457
4755
|
workspace: args.workspace
|
|
4458
4756
|
});
|
|
4459
|
-
} catch (
|
|
4460
|
-
printCliError(
|
|
4757
|
+
} catch (error) {
|
|
4758
|
+
printCliError(error, Boolean(args.json));
|
|
4461
4759
|
process.exit(1);
|
|
4462
4760
|
}
|
|
4463
4761
|
}
|
|
@@ -4477,8 +4775,8 @@ var login2 = defineCommand({
|
|
|
4477
4775
|
async run({ args }) {
|
|
4478
4776
|
try {
|
|
4479
4777
|
await login({ apiKey: args["api-key"], json: Boolean(args.json) });
|
|
4480
|
-
} catch (
|
|
4481
|
-
printCliError(
|
|
4778
|
+
} catch (error) {
|
|
4779
|
+
printCliError(error, Boolean(args.json));
|
|
4482
4780
|
process.exit(1);
|
|
4483
4781
|
}
|
|
4484
4782
|
}
|
|
@@ -4494,8 +4792,8 @@ var logout2 = defineCommand({
|
|
|
4494
4792
|
run({ args }) {
|
|
4495
4793
|
try {
|
|
4496
4794
|
logout({ json: Boolean(args.json) });
|
|
4497
|
-
} catch (
|
|
4498
|
-
printCliError(
|
|
4795
|
+
} catch (error) {
|
|
4796
|
+
printCliError(error, Boolean(args.json));
|
|
4499
4797
|
process.exit(1);
|
|
4500
4798
|
}
|
|
4501
4799
|
}
|
|
@@ -4511,8 +4809,8 @@ var whoami2 = defineCommand({
|
|
|
4511
4809
|
async run({ args }) {
|
|
4512
4810
|
try {
|
|
4513
4811
|
await whoami({ json: Boolean(args.json) });
|
|
4514
|
-
} catch (
|
|
4515
|
-
printCliError(
|
|
4812
|
+
} catch (error) {
|
|
4813
|
+
printCliError(error, Boolean(args.json));
|
|
4516
4814
|
process.exit(1);
|
|
4517
4815
|
}
|
|
4518
4816
|
}
|
|
@@ -4540,8 +4838,8 @@ var list2 = defineCommand({
|
|
|
4540
4838
|
json: Boolean(args.json),
|
|
4541
4839
|
workspace: args.workspace
|
|
4542
4840
|
});
|
|
4543
|
-
} catch (
|
|
4544
|
-
printCliError(
|
|
4841
|
+
} catch (error) {
|
|
4842
|
+
printCliError(error, Boolean(args.json));
|
|
4545
4843
|
process.exit(1);
|
|
4546
4844
|
}
|
|
4547
4845
|
}
|
|
@@ -4587,8 +4885,8 @@ var folders2 = defineCommand({
|
|
|
4587
4885
|
visibility: args.visibility,
|
|
4588
4886
|
workspace: args.workspace
|
|
4589
4887
|
});
|
|
4590
|
-
} catch (
|
|
4591
|
-
printCliError(
|
|
4888
|
+
} catch (error) {
|
|
4889
|
+
printCliError(error, Boolean(args.json));
|
|
4592
4890
|
process.exit(1);
|
|
4593
4891
|
}
|
|
4594
4892
|
}
|
|
@@ -4632,8 +4930,8 @@ var move2 = defineCommand({
|
|
|
4632
4930
|
json: Boolean(args.json),
|
|
4633
4931
|
unfiled: Boolean(args.unfiled)
|
|
4634
4932
|
});
|
|
4635
|
-
} catch (
|
|
4636
|
-
printCliError(
|
|
4933
|
+
} catch (error) {
|
|
4934
|
+
printCliError(error, Boolean(args.json));
|
|
4637
4935
|
process.exit(1);
|
|
4638
4936
|
}
|
|
4639
4937
|
}
|
|
@@ -4656,8 +4954,8 @@ var workspaces2 = defineCommand({
|
|
|
4656
4954
|
apiKey: args["api-key"],
|
|
4657
4955
|
json: Boolean(args.json)
|
|
4658
4956
|
});
|
|
4659
|
-
} catch (
|
|
4660
|
-
printCliError(
|
|
4957
|
+
} catch (error) {
|
|
4958
|
+
printCliError(error, Boolean(args.json));
|
|
4661
4959
|
process.exit(1);
|
|
4662
4960
|
}
|
|
4663
4961
|
}
|
|
@@ -4696,8 +4994,8 @@ var link2 = defineCommand({
|
|
|
4696
4994
|
json: Boolean(args.json),
|
|
4697
4995
|
slug: args.slug
|
|
4698
4996
|
});
|
|
4699
|
-
} catch (
|
|
4700
|
-
printCliError(
|
|
4997
|
+
} catch (error) {
|
|
4998
|
+
printCliError(error, Boolean(args.json));
|
|
4701
4999
|
process.exit(1);
|
|
4702
5000
|
}
|
|
4703
5001
|
}
|
|
@@ -4738,8 +5036,8 @@ var expiration2 = defineCommand({
|
|
|
4738
5036
|
json: Boolean(args.json),
|
|
4739
5037
|
keep: Boolean(args.keep)
|
|
4740
5038
|
});
|
|
4741
|
-
} catch (
|
|
4742
|
-
printCliError(
|
|
5039
|
+
} catch (error) {
|
|
5040
|
+
printCliError(error, Boolean(args.json));
|
|
4743
5041
|
process.exitCode = 1;
|
|
4744
5042
|
}
|
|
4745
5043
|
}
|
|
@@ -4788,9 +5086,7 @@ var comments2 = defineCommand({
|
|
|
4788
5086
|
try {
|
|
4789
5087
|
const timeout = Number(args.timeout);
|
|
4790
5088
|
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
4791
|
-
throw new Error(
|
|
4792
|
-
`--timeout must be a positive number of seconds (got "${args.timeout}").`
|
|
4793
|
-
);
|
|
5089
|
+
throw new Error(`--timeout must be a positive number of seconds (got "${args.timeout}").`);
|
|
4794
5090
|
}
|
|
4795
5091
|
await comments({
|
|
4796
5092
|
apiKey: args["api-key"],
|
|
@@ -4802,8 +5098,8 @@ var comments2 = defineCommand({
|
|
|
4802
5098
|
version: args.version,
|
|
4803
5099
|
wait: Boolean(args.wait)
|
|
4804
5100
|
});
|
|
4805
|
-
} catch (
|
|
4806
|
-
printCliError(
|
|
5101
|
+
} catch (error) {
|
|
5102
|
+
printCliError(error, Boolean(args.json));
|
|
4807
5103
|
process.exit(1);
|
|
4808
5104
|
}
|
|
4809
5105
|
}
|
|
@@ -4848,9 +5144,7 @@ var listen = defineCommand({
|
|
|
4848
5144
|
try {
|
|
4849
5145
|
const timeout = Number(args.timeout);
|
|
4850
5146
|
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
4851
|
-
throw new Error(
|
|
4852
|
-
`--timeout must be a positive number of seconds (got "${args.timeout}").`
|
|
4853
|
-
);
|
|
5147
|
+
throw new Error(`--timeout must be a positive number of seconds (got "${args.timeout}").`);
|
|
4854
5148
|
}
|
|
4855
5149
|
await comments({
|
|
4856
5150
|
apiKey: args["api-key"],
|
|
@@ -4862,8 +5156,8 @@ var listen = defineCommand({
|
|
|
4862
5156
|
version: args.version,
|
|
4863
5157
|
wait: true
|
|
4864
5158
|
});
|
|
4865
|
-
} catch (
|
|
4866
|
-
printCliError(
|
|
5159
|
+
} catch (error) {
|
|
5160
|
+
printCliError(error, Boolean(args.json));
|
|
4867
5161
|
process.exit(1);
|
|
4868
5162
|
}
|
|
4869
5163
|
}
|
|
@@ -4903,8 +5197,8 @@ var statusCmd = defineCommand({
|
|
|
4903
5197
|
json: Boolean(args.json),
|
|
4904
5198
|
state: args.state
|
|
4905
5199
|
});
|
|
4906
|
-
} catch (
|
|
4907
|
-
printCliError(
|
|
5200
|
+
} catch (error) {
|
|
5201
|
+
printCliError(error, Boolean(args.json));
|
|
4908
5202
|
process.exit(1);
|
|
4909
5203
|
}
|
|
4910
5204
|
}
|
|
@@ -4942,8 +5236,8 @@ var protect2 = defineCommand({
|
|
|
4942
5236
|
off: Boolean(args.off),
|
|
4943
5237
|
password: args.password
|
|
4944
5238
|
});
|
|
4945
|
-
} catch (
|
|
4946
|
-
printCliError(
|
|
5239
|
+
} catch (error) {
|
|
5240
|
+
printCliError(error, Boolean(args.json));
|
|
4947
5241
|
process.exit(1);
|
|
4948
5242
|
}
|
|
4949
5243
|
}
|
|
@@ -4983,8 +5277,8 @@ var review2 = defineCommand({
|
|
|
4983
5277
|
json: Boolean(args.json),
|
|
4984
5278
|
mode: args.mode
|
|
4985
5279
|
});
|
|
4986
|
-
} catch (
|
|
4987
|
-
printCliError(
|
|
5280
|
+
} catch (error) {
|
|
5281
|
+
printCliError(error, Boolean(args.json));
|
|
4988
5282
|
process.exit(1);
|
|
4989
5283
|
}
|
|
4990
5284
|
}
|
|
@@ -5029,8 +5323,8 @@ var threadStatusCommand = (action) => defineCommand({
|
|
|
5029
5323
|
json: Boolean(args.json),
|
|
5030
5324
|
thread: args.thread
|
|
5031
5325
|
});
|
|
5032
|
-
} catch (
|
|
5033
|
-
printCliError(
|
|
5326
|
+
} catch (error) {
|
|
5327
|
+
printCliError(error, Boolean(args.json));
|
|
5034
5328
|
process.exit(1);
|
|
5035
5329
|
}
|
|
5036
5330
|
}
|
|
@@ -5088,8 +5382,8 @@ var reply = defineCommand({
|
|
|
5088
5382
|
resolve: Boolean(args.resolve),
|
|
5089
5383
|
thread: args.thread
|
|
5090
5384
|
});
|
|
5091
|
-
} catch (
|
|
5092
|
-
printCliError(
|
|
5385
|
+
} catch (error) {
|
|
5386
|
+
printCliError(error, Boolean(args.json));
|
|
5093
5387
|
process.exit(1);
|
|
5094
5388
|
}
|
|
5095
5389
|
}
|
|
@@ -5158,8 +5452,8 @@ var comment2 = defineCommand({
|
|
|
5158
5452
|
suffix: args.suffix,
|
|
5159
5453
|
via: args.via
|
|
5160
5454
|
});
|
|
5161
|
-
} catch (
|
|
5162
|
-
printCliError(
|
|
5455
|
+
} catch (error) {
|
|
5456
|
+
printCliError(error, Boolean(args.json));
|
|
5163
5457
|
process.exit(1);
|
|
5164
5458
|
}
|
|
5165
5459
|
}
|
|
@@ -5201,8 +5495,8 @@ var edit = defineCommand({
|
|
|
5201
5495
|
json: Boolean(args.json),
|
|
5202
5496
|
message: args.message
|
|
5203
5497
|
});
|
|
5204
|
-
} catch (
|
|
5205
|
-
printCliError(
|
|
5498
|
+
} catch (error) {
|
|
5499
|
+
printCliError(error, Boolean(args.json));
|
|
5206
5500
|
process.exit(1);
|
|
5207
5501
|
}
|
|
5208
5502
|
}
|
|
@@ -5239,8 +5533,8 @@ var del = defineCommand({
|
|
|
5239
5533
|
json: Boolean(args.json),
|
|
5240
5534
|
message: args.message
|
|
5241
5535
|
});
|
|
5242
|
-
} catch (
|
|
5243
|
-
printCliError(
|
|
5536
|
+
} catch (error) {
|
|
5537
|
+
printCliError(error, Boolean(args.json));
|
|
5244
5538
|
process.exit(1);
|
|
5245
5539
|
}
|
|
5246
5540
|
}
|
|
@@ -5281,8 +5575,8 @@ var share2 = defineCommand({
|
|
|
5281
5575
|
json: Boolean(args.json),
|
|
5282
5576
|
list: Boolean(args.list)
|
|
5283
5577
|
});
|
|
5284
|
-
} catch (
|
|
5285
|
-
printCliError(
|
|
5578
|
+
} catch (error) {
|
|
5579
|
+
printCliError(error, Boolean(args.json));
|
|
5286
5580
|
process.exit(1);
|
|
5287
5581
|
}
|
|
5288
5582
|
}
|
|
@@ -5322,8 +5616,8 @@ var unshare2 = defineCommand({
|
|
|
5322
5616
|
email: args.email,
|
|
5323
5617
|
json: Boolean(args.json)
|
|
5324
5618
|
});
|
|
5325
|
-
} catch (
|
|
5326
|
-
printCliError(
|
|
5619
|
+
} catch (error) {
|
|
5620
|
+
printCliError(error, Boolean(args.json));
|
|
5327
5621
|
process.exit(1);
|
|
5328
5622
|
}
|
|
5329
5623
|
}
|
|
@@ -5352,8 +5646,8 @@ var open = defineCommand({
|
|
|
5352
5646
|
artifact: args.artifact,
|
|
5353
5647
|
dir: args.dir
|
|
5354
5648
|
});
|
|
5355
|
-
} catch (
|
|
5356
|
-
printCliError(
|
|
5649
|
+
} catch (error) {
|
|
5650
|
+
printCliError(error, false);
|
|
5357
5651
|
process.exit(1);
|
|
5358
5652
|
}
|
|
5359
5653
|
}
|
|
@@ -5373,8 +5667,8 @@ var dashboard = defineCommand({
|
|
|
5373
5667
|
run({ args }) {
|
|
5374
5668
|
try {
|
|
5375
5669
|
openDashboard({ dir: args.dir });
|
|
5376
|
-
} catch (
|
|
5377
|
-
printCliError(
|
|
5670
|
+
} catch (error) {
|
|
5671
|
+
printCliError(error, false);
|
|
5378
5672
|
process.exit(1);
|
|
5379
5673
|
}
|
|
5380
5674
|
}
|
|
@@ -5406,8 +5700,8 @@ var dataLsCommand = defineCommand({
|
|
|
5406
5700
|
json: Boolean(args.json),
|
|
5407
5701
|
limit: args.limit === void 0 ? void 0 : Number(args.limit)
|
|
5408
5702
|
});
|
|
5409
|
-
} catch (
|
|
5410
|
-
printCliError(
|
|
5703
|
+
} catch (error) {
|
|
5704
|
+
printCliError(error, Boolean(args.json));
|
|
5411
5705
|
process.exit(1);
|
|
5412
5706
|
}
|
|
5413
5707
|
}
|
|
@@ -5447,8 +5741,8 @@ var dataInsertCommand = defineCommand({
|
|
|
5447
5741
|
json: Boolean(args.json),
|
|
5448
5742
|
record: args.record
|
|
5449
5743
|
});
|
|
5450
|
-
} catch (
|
|
5451
|
-
printCliError(
|
|
5744
|
+
} catch (error) {
|
|
5745
|
+
printCliError(error, Boolean(args.json));
|
|
5452
5746
|
process.exit(1);
|
|
5453
5747
|
}
|
|
5454
5748
|
}
|
|
@@ -5474,8 +5768,8 @@ var dataExportCommand = defineCommand({
|
|
|
5474
5768
|
async run({ args }) {
|
|
5475
5769
|
try {
|
|
5476
5770
|
await dataExport({ dir: args.dir });
|
|
5477
|
-
} catch (
|
|
5478
|
-
printCliError(
|
|
5771
|
+
} catch (error) {
|
|
5772
|
+
printCliError(error, true);
|
|
5479
5773
|
process.exit(1);
|
|
5480
5774
|
}
|
|
5481
5775
|
}
|
|
@@ -5507,8 +5801,8 @@ var dataClearCommand = defineCommand({
|
|
|
5507
5801
|
json: Boolean(args.json),
|
|
5508
5802
|
yes: Boolean(args.yes)
|
|
5509
5803
|
});
|
|
5510
|
-
} catch (
|
|
5511
|
-
printCliError(
|
|
5804
|
+
} catch (error) {
|
|
5805
|
+
printCliError(error, Boolean(args.json));
|
|
5512
5806
|
process.exit(1);
|
|
5513
5807
|
}
|
|
5514
5808
|
}
|
|
@@ -5561,6 +5855,7 @@ var main = defineCommand({
|
|
|
5561
5855
|
open,
|
|
5562
5856
|
protect: protect2,
|
|
5563
5857
|
publish: publish2,
|
|
5858
|
+
pull: pull2,
|
|
5564
5859
|
reopen,
|
|
5565
5860
|
reply,
|
|
5566
5861
|
resolve,
|