@yargote/artifacts-cli 0.1.0-beta.6 → 0.1.0-beta.8
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 +44 -13
- package/package.json +3 -3
- package/skills/artifacts-cli/SKILL.md +8 -4
- package/skills/artifacts-cli/references/commands.md +39 -6
- 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 +12 -2
- package/skills/artifacts-design/references/canvas.md +100 -0
- package/skills/artifacts-design/references/local-checks.md +70 -0
- package/skills/artifacts-design/scripts/embed-fonts.mjs +208 -7
- package/skills/artifacts-design/scripts/verify-local.mjs +333 -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 +48 -12
- package/src/operations.d.ts +3 -0
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@ Use Node 24 or later. The beta targets macOS and Linux. Windows has not been ver
|
|
|
11
11
|
Choose one persistent installation:
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
npm install --global @yargote/artifacts-cli@0.1.0-beta.
|
|
14
|
+
npm install --global @yargote/artifacts-cli@0.1.0-beta.8
|
|
15
15
|
# Or, with pnpm and an existing global bin directory on PATH:
|
|
16
|
-
pnpm add --global @yargote/artifacts-cli@0.1.0-beta.
|
|
16
|
+
pnpm add --global @yargote/artifacts-cli@0.1.0-beta.8
|
|
17
17
|
artifacts --version
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -22,23 +22,26 @@ If pnpm reports that no global bin directory exists, run `pnpm setup` and reopen
|
|
|
22
22
|
For one-off execution, pin the same version:
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
|
-
npx --yes @yargote/artifacts-cli@0.1.0-beta.
|
|
26
|
-
pnpm dlx @yargote/artifacts-cli@0.1.0-beta.
|
|
25
|
+
npx --yes @yargote/artifacts-cli@0.1.0-beta.8 --help
|
|
26
|
+
pnpm dlx @yargote/artifacts-cli@0.1.0-beta.8 --help
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
`@beta` follows future prereleases. Exact versions remain reproducible. No stable `latest` release is part of this beta. An organization's pnpm minimum release age or trust policy can delay a new release. Follow that policy rather than disabling it globally.
|
|
30
30
|
|
|
31
31
|
## Complete first use
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Check `artifacts --help` for saved-connection support. With an older package that requires a server on every command, keep `ARTIFACTS_SERVER` set to the selected REST resource.
|
|
34
|
+
|
|
35
|
+
An existing Artifacts account is required. Select the REST OAuth resource, which is the API origin without `/mcp`. When upgrading an older deployment, complete the [API migration](../../docs/deployment.md#migrate-the-api-hostname) before using the address below.
|
|
34
36
|
|
|
35
37
|
```sh
|
|
36
|
-
|
|
37
|
-
artifacts login
|
|
38
|
+
artifacts login --server 'https://artifacts-api.yersonargotev.workers.dev'
|
|
38
39
|
artifacts whoami
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
Login opens browser authorization. After you approve access
|
|
42
|
+
Login opens browser authorization. After you approve access and the CLI verifies your identity, it saves the resolved REST resource in private state. A new terminal can run `artifacts whoami` without a server option or environment variable. It prints JSON with your user ID and scopes.
|
|
43
|
+
|
|
44
|
+
Use `artifacts login --no-browser` to open the printed authorization URL yourself on the same machine. This manual flow saves the connection only after identity verification succeeds.
|
|
42
45
|
|
|
43
46
|
Create `snapshot.json`:
|
|
44
47
|
|
|
@@ -82,19 +85,33 @@ Run `artifacts logout` before uninstalling if you want to clear credentials. Uni
|
|
|
82
85
|
|
|
83
86
|
Skills, help, and version commands work without a server or login. Skills commands do not read credentials or create private state. They accept and ignore global `--server` and `--state-dir` values, including their environment defaults.
|
|
84
87
|
|
|
85
|
-
`--server RESOURCE`
|
|
88
|
+
`--server RESOURCE` overrides `ARTIFACTS_SERVER`, which overrides the connection saved by the last successful login in the selected state directory.
|
|
89
|
+
|
|
90
|
+
An ordinary command override applies to that command and does not replace the saved connection. A later successful login replaces it with the verified canonical REST resource. A failed login to another server preserves the previous selection and its credentials.
|
|
91
|
+
|
|
92
|
+
Login also accepts an MCP resource that advertises its REST resource. Use the configured `API_URL` origin to call REST under `/v1`.
|
|
86
93
|
|
|
87
|
-
|
|
94
|
+
Without an override or saved connection, run `artifacts login --server RESOURCE` first. The CLI has no production default and does not rewrite the supplied URL. Existing installations keep their exact-resource credentials, but must complete a successful login to save a connection.
|
|
95
|
+
|
|
96
|
+
Older deployments without `API_URL` still use their complete MCP resource and routes under `/mcp/v1`.
|
|
97
|
+
|
|
98
|
+
A hostname change requires a new `artifacts login --server RESOURCE`. Credentials and retained operations remain bound to their exact original resource. Do not move an old operation file to the new resource or redirect its bearer requests.
|
|
99
|
+
|
|
100
|
+
`--state-dir DIR` overrides `ARTIFACTS_STATE_DIR`. The default is `~/.local/state/artifacts`. The state directory must be private and owned by the current user. Each state directory has its own saved connection and credential profiles. Credentials and the saved connection share one private database, separate from operation files. Profiles bind credentials to the exact resource. Retained operation paths hash the resource and request ID, so request IDs cannot select filesystem paths.
|
|
88
101
|
|
|
89
102
|
HTTPS is required except for HTTP on the exact loopback hosts `localhost`, `127.0.0.1`, and `[::1]`. Server URLs cannot contain credentials, a query, or a fragment.
|
|
90
103
|
|
|
91
104
|
## Authentication
|
|
92
105
|
|
|
93
|
-
`artifacts login --server RESOURCE` verifies protected-resource metadata
|
|
106
|
+
`artifacts login --server RESOURCE` verifies protected-resource metadata. If a resource advertises `artifacts_rest_resource`, the relation may name the same resource or one distinct REST resource. Login verifies metadata for a distinct resource and prints its REST URL to stderr. The distinct resource may omit the relation or name itself, but cannot name another resource. After identity verification, login saves that REST URL for later commands. An existing `ARTIFACTS_SERVER` still overrides it. Unset the variable to use the saved connection. Login anonymously checks `<resource>/v1/identity` before registering a public native OAuth client and opening browser authorization with PKCE. This check confirms REST compatibility. Login verifies your identity again with the received token before atomically saving credentials and the connection.
|
|
107
|
+
|
|
108
|
+
Errors identify the phase, method, endpoint, and HTTP status when a response exists. Network failures have no HTTP status. If the compatibility check fails, confirm the configured REST URL with your service administrator. A valid MCP metadata document alone does not establish REST compatibility.
|
|
109
|
+
|
|
110
|
+
`--no-browser` runs the same checks and prints the authorization URL for manual navigation. The loopback callback listener starts before browser navigation. Login requests read, write, and publication scopes. Client registration is reused for the profile.
|
|
94
111
|
|
|
95
112
|
`artifacts whoami` returns the verified user, session, OAuth client, and scopes.
|
|
96
113
|
|
|
97
|
-
`artifacts logout` clears local credentials and attempts to revoke the refresh token. Its JSON contains `signedOut: true` and `remoteRevocation` with `confirmed`, `unconfirmed`, or `not-needed`. Issued access JWTs can remain valid until expiry or central-session revocation.
|
|
114
|
+
`artifacts logout` clears local credentials and attempts to revoke the refresh token. It preserves the saved connection, so `artifacts login` can reuse the server without another URL. Its JSON contains `signedOut: true` and `remoteRevocation` with `confirmed`, `unconfirmed`, or `not-needed`. Issued access JWTs can remain valid until expiry or central-session revocation.
|
|
98
115
|
|
|
99
116
|
`ARTIFACTS_ACCESS_TOKEN` supplies an access token for automation. It does not require local login. Tokens are never command arguments or retained operation fields.
|
|
100
117
|
|
|
@@ -108,12 +125,14 @@ The private `credentials.sqlite` file stores credentials. The separate `credenti
|
|
|
108
125
|
artifacts login [--no-browser]
|
|
109
126
|
artifacts logout
|
|
110
127
|
artifacts whoami
|
|
111
|
-
artifacts create --snapshot FILE [--project NAME] [--request-id ID]
|
|
128
|
+
artifacts create --snapshot FILE [--project NAME] [--slug SLUG] [--request-id ID]
|
|
112
129
|
[--response full|compact]
|
|
113
130
|
artifacts create --command FILE
|
|
114
131
|
artifacts list [--limit N] [--cursor CURSOR] [--project NAME]
|
|
115
132
|
artifacts projects
|
|
116
133
|
artifacts read ID [--revision REV] [--response full|compact] [--out FILE [--quiet]]
|
|
134
|
+
artifacts read --slug SLUG [--project NAME] [--revision REV] [--response full|compact]
|
|
135
|
+
[--out FILE [--quiet]]
|
|
117
136
|
artifacts update ID --snapshot FILE --expected-revision REV [--request-id ID]
|
|
118
137
|
[--response full|compact]
|
|
119
138
|
artifacts update [ID] --command FILE
|
|
@@ -200,6 +219,18 @@ Every artifact read and list item includes `project`, a name or null. `projects`
|
|
|
200
219
|
|
|
201
220
|
Version `0.1.0-beta.4` and earlier parse artifact responses strictly and fail with `invalid_response` against a server that returns `project`. Upgrade the CLI together with the server.
|
|
202
221
|
|
|
222
|
+
## Slugs
|
|
223
|
+
|
|
224
|
+
A slug names a page within its project, so `read` can find it without its ID. `create --slug SLUG` sets it. A slug contains 1 to 160 characters of lowercase ASCII kebab-case segments separated by `/`, such as `concepts/rag` or `index`. It is never normalized, and a trailing `.md` is rejected with a hint to remove it. A complete create command file can include the same optional `slug` field.
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
artifacts create --snapshot snapshot.json --project Mi-Wiki --slug concepts/rag
|
|
228
|
+
artifacts read --project mi-wiki --slug concepts/rag
|
|
229
|
+
artifacts read --slug index
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
A slug is unique within the owner's project, matched by the project's stored spelling. Artifacts without a project share one unfiled namespace, which `read --slug` reads without `--project`. `read` takes exactly one of an ID or `--slug`, and `--project` applies only with `--slug`. A used slug fails `create` with `slug_conflict`, status 409, and the holder's `artifactId`. A locator with no artifact fails with `not_found`. Every artifact read and list item includes `slug`, a name or null. Version `0.1.0-beta.7` and earlier reject responses that include `slug` with `invalid_response`.
|
|
233
|
+
|
|
203
234
|
## Share publication access
|
|
204
235
|
|
|
205
236
|
These commands are available from `0.1.0-beta.6`. Check installed help for support.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yargote/artifacts-cli",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Command-line client for the Artifacts REST API.",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"skills"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@yargote/artifacts-contracts": "0.1.0-beta.
|
|
31
|
-
"@yargote/artifacts-client": "0.1.0-beta.
|
|
30
|
+
"@yargote/artifacts-contracts": "0.1.0-beta.8",
|
|
31
|
+
"@yargote/artifacts-client": "0.1.0-beta.8",
|
|
32
32
|
"openid-client": "6.8.8",
|
|
33
33
|
"zod": "4.6.5"
|
|
34
34
|
},
|
|
@@ -9,7 +9,7 @@ Use the installed `artifacts` executable. Run `artifacts --help` and read [the c
|
|
|
9
9
|
|
|
10
10
|
## Select the account and resource
|
|
11
11
|
|
|
12
|
-
Use the resource selected by the user
|
|
12
|
+
Use the resource selected by the user. Service commands select `--server RESOURCE` before `ARTIFACTS_SERVER`, then the connection saved by a successful login in the selected private state directory. Ordinary command overrides do not replace that connection. Skills, help, and version commands work offline without a server or login. Check installed help for saved-connection support. A resource is the canonical OAuth URL, such as `https://artifacts.example.test/mcp`. REST routes are below that resource at `/v1`. Do not infer a production connection from an example or silently switch to a local server.
|
|
13
13
|
|
|
14
14
|
Run `artifacts whoami` before the first mutation when the current identity is not established. Keep the selected resource and owner consistent throughout one operation. If login is needed, use `artifacts login --server RESOURCE`. The user authenticates in the browser. Do not collect their password or read another application's credentials. Never put tokens in command arguments, operation files, logs, or the final reply.
|
|
15
15
|
|
|
@@ -17,11 +17,11 @@ Run `artifacts whoami` before the first mutation when the current identity is no
|
|
|
17
17
|
|
|
18
18
|
A revision is a complete snapshot with `metadata`, `content`, and `files`. The command reference contains its JSON shape. Use the requested title, language, content, and attachments. Do not include unrelated files or credentials.
|
|
19
19
|
|
|
20
|
-
Use self-contained HTML for interactive content. Inline styles and scripts and embed required assets as data URLs. External requests, CDN dependencies, relative executable assets, forms, browser storage, and multi-file websites are unsupported. Attachments are downloads, not importable website resources. Markdown and text display inertly.
|
|
20
|
+
Use self-contained HTML for interactive content. For several HTML boards in one canvas, use `design` and follow the design skill's `references/canvas.md`. Inline styles and scripts and embed required assets as data URLs. External requests, CDN dependencies, relative executable assets, forms, browser storage, and multi-file websites are unsupported. Attachments are downloads, not importable website resources. Markdown and text display inertly.
|
|
21
21
|
|
|
22
22
|
Keep the whole revision within 1 MiB of decoded content, metadata, attachment paths, media types, and attachment bytes. At most 32 attachments are accepted. Base64 changes wire size, not the decoded limit. If required content exceeds a limit, explain the constraint without silently removing it.
|
|
23
23
|
|
|
24
|
-
Before writing or restyling HTML, run `artifacts skills get artifacts-design --full` and follow it. It owns typography, embedded fonts, color, layout, and the rendered checks. Finish those checks before saving when browser tools are available. A successful CLI write proves persistence, not visual quality or browser compatibility. Interactive HTML follows the server's Chrome policy; do not promise it in every browser.
|
|
24
|
+
Before writing or restyling HTML or design boards, run `artifacts skills get artifacts-design --full` and follow it. It owns typography, embedded fonts, color, layout, and the rendered checks. Finish those checks before saving when browser tools are available. A successful CLI write proves persistence, not visual quality or browser compatibility. Interactive HTML follows the server's Chrome policy; do not promise it in every browser.
|
|
25
25
|
|
|
26
26
|
## Create or revise
|
|
27
27
|
|
|
@@ -35,6 +35,8 @@ Use `edit` for partial changes that preserve omitted fields. Use `update` for a
|
|
|
35
35
|
|
|
36
36
|
When the user names a project for new content, add `--project NAME` to `create`. Run `artifacts projects` first when an existing project may already fit. A name matching an existing project regardless of letter case joins it and keeps the stored spelling. Do not invent a project the user did not ask for. Use `list --project NAME` to find artifacts in a project.
|
|
37
37
|
|
|
38
|
+
When the user names a page, add `--slug SLUG` such as `concepts/rag` to `create`, and read it later with `read --project NAME --slug SLUG`, or `read --slug SLUG` for an unfiled page, instead of listing for its ID. Omit the `.md` suffix. A `slug_conflict` error names the `artifactId` that already holds the slug in that project.
|
|
39
|
+
|
|
38
40
|
Use `move ID --project NAME` to file an existing artifact under a project and `move ID --no-project` to remove it from one. A move changes only the project. It creates no revision and leaves the publication unchanged. It is a retained mutation, so recover an uncertain move with `retry`. Verify it with `read` or `list --project NAME`. A project disappears when its last artifact leaves it.
|
|
39
41
|
|
|
40
42
|
## Publish deliberately
|
|
@@ -63,10 +65,12 @@ A `request_conflict` means the ID was used with different arguments. Do not desc
|
|
|
63
65
|
|
|
64
66
|
## Verify and deliver
|
|
65
67
|
|
|
66
|
-
After create or update, read the explicit returned revision. Use `read ARTIFACT_ID --revision REVISION_ID --out FILE --quiet` to retain the full response without printing its snapshot. Check that the installed `--help` supports `--quiet` first. For older versions, use the command workflow's stdout redirection fallback. Compare the saved complete metadata, content, and decoded attachment bytes with the intended snapshot. For an update, check that its parent is the head used for preparation. Compact responses cannot verify omitted content.
|
|
68
|
+
Retain successful create, update, or edit stdout in a private receipt file. After create or update, read the explicit returned revision. Use `read ARTIFACT_ID --revision REVISION_ID --out FILE --quiet` to retain the full response without printing its snapshot. Check that the installed `--help` supports `--quiet` first. For older versions, use the command workflow's stdout redirection fallback. Compare the saved complete metadata, content, and decoded attachment bytes with the intended snapshot. For an update, check that its parent is the head used for preparation. Compact responses cannot verify omitted content.
|
|
67
69
|
|
|
68
70
|
After publication, read current publication and compare revision, version, and URL with the committed result. A replayed receipt describes the original commit; another writer may have changed the current publication afterward. Report that difference.
|
|
69
71
|
|
|
72
|
+
For real panel and requested protected-publication checks, follow [the exact saved revision recipe](references/verify-retained.md). It prepares the existing verifier inputs from the write receipt and independent full read. It also gives an installed-browser route when the complete verifier runtime is unavailable. Report persistence, local rendering, panel rendering, and protected publication separately. Input preparation is not a browser pass.
|
|
73
|
+
|
|
70
74
|
Follow list and history cursors when the request needs more than the first page. Export an explicit retained revision to the requested destination. The CLI checks the archive digest. Confirm that the file exists and inspect its manifest and relevant content before linking it.
|
|
71
75
|
|
|
72
76
|
Report the title, saved revision, and draft or published state in the user's language. Link the actual export or returned protected publication URL. Distinguish persistence from any visual or browser checks that did not run. Keep local credential and session identifiers private.
|
|
@@ -24,16 +24,17 @@ The published `0.1.0-beta.1` package predates skills commands. Do not assume sup
|
|
|
24
24
|
## Authenticate
|
|
25
25
|
|
|
26
26
|
```sh
|
|
27
|
-
|
|
28
|
-
artifacts login
|
|
27
|
+
artifacts login --server https://artifacts.example.test/mcp
|
|
29
28
|
artifacts whoami
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
Replace the example with the selected resource before executing it. Service commands
|
|
31
|
+
Replace the example with the selected resource before executing it. Service commands select `--server RESOURCE` before `ARTIFACTS_SERVER`, then the connection saved by a successful identity-verified login. Ordinary overrides do not replace the saved connection. A later successful login replaces it with the verified REST resource. A failed login to another server preserves the previous connection and its credentials. Check installed help for saved-connection support.
|
|
32
|
+
|
|
33
|
+
`--state-dir DIR` or `ARTIFACTS_STATE_DIR` selects an isolated private state directory. For automation, `ARTIFACTS_ACCESS_TOKEN` supplies an already authorized bearer token. Do not echo it or put it in an argument.
|
|
33
34
|
|
|
34
35
|
Use `--no-browser` when the environment cannot open a browser automatically. Open the printed authorization URL and keep the CLI running for its loopback callback. A remote machine's loopback callback must be reachable by the browser used for login. Do not replace the callback with an arbitrary public listener.
|
|
35
36
|
|
|
36
|
-
`logout` clears local credentials and attempts refresh-token revocation. Copied access JWTs can remain valid until expiry. The existing account page's global session revocation stops access across CLI, REST, MCP, and panel. Local logout does not claim to perform that global action.
|
|
37
|
+
`logout` clears local credentials and attempts refresh-token revocation. It keeps the saved connection, so a later `artifacts login` can reuse it. Copied access JWTs can remain valid until expiry. The existing account page's global session revocation stops access across CLI, REST, MCP, and panel. Local logout does not claim to perform that global action.
|
|
37
38
|
|
|
38
39
|
## Create a complete snapshot
|
|
39
40
|
|
|
@@ -61,7 +62,7 @@ Save this JSON as `snapshot.json`, replacing its content with the requested arti
|
|
|
61
62
|
}
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
`metadata.description` and `files` are required even when empty. Content kind is `html`, `markdown`, or `
|
|
65
|
+
`metadata.description` and `files` are required even when empty. Content kind is `html`, `markdown`, `text`, or `design`. Binary files use `encoding: "base64"` and canonical padded base64. Paths are unique normalized relative paths. Do not use empty, dot, parent, absolute, backslash, percent, or colon path segments. A file cannot also be another file's directory.
|
|
65
66
|
|
|
66
67
|
```sh
|
|
67
68
|
artifacts create --snapshot snapshot.json
|
|
@@ -75,6 +76,20 @@ For `read`, `--out FILE --quiet` saves the returned response without printing su
|
|
|
75
76
|
|
|
76
77
|
Check installed help for `--quiet` support before running these examples. Older versions, including `0.1.0-beta.1`, can suppress stdout with `artifacts read ARTIFACT_ID --out saved.json > /dev/null`. This redirection preserves stderr and the command's exit status. Use the same fallback for the revision reads below.
|
|
77
78
|
|
|
79
|
+
## Prepare a design canvas
|
|
80
|
+
|
|
81
|
+
Read the design skill's `references/canvas.md` through `artifacts skills get artifacts-design --full`. Keep the index in `my-canvas/canvas.json` and complete UTF-8 board documents in `my-canvas/boards/*.html`.
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
artifacts skills path artifacts-cli
|
|
85
|
+
node <returned-skill-directory>/scripts/prepare-retained.mjs ./my-canvas > snapshot.json
|
|
86
|
+
artifacts create --snapshot snapshot.json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The positional form accepts exactly one directory. It preserves the index text and board HTML, sorts snapshot files by path, validates the complete `design` snapshot with the installed contracts, and rejects missing or orphan boards. It uses the directory basename as the title and an empty description. Edit the emitted metadata before saving when needed. Keep the script under the path returned by `skills path` so its package dependencies resolve. All board data, embedded fonts, index text, and metadata share the 1 MiB budget.
|
|
90
|
+
|
|
91
|
+
The existing named-option form of `prepare-retained.mjs` builds a private retained-verification config from saved receipts and an independent read. It does not prepare a content snapshot. The two input forms cannot be combined.
|
|
92
|
+
|
|
78
93
|
## Organize projects
|
|
79
94
|
|
|
80
95
|
Check that installed help lists `projects` and `move` before using them.
|
|
@@ -91,6 +106,20 @@ A project name contains 1 to 80 characters after trimming and NFC normalization,
|
|
|
91
106
|
|
|
92
107
|
`move` takes exactly one of `--project NAME` or `--no-project` and prints `{ "artifactId": "...", "project": "Archive" }`, or `null` for the project after `--no-project`. It creates no revision and leaves the publication unchanged. Moving to the current project succeeds without change. A lost move response is recovered with `retry REQUEST_ID` like any other retained mutation.
|
|
93
108
|
|
|
109
|
+
## Name pages with slugs
|
|
110
|
+
|
|
111
|
+
Check that installed help lists `--slug` before using it.
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
artifacts create --snapshot snapshot.json --project "Mi wiki" --slug concepts/rag
|
|
115
|
+
artifacts read --project "mi wiki" --slug concepts/rag
|
|
116
|
+
artifacts read --slug index
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
A slug contains 1 to 160 characters of lowercase ASCII kebab-case segments separated by `/`, such as `concepts/rag` or `index`. It is never normalized, and a trailing `.md` is rejected with a hint to remove it. A slug is unique within the owner's project, matched by the project's stored spelling. Artifacts without a project share one unfiled namespace, which `read --slug` without `--project` reads. Every read and list item includes `slug`, a name or null.
|
|
120
|
+
|
|
121
|
+
`read` takes exactly one of an ID or `--slug`. `--project` applies only with `--slug`. A used slug fails `create` with `slug_conflict`, status 409, and the holder's `artifactId` in `details`. A locator with no artifact fails with `not_found`. A move keeps the slug and fails with `slug_conflict` when the destination project already uses it.
|
|
122
|
+
|
|
94
123
|
## Edit without losing retained data
|
|
95
124
|
|
|
96
125
|
For a title change, save `changes.json` with only the requested field.
|
|
@@ -120,7 +149,7 @@ Command files contain the command itself, without the CLI's retained-operation w
|
|
|
120
149
|
| `update` | `requestId`, `artifactId`, `expectedRevisionId`, `snapshot` |
|
|
121
150
|
| `publish` | `requestId`, `artifactId`, `revisionId`, `expectedPublicationVersion` |
|
|
122
151
|
|
|
123
|
-
`requestId` is a caller-chosen string of 8 to 128 characters. Create and update also accept optional `response` with value `full` or `compact`. Create also accepts an optional `project` name
|
|
152
|
+
`requestId` is a caller-chosen string of 8 to 128 characters. Create and update also accept optional `response` with value `full` or `compact`. Create also accepts an optional `project` name and an optional `slug`. This complete update example replaces a snapshot. Substitute the observed IDs and preserve all required content and attachments in its `snapshot`.
|
|
124
153
|
|
|
125
154
|
```json
|
|
126
155
|
{
|
|
@@ -179,6 +208,10 @@ Share with `--notify` to request one email attempt. Access succeeds when mail is
|
|
|
179
208
|
|
|
180
209
|
Owners can reserve 100 invitations per UTC day. Revoke, deletion, and failed delivery do not refund reservations. New access is limited to 100 people per artifact and 1000 grants per owner. Existing access remains listable and revocable.
|
|
181
210
|
|
|
211
|
+
## Verify the exact saved revision
|
|
212
|
+
|
|
213
|
+
Follow [the retained revision recipe](verify-retained.md) after successful authoring and requested publication. Retain each successful write stdout in a private receipt file. The recipe extracts its IDs, performs a full independent explicit-revision read, and prepares the existing verifier config without authoring or account mutations. It includes an installed-browser path and identifies the additional capabilities for complete verification.
|
|
214
|
+
|
|
182
215
|
## Retry and export
|
|
183
216
|
|
|
184
217
|
```sh
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Verify the saved revision in the panel
|
|
2
|
+
|
|
3
|
+
Use the successful create, update, or edit result and an independent full read to select the exact saved revision. Use an authorized browser session to inspect its real panel and, when requested, its protected publication. Preparation alone does not prove persistence or browser behavior.
|
|
4
|
+
|
|
5
|
+
## Retain the authoring results
|
|
6
|
+
|
|
7
|
+
Before the authorized write, use `umask 077` and save successful stdout to a new private receipt file. For example, add `> write-result.json` to the create or update command you already intend to run. Keep stderr and the retained request ID for recovery. Continue only after exit zero. Do not repeat a write to obtain a receipt after an uncertain response. Follow [retry recovery](commands.md#retry-and-export).
|
|
8
|
+
|
|
9
|
+
If publication was requested, also retain the successful publish stdout as `publish-result.json`. Finish that authorized publication before the independent read below. Do not publish, share, or revoke access merely to verify an artifact.
|
|
10
|
+
|
|
11
|
+
## Prepare the exact selection
|
|
12
|
+
|
|
13
|
+
Use Node 24+ and a CLI whose help lists `skills path` and `read --quiet`. For older read output, use the [stdout fallback](commands.md#create-a-complete-snapshot). The installed helper requires only Node built-ins. It makes no service request and does not open a browser.
|
|
14
|
+
|
|
15
|
+
Set `observed_panel_origin` to the panel origin observed in the authorized browser or supplied service configuration. Do not derive it from `ARTIFACTS_SERVER` or the publication host. Replace the example origin before execution.
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
umask 077
|
|
19
|
+
observed_panel_origin=https://panel.example.test
|
|
20
|
+
skill_directory=$(artifacts skills path artifacts-cli)
|
|
21
|
+
artifact_id=$(node -e '
|
|
22
|
+
try {
|
|
23
|
+
const id = JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8")).artifactId;
|
|
24
|
+
if (typeof id !== "string" || id.length === 0) throw new Error();
|
|
25
|
+
process.stdout.write(id);
|
|
26
|
+
} catch {
|
|
27
|
+
console.error("Cannot extract the artifact ID from the write receipt");
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
}
|
|
30
|
+
' write-result.json)
|
|
31
|
+
revision_id=$(node -e '
|
|
32
|
+
try {
|
|
33
|
+
const id = JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8")).revision.id;
|
|
34
|
+
if (typeof id !== "string" || id.length === 0) throw new Error();
|
|
35
|
+
process.stdout.write(id);
|
|
36
|
+
} catch {
|
|
37
|
+
console.error("Cannot extract the revision ID from the write receipt");
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
40
|
+
' write-result.json)
|
|
41
|
+
artifacts read "$artifact_id" --revision "$revision_id" --response full --out saved-read.json --quiet
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Stop if any extraction or read fails. A compact write receipt can select a revision. The independent read must contain the complete snapshot. Compare its complete metadata, content, and decoded attachment bytes with the intended snapshot. For an update, check the saved parent against the head used to prepare that update.
|
|
45
|
+
|
|
46
|
+
Save `checks.json` with the requested viewport sizes and meaningful expectations for this document. Replace the example selectors and values with intended content behavior. Expectations run in order inside the content iframe after a fresh load at each viewport.
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"viewports": [{ "width": 1440, "height": 900 }, { "width": 390, "height": 844 }],
|
|
51
|
+
"expectations": [
|
|
52
|
+
{ "kind": "text", "selector": "#total", "text": "5.00" },
|
|
53
|
+
{ "kind": "click", "selector": "#change" },
|
|
54
|
+
{ "kind": "text", "selector": "#total", "text": "28.40" }
|
|
55
|
+
],
|
|
56
|
+
"timeoutMs": 10000
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use `text` for exact rendered text, `visible` for visibility, `count` for an element count, or `attribute` for an exact attribute value. Use `name` and `value` with `attribute`. Interactions also support `fill` with `value`, `check` with `checked`, and `select` with a `values` array. Do not use arbitrary JavaScript or target panel controls in these expectations.
|
|
61
|
+
|
|
62
|
+
Prepare a new private config with a future evidence directory that does not yet exist.
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
node "$skill_directory/scripts/prepare-retained.mjs" \
|
|
66
|
+
--receipt write-result.json --read saved-read.json \
|
|
67
|
+
--panel-origin "$observed_panel_origin" --checks checks.json \
|
|
68
|
+
--evidence "$PWD/new-retained-evidence" --out retained-config.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
When publication was requested, add `--publication-receipt publish-result.json` to that invocation. The helper compares its artifact, revision, version, and URL with the current publication in the saved read. A different publication or concurrent change fails preparation. Report the difference. Do not replace expected versions or publish again to make verification pass.
|
|
72
|
+
|
|
73
|
+
The helper derives all selected IDs and URLs from these inputs. It accepts a newer draft head while keeping the saved revision pinned and reports `headChanged: true`. It emits `status: "prepared"`, never a verification pass. Without a publication receipt, it omits publication coverage even when another revision is published. Existing output files are preserved.
|
|
74
|
+
|
|
75
|
+
## Inspect with an authorized browser
|
|
76
|
+
|
|
77
|
+
This route works with the installed CLI, its bundled files, and browser-control capabilities. It requires no repository checkout. Use a user-supplied session or complete the normal sign-in flow. CLI OAuth login does not supply panel cookies. Keep browser authentication inside the browser. Never extract credentials to construct a Playwright context or state file.
|
|
78
|
+
|
|
79
|
+
After sign-in setup, perform these read-only checks on the selected artifact. Do not create revisions, publish, share, log out, or revoke sessions. Normal protected-viewer admission can consume its one-use grant through the existing GET handoff.
|
|
80
|
+
|
|
81
|
+
1. Read `retained-config.json` privately and open its exact `panelUrl`. Compare the artifact, revision, digest, and publication version with `expected`. Current panels expose `data-artifact-id`, `data-revision-id`, `data-revision-sha256`, and `data-publication-version`. Version zero means no publication. Report a newer head separately. Never switch the selected revision to the head.
|
|
82
|
+
2. Establish the actual browser version and available frame, response, console, download, and viewport controls. Record missing capabilities. A browser name, load event, or empty console does not establish that content loaded.
|
|
83
|
+
3. Inspect the actual preview iframe source, pinned revision, sandbox, referrer policy, and dimensions. Wait for a distinctive expected element within the configured deadline. Exercise each authorized content control and record the resulting visible state. Repeat at each available requested viewport and check keyboard interactions and overflow where relevant.
|
|
84
|
+
4. When response inspection or native DevTools is available, inspect the iframe request. Record completion, status, content type, CSP, connection allowlist, cache policy, and DNS policy. Distinguish interactive content, inert fallback, and error responses. A pending request has no final response evidence. A top-level preview navigation deliberately returns 403 and cannot replace this iframe check.
|
|
85
|
+
5. When the config includes `publicationUrl`, open that separate protected viewer through normal admission. Compare its selected revision, digest, and version with `expected`. Observe the real content and repeat the authorized interactions. Open `Archivos` and compare the complete attachment inventory and revision-bound links. Check protected download bytes when that capability is available. Missing attachment or response evidence remains unavailable.
|
|
86
|
+
6. After publication interactions, reread the same explicit revision with the quoted IDs into a new private file. Compare its current `publication.revisionId`, `version`, and `url` with the saved read and publish receipt. Reload the selected panel and compare its publication markers again. Report a changed publication as a concurrent change. Do not report protected-publication success for a different selected revision.
|
|
87
|
+
7. If the frame is blank, distinguish admission, pending transport, policy, browser compatibility, content execution, and browser-control failures from observed evidence. If debugger attachment stalls it, disconnect through normal browser controls, reload the same pinned page, and repeat the interaction with native controls. Preserve authentication and containment. Attribute a debugger failure only when that comparison demonstrates it.
|
|
88
|
+
8. Save a sanitized report and private screenshots. Include selected IDs, digest, browser version, viewport sizes, actions, resulting visible states, observed response policies, and unavailable checks. Exclude cookies, tokens, viewer grants, callback queries, raw authentication requests, and private content from diagnostic logs.
|
|
89
|
+
|
|
90
|
+
This is partial browser verification. Do not return the complete verifier's pass verdict. Missing exact bytes, ZIP, policy, attachment, console, or viewport evidence must remain explicit.
|
|
91
|
+
|
|
92
|
+
## Run the existing complete verifier when available
|
|
93
|
+
|
|
94
|
+
The CLI package does not install Playwright, `tsx`, Chrome, or the repository's verifier and rendering-policy modules. Complete automated verification needs an operator-provided installation of that existing verifier with its dependencies and actual supported Chrome. It also needs an explicitly supplied authenticated Playwright `BrowserContext` or owner-private storage-state file prepared through an approved sign-in flow.
|
|
95
|
+
|
|
96
|
+
For repository operators, run this command from the repository root. The paths in this command are repository capabilities, not paths inside an installed CLI package.
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
node --import tsx .agents/skills/verify-artifacts/scripts/verify-retained.ts \
|
|
100
|
+
--config /private/path/retained-config.json \
|
|
101
|
+
--storage-state /private/path/browser-state.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Pass the prepared config unchanged. The existing verifier validates the full schema and canonical snapshot hash. It checks pinned responses, markers, policies, interactions, ZIP, attachment bytes, viewports, and publication state before and after viewer checks. It writes private evidence and fails on mismatches or concurrent publication changes. Use its callable `verifyRetainedArtifact(context, config)` when an authorized context is supplied. Do not build another viewer or weaken sandbox policies to establish deployed behavior.
|
|
105
|
+
|
|
106
|
+
If this runtime or authenticated input is unavailable, use the partial route above and name that limit. If browser access is unavailable too, report only the persistence checks actually completed.
|
|
107
|
+
|
|
108
|
+
## Deliver the observed outcomes
|
|
109
|
+
|
|
110
|
+
Report each outcome separately, with its evidence and missing checks.
|
|
111
|
+
|
|
112
|
+
| Outcome | Evidence needed |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| Persistence | Full independent explicit-revision read and complete intended-snapshot comparison. Name any exact export check separately. |
|
|
115
|
+
| Local rendering | Observed local document layout and interactions. This does not prove hosted rendering. |
|
|
116
|
+
| Panel rendering | Exact real panel selection, pinned iframe, visible content, and observed interaction. Name missing response, policy, byte, console, or viewport checks. |
|
|
117
|
+
| Protected publication | Separate authorized viewer admission for the selected published revision, visible content, interactions, and attachment checks. Report concurrent changes and unavailable checks. |
|
|
118
|
+
|
|
119
|
+
Use `passed`, `failed`, `unavailable`, or `not requested` for each outcome as the evidence permits. Keep session identifiers and private evidence private. A successful helper invocation establishes only input preparation.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { lstat, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, join, resolve } from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
function requireValue(condition, message) {
|
|
7
|
+
if (!condition) throw new Error(message);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function object(value) {
|
|
11
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function prepareRetainedSelection({ receipt, expected, checks, panelOrigin, evidenceDirectory, publicationReceipt }) {
|
|
15
|
+
requireValue(object(receipt) && object(receipt.revision), 'Write receipt must identify a saved revision');
|
|
16
|
+
requireValue(
|
|
17
|
+
typeof receipt.artifactId === 'string' && receipt.artifactId.length > 0 &&
|
|
18
|
+
typeof receipt.revision.id === 'string' && receipt.revision.id.length > 0 &&
|
|
19
|
+
/^[a-f0-9]{64}$/.test(receipt.revision.sha256),
|
|
20
|
+
'Write receipt must contain artifact, revision, and SHA-256',
|
|
21
|
+
);
|
|
22
|
+
const snapshot = expected?.revision?.snapshot;
|
|
23
|
+
requireValue(
|
|
24
|
+
object(expected) && object(snapshot) && object(snapshot.metadata) &&
|
|
25
|
+
typeof snapshot.metadata.title === 'string' && typeof snapshot.metadata.description === 'string' &&
|
|
26
|
+
object(snapshot.content) && ['html', 'markdown', 'text', 'design'].includes(snapshot.content.kind) &&
|
|
27
|
+
typeof snapshot.content.text === 'string' && Array.isArray(snapshot.files),
|
|
28
|
+
'Independent read must contain the full snapshot, not a compact response',
|
|
29
|
+
);
|
|
30
|
+
requireValue(expected.artifactId === receipt.artifactId && expected.revision.id === receipt.revision.id && expected.revision.sha256 === receipt.revision.sha256, 'Independent read differs from the saved write receipt');
|
|
31
|
+
requireValue(typeof expected.headRevisionId === 'string' && expected.headRevisionId.length > 0, 'Independent read must identify its current draft head');
|
|
32
|
+
requireValue(object(checks) && Object.keys(checks).every((key) => ['viewports', 'expectations', 'timeoutMs'].includes(key)) && Array.isArray(checks.viewports) && checks.viewports.length > 0 && Array.isArray(checks.expectations), 'Checks must contain viewports and expectations, with optional timeoutMs');
|
|
33
|
+
let panel;
|
|
34
|
+
try { panel = new URL(panelOrigin); } catch { throw new Error('Panel origin must be an observed HTTP origin'); }
|
|
35
|
+
requireValue(['http:', 'https:'].includes(panel.protocol) && !panel.username && !panel.password && panel.pathname === '/' && !panel.search && !panel.hash, 'Panel origin must be an observed HTTP origin without a path, credentials, query, or fragment');
|
|
36
|
+
panel.pathname = `/artifacts/${encodeURIComponent(expected.artifactId)}`;
|
|
37
|
+
panel.searchParams.set('revisionId', expected.revision.id);
|
|
38
|
+
let publicationUrl;
|
|
39
|
+
if (publicationReceipt !== undefined) {
|
|
40
|
+
const current = expected.publication;
|
|
41
|
+
requireValue(
|
|
42
|
+
object(publicationReceipt) && object(current) &&
|
|
43
|
+
publicationReceipt.artifactId === expected.artifactId &&
|
|
44
|
+
publicationReceipt.revisionId === expected.revision.id && publicationReceipt.revisionId === current.revisionId &&
|
|
45
|
+
Number.isInteger(publicationReceipt.version) && publicationReceipt.version > 0 && publicationReceipt.version === current.version &&
|
|
46
|
+
typeof publicationReceipt.url === 'string' && publicationReceipt.url === current.url,
|
|
47
|
+
'Publication differs from the requested receipt or selected revision; report the changed or different publication',
|
|
48
|
+
);
|
|
49
|
+
let publication;
|
|
50
|
+
try { publication = new URL(current.url); } catch { throw new Error('Publication receipt must contain a stable HTTP URL'); }
|
|
51
|
+
requireValue(['http:', 'https:'].includes(publication.protocol) && !publication.username && !publication.password && !publication.search && !publication.hash && publication.pathname === `/a/${encodeURIComponent(expected.artifactId)}`, 'Publication receipt must contain the selected artifact stable HTTP URL');
|
|
52
|
+
publicationUrl = publication.href;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
expected,
|
|
56
|
+
panelUrl: panel.href,
|
|
57
|
+
...(publicationUrl === undefined ? {} : { publicationUrl }),
|
|
58
|
+
...checks,
|
|
59
|
+
evidenceDirectory: resolve(evidenceDirectory),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function canvasContracts() {
|
|
64
|
+
const name = '@yargote/artifacts-contracts';
|
|
65
|
+
let path;
|
|
66
|
+
try { path = createRequire(import.meta.url).resolve(name); }
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error.code !== 'MODULE_NOT_FOUND') throw error;
|
|
69
|
+
path = createRequire(new URL('../../../apps/cli/package.json', import.meta.url)).resolve(name);
|
|
70
|
+
}
|
|
71
|
+
return import(pathToFileURL(path).href);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function prepareCanvasDirectory(input) {
|
|
75
|
+
const directory = resolve(input);
|
|
76
|
+
requireValue((await lstat(directory)).isDirectory(), 'Canvas input must be a directory');
|
|
77
|
+
const { canvasIndexSchema, snapshotSchema } = await canvasContracts();
|
|
78
|
+
const indexFile = join(directory, 'canvas.json');
|
|
79
|
+
const indexInfo = await lstat(indexFile);
|
|
80
|
+
requireValue(indexInfo.isFile(), 'canvas.json must be a regular file');
|
|
81
|
+
requireValue(indexInfo.size <= 1_048_576, 'canvas.json exceeds the shared 1 MiB revision budget');
|
|
82
|
+
const indexBytes = await readFile(indexFile);
|
|
83
|
+
const text = indexBytes.toString('utf8');
|
|
84
|
+
requireValue(Buffer.from(text, 'utf8').equals(indexBytes), 'canvas.json must contain valid UTF-8');
|
|
85
|
+
const index = canvasIndexSchema.parse(JSON.parse(text));
|
|
86
|
+
const boardDirectory = join(directory, 'boards');
|
|
87
|
+
requireValue((await lstat(boardDirectory)).isDirectory(), 'boards must be a directory, not a symbolic link');
|
|
88
|
+
const entries = await readdir(boardDirectory, { withFileTypes: true });
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const path = `boards/${entry.name}`;
|
|
91
|
+
requireValue(entry.isFile(), `Canvas board ${path} must be a regular file`);
|
|
92
|
+
requireValue(Object.hasOwn(index.boards, path), `Orphan canvas board ${path} is absent from canvas.json`);
|
|
93
|
+
}
|
|
94
|
+
const files = [];
|
|
95
|
+
for (const path of [...index.order].sort()) {
|
|
96
|
+
const filename = join(directory, path);
|
|
97
|
+
let info;
|
|
98
|
+
try { info = await lstat(filename); }
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error.code === 'ENOENT') throw new Error(`Canvas board ${path} has no matching file`);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
requireValue(info.isFile(), `Canvas board ${path} must be a regular file`);
|
|
104
|
+
requireValue(info.size <= 1_048_576, `Canvas board ${path} exceeds the shared 1 MiB revision budget`);
|
|
105
|
+
const bytes = await readFile(filename);
|
|
106
|
+
const data = bytes.toString('utf8');
|
|
107
|
+
requireValue(Buffer.from(data, 'utf8').equals(bytes), `Canvas board ${path} must contain valid UTF-8`);
|
|
108
|
+
files.push({ path, mediaType: 'text/html', encoding: 'utf8', data });
|
|
109
|
+
}
|
|
110
|
+
return snapshotSchema.parse({
|
|
111
|
+
metadata: { title: basename(directory), description: '' },
|
|
112
|
+
content: { kind: 'design', text },
|
|
113
|
+
files,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const required = ['--receipt', '--read', '--panel-origin', '--checks', '--evidence', '--out'];
|
|
118
|
+
try {
|
|
119
|
+
const args = process.argv.slice(2);
|
|
120
|
+
if (args.length === 1 && !args[0].startsWith('--')) {
|
|
121
|
+
console.log(JSON.stringify(await prepareCanvasDirectory(args[0]), null, 2));
|
|
122
|
+
} else {
|
|
123
|
+
const options = new Map();
|
|
124
|
+
requireValue(args.length % 2 === 0, 'Use named options with one value each');
|
|
125
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
126
|
+
const name = args[index];
|
|
127
|
+
requireValue([...required, '--publication-receipt'].includes(name) && !options.has(name) && typeof args[index + 1] === 'string' && args[index + 1].length > 0, 'Use each supported option once with a nonempty value');
|
|
128
|
+
options.set(name, args[index + 1]);
|
|
129
|
+
}
|
|
130
|
+
requireValue(required.every((name) => options.has(name)), 'Required options are --receipt --read --panel-origin --checks --evidence --out');
|
|
131
|
+
const json = async (name) => {
|
|
132
|
+
try { return JSON.parse(await readFile(options.get(name), 'utf8')); }
|
|
133
|
+
catch { throw new Error(`Cannot read valid JSON for ${name}`); }
|
|
134
|
+
};
|
|
135
|
+
const receipt = await json('--receipt');
|
|
136
|
+
const expected = await json('--read');
|
|
137
|
+
const checks = await json('--checks');
|
|
138
|
+
const publication = options.has('--publication-receipt') ? await json('--publication-receipt') : undefined;
|
|
139
|
+
const config = prepareRetainedSelection({ receipt, expected, checks, panelOrigin: options.get('--panel-origin'), evidenceDirectory: options.get('--evidence'), publicationReceipt: publication });
|
|
140
|
+
try { await writeFile(options.get('--out'), `${JSON.stringify(config, null, 2)}\n`, { flag: 'wx', mode: 0o600 }); }
|
|
141
|
+
catch (error) { throw new Error(error?.code === 'EEXIST' ? 'Config output already exists; choose a new output path' : 'Cannot create the private config output'); }
|
|
142
|
+
const headChanged = expected.headRevisionId !== expected.revision.id;
|
|
143
|
+
console.log(JSON.stringify({ status: 'prepared', headChanged, publication: publication === undefined ? 'not-requested' : 'requested' }));
|
|
144
|
+
if (headChanged) console.error('Current draft head differs from the selected saved revision. The selection remains pinned.');
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(error.message);
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: artifacts-design
|
|
3
|
-
description: Design
|
|
3
|
+
description: Design HTML documents and multi-board design canvases of a retained Artifact with embedded fonts, light and dark Catppuccin tokens, purposeful layout, and rendered checks. Use before writing or restyling HTML or design boards for the artifacts or artifacts-cli skills.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Design HTML artifacts
|
|
@@ -9,6 +9,8 @@ The calling skill owns the snapshot, size limit, and save workflow. This skill o
|
|
|
9
9
|
|
|
10
10
|
The document renders in a sandbox that blocks every network request. Fonts, images, and icons load only as `data:` URIs. External stylesheets, Google Fonts links, and CDN scripts silently fail. Inline all CSS and JavaScript.
|
|
11
11
|
|
|
12
|
+
For multiple boards, read [canvas authoring](references/canvas.md) before writing the index or HTML. It defines the board files, pages, geometry, shared font budget, and complete snapshot workflow.
|
|
13
|
+
|
|
12
14
|
## Choose the composition
|
|
13
15
|
|
|
14
16
|
Before writing HTML, identify the reader's task, the facts and assets available, and what the reader should understand or do first. Use supplied facts. Label illustrative inputs and assumptions, and leave unavailable evidence absent.
|
|
@@ -49,7 +51,7 @@ After the HTML file is complete, embed the fonts it names:
|
|
|
49
51
|
node <skill-directory>/scripts/embed-fonts.mjs page.html
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
For the CLI, `<skill-directory>` is the output of `artifacts skills path artifacts-design`. The script rewrites the file in place and is safe to rerun after edits. It
|
|
54
|
+
For the CLI, `<skill-directory>` is the output of `artifacts skills path artifacts-design`. The script rewrites the file in place and is safe to rerun after edits. It selects families from CSS `font-family` and `font` declarations in style elements and inline styles, including referenced custom properties. Comments, content text, and JavaScript strings do not select fonts. It adds italic faces for CSS `font-style: italic`, italic `font` shorthand, or real `<em>`, `<i>`, and `<cite>` elements. It refuses to write a page above 1 MiB. Three families add about 135 KB. Newsreader adds about 80 to 165 KB. Do not name another family unless you embed its `woff2` yourself, because an unembedded face silently falls back. Without Node, keep the fallback stacks and report that the fonts were not embedded.
|
|
53
55
|
|
|
54
56
|
## Color
|
|
55
57
|
|
|
@@ -77,6 +79,14 @@ Give the `<title>` a specific name of two to four words, or the name the user al
|
|
|
77
79
|
|
|
78
80
|
## Check the rendered page
|
|
79
81
|
|
|
82
|
+
Run the local helper after embedding fonts and before saving:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
node <skill-directory>/scripts/verify-local.mjs page.html --evidence ./page-evidence
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For the CLI, resolve `<skill-directory>` with `artifacts skills path artifacts-design`. Read [local checks](references/local-checks.md) for existing runtime requirements, optional declarative control expectations, browser selection, and report fields. The helper never installs a dependency, changes the HTML, saves, publishes, or requires service credentials. Its report proves **renderizado local** and leaves visual review required. It does not verify the retained revision or authenticated publication.
|
|
89
|
+
|
|
80
90
|
Run the [interface checks](references/interface-checks.md) before saving. They distinguish required checks from visual judgment. Render the finished page at 375 px and 1280 px in both light and dark when browser tools are available. Fix observed failures and repeat the affected checks. Report checks you could not run.
|
|
81
91
|
|
|
82
92
|
The license notices for bundled fonts and adapted guidance are in [third-party notices](THIRD-PARTY-NOTICES.md).
|