artifact-cafe 0.0.2 → 0.0.3
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 +631 -197
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,102 +6,149 @@ import { defineCommand, runMain } from "citty";
|
|
|
6
6
|
// src/comments.ts
|
|
7
7
|
import path2 from "path";
|
|
8
8
|
|
|
9
|
-
// ../shared/src/
|
|
9
|
+
// ../shared/src/account.ts
|
|
10
10
|
import { z } from "zod";
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
var createApiKeySchema = z.object({
|
|
12
|
+
name: z.string().trim().min(1).max(60).optional()
|
|
13
|
+
});
|
|
14
|
+
var createdApiKeySchema = z.object({
|
|
15
|
+
createdAt: z.string(),
|
|
16
|
+
id: z.string(),
|
|
17
|
+
key: z.string(),
|
|
18
|
+
keyPrefix: z.string(),
|
|
19
|
+
name: z.string()
|
|
20
|
+
});
|
|
21
|
+
var apiKeySummarySchema = z.object({
|
|
22
|
+
createdAt: z.string(),
|
|
23
|
+
id: z.string(),
|
|
24
|
+
keyPrefix: z.string(),
|
|
25
|
+
lastUsedAt: z.string().nullable(),
|
|
26
|
+
name: z.string()
|
|
27
|
+
});
|
|
28
|
+
var apiKeyListResponseSchema = z.object({
|
|
29
|
+
keys: z.array(apiKeySummarySchema)
|
|
30
|
+
});
|
|
31
|
+
var revokeApiKeyResponseSchema = z.object({
|
|
32
|
+
revoked: z.literal(true)
|
|
33
|
+
});
|
|
34
|
+
var meResponseSchema = z.object({
|
|
35
|
+
// Best-effort profile field from our own user table; null when unknown.
|
|
36
|
+
email: z.string().nullable(),
|
|
37
|
+
// Present when the request was authenticated with an API key.
|
|
38
|
+
keyName: z.string().optional(),
|
|
39
|
+
userId: z.string()
|
|
40
|
+
});
|
|
41
|
+
var artifactListItemSchema = z.object({
|
|
42
|
+
createdAt: z.string(),
|
|
43
|
+
currentVersionNumber: z.number().int().positive().nullable(),
|
|
44
|
+
id: z.string(),
|
|
45
|
+
openCommentCount: z.number().int().nonnegative(),
|
|
46
|
+
slug: z.string(),
|
|
47
|
+
title: z.string(),
|
|
48
|
+
updatedAt: z.string(),
|
|
49
|
+
url: z.string().url(),
|
|
50
|
+
versionCount: z.number().int().nonnegative()
|
|
51
|
+
});
|
|
52
|
+
var artifactListResponseSchema = z.object({
|
|
53
|
+
artifacts: z.array(artifactListItemSchema)
|
|
16
54
|
});
|
|
17
|
-
|
|
55
|
+
|
|
56
|
+
// ../shared/src/comments.ts
|
|
57
|
+
import { z as z2 } from "zod";
|
|
58
|
+
var elementSnapshotSchema = z2.object({
|
|
59
|
+
classes: z2.string().max(256).optional(),
|
|
60
|
+
id: z2.string().max(256).optional(),
|
|
61
|
+
tag: z2.string().min(1).max(128),
|
|
62
|
+
text: z2.string().max(200).optional()
|
|
63
|
+
});
|
|
64
|
+
var pointAnchorSchema = z2.object({
|
|
18
65
|
element: elementSnapshotSchema.optional(),
|
|
19
66
|
// elementPath is the primary anchor; x/y are offsets within the
|
|
20
67
|
// element's box, normalized 0..1 (SPEC.md §8.1).
|
|
21
|
-
elementPath:
|
|
22
|
-
type:
|
|
23
|
-
viewportHeight:
|
|
24
|
-
viewportWidth:
|
|
25
|
-
x:
|
|
26
|
-
y:
|
|
27
|
-
});
|
|
28
|
-
var textAnchorSchema =
|
|
29
|
-
elementPath:
|
|
30
|
-
endOffset:
|
|
31
|
-
prefix:
|
|
32
|
-
quote:
|
|
33
|
-
startOffset:
|
|
34
|
-
suffix:
|
|
35
|
-
type:
|
|
36
|
-
});
|
|
37
|
-
var anchorSchema =
|
|
68
|
+
elementPath: z2.string().max(2048),
|
|
69
|
+
type: z2.literal("point"),
|
|
70
|
+
viewportHeight: z2.number().int().positive().max(1e5),
|
|
71
|
+
viewportWidth: z2.number().int().positive().max(1e5),
|
|
72
|
+
x: z2.number().min(0).max(1),
|
|
73
|
+
y: z2.number().min(0).max(1)
|
|
74
|
+
});
|
|
75
|
+
var textAnchorSchema = z2.object({
|
|
76
|
+
elementPath: z2.string().max(2048),
|
|
77
|
+
endOffset: z2.number().int().nonnegative().max(1e8),
|
|
78
|
+
prefix: z2.string().max(200).optional(),
|
|
79
|
+
quote: z2.string().min(1).max(1e3),
|
|
80
|
+
startOffset: z2.number().int().nonnegative().max(1e8),
|
|
81
|
+
suffix: z2.string().max(200).optional(),
|
|
82
|
+
type: z2.literal("text")
|
|
83
|
+
});
|
|
84
|
+
var anchorSchema = z2.discriminatedUnion("type", [
|
|
38
85
|
pointAnchorSchema,
|
|
39
86
|
textAnchorSchema
|
|
40
87
|
]);
|
|
41
|
-
var guestSchema =
|
|
42
|
-
displayName:
|
|
43
|
-
guestId:
|
|
88
|
+
var guestSchema = z2.object({
|
|
89
|
+
displayName: z2.string().min(1).max(64),
|
|
90
|
+
guestId: z2.string().min(16)
|
|
44
91
|
});
|
|
45
|
-
var createCommentThreadSchema =
|
|
92
|
+
var createCommentThreadSchema = z2.object({
|
|
46
93
|
anchor: anchorSchema,
|
|
47
|
-
body:
|
|
94
|
+
body: z2.string().min(1).max(1e4),
|
|
48
95
|
guest: guestSchema.optional(),
|
|
49
|
-
versionId:
|
|
96
|
+
versionId: z2.string()
|
|
50
97
|
});
|
|
51
|
-
var createCommentMessageSchema =
|
|
52
|
-
body:
|
|
98
|
+
var createCommentMessageSchema = z2.object({
|
|
99
|
+
body: z2.string().min(1).max(1e4),
|
|
53
100
|
guest: guestSchema.optional()
|
|
54
101
|
});
|
|
55
|
-
var threadActionSchema =
|
|
102
|
+
var threadActionSchema = z2.object({
|
|
56
103
|
guest: guestSchema.optional()
|
|
57
104
|
});
|
|
58
|
-
var commentAuthorSchema =
|
|
59
|
-
displayName:
|
|
60
|
-
guestId:
|
|
105
|
+
var commentAuthorSchema = z2.object({
|
|
106
|
+
displayName: z2.string(),
|
|
107
|
+
guestId: z2.string().nullable()
|
|
61
108
|
});
|
|
62
|
-
var commentMessageSchema =
|
|
109
|
+
var commentMessageSchema = z2.object({
|
|
63
110
|
author: commentAuthorSchema,
|
|
64
|
-
body:
|
|
65
|
-
createdAt:
|
|
66
|
-
id:
|
|
111
|
+
body: z2.string(),
|
|
112
|
+
createdAt: z2.string(),
|
|
113
|
+
id: z2.string()
|
|
67
114
|
});
|
|
68
|
-
var commentThreadSchema =
|
|
115
|
+
var commentThreadSchema = z2.object({
|
|
69
116
|
anchor: anchorSchema,
|
|
70
|
-
artifactId:
|
|
71
|
-
createdAt:
|
|
117
|
+
artifactId: z2.string(),
|
|
118
|
+
createdAt: z2.string(),
|
|
72
119
|
createdBy: commentAuthorSchema,
|
|
73
|
-
id:
|
|
74
|
-
messages:
|
|
75
|
-
status:
|
|
76
|
-
versionId:
|
|
120
|
+
id: z2.string(),
|
|
121
|
+
messages: z2.array(commentMessageSchema),
|
|
122
|
+
status: z2.enum(["open", "resolved"]),
|
|
123
|
+
versionId: z2.string()
|
|
77
124
|
});
|
|
78
|
-
var commentThreadListSchema =
|
|
79
|
-
threads:
|
|
125
|
+
var commentThreadListSchema = z2.object({
|
|
126
|
+
threads: z2.array(commentThreadSchema)
|
|
80
127
|
});
|
|
81
|
-
var commentExportQuerySchema =
|
|
82
|
-
status:
|
|
83
|
-
version:
|
|
128
|
+
var commentExportQuerySchema = z2.object({
|
|
129
|
+
status: z2.enum(["open", "resolved", "all"]).default("open"),
|
|
130
|
+
version: z2.enum(["current", "all"]).default("current")
|
|
84
131
|
});
|
|
85
|
-
var commentExportReplySchema =
|
|
86
|
-
author:
|
|
87
|
-
body:
|
|
88
|
-
createdAt:
|
|
132
|
+
var commentExportReplySchema = z2.object({
|
|
133
|
+
author: z2.string(),
|
|
134
|
+
body: z2.string(),
|
|
135
|
+
createdAt: z2.string()
|
|
89
136
|
});
|
|
90
|
-
var commentExportThreadSchema =
|
|
137
|
+
var commentExportThreadSchema = z2.object({
|
|
91
138
|
anchor: anchorSchema,
|
|
92
|
-
author:
|
|
93
|
-
body:
|
|
94
|
-
createdAt:
|
|
95
|
-
id:
|
|
96
|
-
replies:
|
|
97
|
-
status:
|
|
98
|
-
versionNumber:
|
|
139
|
+
author: z2.string(),
|
|
140
|
+
body: z2.string(),
|
|
141
|
+
createdAt: z2.string(),
|
|
142
|
+
id: z2.string(),
|
|
143
|
+
replies: z2.array(commentExportReplySchema),
|
|
144
|
+
status: z2.enum(["open", "resolved"]),
|
|
145
|
+
versionNumber: z2.number().int().positive()
|
|
99
146
|
});
|
|
100
|
-
var commentExportSchema =
|
|
101
|
-
artifactId:
|
|
102
|
-
comments:
|
|
147
|
+
var commentExportSchema = z2.object({
|
|
148
|
+
artifactId: z2.string(),
|
|
149
|
+
comments: z2.array(commentExportThreadSchema),
|
|
103
150
|
// The artifact's current version (null before the first finalize).
|
|
104
|
-
versionNumber:
|
|
151
|
+
versionNumber: z2.number().int().positive().nullable()
|
|
105
152
|
});
|
|
106
153
|
function elementLabel(el) {
|
|
107
154
|
if (el.id) {
|
|
@@ -112,55 +159,55 @@ function elementLabel(el) {
|
|
|
112
159
|
}
|
|
113
160
|
|
|
114
161
|
// ../shared/src/errors.ts
|
|
115
|
-
import { z as
|
|
116
|
-
var apiErrorSchema =
|
|
117
|
-
code:
|
|
118
|
-
docsUrl:
|
|
119
|
-
message:
|
|
120
|
-
retryAfter:
|
|
162
|
+
import { z as z3 } from "zod";
|
|
163
|
+
var apiErrorSchema = z3.object({
|
|
164
|
+
code: z3.string(),
|
|
165
|
+
docsUrl: z3.string().url(),
|
|
166
|
+
message: z3.string(),
|
|
167
|
+
retryAfter: z3.number().int().positive().optional()
|
|
121
168
|
});
|
|
122
169
|
|
|
123
170
|
// ../shared/src/owner.ts
|
|
124
|
-
import { z as
|
|
171
|
+
import { z as z4 } from "zod";
|
|
125
172
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
|
|
126
|
-
var claimResponseSchema =
|
|
127
|
-
artifactId:
|
|
128
|
-
slug:
|
|
129
|
-
title:
|
|
130
|
-
url:
|
|
131
|
-
});
|
|
132
|
-
var updateArtifactSchema =
|
|
133
|
-
slug:
|
|
173
|
+
var claimResponseSchema = z4.object({
|
|
174
|
+
artifactId: z4.string(),
|
|
175
|
+
slug: z4.string(),
|
|
176
|
+
title: z4.string(),
|
|
177
|
+
url: z4.string().url()
|
|
178
|
+
});
|
|
179
|
+
var updateArtifactSchema = z4.object({
|
|
180
|
+
slug: z4.string().min(3).max(63).regex(SLUG_PATTERN, {
|
|
134
181
|
message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
135
182
|
}).optional(),
|
|
136
|
-
title:
|
|
183
|
+
title: z4.string().min(1).max(200).optional()
|
|
137
184
|
}).refine((body) => body.title !== void 0 || body.slug !== void 0, {
|
|
138
185
|
message: "provide at least one of title, slug"
|
|
139
186
|
});
|
|
140
|
-
var deleteArtifactResponseSchema =
|
|
141
|
-
deleted:
|
|
187
|
+
var deleteArtifactResponseSchema = z4.object({
|
|
188
|
+
deleted: z4.literal(true)
|
|
142
189
|
});
|
|
143
190
|
|
|
144
191
|
// ../shared/src/protection.ts
|
|
145
|
-
import { z as
|
|
192
|
+
import { z as z5 } from "zod";
|
|
146
193
|
var PASSWORD_MIN = 6;
|
|
147
194
|
var PASSWORD_MAX = 128;
|
|
148
|
-
var gatePasswordSchema =
|
|
149
|
-
var setProtectionSchema =
|
|
195
|
+
var gatePasswordSchema = z5.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
|
|
196
|
+
var setProtectionSchema = z5.object({
|
|
150
197
|
password: gatePasswordSchema.nullable()
|
|
151
198
|
});
|
|
152
|
-
var protectionResponseSchema =
|
|
153
|
-
protected:
|
|
199
|
+
var protectionResponseSchema = z5.object({
|
|
200
|
+
protected: z5.boolean()
|
|
154
201
|
});
|
|
155
|
-
var unlockSchema =
|
|
156
|
-
password:
|
|
202
|
+
var unlockSchema = z5.object({
|
|
203
|
+
password: z5.string().min(1).max(1024)
|
|
157
204
|
});
|
|
158
|
-
var unlockResponseSchema =
|
|
159
|
-
unlocked:
|
|
205
|
+
var unlockResponseSchema = z5.object({
|
|
206
|
+
unlocked: z5.literal(true)
|
|
160
207
|
});
|
|
161
208
|
|
|
162
209
|
// ../shared/src/publish.ts
|
|
163
|
-
import { z as
|
|
210
|
+
import { z as z6 } from "zod";
|
|
164
211
|
function isSafeArtifactPath(p) {
|
|
165
212
|
if (p.length === 0 || p.length > 1024) {
|
|
166
213
|
return false;
|
|
@@ -173,95 +220,128 @@ function isSafeArtifactPath(p) {
|
|
|
173
220
|
}
|
|
174
221
|
return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
|
|
175
222
|
}
|
|
176
|
-
var manifestFileSchema =
|
|
177
|
-
hash:
|
|
178
|
-
mimeType:
|
|
179
|
-
path:
|
|
223
|
+
var manifestFileSchema = z6.object({
|
|
224
|
+
hash: z6.string().regex(/^sha256_[a-f0-9]{64}$/),
|
|
225
|
+
mimeType: z6.string().regex(/^[-\w.+]+\/[-\w.+]+$/, { message: "invalid MIME type" }),
|
|
226
|
+
path: z6.string().min(1).max(1024).refine(isSafeArtifactPath, {
|
|
180
227
|
message: "path must be a safe relative path"
|
|
181
228
|
}),
|
|
182
|
-
size:
|
|
229
|
+
size: z6.number().int().positive()
|
|
183
230
|
});
|
|
184
|
-
var publishSourceSchema =
|
|
185
|
-
agent:
|
|
186
|
-
runId:
|
|
187
|
-
type:
|
|
231
|
+
var publishSourceSchema = z6.object({
|
|
232
|
+
agent: z6.string().optional(),
|
|
233
|
+
runId: z6.string().optional(),
|
|
234
|
+
type: z6.literal("cli")
|
|
188
235
|
});
|
|
189
|
-
var createPublishSessionSchema =
|
|
190
|
-
artifactId:
|
|
191
|
-
entryPath:
|
|
192
|
-
files:
|
|
236
|
+
var createPublishSessionSchema = z6.object({
|
|
237
|
+
artifactId: z6.string().nullable(),
|
|
238
|
+
entryPath: z6.string().optional(),
|
|
239
|
+
files: z6.array(manifestFileSchema).min(1).max(1e3),
|
|
193
240
|
source: publishSourceSchema,
|
|
194
|
-
title:
|
|
195
|
-
});
|
|
196
|
-
var publishSessionResponseSchema =
|
|
197
|
-
artifactId:
|
|
198
|
-
expiresAt:
|
|
199
|
-
filesToUpload:
|
|
200
|
-
|
|
201
|
-
method:
|
|
202
|
-
path:
|
|
203
|
-
uploadUrl:
|
|
241
|
+
title: z6.string().max(200).optional()
|
|
242
|
+
});
|
|
243
|
+
var publishSessionResponseSchema = z6.object({
|
|
244
|
+
artifactId: z6.string(),
|
|
245
|
+
expiresAt: z6.string(),
|
|
246
|
+
filesToUpload: z6.array(
|
|
247
|
+
z6.object({
|
|
248
|
+
method: z6.literal("PUT"),
|
|
249
|
+
path: z6.string(),
|
|
250
|
+
uploadUrl: z6.string().url()
|
|
204
251
|
})
|
|
205
252
|
),
|
|
206
|
-
publishSessionId:
|
|
207
|
-
skippedFiles:
|
|
208
|
-
versionId:
|
|
253
|
+
publishSessionId: z6.string(),
|
|
254
|
+
skippedFiles: z6.array(z6.string()),
|
|
255
|
+
versionId: z6.string()
|
|
209
256
|
});
|
|
210
|
-
var finalizeResponseSchema =
|
|
211
|
-
artifactId:
|
|
257
|
+
var finalizeResponseSchema = z6.object({
|
|
258
|
+
artifactId: z6.string(),
|
|
259
|
+
// API-key publishes create the artifact already claimed (SPEC §5.4) —
|
|
260
|
+
// the CLI uses this to tell "claim URL pending" from "already yours".
|
|
261
|
+
claimed: z6.boolean(),
|
|
212
262
|
// Returned only on first publish of a new artifact (one-time).
|
|
213
|
-
claimUrl:
|
|
214
|
-
publishToken:
|
|
215
|
-
url:
|
|
216
|
-
versionId:
|
|
217
|
-
versionNumber:
|
|
218
|
-
});
|
|
219
|
-
var artifactResponseSchema =
|
|
220
|
-
claimed:
|
|
221
|
-
currentVersion:
|
|
222
|
-
entryUrl:
|
|
223
|
-
id:
|
|
224
|
-
versionNumber:
|
|
263
|
+
claimUrl: z6.string().url().optional(),
|
|
264
|
+
publishToken: z6.string().optional(),
|
|
265
|
+
url: z6.string().url(),
|
|
266
|
+
versionId: z6.string(),
|
|
267
|
+
versionNumber: z6.number().int().positive()
|
|
268
|
+
});
|
|
269
|
+
var artifactResponseSchema = z6.object({
|
|
270
|
+
claimed: z6.boolean(),
|
|
271
|
+
currentVersion: z6.object({
|
|
272
|
+
entryUrl: z6.string().url(),
|
|
273
|
+
id: z6.string(),
|
|
274
|
+
versionNumber: z6.number().int().positive()
|
|
225
275
|
}).nullable(),
|
|
226
|
-
id:
|
|
227
|
-
slug:
|
|
228
|
-
title:
|
|
276
|
+
id: z6.string(),
|
|
277
|
+
slug: z6.string(),
|
|
278
|
+
title: z6.string()
|
|
229
279
|
});
|
|
230
280
|
|
|
231
281
|
// ../shared/src/report.ts
|
|
232
|
-
import { z as
|
|
233
|
-
var reportRequestSchema =
|
|
234
|
-
reason:
|
|
282
|
+
import { z as z7 } from "zod";
|
|
283
|
+
var reportRequestSchema = z7.object({
|
|
284
|
+
reason: z7.string().max(1e3).optional()
|
|
235
285
|
});
|
|
236
|
-
var reportResponseSchema =
|
|
237
|
-
reported:
|
|
286
|
+
var reportResponseSchema = z7.object({
|
|
287
|
+
reported: z7.literal(true)
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// ../shared/src/stats.ts
|
|
291
|
+
import { z as z8 } from "zod";
|
|
292
|
+
var avgViewSecondsSchema = z8.number().int().nonnegative().nullable();
|
|
293
|
+
var versionViewStatsSchema = z8.object({
|
|
294
|
+
avgViewSeconds: avgViewSecondsSchema,
|
|
295
|
+
// Views from inside the review shell's iframe vs. direct visits to
|
|
296
|
+
// the render URL. views = embeddedViews + directViews.
|
|
297
|
+
directViews: z8.number().int().nonnegative(),
|
|
298
|
+
embeddedViews: z8.number().int().nonnegative(),
|
|
299
|
+
// Distinct hashed viewer IPs across the version's lifetime.
|
|
300
|
+
uniqueViewers: z8.number().int().nonnegative(),
|
|
301
|
+
versionNumber: z8.number().int().positive(),
|
|
302
|
+
views: z8.number().int().nonnegative()
|
|
303
|
+
});
|
|
304
|
+
var dailyViewsSchema = z8.object({
|
|
305
|
+
avgViewSeconds: avgViewSecondsSchema,
|
|
306
|
+
// UTC day, YYYY-MM-DD.
|
|
307
|
+
day: z8.string(),
|
|
308
|
+
uniqueViewers: z8.number().int().nonnegative(),
|
|
309
|
+
views: z8.number().int().nonnegative()
|
|
310
|
+
});
|
|
311
|
+
var artifactStatsResponseSchema = z8.object({
|
|
312
|
+
avgViewSeconds: avgViewSecondsSchema,
|
|
313
|
+
daily: z8.array(dailyViewsSchema),
|
|
314
|
+
// Distinct hashed viewer IPs across all versions and days.
|
|
315
|
+
totalUniqueViewers: z8.number().int().nonnegative(),
|
|
316
|
+
totalViews: z8.number().int().nonnegative(),
|
|
317
|
+
versions: z8.array(versionViewStatsSchema)
|
|
238
318
|
});
|
|
239
319
|
|
|
240
320
|
// ../shared/src/versions.ts
|
|
241
|
-
import { z as
|
|
242
|
-
var versionSourceSchema =
|
|
243
|
-
agentName:
|
|
244
|
-
type:
|
|
321
|
+
import { z as z9 } from "zod";
|
|
322
|
+
var versionSourceSchema = z9.object({
|
|
323
|
+
agentName: z9.string().nullable(),
|
|
324
|
+
type: z9.string()
|
|
245
325
|
});
|
|
246
|
-
var versionChangesSchema =
|
|
247
|
-
added:
|
|
248
|
-
changed:
|
|
249
|
-
removed:
|
|
326
|
+
var versionChangesSchema = z9.object({
|
|
327
|
+
added: z9.array(z9.string()),
|
|
328
|
+
changed: z9.array(z9.string()),
|
|
329
|
+
removed: z9.array(z9.string())
|
|
250
330
|
});
|
|
251
|
-
var versionSummarySchema =
|
|
331
|
+
var versionSummarySchema = z9.object({
|
|
252
332
|
// Absent for the first version — there is no previous version to diff against.
|
|
253
333
|
changes: versionChangesSchema.nullable(),
|
|
254
|
-
createdAt:
|
|
255
|
-
entryUrl:
|
|
256
|
-
fileCount:
|
|
257
|
-
id:
|
|
258
|
-
isCurrent:
|
|
334
|
+
createdAt: z9.string(),
|
|
335
|
+
entryUrl: z9.string().url(),
|
|
336
|
+
fileCount: z9.number().int().nonnegative(),
|
|
337
|
+
id: z9.string(),
|
|
338
|
+
isCurrent: z9.boolean(),
|
|
259
339
|
source: versionSourceSchema,
|
|
260
|
-
totalSizeBytes:
|
|
261
|
-
versionNumber:
|
|
340
|
+
totalSizeBytes: z9.number().int().nonnegative(),
|
|
341
|
+
versionNumber: z9.number().int().positive()
|
|
262
342
|
});
|
|
263
|
-
var versionListResponseSchema =
|
|
264
|
-
versions:
|
|
343
|
+
var versionListResponseSchema = z9.object({
|
|
344
|
+
versions: z9.array(versionSummarySchema)
|
|
265
345
|
});
|
|
266
346
|
|
|
267
347
|
// src/api.ts
|
|
@@ -306,8 +386,8 @@ async function request(url, init, parse) {
|
|
|
306
386
|
}
|
|
307
387
|
return parse(body);
|
|
308
388
|
}
|
|
309
|
-
function createApiClient(
|
|
310
|
-
const base =
|
|
389
|
+
function createApiClient(apiUrl2, publishToken) {
|
|
390
|
+
const base = apiUrl2.replace(/\/$/, "");
|
|
311
391
|
const headers = {
|
|
312
392
|
"content-type": "application/json"
|
|
313
393
|
};
|
|
@@ -347,6 +427,24 @@ function createApiClient(apiUrl, publishToken) {
|
|
|
347
427
|
(d) => artifactResponseSchema.parse(d)
|
|
348
428
|
);
|
|
349
429
|
},
|
|
430
|
+
// SPEC §6.18 — everything owned by the account this client's API
|
|
431
|
+
// key (or session) belongs to.
|
|
432
|
+
async listArtifacts() {
|
|
433
|
+
return request(
|
|
434
|
+
`${base}/api/v1/artifacts`,
|
|
435
|
+
{ headers, method: "GET" },
|
|
436
|
+
(d) => artifactListResponseSchema.parse(d)
|
|
437
|
+
);
|
|
438
|
+
},
|
|
439
|
+
// SPEC §6.14 — who the presented credential belongs to. Used by
|
|
440
|
+
// `login` to verify a pasted key and by `whoami`.
|
|
441
|
+
async me() {
|
|
442
|
+
return request(
|
|
443
|
+
`${base}/api/v1/me`,
|
|
444
|
+
{ headers, method: "GET" },
|
|
445
|
+
(d) => meResponseSchema.parse(d)
|
|
446
|
+
);
|
|
447
|
+
},
|
|
350
448
|
// SPEC §6.16 — set a password (string) or clear it (null). Authorized
|
|
351
449
|
// by the publish token this client was created with.
|
|
352
450
|
async setProtection(artifactId, password) {
|
|
@@ -383,13 +481,13 @@ import {
|
|
|
383
481
|
writeFileSync
|
|
384
482
|
} from "fs";
|
|
385
483
|
import path from "path";
|
|
386
|
-
import { z as
|
|
387
|
-
var configSchema =
|
|
388
|
-
apiUrl:
|
|
389
|
-
artifactId:
|
|
390
|
-
publishToken:
|
|
391
|
-
slug:
|
|
392
|
-
url:
|
|
484
|
+
import { z as z10 } from "zod";
|
|
485
|
+
var configSchema = z10.object({
|
|
486
|
+
apiUrl: z10.string(),
|
|
487
|
+
artifactId: z10.string(),
|
|
488
|
+
publishToken: z10.string(),
|
|
489
|
+
slug: z10.string(),
|
|
490
|
+
url: z10.string()
|
|
393
491
|
});
|
|
394
492
|
var CONFIG_DIR = ".artifactcafe";
|
|
395
493
|
var CONFIG_FILE = "config.json";
|
|
@@ -458,8 +556,8 @@ async function comments(opts) {
|
|
|
458
556
|
`No artifact linked in ${dir} \u2014 run \`artifact-cafe publish\` there first (.artifactcafe/config.json not found).`
|
|
459
557
|
);
|
|
460
558
|
}
|
|
461
|
-
const
|
|
462
|
-
const client = createApiClient(
|
|
559
|
+
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config.apiUrl;
|
|
560
|
+
const client = createApiClient(apiUrl2);
|
|
463
561
|
const data = await client.exportComments(config.artifactId, {
|
|
464
562
|
status,
|
|
465
563
|
version
|
|
@@ -543,18 +641,131 @@ function printCliError(err, json) {
|
|
|
543
641
|
}
|
|
544
642
|
}
|
|
545
643
|
|
|
546
|
-
// src/
|
|
547
|
-
import
|
|
644
|
+
// src/credentials.ts
|
|
645
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
646
|
+
import { homedir } from "os";
|
|
647
|
+
import path3 from "path";
|
|
648
|
+
import { z as z11 } from "zod";
|
|
649
|
+
var DEFAULT_API_URL = "https://artifact.cafe";
|
|
650
|
+
var credentialsSchema = z11.object({
|
|
651
|
+
keys: z11.record(z11.string(), z11.string()),
|
|
652
|
+
version: z11.literal(1)
|
|
653
|
+
});
|
|
654
|
+
function credentialsPath() {
|
|
655
|
+
return path3.join(homedir(), ".artifactcafe", "credentials");
|
|
656
|
+
}
|
|
657
|
+
function apiOrigin(apiUrl2) {
|
|
658
|
+
return new URL(apiUrl2).origin;
|
|
659
|
+
}
|
|
660
|
+
function readCredentials() {
|
|
661
|
+
const file = credentialsPath();
|
|
662
|
+
if (!existsSync2(file)) {
|
|
663
|
+
return { keys: {}, version: 1 };
|
|
664
|
+
}
|
|
665
|
+
try {
|
|
666
|
+
return credentialsSchema.parse(JSON.parse(readFileSync2(file, "utf8")));
|
|
667
|
+
} catch {
|
|
668
|
+
return { keys: {}, version: 1 };
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function writeCredentials(credentials) {
|
|
672
|
+
const file = credentialsPath();
|
|
673
|
+
mkdirSync2(path3.dirname(file), { mode: 448, recursive: true });
|
|
674
|
+
writeFileSync2(file, `${JSON.stringify(credentials, null, 2)}
|
|
675
|
+
`, {
|
|
676
|
+
mode: 384
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
function storedApiKey(apiUrl2) {
|
|
680
|
+
return readCredentials().keys[apiOrigin(apiUrl2)] ?? null;
|
|
681
|
+
}
|
|
682
|
+
function storeApiKey(apiUrl2, key) {
|
|
683
|
+
const credentials = readCredentials();
|
|
684
|
+
credentials.keys[apiOrigin(apiUrl2)] = key;
|
|
685
|
+
writeCredentials(credentials);
|
|
686
|
+
}
|
|
687
|
+
function removeApiKey(apiUrl2) {
|
|
688
|
+
const credentials = readCredentials();
|
|
689
|
+
const origin = apiOrigin(apiUrl2);
|
|
690
|
+
if (!(origin in credentials.keys)) {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
delete credentials.keys[origin];
|
|
694
|
+
writeCredentials(credentials);
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
function resolveApiKey(apiUrl2, flag) {
|
|
698
|
+
return flag ?? process.env.ARTIFACT_CAFE_API_KEY ?? storedApiKey(apiUrl2) ?? void 0;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// src/list.ts
|
|
702
|
+
async function list(opts) {
|
|
703
|
+
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
704
|
+
/\/$/,
|
|
705
|
+
""
|
|
706
|
+
);
|
|
707
|
+
const key = resolveApiKey(apiUrl2, opts.apiKey);
|
|
708
|
+
if (!key) {
|
|
709
|
+
throw new Error(
|
|
710
|
+
`Listing your artifacts needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
const data = await createApiClient(apiUrl2, key).listArtifacts();
|
|
714
|
+
if (opts.json) {
|
|
715
|
+
console.log(JSON.stringify(data, null, 2));
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (data.artifacts.length === 0) {
|
|
719
|
+
console.log(
|
|
720
|
+
"No artifacts in your account yet \u2014 publish while logged in, or claim one via its claim link."
|
|
721
|
+
);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const plural = data.artifacts.length === 1 ? "" : "s";
|
|
725
|
+
console.log(`${data.artifacts.length} artifact${plural}
|
|
726
|
+
`);
|
|
727
|
+
for (const artifact of data.artifacts) {
|
|
728
|
+
printArtifact(artifact);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function printArtifact(a) {
|
|
732
|
+
const version = a.currentVersionNumber === null ? "no version" : `v${a.currentVersionNumber}`;
|
|
733
|
+
const open2 = a.openCommentCount === 0 ? "no open comments" : `${a.openCommentCount} open comment${a.openCommentCount === 1 ? "" : "s"}`;
|
|
734
|
+
const marker = a.openCommentCount > 0 ? "\u25CF" : "\u25CB";
|
|
735
|
+
console.log(`${marker} ${a.title}`);
|
|
736
|
+
console.log(` ${version} \xB7 ${open2} \xB7 updated ${relativeTime(a.updatedAt)}`);
|
|
737
|
+
console.log(` ${a.url}
|
|
738
|
+
`);
|
|
739
|
+
}
|
|
740
|
+
function relativeTime(iso) {
|
|
741
|
+
const seconds = Math.max(0, (Date.now() - new Date(iso).getTime()) / 1e3);
|
|
742
|
+
if (seconds < 60) {
|
|
743
|
+
return "just now";
|
|
744
|
+
}
|
|
745
|
+
if (seconds < 3600) {
|
|
746
|
+
return `${Math.floor(seconds / 60)}m ago`;
|
|
747
|
+
}
|
|
748
|
+
if (seconds < 86400) {
|
|
749
|
+
return `${Math.floor(seconds / 3600)}h ago`;
|
|
750
|
+
}
|
|
751
|
+
if (seconds < 30 * 86400) {
|
|
752
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
753
|
+
}
|
|
754
|
+
return iso.slice(0, 10);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/login.ts
|
|
758
|
+
import { createInterface } from "readline";
|
|
548
759
|
|
|
549
760
|
// src/publish.ts
|
|
550
761
|
import { exec } from "child_process";
|
|
551
|
-
import { readFileSync as
|
|
552
|
-
import
|
|
762
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
763
|
+
import path5 from "path";
|
|
553
764
|
|
|
554
765
|
// src/manifest.ts
|
|
555
766
|
import { createHash } from "crypto";
|
|
556
|
-
import { readdirSync, readFileSync as
|
|
557
|
-
import
|
|
767
|
+
import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
768
|
+
import path4 from "path";
|
|
558
769
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".artifactcafe"]);
|
|
559
770
|
var IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store"]);
|
|
560
771
|
function isIgnoredFile(name) {
|
|
@@ -603,7 +814,7 @@ var MIME_TYPES = {
|
|
|
603
814
|
".xml": "application/xml"
|
|
604
815
|
};
|
|
605
816
|
function mimeFor(filePath) {
|
|
606
|
-
return MIME_TYPES[
|
|
817
|
+
return MIME_TYPES[path4.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
607
818
|
}
|
|
608
819
|
function scanDirectory(dir) {
|
|
609
820
|
const files = [];
|
|
@@ -611,7 +822,7 @@ function scanDirectory(dir) {
|
|
|
611
822
|
function walk(current, relPrefix) {
|
|
612
823
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
613
824
|
const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
614
|
-
const abs =
|
|
825
|
+
const abs = path4.join(current, entry.name);
|
|
615
826
|
if (entry.isSymbolicLink()) {
|
|
616
827
|
skipped.push(rel);
|
|
617
828
|
continue;
|
|
@@ -636,7 +847,7 @@ function scanDirectory(dir) {
|
|
|
636
847
|
skipped.push(rel);
|
|
637
848
|
continue;
|
|
638
849
|
}
|
|
639
|
-
const content =
|
|
850
|
+
const content = readFileSync3(abs);
|
|
640
851
|
files.push({
|
|
641
852
|
absPath: abs,
|
|
642
853
|
hash: `sha256_${createHash("sha256").update(content).digest("hex")}`,
|
|
@@ -675,10 +886,9 @@ function resolveEntry(files, explicit) {
|
|
|
675
886
|
}
|
|
676
887
|
|
|
677
888
|
// src/publish.ts
|
|
678
|
-
var DEFAULT_API_URL = "https://artifact.cafe";
|
|
679
889
|
var UPLOAD_CONCURRENCY = 6;
|
|
680
890
|
async function publish(opts) {
|
|
681
|
-
const dir =
|
|
891
|
+
const dir = path5.resolve(opts.dir);
|
|
682
892
|
const config = readConfig(dir);
|
|
683
893
|
if (opts.password !== void 0) {
|
|
684
894
|
const parsed = gatePasswordSchema.safeParse(opts.password);
|
|
@@ -688,7 +898,8 @@ async function publish(opts) {
|
|
|
688
898
|
}
|
|
689
899
|
const artifactId = opts.artifact ?? config?.artifactId ?? null;
|
|
690
900
|
const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
|
|
691
|
-
const
|
|
901
|
+
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
|
|
902
|
+
const apiKey = resolveApiKey(apiUrl2, opts.apiKey);
|
|
692
903
|
const { files, skipped } = scanDirectory(dir);
|
|
693
904
|
if (files.length === 0) {
|
|
694
905
|
throw new Error(`Nothing to publish in ${dir} (after ignore rules).`);
|
|
@@ -702,8 +913,7 @@ async function publish(opts) {
|
|
|
702
913
|
if (skipped.length) {
|
|
703
914
|
log(` ignoring ${skipped.length} item(s): ${preview(skipped)}`);
|
|
704
915
|
}
|
|
705
|
-
const
|
|
706
|
-
const session = await client.createSession({
|
|
916
|
+
const sessionBody = {
|
|
707
917
|
artifactId,
|
|
708
918
|
entryPath,
|
|
709
919
|
files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
|
|
@@ -713,7 +923,21 @@ async function publish(opts) {
|
|
|
713
923
|
type: "cli"
|
|
714
924
|
},
|
|
715
925
|
title: opts.title
|
|
716
|
-
}
|
|
926
|
+
};
|
|
927
|
+
let credential = apiKey ?? token;
|
|
928
|
+
let client = createApiClient(apiUrl2, credential);
|
|
929
|
+
let session;
|
|
930
|
+
try {
|
|
931
|
+
session = await client.createSession(sessionBody);
|
|
932
|
+
} catch (err) {
|
|
933
|
+
const keyRefused = apiKey !== void 0 && token !== void 0 && apiKey !== token && artifactId !== null && err instanceof ApiRequestError && (err.status === 401 || err.status === 403);
|
|
934
|
+
if (!keyRefused) {
|
|
935
|
+
throw err;
|
|
936
|
+
}
|
|
937
|
+
credential = token;
|
|
938
|
+
client = createApiClient(apiUrl2, credential);
|
|
939
|
+
session = await client.createSession(sessionBody);
|
|
940
|
+
}
|
|
717
941
|
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
718
942
|
log(
|
|
719
943
|
` uploading ${session.filesToUpload.length} file(s)` + (session.skippedFiles.length ? `, ${session.skippedFiles.length} unchanged` : "")
|
|
@@ -725,14 +949,14 @@ async function publish(opts) {
|
|
|
725
949
|
}
|
|
726
950
|
await uploadFile(
|
|
727
951
|
upload.uploadUrl,
|
|
728
|
-
|
|
952
|
+
readFileSync4(file.absPath),
|
|
729
953
|
file.mimeType
|
|
730
954
|
);
|
|
731
955
|
});
|
|
732
956
|
const result = await client.finalize(session.publishSessionId);
|
|
733
957
|
if (result.publishToken) {
|
|
734
958
|
writeConfig(dir, {
|
|
735
|
-
apiUrl,
|
|
959
|
+
apiUrl: apiUrl2,
|
|
736
960
|
artifactId: result.artifactId,
|
|
737
961
|
publishToken: result.publishToken,
|
|
738
962
|
slug: slugFromUrl(result.url),
|
|
@@ -740,7 +964,10 @@ async function publish(opts) {
|
|
|
740
964
|
});
|
|
741
965
|
}
|
|
742
966
|
if (opts.password !== void 0) {
|
|
743
|
-
const protectClient = createApiClient(
|
|
967
|
+
const protectClient = createApiClient(
|
|
968
|
+
apiUrl2,
|
|
969
|
+
credential ?? result.publishToken
|
|
970
|
+
);
|
|
744
971
|
await protectClient.setProtection(result.artifactId, opts.password);
|
|
745
972
|
}
|
|
746
973
|
if (opts.json) {
|
|
@@ -748,6 +975,7 @@ async function publish(opts) {
|
|
|
748
975
|
JSON.stringify(
|
|
749
976
|
{
|
|
750
977
|
artifactId: result.artifactId,
|
|
978
|
+
claimed: result.claimed,
|
|
751
979
|
url: result.url,
|
|
752
980
|
versionNumber: result.versionNumber,
|
|
753
981
|
...result.claimUrl ? { claimUrl: result.claimUrl } : {},
|
|
@@ -771,6 +999,8 @@ ${result.url}
|
|
|
771
999
|
${result.claimUrl}
|
|
772
1000
|
`
|
|
773
1001
|
);
|
|
1002
|
+
} else if (result.claimed) {
|
|
1003
|
+
console.log("Claimed by your account \u2014 no expiry.\n");
|
|
774
1004
|
}
|
|
775
1005
|
if (opts.password !== void 0) {
|
|
776
1006
|
console.log("Password protection: on\n");
|
|
@@ -823,9 +1053,123 @@ function openInBrowser(url) {
|
|
|
823
1053
|
});
|
|
824
1054
|
}
|
|
825
1055
|
|
|
1056
|
+
// src/login.ts
|
|
1057
|
+
function apiUrl() {
|
|
1058
|
+
return (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
1059
|
+
}
|
|
1060
|
+
function promptSecret(question) {
|
|
1061
|
+
return new Promise((resolve) => {
|
|
1062
|
+
const rl = createInterface({
|
|
1063
|
+
input: process.stdin,
|
|
1064
|
+
output: process.stdout
|
|
1065
|
+
});
|
|
1066
|
+
let muted = false;
|
|
1067
|
+
const raw = rl;
|
|
1068
|
+
raw._writeToOutput = (s) => {
|
|
1069
|
+
if (!muted) {
|
|
1070
|
+
process.stdout.write(s);
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
rl.question(question, (answer) => {
|
|
1074
|
+
rl.close();
|
|
1075
|
+
process.stdout.write("\n");
|
|
1076
|
+
resolve(answer);
|
|
1077
|
+
});
|
|
1078
|
+
muted = true;
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
async function login(opts) {
|
|
1082
|
+
const base = apiUrl();
|
|
1083
|
+
const keysUrl = `${base}/cli`;
|
|
1084
|
+
let key = opts.apiKey?.trim();
|
|
1085
|
+
if (!key) {
|
|
1086
|
+
if (!opts.json) {
|
|
1087
|
+
console.log(`
|
|
1088
|
+
Opening ${keysUrl}`);
|
|
1089
|
+
console.log("Create a CLI key there, then paste it below.\n");
|
|
1090
|
+
}
|
|
1091
|
+
if (process.stdout.isTTY) {
|
|
1092
|
+
openInBrowser(keysUrl);
|
|
1093
|
+
}
|
|
1094
|
+
key = (await promptSecret("API key: ")).trim();
|
|
1095
|
+
}
|
|
1096
|
+
if (!key) {
|
|
1097
|
+
throw new Error("No key entered \u2014 nothing was saved.");
|
|
1098
|
+
}
|
|
1099
|
+
if (!key.startsWith("ack_")) {
|
|
1100
|
+
throw new Error(
|
|
1101
|
+
`That doesn't look like an artifact.cafe API key (they start with "ack_").`
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
const me = await createApiClient(base, key).me();
|
|
1105
|
+
storeApiKey(base, key);
|
|
1106
|
+
const who = me.email ?? me.userId;
|
|
1107
|
+
if (opts.json) {
|
|
1108
|
+
console.log(
|
|
1109
|
+
JSON.stringify(
|
|
1110
|
+
{ apiUrl: base, email: me.email, loggedIn: true, userId: me.userId },
|
|
1111
|
+
null,
|
|
1112
|
+
2
|
|
1113
|
+
)
|
|
1114
|
+
);
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
console.log(`
|
|
1118
|
+
\u2713 Logged in to ${apiOrigin(base)} as ${who}`);
|
|
1119
|
+
console.log(
|
|
1120
|
+
"New publishes are claimed by your account automatically. Try `artifact-cafe list`.\n"
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
async function whoami(opts) {
|
|
1124
|
+
const base = apiUrl();
|
|
1125
|
+
const key = resolveApiKey(base);
|
|
1126
|
+
if (!key) {
|
|
1127
|
+
throw new Error(
|
|
1128
|
+
`Not logged in to ${apiOrigin(base)} \u2014 run \`artifact-cafe login\` first.`
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
const me = await createApiClient(base, key).me();
|
|
1132
|
+
if (opts.json) {
|
|
1133
|
+
console.log(
|
|
1134
|
+
JSON.stringify(
|
|
1135
|
+
{
|
|
1136
|
+
apiUrl: base,
|
|
1137
|
+
email: me.email,
|
|
1138
|
+
keyName: me.keyName ?? null,
|
|
1139
|
+
userId: me.userId
|
|
1140
|
+
},
|
|
1141
|
+
null,
|
|
1142
|
+
2
|
|
1143
|
+
)
|
|
1144
|
+
);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
const keyLabel = me.keyName ? ` (key: ${me.keyName})` : "";
|
|
1148
|
+
console.log(
|
|
1149
|
+
`Logged in to ${apiOrigin(base)} as ${me.email ?? me.userId}${keyLabel}`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
function logout(opts) {
|
|
1153
|
+
const base = apiUrl();
|
|
1154
|
+
const removed = removeApiKey(base);
|
|
1155
|
+
if (opts.json) {
|
|
1156
|
+
console.log(JSON.stringify({ apiUrl: base, loggedOut: removed }, null, 2));
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
if (!removed) {
|
|
1160
|
+
console.log(`No stored key for ${apiOrigin(base)} \u2014 already logged out.`);
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
console.log(`\u2713 Logged out of ${apiOrigin(base)} on this machine.`);
|
|
1164
|
+
console.log(
|
|
1165
|
+
`The key itself is still active \u2014 revoke it at ${base}/cli if this machine is shared.`
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
826
1169
|
// src/open.ts
|
|
1170
|
+
import path6 from "path";
|
|
827
1171
|
function openArtifact(opts) {
|
|
828
|
-
const dir =
|
|
1172
|
+
const dir = path6.resolve(opts.dir);
|
|
829
1173
|
const config = readConfig(dir);
|
|
830
1174
|
if (!config) {
|
|
831
1175
|
throw new Error(
|
|
@@ -839,7 +1183,7 @@ function openArtifact(opts) {
|
|
|
839
1183
|
}
|
|
840
1184
|
|
|
841
1185
|
// src/protect.ts
|
|
842
|
-
import
|
|
1186
|
+
import path7 from "path";
|
|
843
1187
|
async function protect(opts) {
|
|
844
1188
|
if (opts.off && opts.password !== void 0) {
|
|
845
1189
|
throw new Error("Pass either --password or --off, not both.");
|
|
@@ -856,16 +1200,16 @@ async function protect(opts) {
|
|
|
856
1200
|
throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
|
|
857
1201
|
}
|
|
858
1202
|
}
|
|
859
|
-
const dir =
|
|
1203
|
+
const dir = path7.resolve(opts.dir);
|
|
860
1204
|
const config = readConfig(dir);
|
|
861
1205
|
if (!config) {
|
|
862
1206
|
throw new Error(
|
|
863
1207
|
`No artifact linked in ${dir} \u2014 run \`artifact-cafe publish\` there first (.artifactcafe/config.json not found).`
|
|
864
1208
|
);
|
|
865
1209
|
}
|
|
866
|
-
const
|
|
1210
|
+
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config.apiUrl;
|
|
867
1211
|
const token = config.publishToken ?? process.env.ARTIFACT_CAFE_TOKEN;
|
|
868
|
-
const client = createApiClient(
|
|
1212
|
+
const client = createApiClient(apiUrl2, token);
|
|
869
1213
|
const res = await client.setProtection(config.artifactId, password);
|
|
870
1214
|
if (opts.json) {
|
|
871
1215
|
console.log(JSON.stringify({ protected: res.protected }, null, 2));
|
|
@@ -883,6 +1227,10 @@ async function protect(opts) {
|
|
|
883
1227
|
// src/index.ts
|
|
884
1228
|
var publish2 = defineCommand({
|
|
885
1229
|
args: {
|
|
1230
|
+
"api-key": {
|
|
1231
|
+
description: "Publish with an account API key (default: stored login)",
|
|
1232
|
+
type: "string"
|
|
1233
|
+
},
|
|
886
1234
|
artifact: {
|
|
887
1235
|
description: "Publish to an existing artifact ID",
|
|
888
1236
|
type: "string"
|
|
@@ -905,6 +1253,7 @@ var publish2 = defineCommand({
|
|
|
905
1253
|
async run({ args }) {
|
|
906
1254
|
try {
|
|
907
1255
|
await publish({
|
|
1256
|
+
apiKey: args["api-key"],
|
|
908
1257
|
artifact: args.artifact,
|
|
909
1258
|
dir: args.dir,
|
|
910
1259
|
entry: args.entry,
|
|
@@ -919,6 +1268,82 @@ var publish2 = defineCommand({
|
|
|
919
1268
|
}
|
|
920
1269
|
}
|
|
921
1270
|
});
|
|
1271
|
+
var login2 = defineCommand({
|
|
1272
|
+
args: {
|
|
1273
|
+
"api-key": {
|
|
1274
|
+
description: "Store this key without the browser prompt (agents, CI)",
|
|
1275
|
+
type: "string"
|
|
1276
|
+
},
|
|
1277
|
+
json: { description: "Machine-readable output", type: "boolean" }
|
|
1278
|
+
},
|
|
1279
|
+
meta: {
|
|
1280
|
+
description: "Log in \u2014 mint an API key in the browser and store it",
|
|
1281
|
+
name: "login"
|
|
1282
|
+
},
|
|
1283
|
+
async run({ args }) {
|
|
1284
|
+
try {
|
|
1285
|
+
await login({ apiKey: args["api-key"], json: Boolean(args.json) });
|
|
1286
|
+
} catch (err) {
|
|
1287
|
+
printCliError(err, Boolean(args.json));
|
|
1288
|
+
process.exit(1);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
var logout2 = defineCommand({
|
|
1293
|
+
args: {
|
|
1294
|
+
json: { description: "Machine-readable output", type: "boolean" }
|
|
1295
|
+
},
|
|
1296
|
+
meta: {
|
|
1297
|
+
description: "Remove the stored API key for this machine",
|
|
1298
|
+
name: "logout"
|
|
1299
|
+
},
|
|
1300
|
+
run({ args }) {
|
|
1301
|
+
try {
|
|
1302
|
+
logout({ json: Boolean(args.json) });
|
|
1303
|
+
} catch (err) {
|
|
1304
|
+
printCliError(err, Boolean(args.json));
|
|
1305
|
+
process.exit(1);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1309
|
+
var whoami2 = defineCommand({
|
|
1310
|
+
args: {
|
|
1311
|
+
json: { description: "Machine-readable output", type: "boolean" }
|
|
1312
|
+
},
|
|
1313
|
+
meta: {
|
|
1314
|
+
description: "Show which account the stored API key belongs to",
|
|
1315
|
+
name: "whoami"
|
|
1316
|
+
},
|
|
1317
|
+
async run({ args }) {
|
|
1318
|
+
try {
|
|
1319
|
+
await whoami({ json: Boolean(args.json) });
|
|
1320
|
+
} catch (err) {
|
|
1321
|
+
printCliError(err, Boolean(args.json));
|
|
1322
|
+
process.exit(1);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
var list2 = defineCommand({
|
|
1327
|
+
args: {
|
|
1328
|
+
"api-key": {
|
|
1329
|
+
description: "List with this API key (default: stored login)",
|
|
1330
|
+
type: "string"
|
|
1331
|
+
},
|
|
1332
|
+
json: { description: "Machine-readable output", type: "boolean" }
|
|
1333
|
+
},
|
|
1334
|
+
meta: {
|
|
1335
|
+
description: "List the artifacts your account owns",
|
|
1336
|
+
name: "list"
|
|
1337
|
+
},
|
|
1338
|
+
async run({ args }) {
|
|
1339
|
+
try {
|
|
1340
|
+
await list({ apiKey: args["api-key"], json: Boolean(args.json) });
|
|
1341
|
+
} catch (err) {
|
|
1342
|
+
printCliError(err, Boolean(args.json));
|
|
1343
|
+
process.exit(1);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
});
|
|
922
1347
|
var comments2 = defineCommand({
|
|
923
1348
|
args: {
|
|
924
1349
|
dir: {
|
|
@@ -1008,6 +1433,15 @@ var main = defineCommand({
|
|
|
1008
1433
|
description: "Publish AI-generated artifacts for review \u2014 https://artifact.cafe",
|
|
1009
1434
|
name: "artifact-cafe"
|
|
1010
1435
|
},
|
|
1011
|
-
subCommands: {
|
|
1436
|
+
subCommands: {
|
|
1437
|
+
comments: comments2,
|
|
1438
|
+
list: list2,
|
|
1439
|
+
login: login2,
|
|
1440
|
+
logout: logout2,
|
|
1441
|
+
open,
|
|
1442
|
+
protect: protect2,
|
|
1443
|
+
publish: publish2,
|
|
1444
|
+
whoami: whoami2
|
|
1445
|
+
}
|
|
1012
1446
|
});
|
|
1013
1447
|
runMain(main);
|