@gmickel/gno 1.24.0 → 1.26.0
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 +18 -4
- package/assets/skill/SKILL.md +40 -17
- package/assets/skill/recipes/capture-and-file.md +20 -5
- package/browser-extension/artifacts/gno-browser-clipper-v1.26.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.26.0.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 +196 -4
- package/spec/db/schema.sql +102 -1
- 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/project-profile-apply.schema.json +209 -0
- package/spec/output-schemas/project-profile-command.schema.json +160 -0
- package/spec/output-schemas/query-diagnose.schema.json +7 -1
- package/spec/output-schemas/setup-profile-result.schema.json +87 -0
- package/spec/project-profile.schema.json +301 -0
- package/src/cli/commands/collection/add.ts +39 -45
- package/src/cli/commands/collection/remove.ts +28 -28
- package/src/cli/commands/collection/rename.ts +55 -73
- package/src/cli/commands/context/add.ts +37 -26
- package/src/cli/commands/context/rm.ts +47 -20
- package/src/cli/commands/init.ts +55 -125
- package/src/cli/commands/models/use.ts +43 -38
- package/src/cli/commands/profile-apply.ts +334 -0
- package/src/cli/commands/profile.ts +409 -0
- package/src/cli/commands/setup-activation.ts +205 -54
- package/src/cli/commands/setup-profile.ts +223 -0
- package/src/cli/commands/setup.ts +3 -0
- package/src/cli/program.ts +110 -9
- package/src/config/index.ts +3 -0
- package/src/config/project-profile.ts +367 -0
- package/src/config/saver.ts +16 -7
- package/src/config/types.ts +42 -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 +138 -76
- package/src/core/config-write-lock.ts +89 -0
- package/src/core/context-identity.ts +16 -0
- package/src/core/context-resolver.ts +2 -12
- package/src/core/file-lock.ts +20 -6
- package/src/core/folder-setup-planning.ts +6 -21
- package/src/core/folder-setup.ts +30 -2
- package/src/core/path-rules.ts +53 -0
- package/src/core/project-affinity-surface.ts +102 -7
- package/src/core/project-profile-apply-state.ts +268 -0
- package/src/core/project-profile-apply-validation.ts +95 -0
- package/src/core/project-profile-apply.ts +408 -0
- package/src/core/project-profile-canonical.ts +71 -0
- package/src/core/project-profile-diff.ts +302 -0
- package/src/core/project-profile-discovery.ts +519 -0
- package/src/core/project-profile-file.ts +37 -0
- package/src/core/project-profile-parser.ts +98 -0
- package/src/core/project-profile.ts +490 -0
- package/src/ingestion/walker.ts +85 -44
- package/src/llm/cache.ts +21 -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/config-sync.ts +2 -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/resident-runtime.ts +1 -0
- package/src/serve/routes/api.ts +20 -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/021-multi-context-identity.ts +37 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +83 -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.26.0",
|
|
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
|
@@ -65,6 +65,10 @@ equivalent files fail closed as ambiguous.
|
|
|
65
65
|
| status | yes | no | no | yes | no | terminal |
|
|
66
66
|
| init | no | no | no | no | no | terminal |
|
|
67
67
|
| setup | yes | no | no | no | no | terminal |
|
|
68
|
+
| profile check | yes | no | no | no | no | terminal |
|
|
69
|
+
| profile show | yes | no | no | no | no | terminal |
|
|
70
|
+
| profile diff | yes | no | no | no | no | terminal |
|
|
71
|
+
| profile apply | yes | no | no | no | no | terminal |
|
|
68
72
|
| collection add | no | no | no | no | no | terminal |
|
|
69
73
|
| collection list | yes | no | no | yes | no | terminal |
|
|
70
74
|
| collection remove | no | no | no | no | no | terminal |
|
|
@@ -125,6 +129,89 @@ equivalent files fail closed as ambiguous.
|
|
|
125
129
|
|
|
126
130
|
## Commands
|
|
127
131
|
|
|
132
|
+
### gno profile
|
|
133
|
+
|
|
134
|
+
Inspect a repository-owned `.gno/index.yml` retrieval profile without changing
|
|
135
|
+
the user config, index database, model cache, or tracked files.
|
|
136
|
+
|
|
137
|
+
**Synopsis:**
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
gno profile check [path] [--json]
|
|
141
|
+
gno profile show [path] [--json]
|
|
142
|
+
gno profile diff [path] [--json]
|
|
143
|
+
gno profile apply [path] [--json]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
With no path, discovery starts at the canonical current directory and walks
|
|
147
|
+
upward. Each directory is checked before its parent, so the nearest nested
|
|
148
|
+
profile wins and any ancestor profile is reported as shadowed; profiles are
|
|
149
|
+
never merged. Discovery stops after checking the first Git root (`.git` may be a
|
|
150
|
+
directory or a worktree file), before crossing a filesystem device boundary,
|
|
151
|
+
or at the filesystem root. This gives a nested repository precedence over its
|
|
152
|
+
parent repository and lets a monorepo subtree intentionally shadow the root
|
|
153
|
+
profile.
|
|
154
|
+
|
|
155
|
+
An explicit directory path is an exact profile-root override: only
|
|
156
|
+
`<path>/.gno/index.yml` is considered. An explicit
|
|
157
|
+
`<path>/.gno/index.yml` file selects that exact profile. Explicit overrides do
|
|
158
|
+
not fall back to ancestors. The profile file and its `.gno` path must resolve
|
|
159
|
+
inside the selected canonical profile root; symlink escapes fail closed.
|
|
160
|
+
Remote callers cannot enable discovery or cause filesystem probes.
|
|
161
|
+
|
|
162
|
+
`check` validates discovery, schema, referenced paths, local preset aliases,
|
|
163
|
+
and, with global `--offline`, exact cache availability without downloading.
|
|
164
|
+
`show` returns the same receipt plus normalized portable desired state. `diff`
|
|
165
|
+
compares that desired state with the selected user config. It reports stale
|
|
166
|
+
same-name/path mappings and explicit repair/removal choices but never applies
|
|
167
|
+
either choice. A missing user config is treated as empty desired-state input;
|
|
168
|
+
an unreadable or invalid config is an actionable validation diagnostic.
|
|
169
|
+
|
|
170
|
+
The three read-only JSON forms use
|
|
171
|
+
[`project-profile-command@1.0`](./output-schemas/project-profile-command.schema.json).
|
|
172
|
+
Receipts contain no absolute profile, config, database, cache, or model paths,
|
|
173
|
+
no timestamps, and no model URIs. Diagnostics and changes are canonically
|
|
174
|
+
ordered, so identical local state produces byte-identical JSON.
|
|
175
|
+
|
|
176
|
+
`apply` rebuilds the shared diff against config reloaded inside a cross-process
|
|
177
|
+
runtime lock, then creates or updates only resources declared by the profile
|
|
178
|
+
through a resumable operation. It can initialize a missing user config. Omitted collections,
|
|
179
|
+
collection-scoped contexts, and content-type rules remain untouched; stale
|
|
180
|
+
same-path collections are reported as skipped and retained. A stale same-name
|
|
181
|
+
collection root is repaired in place without deleting its collection or index
|
|
182
|
+
identity. Apply also updates a timestamp-free `projectProfileBindings` record
|
|
183
|
+
in the local user config with the canonical absolute profile path, profile
|
|
184
|
+
fingerprint, and projected collection. The public `profile_binding` resource
|
|
185
|
+
receipt identifies only the collection and never exposes that local path.
|
|
186
|
+
Model preset aliases resolve to collection-local model overrides.
|
|
187
|
+
Apply synchronizes the config projection but does not index documents; changed
|
|
188
|
+
collections appear in `pendingIndexing`.
|
|
189
|
+
`affinityDefaults` remains profile-scoped and is compiled at trusted local
|
|
190
|
+
retrieval time; apply does not overwrite the user config `projectAffinity`
|
|
191
|
+
default. Its apply resource is therefore `project_affinity/profile/skipped`,
|
|
192
|
+
while the nearest valid profile can still supply request-local affinity.
|
|
193
|
+
|
|
194
|
+
The deterministic JSON result uses
|
|
195
|
+
[`project-profile-apply@1.0`](./output-schemas/project-profile-apply.schema.json).
|
|
196
|
+
Its created/reused/updated/skipped resource receipt is also atomically saved as
|
|
197
|
+
`project-profiles/apply-receipt.json` under the user data directory. The config,
|
|
198
|
+
index, receipt, and lock paths must all remain outside the selected profile
|
|
199
|
+
root. The tracked `.gno/index.yml` is never written. Interrupted or concurrent
|
|
200
|
+
applies resume from fresh config state and converge idempotently.
|
|
201
|
+
|
|
202
|
+
**Exit Codes:**
|
|
203
|
+
|
|
204
|
+
- `0`: profile found and valid (`diff` may still report changes)
|
|
205
|
+
- `1`: profile missing, ambiguous discovery could not be resolved safely, or
|
|
206
|
+
profile/config validation failed, including runtime-path overlap
|
|
207
|
+
- `2`: unexpected local I/O, lock, receipt, or index-store failure
|
|
208
|
+
|
|
209
|
+
`gno setup` calls the same local `check` composition before its folder
|
|
210
|
+
transaction. Inspection is read-only and non-fatal. Plain setup prints bounded
|
|
211
|
+
preview/apply guidance but preserves the existing setup result contract.
|
|
212
|
+
Explicit `--apply-profile` runs the same lock-safe apply path before lexical
|
|
213
|
+
setup and emits `setup-profile-result@1.0`.
|
|
214
|
+
|
|
128
215
|
### gno status
|
|
129
216
|
|
|
130
217
|
Display index status and health information.
|
|
@@ -365,7 +452,7 @@ to one standalone background worker and never delays lexical success.
|
|
|
365
452
|
```bash
|
|
366
453
|
gno setup <folder> [-n|--name <name>] [--exclude <pattern>]...
|
|
367
454
|
[--authorize-secret-risk] [--connector <id>]...
|
|
368
|
-
[--no-semantic] [--json]
|
|
455
|
+
[--apply-profile] [--no-semantic] [--json]
|
|
369
456
|
```
|
|
370
457
|
|
|
371
458
|
**Options:**
|
|
@@ -376,6 +463,7 @@ gno setup <folder> [-n|--name <name>] [--exclude <pattern>]...
|
|
|
376
463
|
| `--exclude <pattern>` | repeatable | core defaults | One literal exclusion per occurrence; never CSV |
|
|
377
464
|
| `--authorize-secret-risk` | boolean | false | Explicitly authorize likely credentials, private keys, or env files |
|
|
378
465
|
| `--connector <id>` | repeatable | none | Install or reuse and verify one supported connector after lexical proof |
|
|
466
|
+
| `--apply-profile` | boolean | false | Apply a valid discovered project profile before lexical setup |
|
|
379
467
|
| `--no-semantic` | boolean | false | Prove lexical retrieval but record semantic work as skipped |
|
|
380
468
|
| `--json` | boolean | false | Emit one closed setup result object |
|
|
381
469
|
|
|
@@ -392,6 +480,53 @@ exact `activation.evidence.resultUri`. Terminal stage progress uses stderr;
|
|
|
392
480
|
`--quiet` suppresses progress but not the final result. JSON writes exactly one
|
|
393
481
|
canonical result to stdout on both success and domain failure, with no progress.
|
|
394
482
|
|
|
483
|
+
Before the folder transaction, setup performs the same local read-only
|
|
484
|
+
`gno profile check` composition from the supplied folder. Missing and invalid
|
|
485
|
+
profiles remain optional and never block ordinary setup. Terminal output shows
|
|
486
|
+
a valid profile fingerprint plus `profile diff`/`--apply-profile` guidance, or
|
|
487
|
+
an invalid-profile diagnostic, before setup mutates local state.
|
|
488
|
+
|
|
489
|
+
`--apply-profile` is explicit. When the check is valid, setup runs the existing
|
|
490
|
+
cross-process lock-safe, create/update-only `profile apply` path first, then
|
|
491
|
+
uses the applied profile collection's canonical root, name, and filters for
|
|
492
|
+
lexical setup. A nested folder invocation therefore indexes the declared
|
|
493
|
+
profile root rather than creating a duplicate subdirectory collection. The
|
|
494
|
+
profile never deletes omitted resources or overwrites the user-level
|
|
495
|
+
`projectAffinity` default.
|
|
496
|
+
|
|
497
|
+
Once a valid profile has been discovered for an explicit `--apply-profile`
|
|
498
|
+
request, apply is a fail-closed prerequisite. A validation failure returns exit
|
|
499
|
+
1; an apply I/O/lock/store failure, thrown apply transport, missing result, or
|
|
500
|
+
malformed success receipt returns exit 2. In every case setup aborts before its
|
|
501
|
+
folder transaction or connector work. A late apply failure may have persisted
|
|
502
|
+
resumable create/update-only profile state, but cannot trigger ordinary setup
|
|
503
|
+
reconciliation. The outer result is `failed`, the nested lexical error code is
|
|
504
|
+
`profile_apply_failed`, and `profile.apply` retains a returned apply result or
|
|
505
|
+
is `null` when apply produced no result.
|
|
506
|
+
|
|
507
|
+
Missing or invalid profile inspection remains the optional fallback described
|
|
508
|
+
below. A thrown inspection transport/runtime failure for an explicit
|
|
509
|
+
`--apply-profile` request instead returns exit 2 with
|
|
510
|
+
`profile_inspection_failed` before apply or setup mutation. Because no trusted
|
|
511
|
+
profile check result exists, this failure uses the unchanged
|
|
512
|
+
`setup-command-result@1.0` shape rather than fabricating `profile.check`.
|
|
513
|
+
|
|
514
|
+
For a valid profile, `--apply-profile` is mutually exclusive with explicit
|
|
515
|
+
`--name` and `--exclude` values. A conflict returns exit 1 with
|
|
516
|
+
`profile_option_conflict` before config, store, or index mutation. Profile
|
|
517
|
+
include/exclude values use validated Bun-glob semantics; plain exclusion
|
|
518
|
+
components retain component matching. Brace alternatives are rejected; express
|
|
519
|
+
their branches as separate include/exclude entries. Discovery permission/I/O
|
|
520
|
+
failures return exit 2, while missing, disabled, invalid, and unsafe profiles
|
|
521
|
+
return exit 1.
|
|
522
|
+
|
|
523
|
+
Opt-in JSON uses
|
|
524
|
+
[`setup-profile-result@1.0`](./output-schemas/setup-profile-result.schema.json)
|
|
525
|
+
and includes the closed `profile.check`, nullable `profile.apply`, unchanged
|
|
526
|
+
setup result, and connectors. A missing/invalid profile keeps setup usable and
|
|
527
|
+
returns `completed_with_actions` with `profile.apply: null`. Without
|
|
528
|
+
`--apply-profile`, the existing setup/activation JSON bytes remain unchanged.
|
|
529
|
+
|
|
395
530
|
After lexical proof, the command records one private atomic
|
|
396
531
|
`setup-semantic@1.0` receipt per canonical index/folder and starts one detached,
|
|
397
532
|
collection-scoped Bun worker. A matching live worker is reused; a dead worker is
|
|
@@ -430,6 +565,7 @@ second connector fingerprint or cache.
|
|
|
430
565
|
|
|
431
566
|
- [`setup-command-result@1.0`](./output-schemas/setup-command-result.schema.json)
|
|
432
567
|
- [`setup-activation-result@1.0`](./output-schemas/setup-activation-result.schema.json)
|
|
568
|
+
- [`setup-profile-result@1.0`](./output-schemas/setup-profile-result.schema.json)
|
|
433
569
|
- [`setup-semantic@1.0`](./output-schemas/setup-semantic-receipt.schema.json)
|
|
434
570
|
- [`FolderSetupReceipt@1.0`](./output-schemas/setup-receipt.schema.json)
|
|
435
571
|
|
|
@@ -1167,6 +1303,24 @@ Capture writes structured `source:` frontmatter and returns the shared
|
|
|
1167
1303
|
[`capture-receipt`](./output-schemas/capture-receipt.schema.json). `--source-date`
|
|
1168
1304
|
maps to `source.observedAt`; `--source-id` maps to `source.externalId`.
|
|
1169
1305
|
|
|
1306
|
+
Browser-clip receipts MAY extend `source` with `canonicalUrl`, `site`,
|
|
1307
|
+
`publishedAt`, and the closed
|
|
1308
|
+
[`browser-clip`](./output-schemas/browser-clip.schema.json) provenance object.
|
|
1309
|
+
The provenance preserves exact selection text separately from canonical final
|
|
1310
|
+
Markdown and carries extraction/final hashes, deterministic identity and
|
|
1311
|
+
preview digests, normalized dates/URLs, browser metadata, server capture time,
|
|
1312
|
+
and bounded warnings. Existing capture inputs and receipts remain compatible.
|
|
1313
|
+
Browser-clip URLs use a closed, credential-free HTTP(S) subset: ASCII DNS or
|
|
1314
|
+
punycode hosts (or IPv4), optional ports from 1 through 65535, and ASCII
|
|
1315
|
+
RFC 3986 path/query/fragment characters with well-formed percent escapes. Raw
|
|
1316
|
+
Unicode hosts and IPv6 literals are rejected; browser clients should submit
|
|
1317
|
+
their serialized IDNA/punycode URL. Free-form clip strings reject C0/C1 control
|
|
1318
|
+
characters except tab, LF, and CR.
|
|
1319
|
+
For browser provenance, `open_existing` MUST open only when the stored
|
|
1320
|
+
`clipIdentity` matches; missing or different provenance MUST return
|
|
1321
|
+
`collisionPolicyResult: "conflict"` without writing. `create_with_suffix`
|
|
1322
|
+
creates a distinct note.
|
|
1323
|
+
|
|
1170
1324
|
**Path and Collision Rules:**
|
|
1171
1325
|
|
|
1172
1326
|
- Explicit `--path` wins.
|
|
@@ -1320,6 +1474,10 @@ retrieval results and is used as trusted guidance during grounded answer
|
|
|
1320
1474
|
generation. Matching scopes compose once in this order: global, collection,
|
|
1321
1475
|
then path prefixes from broadest to most specific. Context guides interpretation;
|
|
1322
1476
|
it is not searched and does not change ranking.
|
|
1477
|
+
Multiple distinct normalized texts may share one scope. The persisted identity
|
|
1478
|
+
is `(scopeType, canonicalScopeKey, normalizedText)`. Text normalization removes
|
|
1479
|
+
a leading BOM, converts CRLF/CR to LF, applies NFC, and trims surrounding
|
|
1480
|
+
whitespace; an exact normalized duplicate is rejected.
|
|
1323
1481
|
|
|
1324
1482
|
**Synopsis:**
|
|
1325
1483
|
|
|
@@ -1393,18 +1551,20 @@ gno context check [--json|--md]
|
|
|
1393
1551
|
|
|
1394
1552
|
### gno context rm
|
|
1395
1553
|
|
|
1396
|
-
Remove a context.
|
|
1554
|
+
Remove a context. Scope-only removal succeeds only when exactly one record
|
|
1555
|
+
matches. If multiple texts share the scope, the caller MUST pass exact text;
|
|
1556
|
+
ambiguous removal fails without mutation.
|
|
1397
1557
|
|
|
1398
1558
|
**Synopsis:**
|
|
1399
1559
|
|
|
1400
1560
|
```bash
|
|
1401
|
-
gno context rm <scope>
|
|
1561
|
+
gno context rm <scope> [text]
|
|
1402
1562
|
```
|
|
1403
1563
|
|
|
1404
1564
|
**Exit Codes:**
|
|
1405
1565
|
|
|
1406
1566
|
- 0: Success
|
|
1407
|
-
- 1:
|
|
1567
|
+
- 1: Invalid/not-found scope, text not found, or ambiguous scope-only removal
|
|
1408
1568
|
|
|
1409
1569
|
---
|
|
1410
1570
|
|
|
@@ -2952,6 +3112,38 @@ is blocked.
|
|
|
2952
3112
|
- Mounts stateful Streamable HTTP MCP at `/mcp` only after the fail-closed
|
|
2953
3113
|
actual-peer, Host, Origin, bearer, body, rate, request, queue, and session
|
|
2954
3114
|
boundary initializes
|
|
3115
|
+
- On the loopback `gno serve` listener only, mounts the browser-clipper pairing,
|
|
3116
|
+
preview, and capture routes. These require exact extension/same-origin
|
|
3117
|
+
policies and dedicated origin-bound capture grants; MCP bearer tokens,
|
|
3118
|
+
`GNO_API_TOKEN`, and `gateway.enableWrite` do not authorize them. Clipper
|
|
3119
|
+
routes are structurally absent from non-loopback listeners. Capture
|
|
3120
|
+
idempotency persists exact content-free plan hashes and reconciles an
|
|
3121
|
+
interrupted atomic write only at its original path; changed plans fail
|
|
3122
|
+
closed without a duplicate write.
|
|
3123
|
+
- Serves the standalone `/clipper/pair` approval page. Its exact pair-ID
|
|
3124
|
+
fragment is synchronously validated and scrubbed before normal workspace
|
|
3125
|
+
state initializes; the user enters the eight-digit code manually. The page
|
|
3126
|
+
obtains same-origin CSRF state, never receives the extension grant, and does
|
|
3127
|
+
not persist pairing material.
|
|
3128
|
+
- Browser-clipper HTTP write receipts add `schemaVersion: "1.0"` to the closed
|
|
3129
|
+
shared capture receipt. HTTP 409 can therefore be either a valid provenance
|
|
3130
|
+
conflict receipt or a closed clipper error; clients parse the body contract
|
|
3131
|
+
before classifying the outcome.
|
|
3132
|
+
- Browser clip response/status closure is exact: HTTP 200 only for
|
|
3133
|
+
`opened_existing`; HTTP 202 only for `created`, `created_with_suffix`, or
|
|
3134
|
+
`overwritten`; HTTP 409 for either a valid `conflict` receipt or the matching
|
|
3135
|
+
closed `clipper-error@1.0`. Unknown versions, fields, codes, non-JSON bodies,
|
|
3136
|
+
and status/body mismatches fail as invalid responses. `CLIPPER_OFFLINE`,
|
|
3137
|
+
`CLIPPER_INVALID_RESPONSE`, and `CLIPPER_CLIENT` are client-only
|
|
3138
|
+
classifications.
|
|
3139
|
+
- Browser clip provenance contains exactly `extractionHash`, `finalBodyHash`,
|
|
3140
|
+
`clipIdentity`, and `previewDigest`; it does not contain `sourceHash`.
|
|
3141
|
+
- The Chromium service worker stores the loopback origin, plaintext bounded
|
|
3142
|
+
grant, and at most one `{payload, previewDigest, idempotencyKey}` in protected
|
|
3143
|
+
local extension storage. Unfinished pairing state uses extension session
|
|
3144
|
+
storage. Page extraction and the approval page receive neither grant nor
|
|
3145
|
+
pending-write state. Recovery reuses the same logical write or fails closed
|
|
3146
|
+
without changing destination.
|
|
2955
3147
|
- 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
|
|
2956
3148
|
- 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.
|
|
2957
3149
|
- 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
|
@@ -61,7 +61,7 @@ CREATE TABLE IF NOT EXISTS contexts (
|
|
|
61
61
|
scope_key TEXT NOT NULL,
|
|
62
62
|
text TEXT NOT NULL,
|
|
63
63
|
synced_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
64
|
-
PRIMARY KEY (scope_type, scope_key)
|
|
64
|
+
PRIMARY KEY (scope_type, scope_key, text)
|
|
65
65
|
);
|
|
66
66
|
|
|
67
67
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -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
|
+
}
|