@wenathlan/saddle 1.8.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/LICENSE +203 -0
- package/README.md +192 -0
- package/adapters/forge.js +16 -0
- package/adapters/forgejo.js +8 -0
- package/adapters/github.js +19 -0
- package/adapters/gitlab.js +10 -0
- package/adapters/huggingface.js +6 -0
- package/adapters/socket.js +14 -0
- package/adapters/transport.js +30 -0
- package/ai/chunk.js +22 -0
- package/ai/llmstxt.js +12 -0
- package/ai/provenance.js +18 -0
- package/ai/rag.js +14 -0
- package/ai/tokens.js +9 -0
- package/api/auth.js +13 -0
- package/api/contracts.js +17 -0
- package/api/control.js +33 -0
- package/api/http.js +12 -0
- package/api/rate.js +31 -0
- package/api/security.js +42 -0
- package/api/service.js +36 -0
- package/binary/build.js +17 -0
- package/bot/adapter.js +8 -0
- package/bot/bot.js +39 -0
- package/bot/commands.js +18 -0
- package/bot/permissions.js +16 -0
- package/browser/actions.js +33 -0
- package/browser/agent.js +9 -0
- package/browser/context.js +52 -0
- package/browser/fingerprint.js +12 -0
- package/browser/index.js +10 -0
- package/browser/recorder.js +15 -0
- package/browser/session.js +19 -0
- package/browser/snapshot.js +57 -0
- package/captcha/contract.js +15 -0
- package/captcha/evidence.js +9 -0
- package/captcha/guard.js +10 -0
- package/cli/main.js +36 -0
- package/core/errors.js +37 -0
- package/core/events.js +21 -0
- package/core/hash.js +73 -0
- package/core/ids.js +15 -0
- package/crawl/crawler.js +29 -0
- package/crawl/frontier.js +34 -0
- package/crawl/normalize.js +14 -0
- package/crawl/persistent.js +13 -0
- package/dispatch/resumable.js +31 -0
- package/dispatch/workflow.js +33 -0
- package/docs/assets/architecture.svg +45 -0
- package/docs/assets/saddlemark.svg +13 -0
- package/docs/comparativeaudit.md +63 -0
- package/docs/ecosystemplan.md +59 -0
- package/docs/enginearchitecture.md +83 -0
- package/docs/featureaudit.md +63 -0
- package/docs/gapmatrix.md +80 -0
- package/docs/libraryapi.md +63 -0
- package/docs/modes.md +27 -0
- package/docs/productindex.md +28 -0
- package/docs/registryresearch.md +56 -0
- package/docs/release.md +28 -0
- package/docs/release17notes.md +24 -0
- package/docs/release181notes.md +15 -0
- package/docs/release18notes.md +15 -0
- package/docs/roadmapp2p3.md +33 -0
- package/docs/toolchains.md +28 -0
- package/docs/usage.md +107 -0
- package/domain/artifacts.js +13 -0
- package/domain/jobs.js +20 -0
- package/domain/providers.js +8 -0
- package/domain/runtime.js +10 -0
- package/domain/sessions.js +34 -0
- package/errors/taxonomy.js +18 -0
- package/examples/localjob.js +15 -0
- package/examples/publicapi.js +7 -0
- package/extension/README.md +23 -0
- package/extension/content.js +85 -0
- package/extension/index.js +5 -0
- package/extension/manifest.json +10 -0
- package/extension/popup.css +13 -0
- package/extension/popup.html +24 -0
- package/extension/popup.js +25 -0
- package/extension/protocol.js +76 -0
- package/extension/serviceworker.js +43 -0
- package/extension/worker.js +20 -0
- package/format/check.js +21 -0
- package/index.js +120 -0
- package/library/public.js +83 -0
- package/license.md +203 -0
- package/license.txt +203 -0
- package/mcp/browser.js +12 -0
- package/mcp/server.js +28 -0
- package/mcp/transport.js +14 -0
- package/memory/bridge.js +16 -0
- package/memory/engine.js +45 -0
- package/memory/modes.js +55 -0
- package/memory/objects.js +18 -0
- package/memory/targets.js +21 -0
- package/memory/transforms.js +15 -0
- package/modes/matrix.js +20 -0
- package/modes/modes.js +16 -0
- package/modes/resolve.js +39 -0
- package/package.json +47 -0
- package/packager/manifest.js +28 -0
- package/packager/publish.js +15 -0
- package/persistence/adapter.js +8 -0
- package/persistence/drizzle.js +10 -0
- package/persistence/memory.js +26 -0
- package/persistence/migrations.js +14 -0
- package/persistence/prisma.js +23 -0
- package/persistence/schema.js +29 -0
- package/persistence/sql.js +30 -0
- package/protocol/blocks.js +18 -0
- package/protocol/json.js +5 -0
- package/protocol/ndjson.js +17 -0
- package/protocol/sse.js +22 -0
- package/proxy/pool.js +12 -0
- package/queue/idempotency.js +12 -0
- package/queue/persistent.js +44 -0
- package/queue/queue.js +50 -0
- package/queue/saga.js +13 -0
- package/readme.txt +163 -0
- package/retry/circuit.js +15 -0
- package/retry/policy.js +12 -0
- package/runners/health.js +23 -0
- package/runners/heartbeat.js +26 -0
- package/runners/inprocess.js +19 -0
- package/runners/scheduler.js +16 -0
- package/runtime/abort.js +10 -0
- package/runtime/compatibility.js +13 -0
- package/runtime/detect.js +14 -0
- package/runtime/engine.js +56 -0
- package/runtime/worker.js +18 -0
- package/scrape/cache.js +14 -0
- package/scrape/extract.js +14 -0
- package/scrape/robots.js +32 -0
- package/scrape/schema.js +21 -0
- package/scrape/scraper.js +40 -0
- package/scrape/semantic.js +22 -0
- package/server/node.js +34 -0
- package/sessions/file.js +13 -0
- package/sessions/replay.js +21 -0
- package/sessions/store.js +13 -0
- package/storage/adapter.js +8 -0
- package/storage/cache.js +54 -0
- package/storage/checksum.js +17 -0
- package/storage/chunked.js +58 -0
- package/storage/content.js +42 -0
- package/storage/filehosting.js +17 -0
- package/storage/githubcontents.js +18 -0
- package/storage/index.js +10 -0
- package/storage/local.js +35 -0
- package/storage/memory.js +28 -0
- package/storage/s3compatible.js +23 -0
- package/storage/sync.js +55 -0
- package/surfaces/adapters.js +48 -0
- package/surfaces/controls.js +37 -0
- package/surfaces/manifest.js +25 -0
- package/surfaces/n8n.js +24 -0
- package/surfaces/operations.js +43 -0
- package/surfaces/targets.js +16 -0
- package/webhook/delivery.js +26 -0
- package/webhook/receiver.js +20 -0
- package/webhook/signature.js +7 -0
- package/workflow/manifest.js +20 -0
- package/workflow/registry.js +16 -0
- package/workflow/templates.js +18 -0
- package/workflow/triggers.js +31 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# feature gap matrix
|
|
2
|
+
|
|
3
|
+
This matrix turns the supplied README and conclusions into implementation decisions. It does not treat a marketing claim as an implemented feature. A row is marked **implemented** only when the repository contains an executable contract and a deterministic test or a verified workflow.
|
|
4
|
+
|
|
5
|
+
## priority rules
|
|
6
|
+
|
|
7
|
+
| Priority | Meaning |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| P0 | blocks a usable extension or makes a public contract misleading |
|
|
10
|
+
| P1 | required for a credible browser agent or ecosystem integration |
|
|
11
|
+
| P2 | valuable capability that can remain an adapter or later surface |
|
|
12
|
+
| deferred | intentionally not promised by the current core |
|
|
13
|
+
|
|
14
|
+
## matrix
|
|
15
|
+
|
|
16
|
+
| Area | Current state | Gap | Priority | Decision |
|
|
17
|
+
| --- | --- | --- | --- | --- |
|
|
18
|
+
| Root library | Implemented ESM entry point with broad exports | No extension subpath or extension files are shipped | P0 | Add a small `extension/` package surface and export only serializable contracts |
|
|
19
|
+
| Browser agent | Implemented injected action adapter for navigate, click, type, screenshot, DOM, title, scroll and command batches | Vendor-neutral action results and bounded action batches are now public | P1 | Keep the adapter boundary; add vendor adapters without moving browser ownership into the core |
|
|
20
|
+
| Browser snapshots | Implemented public contract | Snapshot ids, bounded elements, stable refs, stale checks and diffs are covered by deterministic tests | P0 | Reuse the contract from MCP and extension transport |
|
|
21
|
+
| Session replay | Partial | Replay and action recording exist with snapshot provenance, but tab/window restoration and frame-aware replay are still absent | P1 | Add context-aware replay after adapter conformance tests |
|
|
22
|
+
| Extension runtime | Surface is only declared in `surfaces/manifest.js` and `surfaces/targets.js` | No Manifest V3 manifest, service worker, content bridge, popup or build artifact | P0 | Implement a pure JavaScript MV3 reference surface with minimal permissions |
|
|
23
|
+
| Extension messaging | Not implemented | No versioned envelope, correlation id, timeout, sender metadata or error response contract | P0 | Add transport-neutral message contracts and Chrome runtime adapter |
|
|
24
|
+
| Service worker resilience | Not implemented | No rehydration or durable state strategy for worker termination | P0 | Persist pending command metadata and session summaries through injected storage |
|
|
25
|
+
| Permissions | Not implemented | No permission policy or optional escalation path | P0 | Start with `storage` and no broad host permissions; make host access caller-configured |
|
|
26
|
+
| Content isolation | Not implemented | No isolated-world DOM bridge or page-to-extension boundary | P0 | Add a narrow content script that reports page facts through the message contract |
|
|
27
|
+
| Task agent | Partial | Jobs, workflows and bot commands exist, but no browser task planner or tool registry | P1 | Reuse workflow, trigger and bot contracts; add browser task commands only after snapshots |
|
|
28
|
+
| MCP | Implemented scrape, crawl, batch, extract and serialize tools with JSON-RPC handling | No browser snapshot or browser action MCP tools | P1 | Add browser tools as an optional adapter over the same snapshot/action contracts |
|
|
29
|
+
| API security | Implemented URL protocol and private hostname/IP checks | Request envelopes, optional authorization, security headers, redirect bounds and injected DNS resolution checks are now available | P0 | Keep credentials caller-owned and reject private or rebinding targets before transport |
|
|
30
|
+
| Apps and bots | Implemented platform adapter, commands, bot and webhook signature contracts | App install/suspend/revoke, command scope checks, idempotency and delivery retry/dead-letter records are now available | P1 | Keep platform tokens and OAuth lifecycle caller-owned |
|
|
31
|
+
| Storage | Implemented local, chunked, S3-compatible, GitHub Contents and file hosting adapters | Range reads, content dedupe, tiered cache, capabilities and conflict-aware sync were missing | P1 | Use the new neutral storage helpers and keep extension storage injected |
|
|
32
|
+
| Queue | Implemented in-memory and persistent queue contracts | No worker-aware resume protocol for extension commands | P1 | Add resumable command records and idempotency keys to extension transport |
|
|
33
|
+
| Remote execution | Implemented provider, scheduler, health, triggers, heartbeat and resumable run contracts | No permissioned extension-to-runner bridge or forge-specific status adapters for every provider | P1 | Require explicit caller-provided endpoint and auth; no default remote host |
|
|
34
|
+
| Scraping | Implemented robots, cache, HTML extraction, schema extraction and scraper | Semantic headings, landmarks, controls and links are now available; content-type normalization remains partial | P1 | Keep extraction safe and bounded; add adapters for richer document types |
|
|
35
|
+
| Crawl | Implemented normalized BFS and persistent frontier | Priority and per-domain budget frontier now exist; sitemap refresh remains absent | P1 | Keep frontier state serializable and caller-persistable |
|
|
36
|
+
| RAG context | Implemented chunk hashes and vector record metadata | Retrieval provenance and merge records now exist; embeddings and indexes remain injected | P1 | Preserve source, document, chunk and score lineage |
|
|
37
|
+
| Observability | Contract slice | Low-cardinality counters and durations are bound to the standard operational metric vocabulary; export and tracing remain caller-owned | P1 | Keep metrics vendor-neutral and bounded |
|
|
38
|
+
| Operations policy | Contract slice | Retention, backup/restore capability and threat ownership are declarative and caller-owned | P1 | Add workers, persistence and incident response only in host surfaces |
|
|
39
|
+
| Web control surface | Contract slice | `controlservice` maps Web Request and Response traffic to the auditable control contract; framework, auth verifier and persistence remain injected | P1 | Keep the HTTP boundary transport-neutral |
|
|
40
|
+
| Auth profiles | Session file and replay contracts exist | No extension profile or consent model | P1 | Defer cookie/profile export; support explicit user-owned session references only |
|
|
41
|
+
| CAPTCHA | Contract, guard and evidence exist | No automatic solver integration | deferred | Keep external/manual solver boundary; do not promise bypass in the extension |
|
|
42
|
+
| Stealth | Fingerprint contract exists | No automatic stealth patching | deferred | Keep opt-in fingerprint metadata; no hidden anti-detection behavior |
|
|
43
|
+
| Packaging | npm, GHCR, Maven, NuGet and RubyGems workflows are live | No extension zip build or release artifact | P1 | Add a deterministic zip/check workflow after the reference surface is tested |
|
|
44
|
+
| Mobile and desktop apps | Contract slice | Desktop/mobile manifests and caller-owned adapter contracts exist; no native project is bundled | P1 | Keep native projects caller-owned and add runtime conformance tests incrementally |
|
|
45
|
+
| n8n surface | Contract slice | Node metadata, trigger matching and declared action execution exist; no n8n host package is bundled | P1 | Keep node registration and credentials caller-owned |
|
|
46
|
+
| Cross-browser | Target profile declares browser and extension | No Firefox, Edge or Safari manifests/build validation | P2 | Keep WebExtension-compatible contracts and add adapters incrementally |
|
|
47
|
+
| Storage equals compute | Memory bridge and engine implement storage-to-working-set-to-artifact; sync and capability negotiation now exist | Remote storage is not physical VRAM and has latency | deferred | Document as a working-set model, never as literal remote VRAM |
|
|
48
|
+
| Site/database deployment | Persistence schemas and adapters exist | No hosted site or database is part of the package | deferred | Keep deploy targets caller-owned and outside the library core |
|
|
49
|
+
|
|
50
|
+
## first implementation slice
|
|
51
|
+
|
|
52
|
+
The first code slice targeted the P0 rows only. It now contains:
|
|
53
|
+
|
|
54
|
+
1. A versioned serializable message envelope with request correlation and error responses.
|
|
55
|
+
2. A snapshot contract with stable references and stale snapshot detection.
|
|
56
|
+
3. A browser context registry for tabs, frames and active state.
|
|
57
|
+
4. Structured action results, failures and bounded action batches.
|
|
58
|
+
5. A recorder linking actions to the snapshot used before execution.
|
|
59
|
+
6. A Chrome MV3 service worker that rehydrates state and routes messages.
|
|
60
|
+
7. A narrow content script that reports document metadata and visible text through the bridge.
|
|
61
|
+
8. Deterministic tests for browser contracts without Chrome credentials or network access.
|
|
62
|
+
|
|
63
|
+
The extension remains an adapter. The root library continues to work without a browser, without an extension and without external memory.
|
|
64
|
+
|
|
65
|
+
## implementation status
|
|
66
|
+
|
|
67
|
+
Version 1.1 implements the first slice in `extension/`: `protocol.js` provides versioned serializable messages and snapshot identity; `serviceworker.js` provides browser independent routing; `worker.js` binds that router to Manifest V3 APIs; `content.js` runs the isolated page bridge; and `popup.html` with `popup.js` provides user initiated snapshot and read actions. The package exports `@wenathlan/saddle/extension`, while the root library remains usable without Chrome.
|
|
68
|
+
|
|
69
|
+
The slice is intentionally not a full autonomous browser agent. Snapshot diffing, tab and frame identity, resumable command records, optional host escalation, browser action results and multi-browser packaging remain P1 or P2 work.
|
|
70
|
+
|
|
71
|
+
## references
|
|
72
|
+
|
|
73
|
+
1. [Chrome message passing](https://developer.chrome.com/docs/extensions/develop/concepts/messaging)
|
|
74
|
+
2. [Chrome content scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts)
|
|
75
|
+
3. [Chrome permissions](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions)
|
|
76
|
+
4. [Chrome service worker lifecycle](https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/lifecycle)
|
|
77
|
+
5. [Microsoft Playwright MCP](https://github.com/microsoft/playwright-mcp)
|
|
78
|
+
6. [Vercel agent-browser](https://github.com/vercel-labs/agent-browser)
|
|
79
|
+
7. [Browser Use](https://github.com/browser-use/browser-use)
|
|
80
|
+
8. [WXT](https://wxt.dev/)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# saddle public library api
|
|
2
|
+
|
|
3
|
+
The public API is designed around injected transports. Consumers can use the same contracts in Node, Bun, Deno, a browser worker, a desktop wrapper, or a server adapter without importing a vendor client into the core.
|
|
4
|
+
|
|
5
|
+
| export | purpose |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `saddleurl` | choose fetch or an injected browser agent |
|
|
8
|
+
| `scrapeurl` | fetch a URL and return extracted content |
|
|
9
|
+
| `scrapehtml` | extract content from an HTML string |
|
|
10
|
+
| `extractcontent` | expose structured extraction directly |
|
|
11
|
+
| `serializeresult` | serialize as JSON, Markdown, text, XML, or Redis payload |
|
|
12
|
+
| `formatforagent` | produce summary, key points, chunks, links, and token count |
|
|
13
|
+
| `batchscrape` | run bounded parallel scrape jobs with progress callbacks |
|
|
14
|
+
| `crawlurl` | run the same crawl contract through the public surface |
|
|
15
|
+
| `browseragent` | adapt navigate, click, type, screenshot, DOM, scroll, and commands |
|
|
16
|
+
| `pagesnapshot` / `snapshotref` | create bounded page state and stable element references |
|
|
17
|
+
| `assertfreshsnapshot` / `snapshotdiff` | reject stale actions and compare page state |
|
|
18
|
+
| `browsercontext` | track tabs, frames and active browser context without a vendor client |
|
|
19
|
+
| `actionbatch` / `actionresult` | execute bounded adapter actions with structured outcomes |
|
|
20
|
+
| `actionrecorder` | record snapshot boundaries and action provenance for replay |
|
|
21
|
+
| `contentstorage` | deduplicate immutable bytes behind logical references |
|
|
22
|
+
| `tieredcache` | serve bounded hot values with persistent cold storage and stale revalidation |
|
|
23
|
+
| `syncobject` / `syncbackends` | compare manifests and copy or resolve updates across adapters |
|
|
24
|
+
| `memoryengine.sync` / `memoryengine.capabilities` | synchronize working set objects and inspect backend capabilities |
|
|
25
|
+
| `runnerhealth` / `runnerhealthall` | report provider readiness, capacity and failures |
|
|
26
|
+
| `heartbeat` | emit cooperative liveness signals for long-running work |
|
|
27
|
+
| `workflowtriggers` / `triggermatch` | normalize and match manual, event, schedule and retry starts |
|
|
28
|
+
| `resumablerun` / `transitionrun` | recover remote run state through legal transitions |
|
|
29
|
+
| `extractsemantic` | expose bounded headings, landmarks, controls and links |
|
|
30
|
+
| `crawlfrontier` | prioritize URLs and enforce page and domain budgets |
|
|
31
|
+
| `provenance` / `mergeprovenance` | link context chunks to source and retrieval evidence |
|
|
32
|
+
| `metricstore` | collect bounded counters and duration summaries |
|
|
33
|
+
| `authorize` | verify caller credentials through an injected verifier |
|
|
34
|
+
| `requestcontext` / `successpayload` / `errorpayload` | create versioned API identity and response contracts |
|
|
35
|
+
| `assertresolvedpublicurl` / `assertredirectchain` | validate resolved destinations and bounded redirects |
|
|
36
|
+
| `browsertools` | expose injected snapshot and action methods as optional MCP tools |
|
|
37
|
+
| `appregistry` | track app installation, scopes and revocation state |
|
|
38
|
+
| `commandguard` | enforce caller-defined bot command scopes |
|
|
39
|
+
| `deliveryqueue` | retry webhook deliveries and retain dead letters |
|
|
40
|
+
| `nodeserver` | expose a Web Request/Response handler through Node HTTP |
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
import { scrapeurl, formatforagent } from "@devthink/saddle";
|
|
44
|
+
|
|
45
|
+
const result = await scrapeurl("https://example.com", {
|
|
46
|
+
format: "markdown",
|
|
47
|
+
fetcher: globalThis.fetch
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
console.log(formatforagent(result));
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The `fetcher`, browser adapter, persistence adapter, proxy pool, captcha solver, webhook handler, and vector store remain caller-owned. This is intentional: the engine coordinates contracts but does not claim ownership of credentials, sessions, remote infrastructure, or external service terms.
|
|
54
|
+
|
|
55
|
+
## package surfaces
|
|
56
|
+
|
|
57
|
+
The package exposes explicit subpaths for `./browser`, `./bot`, `./captcha`, `./memory-engine`, and `./deploy`. Desktop, mobile, and n8n contracts are exported from the root entry; the root entry remains the complete JavaScript API for consumers that prefer one import.
|
|
58
|
+
|
|
59
|
+
Version 1.1 adds `./extension`, which exposes browser-neutral message, snapshot and service-worker routing contracts. The concrete Manifest V3 files live in `extension/`; they are not imported by the core at runtime and do not require Chrome when the library is used as a Node package. The browser surface also exposes snapshots, tab/frame context, action results, bounded action batches and recording through `./browser`.
|
|
60
|
+
|
|
61
|
+
The current main branch adds `desktopmanifest`, `mobilemanifest`, `desktopadapter`, `mobileadapter`, `n8nnode`, `n8nmatch`, `n8nexecute`, `controlsurface`, `controlservice`, and `workerbridge`. These factories describe surface boundaries and invoke caller-owned handlers; they do not install a native toolkit, start an n8n server, create a dashboard, or store credentials. They will be included in the next versioned release after the compatibility gates are complete.
|
|
62
|
+
|
|
63
|
+
The root entry is cross-runtime safe for the tested core contract. Filesystem, Node HTTP, persistent queue, file sessions, local memory and captcha evidence adapters remain available through explicit Node-only files or subpaths. `runtimecontract` reports the capabilities of the current global scope, while `memorystorage` provides a process-local backend for browser workers, Deno, Bun and deterministic tests.
|
package/docs/modes.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# saddle mode matrix
|
|
2
|
+
|
|
3
|
+
The mode resolver keeps execution open. It returns a profile and capability map without starting a server, opening a browser, selecting a vendor, or choosing an infrastructure endpoint.
|
|
4
|
+
|
|
5
|
+
| axis | values |
|
|
6
|
+
|---|---|
|
|
7
|
+
| execution | library, application, browser, desktopapp, mobileapp, extension, cli, binary, computer, internet |
|
|
8
|
+
| runtime | node, browser, deno, bun, worker, unknown |
|
|
9
|
+
| memory | internal, external, physical, vectorized, library |
|
|
10
|
+
| file | internal, external, physical, vector |
|
|
11
|
+
| dependency | internal, external, dev |
|
|
12
|
+
| visibility | visible, headless |
|
|
13
|
+
| pair | without, with |
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { resolvemode } from "@devthink/saddle/modes";
|
|
17
|
+
|
|
18
|
+
const profile = resolvemode({
|
|
19
|
+
execution: "binary",
|
|
20
|
+
memory: "vectorized",
|
|
21
|
+
visibility: "headless"
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
console.log(profile.capabilities);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The same profile contract can be passed to a library runner, an application wrapper, a browser adapter, a desktop shell, a mobile shell, a CLI, or a binary builder. Each host remains responsible for its own credentials, files, sockets, and lifecycle.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# saddle product surfaces
|
|
2
|
+
|
|
3
|
+
Saddle is the contract layer for a family of caller-owned surfaces. The library does not ship a desktop toolkit, mobile runtime, n8n server, browser account, or hosted control plane. It describes the boundaries, validates declarations, orchestrates injected handlers, and preserves structured failure information.
|
|
4
|
+
|
|
5
|
+
## surface map
|
|
6
|
+
|
|
7
|
+
| Surface | Current contract | Caller-owned concern |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| library | root ESM entry and stable factories | application lifecycle and deployment |
|
|
10
|
+
| desktop | `desktopmanifest` and `desktopadapter` | windowing, filesystem policy, signing and native packaging |
|
|
11
|
+
| mobile | `mobilemanifest` and `mobileadapter` | screen lifecycle, secure storage, permissions and app-store packaging |
|
|
12
|
+
| n8n | `n8nnode`, `n8nmatch` and `n8nexecute` | node registration, credential UI, workflow persistence and execution host |
|
|
13
|
+
| operations | `operationsmetrics`, `retentionpolicy`, `backupplan` and `threatmodel` | telemetry exporter, backup store, retention worker and incident response |
|
|
14
|
+
| cross runtime | `runtimecontract`, `memorystorage` and root ESM import | runtime-specific APIs and package loader behavior |
|
|
15
|
+
| browser worker | `workerbridge` and root-safe contracts | worker lifecycle, message transport and extension permissions |
|
|
16
|
+
| browser | browser agent and snapshot contracts | browser vendor adapter, profile and session ownership |
|
|
17
|
+
| extension | Manifest V3 reference files and serializable protocol | browser permission grant, signing and store submission |
|
|
18
|
+
| web control | API, service, `controlsurface` and `controlservice` contracts | operator UI, authentication, database and hosting |
|
|
19
|
+
|
|
20
|
+
## operating boundary
|
|
21
|
+
|
|
22
|
+
The engine never invents a host, port, credential, account, app identifier, browser profile, or cloud provider. A surface adapter should expose only the operations that its host can execute. Unsupported operations return a structured capability result, while handler failures preserve a stable code and message for operator logs.
|
|
23
|
+
|
|
24
|
+
The cross-runtime boundary keeps the root entry free of filesystem, Node HTTP and other Node-only imports. Those adapters remain explicit imports so callers can select them in Node without making browser worker, Deno or Bun consumers pay for unavailable APIs.
|
|
25
|
+
|
|
26
|
+
## block 10 scope
|
|
27
|
+
|
|
28
|
+
The first Block 10 slice establishes the desktop and mobile adapter boundaries, expands the n8n node declaration to the workflow trigger vocabulary already used by the engine, adds product documentation, exposes an auditable operator control contract for jobs, sessions, storage, runners, permissions, logs and artifacts, and defines bounded operations policies for metrics, retention, backup, restore and threat ownership. The concrete UI, authentication, telemetry export, persistence and incident response remain caller-owned.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# registry research
|
|
2
|
+
|
|
3
|
+
## verified facts
|
|
4
|
+
|
|
5
|
+
GitHub's documentation states that GitHub Actions can use the workflow `GITHUB_TOKEN` to publish, install, delete, and restore packages in GitHub Packages without storing a personal access token. The workflow still needs the correct job permission, normally `packages: write`, and the package must target a GitHub Packages registry rather than the public npm registry.
|
|
6
|
+
|
|
7
|
+
npmjs is a separate registry. This repository now uses the owner-managed GitHub Actions secret `NPM_TOKEN` for public npm publication. The value is injected only at runtime through `NODE_AUTH_TOKEN`; it is never committed, printed or used for GitHub Packages. Trusted Publishing remains a possible future migration, but it is not required by the current workflow.
|
|
8
|
+
|
|
9
|
+
The repository therefore needs separate npm workflows: one with `registry-url: https://npm.pkg.github.com` and `GITHUB_TOKEN` for GitHub Packages, and one with `registry-url: https://registry.npmjs.org` and `NPM_TOKEN` for public npmjs. The same package name and version cannot be published to both registries by assuming they are the same destination.
|
|
10
|
+
|
|
11
|
+
GitHub's Container registry documentation confirms that a workflow can authenticate to `ghcr.io` with `GITHUB_TOKEN` for packages associated with the workflow repository. The recommended publication path also links the container package to the repository automatically; the Dockerfile should include an OCI source label so the association remains explicit. The image's first publication is private by default and its visibility must be changed in GitHub package settings if a public image is desired.
|
|
12
|
+
|
|
13
|
+
GitHub's Maven documentation confirms that the Maven distribution URL is `https://maven.pkg.github.com/OWNER/REPOSITORY`, that the POM must use a matching `github` server id, and that a workflow may use `GITHUB_TOKEN` to publish a package associated with the workflow repository. The artifact id must contain only lowercase letters, digits, or hyphens.
|
|
14
|
+
|
|
15
|
+
GitHub's NuGet documentation confirms that the source endpoint is `https://nuget.pkg.github.com/NAMESPACE/index.json` and that a GitHub Actions workflow can add that source with `secrets.GITHUB_TOKEN` before running `dotnet nuget push`. The package metadata should use a package id and repository URL that identify the GitHub owner and repository.
|
|
16
|
+
|
|
17
|
+
GitHub's RubyGems documentation confirms that GitHub Packages uses `https://rubygems.pkg.github.com/NAMESPACE/` and a `~/.gem/credentials` entry in the form `:github: Bearer TOKEN`. In a workflow the token can be the automatically supplied `GITHUB_TOKEN`; no long-lived RubyGems token belongs in the repository.
|
|
18
|
+
|
|
19
|
+
The RubyGems workflow must pass the host without a trailing slash, matching the official `gem push --host https://rubygems.pkg.github.com/NAMESPACE` form. RubyGems appends its API path to that host; leaving an extra slash caused the first publish attempt to redirect permanently and fail.
|
|
20
|
+
|
|
21
|
+
## workflow decisions
|
|
22
|
+
|
|
23
|
+
The repository now uses one release-triggered workflow per destination. `publishgithubnpm.yml` publishes a GitHub Packages npm variant under the repository owner scope, currently `@iakadion/saddle`, with `GITHUB_TOKEN`; the public npm workflow keeps the canonical name `@devthink/saddle`. `publishghcr.yml` publishes `ghcr.io/iakadion/saddle`; `publishmaven.yml`, `publishnuget.yml`, and `publishrubygems.yml` publish minimal ecosystem metadata to the corresponding GitHub Packages registries. Every GitHub Packages job grants only `contents: read` and `packages: write`.
|
|
24
|
+
|
|
25
|
+
`publishnpmjs.yml` is intentionally separate from GitHub Packages. It uses Node 26.7.0, disables package-manager caching for the release job, and runs `npm publish --access public` against `https://registry.npmjs.org` with `NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}`. The secret name is public by design, but its value must remain owner-managed and absent from logs and source files.
|
|
26
|
+
|
|
27
|
+
The public npm package must be created under the owner account before the first successful publication. The current path uses the owner-managed `NPM_TOKEN` secret for that bootstrap and subsequent releases. The exposed credential from the prior conversation must never be used.
|
|
28
|
+
|
|
29
|
+
The GitHub Packages npm workflow must use a package scope authorized for the workflow token. `@devthink` is a GitHub organization namespace, while the selected repository currently belongs to `iakadion`; the first run confirmed that publishing `@devthink/saddle` was rejected with HTTP 403. The workflow now changes only the package metadata in the CI workspace to `@iakadion/saddle` before publishing, while the committed `package.json` and public npm package remain `@devthink/saddle`. The two registries therefore have independent package names and access rules.
|
|
30
|
+
|
|
31
|
+
The GHCR package is linked automatically by publishing from this repository and includes the OCI source label in `dockerfile.saddle`. GitHub creates a first container package as private by default, so the owner must change its package visibility to public if public pulls are required. The same visibility review applies to the Maven, NuGet, RubyGems, and GitHub npm packages after their first publication.
|
|
32
|
+
|
|
33
|
+
The live GHCR workflow `31544093172` completed successfully, and the public package page shows `ghcr.io/iakadion/saddle:latest` with a published image digest. The Maven workflow `31544137277` uploaded `io.devthink:saddle:1.0.0` to `maven.pkg.github.com/iakadion/saddle`. The NuGet workflow `31544179046` pushed `Saddle.1.0.0.nupkg` to `nuget.pkg.github.com/iakadion`. The first RubyGems workflow `31544228642` failed because the host had an extra trailing slash; after removing it, workflow `31544354107` registered `saddle (1.0.0)` successfully.
|
|
34
|
+
|
|
35
|
+
## live verification
|
|
36
|
+
|
|
37
|
+
The first `publishgithubnpm.yml` run failed with HTTP 403 because `@devthink/saddle` was not an authorized package namespace for the `iakadion/saddle` workflow. The corrected run `31543249301` completed successfully after rewriting the package name in the CI workspace to `@iakadion/saddle`. However, the repository Packages page still returned an empty listing immediately afterward, and the available GitHub API token could not read the user package namespace. The next verification must query the owner package page directly and inspect the workflow publish logs before treating the successful exit code as visible package availability.
|
|
38
|
+
|
|
39
|
+
The v1.7.0 release verification used workflow logs as the available artifact evidence. GitHub npm run `31549603859` published `@iakadion/saddle@1.7.0`; GHCR run `31549603838` pushed `ghcr.io/iakadion/saddle:1.7.0` and `latest`; Maven retry run `31549802841` uploaded `io.devthink:saddle:1.7.0`; RubyGems retry run `31549804286` registered `saddle (1.7.0)`; and NuGet retry run `31549972311` created and pushed `Saddle.1.7.0.nupkg`. The GitHub package-list API was not readable with the available integration token, so these statements are limited to successful workflow upload evidence rather than an independent package-page listing.
|
|
40
|
+
|
|
41
|
+
The first public npmjs run `31549603849` used OIDC and returned HTTP 404. Retry `31551708958` used the configured `NPM_TOKEN` secret, and retry `31551771374` confirmed that the masked secret reached the runner after newline normalization; both still returned HTTP 404 for `@devthink/saddle`. The identity-check retry `31552368723` reached `npm whoami` with a masked token but npm returned HTTP 401. The remaining blocker is the token itself or its account/scope permission: the owner must replace `NPM_TOKEN` with a valid raw npm access token authorized to publish `@devthink/saddle`. The exposed token from the prior conversation remains deliberately unused.
|
|
42
|
+
|
|
43
|
+
The cross-runtime workflow `31552266171` and its manual rerun `31552272176` completed successfully. The matrix ran the root probe on Node, Bun and Deno and the deterministic Node suite. The root entry no longer imports filesystem, Node HTTP, persistent queue, file-session or local-memory adapters; those remain explicit Node-only subpaths.
|
|
44
|
+
|
|
45
|
+
Release `v1.8.0` was created from commit `9ddfd6c`. GitHub npm run `31556461901`, GHCR run `31556461887`, NuGet run `31556461883` and RubyGems run `31556461991` completed successfully for `1.8.0`. Maven run `31556461885` initially failed because setup-java rejects `latest`; after changing the workflow to JDK 26, manual run `31556549154` completed successfully. Public npmjs run `31556461909` produced the `@devthink/saddle@1.8.0` tarball but the registry rejected the PUT with HTTP 404 `Scope not found`; no public npmjs publication is claimed.
|
|
46
|
+
|
|
47
|
+
The active package identity on main is now `@wenathlan/saddle`. The existing `v1.8.0` tag intentionally remains historical and still contains the prior package identity; a follow-up tag is required before publishing the renamed package through the normal release workflow.
|
|
48
|
+
|
|
49
|
+
## sources
|
|
50
|
+
|
|
51
|
+
1. GitHub Docs — About permissions for GitHub Packages: https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages
|
|
52
|
+
2. npm Docs — Trusted publishing for npm packages: https://docs.npmjs.com/trusted-publishers
|
|
53
|
+
3. GitHub Docs — Working with the Container registry: https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-container-registry
|
|
54
|
+
4. GitHub Docs — Working with the Apache Maven registry: https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry
|
|
55
|
+
5. GitHub Docs — Working with the NuGet registry: https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry
|
|
56
|
+
6. GitHub Docs — Working with the RubyGems registry: https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-rubygems-registry
|
package/docs/release.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# saddle release 1.8.1
|
|
2
|
+
|
|
3
|
+
## release path
|
|
4
|
+
|
|
5
|
+
The release path is intentionally split into source validation, package validation, tag creation, GitHub release creation, and independent registry publication. The repository never contains a registry token.
|
|
6
|
+
|
|
7
|
+
| step | owner | condition |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| package version | repository | `package.json` is `1.8.1` |
|
|
10
|
+
| quality gate | GitHub Actions | `npm run pack:check` passes |
|
|
11
|
+
| tag | repository owner | tag `v1.8.1` points to the validated release commit |
|
|
12
|
+
| GitHub release | repository owner | release `v1.8.1` is created from the validated tag |
|
|
13
|
+
| GitHub Packages | GitHub Actions | `publishgithubnpm.yml`, `publishghcr.yml`, `publishmaven.yml`, `publishnuget.yml`, and `publishrubygems.yml` use `GITHUB_TOKEN` |
|
|
14
|
+
| public npmjs | owner-managed GitHub Actions secret | `publishnpmjs.yml` uses `NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}` and derives the version from the release tag or latest release in manual maintenance runs |
|
|
15
|
+
|
|
16
|
+
## credential rule
|
|
17
|
+
|
|
18
|
+
The npm token previously sent in chat is compromised and must not be used. GitHub Packages publication does not require a manually created secret because the workflows use the short-lived `GITHUB_TOKEN`. Public npmjs publication uses the owner-managed repository secret `NPM_TOKEN`, injected only as `NODE_AUTH_TOKEN` during the publish step. Its value must never be committed, printed, or sent through chat. Toolchain maintenance does not trigger publication; a release event or explicit manual dispatch is still required.
|
|
19
|
+
|
|
20
|
+
## manual release
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
npm run pack:check
|
|
24
|
+
git tag v1.8.1
|
|
25
|
+
git push origin v1.8.1
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The release-created event is the publication trigger for the registry workflows. A dry-run verifies package shape and local tests, but cannot verify registry ownership, Trusted Publisher configuration, package scope authorization, or package visibility; those remain settings controlled by the owner.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Saddle 1.7.0
|
|
2
|
+
|
|
3
|
+
Saddle 1.7.0 extends the engine from browser and storage primitives into a complete integration boundary for agent systems.
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- Adds app installation, suspension, revocation and scope authorization through `appregistry`.
|
|
8
|
+
- Adds caller-defined bot command policies through `commandguard`.
|
|
9
|
+
- Makes bot command results idempotent when a caller supplies an idempotency key.
|
|
10
|
+
- Adds webhook delivery attempts, retryable errors and dead-letter records through `deliveryqueue`.
|
|
11
|
+
- Keeps credentials, OAuth tokens, provider clients and infrastructure ownership outside the library core.
|
|
12
|
+
- Preserves the root-based JavaScript ESM architecture and vendor-neutral adapter boundaries.
|
|
13
|
+
|
|
14
|
+
## Included since 1.0.0
|
|
15
|
+
|
|
16
|
+
The 1.7.0 release includes browser snapshots and stale-reference checks, tab/frame context, action results and recording, content-addressed storage, range reads, tiered cache, manifest synchronization, runner health, heartbeat, triggers, resumable execution, semantic extraction, crawl budgets, retrieval provenance, low-cardinality metrics, API envelopes, caller-owned authorization, SSRF redirect/DNS checks, optional browser MCP tools, app lifecycle and delivery recovery.
|
|
17
|
+
|
|
18
|
+
## Validation
|
|
19
|
+
|
|
20
|
+
The release was prepared with the deterministic Node test runner, format checks, package dry-run, public export checks and `git diff --check`. The release candidate contains 76 passing tests and produces `devthink-saddle-1.7.0.tgz` without publishing credentials in source files.
|
|
21
|
+
|
|
22
|
+
## Registry targets
|
|
23
|
+
|
|
24
|
+
The release workflows target GitHub Packages npm, npmjs through OIDC Trusted Publishing, GHCR, Maven, NuGet and RubyGems. Each package is verified from its workflow result before being reported as published. npmjs publication remains conditional on the one-time Trusted Publisher configuration for `@devthink/saddle`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Saddle 1.8.1
|
|
2
|
+
|
|
3
|
+
Saddle 1.8.1 is a follow-up release that changes the canonical public npm identity to `@wenathlan/saddle`. The previous `v1.8.0` tag remains immutable and is not rewritten.
|
|
4
|
+
|
|
5
|
+
## package identity
|
|
6
|
+
|
|
7
|
+
The public npm workflow publishes `@wenathlan/saddle` using the owner-managed `NPM_TOKEN`. GitHub Packages continues to publish its repository-owned variant as `@iakadion/saddle` through the workspace namespace rewrite in `publishgithubnpm.yml`. Maven, NuGet, RubyGems and GHCR keep their existing artifact identities.
|
|
8
|
+
|
|
9
|
+
## validation
|
|
10
|
+
|
|
11
|
+
The candidate must pass `npm run check`, `npm run formatcheck`, `npm test`, `npm run pack:check` and `git diff --check`. The package version is derived from the `v1.8.1` tag by the shared release action; no workflow receives a manually typed release version.
|
|
12
|
+
|
|
13
|
+
## publication boundary
|
|
14
|
+
|
|
15
|
+
The release workflows remain separate at the registry job level because each destination requires a different protocol and credential. Shared checkout, Node setup, version resolution and package validation use local actions; incompatible publish commands remain isolated by registry.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Saddle 1.8.0
|
|
2
|
+
|
|
3
|
+
Saddle 1.8.0 extends the transport-neutral engine with a browser worker bridge, package export import coverage, and a current-runtime validation lane. The release also updates the audited CI and container toolchains to Node.js 26.7.0 while preserving the package engine range for existing consumers.
|
|
4
|
+
|
|
5
|
+
## included changes
|
|
6
|
+
|
|
7
|
+
The release includes `runtimecontract`, `memorystorage`, the Node/Bun/Deno root probe, `workerbridge`, all public surface contracts from the first product-surface slice, and deterministic import coverage for every declared package export. The container uses official Node 26.7.0 Alpine and Bookworm Slim bases.
|
|
8
|
+
|
|
9
|
+
## validation
|
|
10
|
+
|
|
11
|
+
The candidate is required to pass `npm run check`, `npm run formatcheck`, `npm test`, `npm run pack:check` and `git diff --check`. The current candidate contains 83 passing tests and produces `devthink-saddle-1.8.0.tgz`. Registry publication is performed only by the release workflows after the tag is created.
|
|
12
|
+
|
|
13
|
+
## registry behavior
|
|
14
|
+
|
|
15
|
+
Maven, NuGet, RubyGems, GHCR and GitHub Packages npm derive `1.8.0` from the `v1.8.0` tag. The Maven workflow required one retry because `java-version: latest` is not a valid setup-java input; it now uses JDK 26 and succeeded. The current main branch changes the canonical npm identity to `@wenathlan/saddle`; publication must use a follow-up tag because the existing `v1.8.0` tag predates this identity migration.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# saddle roadmap p2 p3
|
|
2
|
+
|
|
3
|
+
## implemented surfaces
|
|
4
|
+
|
|
5
|
+
The P2 and P3 cut adds the safe extension points described by the README without forcing browser, captcha, proxy, AI, webhook, or packaging vendors into the core.
|
|
6
|
+
|
|
7
|
+
| area | module | behavior |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| browser | `browser/fingerprint.js` | coherent session profile with stable operating system, browser, locale, timezone, touch, and pixel ratio |
|
|
10
|
+
| browser | `browser/session.js` | binds one profile and one proxy reference to recorded events |
|
|
11
|
+
| proxy | `proxy/pool.js` | least used selection, failure threshold, graveyard, and timed revive |
|
|
12
|
+
| captcha | `captcha/contract.js` | detection, explicit review, optional external solver, and assertion |
|
|
13
|
+
| evidence | `captcha/evidence.js` | hash and metadata manifest without persisting raw secrets by default |
|
|
14
|
+
| ai | `ai/tokens.js` | configurable token estimates and context budgets |
|
|
15
|
+
| ai | `ai/chunk.js` | heading aware Markdown chunks with overlap and token counts |
|
|
16
|
+
| ai | `ai/rag.js` | content hash deduplication and vector record metadata |
|
|
17
|
+
| ai | `ai/llmstxt.js` | compact `llms.txt` and full content variants with absolute HTTPS links |
|
|
18
|
+
| webhooks | `webhook/receiver.js` | HMAC verification, delivery idempotency, and event handler boundary |
|
|
19
|
+
| packaging | `surfaces/manifest.js` | browser, extension, desktop, mobile, n8n, cli, binary, and library manifests |
|
|
20
|
+
|
|
21
|
+
## security boundary
|
|
22
|
+
|
|
23
|
+
The browser layer does not patch `navigator`, TLS, HTTP/2, canvas, audio, or WebGL. It only models a coherent profile and session binding. The captcha layer does not bypass challenges automatically. It stops for review or calls an explicitly injected external solver and records only an auditable result reference.
|
|
24
|
+
|
|
25
|
+
Proxy selection is health based. Repeated failures move an entry to a graveyard state and a timer can revive it later. A session keeps a single proxy reference instead of rotating blindly during an active identity.
|
|
26
|
+
|
|
27
|
+
## extensibility
|
|
28
|
+
|
|
29
|
+
AI code uses plain text and array contracts. A tokenizer, embedding provider, vector database, browser runtime, captcha solver, proxy agent, or webhook transport can be attached through a factory without changing public engine contracts.
|
|
30
|
+
|
|
31
|
+
## validation
|
|
32
|
+
|
|
33
|
+
The repository keeps the root based JavaScript layout, no `src` directory, lowercase modules, no credentials, and no hardcoded local host or port. The current suite validates the new contracts together with the engine foundation and prior P0/P1 layers.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# toolchain policy
|
|
2
|
+
|
|
3
|
+
The repository uses a current-runtime CI lane while preserving a compatible library engine range. Node.js `26.7.0` is the current release, while the official release table identifies Node.js `24.x` as LTS; CI and the container therefore use `26.7.0`, while `package.json` keeps `engines.node >=22` until a deliberate compatibility release changes that contract.[1][2]
|
|
4
|
+
|
|
5
|
+
| Area | Selected version or alias | Policy |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| GitHub Actions checkout | `actions/checkout@v7` | Latest stable action referenced by current official examples |
|
|
8
|
+
| GitHub Actions Node | `actions/setup-node@v7` with `26.7.0` | Exact current Node release for CI, release validation and all package workflows |
|
|
9
|
+
| Docker runtime | `node:26.7.0-alpine` and `node:26.7.0-bookworm-slim` | Exact Node release for the two-stage container image; both tags are official Node image variants [3] |
|
|
10
|
+
| Maven | `actions/setup-java@v5` with `java-version: 26` | Current OpenJDK release available from the action; Maven remains independently versioned [6] |
|
|
11
|
+
| NuGet | `actions/setup-dotnet@v6` with `dotnet-version: latest` | Latest stable .NET SDK resolved by the action |
|
|
12
|
+
| RubyGems | `ruby/setup-ruby@v1` with `ruby-version: ruby` | Latest stable MRI supported by the action |
|
|
13
|
+
| Bun | `oven-sh/setup-bun@v2` with `bun-version: latest` | Latest Bun release in the cross-runtime matrix |
|
|
14
|
+
| Deno | `denoland/setup-deno@v2` with `deno-version: latest` | Latest stable Deno release in the cross-runtime matrix |
|
|
15
|
+
| Docker publishing actions | `docker/login-action@v4`, `docker/build-push-action@v7` | Current stable action majors from the official Docker repositories [4][5] |
|
|
16
|
+
|
|
17
|
+
The package release version is intentionally not coupled to these toolchain versions. Each publishing workflow resolves the version from the release tag, checks it against `package.json`, and only then creates the registry artifact. A toolchain update alone does not create a release, move a tag, or publish to npmjs, GHCR, Maven, NuGet or RubyGems.
|
|
18
|
+
|
|
19
|
+
The public npm workflow remains blocked until the owner replaces the invalid or unauthorized `NPM_TOKEN`. The maintenance commit does not trigger any package publish job because the publish workflows listen for a release or explicit manual dispatch.
|
|
20
|
+
|
|
21
|
+
## References
|
|
22
|
+
|
|
23
|
+
[1]: https://nodejs.org/en/blog/release/v26.7.0 "Node.js v26.7.0 release"
|
|
24
|
+
[2]: https://nodejs.org/en/about/previous-releases "Node.js release status table"
|
|
25
|
+
[3]: https://hub.docker.com/_/node "Docker Official Image: node"
|
|
26
|
+
[4]: https://github.com/docker/login-action "docker/login-action"
|
|
27
|
+
[5]: https://github.com/docker/build-push-action "docker/build-push-action"
|
|
28
|
+
[6]: https://openjdk.org/projects/jdk/26/ "OpenJDK JDK 26"
|
package/docs/usage.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# saddle usage guide
|
|
2
|
+
|
|
3
|
+
## install
|
|
4
|
+
|
|
5
|
+
The canonical public package is `@wenathlan/saddle`. The GitHub Packages npm variant is `@iakadion/saddle`; consumers of that registry must configure npm for the repository owner scope before installing.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @wenathlan/saddle
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## desktop surface
|
|
12
|
+
|
|
13
|
+
Desktop integrations declare their packaging intent and inject native operations. Saddle does not start a window manager or select a desktop framework.
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { desktopadapter, desktopmanifest } from "@wenathlan/saddle";
|
|
17
|
+
|
|
18
|
+
const manifest = desktopmanifest({ name: "saddle-console", formats: ["appimage"] });
|
|
19
|
+
const adapter = desktopadapter({
|
|
20
|
+
handlers: {
|
|
21
|
+
status: async () => ({ ready: true }),
|
|
22
|
+
open: async (input) => ({ opened: input?.route ?? "/" })
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const status = await adapter.invoke("status");
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## mobile surface
|
|
30
|
+
|
|
31
|
+
Mobile integrations use the same adapter shape but declare mobile packaging formats and capabilities. Secure storage, screen navigation, permissions, and lifecycle events remain owned by the mobile host.
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
import { mobileadapter, mobilemanifest } from "@wenathlan/saddle";
|
|
35
|
+
|
|
36
|
+
const manifest = mobilemanifest({ name: "saddle-mobile", formats: ["apk"] });
|
|
37
|
+
const adapter = mobileadapter({
|
|
38
|
+
handlers: {
|
|
39
|
+
status: async () => ({ ready: true }),
|
|
40
|
+
invoke: async (input) => ({ accepted: Boolean(input) })
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const result = await adapter.invoke("invoke", { command: "sync" });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## n8n surface
|
|
48
|
+
|
|
49
|
+
The n8n contract is metadata plus a caller-owned execution handler. The node supports the engine trigger vocabulary and rejects actions that were not declared by the node.
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { n8nexecute, n8nnode } from "@wenathlan/saddle";
|
|
53
|
+
|
|
54
|
+
const node = n8nnode({
|
|
55
|
+
triggers: ["webhook", "schedule"],
|
|
56
|
+
actions: ["scrape", "extract"]
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const output = await n8nexecute(node, { command: "scrape", url: "https://example.com" }, async ({ input }) => {
|
|
60
|
+
return { action: input.action, url: input.url };
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Credentials, webhook verification, URL security, browser sessions, workflow storage, and n8n node registration must be supplied by the host application. This keeps the package usable in local, CI, container, desktop, mobile, and browser-worker contexts.
|
|
65
|
+
|
|
66
|
+
## operator controls
|
|
67
|
+
|
|
68
|
+
An operator surface can bind resource handlers without forcing a database or dashboard framework into the library. Every response carries a request id, resource, operation and success state; the optional audit callback receives the same serializable response.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import { controlsurface } from "@wenathlan/saddle";
|
|
72
|
+
|
|
73
|
+
const controls = controlsurface({
|
|
74
|
+
adapters: {
|
|
75
|
+
jobs: { list: async () => [{ id: "job1", status: "running" }] },
|
|
76
|
+
permissions: { check: async ({ scope }) => ({ allowed: scope === "read" }) }
|
|
77
|
+
},
|
|
78
|
+
audit: async (event) => console.log(event)
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const jobs = await controls.execute({ resource: "jobs", operation: "list" });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The same contract can be mounted behind any Web Request and Response server. The handler does not bind a framework, host, port, database or authentication scheme.
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
import { controlservice } from "@wenathlan/saddle";
|
|
88
|
+
|
|
89
|
+
const service = controlservice({
|
|
90
|
+
verify: async (token) => token === "caller-token" ? { subject: "operator" } : null,
|
|
91
|
+
adapters: { jobs: { list: async () => [{ id: "job1" }] } }
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## operations policies
|
|
96
|
+
|
|
97
|
+
Operational policies remain declarative. An existing metric collector can receive a bounded vocabulary, while retention, recovery and threat ownership are represented without starting background workers or making storage assumptions.
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import { backupplan, metricstore, operationsmetrics, retentionpolicy, threatmodel } from "@wenathlan/saddle";
|
|
101
|
+
|
|
102
|
+
const metrics = operationsmetrics({ collector: metricstore() });
|
|
103
|
+
metrics.record("runnerselection", 1, { runner: "primary" });
|
|
104
|
+
const retention = retentionpolicy({ days: 30, maxbytes: 500000000 });
|
|
105
|
+
const recovery = backupplan({ backup: async (input) => ({ saved: input }), restore: async (input) => ({ restored: input }) });
|
|
106
|
+
const security = threatmodel({ owner: "platform-team", controls: ["url validation", "audit log"] });
|
|
107
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* artifacts are serializable manifests with content checksums.
|
|
3
|
+
*/
|
|
4
|
+
export function artifactmanifest(input) {
|
|
5
|
+
return {
|
|
6
|
+
key: input.key,
|
|
7
|
+
sizebytes: input.sizebytes,
|
|
8
|
+
sha256: input.sha256,
|
|
9
|
+
contenttype: input.contenttype,
|
|
10
|
+
createdat: input.createdat,
|
|
11
|
+
metadata: { ...(input.metadata ?? {}) }
|
|
12
|
+
};
|
|
13
|
+
}
|
package/domain/jobs.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* job creation is kept small and side effect free.
|
|
3
|
+
*/
|
|
4
|
+
import { validationerror } from "../core/errors.js";
|
|
5
|
+
|
|
6
|
+
export const jobstatuses = Object.freeze(["queued", "preparing", "running", "syncing", "completed", "failed", "cancelled"]);
|
|
7
|
+
|
|
8
|
+
export function createjob(spec, ids, clock) {
|
|
9
|
+
if (!spec?.name || !spec.name.trim()) throw validationerror("job name cannot be empty");
|
|
10
|
+
return {
|
|
11
|
+
id: ids.next("job"),
|
|
12
|
+
name: spec.name,
|
|
13
|
+
input: spec.input,
|
|
14
|
+
priority: spec.priority ?? 0,
|
|
15
|
+
outputkey: spec.outputkey,
|
|
16
|
+
metadata: { ...(spec.metadata ?? {}) },
|
|
17
|
+
status: "queued",
|
|
18
|
+
createdat: clock.now()
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* providers declare capacity while scheduling remains a runtime decision.
|
|
3
|
+
*/
|
|
4
|
+
export const runnerstatuses = Object.freeze(["available", "busy", "offline"]);
|
|
5
|
+
|
|
6
|
+
export function runnercontext(job, workingset, signal) {
|
|
7
|
+
return { job, workingset, signal };
|
|
8
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runtime records describe temporary process space without claiming physical vram.
|
|
3
|
+
*/
|
|
4
|
+
export function workingset(jobid, location, resultpath, createdat = Date.now()) {
|
|
5
|
+
return { jobid, location, resultpath, createdat };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function syncresult(bytes, location) {
|
|
9
|
+
return { bytes, location };
|
|
10
|
+
}
|