artifact-cafe 0.0.1 → 0.0.2

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.
Files changed (2) hide show
  1. package/dist/index.js +557 -304
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,225 +6,262 @@ import { defineCommand, runMain } from "citty";
6
6
  // src/comments.ts
7
7
  import path2 from "path";
8
8
 
9
- // ../shared/src/publish.ts
10
- import { z } from "zod";
11
- function isSafeArtifactPath(p) {
12
- if (p.length === 0 || p.length > 1024) return false;
13
- if (p.includes("\\") || p.includes("\0")) return false;
14
- if (p.startsWith("/") || p.startsWith("~")) return false;
15
- return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
16
- }
17
- var manifestFileSchema = z.object({
18
- path: z.string().min(1).max(1024).refine(isSafeArtifactPath, { message: "path must be a safe relative path" }),
19
- hash: z.string().regex(/^sha256_[a-f0-9]{64}$/),
20
- size: z.number().int().positive(),
21
- mimeType: z.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" })
22
- });
23
- var publishSourceSchema = z.object({
24
- type: z.literal("cli"),
25
- agent: z.string().optional(),
26
- runId: z.string().optional()
27
- });
28
- var createPublishSessionSchema = z.object({
29
- artifactId: z.string().nullable(),
30
- title: z.string().max(200).optional(),
31
- entryPath: z.string().optional(),
32
- files: z.array(manifestFileSchema).min(1).max(1e3),
33
- source: publishSourceSchema
34
- });
35
- var publishSessionResponseSchema = z.object({
36
- publishSessionId: z.string(),
37
- artifactId: z.string(),
38
- versionId: z.string(),
39
- filesToUpload: z.array(
40
- z.object({
41
- path: z.string(),
42
- uploadUrl: z.string().url(),
43
- method: z.literal("PUT")
44
- })
45
- ),
46
- skippedFiles: z.array(z.string()),
47
- expiresAt: z.string()
48
- });
49
- var finalizeResponseSchema = z.object({
50
- artifactId: z.string(),
51
- versionId: z.string(),
52
- versionNumber: z.number().int().positive(),
53
- url: z.string().url(),
54
- // Returned only on first publish of a new artifact (one-time).
55
- claimUrl: z.string().url().optional(),
56
- publishToken: z.string().optional()
57
- });
58
- var artifactResponseSchema = z.object({
59
- id: z.string(),
60
- slug: z.string(),
61
- title: z.string(),
62
- currentVersion: z.object({
63
- id: z.string(),
64
- versionNumber: z.number().int().positive(),
65
- entryUrl: z.string().url()
66
- }).nullable(),
67
- claimed: z.boolean()
68
- });
69
-
70
9
  // ../shared/src/comments.ts
