@yargote/artifacts-cli 0.1.0-beta.1 → 0.1.0-beta.11
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/README.md +167 -19
- package/package.json +9 -4
- package/skills/artifacts-cli/SKILL.md +80 -0
- package/skills/artifacts-cli/references/commands.md +250 -0
- package/skills/artifacts-cli/references/verify-retained.md +119 -0
- package/skills/artifacts-cli/scripts/prepare-retained.mjs +149 -0
- package/skills/artifacts-design/SKILL.md +92 -0
- package/skills/artifacts-design/THIRD-PARTY-NOTICES.md +155 -0
- package/skills/artifacts-design/fonts/OFL.txt +96 -0
- package/skills/artifacts-design/fonts/bricolage-grotesque.woff2 +0 -0
- package/skills/artifacts-design/fonts/jetbrains-mono.woff2 +0 -0
- package/skills/artifacts-design/fonts/newsreader-italic.woff2 +0 -0
- package/skills/artifacts-design/fonts/newsreader.woff2 +0 -0
- package/skills/artifacts-design/fonts/public-sans-italic.woff2 +0 -0
- package/skills/artifacts-design/fonts/public-sans.woff2 +0 -0
- package/skills/artifacts-design/references/canvas.md +100 -0
- package/skills/artifacts-design/references/catppuccin.md +38 -0
- package/skills/artifacts-design/references/interface-checks.md +60 -0
- package/skills/artifacts-design/references/local-checks.md +70 -0
- package/skills/artifacts-design/references/visual-explanations.md +13 -0
- package/skills/artifacts-design/scripts/embed-fonts.mjs +246 -0
- package/skills/artifacts-design/scripts/verify-local.mjs +333 -0
- package/skills/artifacts-design/templates/starter.html +170 -0
- package/src/auth.d.ts +4 -1
- package/src/auth.js +184 -45
- package/src/credentials.d.ts +2 -0
- package/src/credentials.js +22 -0
- package/src/index.js +153 -31
- package/src/operations.d.ts +155 -38
- package/src/operations.js +11 -1
- package/src/skills.d.ts +9 -0
- package/src/skills.js +181 -0
package/src/index.js
CHANGED
|
@@ -6,56 +6,120 @@ import { homedir } from 'node:os';
|
|
|
6
6
|
import { join, resolve } from 'node:path';
|
|
7
7
|
import { parseArgs } from 'node:util';
|
|
8
8
|
import { z } from 'zod';
|
|
9
|
-
import { createSchema, publishSchema, updateSchema } from '@yargote/artifacts-contracts';
|
|
9
|
+
import { createSchema, listSharesSchema, moveSchema, publishSchema, shareSchema, unshareSchema, updateSchema } from '@yargote/artifacts-contracts';
|
|
10
10
|
import { canonicalResource } from '@yargote/artifacts-contracts/http';
|
|
11
|
-
import { createArtifactHttpClient, HttpError, prepareArtifactEdit } from '@yargote/artifacts-client';
|
|
11
|
+
import { BatchOperationError, createArtifactHttpClient, HttpError, prepareArtifactEdit, prepareBatch } from '@yargote/artifacts-client';
|
|
12
12
|
import { accessToken, login, logout } from "./auth.js";
|
|
13
|
+
import { CredentialStore } from "./credentials.js";
|
|
13
14
|
import { loadOperation, retainOperation, submitOperation } from "./operations.js";
|
|
15
|
+
import { runSkillsCommand } from "./skills.js";
|
|
14
16
|
const help = `Usage: artifacts COMMAND [OPTIONS]
|
|
15
17
|
|
|
16
18
|
Global options:
|
|
17
|
-
--server RESOURCE
|
|
19
|
+
--server RESOURCE Exact REST OAuth resource, or ARTIFACTS_SERVER
|
|
20
|
+
Defaults to the last successful login
|
|
18
21
|
--state-dir DIR Private local state, or ARTIFACTS_STATE_DIR
|
|
19
22
|
Default: ~/.local/state/artifacts
|
|
20
23
|
--help Print this reference
|
|
21
24
|
--version Print the installed package version
|
|
22
25
|
|
|
23
26
|
Commands:
|
|
27
|
+
skills [list]
|
|
28
|
+
skills get NAME [--full]
|
|
29
|
+
skills get --all [--full]
|
|
30
|
+
skills path [NAME]
|
|
31
|
+
skills install [NAME] [--agent claude] [--global] [--force]
|
|
24
32
|
login [--no-browser]
|
|
25
33
|
logout
|
|
26
34
|
whoami
|
|
27
|
-
create --snapshot FILE [--
|
|
35
|
+
create --snapshot FILE [--project NAME] [--slug SLUG] [--request-id ID]
|
|
36
|
+
[--response full|compact]
|
|
28
37
|
create --command FILE
|
|
29
|
-
list [--limit N] [--cursor CURSOR]
|
|
30
|
-
|
|
38
|
+
list [--limit N] [--cursor CURSOR] [--project NAME]
|
|
39
|
+
projects
|
|
40
|
+
search QUERY [--project NAME] [--limit N] [--cursor CURSOR]
|
|
41
|
+
read ID [--revision REV] [--response full|compact] [--out FILE [--quiet]]
|
|
42
|
+
read --slug SLUG [--project NAME] [--revision REV] [--response full|compact]
|
|
43
|
+
[--out FILE [--quiet]]
|
|
31
44
|
update ID --snapshot FILE --expected-revision REV [--request-id ID]
|
|
32
45
|
[--response full|compact]
|
|
33
46
|
update [ID] --command FILE
|
|
34
47
|
edit ID --changes FILE [--request-id ID]
|
|
48
|
+
batch --command FILE
|
|
35
49
|
history ID [--limit N] [--before-sequence N] [--summary]
|
|
36
50
|
publish ID --revision REV --expected-version N [--request-id ID]
|
|
37
51
|
publish [ID] --command FILE
|
|
38
52
|
export ID --revision REV --out FILE
|
|
53
|
+
move ID [--project NAME | --no-project] [--slug SLUG | --no-slug]
|
|
54
|
+
[--request-id ID]
|
|
55
|
+
share ID --email EMAIL [--notify] [--request-id ID]
|
|
56
|
+
shares ID
|
|
57
|
+
unshare ID SHARE_ID [--request-id ID]
|
|
39
58
|
retry REQUEST_ID
|
|
40
59
|
|
|
41
|
-
Mutation command files contain the complete create, update, or
|
|
60
|
+
Mutation command files contain the complete create, update, publish, or batch
|
|
61
|
+
JSON.
|
|
42
62
|
Mutation request IDs default to a random UUID. Commands are retained before
|
|
43
63
|
submission. Retry sends the exact retained command for the same server and owner.
|
|
44
|
-
|
|
64
|
+
Projects group artifacts by name without regard to letter case. A project
|
|
65
|
+
exists while it contains an artifact. Move changes the project or the slug of
|
|
66
|
+
an artifact without creating a revision. It needs at least one of its four
|
|
67
|
+
options. --no-project removes the artifact from its project, and --no-slug
|
|
68
|
+
clears its slug. An option left out keeps that value, so a move to another
|
|
69
|
+
project keeps the slug.
|
|
70
|
+
A slug such as concepts/rag names an artifact within its project, or within
|
|
71
|
+
the unfiled artifacts without --project. Read --slug finds it without its ID.
|
|
72
|
+
Create or move fails with slug_conflict when the project already uses the slug.
|
|
73
|
+
Search finds owned artifacts whose head contains every term of QUERY, ignoring
|
|
74
|
+
letter case and accents. It reads Markdown and text content, the visible text
|
|
75
|
+
of HTML, and the title and description of every kind. Quote a QUERY with
|
|
76
|
+
spaces. Results are ordered by relevance with a **bold** snippet.
|
|
77
|
+
Batch applies 1 to 25 create, update, edit, and move operations from one command
|
|
78
|
+
file with a requestId and operations. All succeed or none do. A failure names the
|
|
79
|
+
0-based index of its operation. An update addresses its artifact by artifactId or
|
|
80
|
+
by project and slug, including a page created earlier in the batch, and omits
|
|
81
|
+
expectedRevisionId only for such a page. An edit takes the same address and the
|
|
82
|
+
changes of edit --changes for a page that exists before the batch. The CLI reads
|
|
83
|
+
its head and sends an update with that expectedRevisionId, so a page changed
|
|
84
|
+
meanwhile, or written earlier in the batch, fails with conflict; use update for
|
|
85
|
+
such a page. Retry sends the prepared batch. A move addresses its artifact by
|
|
86
|
+
artifactId or by from { project, slug } and takes the move project and slug.
|
|
87
|
+
Later operations address a moved page by its new slug. A batch never publishes.
|
|
88
|
+
Read --out saves the returned artifact JSON. Export --out saves the ZIP.
|
|
89
|
+
Read --quiet requires --out and suppresses successful JSON stdout.
|
|
45
90
|
Output files are created exclusively and never overwrite an existing path.
|
|
46
91
|
JSON results go to stdout. Diagnostics and recovery guidance go to stderr.
|
|
92
|
+
Login accepts an MCP URL when its metadata advertises artifacts_rest_resource.
|
|
93
|
+
Split MCP and REST resources require this relation. Login remembers the verified
|
|
94
|
+
REST URL for later commands. --server takes precedence over ARTIFACTS_SERVER,
|
|
95
|
+
which takes precedence over the saved URL. Ordinary overrides do not replace
|
|
96
|
+
the saved URL. Logout keeps it for a later login.
|
|
97
|
+
Login checks <resource>/v1/identity before browser
|
|
98
|
+
authorization. Legacy /mcp
|
|
99
|
+
resources work when their REST API is at /mcp/v1. If the check fails, confirm
|
|
100
|
+
the configured REST URL with your service administrator.
|
|
101
|
+
Login errors report the phase, method, endpoint, and HTTP status when received.
|
|
102
|
+
Network failures have no HTTP status. Login verifies identity again after OAuth.
|
|
47
103
|
ARTIFACTS_ACCESS_TOKEN supplies a token for automation without local login.
|
|
104
|
+
Skills commands work offline without a server, login, or private state.
|
|
105
|
+
They print plain text. Global server and state options are accepted and ignored.
|
|
106
|
+
Skills install defaults to artifacts-cli under .agents/skills in the current
|
|
107
|
+
directory. --agent claude uses .claude/skills. --global uses your home directory.
|
|
108
|
+
The installed discovery file loads instructions through artifacts skills get.
|
|
109
|
+
Identical files stay unchanged. Differing files require --force.
|
|
48
110
|
`;
|
|
49
111
|
class UsageError extends Error {
|
|
50
112
|
}
|
|
51
113
|
const commandOptions = {
|
|
52
114
|
login: ['no-browser'], logout: [], whoami: [],
|
|
53
|
-
create: ['snapshot', 'request-id', 'response', 'command'],
|
|
54
|
-
list: ['limit', 'cursor'], read: ['revision', 'response', 'out'],
|
|
115
|
+
create: ['snapshot', 'project', 'slug', 'request-id', 'response', 'command'],
|
|
116
|
+
list: ['limit', 'cursor', 'project'], projects: [], search: ['project', 'limit', 'cursor'], read: ['slug', 'project', 'revision', 'response', 'out', 'quiet'],
|
|
55
117
|
update: ['snapshot', 'expected-revision', 'request-id', 'response', 'command'],
|
|
56
|
-
edit: ['changes', 'request-id'], history: ['limit', 'before-sequence', 'summary'],
|
|
118
|
+
edit: ['changes', 'request-id'], batch: ['command'], history: ['limit', 'before-sequence', 'summary'],
|
|
57
119
|
publish: ['revision', 'expected-version', 'request-id', 'command'],
|
|
58
|
-
|
|
120
|
+
share: ['email', 'notify', 'request-id'], shares: [], unshare: ['request-id'],
|
|
121
|
+
export: ['revision', 'out'], move: ['project', 'no-project', 'slug', 'no-slug', 'request-id'], retry: [],
|
|
122
|
+
skills: ['full', 'all', 'agent', 'global', 'force'],
|
|
59
123
|
};
|
|
60
124
|
async function jsonFile(path) {
|
|
61
125
|
const file = await open(path, constants.O_RDONLY);
|
|
@@ -88,7 +152,10 @@ async function main() {
|
|
|
88
152
|
changes: { type: 'string' }, 'request-id': { type: 'string' }, response: { type: 'string' },
|
|
89
153
|
revision: { type: 'string' }, 'expected-revision': { type: 'string' }, 'expected-version': { type: 'string' },
|
|
90
154
|
limit: { type: 'string' }, cursor: { type: 'string' }, 'before-sequence': { type: 'string' },
|
|
91
|
-
summary: { type: 'boolean' }, out: { type: 'string' },
|
|
155
|
+
summary: { type: 'boolean' }, out: { type: 'string' }, quiet: { type: 'boolean' },
|
|
156
|
+
full: { type: 'boolean' }, all: { type: 'boolean' }, agent: { type: 'string' },
|
|
157
|
+
global: { type: 'boolean' }, force: { type: 'boolean' },
|
|
158
|
+
project: { type: 'string' }, 'no-project': { type: 'boolean' }, slug: { type: 'string' }, 'no-slug': { type: 'boolean' }, email: { type: 'string' }, notify: { type: 'boolean' },
|
|
92
159
|
},
|
|
93
160
|
});
|
|
94
161
|
const seen = new Set();
|
|
@@ -114,11 +181,22 @@ async function main() {
|
|
|
114
181
|
for (const option of seen)
|
|
115
182
|
if (!['server', 'state-dir', 'help', ...commandOptions[command]].includes(option))
|
|
116
183
|
throw new UsageError(`Option --${option} is not supported by ${command}`);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
throw new UsageError('Specify --server RESOURCE or ARTIFACTS_SERVER. No production server is selected by default.');
|
|
120
|
-
const resource = canonicalResource(resourceValue);
|
|
184
|
+
if (command === 'skills')
|
|
185
|
+
return runSkillsCommand(arguments_, values, seen);
|
|
121
186
|
const stateDir = resolve(values['state-dir'] ?? process.env.ARTIFACTS_STATE_DIR ?? join(homedir(), '.local', 'state', 'artifacts'));
|
|
187
|
+
let resourceValue = values.server ?? process.env.ARTIFACTS_SERVER;
|
|
188
|
+
if (resourceValue === undefined) {
|
|
189
|
+
const store = await CredentialStore.open(stateDir);
|
|
190
|
+
try {
|
|
191
|
+
resourceValue = await store.exclusive(() => store.readSelectedResource());
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
store.close();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (resourceValue === undefined)
|
|
198
|
+
throw new UsageError('No server is saved. Run artifacts login --server RESOURCE or set ARTIFACTS_SERVER.');
|
|
199
|
+
const resource = canonicalResource(resourceValue);
|
|
122
200
|
function argument(required = true) {
|
|
123
201
|
if (arguments_.length > 1 || (required && arguments_.length !== 1))
|
|
124
202
|
throw new UsageError(`${command} requires ${required ? 'one' : 'zero or one'} positional argument`);
|
|
@@ -142,9 +220,17 @@ async function main() {
|
|
|
142
220
|
throw new UsageError(`--${name} requires a nonnegative safe integer`);
|
|
143
221
|
return Number(value);
|
|
144
222
|
}
|
|
223
|
+
function cleared(name, value, clear) {
|
|
224
|
+
if (value !== undefined && clear)
|
|
225
|
+
throw new UsageError(`Specify at most one of --${name} or --no-${name}`);
|
|
226
|
+
return clear ? null : value;
|
|
227
|
+
}
|
|
145
228
|
const response = values.response;
|
|
146
229
|
if (response !== undefined && response !== 'full' && response !== 'compact')
|
|
147
230
|
throw new UsageError('--response requires full or compact');
|
|
231
|
+
const quiet = values.quiet ?? false;
|
|
232
|
+
if (quiet)
|
|
233
|
+
required('out');
|
|
148
234
|
let commandToken;
|
|
149
235
|
const client = createArtifactHttpClient({ resource, accessToken: () => commandToken ??= accessToken({ resource, stateDir }) });
|
|
150
236
|
async function mutate(mutation) {
|
|
@@ -158,9 +244,9 @@ async function main() {
|
|
|
158
244
|
if (extra.length)
|
|
159
245
|
throw new UsageError('--command cannot be combined with mutation options');
|
|
160
246
|
const value = await jsonFile(required('command'));
|
|
161
|
-
if (kind === 'create') {
|
|
247
|
+
if (kind === 'create' || kind === 'batch') {
|
|
162
248
|
none();
|
|
163
|
-
return { kind, command: createSchema.parse(value) };
|
|
249
|
+
return kind === 'create' ? { kind, command: createSchema.parse(value) } : { kind, command: await prepareBatch(client, value) };
|
|
164
250
|
}
|
|
165
251
|
const mutation = kind === 'update' ? { kind, command: updateSchema.parse(value) } : { kind, command: publishSchema.parse(value) };
|
|
166
252
|
const id = argument(false);
|
|
@@ -171,7 +257,12 @@ async function main() {
|
|
|
171
257
|
switch (command) {
|
|
172
258
|
case 'login':
|
|
173
259
|
none();
|
|
174
|
-
|
|
260
|
+
{
|
|
261
|
+
const result = await login({ resource, stateDir, noBrowser: values['no-browser'] ?? false, onAuthorizationUrl: (url) => { process.stderr.write(`Authorize this login in your browser:\n${url}\n`); } });
|
|
262
|
+
if (result.resource !== resource)
|
|
263
|
+
process.stderr.write(`REST resource: ${result.resource}\nThis verified REST URL is saved for subsequent commands.\n`);
|
|
264
|
+
return result.identity;
|
|
265
|
+
}
|
|
175
266
|
case 'logout':
|
|
176
267
|
none();
|
|
177
268
|
return logout({ resource, stateDir });
|
|
@@ -182,16 +273,25 @@ async function main() {
|
|
|
182
273
|
if (values.command)
|
|
183
274
|
return mutate(await commandFile('create'));
|
|
184
275
|
none();
|
|
185
|
-
return mutate({ kind: 'create', command: createSchema.parse({ requestId: values['request-id'] ?? randomUUID(), snapshot: await jsonFile(required('snapshot')), ...(response === undefined ? {} : { response }) }) });
|
|
276
|
+
return mutate({ kind: 'create', command: createSchema.parse({ requestId: values['request-id'] ?? randomUUID(), snapshot: await jsonFile(required('snapshot')), ...(values.project === undefined ? {} : { project: values.project }), ...(values.slug === undefined ? {} : { slug: values.slug }), ...(response === undefined ? {} : { response }) }) });
|
|
186
277
|
}
|
|
187
278
|
case 'list':
|
|
188
279
|
none();
|
|
189
|
-
return client.list({ ...(values.limit === undefined ? {} : { limit: integer('limit') }), ...(values.cursor === undefined ? {} : { cursor: values.cursor }) });
|
|
280
|
+
return client.list({ ...(values.limit === undefined ? {} : { limit: integer('limit') }), ...(values.cursor === undefined ? {} : { cursor: values.cursor }), ...(values.project === undefined ? {} : { project: values.project }) });
|
|
281
|
+
case 'projects':
|
|
282
|
+
none();
|
|
283
|
+
return client.projects();
|
|
284
|
+
case 'search': return client.search({ query: argument(), ...(values.project === undefined ? {} : { project: values.project }), ...(values.limit === undefined ? {} : { limit: integer('limit') }), ...(values.cursor === undefined ? {} : { cursor: values.cursor }) });
|
|
190
285
|
case 'read': {
|
|
191
|
-
|
|
286
|
+
if (values.slug === undefined && values.project !== undefined)
|
|
287
|
+
throw new UsageError('read --project requires --slug');
|
|
288
|
+
if ((values.slug === undefined) !== (arguments_.length === 1) || arguments_.length > 1)
|
|
289
|
+
throw new UsageError('read requires exactly one of an artifact ID or --slug SLUG');
|
|
290
|
+
const target = values.slug === undefined ? { artifactId: arguments_[0] } : { slug: values.slug, ...(values.project === undefined ? {} : { project: values.project }) };
|
|
291
|
+
const result = await client.read({ ...target, ...(values.revision === undefined ? {} : { revisionId: values.revision }), ...(response === undefined ? {} : { response }) });
|
|
192
292
|
if (values.out)
|
|
193
293
|
await writeOutput(values.out, `${JSON.stringify(result, null, 2)}\n`);
|
|
194
|
-
return result;
|
|
294
|
+
return quiet ? undefined : result;
|
|
195
295
|
}
|
|
196
296
|
case 'update': {
|
|
197
297
|
if (values.command)
|
|
@@ -204,6 +304,7 @@ async function main() {
|
|
|
204
304
|
const input = await prepareArtifactEdit(client, { artifactId, requestId: values['request-id'] ?? randomUUID(), changes: changes });
|
|
205
305
|
return mutate({ kind: 'update', command: updateSchema.parse(input) });
|
|
206
306
|
}
|
|
307
|
+
case 'batch': return mutate(await commandFile('batch'));
|
|
207
308
|
case 'history': return client.history({ artifactId: argument(), ...(values.limit === undefined ? {} : { limit: integer('limit') }), ...(values['before-sequence'] === undefined ? {} : { beforeSequence: integer('before-sequence') }), ...(values.summary ? { response: 'summary' } : {}) });
|
|
208
309
|
case 'publish': {
|
|
209
310
|
if (values.command)
|
|
@@ -217,26 +318,47 @@ async function main() {
|
|
|
217
318
|
await writeOutput(output, archive.bytes);
|
|
218
319
|
return { filename: archive.filename, path: resolve(output), mediaType: archive.mediaType, sha256: archive.sha256, bytes: archive.bytes.byteLength };
|
|
219
320
|
}
|
|
321
|
+
case 'move': {
|
|
322
|
+
const artifactId = argument();
|
|
323
|
+
const project = cleared('project', values.project, values['no-project']);
|
|
324
|
+
const slug = cleared('slug', values.slug, values['no-slug']);
|
|
325
|
+
if (project === undefined && slug === undefined)
|
|
326
|
+
throw new UsageError('move requires --project NAME, --no-project, --slug SLUG, or --no-slug');
|
|
327
|
+
return mutate({ kind: 'move', command: moveSchema.parse({ artifactId, requestId: values['request-id'] ?? randomUUID(), ...(project === undefined ? {} : { project }), ...(slug === undefined ? {} : { slug }) }) });
|
|
328
|
+
}
|
|
329
|
+
case 'share': return mutate({ kind: 'share', command: shareSchema.parse({ artifactId: argument(), requestId: values['request-id'] ?? randomUUID(), email: required('email'), ...(values.notify === undefined ? {} : { notify: values.notify }) }) });
|
|
330
|
+
case 'shares': return client.listShares(listSharesSchema.parse({ artifactId: argument() }));
|
|
331
|
+
case 'unshare': {
|
|
332
|
+
if (arguments_.length !== 2)
|
|
333
|
+
throw new UsageError('unshare requires two positional arguments');
|
|
334
|
+
return mutate({ kind: 'unshare', command: unshareSchema.parse({ artifactId: arguments_[0], shareId: arguments_[1], requestId: values['request-id'] ?? randomUUID() }) });
|
|
335
|
+
}
|
|
220
336
|
case 'retry': {
|
|
221
337
|
const requestId = argument();
|
|
222
338
|
const owner = await client.identity();
|
|
223
339
|
const operation = await loadOperation(stateDir, requestId, resource, owner.userId);
|
|
224
|
-
process.stderr.write(`Replaying retained ${operation.kind} operation ${requestId}.
|
|
340
|
+
process.stderr.write(`Replaying retained ${operation.kind} operation ${requestId}. Replay confirms the original operation and does not establish current state.\n`);
|
|
225
341
|
return submitOperation(client, operation);
|
|
226
342
|
}
|
|
227
343
|
}
|
|
228
344
|
}
|
|
345
|
+
function errorBody(error) {
|
|
346
|
+
if (error instanceof BatchOperationError) {
|
|
347
|
+
const body = errorBody(error.cause);
|
|
348
|
+
return { ...body, details: { ...body.details, index: error.index } };
|
|
349
|
+
}
|
|
350
|
+
return error instanceof HttpError
|
|
351
|
+
? { code: error.code, message: error.message, status: error.status, details: error.details }
|
|
352
|
+
: error instanceof z.ZodError
|
|
353
|
+
? { code: 'invalid_input', message: 'Input does not match the artifact command schema', details: { issues: error.issues } }
|
|
354
|
+
: { code: error instanceof UsageError || error instanceof TypeError || error instanceof SyntaxError ? 'invalid_arguments' : 'local_error', message: error instanceof Error ? error.message : 'Command failed', details: {} };
|
|
355
|
+
}
|
|
229
356
|
try {
|
|
230
357
|
const result = await main();
|
|
231
358
|
if (result !== undefined)
|
|
232
359
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
233
360
|
}
|
|
234
361
|
catch (error) {
|
|
235
|
-
|
|
236
|
-
? { code: error.code, message: error.message, status: error.status, details: error.details }
|
|
237
|
-
: error instanceof z.ZodError
|
|
238
|
-
? { code: 'invalid_input', message: 'Input does not match the artifact command schema', details: { issues: error.issues } }
|
|
239
|
-
: { code: error instanceof UsageError || error instanceof TypeError || error instanceof SyntaxError ? 'invalid_arguments' : 'local_error', message: error instanceof Error ? error.message : 'Command failed', details: {} };
|
|
240
|
-
process.stderr.write(`${JSON.stringify({ error: body })}\n`);
|
|
362
|
+
process.stderr.write(`${JSON.stringify({ error: errorBody(error) })}\n`);
|
|
241
363
|
process.exitCode = 1;
|
|
242
364
|
}
|
package/src/operations.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { createSchema, publishSchema, updateSchema } from '@yargote/artifacts-contracts';
|
|
2
|
+
import { batchSchema, createSchema, moveSchema, publishSchema, shareSchema, unshareSchema, updateSchema } from '@yargote/artifacts-contracts';
|
|
3
3
|
import type { ArtifactHttpClient } from '@yargote/artifacts-client';
|
|
4
4
|
export declare const operationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
5
5
|
kind: z.ZodLiteral<"create">;
|
|
@@ -16,6 +16,7 @@ export declare const operationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
16
16
|
text: "text";
|
|
17
17
|
html: "html";
|
|
18
18
|
markdown: "markdown";
|
|
19
|
+
design: "design";
|
|
19
20
|
}>;
|
|
20
21
|
text: z.ZodString;
|
|
21
22
|
}, z.core.$strict>;
|
|
@@ -29,6 +30,8 @@ export declare const operationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
29
30
|
data: z.ZodString;
|
|
30
31
|
}, z.core.$strict>>;
|
|
31
32
|
}, z.core.$strict>;
|
|
33
|
+
project: z.ZodOptional<z.ZodString>;
|
|
34
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
32
35
|
response: z.ZodOptional<z.ZodEnum<{
|
|
33
36
|
full: "full";
|
|
34
37
|
compact: "compact";
|
|
@@ -54,6 +57,7 @@ export declare const operationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
54
57
|
text: "text";
|
|
55
58
|
html: "html";
|
|
56
59
|
markdown: "markdown";
|
|
60
|
+
design: "design";
|
|
57
61
|
}>;
|
|
58
62
|
text: z.ZodString;
|
|
59
63
|
}, z.core.$strict>;
|
|
@@ -86,6 +90,117 @@ export declare const operationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
86
90
|
version: z.ZodLiteral<1>;
|
|
87
91
|
resource: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
88
92
|
ownerId: z.ZodString;
|
|
93
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
94
|
+
kind: z.ZodLiteral<"move">;
|
|
95
|
+
command: z.ZodObject<{
|
|
96
|
+
requestId: z.ZodString;
|
|
97
|
+
artifactId: z.ZodString;
|
|
98
|
+
project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
99
|
+
slug: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
100
|
+
}, z.core.$strict>;
|
|
101
|
+
version: z.ZodLiteral<1>;
|
|
102
|
+
resource: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
103
|
+
ownerId: z.ZodString;
|
|
104
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
105
|
+
kind: z.ZodLiteral<"share">;
|
|
106
|
+
command: z.ZodObject<{
|
|
107
|
+
requestId: z.ZodString;
|
|
108
|
+
artifactId: z.ZodString;
|
|
109
|
+
email: z.ZodPipe<z.ZodString, z.ZodEmail>;
|
|
110
|
+
notify: z.ZodOptional<z.ZodBoolean>;
|
|
111
|
+
requireNew: z.ZodOptional<z.ZodLiteral<true>>;
|
|
112
|
+
}, z.core.$strict>;
|
|
113
|
+
version: z.ZodLiteral<1>;
|
|
114
|
+
resource: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
115
|
+
ownerId: z.ZodString;
|
|
116
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
117
|
+
kind: z.ZodLiteral<"unshare">;
|
|
118
|
+
command: z.ZodObject<{
|
|
119
|
+
requestId: z.ZodString;
|
|
120
|
+
artifactId: z.ZodString;
|
|
121
|
+
shareId: z.ZodString;
|
|
122
|
+
}, z.core.$strict>;
|
|
123
|
+
version: z.ZodLiteral<1>;
|
|
124
|
+
resource: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
125
|
+
ownerId: z.ZodString;
|
|
126
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
127
|
+
kind: z.ZodLiteral<"batch">;
|
|
128
|
+
command: z.ZodObject<{
|
|
129
|
+
requestId: z.ZodString;
|
|
130
|
+
operations: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
131
|
+
op: z.ZodLiteral<"create">;
|
|
132
|
+
snapshot: z.ZodObject<{
|
|
133
|
+
metadata: z.ZodObject<{
|
|
134
|
+
title: z.ZodString;
|
|
135
|
+
description: z.ZodString;
|
|
136
|
+
custom: z.ZodOptional<z.ZodJSONSchema>;
|
|
137
|
+
}, z.core.$strict>;
|
|
138
|
+
content: z.ZodObject<{
|
|
139
|
+
kind: z.ZodEnum<{
|
|
140
|
+
text: "text";
|
|
141
|
+
html: "html";
|
|
142
|
+
markdown: "markdown";
|
|
143
|
+
design: "design";
|
|
144
|
+
}>;
|
|
145
|
+
text: z.ZodString;
|
|
146
|
+
}, z.core.$strict>;
|
|
147
|
+
files: z.ZodArray<z.ZodObject<{
|
|
148
|
+
path: z.ZodString;
|
|
149
|
+
mediaType: z.ZodString;
|
|
150
|
+
encoding: z.ZodEnum<{
|
|
151
|
+
utf8: "utf8";
|
|
152
|
+
base64: "base64";
|
|
153
|
+
}>;
|
|
154
|
+
data: z.ZodString;
|
|
155
|
+
}, z.core.$strict>>;
|
|
156
|
+
}, z.core.$strict>;
|
|
157
|
+
project: z.ZodOptional<z.ZodString>;
|
|
158
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
159
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
160
|
+
expectedRevisionId: z.ZodOptional<z.ZodString>;
|
|
161
|
+
snapshot: z.ZodObject<{
|
|
162
|
+
metadata: z.ZodObject<{
|
|
163
|
+
title: z.ZodString;
|
|
164
|
+
description: z.ZodString;
|
|
165
|
+
custom: z.ZodOptional<z.ZodJSONSchema>;
|
|
166
|
+
}, z.core.$strict>;
|
|
167
|
+
content: z.ZodObject<{
|
|
168
|
+
kind: z.ZodEnum<{
|
|
169
|
+
text: "text";
|
|
170
|
+
html: "html";
|
|
171
|
+
markdown: "markdown";
|
|
172
|
+
design: "design";
|
|
173
|
+
}>;
|
|
174
|
+
text: z.ZodString;
|
|
175
|
+
}, z.core.$strict>;
|
|
176
|
+
files: z.ZodArray<z.ZodObject<{
|
|
177
|
+
path: z.ZodString;
|
|
178
|
+
mediaType: z.ZodString;
|
|
179
|
+
encoding: z.ZodEnum<{
|
|
180
|
+
utf8: "utf8";
|
|
181
|
+
base64: "base64";
|
|
182
|
+
}>;
|
|
183
|
+
data: z.ZodString;
|
|
184
|
+
}, z.core.$strict>>;
|
|
185
|
+
}, z.core.$strict>;
|
|
186
|
+
artifactId: z.ZodOptional<z.ZodString>;
|
|
187
|
+
project: z.ZodOptional<z.ZodString>;
|
|
188
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
189
|
+
op: z.ZodLiteral<"update">;
|
|
190
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
191
|
+
op: z.ZodLiteral<"move">;
|
|
192
|
+
artifactId: z.ZodOptional<z.ZodString>;
|
|
193
|
+
from: z.ZodOptional<z.ZodObject<{
|
|
194
|
+
project: z.ZodOptional<z.ZodString>;
|
|
195
|
+
slug: z.ZodString;
|
|
196
|
+
}, z.core.$strict>>;
|
|
197
|
+
project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
198
|
+
slug: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
199
|
+
}, z.core.$strict>], "op">>;
|
|
200
|
+
}, z.core.$strict>;
|
|
201
|
+
version: z.ZodLiteral<1>;
|
|
202
|
+
resource: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
203
|
+
ownerId: z.ZodString;
|
|
89
204
|
}, z.core.$strict>], "kind">;
|
|
90
205
|
export type PreparedOperation = z.infer<typeof operationSchema>;
|
|
91
206
|
export type Mutation = {
|
|
@@ -97,48 +212,50 @@ export type Mutation = {
|
|
|
97
212
|
} | {
|
|
98
213
|
kind: 'publish';
|
|
99
214
|
command: z.input<typeof publishSchema>;
|
|
215
|
+
} | {
|
|
216
|
+
kind: 'move';
|
|
217
|
+
command: z.input<typeof moveSchema>;
|
|
218
|
+
} | {
|
|
219
|
+
kind: 'share';
|
|
220
|
+
command: z.input<typeof shareSchema>;
|
|
221
|
+
} | {
|
|
222
|
+
kind: 'unshare';
|
|
223
|
+
command: z.input<typeof unshareSchema>;
|
|
224
|
+
} | {
|
|
225
|
+
kind: 'batch';
|
|
226
|
+
command: z.input<typeof batchSchema>;
|
|
100
227
|
};
|
|
101
228
|
export declare function operationPath(stateDir: string, resource: string, requestId: string): string;
|
|
102
229
|
export declare function retainOperation(stateDir: string, resource: string, ownerId: string, mutation: Mutation): Promise<PreparedOperation>;
|
|
103
230
|
export declare function loadOperation(stateDir: string, requestId: string, resource: string, ownerId: string): Promise<PreparedOperation>;
|
|
104
|
-
export declare function submitOperation(client: ArtifactHttpClient, operation: PreparedOperation): Promise<{
|
|
231
|
+
export declare function submitOperation(client: ArtifactHttpClient, operation: PreparedOperation): Promise<void> | Promise<{
|
|
232
|
+
results: {
|
|
233
|
+
index: number;
|
|
234
|
+
artifactId: string;
|
|
235
|
+
revisionId: string;
|
|
236
|
+
}[];
|
|
237
|
+
}> | Promise<{
|
|
238
|
+
shareId: string;
|
|
239
|
+
artifactId: string;
|
|
240
|
+
kind: "email";
|
|
241
|
+
value: string;
|
|
242
|
+
createdAt: string;
|
|
243
|
+
notification?: {
|
|
244
|
+
status: "requested";
|
|
245
|
+
} | {
|
|
246
|
+
status: "skipped";
|
|
247
|
+
reason: "disabled";
|
|
248
|
+
} | {
|
|
249
|
+
status: "skipped";
|
|
250
|
+
reason: "rate_limited";
|
|
251
|
+
retryAt: string;
|
|
252
|
+
} | undefined;
|
|
253
|
+
}> | Promise<{
|
|
254
|
+
artifactId: string;
|
|
255
|
+
project: string | null;
|
|
256
|
+
}> | Promise<{
|
|
105
257
|
revisionId: string;
|
|
106
258
|
version: number;
|
|
107
259
|
url: string;
|
|
108
260
|
artifactId: string;
|
|
109
|
-
}
|
|
110
|
-
artifactId: string;
|
|
111
|
-
ownerId: string;
|
|
112
|
-
headRevisionId: string;
|
|
113
|
-
publication: {
|
|
114
|
-
revisionId: string;
|
|
115
|
-
version: number;
|
|
116
|
-
url: string;
|
|
117
|
-
} | null;
|
|
118
|
-
revision: {
|
|
119
|
-
id: string;
|
|
120
|
-
parentRevisionId: string | null;
|
|
121
|
-
sequence: number;
|
|
122
|
-
authorUserId: string;
|
|
123
|
-
authorClientId: string;
|
|
124
|
-
createdAt: string;
|
|
125
|
-
sha256: string;
|
|
126
|
-
snapshot: {
|
|
127
|
-
metadata: {
|
|
128
|
-
title: string;
|
|
129
|
-
description: string;
|
|
130
|
-
custom?: z.core.util.JSONType | undefined;
|
|
131
|
-
};
|
|
132
|
-
content: {
|
|
133
|
-
kind: "text" | "html" | "markdown";
|
|
134
|
-
text: string;
|
|
135
|
-
};
|
|
136
|
-
files: {
|
|
137
|
-
path: string;
|
|
138
|
-
mediaType: string;
|
|
139
|
-
encoding: "utf8" | "base64";
|
|
140
|
-
data: string;
|
|
141
|
-
}[];
|
|
142
|
-
};
|
|
143
|
-
};
|
|
144
|
-
} | import("@yargote/artifacts-client").CompactArtifactRead>;
|
|
261
|
+
}>;
|
package/src/operations.js
CHANGED
|
@@ -3,7 +3,7 @@ import { lstat, mkdir, open } from 'node:fs/promises';
|
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import { createSchema, publishSchema, updateSchema } from '@yargote/artifacts-contracts';
|
|
6
|
+
import { batchSchema, createSchema, moveSchema, publishSchema, shareSchema, unshareSchema, updateSchema } from '@yargote/artifacts-contracts';
|
|
7
7
|
import { canonicalResource } from '@yargote/artifacts-contracts/http';
|
|
8
8
|
const binding = {
|
|
9
9
|
version: z.literal(1),
|
|
@@ -14,6 +14,10 @@ export const operationSchema = z.discriminatedUnion('kind', [
|
|
|
14
14
|
z.strictObject({ ...binding, kind: z.literal('create'), command: createSchema }),
|
|
15
15
|
z.strictObject({ ...binding, kind: z.literal('update'), command: updateSchema }),
|
|
16
16
|
z.strictObject({ ...binding, kind: z.literal('publish'), command: publishSchema }),
|
|
17
|
+
z.strictObject({ ...binding, kind: z.literal('move'), command: moveSchema }),
|
|
18
|
+
z.strictObject({ ...binding, kind: z.literal('share'), command: shareSchema }),
|
|
19
|
+
z.strictObject({ ...binding, kind: z.literal('unshare'), command: unshareSchema }),
|
|
20
|
+
z.strictObject({ ...binding, kind: z.literal('batch'), command: batchSchema }),
|
|
17
21
|
]);
|
|
18
22
|
export function operationPath(stateDir, resource, requestId) {
|
|
19
23
|
createSchema.shape.requestId.parse(requestId);
|
|
@@ -86,5 +90,11 @@ export function submitOperation(client, operation) {
|
|
|
86
90
|
case 'create': return client.create(operation.command);
|
|
87
91
|
case 'update': return client.update(operation.command);
|
|
88
92
|
case 'publish': return client.publish(operation.command);
|
|
93
|
+
case 'move': return client.move(operation.command);
|
|
94
|
+
case 'share': return client.share(operation.command);
|
|
95
|
+
case 'unshare': return client.unshare(operation.command);
|
|
96
|
+
case 'batch': return client.batch(operation.command);
|
|
89
97
|
}
|
|
98
|
+
const exhaustive = operation;
|
|
99
|
+
throw new Error(`Unsupported retained operation: ${exhaustive}`);
|
|
90
100
|
}
|
package/src/skills.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface SkillsOptions {
|
|
2
|
+
full?: boolean;
|
|
3
|
+
all?: boolean;
|
|
4
|
+
agent?: string;
|
|
5
|
+
global?: boolean;
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function runSkillsCommand(positionals: string[], options: SkillsOptions, seen: ReadonlySet<string>): Promise<void>;
|
|
9
|
+
export {};
|