@gmickel/gno 1.23.0 → 1.25.1
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 +26 -12
- package/assets/skill/SKILL.md +37 -19
- package/assets/skill/recipes/capture-and-file.md +20 -5
- package/browser-extension/artifacts/gno-browser-clipper-v1.25.1.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.25.1.zip.sha256 +1 -0
- package/browser-extension/dist/PRIVACY.md +55 -0
- package/browser-extension/dist/chunk-vn5f663b.js +50 -0
- package/browser-extension/dist/chunk-ydfx5d7p.css +1 -0
- package/browser-extension/dist/content.js +1 -0
- package/browser-extension/dist/manifest.json +25 -0
- package/browser-extension/dist/preview.html +13 -0
- package/browser-extension/dist/service-worker.js +40 -0
- package/package.json +13 -3
- package/spec/cli.md +141 -0
- package/spec/db/schema.sql +101 -0
- package/spec/mcp.md +10 -0
- package/spec/output-schemas/browser-clip-preview.schema.json +83 -0
- package/spec/output-schemas/browser-clip.schema.json +586 -0
- package/spec/output-schemas/capture-receipt.schema.json +22 -1
- package/spec/output-schemas/clipper-csrf.schema.json +12 -0
- package/spec/output-schemas/clipper-error.schema.json +46 -0
- package/spec/output-schemas/clipper-pair-approval.schema.json +17 -0
- package/spec/output-schemas/clipper-pair-start.schema.json +26 -0
- package/spec/output-schemas/clipper-pair-status.schema.json +46 -0
- package/spec/output-schemas/clipper-revoke.schema.json +28 -0
- package/spec/output-schemas/mcp-capture-result.schema.json +12 -1
- package/spec/output-schemas/setup-activation-result.schema.json +456 -0
- package/spec/output-schemas/setup-command-result.schema.json +93 -0
- package/spec/output-schemas/setup-receipt.schema.json +258 -0
- package/spec/output-schemas/setup-semantic-receipt.schema.json +195 -0
- package/src/cli/commands/completion/scripts.ts +2 -0
- package/src/cli/commands/embed.ts +7 -2
- package/src/cli/commands/setup-activation.ts +324 -0
- package/src/cli/commands/setup-semantic.ts +591 -0
- package/src/cli/commands/setup.ts +410 -0
- package/src/cli/program.ts +64 -0
- package/src/cli/setup-semantic-worker.ts +177 -0
- package/src/core/browser-clip-provenance.ts +139 -0
- package/src/core/browser-clip.ts +473 -0
- package/src/core/capture-write.ts +5 -0
- package/src/core/capture.ts +75 -18
- package/src/core/config-mutation.ts +94 -64
- package/src/core/file-lock.ts +89 -36
- package/src/core/folder-setup-planning.ts +453 -0
- package/src/core/folder-setup.ts +490 -0
- package/src/core/setup-activation.ts +309 -0
- package/src/core/setup-receipt.ts +321 -0
- package/src/serve/capture-service.ts +420 -0
- package/src/serve/clipper-body.ts +62 -0
- package/src/serve/clipper-capture.ts +248 -0
- package/src/serve/clipper-contract.ts +57 -0
- package/src/serve/clipper-idempotency.ts +35 -0
- package/src/serve/clipper-pairing.ts +297 -0
- package/src/serve/clipper-security-errors.ts +23 -0
- package/src/serve/clipper-security.ts +449 -0
- package/src/serve/connectors.ts +29 -2
- package/src/serve/public/app.tsx +8 -1
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/index.html +1 -0
- package/src/serve/public/lib/clipper-approval.ts +206 -0
- package/src/serve/public/pages/ClipperPairing.tsx +210 -0
- package/src/serve/routes/api.ts +19 -115
- package/src/serve/routes/clipper.ts +394 -0
- package/src/serve/server.ts +22 -0
- package/src/store/migrations/020-browser-clipper-security.ts +128 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/sqlite/clipper-store-types.ts +104 -0
- package/src/store/sqlite/clipper-store.ts +496 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmickel/gno",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.1",
|
|
4
4
|
"description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"embeddings",
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"assets",
|
|
30
|
+
"browser-extension/artifacts",
|
|
31
|
+
"browser-extension/dist",
|
|
30
32
|
"bunfig.toml",
|
|
31
33
|
"spec",
|
|
32
34
|
"src",
|
|
@@ -53,6 +55,9 @@
|
|
|
53
55
|
"scripts": {
|
|
54
56
|
"dev": "bun run --hot src/index.ts",
|
|
55
57
|
"build": "bun build src/index.ts",
|
|
58
|
+
"build:clipper": "bun browser-extension/build-cli.ts",
|
|
59
|
+
"package:clipper": "bun browser-extension/package-cli.ts",
|
|
60
|
+
"verify:clipper-package": "bun browser-extension/verify-package.ts",
|
|
56
61
|
"start": "bun run src/index.ts",
|
|
57
62
|
"lint": "oxlint --fix --type-aware --type-check && oxfmt .",
|
|
58
63
|
"lint:check": "oxlint --type-aware --type-check && oxfmt --check .",
|
|
@@ -60,8 +65,10 @@
|
|
|
60
65
|
"test": "bun test",
|
|
61
66
|
"test:web": "bun test test/serve/public --timeout 30000",
|
|
62
67
|
"test:e2e": "bun scripts/web-ui-smoke.ts",
|
|
68
|
+
"test:e2e:clipper": "bun test test/clipper/e2e.test.ts --timeout 240000",
|
|
63
69
|
"test:e2e:install": "bunx playwright install chromium",
|
|
64
70
|
"test:package": "bun scripts/package-smoke.ts",
|
|
71
|
+
"test:package:clipper": "bun scripts/package-smoke-clipper-cli.ts",
|
|
65
72
|
"test:watch": "bun test --watch",
|
|
66
73
|
"test:coverage": "bun test --coverage",
|
|
67
74
|
"test:coverage:html": "bun test --coverage --html",
|
|
@@ -137,10 +144,11 @@
|
|
|
137
144
|
"version:patch": "npm version patch --no-git-tag-version",
|
|
138
145
|
"version:minor": "npm version minor --no-git-tag-version",
|
|
139
146
|
"version:major": "npm version major --no-git-tag-version",
|
|
140
|
-
"prerelease": "bun run lint:check && bun test && bun run docs:verify && bun run test:package",
|
|
147
|
+
"prerelease": "bun run lint:check && bun test && bun run docs:verify && bun run verify:clipper-package && bun run test:package",
|
|
141
148
|
"release:dry-run": "gh workflow run publish.yml -f publish=false",
|
|
142
149
|
"release:trigger": "gh workflow run publish.yml -f publish=true",
|
|
143
|
-
"prepare": "lefthook install"
|
|
150
|
+
"prepare": "lefthook install",
|
|
151
|
+
"prepack": "bun run package:clipper"
|
|
144
152
|
},
|
|
145
153
|
"dependencies": {
|
|
146
154
|
"@codemirror/lang-markdown": "6.5.0",
|
|
@@ -199,6 +207,7 @@
|
|
|
199
207
|
"@vscode/tree-sitter-wasm": "0.3.1",
|
|
200
208
|
"ajv": "8.17.1",
|
|
201
209
|
"ajv-formats": "3.0.1",
|
|
210
|
+
"bun": "1.3.14",
|
|
202
211
|
"docx": "9.5.1",
|
|
203
212
|
"evalite": "1.0.0-beta.16",
|
|
204
213
|
"exceljs": "4.4.0",
|
|
@@ -221,6 +230,7 @@
|
|
|
221
230
|
"bun": ">=1.3.0"
|
|
222
231
|
},
|
|
223
232
|
"trustedDependencies": [
|
|
233
|
+
"bun",
|
|
224
234
|
"node-llama-cpp"
|
|
225
235
|
]
|
|
226
236
|
}
|
package/spec/cli.md
CHANGED
|
@@ -64,6 +64,7 @@ equivalent files fail closed as ambiguous.
|
|
|
64
64
|
| ------------------ | ------ | ------- | ----- | ---- | ----- | -------- |
|
|
65
65
|
| status | yes | no | no | yes | no | terminal |
|
|
66
66
|
| init | no | no | no | no | no | terminal |
|
|
67
|
+
| setup | yes | no | no | no | no | terminal |
|
|
67
68
|
| collection add | no | no | no | no | no | terminal |
|
|
68
69
|
| collection list | yes | no | no | yes | no | terminal |
|
|
69
70
|
| collection remove | no | no | no | no | no | terminal |
|
|
@@ -353,6 +354,96 @@ projected wiki/markdown edges and surface in graph traversal/diagnose metadata.
|
|
|
353
354
|
|
|
354
355
|
---
|
|
355
356
|
|
|
357
|
+
### gno setup
|
|
358
|
+
|
|
359
|
+
Add or reuse one folder collection, build its lexical index, and prove a real
|
|
360
|
+
corpus-derived retrieval before reporting success. Semantic indexing is handed
|
|
361
|
+
to one standalone background worker and never delays lexical success.
|
|
362
|
+
|
|
363
|
+
**Synopsis:**
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
gno setup <folder> [-n|--name <name>] [--exclude <pattern>]...
|
|
367
|
+
[--authorize-secret-risk] [--connector <id>]...
|
|
368
|
+
[--no-semantic] [--json]
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Options:**
|
|
372
|
+
|
|
373
|
+
| Option | Type | Default | Description |
|
|
374
|
+
| ------------------------- | ---------- | ------------- | ---------------------------------------------------------------------------- |
|
|
375
|
+
| `-n, --name <name>` | string | dirname | Requested collection name; exact-root reruns reuse the configured collection |
|
|
376
|
+
| `--exclude <pattern>` | repeatable | core defaults | One literal exclusion per occurrence; never CSV |
|
|
377
|
+
| `--authorize-secret-risk` | boolean | false | Explicitly authorize likely credentials, private keys, or env files |
|
|
378
|
+
| `--connector <id>` | repeatable | none | Install or reuse and verify one supported connector after lexical proof |
|
|
379
|
+
| `--no-semantic` | boolean | false | Prove lexical retrieval but record semantic work as skipped |
|
|
380
|
+
| `--json` | boolean | false | Emit one closed setup result object |
|
|
381
|
+
|
|
382
|
+
Omitting `--exclude` preserves the core create defaults or the filters already
|
|
383
|
+
configured for an exact-root rerun. An empty occurrence is invalid. Global
|
|
384
|
+
`--yes` accepts safe defaults only; it never authorizes secret risk. Only an
|
|
385
|
+
interactive terminal may ask one default-No question after the lexical core
|
|
386
|
+
returns `secret_risk`. JSON, `--yes`, non-TTY, decline, and EOF fail closed.
|
|
387
|
+
|
|
388
|
+
The command bootstraps missing config/data/database state without adding the
|
|
389
|
+
folder through `init`, then runs the folder setup transaction. Success requires
|
|
390
|
+
a completed `FolderSetupReceipt@1.0`, `activation.ready=true`, and a non-empty
|
|
391
|
+
exact `activation.evidence.resultUri`. Terminal stage progress uses stderr;
|
|
392
|
+
`--quiet` suppresses progress but not the final result. JSON writes exactly one
|
|
393
|
+
canonical result to stdout on both success and domain failure, with no progress.
|
|
394
|
+
|
|
395
|
+
After lexical proof, the command records one private atomic
|
|
396
|
+
`setup-semantic@1.0` receipt per canonical index/folder and starts one detached,
|
|
397
|
+
collection-scoped Bun worker. A matching live worker is reused; a dead worker is
|
|
398
|
+
replaced; and a live worker with an older identity remains authoritative until
|
|
399
|
+
it exits so its completion receipt cannot be stranded. A later setup rerun may
|
|
400
|
+
then schedule the new identity. The worker uses the normal model
|
|
401
|
+
download/offline policy, records `completed` only when embedding and vector
|
|
402
|
+
synchronization finish without errors, and exits. Spawn, download, partial
|
|
403
|
+
embedding, and vector-sync failures retain an exact foreground
|
|
404
|
+
`gno ... embed <collection>` remediation and never change proven lexical exit 0.
|
|
405
|
+
Direct setup never contacts a resident, MCP, or Web runtime.
|
|
406
|
+
|
|
407
|
+
`--connector` is explicit and repeatable; omission performs no connector
|
|
408
|
+
inspection, installation, or verification. Supported IDs are
|
|
409
|
+
`claude-code-skill`, `claude-desktop-mcp`, `cursor-mcp`, `codex-skill`,
|
|
410
|
+
`opencode-skill`, `openclaw-skill`, and `hermes-skill`. Exact duplicates dedupe
|
|
411
|
+
in first-seen order. Unknown IDs fail before setup side effects.
|
|
412
|
+
Any connector-mode JSON invocation emits `setup-activation-result@1.0`.
|
|
413
|
+
Argument or lexical failure uses outer `status: failed`, nests the unchanged
|
|
414
|
+
failed setup result, and keeps `connectors: []`; it retains the original setup
|
|
415
|
+
exit code and performs no connector action.
|
|
416
|
+
|
|
417
|
+
After proven lexical success and after the lexical store closes, the direct CLI
|
|
418
|
+
opens a new standalone store for connector composition. Missing targets use the
|
|
419
|
+
existing read-only installer; existing entries are reused without overwrite.
|
|
420
|
+
Malformed or unreadable entries are preserved. MCP targets run the shipped
|
|
421
|
+
bounded read-only activation smoke. Skill targets install but return
|
|
422
|
+
`skipped/target_runtime_unverifiable` because they expose no safe runtime hook.
|
|
423
|
+
Connector failures or skips do not roll back lexical success: the command exits
|
|
424
|
+
0 with `completed_with_actions` and bounded per-target remediation. No raw child
|
|
425
|
+
output or client config path is serialized. Reruns delegate passed-receipt reuse
|
|
426
|
+
and recoverable retry to the shipped activation verifier; setup introduces no
|
|
427
|
+
second connector fingerprint or cache.
|
|
428
|
+
|
|
429
|
+
**Structured schemas:**
|
|
430
|
+
|
|
431
|
+
- [`setup-command-result@1.0`](./output-schemas/setup-command-result.schema.json)
|
|
432
|
+
- [`setup-activation-result@1.0`](./output-schemas/setup-activation-result.schema.json)
|
|
433
|
+
- [`setup-semantic@1.0`](./output-schemas/setup-semantic-receipt.schema.json)
|
|
434
|
+
- [`FolderSetupReceipt@1.0`](./output-schemas/setup-receipt.schema.json)
|
|
435
|
+
|
|
436
|
+
**Exit Codes:**
|
|
437
|
+
|
|
438
|
+
- 0: Lexical setup completed with a real exact result URI; semantic state may
|
|
439
|
+
be scheduled, running, pending, completed, or skipped; requested connector
|
|
440
|
+
actions may require bounded follow-up
|
|
441
|
+
- 1: Invalid/safe-input rejection, collection/filter/index disagreement,
|
|
442
|
+
secret-risk refusal, or declined confirmation
|
|
443
|
+
- 2: Config, receipt, IO, store, indexing, proof, or internal invariant failure
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
356
447
|
### gno collection add
|
|
357
448
|
|
|
358
449
|
Add a new collection to the index.
|
|
@@ -1076,6 +1167,24 @@ Capture writes structured `source:` frontmatter and returns the shared
|
|
|
1076
1167
|
[`capture-receipt`](./output-schemas/capture-receipt.schema.json). `--source-date`
|
|
1077
1168
|
maps to `source.observedAt`; `--source-id` maps to `source.externalId`.
|
|
1078
1169
|
|
|
1170
|
+
Browser-clip receipts MAY extend `source` with `canonicalUrl`, `site`,
|
|
1171
|
+
`publishedAt`, and the closed
|
|
1172
|
+
[`browser-clip`](./output-schemas/browser-clip.schema.json) provenance object.
|
|
1173
|
+
The provenance preserves exact selection text separately from canonical final
|
|
1174
|
+
Markdown and carries extraction/final hashes, deterministic identity and
|
|
1175
|
+
preview digests, normalized dates/URLs, browser metadata, server capture time,
|
|
1176
|
+
and bounded warnings. Existing capture inputs and receipts remain compatible.
|
|
1177
|
+
Browser-clip URLs use a closed, credential-free HTTP(S) subset: ASCII DNS or
|
|
1178
|
+
punycode hosts (or IPv4), optional ports from 1 through 65535, and ASCII
|
|
1179
|
+
RFC 3986 path/query/fragment characters with well-formed percent escapes. Raw
|
|
1180
|
+
Unicode hosts and IPv6 literals are rejected; browser clients should submit
|
|
1181
|
+
their serialized IDNA/punycode URL. Free-form clip strings reject C0/C1 control
|
|
1182
|
+
characters except tab, LF, and CR.
|
|
1183
|
+
For browser provenance, `open_existing` MUST open only when the stored
|
|
1184
|
+
`clipIdentity` matches; missing or different provenance MUST return
|
|
1185
|
+
`collisionPolicyResult: "conflict"` without writing. `create_with_suffix`
|
|
1186
|
+
creates a distinct note.
|
|
1187
|
+
|
|
1079
1188
|
**Path and Collision Rules:**
|
|
1080
1189
|
|
|
1081
1190
|
- Explicit `--path` wins.
|
|
@@ -2861,6 +2970,38 @@ is blocked.
|
|
|
2861
2970
|
- Mounts stateful Streamable HTTP MCP at `/mcp` only after the fail-closed
|
|
2862
2971
|
actual-peer, Host, Origin, bearer, body, rate, request, queue, and session
|
|
2863
2972
|
boundary initializes
|
|
2973
|
+
- On the loopback `gno serve` listener only, mounts the browser-clipper pairing,
|
|
2974
|
+
preview, and capture routes. These require exact extension/same-origin
|
|
2975
|
+
policies and dedicated origin-bound capture grants; MCP bearer tokens,
|
|
2976
|
+
`GNO_API_TOKEN`, and `gateway.enableWrite` do not authorize them. Clipper
|
|
2977
|
+
routes are structurally absent from non-loopback listeners. Capture
|
|
2978
|
+
idempotency persists exact content-free plan hashes and reconciles an
|
|
2979
|
+
interrupted atomic write only at its original path; changed plans fail
|
|
2980
|
+
closed without a duplicate write.
|
|
2981
|
+
- Serves the standalone `/clipper/pair` approval page. Its exact pair-ID
|
|
2982
|
+
fragment is synchronously validated and scrubbed before normal workspace
|
|
2983
|
+
state initializes; the user enters the eight-digit code manually. The page
|
|
2984
|
+
obtains same-origin CSRF state, never receives the extension grant, and does
|
|
2985
|
+
not persist pairing material.
|
|
2986
|
+
- Browser-clipper HTTP write receipts add `schemaVersion: "1.0"` to the closed
|
|
2987
|
+
shared capture receipt. HTTP 409 can therefore be either a valid provenance
|
|
2988
|
+
conflict receipt or a closed clipper error; clients parse the body contract
|
|
2989
|
+
before classifying the outcome.
|
|
2990
|
+
- Browser clip response/status closure is exact: HTTP 200 only for
|
|
2991
|
+
`opened_existing`; HTTP 202 only for `created`, `created_with_suffix`, or
|
|
2992
|
+
`overwritten`; HTTP 409 for either a valid `conflict` receipt or the matching
|
|
2993
|
+
closed `clipper-error@1.0`. Unknown versions, fields, codes, non-JSON bodies,
|
|
2994
|
+
and status/body mismatches fail as invalid responses. `CLIPPER_OFFLINE`,
|
|
2995
|
+
`CLIPPER_INVALID_RESPONSE`, and `CLIPPER_CLIENT` are client-only
|
|
2996
|
+
classifications.
|
|
2997
|
+
- Browser clip provenance contains exactly `extractionHash`, `finalBodyHash`,
|
|
2998
|
+
`clipIdentity`, and `previewDigest`; it does not contain `sourceHash`.
|
|
2999
|
+
- The Chromium service worker stores the loopback origin, plaintext bounded
|
|
3000
|
+
grant, and at most one `{payload, previewDigest, idempotencyKey}` in protected
|
|
3001
|
+
local extension storage. Unfinished pairing state uses extension session
|
|
3002
|
+
storage. Page extraction and the approval page receive neither grant nor
|
|
3003
|
+
pending-write state. Recovery reuses the same logical write or fails closed
|
|
3004
|
+
without changing destination.
|
|
2864
3005
|
- On `--detach`: forks a detached child with stdio redirected to `--log-file`, writes pid-file JSON (`{pid, port, cmd:"serve", version, started_at}`), prints `{pid, url}` on stdout, exits 0
|
|
2865
3006
|
- On `--status`: output matches the [process-status schema](./output-schemas/process-status.schema.json). Liveness via `process.kill(pid, 0)`; stale pid-files (ESRCH) are reported as `running:false`. Live status best-effort reads the same redacted `resident-status@1.0` snapshot from the recorded listener.
|
|
2866
3007
|
- On `--stop`: sends SIGTERM, polls every 100ms for up to 10s, falls back to SIGKILL, polls 2s more, unlinks pid-file if the process cleaned up after itself
|
package/spec/db/schema.sql
CHANGED
|
@@ -610,3 +610,104 @@ CREATE TABLE IF NOT EXISTS saved_capsule_reverification_state (
|
|
|
610
610
|
registration_epoch INTEGER NOT NULL DEFAULT 0
|
|
611
611
|
CHECK (registration_epoch >= 0)
|
|
612
612
|
);
|
|
613
|
+
|
|
614
|
+
-- ─────────────────────────────────────────────────────────────────────────────
|
|
615
|
+
-- Browser clipper security state
|
|
616
|
+
-- ─────────────────────────────────────────────────────────────────────────────
|
|
617
|
+
|
|
618
|
+
CREATE TABLE IF NOT EXISTS clipper_grants (
|
|
619
|
+
id TEXT PRIMARY KEY,
|
|
620
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
621
|
+
origin TEXT NOT NULL,
|
|
622
|
+
scope TEXT NOT NULL DEFAULT 'capture'
|
|
623
|
+
CHECK (scope = 'capture'),
|
|
624
|
+
created_at_ms INTEGER NOT NULL CHECK (created_at_ms >= 0),
|
|
625
|
+
expires_at_ms INTEGER NOT NULL CHECK (expires_at_ms > created_at_ms),
|
|
626
|
+
revoked_at_ms INTEGER
|
|
627
|
+
CHECK (revoked_at_ms IS NULL OR revoked_at_ms >= created_at_ms),
|
|
628
|
+
CHECK (length(id) BETWEEN 1 AND 128),
|
|
629
|
+
CHECK (
|
|
630
|
+
length(token_hash) = 64
|
|
631
|
+
AND token_hash NOT GLOB '*[^0-9a-f]*'
|
|
632
|
+
),
|
|
633
|
+
CHECK (
|
|
634
|
+
length(origin) = 51
|
|
635
|
+
AND substr(origin, 1, 19) = 'chrome-extension://'
|
|
636
|
+
AND substr(origin, 20) NOT GLOB '*[^a-p]*'
|
|
637
|
+
)
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
CREATE INDEX IF NOT EXISTS idx_clipper_grants_expiry
|
|
641
|
+
ON clipper_grants(expires_at_ms, id);
|
|
642
|
+
|
|
643
|
+
CREATE TABLE IF NOT EXISTS clipper_capture_idempotency (
|
|
644
|
+
grant_id TEXT NOT NULL,
|
|
645
|
+
key_hash TEXT NOT NULL,
|
|
646
|
+
request_digest TEXT NOT NULL,
|
|
647
|
+
collection TEXT NOT NULL,
|
|
648
|
+
rel_path TEXT NOT NULL,
|
|
649
|
+
collision_policy_result TEXT NOT NULL
|
|
650
|
+
CHECK (
|
|
651
|
+
collision_policy_result IN (
|
|
652
|
+
'created',
|
|
653
|
+
'opened_existing',
|
|
654
|
+
'created_with_suffix',
|
|
655
|
+
'overwritten',
|
|
656
|
+
'conflict'
|
|
657
|
+
)
|
|
658
|
+
),
|
|
659
|
+
content_hash TEXT NOT NULL,
|
|
660
|
+
clip_identity TEXT NOT NULL,
|
|
661
|
+
state TEXT NOT NULL CHECK (state IN ('pending', 'completed')),
|
|
662
|
+
status_code INTEGER,
|
|
663
|
+
response_json TEXT,
|
|
664
|
+
created_at_ms INTEGER NOT NULL CHECK (created_at_ms >= 0),
|
|
665
|
+
completed_at_ms INTEGER
|
|
666
|
+
CHECK (completed_at_ms IS NULL OR completed_at_ms >= created_at_ms),
|
|
667
|
+
PRIMARY KEY (grant_id, key_hash),
|
|
668
|
+
FOREIGN KEY (grant_id)
|
|
669
|
+
REFERENCES clipper_grants(id)
|
|
670
|
+
ON DELETE CASCADE,
|
|
671
|
+
CHECK (
|
|
672
|
+
length(key_hash) = 64
|
|
673
|
+
AND key_hash NOT GLOB '*[^0-9a-f]*'
|
|
674
|
+
),
|
|
675
|
+
UNIQUE (grant_id, request_digest),
|
|
676
|
+
CHECK (
|
|
677
|
+
length(request_digest) = 64
|
|
678
|
+
AND request_digest NOT GLOB '*[^0-9a-f]*'
|
|
679
|
+
),
|
|
680
|
+
CHECK (length(CAST(collection AS BLOB)) BETWEEN 1 AND 256),
|
|
681
|
+
CHECK (length(CAST(rel_path AS BLOB)) BETWEEN 1 AND 8192),
|
|
682
|
+
CHECK (
|
|
683
|
+
length(content_hash) = 64
|
|
684
|
+
AND content_hash NOT GLOB '*[^0-9a-f]*'
|
|
685
|
+
),
|
|
686
|
+
CHECK (
|
|
687
|
+
length(clip_identity) = 64
|
|
688
|
+
AND clip_identity NOT GLOB '*[^0-9a-f]*'
|
|
689
|
+
),
|
|
690
|
+
CHECK (
|
|
691
|
+
response_json IS NULL
|
|
692
|
+
OR length(CAST(response_json AS BLOB)) <= 2097152
|
|
693
|
+
),
|
|
694
|
+
CHECK (
|
|
695
|
+
(
|
|
696
|
+
state = 'pending'
|
|
697
|
+
AND status_code IS NULL
|
|
698
|
+
AND response_json IS NULL
|
|
699
|
+
AND completed_at_ms IS NULL
|
|
700
|
+
)
|
|
701
|
+
OR
|
|
702
|
+
(
|
|
703
|
+
state = 'completed'
|
|
704
|
+
AND status_code BETWEEN 200 AND 599
|
|
705
|
+
AND response_json IS NOT NULL
|
|
706
|
+
AND json_valid(response_json)
|
|
707
|
+
AND completed_at_ms IS NOT NULL
|
|
708
|
+
)
|
|
709
|
+
)
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
CREATE INDEX IF NOT EXISTS idx_clipper_idempotency_created
|
|
713
|
+
ON clipper_capture_idempotency(created_at_ms, grant_id, key_hash);
|
package/spec/mcp.md
CHANGED
|
@@ -1101,6 +1101,16 @@ Create a new document in a collection (write-enabled).
|
|
|
1101
1101
|
- Capture writes structured `source:` frontmatter with `kind`, `capturedAt`,
|
|
1102
1102
|
and optional `url`, `uri`, `docid`, `mime`, `ext`, `author`, `observedAt`,
|
|
1103
1103
|
`externalId`, and `title`
|
|
1104
|
+
- Browser-clip receipts MAY additionally carry normalized `canonicalUrl`,
|
|
1105
|
+
`site`, `publishedAt`, and closed `browserClip` provenance as defined by
|
|
1106
|
+
`browser-clip.schema.json`; existing `gno_capture` inputs remain compatible
|
|
1107
|
+
- Browser provenance preserves exact selection separately from canonical final
|
|
1108
|
+
Markdown and includes extraction/final hashes, deterministic clip/preview
|
|
1109
|
+
digests, normalized dates/URLs, browser metadata, server capture time, and
|
|
1110
|
+
bounded warnings
|
|
1111
|
+
- Browser `open_existing` MUST require a matching stored `clipIdentity`;
|
|
1112
|
+
missing or different provenance returns `collisionPolicyResult: "conflict"`
|
|
1113
|
+
without writing, while `create_with_suffix` creates a distinct note
|
|
1104
1114
|
- Tags are validated and normalized to lowercase
|
|
1105
1115
|
- For Markdown files, tags are added to frontmatter
|
|
1106
1116
|
- For non-Markdown files, tags are stored as user-source in the database
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "gno://schemas/browser-clip-preview@1.0",
|
|
4
|
+
"title": "Browser Clip Preview",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "preview", "provenance", "plan"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": { "const": "1.0" },
|
|
10
|
+
"preview": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"required": ["body", "digest", "source", "destination", "tags"],
|
|
14
|
+
"properties": {
|
|
15
|
+
"body": { "type": "string" },
|
|
16
|
+
"digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
17
|
+
"source": { "$ref": "#/definitions/source" },
|
|
18
|
+
"destination": {
|
|
19
|
+
"$ref": "gno://schemas/browser-clip@1.0#/properties/destination"
|
|
20
|
+
},
|
|
21
|
+
"tags": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"items": { "type": "string" }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"provenance": {
|
|
28
|
+
"$ref": "gno://schemas/browser-clip@1.0#/definitions/provenance"
|
|
29
|
+
},
|
|
30
|
+
"plan": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"required": ["collection", "relPath", "outcome", "provenanceConflict"],
|
|
34
|
+
"properties": {
|
|
35
|
+
"collection": { "type": "string", "minLength": 1 },
|
|
36
|
+
"relPath": { "type": "string", "minLength": 1 },
|
|
37
|
+
"outcome": {
|
|
38
|
+
"enum": [
|
|
39
|
+
"created",
|
|
40
|
+
"opened_existing",
|
|
41
|
+
"created_with_suffix",
|
|
42
|
+
"overwritten",
|
|
43
|
+
"conflict"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"provenanceConflict": { "type": "boolean" }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"definitions": {
|
|
51
|
+
"source": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"required": [
|
|
55
|
+
"kind",
|
|
56
|
+
"title",
|
|
57
|
+
"url",
|
|
58
|
+
"observedAt",
|
|
59
|
+
"capturedAt",
|
|
60
|
+
"browserClip"
|
|
61
|
+
],
|
|
62
|
+
"properties": {
|
|
63
|
+
"kind": { "const": "web" },
|
|
64
|
+
"title": { "type": "string" },
|
|
65
|
+
"url": { "type": "string", "format": "uri" },
|
|
66
|
+
"author": { "type": "string" },
|
|
67
|
+
"observedAt": { "type": "string", "format": "date-time" },
|
|
68
|
+
"capturedAt": { "type": "string", "format": "date-time" },
|
|
69
|
+
"canonicalUrl": { "type": "string", "format": "uri" },
|
|
70
|
+
"site": { "type": "string" },
|
|
71
|
+
"publishedAt": {
|
|
72
|
+
"oneOf": [
|
|
73
|
+
{ "type": "string", "format": "date" },
|
|
74
|
+
{ "type": "string", "format": "date-time" }
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"browserClip": {
|
|
78
|
+
"$ref": "gno://schemas/browser-clip@1.0#/definitions/provenance"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|