artifact-cafe 0.0.13 → 0.0.15
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 +869 -608
- package/package.json +12 -1
package/dist/index.js
CHANGED
|
@@ -83,35 +83,125 @@ var artifactListResponseSchema = z.object({
|
|
|
83
83
|
artifacts: z.array(artifactListItemSchema)
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
// ../shared/src/
|
|
86
|
+
// ../shared/src/agent-tokens.ts
|
|
87
87
|
import { z as z2 } from "zod";
|
|
88
|
+
var createAgentTokenSchema = z2.object({
|
|
89
|
+
externalId: z2.string().min(1).max(1024),
|
|
90
|
+
name: z2.string().trim().min(1).max(100).optional(),
|
|
91
|
+
ttlSeconds: z2.number().int().positive().max(3600)
|
|
92
|
+
});
|
|
93
|
+
var agentTokenResponseSchema = z2.object({
|
|
94
|
+
expiresAt: z2.string().datetime(),
|
|
95
|
+
token: z2.string(),
|
|
96
|
+
workspaceId: z2.string()
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ../shared/src/agents.ts
|
|
100
|
+
var AGENT_BRANDS = [
|
|
101
|
+
{
|
|
102
|
+
id: "claude-code",
|
|
103
|
+
label: "Claude Code",
|
|
104
|
+
match: ["claude"],
|
|
105
|
+
short: "Claude"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: "codex",
|
|
109
|
+
label: "OpenAI Codex",
|
|
110
|
+
match: ["codex"],
|
|
111
|
+
short: "Codex"
|
|
112
|
+
},
|
|
113
|
+
{ id: "cursor", label: "Cursor", match: ["cursor"], short: "Cursor" },
|
|
114
|
+
{
|
|
115
|
+
id: "copilot",
|
|
116
|
+
label: "GitHub Copilot",
|
|
117
|
+
match: ["copilot"],
|
|
118
|
+
short: "Copilot"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: "gemini-cli",
|
|
122
|
+
label: "Gemini CLI",
|
|
123
|
+
match: ["gemini"],
|
|
124
|
+
short: "Gemini"
|
|
125
|
+
},
|
|
126
|
+
{ id: "opencode", label: "opencode", match: ["opencode"], short: "opencode" },
|
|
127
|
+
{
|
|
128
|
+
id: "openclaw",
|
|
129
|
+
label: "OpenClaw",
|
|
130
|
+
match: ["openclaw"],
|
|
131
|
+
short: "OpenClaw"
|
|
132
|
+
},
|
|
133
|
+
{ id: "hermes", label: "Hermes", match: ["hermes"], short: "Hermes" }
|
|
134
|
+
];
|
|
135
|
+
function normalizeAgent(raw) {
|
|
136
|
+
const value = raw?.trim().toLowerCase();
|
|
137
|
+
if (!value) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return AGENT_BRANDS.find((brand) => brand.id === value) ?? AGENT_BRANDS.find((brand) => brand.match.some((m) => value.includes(m))) ?? null;
|
|
141
|
+
}
|
|
142
|
+
var ENV_FINGERPRINTS = [
|
|
143
|
+
{ id: "claude-code", prefixes: ["CLAUDECODE", "CLAUDE_CODE_"] },
|
|
144
|
+
{ id: "codex", prefixes: ["CODEX_"] },
|
|
145
|
+
{ id: "cursor", prefixes: ["CURSOR_"] },
|
|
146
|
+
{ id: "copilot", prefixes: ["COPILOT_", "GITHUB_COPILOT_"] },
|
|
147
|
+
{ id: "gemini-cli", prefixes: ["GEMINI_CLI", "GEMINI_SANDBOX"] },
|
|
148
|
+
{ id: "opencode", prefixes: ["OPENCODE"] }
|
|
149
|
+
];
|
|
150
|
+
function detectAgentFromEnv(env) {
|
|
151
|
+
const explicit = env.ARTIFACT_CAFE_AGENT?.trim();
|
|
152
|
+
if (explicit) {
|
|
153
|
+
return explicit;
|
|
154
|
+
}
|
|
155
|
+
const keys = Object.keys(env).filter((key) => env[key]);
|
|
156
|
+
for (const { id, prefixes } of ENV_FINGERPRINTS) {
|
|
157
|
+
const hit = keys.some(
|
|
158
|
+
(key) => prefixes.some((prefix) => key.startsWith(prefix))
|
|
159
|
+
);
|
|
160
|
+
if (hit) {
|
|
161
|
+
return AGENT_BRANDS.find((brand) => brand.id === id)?.label;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return normalizeAgent(env.AI_AGENT)?.label;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ../shared/src/avatars.ts
|
|
168
|
+
import { z as z3 } from "zod";
|
|
88
169
|
var AVATAR_CONTENT_TYPES = [
|
|
89
170
|
"image/png",
|
|
90
171
|
"image/jpeg",
|
|
91
172
|
"image/webp"
|
|
92
173
|
];
|
|
93
174
|
var AVATAR_MAX_BYTES = 2 * 1024 * 1024;
|
|
94
|
-
var avatarContentTypeSchema =
|
|
95
|
-
var userAvatarResponseSchema =
|
|
96
|
-
image:
|
|
175
|
+
var avatarContentTypeSchema = z3.enum(AVATAR_CONTENT_TYPES);
|
|
176
|
+
var userAvatarResponseSchema = z3.object({
|
|
177
|
+
image: z3.string().nullable()
|
|
97
178
|
});
|
|
98
|
-
var workspaceAvatarResponseSchema =
|
|
99
|
-
logo:
|
|
179
|
+
var workspaceAvatarResponseSchema = z3.object({
|
|
180
|
+
logo: z3.string().nullable()
|
|
100
181
|
});
|
|
101
182
|
|
|
102
183
|
// ../shared/src/comments.ts
|
|
103
|
-
import { z as
|
|
184
|
+
import { z as z7 } from "zod";
|
|
104
185
|
|
|
105
|
-
// ../shared/src/
|
|
186
|
+
// ../shared/src/emoji.ts
|
|
106
187
|
import { z as z4 } from "zod";
|
|
188
|
+
var SINGLE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
|
|
189
|
+
var REDUNDANT_VS16 = new RegExp("(\\p{Emoji_Presentation})\\uFE0F", "gu");
|
|
190
|
+
function canonicalizeEmoji(s) {
|
|
191
|
+
return s.replace(REDUNDANT_VS16, "$1");
|
|
192
|
+
}
|
|
193
|
+
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.");
|
|
194
|
+
|
|
195
|
+
// ../shared/src/publish.ts
|
|
196
|
+
import { z as z6 } from "zod";
|
|
107
197
|
|
|
108
198
|
// ../shared/src/review-mode.ts
|
|
109
|
-
import { z as
|
|
110
|
-
var reviewModeSchema =
|
|
111
|
-
var setReviewModeSchema =
|
|
199
|
+
import { z as z5 } from "zod";
|
|
200
|
+
var reviewModeSchema = z5.enum(["open", "readonly", "off"]);
|
|
201
|
+
var setReviewModeSchema = z5.object({
|
|
112
202
|
mode: reviewModeSchema
|
|
113
203
|
});
|
|
114
|
-
var reviewModeResponseSchema =
|
|
204
|
+
var reviewModeResponseSchema = z5.object({
|
|
115
205
|
mode: reviewModeSchema
|
|
116
206
|
});
|
|
117
207
|
|
|
@@ -128,266 +218,276 @@ function isSafeArtifactPath(p) {
|
|
|
128
218
|
}
|
|
129
219
|
return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
|
|
130
220
|
}
|
|
131
|
-
var
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
221
|
+
var CARD_DESCRIPTION_MAX = 300;
|
|
222
|
+
var CARD_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
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, {
|
|
135
227
|
message: "path must be a safe relative path"
|
|
136
228
|
}),
|
|
137
|
-
size:
|
|
138
|
-
});
|
|
139
|
-
var publishSourceSchema =
|
|
140
|
-
agent:
|
|
141
|
-
runId:
|
|
142
|
-
type:
|
|
143
|
-
});
|
|
144
|
-
var createPublishSessionSchema =
|
|
145
|
-
artifactId:
|
|
146
|
-
|
|
147
|
-
|
|
229
|
+
size: z6.number().int().positive()
|
|
230
|
+
});
|
|
231
|
+
var publishSourceSchema = z6.object({
|
|
232
|
+
agent: z6.string().optional(),
|
|
233
|
+
runId: z6.string().optional(),
|
|
234
|
+
type: z6.literal("cli")
|
|
235
|
+
});
|
|
236
|
+
var createPublishSessionSchema = z6.object({
|
|
237
|
+
artifactId: z6.string().nullable(),
|
|
238
|
+
// One-line share-card summary (`--description`). Overrides whatever the
|
|
239
|
+
// entry HTML declares; leave it off and the API reads the document's own
|
|
240
|
+
// `og:description`/`description` at finalize.
|
|
241
|
+
description: z6.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
242
|
+
entryPath: z6.string().optional(),
|
|
243
|
+
files: z6.array(manifestFileSchema).min(1).max(1e3),
|
|
148
244
|
// SPEC-2 §6.4 — workspace placement is available only to authenticated
|
|
149
245
|
// members; the API enforces membership and folder ownership. folderId
|
|
150
246
|
// without workspaceId resolves against the account's personal workspace.
|
|
151
|
-
folderId:
|
|
247
|
+
folderId: z6.string().min(1).optional(),
|
|
152
248
|
// Bullet-point changelog for this version, authored by the publisher.
|
|
153
249
|
// Optional on every publish (incl. v1); stored immutably on the version.
|
|
154
|
-
notes:
|
|
250
|
+
notes: z6.string().max(4e3).optional(),
|
|
155
251
|
source: publishSourceSchema,
|
|
156
|
-
title:
|
|
157
|
-
workspaceId:
|
|
158
|
-
});
|
|
159
|
-
var publishSessionResponseSchema =
|
|
160
|
-
artifactId:
|
|
161
|
-
expiresAt:
|
|
162
|
-
filesToUpload:
|
|
163
|
-
|
|
164
|
-
method:
|
|
165
|
-
path:
|
|
166
|
-
uploadUrl:
|
|
252
|
+
title: z6.string().max(200).optional(),
|
|
253
|
+
workspaceId: z6.string().min(1).optional()
|
|
254
|
+
});
|
|
255
|
+
var publishSessionResponseSchema = z6.object({
|
|
256
|
+
artifactId: z6.string(),
|
|
257
|
+
expiresAt: z6.string(),
|
|
258
|
+
filesToUpload: z6.array(
|
|
259
|
+
z6.object({
|
|
260
|
+
method: z6.literal("PUT"),
|
|
261
|
+
path: z6.string(),
|
|
262
|
+
uploadUrl: z6.string().url()
|
|
167
263
|
})
|
|
168
264
|
),
|
|
169
|
-
publishSessionId:
|
|
170
|
-
skippedFiles:
|
|
171
|
-
versionId:
|
|
265
|
+
publishSessionId: z6.string(),
|
|
266
|
+
skippedFiles: z6.array(z6.string()),
|
|
267
|
+
versionId: z6.string()
|
|
172
268
|
});
|
|
173
|
-
var finalizeResponseSchema =
|
|
174
|
-
artifactId:
|
|
269
|
+
var finalizeResponseSchema = z6.object({
|
|
270
|
+
artifactId: z6.string(),
|
|
175
271
|
// API-key publishes create the artifact already claimed (SPEC §5.4) —
|
|
176
272
|
// the CLI uses this to tell "claim URL pending" from "already yours".
|
|
177
|
-
claimed:
|
|
273
|
+
claimed: z6.boolean(),
|
|
178
274
|
// Returned only on first publish of a new artifact (one-time).
|
|
179
|
-
claimUrl:
|
|
180
|
-
publishToken:
|
|
181
|
-
url:
|
|
182
|
-
versionId:
|
|
183
|
-
versionNumber:
|
|
275
|
+
claimUrl: z6.string().url().optional(),
|
|
276
|
+
publishToken: z6.string().optional(),
|
|
277
|
+
url: z6.string().url(),
|
|
278
|
+
versionId: z6.string(),
|
|
279
|
+
versionNumber: z6.number().int().positive(),
|
|
184
280
|
// SPEC-2 §5.2 — who can open the review URL. A publish into a folder
|
|
185
281
|
// inherits that folder's visibility, so this is the only place the CLI
|
|
186
282
|
// learns that a link it just printed is members-only. Optional so a new
|
|
187
283
|
// CLI against an older API degrades to "unknown" instead of failing the
|
|
188
284
|
// publish over a field that only shapes the closing message.
|
|
189
|
-
visibility:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
285
|
+
visibility: z6.enum(["public", "unlisted", "workspace", "private"]).optional(),
|
|
286
|
+
workspaceId: z6.string().nullable().optional()
|
|
287
|
+
});
|
|
288
|
+
var artifactResponseSchema = z6.object({
|
|
289
|
+
claimed: z6.boolean(),
|
|
290
|
+
currentVersion: z6.object({
|
|
291
|
+
entryUrl: z6.string().url(),
|
|
292
|
+
id: z6.string(),
|
|
293
|
+
versionNumber: z6.number().int().positive()
|
|
197
294
|
}).nullable(),
|
|
295
|
+
// Custom icon, or null to render the default document glyph.
|
|
296
|
+
emoji: z6.string().nullable(),
|
|
198
297
|
// SPEC-2 §6.4 — placement + visibility surface on the artifact.
|
|
199
|
-
folderId:
|
|
200
|
-
id:
|
|
298
|
+
folderId: z6.string().nullable(),
|
|
299
|
+
id: z6.string(),
|
|
201
300
|
// SPEC-2 §5.8 — what a viewer may do once in (open / readonly / off).
|
|
202
301
|
reviewMode: reviewModeSchema,
|
|
203
|
-
slug:
|
|
204
|
-
title:
|
|
205
|
-
visibility:
|
|
206
|
-
workspaceId:
|
|
302
|
+
slug: z6.string(),
|
|
303
|
+
title: z6.string(),
|
|
304
|
+
visibility: z6.enum(["public", "unlisted", "workspace", "private"]),
|
|
305
|
+
workspaceId: z6.string().nullable()
|
|
207
306
|
});
|
|
208
307
|
|
|
209
308
|
// ../shared/src/comments.ts
|
|
210
|
-
var anchorPageSchema =
|
|
309
|
+
var anchorPageSchema = z7.string().max(1024).refine(isSafeArtifactPath, {
|
|
211
310
|
message: "page must be a safe relative path"
|
|
212
311
|
}).optional();
|
|
213
|
-
var elementSnapshotSchema =
|
|
214
|
-
classes:
|
|
215
|
-
id:
|
|
216
|
-
tag:
|
|
217
|
-
text:
|
|
312
|
+
var elementSnapshotSchema = z7.object({
|
|
313
|
+
classes: z7.string().max(256).optional(),
|
|
314
|
+
id: z7.string().max(256).optional(),
|
|
315
|
+
tag: z7.string().min(1).max(128),
|
|
316
|
+
text: z7.string().max(200).optional()
|
|
218
317
|
});
|
|
219
|
-
var pointAnchorSchema =
|
|
318
|
+
var pointAnchorSchema = z7.object({
|
|
220
319
|
element: elementSnapshotSchema.optional(),
|
|
221
320
|
// elementPath is the primary anchor; x/y are offsets within the
|
|
222
321
|
// element's box, normalized 0..1 (SPEC.md §8.1).
|
|
223
|
-
elementPath:
|
|
322
|
+
elementPath: z7.string().max(2048),
|
|
224
323
|
page: anchorPageSchema,
|
|
225
|
-
type:
|
|
226
|
-
viewportHeight:
|
|
227
|
-
viewportWidth:
|
|
228
|
-
x:
|
|
229
|
-
y:
|
|
230
|
-
});
|
|
231
|
-
var textAnchorSchema =
|
|
232
|
-
elementPath:
|
|
233
|
-
endOffset:
|
|
324
|
+
type: z7.literal("point"),
|
|
325
|
+
viewportHeight: z7.number().int().positive().max(1e5),
|
|
326
|
+
viewportWidth: z7.number().int().positive().max(1e5),
|
|
327
|
+
x: z7.number().min(0).max(1),
|
|
328
|
+
y: z7.number().min(0).max(1)
|
|
329
|
+
});
|
|
330
|
+
var textAnchorSchema = z7.object({
|
|
331
|
+
elementPath: z7.string().max(2048),
|
|
332
|
+
endOffset: z7.number().int().nonnegative().max(1e8),
|
|
234
333
|
page: anchorPageSchema,
|
|
235
|
-
prefix:
|
|
236
|
-
quote:
|
|
237
|
-
startOffset:
|
|
238
|
-
suffix:
|
|
239
|
-
type:
|
|
334
|
+
prefix: z7.string().max(200).optional(),
|
|
335
|
+
quote: z7.string().min(1).max(1e3),
|
|
336
|
+
startOffset: z7.number().int().nonnegative().max(1e8),
|
|
337
|
+
suffix: z7.string().max(200).optional(),
|
|
338
|
+
type: z7.literal("text")
|
|
240
339
|
});
|
|
241
|
-
var pageAnchorSchema =
|
|
340
|
+
var pageAnchorSchema = z7.object({
|
|
242
341
|
page: anchorPageSchema,
|
|
243
|
-
type:
|
|
342
|
+
type: z7.literal("page")
|
|
244
343
|
});
|
|
245
|
-
var anchorSchema =
|
|
344
|
+
var anchorSchema = z7.discriminatedUnion("type", [
|
|
246
345
|
pointAnchorSchema,
|
|
247
346
|
textAnchorSchema,
|
|
248
347
|
pageAnchorSchema
|
|
249
348
|
]);
|
|
250
|
-
var displayNameSchema =
|
|
251
|
-
var guestSchema =
|
|
349
|
+
var displayNameSchema = z7.string().trim().min(1).max(64);
|
|
350
|
+
var guestSchema = z7.object({
|
|
252
351
|
displayName: displayNameSchema,
|
|
253
|
-
guestId:
|
|
352
|
+
guestId: z7.string().min(16)
|
|
254
353
|
});
|
|
255
|
-
var viaLabelSchema =
|
|
256
|
-
var createCommentThreadSchema =
|
|
354
|
+
var viaLabelSchema = z7.string().trim().min(1).max(64);
|
|
355
|
+
var createCommentThreadSchema = z7.object({
|
|
257
356
|
anchor: anchorSchema,
|
|
258
|
-
body:
|
|
357
|
+
body: z7.string().min(1).max(1e4),
|
|
259
358
|
guest: guestSchema.optional(),
|
|
260
|
-
versionId:
|
|
359
|
+
versionId: z7.string(),
|
|
261
360
|
via: viaLabelSchema.optional()
|
|
262
361
|
});
|
|
263
|
-
var createCommentMessageSchema =
|
|
264
|
-
body:
|
|
362
|
+
var createCommentMessageSchema = z7.object({
|
|
363
|
+
body: z7.string().min(1).max(1e4),
|
|
265
364
|
guest: guestSchema.optional(),
|
|
266
365
|
via: viaLabelSchema.optional()
|
|
267
366
|
});
|
|
268
|
-
var editCommentMessageSchema =
|
|
269
|
-
body:
|
|
367
|
+
var editCommentMessageSchema = z7.object({
|
|
368
|
+
body: z7.string().min(1).max(1e4),
|
|
270
369
|
guest: guestSchema.optional()
|
|
271
370
|
});
|
|
272
|
-
var threadActionSchema =
|
|
371
|
+
var threadActionSchema = z7.object({
|
|
273
372
|
guest: guestSchema.optional()
|
|
274
373
|
});
|
|
275
|
-
var
|
|
276
|
-
var
|
|
277
|
-
function canonicalizeEmoji(s) {
|
|
278
|
-
return s.replace(REDUNDANT_VS16, "$1");
|
|
279
|
-
}
|
|
280
|
-
var reactionEmojiSchema = z5.string().max(64).transform(canonicalizeEmoji).refine((s) => SINGLE_EMOJI.test(s), { message: "Must be a single emoji." }).describe("A single emoji, e.g. \u{1F44D}");
|
|
281
|
-
var toggleReactionSchema = z5.object({
|
|
374
|
+
var reactionEmojiSchema = z7.string().max(64).transform(canonicalizeEmoji).refine((s) => SINGLE_EMOJI.test(s), { message: "Must be a single emoji." }).describe("A single emoji, e.g. \u{1F44D}");
|
|
375
|
+
var toggleReactionSchema = z7.object({
|
|
282
376
|
emoji: reactionEmojiSchema,
|
|
283
377
|
guest: guestSchema.optional()
|
|
284
378
|
});
|
|
285
|
-
var
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
379
|
+
var commentAgentSchema = z7.object({
|
|
380
|
+
id: z7.string().nullable(),
|
|
381
|
+
label: z7.string()
|
|
382
|
+
});
|
|
383
|
+
var commentAuthorSchema = z7.object({
|
|
384
|
+
agent: commentAgentSchema.nullable().default(null),
|
|
385
|
+
avatarUrl: z7.string().nullable(),
|
|
386
|
+
displayName: z7.string(),
|
|
387
|
+
guestId: z7.string().nullable(),
|
|
388
|
+
isViewer: z7.boolean(),
|
|
389
|
+
kind: z7.enum(["member", "guest"]),
|
|
291
390
|
// Optional "via" attribution (§6.4). Null for plain comments; a bounded
|
|
292
|
-
// label the shell appends after the display name.
|
|
293
|
-
|
|
391
|
+
// label the shell appends after the display name. Defaulted for the same
|
|
392
|
+
// reason as `agent`: a server predating §6.4 omits the key entirely.
|
|
393
|
+
via: z7.string().nullable().default(null)
|
|
294
394
|
});
|
|
295
|
-
var messageReactionSchema =
|
|
296
|
-
emoji:
|
|
297
|
-
reactors:
|
|
395
|
+
var messageReactionSchema = z7.object({
|
|
396
|
+
emoji: z7.string(),
|
|
397
|
+
reactors: z7.array(commentAuthorSchema)
|
|
298
398
|
});
|
|
299
|
-
var commentMessageSchema =
|
|
399
|
+
var commentMessageSchema = z7.object({
|
|
300
400
|
author: commentAuthorSchema,
|
|
301
401
|
// Empty string for a deleted message — the body is stripped server-side
|
|
302
402
|
// (§6.7.1 tombstone), never sent to the client and hidden there.
|
|
303
|
-
body:
|
|
304
|
-
createdAt:
|
|
403
|
+
body: z7.string(),
|
|
404
|
+
createdAt: z7.string(),
|
|
305
405
|
// Soft-deleted (§6.7.1). A tombstone keeps thread structure and replies;
|
|
306
406
|
// the shell renders "Comment deleted" instead of the (absent) body.
|
|
307
|
-
deleted:
|
|
407
|
+
deleted: z7.boolean(),
|
|
308
408
|
// Last edit time (§6.7.1), or null if never edited — drives the "edited"
|
|
309
409
|
// badge. No edit history is kept.
|
|
310
|
-
editedAt:
|
|
311
|
-
id:
|
|
410
|
+
editedAt: z7.string().nullable(),
|
|
411
|
+
id: z7.string(),
|
|
312
412
|
// Defaulted, not required: a new CLI parsing an older server's
|
|
313
413
|
// response must not fail on the missing key.
|
|
314
|
-
reactions:
|
|
414
|
+
reactions: z7.array(messageReactionSchema).default([])
|
|
315
415
|
});
|
|
316
|
-
var threadActorSchema =
|
|
317
|
-
displayName:
|
|
318
|
-
kind:
|
|
416
|
+
var threadActorSchema = z7.object({
|
|
417
|
+
displayName: z7.string(),
|
|
418
|
+
kind: z7.enum(["member", "guest", "agent"])
|
|
319
419
|
});
|
|
320
|
-
var commentThreadSchema =
|
|
420
|
+
var commentThreadSchema = z7.object({
|
|
321
421
|
// §6.6.1 — set while open to signal "seen, working on it"; cleared on
|
|
322
422
|
// reopen, kept (but superseded in display) once resolved. Defaulted so
|
|
323
423
|
// clients parsing an older server's response don't fail.
|
|
324
|
-
acknowledgedAt:
|
|
424
|
+
acknowledgedAt: z7.string().nullable().default(null),
|
|
325
425
|
acknowledgedBy: threadActorSchema.nullable().default(null),
|
|
326
426
|
anchor: anchorSchema,
|
|
327
|
-
artifactId:
|
|
328
|
-
createdAt:
|
|
427
|
+
artifactId: z7.string(),
|
|
428
|
+
createdAt: z7.string(),
|
|
329
429
|
createdBy: commentAuthorSchema,
|
|
330
|
-
id:
|
|
331
|
-
messages:
|
|
430
|
+
id: z7.string(),
|
|
431
|
+
messages: z7.array(commentMessageSchema),
|
|
332
432
|
// Null on threads resolved before actor recording existed, and always
|
|
333
433
|
// null while open.
|
|
334
434
|
resolvedBy: threadActorSchema.nullable().default(null),
|
|
335
|
-
status:
|
|
336
|
-
versionId:
|
|
435
|
+
status: z7.enum(["open", "resolved"]),
|
|
436
|
+
versionId: z7.string()
|
|
337
437
|
});
|
|
338
|
-
var commentThreadListSchema =
|
|
339
|
-
threads:
|
|
438
|
+
var commentThreadListSchema = z7.object({
|
|
439
|
+
threads: z7.array(commentThreadSchema)
|
|
340
440
|
});
|
|
341
|
-
var commentExportQuerySchema =
|
|
342
|
-
status:
|
|
343
|
-
version:
|
|
441
|
+
var commentExportQuerySchema = z7.object({
|
|
442
|
+
status: z7.enum(["open", "resolved", "all"]).default("open"),
|
|
443
|
+
version: z7.enum(["current", "all"]).default("current")
|
|
344
444
|
});
|
|
345
|
-
var exportReactionSchema =
|
|
346
|
-
count:
|
|
347
|
-
emoji:
|
|
445
|
+
var exportReactionSchema = z7.object({
|
|
446
|
+
count: z7.number().int().positive(),
|
|
447
|
+
emoji: z7.string()
|
|
348
448
|
});
|
|
349
|
-
var commentExportReplySchema =
|
|
350
|
-
author:
|
|
351
|
-
authorKind:
|
|
352
|
-
body:
|
|
353
|
-
createdAt:
|
|
449
|
+
var commentExportReplySchema = z7.object({
|
|
450
|
+
author: z7.string(),
|
|
451
|
+
authorKind: z7.enum(["member", "guest"]),
|
|
452
|
+
body: z7.string(),
|
|
453
|
+
createdAt: z7.string(),
|
|
354
454
|
// Soft-delete tombstone (§6.7.1): body is "" when true.
|
|
355
|
-
deleted:
|
|
356
|
-
editedAt:
|
|
455
|
+
deleted: z7.boolean(),
|
|
456
|
+
editedAt: z7.string().nullable(),
|
|
357
457
|
// The message id — what `edit`/`delete` target (§5.1).
|
|
358
|
-
id:
|
|
359
|
-
reactions:
|
|
458
|
+
id: z7.string(),
|
|
459
|
+
reactions: z7.array(exportReactionSchema).optional()
|
|
360
460
|
});
|
|
361
|
-
var commentExportThreadSchema =
|
|
461
|
+
var commentExportThreadSchema = z7.object({
|
|
362
462
|
// §6.6.1 — lets a polling agent skip threads it already acknowledged
|
|
363
463
|
// (acknowledged threads stay in status=open exports on purpose).
|
|
364
|
-
acknowledgedAt:
|
|
464
|
+
acknowledgedAt: z7.string().nullable().default(null),
|
|
365
465
|
anchor: anchorSchema,
|
|
366
|
-
author:
|
|
367
|
-
authorKind:
|
|
368
|
-
body:
|
|
369
|
-
createdAt:
|
|
370
|
-
deleted:
|
|
371
|
-
editedAt:
|
|
372
|
-
id:
|
|
466
|
+
author: z7.string(),
|
|
467
|
+
authorKind: z7.enum(["member", "guest"]),
|
|
468
|
+
body: z7.string(),
|
|
469
|
+
createdAt: z7.string(),
|
|
470
|
+
deleted: z7.boolean(),
|
|
471
|
+
editedAt: z7.string().nullable(),
|
|
472
|
+
id: z7.string(),
|
|
373
473
|
// The opening message's id (distinct from the thread `id`) — `edit`/
|
|
374
474
|
// `delete` operate on messages, so the CLI needs it to target the first
|
|
375
475
|
// comment, not just the replies.
|
|
376
|
-
messageId:
|
|
377
|
-
reactions:
|
|
378
|
-
replies:
|
|
476
|
+
messageId: z7.string(),
|
|
477
|
+
reactions: z7.array(exportReactionSchema).optional(),
|
|
478
|
+
replies: z7.array(commentExportReplySchema),
|
|
379
479
|
resolvedBy: threadActorSchema.nullable().default(null),
|
|
380
|
-
status:
|
|
381
|
-
versionNumber:
|
|
480
|
+
status: z7.enum(["open", "resolved"]),
|
|
481
|
+
versionNumber: z7.number().int().positive()
|
|
382
482
|
});
|
|
383
|
-
var commentExportSchema =
|
|
384
|
-
artifactId:
|
|
385
|
-
comments:
|
|
483
|
+
var commentExportSchema = z7.object({
|
|
484
|
+
artifactId: z7.string(),
|
|
485
|
+
comments: z7.array(commentExportThreadSchema),
|
|
386
486
|
// The artifact's current version (null before the first finalize).
|
|
387
|
-
versionNumber:
|
|
487
|
+
versionNumber: z7.number().int().positive().nullable()
|
|
388
488
|
});
|
|
389
|
-
var commentReadResponseSchema =
|
|
390
|
-
lastSeenAt:
|
|
489
|
+
var commentReadResponseSchema = z7.object({
|
|
490
|
+
lastSeenAt: z7.string()
|
|
391
491
|
});
|
|
392
492
|
function elementLabel(el) {
|
|
393
493
|
if (el.id) {
|
|
@@ -398,499 +498,554 @@ function elementLabel(el) {
|
|
|
398
498
|
}
|
|
399
499
|
|
|
400
500
|
// ../shared/src/errors.ts
|
|
401
|
-
import { z as
|
|
402
|
-
var apiErrorSchema =
|
|
403
|
-
code:
|
|
404
|
-
docsUrl:
|
|
405
|
-
message:
|
|
406
|
-
retryAfter:
|
|
501
|
+
import { z as z8 } from "zod";
|
|
502
|
+
var apiErrorSchema = z8.object({
|
|
503
|
+
code: z8.string(),
|
|
504
|
+
docsUrl: z8.string().url(),
|
|
505
|
+
message: z8.string(),
|
|
506
|
+
retryAfter: z8.number().int().positive().optional()
|
|
407
507
|
});
|
|
408
508
|
|
|
409
509
|
// ../shared/src/feedback.ts
|
|
410
|
-
import { z as
|
|
411
|
-
var feedbackRequestSchema =
|
|
412
|
-
email:
|
|
413
|
-
message:
|
|
510
|
+
import { z as z9 } from "zod";
|
|
511
|
+
var feedbackRequestSchema = z9.object({
|
|
512
|
+
email: z9.string().trim().toLowerCase().email().optional().or(z9.literal("")),
|
|
513
|
+
message: z9.string().trim().min(1).max(4e3)
|
|
414
514
|
});
|
|
415
|
-
var feedbackResponseSchema =
|
|
416
|
-
sent:
|
|
515
|
+
var feedbackResponseSchema = z9.object({
|
|
516
|
+
sent: z9.literal(true)
|
|
417
517
|
});
|
|
418
518
|
|
|
419
519
|
// ../shared/src/grants.ts
|
|
420
|
-
import { z as
|
|
421
|
-
var grantRoleSchema =
|
|
422
|
-
var createGrantSchema =
|
|
423
|
-
email:
|
|
520
|
+
import { z as z10 } from "zod";
|
|
521
|
+
var grantRoleSchema = z10.enum(["viewer"]);
|
|
522
|
+
var createGrantSchema = z10.object({
|
|
523
|
+
email: z10.string().trim().toLowerCase().email().optional(),
|
|
424
524
|
role: grantRoleSchema.default("viewer"),
|
|
425
|
-
userId:
|
|
525
|
+
userId: z10.string().min(1).optional()
|
|
426
526
|
}).refine((body) => Boolean(body.email) !== Boolean(body.userId), {
|
|
427
527
|
message: "provide exactly one of email or userId"
|
|
428
528
|
});
|
|
429
|
-
var grantSchema =
|
|
430
|
-
createdAt:
|
|
431
|
-
email:
|
|
432
|
-
expiresAt:
|
|
433
|
-
id:
|
|
529
|
+
var grantSchema = z10.object({
|
|
530
|
+
createdAt: z10.string(),
|
|
531
|
+
email: z10.string().email().nullable(),
|
|
532
|
+
expiresAt: z10.string().nullable(),
|
|
533
|
+
id: z10.string(),
|
|
434
534
|
// The subject's avatar once they have an account; null otherwise.
|
|
435
|
-
image:
|
|
535
|
+
image: z10.string().nullable(),
|
|
436
536
|
// Present on the invited account after redemption / when granting an
|
|
437
537
|
// existing user; null for a still-pending email invite.
|
|
438
|
-
name:
|
|
439
|
-
redeemedAt:
|
|
538
|
+
name: z10.string().nullable(),
|
|
539
|
+
redeemedAt: z10.string().nullable(),
|
|
440
540
|
role: grantRoleSchema,
|
|
441
541
|
// Shown as "· pending" beside the role until the link is redeemed.
|
|
442
|
-
status:
|
|
542
|
+
status: z10.enum(["pending", "active"]),
|
|
443
543
|
// Display fragment of the live invitation token, or null once redeemed.
|
|
444
|
-
tokenPrefix:
|
|
445
|
-
userId:
|
|
544
|
+
tokenPrefix: z10.string().nullable(),
|
|
545
|
+
userId: z10.string().nullable()
|
|
446
546
|
});
|
|
447
547
|
var createdGrantSchema = grantSchema.extend({
|
|
448
|
-
url:
|
|
548
|
+
url: z10.string().url()
|
|
449
549
|
});
|
|
450
|
-
var grantListResponseSchema =
|
|
451
|
-
grants:
|
|
550
|
+
var grantListResponseSchema = z10.object({
|
|
551
|
+
grants: z10.array(grantSchema),
|
|
452
552
|
// The artifact's current general-access value, so the share surface can
|
|
453
553
|
// render the "General access" line without a second read.
|
|
454
|
-
visibility:
|
|
554
|
+
visibility: z10.enum(["public", "unlisted", "workspace", "private"]),
|
|
455
555
|
// The owning workspace's logo, so the share modal can draw the workspace
|
|
456
556
|
// itself rather than a generic glyph. Null when none was uploaded.
|
|
457
|
-
workspaceLogo:
|
|
557
|
+
workspaceLogo: z10.string().nullable().optional(),
|
|
458
558
|
// The owning workspace's display name, so "People with access" can name the
|
|
459
559
|
// audience `workspace` visibility actually admits ("All Acme members") rather
|
|
460
560
|
// than leaving an empty list to imply nobody. Null for a workspace-less
|
|
461
561
|
// artifact; optional so an older server still parses.
|
|
462
|
-
workspaceName:
|
|
562
|
+
workspaceName: z10.string().nullable().optional()
|
|
463
563
|
});
|
|
464
|
-
var revokeGrantResponseSchema =
|
|
465
|
-
revoked:
|
|
564
|
+
var revokeGrantResponseSchema = z10.object({
|
|
565
|
+
revoked: z10.literal(true)
|
|
466
566
|
});
|
|
467
|
-
var acceptGrantSchema =
|
|
468
|
-
token:
|
|
567
|
+
var acceptGrantSchema = z10.object({
|
|
568
|
+
token: z10.string().min(1)
|
|
469
569
|
});
|
|
470
|
-
var acceptGrantResponseSchema =
|
|
471
|
-
artifactId:
|
|
570
|
+
var acceptGrantResponseSchema = z10.object({
|
|
571
|
+
artifactId: z10.string(),
|
|
472
572
|
// The review shell URL to land on, carrying ?invited=1 for the banner.
|
|
473
|
-
artifactUrl:
|
|
474
|
-
slug:
|
|
475
|
-
title:
|
|
476
|
-
});
|
|
477
|
-
var workspaceGuestSchema =
|
|
478
|
-
artifacts:
|
|
479
|
-
|
|
480
|
-
artifactId:
|
|
481
|
-
grantId:
|
|
482
|
-
slug:
|
|
483
|
-
status:
|
|
484
|
-
title:
|
|
573
|
+
artifactUrl: z10.string().url(),
|
|
574
|
+
slug: z10.string(),
|
|
575
|
+
title: z10.string()
|
|
576
|
+
});
|
|
577
|
+
var workspaceGuestSchema = z10.object({
|
|
578
|
+
artifacts: z10.array(
|
|
579
|
+
z10.object({
|
|
580
|
+
artifactId: z10.string(),
|
|
581
|
+
grantId: z10.string(),
|
|
582
|
+
slug: z10.string(),
|
|
583
|
+
status: z10.enum(["pending", "active"]),
|
|
584
|
+
title: z10.string()
|
|
485
585
|
})
|
|
486
586
|
),
|
|
487
|
-
email:
|
|
488
|
-
image:
|
|
489
|
-
name:
|
|
490
|
-
userId:
|
|
491
|
-
});
|
|
492
|
-
var workspaceGuestListResponseSchema =
|
|
493
|
-
guests:
|
|
494
|
-
});
|
|
495
|
-
var sharedWithMeArtifactSchema =
|
|
496
|
-
artifactId:
|
|
497
|
-
slug:
|
|
498
|
-
title:
|
|
499
|
-
url:
|
|
587
|
+
email: z10.string().nullable(),
|
|
588
|
+
image: z10.string().nullable(),
|
|
589
|
+
name: z10.string().nullable(),
|
|
590
|
+
userId: z10.string().nullable()
|
|
591
|
+
});
|
|
592
|
+
var workspaceGuestListResponseSchema = z10.object({
|
|
593
|
+
guests: z10.array(workspaceGuestSchema)
|
|
594
|
+
});
|
|
595
|
+
var sharedWithMeArtifactSchema = z10.object({
|
|
596
|
+
artifactId: z10.string(),
|
|
597
|
+
slug: z10.string(),
|
|
598
|
+
title: z10.string(),
|
|
599
|
+
url: z10.string().url(),
|
|
500
600
|
// Who shared it, for the "Shared with me" list.
|
|
501
|
-
workspaceName:
|
|
601
|
+
workspaceName: z10.string()
|
|
502
602
|
});
|
|
503
|
-
var sharedWithMeListResponseSchema =
|
|
504
|
-
artifacts:
|
|
603
|
+
var sharedWithMeListResponseSchema = z10.object({
|
|
604
|
+
artifacts: z10.array(sharedWithMeArtifactSchema)
|
|
505
605
|
});
|
|
506
606
|
|
|
607
|
+
// ../shared/src/html-meta.ts
|
|
608
|
+
var HEAD_SCAN_CHARS = 64 * 1024;
|
|
609
|
+
|
|
507
610
|
// ../shared/src/me.ts
|
|
508
|
-
import { z as
|
|
611
|
+
import { z as z12 } from "zod";
|
|
509
612
|
|
|
510
613
|
// ../shared/src/workspaces.ts
|
|
511
|
-
import { z as
|
|
614
|
+
import { z as z11 } from "zod";
|
|
512
615
|
var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
|
|
513
|
-
var workspaceHandleUnavailableReasonSchema =
|
|
616
|
+
var workspaceHandleUnavailableReasonSchema = z11.enum([
|
|
514
617
|
"held",
|
|
515
618
|
"invalid",
|
|
516
619
|
"reserved",
|
|
517
620
|
"taken"
|
|
518
621
|
]);
|
|
519
|
-
var workspaceHandleAvailabilitySchema =
|
|
520
|
-
available:
|
|
521
|
-
handle:
|
|
622
|
+
var workspaceHandleAvailabilitySchema = z11.object({
|
|
623
|
+
available: z11.boolean(),
|
|
624
|
+
handle: z11.string(),
|
|
522
625
|
// Human-readable sentence for the field hint; null when available.
|
|
523
|
-
message:
|
|
626
|
+
message: z11.string().nullable(),
|
|
524
627
|
reason: workspaceHandleUnavailableReasonSchema.nullable()
|
|
525
628
|
});
|
|
526
|
-
var workspaceRoleSchema =
|
|
527
|
-
var workspaceInviteRoleSchema =
|
|
528
|
-
var workspaceHandleSchema =
|
|
629
|
+
var workspaceRoleSchema = z11.enum(["owner", "admin", "member"]);
|
|
630
|
+
var workspaceInviteRoleSchema = z11.enum(["admin", "member"]);
|
|
631
|
+
var workspaceHandleSchema = z11.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
|
|
529
632
|
message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
530
633
|
});
|
|
531
|
-
var createWorkspaceSchema =
|
|
634
|
+
var createWorkspaceSchema = z11.object({
|
|
532
635
|
handle: workspaceHandleSchema.optional(),
|
|
533
|
-
name:
|
|
636
|
+
name: z11.string().trim().min(1).max(100)
|
|
534
637
|
});
|
|
535
|
-
var updateWorkspaceSchema =
|
|
638
|
+
var updateWorkspaceSchema = z11.object({
|
|
536
639
|
handle: workspaceHandleSchema.optional(),
|
|
537
|
-
name:
|
|
640
|
+
name: z11.string().trim().min(1).max(100).optional()
|
|
538
641
|
}).refine((body) => body.handle !== void 0 || body.name !== void 0, {
|
|
539
642
|
message: "provide at least one of name or handle"
|
|
540
643
|
});
|
|
541
|
-
var workspaceSchema =
|
|
542
|
-
createdAt:
|
|
543
|
-
handle:
|
|
644
|
+
var workspaceSchema = z11.object({
|
|
645
|
+
createdAt: z11.string(),
|
|
646
|
+
handle: z11.string(),
|
|
544
647
|
// ISO timestamp when `{handle}.artifact.cafe` was provisioned, or null.
|
|
545
648
|
// Set by the one-time, self-serve enable flow (§4.4); never auto at
|
|
546
649
|
// create, and always null for personal workspaces.
|
|
547
|
-
hostnameProvisionedAt:
|
|
548
|
-
id:
|
|
650
|
+
hostnameProvisionedAt: z11.string().nullable(),
|
|
651
|
+
id: z11.string(),
|
|
549
652
|
// Auto-provisioned solo workspace, one per user: non-deletable, takes
|
|
550
653
|
// no members, renameable like any workspace.
|
|
551
|
-
isPersonal:
|
|
654
|
+
isPersonal: z11.boolean(),
|
|
552
655
|
// Uploaded logo URL, or null for the initials fallback. Set through the
|
|
553
656
|
// dedicated /workspaces/:id/avatar route, never the generic update.
|
|
554
|
-
logo:
|
|
555
|
-
name:
|
|
657
|
+
logo: z11.string().nullable(),
|
|
658
|
+
name: z11.string(),
|
|
556
659
|
role: workspaceRoleSchema.nullable(),
|
|
557
|
-
updatedAt:
|
|
660
|
+
updatedAt: z11.string()
|
|
558
661
|
});
|
|
559
|
-
var workspaceListResponseSchema =
|
|
560
|
-
workspaces:
|
|
662
|
+
var workspaceListResponseSchema = z11.object({
|
|
663
|
+
workspaces: z11.array(workspaceSchema)
|
|
561
664
|
});
|
|
562
|
-
var deleteWorkspaceResponseSchema =
|
|
563
|
-
deleted:
|
|
665
|
+
var deleteWorkspaceResponseSchema = z11.object({
|
|
666
|
+
deleted: z11.literal(true)
|
|
564
667
|
});
|
|
565
|
-
var workspaceMemberSchema =
|
|
566
|
-
createdAt:
|
|
567
|
-
email:
|
|
568
|
-
image:
|
|
569
|
-
name:
|
|
668
|
+
var workspaceMemberSchema = z11.object({
|
|
669
|
+
createdAt: z11.string(),
|
|
670
|
+
email: z11.string().email(),
|
|
671
|
+
image: z11.string().nullable(),
|
|
672
|
+
name: z11.string(),
|
|
570
673
|
role: workspaceRoleSchema,
|
|
571
|
-
userId:
|
|
674
|
+
userId: z11.string()
|
|
572
675
|
});
|
|
573
|
-
var workspaceMemberListResponseSchema =
|
|
574
|
-
members:
|
|
676
|
+
var workspaceMemberListResponseSchema = z11.object({
|
|
677
|
+
members: z11.array(workspaceMemberSchema)
|
|
575
678
|
});
|
|
576
|
-
var updateWorkspaceMemberSchema =
|
|
679
|
+
var updateWorkspaceMemberSchema = z11.object({
|
|
577
680
|
role: workspaceRoleSchema
|
|
578
681
|
});
|
|
579
|
-
var removeWorkspaceMemberResponseSchema =
|
|
580
|
-
removed:
|
|
682
|
+
var removeWorkspaceMemberResponseSchema = z11.object({
|
|
683
|
+
removed: z11.literal(true)
|
|
581
684
|
});
|
|
582
|
-
var createWorkspaceInviteSchema =
|
|
583
|
-
email:
|
|
584
|
-
expiresAt:
|
|
585
|
-
maxUses:
|
|
685
|
+
var createWorkspaceInviteSchema = z11.object({
|
|
686
|
+
email: z11.string().trim().toLowerCase().email().optional(),
|
|
687
|
+
expiresAt: z11.string().datetime(),
|
|
688
|
+
maxUses: z11.number().int().positive().optional(),
|
|
586
689
|
role: workspaceInviteRoleSchema
|
|
587
690
|
});
|
|
588
|
-
var workspaceInviteSchema =
|
|
589
|
-
createdAt:
|
|
590
|
-
email:
|
|
591
|
-
expiresAt:
|
|
592
|
-
id:
|
|
593
|
-
maxUses:
|
|
691
|
+
var workspaceInviteSchema = z11.object({
|
|
692
|
+
createdAt: z11.string(),
|
|
693
|
+
email: z11.string().email().nullable(),
|
|
694
|
+
expiresAt: z11.string(),
|
|
695
|
+
id: z11.string(),
|
|
696
|
+
maxUses: z11.number().int().positive().nullable(),
|
|
594
697
|
role: workspaceInviteRoleSchema,
|
|
595
|
-
tokenPrefix:
|
|
596
|
-
useCount:
|
|
698
|
+
tokenPrefix: z11.string(),
|
|
699
|
+
useCount: z11.number().int().nonnegative()
|
|
597
700
|
});
|
|
598
701
|
var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
|
|
599
|
-
url:
|
|
702
|
+
url: z11.string().url()
|
|
600
703
|
});
|
|
601
|
-
var workspaceInviteListResponseSchema =
|
|
602
|
-
invites:
|
|
704
|
+
var workspaceInviteListResponseSchema = z11.object({
|
|
705
|
+
invites: z11.array(workspaceInviteSchema)
|
|
603
706
|
});
|
|
604
|
-
var revokeWorkspaceInviteResponseSchema =
|
|
605
|
-
revoked:
|
|
707
|
+
var revokeWorkspaceInviteResponseSchema = z11.object({
|
|
708
|
+
revoked: z11.literal(true)
|
|
606
709
|
});
|
|
607
|
-
var acceptWorkspaceInviteSchema =
|
|
608
|
-
token:
|
|
710
|
+
var acceptWorkspaceInviteSchema = z11.object({
|
|
711
|
+
token: z11.string().min(1)
|
|
609
712
|
});
|
|
610
|
-
var acceptWorkspaceInviteResponseSchema =
|
|
713
|
+
var acceptWorkspaceInviteResponseSchema = z11.object({
|
|
611
714
|
role: workspaceRoleSchema,
|
|
612
715
|
workspace: workspaceSchema
|
|
613
716
|
});
|
|
614
|
-
var folderVisibilitySchema =
|
|
615
|
-
var artifactVisibilitySchema =
|
|
717
|
+
var folderVisibilitySchema = z11.enum(["public", "workspace"]);
|
|
718
|
+
var artifactVisibilitySchema = z11.enum([
|
|
616
719
|
"public",
|
|
617
720
|
"unlisted",
|
|
618
721
|
"workspace",
|
|
619
722
|
"private"
|
|
620
723
|
]);
|
|
621
|
-
var createFolderSchema =
|
|
622
|
-
|
|
724
|
+
var createFolderSchema = z11.object({
|
|
725
|
+
// Optional custom icon; omitted (or null) leaves the default glyph.
|
|
726
|
+
emoji: iconEmojiSchema.optional(),
|
|
727
|
+
name: z11.string().trim().min(1).max(100),
|
|
623
728
|
visibility: folderVisibilitySchema
|
|
624
729
|
});
|
|
625
|
-
var updateFolderSchema =
|
|
730
|
+
var updateFolderSchema = z11.object({
|
|
626
731
|
// With `visibility`: bulk-update the folder's artifacts to match
|
|
627
732
|
// (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
|
|
628
|
-
cascade:
|
|
629
|
-
|
|
630
|
-
|
|
733
|
+
cascade: z11.boolean().optional(),
|
|
734
|
+
// Explicit null clears the icon back to the default glyph, so this is
|
|
735
|
+
// a field where "sent as null" and "omitted" mean different things.
|
|
736
|
+
emoji: iconEmojiSchema.optional(),
|
|
737
|
+
name: z11.string().trim().min(1).max(100).optional(),
|
|
738
|
+
sortOrder: z11.number().int().min(0).optional(),
|
|
631
739
|
visibility: folderVisibilitySchema.optional()
|
|
632
740
|
}).refine(
|
|
633
|
-
(body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0,
|
|
634
|
-
{ message: "provide at least one of name, visibility, sortOrder" }
|
|
741
|
+
(body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0 || body.emoji !== void 0,
|
|
742
|
+
{ message: "provide at least one of name, visibility, sortOrder, emoji" }
|
|
635
743
|
).refine((body) => !body.cascade || body.visibility !== void 0, {
|
|
636
744
|
message: "cascade requires visibility"
|
|
637
745
|
});
|
|
638
|
-
var folderSchema =
|
|
639
|
-
createdAt:
|
|
640
|
-
|
|
641
|
-
|
|
746
|
+
var folderSchema = z11.object({
|
|
747
|
+
createdAt: z11.string(),
|
|
748
|
+
// Custom icon, or null to render the default folder glyph.
|
|
749
|
+
emoji: z11.string().nullable(),
|
|
750
|
+
id: z11.string(),
|
|
751
|
+
name: z11.string(),
|
|
642
752
|
// Permalink segment for the public folder page (/w/{handle}/f/{slug}).
|
|
643
753
|
// Server-derived from the name; renames regenerate it.
|
|
644
|
-
slug:
|
|
645
|
-
sortOrder:
|
|
646
|
-
updatedAt:
|
|
754
|
+
slug: z11.string(),
|
|
755
|
+
sortOrder: z11.number().int(),
|
|
756
|
+
updatedAt: z11.string(),
|
|
647
757
|
visibility: folderVisibilitySchema,
|
|
648
|
-
workspaceId:
|
|
758
|
+
workspaceId: z11.string()
|
|
649
759
|
});
|
|
650
|
-
var folderListResponseSchema =
|
|
651
|
-
folders:
|
|
760
|
+
var folderListResponseSchema = z11.object({
|
|
761
|
+
folders: z11.array(folderSchema)
|
|
652
762
|
});
|
|
653
|
-
var deleteFolderResponseSchema =
|
|
654
|
-
deleted:
|
|
763
|
+
var deleteFolderResponseSchema = z11.object({
|
|
764
|
+
deleted: z11.literal(true)
|
|
655
765
|
});
|
|
656
|
-
var workspaceArtifactSchema =
|
|
657
|
-
createdAt:
|
|
658
|
-
currentVersionNumber:
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
766
|
+
var workspaceArtifactSchema = z11.object({
|
|
767
|
+
createdAt: z11.string(),
|
|
768
|
+
currentVersionNumber: z11.number().int().nullable(),
|
|
769
|
+
// Custom icon, or null to render the default document glyph.
|
|
770
|
+
emoji: z11.string().nullable(),
|
|
771
|
+
folderId: z11.string().nullable(),
|
|
772
|
+
id: z11.string(),
|
|
773
|
+
openCommentCount: z11.number().int().nonnegative(),
|
|
662
774
|
reviewMode: reviewModeSchema,
|
|
663
|
-
slug:
|
|
664
|
-
title:
|
|
665
|
-
updatedAt:
|
|
666
|
-
url:
|
|
667
|
-
versionCount:
|
|
775
|
+
slug: z11.string(),
|
|
776
|
+
title: z11.string(),
|
|
777
|
+
updatedAt: z11.string(),
|
|
778
|
+
url: z11.string().url(),
|
|
779
|
+
versionCount: z11.number().int().nonnegative(),
|
|
668
780
|
visibility: artifactVisibilitySchema
|
|
669
781
|
});
|
|
670
|
-
var workspaceArtifactListResponseSchema =
|
|
671
|
-
artifacts:
|
|
782
|
+
var workspaceArtifactListResponseSchema = z11.object({
|
|
783
|
+
artifacts: z11.array(workspaceArtifactSchema)
|
|
672
784
|
});
|
|
673
|
-
var workspaceArtifactSearchResultSchema =
|
|
674
|
-
id:
|
|
675
|
-
slug:
|
|
676
|
-
title:
|
|
785
|
+
var workspaceArtifactSearchResultSchema = z11.object({
|
|
786
|
+
id: z11.string(),
|
|
787
|
+
slug: z11.string(),
|
|
788
|
+
title: z11.string()
|
|
677
789
|
});
|
|
678
|
-
var workspaceArtifactSearchResponseSchema =
|
|
679
|
-
results:
|
|
790
|
+
var workspaceArtifactSearchResponseSchema = z11.object({
|
|
791
|
+
results: z11.array(workspaceArtifactSearchResultSchema)
|
|
680
792
|
});
|
|
681
793
|
|
|
682
794
|
// ../shared/src/me.ts
|
|
683
|
-
var
|
|
684
|
-
|
|
685
|
-
|
|
795
|
+
var personSchema = z12.object({
|
|
796
|
+
image: z12.string().nullable(),
|
|
797
|
+
name: z12.string()
|
|
798
|
+
});
|
|
799
|
+
var ownArtifactSchema = z12.object({
|
|
800
|
+
// Everyone else who has published a version, oldest first. Any member may
|
|
801
|
+
// publish a new one (SPEC-2 §5.1), so an artifact's byline is often more
|
|
802
|
+
// than one person and the card stacks their faces rather than crediting
|
|
803
|
+
// whoever shipped last. Excludes `publishedBy`; empty on a solo artifact.
|
|
804
|
+
coPublishers: z12.array(personSchema),
|
|
805
|
+
createdAt: z12.string(),
|
|
806
|
+
// SPEC-2 §5.1 — who *made* the artifact: the author of its first
|
|
807
|
+
// finalized version, the same row `viewerCanManage` is decided on. Not
|
|
808
|
+
// the same person as `publishedBy` once an agent ships v2, which is why
|
|
809
|
+
// the dashboard filters on both. Null for a token-only publish.
|
|
810
|
+
createdBy: z12.object({ image: z12.string().nullable(), name: z12.string() }).nullable(),
|
|
811
|
+
currentVersionNumber: z12.number().nullable(),
|
|
812
|
+
// Custom icon, or null to render the default document glyph.
|
|
813
|
+
emoji: z12.string().nullable(),
|
|
686
814
|
// Owned artifacts normally never expire; non-null only while an
|
|
687
815
|
// expiry is still set (e.g. an unclaimed publish surfaced here later).
|
|
688
|
-
expiresAt:
|
|
816
|
+
expiresAt: z12.string().nullable(),
|
|
689
817
|
// SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
|
|
690
818
|
// both null for personal artifacts. `folderId` drives the dashboard's
|
|
691
|
-
// move control and folder filter; `folderName` is the display label
|
|
692
|
-
|
|
693
|
-
|
|
819
|
+
// move control and folder filter; `folderName` is the display label,
|
|
820
|
+
// `folderEmoji` its icon (null = the default folder glyph).
|
|
821
|
+
folderEmoji: z12.string().nullable(),
|
|
822
|
+
folderId: z12.string().nullable(),
|
|
823
|
+
folderName: z12.string().nullable(),
|
|
694
824
|
// Whether a publish token is still active — revocable from the
|
|
695
825
|
// dashboard (§6.18); the token itself is never returned.
|
|
696
|
-
hasPublishToken:
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
826
|
+
hasPublishToken: z12.boolean(),
|
|
827
|
+
// Owner preference: hide the version list from reviewers in the review
|
|
828
|
+
// shell. Managers still see it.
|
|
829
|
+
hideVersions: z12.boolean(),
|
|
830
|
+
id: z12.string(),
|
|
831
|
+
openThreads: z12.number(),
|
|
832
|
+
protected: z12.boolean(),
|
|
700
833
|
// Who published the current version (falling back to the claiming
|
|
701
834
|
// owner) — the dashboard shows a name and avatar per artifact so a
|
|
702
835
|
// shared workspace reads as people's work, not anonymous rows. Null
|
|
703
836
|
// when the publish was token-only and the artifact is unclaimed.
|
|
704
|
-
publishedBy:
|
|
837
|
+
publishedBy: personSchema.nullable(),
|
|
705
838
|
// SPEC-2 §5.8 — the review dial, so the dashboard's settings dialog can
|
|
706
839
|
// render current state without a second fetch.
|
|
707
840
|
reviewMode: reviewModeSchema,
|
|
708
|
-
slug:
|
|
709
|
-
title:
|
|
710
|
-
totalViews:
|
|
841
|
+
slug: z12.string(),
|
|
842
|
+
title: z12.string(),
|
|
843
|
+
totalViews: z12.number(),
|
|
711
844
|
// SPEC §6.20 — messages by others newer than the viewer's last-seen
|
|
712
845
|
// watermark. 0 for signed-out/API-key-less contexts.
|
|
713
|
-
unreadComments:
|
|
714
|
-
updatedAt:
|
|
715
|
-
url:
|
|
716
|
-
versionCount:
|
|
846
|
+
unreadComments: z12.number(),
|
|
847
|
+
updatedAt: z12.string(),
|
|
848
|
+
url: z12.string(),
|
|
849
|
+
versionCount: z12.number(),
|
|
717
850
|
// SPEC-2 §5.1 — whether the caller clears the manage bar for this
|
|
718
851
|
// artifact (workspace admin, or the person who first published it).
|
|
719
852
|
// Only a manager's folder move inherits the folder's visibility, so the
|
|
720
853
|
// dashboard warns exactly the people the change can actually affect, and
|
|
721
854
|
// only a manager is offered the Share entry (§5.7).
|
|
722
|
-
viewerCanManage:
|
|
855
|
+
viewerCanManage: z12.boolean(),
|
|
723
856
|
// SPEC-2 §5.2 — the artifact's own visibility, the single enforcement
|
|
724
857
|
// source. The dashboard needs it to tell whether a folder move would
|
|
725
|
-
// widen exposure before it sends the PATCH (§3.4), and to render the
|
|
726
|
-
// glyph on
|
|
858
|
+
// widen exposure before it sends the PATCH (§3.4), and to render the
|
|
859
|
+
// audience glyph — one per level — on rows and cards (§5.7).
|
|
727
860
|
visibility: artifactVisibilitySchema
|
|
728
861
|
});
|
|
729
|
-
var ownArtifactsResponseSchema =
|
|
730
|
-
artifacts:
|
|
862
|
+
var ownArtifactsResponseSchema = z12.object({
|
|
863
|
+
artifacts: z12.array(ownArtifactSchema)
|
|
731
864
|
});
|
|
732
|
-
var revokePublishTokenResponseSchema =
|
|
733
|
-
revoked:
|
|
865
|
+
var revokePublishTokenResponseSchema = z12.object({
|
|
866
|
+
revoked: z12.boolean()
|
|
734
867
|
});
|
|
735
868
|
|
|
736
869
|
// ../shared/src/owner.ts
|
|
737
|
-
import { z as
|
|
870
|
+
import { z as z13 } from "zod";
|
|
738
871
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
|
|
739
|
-
var claimResponseSchema =
|
|
740
|
-
artifactId:
|
|
741
|
-
slug:
|
|
742
|
-
title:
|
|
743
|
-
url:
|
|
744
|
-
});
|
|
745
|
-
var updateArtifactSchema =
|
|
872
|
+
var claimResponseSchema = z13.object({
|
|
873
|
+
artifactId: z13.string(),
|
|
874
|
+
slug: z13.string(),
|
|
875
|
+
title: z13.string(),
|
|
876
|
+
url: z13.string().url()
|
|
877
|
+
});
|
|
878
|
+
var updateArtifactSchema = z13.object({
|
|
879
|
+
// Custom icon; explicit null clears it back to the default glyph.
|
|
880
|
+
// Part of what the artifact *is* (like the title), so it takes the
|
|
881
|
+
// same manage rights as title/slug — not the member-level folder move.
|
|
882
|
+
emoji: iconEmojiSchema.optional(),
|
|
746
883
|
// SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
|
|
747
884
|
// folder; `workspaceId: null` moves the artifact out of its workspace
|
|
748
885
|
// (§5.5 ownership transfer).
|
|
749
|
-
folderId:
|
|
750
|
-
|
|
886
|
+
folderId: z13.string().min(1).nullable().optional(),
|
|
887
|
+
// Keep the version list out of the review shell. Presentation only:
|
|
888
|
+
// versions stay immutable, and a `/vN/` URL still resolves.
|
|
889
|
+
hideVersions: z13.boolean().optional(),
|
|
890
|
+
slug: z13.string().min(3).max(63).regex(SLUG_PATTERN, {
|
|
751
891
|
message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
|
|
752
892
|
}).optional(),
|
|
753
|
-
title:
|
|
893
|
+
title: z13.string().min(1).max(200).optional(),
|
|
754
894
|
visibility: artifactVisibilitySchema.optional(),
|
|
755
|
-
workspaceId:
|
|
895
|
+
workspaceId: z13.string().min(1).nullable().optional()
|
|
756
896
|
}).refine(
|
|
757
|
-
(body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0,
|
|
897
|
+
(body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0 || body.emoji !== void 0 || body.hideVersions !== void 0,
|
|
758
898
|
{
|
|
759
|
-
message: "provide at least one of title, slug, visibility, folderId, workspaceId"
|
|
899
|
+
message: "provide at least one of title, slug, visibility, folderId, workspaceId, emoji, hideVersions"
|
|
760
900
|
}
|
|
761
901
|
);
|
|
762
|
-
var deleteArtifactResponseSchema =
|
|
763
|
-
deleted:
|
|
902
|
+
var deleteArtifactResponseSchema = z13.object({
|
|
903
|
+
deleted: z13.literal(true)
|
|
764
904
|
});
|
|
765
905
|
|
|
766
906
|
// ../shared/src/presence.ts
|
|
767
|
-
import { z as
|
|
907
|
+
import { z as z14 } from "zod";
|
|
768
908
|
var AGENT_STATES = ["listening", "editing", "publishing"];
|
|
769
|
-
var agentStateSchema =
|
|
909
|
+
var agentStateSchema = z14.enum(AGENT_STATES);
|
|
770
910
|
var AGENT_PRESENCE = {
|
|
771
911
|
editingTtlSeconds: 300,
|
|
772
912
|
heartbeatSeconds: 15,
|
|
773
913
|
shellPollSeconds: 15,
|
|
774
914
|
ttlSeconds: 45
|
|
775
915
|
};
|
|
776
|
-
var agentHeartbeatBodySchema =
|
|
916
|
+
var agentHeartbeatBodySchema = z14.object({
|
|
777
917
|
state: agentStateSchema.optional()
|
|
778
918
|
});
|
|
779
|
-
var agentHeartbeatResponseSchema =
|
|
780
|
-
listening:
|
|
919
|
+
var agentHeartbeatResponseSchema = z14.object({
|
|
920
|
+
listening: z14.literal(true),
|
|
781
921
|
state: agentStateSchema.optional(),
|
|
782
|
-
ttlSeconds:
|
|
922
|
+
ttlSeconds: z14.number().int().positive()
|
|
783
923
|
});
|
|
784
|
-
var agentPresenceSchema =
|
|
785
|
-
|
|
924
|
+
var agentPresenceSchema = z14.object({
|
|
925
|
+
agent: z14.object({ id: z14.string().nullable(), label: z14.string() }).nullable().default(null),
|
|
926
|
+
listening: z14.boolean(),
|
|
786
927
|
state: agentStateSchema.nullable().optional(),
|
|
787
|
-
versionNumber:
|
|
928
|
+
versionNumber: z14.number().int().positive().nullable()
|
|
788
929
|
});
|
|
789
930
|
|
|
790
931
|
// ../shared/src/protection.ts
|
|
791
|
-
import { z as
|
|
932
|
+
import { z as z15 } from "zod";
|
|
792
933
|
var PASSWORD_MIN = 6;
|
|
793
934
|
var PASSWORD_MAX = 128;
|
|
794
|
-
var gatePasswordSchema =
|
|
795
|
-
var setProtectionSchema =
|
|
935
|
+
var gatePasswordSchema = z15.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
|
|
936
|
+
var setProtectionSchema = z15.object({
|
|
796
937
|
password: gatePasswordSchema.nullable()
|
|
797
938
|
});
|
|
798
|
-
var protectionResponseSchema =
|
|
799
|
-
protected:
|
|
939
|
+
var protectionResponseSchema = z15.object({
|
|
940
|
+
protected: z15.boolean()
|
|
800
941
|
});
|
|
801
|
-
var unlockSchema =
|
|
802
|
-
password:
|
|
942
|
+
var unlockSchema = z15.object({
|
|
943
|
+
password: z15.string().min(1).max(1024)
|
|
803
944
|
});
|
|
804
|
-
var unlockResponseSchema =
|
|
805
|
-
unlocked:
|
|
945
|
+
var unlockResponseSchema = z15.object({
|
|
946
|
+
unlocked: z15.literal(true)
|
|
806
947
|
});
|
|
807
948
|
|
|
808
949
|
// ../shared/src/report.ts
|
|
809
|
-
import { z as
|
|
810
|
-
var reportRequestSchema =
|
|
811
|
-
reason:
|
|
950
|
+
import { z as z16 } from "zod";
|
|
951
|
+
var reportRequestSchema = z16.object({
|
|
952
|
+
reason: z16.string().max(1e3).optional()
|
|
812
953
|
});
|
|
813
|
-
var reportResponseSchema =
|
|
814
|
-
reported:
|
|
954
|
+
var reportResponseSchema = z16.object({
|
|
955
|
+
reported: z16.literal(true)
|
|
815
956
|
});
|
|
816
957
|
|
|
817
958
|
// ../shared/src/reviewers.ts
|
|
818
|
-
import { z as
|
|
819
|
-
var artifactReviewerSchema =
|
|
820
|
-
avatarUrl:
|
|
821
|
-
displayName:
|
|
959
|
+
import { z as z17 } from "zod";
|
|
960
|
+
var artifactReviewerSchema = z17.object({
|
|
961
|
+
avatarUrl: z17.string().nullable(),
|
|
962
|
+
displayName: z17.string(),
|
|
822
963
|
// Exactly one of userId/guestId is set, per `kind`.
|
|
823
|
-
guestId:
|
|
964
|
+
guestId: z17.string().nullable(),
|
|
824
965
|
// "account", not commentAuthorSchema's "member" — the whole point here is
|
|
825
966
|
// that these people need not be workspace members. A guest can be named in
|
|
826
967
|
// a comment but never notified: guest_identities carries a display name and
|
|
827
968
|
// no email, so there is nowhere to send anything.
|
|
828
|
-
kind:
|
|
829
|
-
userId:
|
|
969
|
+
kind: z17.enum(["account", "guest"]),
|
|
970
|
+
userId: z17.string().nullable()
|
|
830
971
|
});
|
|
831
|
-
var artifactReviewerListResponseSchema =
|
|
832
|
-
reviewers:
|
|
972
|
+
var artifactReviewerListResponseSchema = z17.object({
|
|
973
|
+
reviewers: z17.array(artifactReviewerSchema)
|
|
833
974
|
});
|
|
834
975
|
|
|
835
976
|
// ../shared/src/stats.ts
|
|
836
|
-
import { z as
|
|
837
|
-
var avgViewSecondsSchema =
|
|
838
|
-
var versionViewStatsSchema =
|
|
977
|
+
import { z as z18 } from "zod";
|
|
978
|
+
var avgViewSecondsSchema = z18.number().int().nonnegative().nullable();
|
|
979
|
+
var versionViewStatsSchema = z18.object({
|
|
839
980
|
avgViewSeconds: avgViewSecondsSchema,
|
|
840
981
|
// Views from inside the review shell's iframe vs. direct visits to
|
|
841
982
|
// the render URL. views = embeddedViews + directViews.
|
|
842
|
-
directViews:
|
|
843
|
-
embeddedViews:
|
|
983
|
+
directViews: z18.number().int().nonnegative(),
|
|
984
|
+
embeddedViews: z18.number().int().nonnegative(),
|
|
844
985
|
// Distinct hashed viewer IPs across the version's lifetime.
|
|
845
|
-
uniqueViewers:
|
|
846
|
-
versionNumber:
|
|
847
|
-
views:
|
|
986
|
+
uniqueViewers: z18.number().int().nonnegative(),
|
|
987
|
+
versionNumber: z18.number().int().positive(),
|
|
988
|
+
views: z18.number().int().nonnegative()
|
|
848
989
|
});
|
|
849
|
-
var dailyViewsSchema =
|
|
990
|
+
var dailyViewsSchema = z18.object({
|
|
850
991
|
avgViewSeconds: avgViewSecondsSchema,
|
|
851
992
|
// UTC day, YYYY-MM-DD.
|
|
852
|
-
day:
|
|
853
|
-
uniqueViewers:
|
|
854
|
-
views:
|
|
993
|
+
day: z18.string(),
|
|
994
|
+
uniqueViewers: z18.number().int().nonnegative(),
|
|
995
|
+
views: z18.number().int().nonnegative()
|
|
855
996
|
});
|
|
856
|
-
var artifactStatsResponseSchema =
|
|
997
|
+
var artifactStatsResponseSchema = z18.object({
|
|
857
998
|
avgViewSeconds: avgViewSecondsSchema,
|
|
858
|
-
daily:
|
|
999
|
+
daily: z18.array(dailyViewsSchema),
|
|
859
1000
|
// Distinct hashed viewer IPs across all versions and days.
|
|
860
|
-
totalUniqueViewers:
|
|
861
|
-
totalViews:
|
|
862
|
-
versions:
|
|
1001
|
+
totalUniqueViewers: z18.number().int().nonnegative(),
|
|
1002
|
+
totalViews: z18.number().int().nonnegative(),
|
|
1003
|
+
versions: z18.array(versionViewStatsSchema)
|
|
863
1004
|
});
|
|
864
1005
|
|
|
865
1006
|
// ../shared/src/versions.ts
|
|
866
|
-
import { z as
|
|
867
|
-
var versionSourceSchema =
|
|
868
|
-
agentName:
|
|
869
|
-
type:
|
|
1007
|
+
import { z as z19 } from "zod";
|
|
1008
|
+
var versionSourceSchema = z19.object({
|
|
1009
|
+
agentName: z19.string().nullable(),
|
|
1010
|
+
type: z19.string()
|
|
870
1011
|
});
|
|
871
|
-
var versionChangesSchema =
|
|
872
|
-
added:
|
|
873
|
-
changed:
|
|
874
|
-
removed:
|
|
1012
|
+
var versionChangesSchema = z19.object({
|
|
1013
|
+
added: z19.array(z19.string()),
|
|
1014
|
+
changed: z19.array(z19.string()),
|
|
1015
|
+
removed: z19.array(z19.string())
|
|
875
1016
|
});
|
|
876
|
-
var versionSummarySchema =
|
|
1017
|
+
var versionSummarySchema = z19.object({
|
|
877
1018
|
// Absent for the first version — there is no previous version to diff against.
|
|
878
1019
|
changes: versionChangesSchema.nullable(),
|
|
879
|
-
createdAt:
|
|
880
|
-
entryUrl:
|
|
881
|
-
fileCount:
|
|
882
|
-
id:
|
|
883
|
-
isCurrent:
|
|
1020
|
+
createdAt: z19.string(),
|
|
1021
|
+
entryUrl: z19.string().url(),
|
|
1022
|
+
fileCount: z19.number().int().nonnegative(),
|
|
1023
|
+
id: z19.string(),
|
|
1024
|
+
isCurrent: z19.boolean(),
|
|
884
1025
|
// Author-supplied bullet-point changelog; null when the publish omitted it.
|
|
885
|
-
notes:
|
|
1026
|
+
notes: z19.string().nullable(),
|
|
1027
|
+
// The account that published this version, with their own avatar where
|
|
1028
|
+
// they've set one. Any workspace member can publish a new version
|
|
1029
|
+
// (SPEC-2 §5.1), so "which teammate" outranks "which agent" — the agent
|
|
1030
|
+
// is the tool, this is the person. Null for token-only publishes, which
|
|
1031
|
+
// carry no account.
|
|
1032
|
+
publishedBy: z19.object({ image: z19.string().nullable(), name: z19.string() }).nullable(),
|
|
886
1033
|
source: versionSourceSchema,
|
|
887
|
-
totalSizeBytes:
|
|
888
|
-
versionNumber:
|
|
1034
|
+
totalSizeBytes: z19.number().int().nonnegative(),
|
|
1035
|
+
versionNumber: z19.number().int().positive()
|
|
889
1036
|
});
|
|
890
|
-
var versionListResponseSchema =
|
|
891
|
-
versions:
|
|
1037
|
+
var versionListResponseSchema = z19.object({
|
|
1038
|
+
versions: z19.array(versionSummarySchema)
|
|
892
1039
|
});
|
|
893
1040
|
|
|
1041
|
+
// src/agent-identity.ts
|
|
1042
|
+
function agentLabel(explicit) {
|
|
1043
|
+
if (explicit !== void 0) {
|
|
1044
|
+
return explicit.trim() || void 0;
|
|
1045
|
+
}
|
|
1046
|
+
return detectAgentFromEnv(process.env);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
894
1049
|
// src/api.ts
|
|
895
1050
|
var ApiRequestError = class extends Error {
|
|
896
1051
|
constructor(status2, code, message, retryAfter) {
|
|
@@ -1108,11 +1263,13 @@ function createApiClient(apiUrl2, publishToken) {
|
|
|
1108
1263
|
// SPEC §6.5 — post a reply into a thread. Authorized by this client's
|
|
1109
1264
|
// credential: the artifact's publish token (the reply renders as the
|
|
1110
1265
|
// agent) or the owner's account key (renders as the owner). No guest
|
|
1111
|
-
// payload — the credential is the identity.
|
|
1112
|
-
|
|
1266
|
+
// payload — the credential is the identity. `via` names the tool: it
|
|
1267
|
+
// becomes the agent's byline on a token reply, a "via" label on an
|
|
1268
|
+
// account one.
|
|
1269
|
+
async replyThread(threadId, body, via) {
|
|
1113
1270
|
return request(
|
|
1114
1271
|
`${base}/api/v1/comment-threads/${threadId}/messages`,
|
|
1115
|
-
{ body: JSON.stringify({ body }), headers, method: "POST" },
|
|
1272
|
+
{ body: JSON.stringify({ body, via }), headers, method: "POST" },
|
|
1116
1273
|
(d) => commentThreadSchema.parse(d)
|
|
1117
1274
|
);
|
|
1118
1275
|
},
|
|
@@ -1189,17 +1346,17 @@ import {
|
|
|
1189
1346
|
writeFileSync as writeFileSync2
|
|
1190
1347
|
} from "fs";
|
|
1191
1348
|
import path2 from "path";
|
|
1192
|
-
import { z as
|
|
1349
|
+
import { z as z21 } from "zod";
|
|
1193
1350
|
|
|
1194
1351
|
// src/credentials.ts
|
|
1195
1352
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
1196
1353
|
import { homedir } from "os";
|
|
1197
1354
|
import path from "path";
|
|
1198
|
-
import { z as
|
|
1355
|
+
import { z as z20 } from "zod";
|
|
1199
1356
|
var DEFAULT_API_URL = "https://artifact.cafe";
|
|
1200
|
-
var credentialsSchema =
|
|
1201
|
-
keys:
|
|
1202
|
-
version:
|
|
1357
|
+
var credentialsSchema = z20.object({
|
|
1358
|
+
keys: z20.record(z20.string(), z20.string()),
|
|
1359
|
+
version: z20.literal(1)
|
|
1203
1360
|
});
|
|
1204
1361
|
function credentialsPath() {
|
|
1205
1362
|
return path.join(homedir(), ".artifactcafe", "credentials");
|
|
@@ -1249,24 +1406,24 @@ function resolveApiKey(apiUrl2, flag) {
|
|
|
1249
1406
|
}
|
|
1250
1407
|
|
|
1251
1408
|
// src/config.ts
|
|
1252
|
-
var configSchema =
|
|
1253
|
-
apiUrl:
|
|
1254
|
-
artifactId:
|
|
1255
|
-
folder:
|
|
1256
|
-
folderId:
|
|
1409
|
+
var configSchema = z21.object({
|
|
1410
|
+
apiUrl: z21.string(),
|
|
1411
|
+
artifactId: z21.string(),
|
|
1412
|
+
folder: z21.string().optional(),
|
|
1413
|
+
folderId: z21.string().optional(),
|
|
1257
1414
|
// Optional: a folder linked by `link` to an artifact you own carries no
|
|
1258
1415
|
// token — owner actions authorize via your stored login instead. A first
|
|
1259
1416
|
// publish still writes the anonymous-mode token here.
|
|
1260
|
-
publishToken:
|
|
1261
|
-
slug:
|
|
1262
|
-
url:
|
|
1263
|
-
workspace:
|
|
1264
|
-
workspaceId:
|
|
1265
|
-
workspaceKind:
|
|
1266
|
-
});
|
|
1267
|
-
var projectConfigSchema =
|
|
1268
|
-
folder:
|
|
1269
|
-
workspace:
|
|
1417
|
+
publishToken: z21.string().optional(),
|
|
1418
|
+
slug: z21.string(),
|
|
1419
|
+
url: z21.string(),
|
|
1420
|
+
workspace: z21.string().optional(),
|
|
1421
|
+
workspaceId: z21.string().optional(),
|
|
1422
|
+
workspaceKind: z21.enum(["personal", "workspace"]).optional()
|
|
1423
|
+
});
|
|
1424
|
+
var projectConfigSchema = z21.object({
|
|
1425
|
+
folder: z21.string().trim().min(1).optional(),
|
|
1426
|
+
workspace: z21.string().trim().min(1)
|
|
1270
1427
|
}).strict();
|
|
1271
1428
|
var CONFIG_DIR = ".artifactcafe";
|
|
1272
1429
|
var CONFIG_FILE = "config.json";
|
|
@@ -1302,7 +1459,7 @@ function readProjectConfig(dir) {
|
|
|
1302
1459
|
JSON.parse(readFileSync2(file, "utf8"))
|
|
1303
1460
|
);
|
|
1304
1461
|
} catch (error) {
|
|
1305
|
-
const message = error instanceof
|
|
1462
|
+
const message = error instanceof z21.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
|
|
1306
1463
|
throw new Error(`Invalid ${file}: ${message}.`);
|
|
1307
1464
|
}
|
|
1308
1465
|
}
|
|
@@ -1367,7 +1524,8 @@ function resolveArtifactTarget(dir, opts = {}) {
|
|
|
1367
1524
|
);
|
|
1368
1525
|
}
|
|
1369
1526
|
const folderToken = artifactId === config?.artifactId ? config.publishToken : void 0;
|
|
1370
|
-
const
|
|
1527
|
+
const apiKey = resolveApiKey(apiUrl2, opts.apiKey);
|
|
1528
|
+
const credential = (apiKey?.startsWith("aat_") ? apiKey : void 0) ?? folderToken ?? process.env.ARTIFACT_CAFE_TOKEN ?? apiKey;
|
|
1371
1529
|
const url = artifactId === config?.artifactId ? config.url : void 0;
|
|
1372
1530
|
return { apiUrl: apiUrl2, artifactId, credential, url };
|
|
1373
1531
|
}
|
|
@@ -1441,7 +1599,10 @@ async function comment(opts) {
|
|
|
1441
1599
|
anchor,
|
|
1442
1600
|
body,
|
|
1443
1601
|
versionId: artifact.currentVersion.id,
|
|
1444
|
-
|
|
1602
|
+
// An account comment keeps its own name and takes the tool as a "via"
|
|
1603
|
+
// label (§6.4) — detected when neither flag is given, so an agent
|
|
1604
|
+
// commenting with your key still says which tool typed it.
|
|
1605
|
+
via: agentLabel(opts.as ?? opts.via)
|
|
1445
1606
|
});
|
|
1446
1607
|
} catch (err) {
|
|
1447
1608
|
if (err instanceof ApiRequestError && err.status === 401) {
|
|
@@ -1851,6 +2012,7 @@ async function publish(opts) {
|
|
|
1851
2012
|
const token = (artifactId !== null && artifactId === config?.artifactId ? config.publishToken : void 0) ?? process.env.ARTIFACT_CAFE_TOKEN;
|
|
1852
2013
|
const apiUrl2 = process.env.ARTIFACT_CAFE_URL ?? config?.apiUrl ?? DEFAULT_API_URL;
|
|
1853
2014
|
const apiKey = resolveApiKey(apiUrl2, opts.apiKey);
|
|
2015
|
+
const usingAgentToken = apiKey?.startsWith("aat_") === true;
|
|
1854
2016
|
const placement = await resolvePlacement({
|
|
1855
2017
|
apiKey,
|
|
1856
2018
|
apiUrl: apiUrl2,
|
|
@@ -1873,6 +2035,9 @@ async function publish(opts) {
|
|
|
1873
2035
|
}
|
|
1874
2036
|
const sessionBody = {
|
|
1875
2037
|
artifactId,
|
|
2038
|
+
// Overrides the entry HTML's own meta description on the share card.
|
|
2039
|
+
// Omitted entirely when absent, so the document keeps speaking for itself.
|
|
2040
|
+
...opts.description?.trim() ? { description: opts.description.trim() } : {},
|
|
1876
2041
|
entryPath,
|
|
1877
2042
|
files: files.map(({ absPath: _absPath, ...manifest }) => manifest),
|
|
1878
2043
|
...placement.folderId ? { folderId: placement.folderId } : {},
|
|
@@ -1880,7 +2045,10 @@ async function publish(opts) {
|
|
|
1880
2045
|
// backslash-n; turn those back into real newlines so bullets survive.
|
|
1881
2046
|
...opts.notes?.trim() ? { notes: opts.notes.replace(/\\n/g, "\n").trim() } : {},
|
|
1882
2047
|
source: {
|
|
1883
|
-
|
|
2048
|
+
// ARTIFACT_CAFE_AGENT if it's set, else whichever agent's environment
|
|
2049
|
+
// we recognize — this is what names the agent on every reply to a
|
|
2050
|
+
// thread opened against this version (§6.5).
|
|
2051
|
+
agent: agentLabel(),
|
|
1884
2052
|
runId: process.env.ARTIFACT_CAFE_RUN_ID,
|
|
1885
2053
|
type: "cli"
|
|
1886
2054
|
},
|
|
@@ -1893,7 +2061,7 @@ async function publish(opts) {
|
|
|
1893
2061
|
try {
|
|
1894
2062
|
session = await client.createSession(sessionBody);
|
|
1895
2063
|
} catch (err) {
|
|
1896
|
-
const keyRefused = apiKey !== void 0 && token !== void 0 && apiKey !== token && artifactId !== null && err instanceof ApiRequestError && (err.status === 401 || err.status === 403);
|
|
2064
|
+
const keyRefused = apiKey !== void 0 && !usingAgentToken && token !== void 0 && apiKey !== token && artifactId !== null && err instanceof ApiRequestError && (err.status === 401 || err.status === 403);
|
|
1897
2065
|
if (!keyRefused) {
|
|
1898
2066
|
throw err;
|
|
1899
2067
|
}
|
|
@@ -1926,16 +2094,19 @@ async function publish(opts) {
|
|
|
1926
2094
|
presence.stop();
|
|
1927
2095
|
}
|
|
1928
2096
|
const result = await client.finalize(session.publishSessionId);
|
|
2097
|
+
if (usingAgentToken && result.workspaceId) {
|
|
2098
|
+
placement.workspaceId = result.workspaceId;
|
|
2099
|
+
}
|
|
1929
2100
|
const rememberedProjectFile = placement.rememberProjectDefault ? writeProjectConfig(dir, placement.rememberProjectDefault) : null;
|
|
1930
|
-
const existingToken = config?.artifactId === result.artifactId ? config.publishToken : void 0;
|
|
1931
|
-
const shouldWriteConfig = result.publishToken !== void 0 || config?.artifactId === result.artifactId || opts.workspace !== void 0 || opts.folder !== void 0 || placement.workspaceId !== void 0;
|
|
2101
|
+
const existingToken = !usingAgentToken && config?.artifactId === result.artifactId ? config.publishToken : void 0;
|
|
2102
|
+
const shouldWriteConfig = usingAgentToken || result.publishToken !== void 0 || config?.artifactId === result.artifactId || opts.workspace !== void 0 || opts.folder !== void 0 || placement.workspaceId !== void 0;
|
|
1932
2103
|
if (shouldWriteConfig) {
|
|
1933
2104
|
writeConfig(dir, {
|
|
1934
2105
|
apiUrl: apiUrl2,
|
|
1935
2106
|
artifactId: result.artifactId,
|
|
1936
2107
|
...placement.folder ? { folder: placement.folder } : {},
|
|
1937
2108
|
...placement.folderId ? { folderId: placement.folderId } : {},
|
|
1938
|
-
|
|
2109
|
+
...!usingAgentToken && (result.publishToken ?? existingToken) ? { publishToken: result.publishToken ?? existingToken } : {},
|
|
1939
2110
|
slug: slugFromUrl(result.url),
|
|
1940
2111
|
url: result.url,
|
|
1941
2112
|
...placement.workspace ? { workspace: placement.workspace } : {},
|
|
@@ -2055,6 +2226,18 @@ function accessNote(visibility) {
|
|
|
2055
2226
|
}
|
|
2056
2227
|
async function resolvePlacement(params) {
|
|
2057
2228
|
const { apiKey, apiUrl: apiUrl2, config, isNewArtifact, opts } = params;
|
|
2229
|
+
if (apiKey?.startsWith("aat_")) {
|
|
2230
|
+
if (opts.workspace !== void 0 || opts.folder !== void 0) {
|
|
2231
|
+
throw new Error(
|
|
2232
|
+
"An agent token selects its own workspace; omit --workspace and --folder."
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
return {
|
|
2236
|
+
destination: "workspace",
|
|
2237
|
+
destinationLabel: "Agent token's workspace",
|
|
2238
|
+
destinationSource: "agent-token"
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2058
2241
|
const hasPlacementFlag = opts.workspace !== void 0 || opts.folder !== void 0;
|
|
2059
2242
|
if (!hasPlacementFlag && config?.workspaceId) {
|
|
2060
2243
|
const isPersonal = config.workspaceKind === "personal";
|
|
@@ -2415,100 +2598,45 @@ function printFolder(folder) {
|
|
|
2415
2598
|
// src/link.ts
|
|
2416
2599
|
import path8 from "path";
|
|
2417
2600
|
import { createInterface as createInterface2 } from "readline";
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
const { artifacts } = await createApiClient(apiUrl2, key).listArtifacts();
|
|
2430
|
-
if (artifacts.length === 0) {
|
|
2431
|
-
throw new Error(
|
|
2432
|
-
"No artifacts in your account yet \u2014 publish one first, or claim one via its claim link."
|
|
2433
|
-
);
|
|
2434
|
-
}
|
|
2435
|
-
const selector = opts.artifact ?? opts.slug;
|
|
2436
|
-
let chosen;
|
|
2437
|
-
if (opts.artifact) {
|
|
2438
|
-
chosen = artifacts.find((a) => a.id === opts.artifact);
|
|
2439
|
-
} else if (opts.slug) {
|
|
2440
|
-
chosen = artifacts.find((a) => a.slug === opts.slug);
|
|
2441
|
-
}
|
|
2442
|
-
if (selector && !chosen) {
|
|
2443
|
-
throw new Error(
|
|
2444
|
-
`No artifact "${selector}" in your account \u2014 run \`artifact-cafe list\` to see them.`
|
|
2445
|
-
);
|
|
2446
|
-
}
|
|
2447
|
-
if (opts.json && !chosen) {
|
|
2448
|
-
console.log(JSON.stringify({ artifacts }, null, 2));
|
|
2449
|
-
return;
|
|
2450
|
-
}
|
|
2451
|
-
if (!chosen) {
|
|
2452
|
-
if (!process.stdin.isTTY) {
|
|
2453
|
-
throw new Error(
|
|
2454
|
-
"Pick an artifact with --artifact <id> or --slug <slug> (or --json to list them). Interactive selection needs a terminal."
|
|
2455
|
-
);
|
|
2456
|
-
}
|
|
2457
|
-
chosen = await pickArtifact(artifacts);
|
|
2458
|
-
}
|
|
2459
|
-
const dir = path8.resolve(opts.dir);
|
|
2460
|
-
writeConfig(dir, {
|
|
2461
|
-
apiUrl: apiUrl2,
|
|
2462
|
-
artifactId: chosen.id,
|
|
2463
|
-
slug: chosen.slug,
|
|
2464
|
-
url: chosen.url
|
|
2465
|
-
// No publishToken — this folder authorizes via your login, not a secret.
|
|
2466
|
-
});
|
|
2467
|
-
if (opts.json) {
|
|
2468
|
-
console.log(
|
|
2469
|
-
JSON.stringify(
|
|
2470
|
-
{
|
|
2471
|
-
artifactId: chosen.id,
|
|
2472
|
-
linked: true,
|
|
2473
|
-
slug: chosen.slug,
|
|
2474
|
-
url: chosen.url
|
|
2475
|
-
},
|
|
2476
|
-
null,
|
|
2477
|
-
2
|
|
2478
|
-
)
|
|
2479
|
-
);
|
|
2480
|
-
return;
|
|
2481
|
-
}
|
|
2482
|
-
console.log(`
|
|
2483
|
-
\u2713 Linked ${dir} \u2192 "${chosen.title}" \u2014 ${chosen.url}`);
|
|
2484
|
-
console.log(
|
|
2485
|
-
" Owner actions (publish, comments --wait, protect) use your login.\n"
|
|
2601
|
+
|
|
2602
|
+
// src/list.ts
|
|
2603
|
+
async function teamGroups(client) {
|
|
2604
|
+
const { workspaces: workspaces3 } = await client.listWorkspaces();
|
|
2605
|
+
return await Promise.all(
|
|
2606
|
+
workspaces3.filter((workspace) => !workspace.isPersonal).map(async (workspace) => ({
|
|
2607
|
+
artifacts: (await client.listWorkspaceArtifacts(workspace.id)).artifacts,
|
|
2608
|
+
handle: workspace.handle,
|
|
2609
|
+
id: workspace.id,
|
|
2610
|
+
name: workspace.name
|
|
2611
|
+
}))
|
|
2486
2612
|
);
|
|
2487
2613
|
}
|
|
2488
|
-
function
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
)
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2614
|
+
async function reachableArtifacts(client) {
|
|
2615
|
+
const [own, groups] = await Promise.all([
|
|
2616
|
+
client.listArtifacts(),
|
|
2617
|
+
teamGroups(client)
|
|
2618
|
+
]);
|
|
2619
|
+
return [
|
|
2620
|
+
...own.artifacts.map((artifact) => ({
|
|
2621
|
+
currentVersionNumber: artifact.currentVersionNumber,
|
|
2622
|
+
id: artifact.id,
|
|
2623
|
+
slug: artifact.slug,
|
|
2624
|
+
title: artifact.title,
|
|
2625
|
+
url: artifact.url,
|
|
2626
|
+
workspaceHandle: null
|
|
2627
|
+
})),
|
|
2628
|
+
...groups.flatMap(
|
|
2629
|
+
(group) => group.artifacts.map((artifact) => ({
|
|
2630
|
+
currentVersionNumber: artifact.currentVersionNumber,
|
|
2631
|
+
id: artifact.id,
|
|
2632
|
+
slug: artifact.slug,
|
|
2633
|
+
title: artifact.title,
|
|
2634
|
+
url: artifact.url,
|
|
2635
|
+
workspaceHandle: group.handle
|
|
2636
|
+
}))
|
|
2637
|
+
)
|
|
2638
|
+
];
|
|
2509
2639
|
}
|
|
2510
|
-
|
|
2511
|
-
// src/list.ts
|
|
2512
2640
|
async function list(opts) {
|
|
2513
2641
|
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
2514
2642
|
/\/$/,
|
|
@@ -2525,22 +2653,44 @@ async function list(opts) {
|
|
|
2525
2653
|
await listWorkspace(client, opts);
|
|
2526
2654
|
return;
|
|
2527
2655
|
}
|
|
2528
|
-
const data = await
|
|
2656
|
+
const [data, groups] = await Promise.all([
|
|
2657
|
+
client.listArtifacts(),
|
|
2658
|
+
teamGroups(client)
|
|
2659
|
+
]);
|
|
2529
2660
|
if (opts.json) {
|
|
2530
|
-
console.log(
|
|
2661
|
+
console.log(
|
|
2662
|
+
JSON.stringify({ artifacts: data.artifacts, workspaces: groups }, null, 2)
|
|
2663
|
+
);
|
|
2531
2664
|
return;
|
|
2532
2665
|
}
|
|
2533
|
-
|
|
2666
|
+
const total = data.artifacts.length + groups.reduce((sum, group) => sum + group.artifacts.length, 0);
|
|
2667
|
+
if (total === 0) {
|
|
2534
2668
|
console.log(
|
|
2535
|
-
"No artifacts
|
|
2669
|
+
"No artifacts you can publish to yet \u2014 publish while logged in, or claim one via its claim link."
|
|
2536
2670
|
);
|
|
2537
2671
|
return;
|
|
2538
2672
|
}
|
|
2539
|
-
|
|
2540
|
-
|
|
2673
|
+
if (data.artifacts.length > 0) {
|
|
2674
|
+
const plural = data.artifacts.length === 1 ? "" : "s";
|
|
2675
|
+
console.log(`${data.artifacts.length} artifact${plural}
|
|
2541
2676
|
`);
|
|
2542
|
-
|
|
2543
|
-
|
|
2677
|
+
for (const artifact of data.artifacts) {
|
|
2678
|
+
printArtifact(artifact);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
for (const group of groups) {
|
|
2682
|
+
if (group.artifacts.length === 0) {
|
|
2683
|
+
continue;
|
|
2684
|
+
}
|
|
2685
|
+
const plural = group.artifacts.length === 1 ? "" : "s";
|
|
2686
|
+
console.log(
|
|
2687
|
+
`${group.artifacts.length} artifact${plural} in ${group.handle}
|
|
2688
|
+
`
|
|
2689
|
+
);
|
|
2690
|
+
for (const artifact of group.artifacts) {
|
|
2691
|
+
printWorkspaceArtifact(artifact);
|
|
2692
|
+
}
|
|
2693
|
+
console.log("");
|
|
2544
2694
|
}
|
|
2545
2695
|
}
|
|
2546
2696
|
async function listWorkspace(client, opts) {
|
|
@@ -2633,6 +2783,101 @@ function relativeTime(iso) {
|
|
|
2633
2783
|
return iso.slice(0, 10);
|
|
2634
2784
|
}
|
|
2635
2785
|
|
|
2786
|
+
// src/link.ts
|
|
2787
|
+
async function link(opts) {
|
|
2788
|
+
const apiUrl2 = (process.env.ARTIFACT_CAFE_URL ?? DEFAULT_API_URL).replace(
|
|
2789
|
+
/\/$/,
|
|
2790
|
+
""
|
|
2791
|
+
);
|
|
2792
|
+
const key = resolveApiKey(apiUrl2, opts.apiKey);
|
|
2793
|
+
if (!key) {
|
|
2794
|
+
throw new Error(
|
|
2795
|
+
`Linking needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
|
|
2796
|
+
);
|
|
2797
|
+
}
|
|
2798
|
+
const artifacts = await reachableArtifacts(createApiClient(apiUrl2, key));
|
|
2799
|
+
if (artifacts.length === 0) {
|
|
2800
|
+
throw new Error(
|
|
2801
|
+
"No artifacts you can publish to yet \u2014 publish one first, or claim one via its claim link."
|
|
2802
|
+
);
|
|
2803
|
+
}
|
|
2804
|
+
const selector = opts.artifact ?? opts.slug;
|
|
2805
|
+
let chosen;
|
|
2806
|
+
if (opts.artifact) {
|
|
2807
|
+
chosen = artifacts.find((a) => a.id === opts.artifact);
|
|
2808
|
+
} else if (opts.slug) {
|
|
2809
|
+
chosen = artifacts.find((a) => a.slug === opts.slug);
|
|
2810
|
+
}
|
|
2811
|
+
if (selector && !chosen) {
|
|
2812
|
+
throw new Error(
|
|
2813
|
+
`No artifact "${selector}" you can publish to \u2014 run \`artifact-cafe list\` to see them.`
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
if (opts.json && !chosen) {
|
|
2817
|
+
console.log(JSON.stringify({ artifacts }, null, 2));
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
if (!chosen) {
|
|
2821
|
+
if (!process.stdin.isTTY) {
|
|
2822
|
+
throw new Error(
|
|
2823
|
+
"Pick an artifact with --artifact <id> or --slug <slug> (or --json to list them). Interactive selection needs a terminal."
|
|
2824
|
+
);
|
|
2825
|
+
}
|
|
2826
|
+
chosen = await pickArtifact(artifacts);
|
|
2827
|
+
}
|
|
2828
|
+
const dir = path8.resolve(opts.dir);
|
|
2829
|
+
writeConfig(dir, {
|
|
2830
|
+
apiUrl: apiUrl2,
|
|
2831
|
+
artifactId: chosen.id,
|
|
2832
|
+
slug: chosen.slug,
|
|
2833
|
+
url: chosen.url
|
|
2834
|
+
// No publishToken — this folder authorizes via your login, not a secret.
|
|
2835
|
+
});
|
|
2836
|
+
if (opts.json) {
|
|
2837
|
+
console.log(
|
|
2838
|
+
JSON.stringify(
|
|
2839
|
+
{
|
|
2840
|
+
artifactId: chosen.id,
|
|
2841
|
+
linked: true,
|
|
2842
|
+
slug: chosen.slug,
|
|
2843
|
+
url: chosen.url
|
|
2844
|
+
},
|
|
2845
|
+
null,
|
|
2846
|
+
2
|
|
2847
|
+
)
|
|
2848
|
+
);
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2851
|
+
console.log(`
|
|
2852
|
+
\u2713 Linked ${dir} \u2192 "${chosen.title}" \u2014 ${chosen.url}`);
|
|
2853
|
+
console.log(
|
|
2854
|
+
" Owner actions (publish, comments --wait, protect) use your login.\n"
|
|
2855
|
+
);
|
|
2856
|
+
}
|
|
2857
|
+
function pickArtifact(artifacts) {
|
|
2858
|
+
console.log("Artifacts you can publish to:\n");
|
|
2859
|
+
artifacts.forEach((a, i) => {
|
|
2860
|
+
const v = a.currentVersionNumber ? `v${a.currentVersionNumber}` : "\u2014";
|
|
2861
|
+
const where = a.workspaceHandle ? ` \xB7 ${a.workspaceHandle}` : "";
|
|
2862
|
+
console.log(
|
|
2863
|
+
` ${String(i + 1).padStart(2)}. ${a.title} \xB7 ${v}${where} \xB7 ${a.url}`
|
|
2864
|
+
);
|
|
2865
|
+
});
|
|
2866
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
2867
|
+
return new Promise((resolve2, reject) => {
|
|
2868
|
+
rl.question(`
|
|
2869
|
+
Link which? [1-${artifacts.length}]: `, (answer) => {
|
|
2870
|
+
rl.close();
|
|
2871
|
+
const n = Number(answer.trim());
|
|
2872
|
+
if (!(Number.isInteger(n) && n >= 1 && n <= artifacts.length)) {
|
|
2873
|
+
reject(new Error(`Enter a number between 1 and ${artifacts.length}.`));
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
resolve2(artifacts[n - 1]);
|
|
2877
|
+
});
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2636
2881
|
// src/login.ts
|
|
2637
2882
|
import { createInterface as createInterface3 } from "readline";
|
|
2638
2883
|
function apiUrl() {
|
|
@@ -2994,7 +3239,7 @@ async function replyToThread(opts) {
|
|
|
2994
3239
|
artifact: opts.artifact
|
|
2995
3240
|
});
|
|
2996
3241
|
const client = createApiClient(target.apiUrl, target.credential);
|
|
2997
|
-
let thread = await client.replyThread(threadId, body);
|
|
3242
|
+
let thread = await client.replyThread(threadId, body, agentLabel(opts.as));
|
|
2998
3243
|
if (opts.resolve) {
|
|
2999
3244
|
thread = await client.resolveThread(threadId);
|
|
3000
3245
|
}
|
|
@@ -3217,18 +3462,18 @@ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as rea
|
|
|
3217
3462
|
import { createRequire } from "module";
|
|
3218
3463
|
import { homedir as homedir2 } from "os";
|
|
3219
3464
|
import path18 from "path";
|
|
3220
|
-
import { z as
|
|
3465
|
+
import { z as z22 } from "zod";
|
|
3221
3466
|
var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
|
|
3222
3467
|
var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
|
|
3223
3468
|
var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
|
|
3224
3469
|
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3225
3470
|
var FETCH_TIMEOUT_MS = 2e3;
|
|
3226
3471
|
var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
|
|
3227
|
-
var cacheSchema =
|
|
3228
|
-
checkedAt:
|
|
3229
|
-
cliLatest:
|
|
3230
|
-
skillLatest:
|
|
3231
|
-
version:
|
|
3472
|
+
var cacheSchema = z22.object({
|
|
3473
|
+
checkedAt: z22.number(),
|
|
3474
|
+
cliLatest: z22.string().nullable(),
|
|
3475
|
+
skillLatest: z22.string().nullable(),
|
|
3476
|
+
version: z22.literal(1)
|
|
3232
3477
|
});
|
|
3233
3478
|
function cliVersion() {
|
|
3234
3479
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
@@ -3283,7 +3528,7 @@ async function fetchText(url) {
|
|
|
3283
3528
|
}
|
|
3284
3529
|
async function fetchLatestVersions(previous) {
|
|
3285
3530
|
const [cli, skill] = await Promise.all([
|
|
3286
|
-
fetchJson(REGISTRY_LATEST_URL).then((body) =>
|
|
3531
|
+
fetchJson(REGISTRY_LATEST_URL).then((body) => z22.object({ version: z22.string() }).parse(body).version).catch(() => null),
|
|
3287
3532
|
fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
|
|
3288
3533
|
]);
|
|
3289
3534
|
return {
|
|
@@ -3396,30 +3641,35 @@ function printWorkspace(workspace) {
|
|
|
3396
3641
|
}
|
|
3397
3642
|
|
|
3398
3643
|
// src/index.ts
|
|
3399
|
-
function
|
|
3644
|
+
function readStringArg(argv, flag) {
|
|
3645
|
+
const prefix = `${flag}=`;
|
|
3400
3646
|
for (let i = 0; i < argv.length; i++) {
|
|
3401
3647
|
const token = argv[i];
|
|
3402
3648
|
if (token === void 0) {
|
|
3403
3649
|
continue;
|
|
3404
3650
|
}
|
|
3405
|
-
if (token ===
|
|
3651
|
+
if (token === flag) {
|
|
3406
3652
|
return argv[i + 1];
|
|
3407
3653
|
}
|
|
3408
|
-
if (token.startsWith(
|
|
3409
|
-
return token.slice(
|
|
3654
|
+
if (token.startsWith(prefix)) {
|
|
3655
|
+
return token.slice(prefix.length);
|
|
3410
3656
|
}
|
|
3411
3657
|
}
|
|
3412
3658
|
}
|
|
3413
3659
|
var publish2 = defineCommand({
|
|
3414
3660
|
args: {
|
|
3415
3661
|
"api-key": {
|
|
3416
|
-
description: "Publish with an account
|
|
3662
|
+
description: "Publish with an account key or agent token (default: env or login)",
|
|
3417
3663
|
type: "string"
|
|
3418
3664
|
},
|
|
3419
3665
|
artifact: {
|
|
3420
3666
|
description: "Publish to an existing artifact ID",
|
|
3421
3667
|
type: "string"
|
|
3422
3668
|
},
|
|
3669
|
+
description: {
|
|
3670
|
+
description: "One-line share-card summary (default: the entry HTML's own meta description)",
|
|
3671
|
+
type: "string"
|
|
3672
|
+
},
|
|
3423
3673
|
dir: {
|
|
3424
3674
|
default: ".",
|
|
3425
3675
|
description: "Directory to publish",
|
|
@@ -3456,11 +3706,12 @@ var publish2 = defineCommand({
|
|
|
3456
3706
|
await publish({
|
|
3457
3707
|
apiKey: args["api-key"],
|
|
3458
3708
|
artifact: args.artifact,
|
|
3709
|
+
description: readStringArg(process.argv.slice(2), "--description"),
|
|
3459
3710
|
dir: args.dir,
|
|
3460
3711
|
entry: args.entry,
|
|
3461
3712
|
folder: args.folder,
|
|
3462
3713
|
json: Boolean(args.json),
|
|
3463
|
-
notes:
|
|
3714
|
+
notes: readStringArg(process.argv.slice(2), "--notes"),
|
|
3464
3715
|
open: !args["no-open"],
|
|
3465
3716
|
password: args.password,
|
|
3466
3717
|
review: args.review,
|
|
@@ -3541,7 +3792,7 @@ var list2 = defineCommand({
|
|
|
3541
3792
|
}
|
|
3542
3793
|
},
|
|
3543
3794
|
meta: {
|
|
3544
|
-
description: "List the artifacts your account
|
|
3795
|
+
description: "List the artifacts your account can publish to",
|
|
3545
3796
|
name: "list"
|
|
3546
3797
|
},
|
|
3547
3798
|
async run({ args }) {
|
|
@@ -3695,7 +3946,7 @@ var link2 = defineCommand({
|
|
|
3695
3946
|
}
|
|
3696
3947
|
},
|
|
3697
3948
|
meta: {
|
|
3698
|
-
description: "Link a folder to
|
|
3949
|
+
description: "Link a folder to an artifact you can publish to (yours or a teammate's)",
|
|
3699
3950
|
name: "link"
|
|
3700
3951
|
},
|
|
3701
3952
|
async run({ args }) {
|
|
@@ -3716,7 +3967,7 @@ var link2 = defineCommand({
|
|
|
3716
3967
|
var comments2 = defineCommand({
|
|
3717
3968
|
args: {
|
|
3718
3969
|
"api-key": {
|
|
3719
|
-
description: "Authorize with an account
|
|
3970
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
3720
3971
|
type: "string"
|
|
3721
3972
|
},
|
|
3722
3973
|
artifact: {
|
|
@@ -3780,7 +4031,7 @@ var comments2 = defineCommand({
|
|
|
3780
4031
|
var listen = defineCommand({
|
|
3781
4032
|
args: {
|
|
3782
4033
|
"api-key": {
|
|
3783
|
-
description: "Authorize with an account
|
|
4034
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
3784
4035
|
type: "string"
|
|
3785
4036
|
},
|
|
3786
4037
|
artifact: {
|
|
@@ -3840,7 +4091,7 @@ var listen = defineCommand({
|
|
|
3840
4091
|
var statusCmd = defineCommand({
|
|
3841
4092
|
args: {
|
|
3842
4093
|
"api-key": {
|
|
3843
|
-
description: "Authorize with an account
|
|
4094
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
3844
4095
|
type: "string"
|
|
3845
4096
|
},
|
|
3846
4097
|
artifact: {
|
|
@@ -3881,7 +4132,7 @@ var statusCmd = defineCommand({
|
|
|
3881
4132
|
var protect2 = defineCommand({
|
|
3882
4133
|
args: {
|
|
3883
4134
|
"api-key": {
|
|
3884
|
-
description: "Authorize with an account
|
|
4135
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
3885
4136
|
type: "string"
|
|
3886
4137
|
},
|
|
3887
4138
|
artifact: {
|
|
@@ -3920,7 +4171,7 @@ var protect2 = defineCommand({
|
|
|
3920
4171
|
var review2 = defineCommand({
|
|
3921
4172
|
args: {
|
|
3922
4173
|
"api-key": {
|
|
3923
|
-
description: "Authorize with an account
|
|
4174
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
3924
4175
|
type: "string"
|
|
3925
4176
|
},
|
|
3926
4177
|
artifact: {
|
|
@@ -3961,7 +4212,7 @@ var review2 = defineCommand({
|
|
|
3961
4212
|
var threadStatusCommand = (action) => defineCommand({
|
|
3962
4213
|
args: {
|
|
3963
4214
|
"api-key": {
|
|
3964
|
-
description: "Authorize with an account
|
|
4215
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
3965
4216
|
type: "string"
|
|
3966
4217
|
},
|
|
3967
4218
|
artifact: {
|
|
@@ -4010,13 +4261,17 @@ var ack = threadStatusCommand("ack");
|
|
|
4010
4261
|
var reply = defineCommand({
|
|
4011
4262
|
args: {
|
|
4012
4263
|
"api-key": {
|
|
4013
|
-
description: "Authorize with an account
|
|
4264
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
4014
4265
|
type: "string"
|
|
4015
4266
|
},
|
|
4016
4267
|
artifact: {
|
|
4017
4268
|
description: "Act on an artifact ID instead of the linked folder",
|
|
4018
4269
|
type: "string"
|
|
4019
4270
|
},
|
|
4271
|
+
as: {
|
|
4272
|
+
description: 'Who is replying, e.g. "Claude Code" (default: detected; "" to opt out)',
|
|
4273
|
+
type: "string"
|
|
4274
|
+
},
|
|
4020
4275
|
body: {
|
|
4021
4276
|
description: "Reply text (omit to read from stdin)",
|
|
4022
4277
|
type: "string"
|
|
@@ -4046,6 +4301,7 @@ var reply = defineCommand({
|
|
|
4046
4301
|
await replyToThread({
|
|
4047
4302
|
apiKey: args["api-key"],
|
|
4048
4303
|
artifact: args.artifact,
|
|
4304
|
+
as: args.as,
|
|
4049
4305
|
body: args.body,
|
|
4050
4306
|
dir: args.dir,
|
|
4051
4307
|
json: Boolean(args.json),
|
|
@@ -4068,6 +4324,10 @@ var comment2 = defineCommand({
|
|
|
4068
4324
|
description: "Act on an artifact ID instead of the linked folder",
|
|
4069
4325
|
type: "string"
|
|
4070
4326
|
},
|
|
4327
|
+
as: {
|
|
4328
|
+
description: 'Tool posting on your behalf, e.g. "Claude Code" (default: detected)',
|
|
4329
|
+
type: "string"
|
|
4330
|
+
},
|
|
4071
4331
|
body: {
|
|
4072
4332
|
description: "Comment text (omit to read from stdin)",
|
|
4073
4333
|
type: "string"
|
|
@@ -4095,7 +4355,7 @@ var comment2 = defineCommand({
|
|
|
4095
4355
|
type: "string"
|
|
4096
4356
|
},
|
|
4097
4357
|
via: {
|
|
4098
|
-
description:
|
|
4358
|
+
description: "Older name for --as",
|
|
4099
4359
|
type: "string"
|
|
4100
4360
|
}
|
|
4101
4361
|
},
|
|
@@ -4108,6 +4368,7 @@ var comment2 = defineCommand({
|
|
|
4108
4368
|
await comment({
|
|
4109
4369
|
apiKey: args["api-key"],
|
|
4110
4370
|
artifact: args.artifact,
|
|
4371
|
+
as: args.as,
|
|
4111
4372
|
body: args.body,
|
|
4112
4373
|
dir: args.dir,
|
|
4113
4374
|
json: Boolean(args.json),
|
|
@@ -4126,7 +4387,7 @@ var comment2 = defineCommand({
|
|
|
4126
4387
|
var edit = defineCommand({
|
|
4127
4388
|
args: {
|
|
4128
4389
|
"api-key": {
|
|
4129
|
-
description: "Authorize with an account
|
|
4390
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
4130
4391
|
type: "string"
|
|
4131
4392
|
},
|
|
4132
4393
|
artifact: {
|
|
@@ -4169,7 +4430,7 @@ var edit = defineCommand({
|
|
|
4169
4430
|
var del = defineCommand({
|
|
4170
4431
|
args: {
|
|
4171
4432
|
"api-key": {
|
|
4172
|
-
description: "Authorize with an account
|
|
4433
|
+
description: "Authorize with an account key or agent token (default: env or login)",
|
|
4173
4434
|
type: "string"
|
|
4174
4435
|
},
|
|
4175
4436
|
artifact: {
|