71
- import { z as z2 } from "zod";
72
- var elementSnapshotSchema = z2.object({
73
- tag: z2.string().min(1).max(128),
74
- id: z2.string().max(256).optional(),
75
- classes: z2.string().max(256).optional(),
76
- text: z2.string().max(200).optional()
77
- });
78
- var pointAnchorSchema = z2.object({
79
- type: z2.literal("point"),
10
+ import { z } from "zod";
11
+ var elementSnapshotSchema = z.object({
12
+ classes: z.string().max(256).optional(),
13
+ id: z.string().max(256).optional(),
14
+ tag: z.string().min(1).max(128),
15
+ text: z.string().max(200).optional()
16
+ });
17
+ var pointAnchorSchema = z.object({
18
+ element: elementSnapshotSchema.optional(),
80
19
  // elementPath is the primary anchor; x/y are offsets within the
81
20
  // element's box, normalized 0..1 (SPEC.md §8.1).
82
- elementPath: z2.string().max(2048),
83
- x: z2.number().min(0).max(1),
84
- y: z2.number().min(0).max(1),
85
- viewportWidth: z2.number().int().positive().max(1e5),
86
- viewportHeight: z2.number().int().positive().max(1e5),
87
- element: elementSnapshotSchema.optional()
88
- });
89
- var textAnchorSchema = z2.object({
90
- type: z2.literal("text"),
91
- quote: z2.string().min(1).max(1e3),
92
- prefix: z2.string().max(200).optional(),
93
- suffix: z2.string().max(200).optional(),
94
- startOffset: z2.number().int().nonnegative().max(1e8),
95
- endOffset: z2.number().int().nonnegative().max(1e8),
96
- elementPath: z2.string().max(2048)
97
- });
98
- var anchorSchema = z2.discriminatedUnion("type", [
21
+ elementPath: z.string().max(2048),
22
+ type: z.literal("point"),
23
+ viewportHeight: z.number().int().positive().max(1e5),
24
+ viewportWidth: z.number().int().positive().max(1e5),
25
+ x: z.number().min(0).max(1),
26
+ y: z.number().min(0).max(1)
27
+ });
28
+ var textAnchorSchema = z.object({
29
+ elementPath: z.string().max(2048),
30
+ endOffset: z.number().int().nonnegative().max(1e8),
31
+ prefix: z.string().max(200).optional(),
32
+ quote: z.string().min(1).max(1e3),
33
+ startOffset: z.number().int().nonnegative().max(1e8),
34
+ suffix: z.string().max(200).optional(),
35
+ type: z.literal("text")
36
+ });
37
+ var anchorSchema = z.discriminatedUnion("type", [
99
38
  pointAnchorSchema,
100
39
  textAnchorSchema
101
40
  ]);
102
- var guestSchema = z2.object({
103
- guestId: z2.string().min(16),
104
- displayName: z2.string().min(1).max(64)
41
+ var guestSchema = z.object({
42
+ displayName: z.string().min(1).max(64),
43
+ guestId: z.string().min(16)
105
44
  });
106
- var createCommentThreadSchema = z2.object({
107
- versionId: z2.string(),
108
- guest: guestSchema.optional(),
45
+ var createCommentThreadSchema = z.object({
109
46
  anchor: anchorSchema,
110
- body: z2.string().min(1).max(1e4)
111
- });
112
- var createCommentMessageSchema = z2.object({
47
+ body: z.string().min(1).max(1e4),
113
48
  guest: guestSchema.optional(),
114
- body: z2.string().min(1).max(1e4)
49
+ versionId: z.string()
50
+ });
51
+ var createCommentMessageSchema = z.object({
52
+ body: z.string().min(1).max(1e4),
53
+ guest: guestSchema.optional()
115
54
  });
116
- var threadActionSchema = z2.object({
55
+ var threadActionSchema = z.object({
117
56
  guest: guestSchema.optional()
118
57
  });
119
- var commentAuthorSchema = z2.object({
120
- guestId: z2.string().nullable(),
121
- displayName: z2.string()
58
+ var commentAuthorSchema = z.object({
59
+ displayName: z.string(),
60
+ guestId: z.string().nullable()
122
61
  });
123
- var commentMessageSchema = z2.object({
124
- id: z2.string(),
125
- body: z2.string(),
62
+ var commentMessageSchema = z.object({
126
63
  author: commentAuthorSchema,
127
- createdAt: z2.string()
64
+ body: z.string(),
65
+ createdAt: z.string(),
66
+ id: z.string()
128
67
  });
129
- var commentThreadSchema = z2.object({
130
- id: z2.string(),
131
- artifactId: z2.string(),
132
- versionId: z2.string(),
68
+ var commentThreadSchema = z.object({
133
69
  anchor: anchorSchema,
134
- status: z2.enum(["open", "resolved"]),
70
+ artifactId: z.string(),
71
+ createdAt: z.string(),
135
72
  createdBy: commentAuthorSchema,
136
- createdAt: z2.string(),
137
- messages: z2.array(commentMessageSchema)
138
- });
139
- var commentThreadListSchema = z2.object({
140
- threads: z2.array(commentThreadSchema)
141
- });
142
- var commentExportQuerySchema = z2.object({
143
- status: z2.enum(["open", "resolved", "all"]).default("open"),
144
- version: z2.enum(["current", "all"]).default("current")
145
- });
146
- var commentExportReplySchema = z2.object({
147
- author: z2.string(),
148
- body: z2.string(),
149
- createdAt: z2.string()
150
- });
151
- var commentExportThreadSchema = z2.object({
152
- id: z2.string(),
153
- status: z2.enum(["open", "resolved"]),
154
- versionNumber: z2.number().int().positive(),
155
- author: z2.string(),
156
- body: z2.string(),
73
+ id: z.string(),
74
+ messages: z.array(commentMessageSchema),
75
+ status: z.enum(["open", "resolved"]),
76
+ versionId: z.string()
77
+ });
78
+ var commentThreadListSchema = z.object({
79
+ threads: z.array(commentThreadSchema)
80
+ });
81
+ var commentExportQuerySchema = z.object({
82
+ status: z.enum(["open", "resolved", "all"]).default("open"),
83
+ version: z.enum(["current", "all"]).default("current")
84
+ });
85
+ var commentExportReplySchema = z.object({
86
+ author: z.string(),
87
+ body: z.string(),
88
+ createdAt: z.string()
89
+ });
90
+ var commentExportThreadSchema = z.object({
157
91
  anchor: anchorSchema,
158
- createdAt: z2.string(),
159
- replies: z2.array(commentExportReplySchema)
92
+ author: z.string(),
93
+ body: z.string(),
94
+ createdAt: z.string(),
95
+ id: z.string(),
96
+ replies: z.array(commentExportReplySchema),
97
+ status: z.enum(["open", "resolved"]),
98
+ versionNumber: z.number().int().positive()
160
99
  });
161
- var commentExportSchema = z2.object({
162
- artifactId: z2.string(),
100
+ var commentExportSchema = z.object({
101
+ artifactId: z.string(),
102
+ comments: z.array(commentExportThreadSchema),
163
103
  // The artifact's current version (null before the first finalize).
164
- versionNumber: z2.number().int().positive().nullable(),
165
- comments: z2.array(commentExportThreadSchema)
104
+ versionNumber: z.number().int().positive().nullable()
166
105
  });
167
106
  function elementLabel(el) {
168
- if (el.id) return `<${el.tag}#${el.id}>`;
107
+ if (el.id) {
108
+ return `<${el.tag}#${el.id}>`;
109
+ }
169
110
  const classes = el.classes ? el.classes.split(" ").slice(0, 2) : [];
170
111
  return classes.length ? `<${el.tag}.${classes.join(".")}>` : `<${el.tag}>`;
171
112
  }
172
113
 
173
114
  // ../shared/src/errors.ts
115
+ import { z as z2 } from "zod";
116
+ var apiErrorSchema = z2.object({
117
+ code: z2.string(),
118
+ docsUrl: z2.string().url(),
119
+ message: z2.string(),
120
+ retryAfter: z2.number().int().positive().optional()
121
+ });
122
+
123
+ // ../shared/src/owner.ts
174
124
  import { z as z3 } from "zod";
175
- var apiErrorSchema = z3.object({
176
- code: z3.string(),
177
- message: z3.string(),
178
- retryAfter: z3.number().int().positive().optional(),
179
- docsUrl: z3.string().url()
125
+ var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
126
+ var claimResponseSchema = z3.object({
127
+ artifactId: z3.string(),
128
+ slug: z3.string(),
129
+ title: z3.string(),
130
+ url: z3.string().url()
131
+ });
132
+ var updateArtifactSchema = z3.object({
133
+ slug: z3.string().min(3).max(63).regex(SLUG_PATTERN, {
134
+ message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
135
+ }).optional(),
136
+ title: z3.string().min(1).max(200).optional()
137
+ }).refine((body) => body.title !== void 0 || body.slug !== void 0, {
138
+ message: "provide at least one of title, slug"
139
+ });
140
+ var deleteArtifactResponseSchema = z3.object({
141
+ deleted: z3.literal(true)
180
142
  });
181
143
 
182
- // ../shared/src/versions.ts
144
+ // ../shared/src/protection.ts
183
145
  import { z as z4 } from "zod";
184
- var versionSourceSchema = z4.object({
185
- type: z4.string(),
186
- agentName: z4.string().nullable()
187
- });
188
- var versionChangesSchema = z4.object({
189
- added: z4.array(z4.string()),
190
- changed: z4.array(z4.string()),
191
- removed: z4.array(z4.string())
192
- });
193
- var versionSummarySchema = z4.object({
194
- id: z4.string(),
195
- versionNumber: z4.number().int().positive(),
196
- createdAt: z4.string(),
197
- entryUrl: z4.string().url(),
198
- fileCount: z4.number().int().nonnegative(),
199
- totalSizeBytes: z4.number().int().nonnegative(),
200
- source: versionSourceSchema,
201
- isCurrent: z4.boolean(),
202
- // Absent for the first version — there is no previous version to diff against.
203
- changes: versionChangesSchema.nullable()
146
+ var PASSWORD_MIN = 6;
147
+ var PASSWORD_MAX = 128;
148
+ var gatePasswordSchema = z4.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
149
+ var setProtectionSchema = z4.object({
150
+ password: gatePasswordSchema.nullable()
151
+ });
152
+ var protectionResponseSchema = z4.object({
153
+ protected: z4.boolean()
204
154
  });
205
- var versionListResponseSchema = z4.object({
206
- versions: z4.array(versionSummarySchema)
155
+ var unlockSchema = z4.object({
156
+ password: z4.string().min(1).max(1024)
157
+ });
158
+ var unlockResponseSchema = z4.object({
159
+ unlocked: z4.literal(true)
207
160
  });
208
161
 
209
- // ../shared/src/owner.ts
162
+ // ../shared/src/publish.ts
210
163
  import { z as z5 } from "zod";
211
- var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
212
- var claimResponseSchema = z5.object({
164
+ function isSafeArtifactPath(p) {
165
+ if (p.length === 0 || p.length > 1024) {
166
+ return false;
167
+ }
168
+ if (p.includes("\\") || p.includes("\0")) {
169
+ return false;
170
+ }
171
+ if (p.startsWith("/") || p.startsWith("~")) {
172
+ return false;
173
+ }
174
+ return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
175
+ }
176
+ var manifestFileSchema = z5.object({
177
+ hash: z5.string().regex(/^sha256_[a-f0-9]{64}$/),
178
+ mimeType: z5.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
179
+ path: z5.string().min(1).max(1024).refine(isSafeArtifactPath, {
180
+ message: "path must be a safe relative path"
181
+ }),
182
+ size: z5.number().int().positive()
183
+ });
184
+ var publishSourceSchema = z5.object({
185
+ agent: z5.string().optional(),
186
+ runId: z5.string().optional(),
187
+ type: z5.literal("cli")
188
+ });
189
+ var createPublishSessionSchema = z5.object({
190
+ artifactId: z5.string().nullable(),
191
+ entryPath: z5.string().optional(),
192
+ files: z5.array(manifestFileSchema).min(1).max(1e3),
193
+ source: publishSourceSchema,
194
+ title: z5.string().max(200).optional()
195
+ });
196
+ var publishSessionResponseSchema = z5.object({
197
+ artifactId: z5.string(),
198
+ expiresAt: z5.string(),
199
+ filesToUpload: z5.array(
200
+ z5.object({
201
+ method: z5.literal("PUT"),
202
+ path: z5.string(),
203
+ uploadUrl: z5.string().url()
204
+ })
205
+ ),
206
+ publishSessionId: z5.string(),
207
+ skippedFiles: z5.array(z5.string()),
208
+ versionId: z5.string()
209
+ });
210
+ var finalizeResponseSchema = z5.object({
213
211
  artifactId: z5.string(),
212
+ // Returned only on first publish of a new artifact (one-time).
213
+ claimUrl: z5.string().url().optional(),
214
+ publishToken: z5.string().optional(),
215
+ url: z5.string().url(),
216
+ versionId: z5.string(),
217
+ versionNumber: z5.number().int().positive()
218
+ });
219
+ var artifactResponseSchema = z5.object({
220
+ claimed: z5.boolean(),
221
+ currentVersion: z5.object({
222
+ entryUrl: z5.string().url(),
223
+ id: z5.string(),
224
+ versionNumber: z5.number().int().positive()
225
+ }).nullable(),
226
+ id: z5.string(),
214
227
  slug: z5.string(),
215
- title: z5.string(),
216
- url: z5.string().url()
228
+ title: z5.string()
217
229
  });
218
- var updateArtifactSchema = z5.object({
219
- title: z5.string().min(1).max(200).optional(),
220
- slug: z5.string().min(3).max(63).regex(SLUG_PATTERN, {
221
- message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
222
- }).optional()
223
- }).refine((body) => body.title !== void 0 || body.slug !== void 0, {
224
- message: "provide at least one of title, slug"
230
+
231
+ // ../shared/src/report.ts
232
+ import { z as z6 } from "zod";
233
+ var reportRequestSchema = z6.object({
234
+ reason: z6.string().max(1e3).optional()
235
+ });
236
+ var reportResponseSchema = z6.object({
237
+ reported: z6.literal(true)
238
+ });
239
+
240
+ // ../shared/src/versions.ts
241
+ import { z as z7 } from "zod";
242
+ var versionSourceSchema = z7.object({
243
+ agentName: z7.string().nullable(),
244
+ type: z7.string()
245
+ });
246
+ var versionChangesSchema = z7.object({
247
+ added: z7.array(z7.string()),
248
+ changed: z7.array(z7.string()),
249
+ removed: z7.array(z7.string())
250
+ });
251
+ var versionSummarySchema = z7.object({
252
+ // Absent for the first version — there is no previous version to diff against.
253
+ changes: versionChangesSchema.nullable(),
254
+ createdAt: z7.string(),
255
+ entryUrl: z7.string().url(),
256
+ fileCount: z7.number().int().nonnegative(),
257
+ id: z7.string(),
258
+ isCurrent: z7.boolean(),
259
+ source: versionSourceSchema,
260
+ totalSizeBytes: z7.number().int().nonnegative(),
261
+ versionNumber: z7.number().int().positive()
225
262
  });
226
- var deleteArtifactResponseSchema = z5.object({
227
- deleted: z5.literal(true)
263
+ var versionListResponseSchema = z7.object({
264
+ versions: z7.array(versionSummarySchema)
228
265
  });
229
266
 
230
267
  // src/api.ts
@@ -246,7 +283,8 @@ async function request(url, init, parse) {
246
283
  res = await fetch(url, init);
247
284
  } catch (err) {
248
285
  throw new Error(
249
- `Could not reach ${new URL(url).origin} \u2014 is the API up? (${err instanceof Error ? err.message : err})`
286
+ `Could not reach ${new URL(url).origin} \u2014 is the API up? (${err instanceof Error ? err.message : err})`,
287
+ { cause: err }
250
288
  );
251
289
  }
252
290
  const body = await res.json().catch(() => null);
@@ -260,26 +298,45 @@ async function request(url, init, parse) {
260
298
  parsed.data.retryAfter
261
299
  );
262
300
  }
263
- throw new ApiRequestError(res.status, "unknown_error", `API returned ${res.status}`);
301
+ throw new ApiRequestError(
302
+ res.status,
303
+ "unknown_error",
304
+ `API returned ${res.status}`
305
+ );
264
306
  }
265
307
  return parse(body);
266
308
  }
267
309
  function createApiClient(apiUrl, publishToken) {
268
310
  const base = apiUrl.replace(/\/$/, "");
269
- const headers = { "content-type": "application/json" };
270
- if (publishToken) headers.authorization = `Bearer ${publishToken}`;
311
+ const headers = {
312
+ "content-type": "application/json"
313
+ };
314
+ if (publishToken) {
315
+ headers.authorization = `Bearer ${publishToken}`;
316
+ }
271
317
  return {
272
318
  async createSession(body) {
273
319
  return request(
274
320
  `${base}/api/v1/publish`,
275
- { method: "POST", headers, body: JSON.stringify(body) },
321
+ { body: JSON.stringify(body), headers, method: "POST" },
276
322
  (d) => publishSessionResponseSchema.parse(d)
277
323
  );
278
324
  },
325
+ async exportComments(artifactId, query) {
326
+ const params = new URLSearchParams({
327
+ status: query.status,
328
+ version: query.version
329
+ });
330
+ return request(
331
+ `${base}/api/v1/artifacts/${artifactId}/comments/export?${params}`,
332
+ { method: "GET" },
333
+ (d) => commentExportSchema.parse(d)
334
+ );
335
+ },
279
336
  async finalize(publishSessionId) {
280
337
  return request(
281
338
  `${base}/api/v1/publish/${publishSessionId}/finalize`,
282
- { method: "POST", headers },
339
+ { headers, method: "POST" },
283
340
  (d) => finalizeResponseSchema.parse(d)
284
341
  );
285
342
  },
@@ -290,43 +347,57 @@ function createApiClient(apiUrl, publishToken) {
290
347
  (d) => artifactResponseSchema.parse(d)
291
348
  );
292
349
  },
293
- async exportComments(artifactId, query) {
294
- const params = new URLSearchParams({ status: query.status, version: query.version });
350
+ // SPEC §6.16 — set a password (string) or clear it (null). Authorized
351
+ // by the publish token this client was created with.
352
+ async setProtection(artifactId, password) {
295
353
  return request(
296
- `${base}/api/v1/artifacts/${artifactId}/comments/export?${params}`,
297
- { method: "GET" },
298
- (d) => commentExportSchema.parse(d)
354
+ `${base}/api/v1/artifacts/${artifactId}/protection`,
355
+ { body: JSON.stringify({ password }), headers, method: "PUT" },
356
+ (d) => protectionResponseSchema.parse(d)
299
357
  );
300
358
  }
301
359
  };
302
360
  }
303
361
  async function uploadFile(uploadUrl, content, mimeType) {
304
362
  const res = await fetch(uploadUrl, {
305
- method: "PUT",
306
- headers: { "content-type": mimeType, "content-length": String(content.byteLength) },
307
- body: new Uint8Array(content)
363
+ body: new Uint8Array(content),
364
+ headers: {
365
+ "content-length": String(content.byteLength),
366
+ "content-type": mimeType
367
+ },
368
+ method: "PUT"
308
369
  });
309
370
  if (!res.ok) {
310
- throw new Error(`Upload failed (${res.status}): ${(await res.text()).slice(0, 200)}`);
371
+ throw new Error(
372
+ `Upload failed (${res.status}): ${(await res.text()).slice(0, 200)}`
373
+ );
311
374
  }
312
375
  }
313
376
 
314
377
  // src/config.ts
315
- import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from "fs";
378
+ import {
379
+ appendFileSync,
380
+ existsSync,
381
+ mkdirSync,
382
+ readFileSync,
383
+ writeFileSync
384
+ } from "fs";
316
385
  import path from "path";
317
- import { z as z6 } from "zod";
318
- var configSchema = z6.object({
319
- artifactId: z6.string(),
320
- slug: z6.string(),
321
- url: z6.string(),
322
- apiUrl: z6.string(),
323
- publishToken: z6.string()
386
+ import { z as z8 } from "zod";
387
+ var configSchema = z8.object({
388
+ apiUrl: z8.string(),
389
+ artifactId: z8.string(),
390
+ publishToken: z8.string(),
391
+ slug: z8.string(),
392
+ url: z8.string()
324
393
  });
325
394
  var CONFIG_DIR = ".artifactcafe";
326
395
  var CONFIG_FILE = "config.json";
327
396
  function readConfig(dir) {
328
397
  const file = path.join(dir, CONFIG_DIR, CONFIG_FILE);
329
- if (!existsSync(file)) return null;
398
+ if (!existsSync(file)) {
399
+ return null;
400
+ }
330
401
  try {
331
402
  return configSchema.parse(JSON.parse(readFileSync(file, "utf8")));
332
403
  } catch {
@@ -337,24 +408,30 @@ function writeConfig(dir, config) {
337
408
  mkdirSync(path.join(dir, CONFIG_DIR), { recursive: true });
338
409
  writeFileSync(
339
410
  path.join(dir, CONFIG_DIR, CONFIG_FILE),
340
- JSON.stringify(config, null, 2) + "\n",
411
+ `${JSON.stringify(config, null, 2)}
412
+ `,
341
413
  { mode: 384 }
342
414
  );
343
415
  ensureGitignored(dir);
344
416
  }
345
417
  function ensureGitignored(dir) {
346
418
  const gitignore = path.join(dir, ".gitignore");
347
- if (!existsSync(gitignore)) return;
419
+ if (!existsSync(gitignore)) {
420
+ return;
421
+ }
348
422
  const lines = readFileSync(gitignore, "utf8").split(/\r?\n/);
349
423
  const present = lines.some((l) => {
350
424
  const t = l.trim();
351
425
  return t === CONFIG_DIR || t === `${CONFIG_DIR}/` || t === `/${CONFIG_DIR}` || t === `/${CONFIG_DIR}/`;
352
426
  });
353
427
  if (!present) {
354
- appendFileSync(gitignore, `
428
+ appendFileSync(
429
+ gitignore,
430
+ `
355
431
  # artifact.cafe publish token
356
432
  ${CONFIG_DIR}/
357
- `);
433
+ `
434
+ );
358
435
  }
359
436
  }
360
437
 
@@ -363,10 +440,14 @@ var STATUSES = ["open", "resolved", "all"];
363
440
  var VERSIONS = ["current", "all"];
364
441
  async function comments(opts) {
365
442
  if (!STATUSES.includes(opts.status)) {
366
- throw new Error(`--status must be one of: ${STATUSES.join(", ")} (got "${opts.status}").`);
443
+ throw new Error(
444
+ `--status must be one of: ${STATUSES.join(", ")} (got "${opts.status}").`
445
+ );
367
446
  }
368
447
  if (!VERSIONS.includes(opts.version)) {
369
- throw new Error(`--version must be one of: ${VERSIONS.join(", ")} (got "${opts.version}").`);
448
+ throw new Error(
449
+ `--version must be one of: ${VERSIONS.join(", ")} (got "${opts.version}").`
450
+ );
370
451
  }
371
452
  const status = opts.status;
372
453
  const version = opts.version;
@@ -379,7 +460,10 @@ async function comments(opts) {
379
460
  }
380
461
  const apiUrl = process.env.ARTIFACT_CAFE_URL ?? config.apiUrl;
381
462
  const client = createApiClient(apiUrl);
382
- const data = await client.exportComments(config.artifactId, { status, version });
463
+ const data = await client.exportComments(config.artifactId, {
464
+ status,
465
+ version
466
+ });
383
467
  if (opts.json) {
384
468
  console.log(JSON.stringify(data, null, 2));
385
469
  return;
@@ -388,14 +472,19 @@ async function comments(opts) {
388
472
  }
389
473
  function printHuman(data, url, status, version) {
390
474
  const statusLabel = status === "all" ? "" : `${status} `;
391
- const scope = version === "current" ? data.versionNumber !== null ? `on v${data.versionNumber}` : "on the current version" : "across all versions";
475
+ let scope = "across all versions";
476
+ if (version === "current") {
477
+ scope = data.versionNumber === null ? "on the current version" : `on v${data.versionNumber}`;
478
+ }
392
479
  if (data.comments.length === 0) {
393
480
  console.log(`No ${statusLabel}comments ${scope}.`);
394
481
  return;
395
482
  }
396
483
  const plural = data.comments.length === 1 ? "" : "s";
397
- console.log(`${data.comments.length} ${statusLabel}thread${plural} ${scope} \u2014 ${url}
398
- `);
484
+ console.log(
485
+ `${data.comments.length} ${statusLabel}thread${plural} ${scope} \u2014 ${url}
486
+ `
487
+ );
399
488
  for (const thread of data.comments) {
400
489
  printThread(thread);
401
490
  }
@@ -403,7 +492,9 @@ function printHuman(data, url, status, version) {
403
492
  function printThread(t) {
404
493
  const marker = t.status === "resolved" ? "\u2713" : "\u25CF";
405
494
  const date = t.createdAt.slice(0, 10);
406
- console.log(`${marker} ${t.author} \xB7 v${t.versionNumber} \xB7 ${date} \xB7 ${t.id}`);
495
+ console.log(
496
+ `${marker} ${t.author} \xB7 v${t.versionNumber} \xB7 ${date} \xB7 ${t.id}`
497
+ );
407
498
  if (t.anchor.type === "text") {
408
499
  console.log(` \u21B3 "${t.anchor.quote}"`);
409
500
  } else if (t.anchor.element) {
@@ -429,27 +520,36 @@ function printCliError(err, json) {
429
520
  if (json) {
430
521
  console.log(
431
522
  JSON.stringify({
432
- error: { code: err.code, message: err.message, retryAfter: err.retryAfter }
523
+ error: {
524
+ code: err.code,
525
+ message: err.message,
526
+ retryAfter: err.retryAfter
527
+ }
433
528
  })
434
529
  );
435
530
  } else {
436
531
  console.error(`\u2717 ${err.message} (${err.code})`);
437
- if (err.retryAfter) console.error(` Retry in ${err.retryAfter}s.`);
532
+ if (err.retryAfter) {
533
+ console.error(` Retry in ${err.retryAfter}s.`);
534
+ }
438
535
  }
439
536
  return;
440
537
  }
441
538
  const message = err instanceof Error ? err.message : String(err);
442
- if (json) console.log(JSON.stringify({ error: { code: "cli_error", message } }));
443
- else console.error(`\u2717 ${message}`);
539
+ if (json) {
540
+ console.log(JSON.stringify({ error: { code: "cli_error", message } }));
541
+ } else {
542
+ console.error(`\u2717 ${message}`);
543
+ }
444
544
  }
445
545
 
446
546
  // src/open.ts
447
547
  import path5 from "path";
448
548
 
449
549
  // src/publish.ts
550
+ import { exec } from "child_process";
450
551
  import { readFileSync as readFileSync3 } from "fs";
451
552
  import path4 from "path";
452
- import { exec } from "child_process";
453
553
 
454
554
  // src/manifest.ts
455
555
  import { createHash } from "crypto";
@@ -458,41 +558,49 @@ import path3 from "path";
458
558
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".artifactcafe"]);
459
559
  var IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store"]);
460
560
  function isIgnoredFile(name) {
461
- if (IGNORED_FILES.has(name)) return true;
462
- if (name === ".env" || name.startsWith(".env.")) return true;
463
- if (name.endsWith(".pem") || name.endsWith(".key")) return true;
464
- if (name.startsWith(".git")) return true;
561
+ if (IGNORED_FILES.has(name)) {
562
+ return true;
563
+ }
564
+ if (name === ".env" || name.startsWith(".env.")) {
565
+ return true;
566
+ }
567
+ if (name.endsWith(".pem") || name.endsWith(".key")) {
568
+ return true;
569
+ }
570
+ if (name.startsWith(".git")) {
571
+ return true;
572
+ }
465
573
  return false;
466
574
  }
467
575
  var MIME_TYPES = {
468
- ".html": "text/html",
469
- ".htm": "text/html",
576
+ ".avif": "image/avif",
470
577
  ".css": "text/css",
578
+ ".gif": "image/gif",
579
+ ".htm": "text/html",
580
+ ".html": "text/html",
581
+ ".ico": "image/x-icon",
582
+ ".jpeg": "image/jpeg",
583
+ ".jpg": "image/jpeg",
471
584
  ".js": "text/javascript",
472
- ".mjs": "text/javascript",
473
585
  ".json": "application/json",
474
586
  ".map": "application/json",
475
- ".txt": "text/plain",
476
587
  ".md": "text/markdown",
477
- ".xml": "application/xml",
478
- ".svg": "image/svg+xml",
588
+ ".mjs": "text/javascript",
589
+ ".mp3": "audio/mpeg",
590
+ ".mp4": "video/mp4",
591
+ ".otf": "font/otf",
592
+ ".pdf": "application/pdf",
479
593
  ".png": "image/png",
480
- ".jpg": "image/jpeg",
481
- ".jpeg": "image/jpeg",
482
- ".gif": "image/gif",
594
+ ".svg": "image/svg+xml",
595
+ ".ttf": "font/ttf",
596
+ ".txt": "text/plain",
597
+ ".wasm": "application/wasm",
598
+ ".wav": "audio/wav",
599
+ ".webm": "video/webm",
483
600
  ".webp": "image/webp",
484
- ".avif": "image/avif",
485
- ".ico": "image/x-icon",
486
601
  ".woff": "font/woff",
487
602
  ".woff2": "font/woff2",
488
- ".ttf": "font/ttf",
489
- ".otf": "font/otf",
490
- ".pdf": "application/pdf",
491
- ".mp4": "video/mp4",
492
- ".webm": "video/webm",
493
- ".mp3": "audio/mpeg",
494
- ".wav": "audio/wav",
495
- ".wasm": "application/wasm"
603
+ ".xml": "application/xml"
496
604
  };
497
605
  function mimeFor(filePath) {
498
606
  return MIME_TYPES[path3.extname(filePath).toLowerCase()] ?? "application/octet-stream";
@@ -516,23 +624,25 @@ function scanDirectory(dir) {
516
624
  }
517
625
  continue;
518
626
  }
519
- if (!entry.isFile()) continue;
627
+ if (!entry.isFile()) {
628
+ continue;
629
+ }
520
630
  if (isIgnoredFile(entry.name) || !isSafeArtifactPath(rel)) {
521
631
  skipped.push(rel);
522
632
  continue;
523
633
  }
524
- const size = statSync(abs).size;
634
+ const { size } = statSync(abs);
525
635
  if (size === 0) {
526
636
  skipped.push(rel);
527
637
  continue;
528
638
  }
529
639
  const content = readFileSync2(abs);
530
640
  files.push({
531
- path: rel,
641
+ absPath: abs,
532
642
  hash: `sha256_${createHash("sha256").update(content).digest("hex")}`,
533
- size,
534
643
  mimeType: mimeFor(rel),
535
- absPath: abs
644
+ path: rel,
645
+ size
536
646
  });
537
647
  }
538
648
  }
@@ -543,13 +653,22 @@ function scanDirectory(dir) {
543
653
  function resolveEntry(files, explicit) {
544
654
  if (explicit) {
545
655
  if (!files.some((f) => f.path === explicit)) {
546
- throw new Error(`Entry file "${explicit}" not found in the publish folder.`);
656
+ throw new Error(
657
+ `Entry file "${explicit}" not found in the publish folder.`
658
+ );
547
659
  }
548
660
  return explicit;
549
661
  }
550
- if (files.some((f) => f.path === "index.html")) return "index.html";
551
- const htmlFiles = files.filter((f) => f.path.endsWith(".html") && !f.path.includes("/"));
552
- if (htmlFiles.length === 1) return htmlFiles[0].path;
662
+ if (files.some((f) => f.path === "index.html")) {
663
+ return "index.html";
664
+ }
665
+ const htmlFiles = files.filter(
666
+ (f) => f.path.endsWith(".html") && !f.path.includes("/")
667
+ );
668
+ const [onlyHtmlFile] = htmlFiles;
669
+ if (onlyHtmlFile && htmlFiles.length === 1) {
670
+ return onlyHtmlFile.path;
671
+ }
553
672
  throw new Error(
554
673
  htmlFiles.length === 0 ? "No entry file found. Add an index.html or pass --entry <file>." : `Multiple HTML files found (${htmlFiles.map((f) => f.path).join(", ")}). Pass --entry <file>.`
555
674
  );
@@ -561,6 +680,12 @@ var UPLOAD_CONCURRENCY = 6;
561
680
  async function publish(opts) {
562
681
  const dir = path4.resolve(opts.dir);
563
682
  const config = readConfig(dir);
683
+ if (opts.password !== void 0) {
684
+ const parsed = gatePasswordSchema.safeParse(opts.password);
685
+ if (!parsed.success) {
686
+ throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
687
+ }
688
+ }
564
689
  const artifactId = opts.artifact ?? config?.artifactId ?? null;
565
690
  const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
566
691
  const apiUrl = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
@@ -570,7 +695,9 @@ async function publish(opts) {
570
695
  }
571
696
  const entryPath = resolveEntry(files, opts.entry);
572
697
  const log = (msg) => {
573
- if (!opts.json) console.log(msg);
698
+ if (!opts.json) {
699
+ console.log(msg);
700
+ }
574
701
  };
575
702
  if (skipped.length) {
576
703
  log(` ignoring ${skipped.length} item(s): ${preview(skipped)}`);
@@ -578,14 +705,14 @@ async function publish(opts) {
578
705
  const client = createApiClient(apiUrl, token);
579
706
  const session = await client.createSession({
580
707
  artifactId,
581
- title: opts.title,
582
708
  entryPath,
583
709
  files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
584
710
  source: {
585
- type: "cli",
586
711
  agent: process.env.ARTIFACT_CAFE_AGENT,
587
- runId: process.env.ARTIFACT_CAFE_RUN_ID
588
- }
712
+ runId: process.env.ARTIFACT_CAFE_RUN_ID,
713
+ type: "cli"
714
+ },
715
+ title: opts.title
589
716
  });
590
717
  const byPath = new Map(files.map((f) => [f.path, f]));
591
718
  log(
@@ -593,46 +720,60 @@ async function publish(opts) {
593
720
  );
594
721
  await inBatches(session.filesToUpload, UPLOAD_CONCURRENCY, async (upload) => {
595
722
  const file = byPath.get(upload.path);
596
- if (!file) throw new Error(`Server asked for unknown file ${upload.path}`);
597
- await uploadFile(upload.uploadUrl, readFileSync3(file.absPath), file.mimeType);
723
+ if (!file) {
724
+ throw new Error(`Server asked for unknown file ${upload.path}`);
725
+ }
726
+ await uploadFile(
727
+ upload.uploadUrl,
728
+ readFileSync3(file.absPath),
729
+ file.mimeType
730
+ );
598
731
  });
599
732
  const result = await client.finalize(session.publishSessionId);
600
733
  if (result.publishToken) {
601
734
  writeConfig(dir, {
735
+ apiUrl,
602
736
  artifactId: result.artifactId,
737
+ publishToken: result.publishToken,
603
738
  slug: slugFromUrl(result.url),
604
- url: result.url,
605
- apiUrl,
606
- publishToken: result.publishToken
739
+ url: result.url
607
740
  });
608
741
  }
742
+ if (opts.password !== void 0) {
743
+ const protectClient = createApiClient(apiUrl, token ?? result.publishToken);
744
+ await protectClient.setProtection(result.artifactId, opts.password);
745
+ }
609
746
  if (opts.json) {
610
747
  console.log(
611
748
  JSON.stringify(
612
749
  {
613
750
  artifactId: result.artifactId,
614
- versionNumber: result.versionNumber,
615
751
  url: result.url,
752
+ versionNumber: result.versionNumber,
616
753
  ...result.claimUrl ? { claimUrl: result.claimUrl } : {},
754
+ ...opts.password === void 0 ? {} : { protected: true },
617
755
  filesUploaded: session.filesToUpload.map((f) => f.path),
618
- skippedFiles: session.skippedFiles,
619
- ignored: skipped
756
+ ignored: skipped,
757
+ skippedFiles: session.skippedFiles
620
758
  },
621
759
  null,
622
760
  2
623
761
  )
624
762
  );
625
763
  } else if (result.versionNumber === 1) {
626
- console.log(`
627
- \u2713 Published to artifact.cafe
628
- `);
764
+ console.log("\n\u2713 Published to artifact.cafe\n");
629
765
  console.log(`Review URL:
630
766
  ${result.url}
631
767
  `);
632
768
  if (result.claimUrl) {
633
- console.log(`Claim URL (shown once \u2014 claim to keep this artifact):
769
+ console.log(
770
+ `Claim URL (shown once \u2014 claim to keep this artifact):
634
771
  ${result.claimUrl}
635
- `);
772
+ `
773
+ );
774
+ }
775
+ if (opts.password !== void 0) {
776
+ console.log("Password protection: on\n");
636
777
  }
637
778
  } else {
638
779
  console.log(`
@@ -640,6 +781,9 @@ ${result.claimUrl}
640
781
  `);
641
782
  console.log(`${result.url}
642
783
  `);
784
+ if (opts.password !== void 0) {
785
+ console.log("Password protection: on\n");
786
+ }
643
787
  }
644
788
  if (opts.open && !opts.json && process.stdout.isTTY) {
645
789
  openInBrowser(result.url);
@@ -647,12 +791,19 @@ ${result.claimUrl}
647
791
  }
648
792
  async function inBatches(items, concurrency, fn) {
649
793
  let next = 0;
650
- const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
651
- while (next < items.length) {
652
- const item = items[next++];
653
- await fn(item);
794
+ const workers = Array.from(
795
+ { length: Math.min(concurrency, items.length) },
796
+ async () => {
797
+ while (next < items.length) {
798
+ const item = items[next];
799
+ next += 1;
800
+ if (item === void 0) {
801
+ continue;
802
+ }
803
+ await fn(item);
804
+ }
654
805
  }
655
- });
806
+ );
656
807
  await Promise.all(workers);
657
808
  }
658
809
  function slugFromUrl(url) {
@@ -662,7 +813,12 @@ function preview(items) {
662
813
  return items.slice(0, 5).join(", ") + (items.length > 5 ? ", \u2026" : "");
663
814
  }
664
815
  function openInBrowser(url) {
665
- const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
816
+ let cmd = "xdg-open";
817
+ if (process.platform === "darwin") {
818
+ cmd = "open";
819
+ } else if (process.platform === "win32") {
820
+ cmd = "start";
821
+ }
666
822
  exec(`${cmd} ${JSON.stringify(url)}`, () => {
667
823
  });
668
824
  }
@@ -677,29 +833,85 @@ function openArtifact(opts) {
677
833
  );
678
834
  }
679
835
  console.log(config.url);
680
- if (process.stdout.isTTY) openInBrowser(config.url);
836
+ if (process.stdout.isTTY) {
837
+ openInBrowser(config.url);
838
+ }
839
+ }
840
+
841
+ // src/protect.ts
842
+ import path6 from "path";
843
+ async function protect(opts) {
844
+ if (opts.off && opts.password !== void 0) {
845
+ throw new Error("Pass either --password or --off, not both.");
846
+ }
847
+ if (!opts.off && opts.password === void 0) {
848
+ throw new Error(
849
+ "Provide --password <password> to set a gate, or --off to remove it."
850
+ );
851
+ }
852
+ const password = opts.off ? null : opts.password;
853
+ if (password !== null) {
854
+ const parsed = gatePasswordSchema.safeParse(password);
855
+ if (!parsed.success) {
856
+ throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
857
+ }
858
+ }
859
+ const dir = path6.resolve(opts.dir);
860
+ const config = readConfig(dir);
861
+ if (!config) {
862
+ throw new Error(
863
+ `No artifact linked in ${dir} \u2014 run \`artifact-cafe publish\` there first (.artifactcafe/config.json not found).`
864
+ );
865
+ }
866
+ const apiUrl = process.env.ARTIFACT_CAFE_URL ?? config.apiUrl;
867
+ const token = config.publishToken ?? process.env.ARTIFACT_CAFE_TOKEN;
868
+ const client = createApiClient(apiUrl, token);
869
+ const res = await client.setProtection(config.artifactId, password);
870
+ if (opts.json) {
871
+ console.log(JSON.stringify({ protected: res.protected }, null, 2));
872
+ return;
873
+ }
874
+ console.log(
875
+ res.protected ? `
876
+ \u2713 Password protection on \u2014 ${config.url}
877
+ ` : `
878
+ \u2713 Password protection removed \u2014 ${config.url}
879
+ `
880
+ );
681
881
  }
682
882
 
683
883
  // src/index.ts
684
884
  var publish2 = defineCommand({
685
- meta: { name: "publish", description: "Publish a folder as an artifact" },
686
885
  args: {
687
- dir: { type: "positional", description: "Directory to publish", default: "." },
688
- title: { type: "string", description: "Artifact title" },
689
- entry: { type: "string", description: "Entry file (default: auto-detect)" },
690
- artifact: { type: "string", description: "Publish to an existing artifact ID" },
691
- json: { type: "boolean", description: "Machine-readable output" },
692
- "no-open": { type: "boolean", description: "Don't open the review URL" }
886
+ artifact: {
887
+ description: "Publish to an existing artifact ID",
888
+ type: "string"
889
+ },
890
+ dir: {
891
+ default: ".",
892
+ description: "Directory to publish",
893
+ type: "positional"
894
+ },
895
+ entry: { description: "Entry file (default: auto-detect)", type: "string" },
896
+ json: { description: "Machine-readable output", type: "boolean" },
897
+ "no-open": { description: "Don't open the review URL", type: "boolean" },
898
+ password: {
899
+ description: "Password-protect the artifact (min 6 chars)",
900
+ type: "string"
901
+ },
902
+ title: { description: "Artifact title", type: "string" }
693
903
  },
904
+ meta: { description: "Publish a folder as an artifact", name: "publish" },
694
905
  async run({ args }) {
695
906
  try {
696
907
  await publish({
908
+ artifact: args.artifact,
697
909
  dir: args.dir,
698
- title: args.title,
699
910
  entry: args.entry,
700
- artifact: args.artifact,
701
911
  json: Boolean(args.json),
702
- open: !args["no-open"]
912
+ open: !args["no-open"],
913
+ password: args.password,
914
+ title: args.title
703
915
  });
704
916
  } catch (err) {
705
917
  printCliError(err, Boolean(args.json));
@@ -708,12 +920,27 @@ var publish2 = defineCommand({
708
920
  }
709
921
  });
710
922
  var comments2 = defineCommand({
711
- meta: { name: "comments", description: "Pull comments for the linked artifact" },
712
923
  args: {
713
- dir: { type: "positional", description: "Published directory", default: "." },
714
- json: { type: "boolean", description: "Machine-readable output" },
715
- status: { type: "string", description: "Filter: open | resolved | all", default: "open" },
716
- version: { type: "string", description: "Filter: current | all", default: "current" }
924
+ dir: {
925
+ default: ".",
926
+ description: "Published directory",
927
+ type: "positional"
928
+ },
929
+ json: { description: "Machine-readable output", type: "boolean" },
930
+ status: {
931
+ default: "open",
932
+ description: "Filter: open | resolved | all",
933
+ type: "string"
934
+ },
935
+ version: {
936
+ default: "current",
937
+ description: "Filter: current | all",
938
+ type: "string"
939
+ }
940
+ },
941
+ meta: {
942
+ description: "Pull comments for the linked artifact",
943
+ name: "comments"
717
944
  },
718
945
  async run({ args }) {
719
946
  try {
@@ -729,11 +956,44 @@ var comments2 = defineCommand({
729
956
  }
730
957
  }
731
958
  });
959
+ var protect2 = defineCommand({
960
+ args: {
961
+ dir: {
962
+ default: ".",
963
+ description: "Published directory",
964
+ type: "positional"
965
+ },
966
+ json: { description: "Machine-readable output", type: "boolean" },
967
+ off: { description: "Remove the password gate", type: "boolean" },
968
+ password: { description: "Gate password (min 6 chars)", type: "string" }
969
+ },
970
+ meta: {
971
+ description: "Password-protect the linked artifact (or --off to remove)",
972
+ name: "protect"
973
+ },
974
+ async run({ args }) {
975
+ try {
976
+ await protect({
977
+ dir: args.dir,
978
+ json: Boolean(args.json),
979
+ off: Boolean(args.off),
980
+ password: args.password
981
+ });
982
+ } catch (err) {
983
+ printCliError(err, Boolean(args.json));
984
+ process.exit(1);
985
+ }
986
+ }
987
+ });
732
988
  var open = defineCommand({
733
- meta: { name: "open", description: "Open the linked artifact's review URL" },
734
989
  args: {
735
- dir: { type: "positional", description: "Published directory", default: "." }
990
+ dir: {
991
+ default: ".",
992
+ description: "Published directory",
993
+ type: "positional"
994
+ }
736
995
  },
996
+ meta: { description: "Open the linked artifact's review URL", name: "open" },
737
997
  run({ args }) {
738
998
  try {
739
999
  openArtifact({ dir: args.dir });
@@ -743,18 +1003,11 @@ var open = defineCommand({
743
1003
  }
744
1004
  }
745
1005
  });
746
- var login = defineCommand({
747
- meta: { name: "login", description: "Log in (optional; needed for owner actions)" },
748
- async run() {
749
- console.error("login not implemented yet");
750
- process.exit(1);
751
- }
752
- });
753
1006
  var main = defineCommand({
754
1007
  meta: {
755
- name: "artifact-cafe",
756
- description: "Publish AI-generated artifacts for review \u2014 https://artifact.cafe"
1008
+ description: "Publish AI-generated artifacts for review \u2014 https://artifact.cafe",
1009
+ name: "artifact-cafe"
757
1010
  },
758
- subCommands: { publish: publish2, comments: comments2, open, login }
1011
+ subCommands: { comments: comments2, open, protect: protect2, publish: publish2 }
759
1012
  });
760
1013
  runMain(main);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artifact-cafe",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Publish AI-generated artifacts for review on artifact.cafe",
5
5
  "type": "module",
6
6
  "bin": {