@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,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* webhook receiver validates signatures before dispatch and drops duplicate delivery ids.
|
|
3
|
+
*/
|
|
4
|
+
import { webhookverify } from "./signature.js";
|
|
5
|
+
|
|
6
|
+
export function webhookreceiver(options = {}) {
|
|
7
|
+
if (typeof options.secret !== "string" || typeof options.handle !== "function") throw new TypeError("webhook receiver requires secret and handler");
|
|
8
|
+
const deliveries = new Set();
|
|
9
|
+
return {
|
|
10
|
+
async receive(input = {}) {
|
|
11
|
+
if (!webhookverify(input.body, input.signature, options.secret)) return { accepted: false, status: 401, reason: "invalid signature" };
|
|
12
|
+
const id = input.deliveryid ?? input.eventid;
|
|
13
|
+
if (id && deliveries.has(id)) return { accepted: false, status: 200, duplicate: true };
|
|
14
|
+
if (id) deliveries.add(id);
|
|
15
|
+
const result = await options.handle(input.event, input.body);
|
|
16
|
+
return { accepted: true, status: 202, deliveryid: id, result };
|
|
17
|
+
},
|
|
18
|
+
deliveries() { return [...deliveries]; }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* webhook signatures use hmac sha256 and keep secrets outside serialized events.
|
|
3
|
+
*/
|
|
4
|
+
import { constanttimeequal, hmacsha256 } from "../core/hash.js";
|
|
5
|
+
|
|
6
|
+
export function webhooksig(payload, secret) { return hmacsha256(typeof payload === "string" ? payload : JSON.stringify(payload), secret); }
|
|
7
|
+
export function webhookverify(payload, signature, secret) { return constanttimeequal(webhooksig(payload, secret), signature); }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workflow manifests describe the same job surface for different forge runtimes.
|
|
3
|
+
*/
|
|
4
|
+
export const forgeprofiles = Object.freeze(["github", "forgejo", "gitea", "woodpecker", "gitlab"]);
|
|
5
|
+
|
|
6
|
+
export function workflowmanifest(options = {}) {
|
|
7
|
+
if (!options.name || !options.command) throw new TypeError("workflow manifest requires name and command");
|
|
8
|
+
return {
|
|
9
|
+
name: options.name,
|
|
10
|
+
command: options.command,
|
|
11
|
+
trigger: options.trigger ?? ["manual", "dispatch"],
|
|
12
|
+
platforms: options.platforms ?? forgeprofiles,
|
|
13
|
+
environment: options.environment ?? {},
|
|
14
|
+
artifacts: options.artifacts ?? ["results/**"],
|
|
15
|
+
timeoutminutes: options.timeoutminutes ?? 30,
|
|
16
|
+
publicrunner: options.publicrunner ?? true
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function workflowinputs(manifest) { return { name: manifest.name, command: manifest.command, timeoutminutes: manifest.timeoutminutes, artifacts: manifest.artifacts.join(",") }; }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workflow registry stores generated manifests and templates for later dispatch.
|
|
3
|
+
*/
|
|
4
|
+
import { forgeprofiles } from "./manifest.js";
|
|
5
|
+
import { forgejoworkflow, giteaworkflow, githubworkflow, gitlabworkflow, woodpeckerworkflow } from "./templates.js";
|
|
6
|
+
|
|
7
|
+
export function workflowregistry() {
|
|
8
|
+
const manifests = new Map();
|
|
9
|
+
const renderers = { github: githubworkflow, forgejo: forgejoworkflow, gitea: giteaworkflow, woodpecker: woodpeckerworkflow, gitlab: gitlabworkflow };
|
|
10
|
+
return {
|
|
11
|
+
register(manifest) { manifests.set(manifest.name, manifest); return manifest; },
|
|
12
|
+
get(name) { return manifests.get(name) ?? null; },
|
|
13
|
+
render(name, forge) { if (!renderers[forge] || !forgeprofiles.includes(forge)) throw new TypeError(`unsupported forge: ${forge}`); const manifest = manifests.get(name); if (!manifest) throw new Error(`workflow not found: ${name}`); return renderers[forge](manifest); },
|
|
14
|
+
list() { return [...manifests.values()]; }
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workflow templates use explicit environment names and keep provider secrets external.
|
|
3
|
+
*/
|
|
4
|
+
import { workflowinputs } from "./manifest.js";
|
|
5
|
+
|
|
6
|
+
export function githubworkflow(manifest) {
|
|
7
|
+
const input = workflowinputs(manifest);
|
|
8
|
+
return `name: ${manifest.name}\non:\n workflow_dispatch:\n inputs:\n jobid:\n required: true\n type: string\n command:\n required: true\n type: string\n default: ${manifest.command}\n repository_dispatch:\n types: [saddle-job]\npermissions:\n contents: read\njobs:\n process:\n runs-on: ubuntu-latest\n timeout-minutes: ${input.timeoutminutes}\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 22\n - run: npm ci\n - run: \${{ github.event.inputs.command || '${manifest.command}' }}\n env:\n SBOT_JOB_ID: \${{ github.event.inputs.jobid || github.event.client_payload.jobid }}\n - uses: actions/upload-artifact@v4\n with:\n name: saddle-results\n path: ${manifest.artifacts.join("\n ")}\n if-no-files-found: ignore\n`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function forgejoworkflow(manifest) { return genericworkflow(manifest, "forgejo"); }
|
|
12
|
+
export function giteaworkflow(manifest) { return genericworkflow(manifest, "gitea"); }
|
|
13
|
+
export function woodpeckerworkflow(manifest) { return `when:\n - event: push\n - event: manual\nsteps:\n process:\n image: node:22\n commands:\n - npm ci\n - ${manifest.command}\n`;
|
|
14
|
+
}
|
|
15
|
+
export function gitlabworkflow(manifest) { return `stages:\n - process\nprocess:\n stage: process\n image: node:22\n script:\n - npm ci\n - ${manifest.command}\n artifacts:\n when: always\n paths:\n${manifest.artifacts.map((item) => ` - ${item}`).join("\n")}\n`; }
|
|
16
|
+
|
|
17
|
+
function genericworkflow(manifest, name) { return `name: ${manifest.name}\non:\n workflow_dispatch:\njobs:\n process:\n runs-on: ubuntu-latest\n timeout-minutes: ${manifest.timeoutminutes}\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 22\n - run: npm ci\n - run: ${manifest.command}\n`;
|
|
18
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workflow triggers normalize manual, event, schedule and retry starts without binding a forge.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const triggernames = Object.freeze(["manual", "dispatch", "webhook", "schedule", "retry", "heartbeat"]);
|
|
6
|
+
|
|
7
|
+
/** Validates and normalizes a trigger declaration. */
|
|
8
|
+
export function workflowtriggers(value = ["manual"]) {
|
|
9
|
+
const list = Array.isArray(value) ? value : [value];
|
|
10
|
+
if (list.length === 0 || list.some((name) => !triggernames.includes(name))) throw new TypeError("workflow trigger is unsupported");
|
|
11
|
+
return [...new Set(list)];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Matches a workflow trigger against an incoming event without executing it. */
|
|
15
|
+
export function triggermatch(manifest, event = {}) {
|
|
16
|
+
const triggers = workflowtriggers(manifest?.trigger);
|
|
17
|
+
const type = String(event.type ?? "manual");
|
|
18
|
+
if (!triggers.includes(type)) return { matched: false, type, reason: "trigger-not-declared" };
|
|
19
|
+
if (type === "schedule" && event.at !== undefined && Number(event.at) > Date.now()) return { matched: false, type, reason: "not-due" };
|
|
20
|
+
return { matched: true, type, requestid: event.requestid ?? `${manifest.name}/${type}/${event.id ?? Date.now()}` };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Keeps trigger declarations and produces deterministic matching results. */
|
|
24
|
+
export function triggerregistry() {
|
|
25
|
+
const values = new Map();
|
|
26
|
+
function register(manifest) { if (!manifest?.name) throw new TypeError("trigger manifest requires name"); const normalized = { ...manifest, trigger: workflowtriggers(manifest.trigger) }; values.set(normalized.name, normalized); return normalized; }
|
|
27
|
+
function get(name) { return values.get(name); }
|
|
28
|
+
function match(name, event) { const manifest = get(name); if (!manifest) throw new Error(`workflow not found: ${name}`); return triggermatch(manifest, event); }
|
|
29
|
+
function list() { return [...values.values()].map((manifest) => ({ ...manifest, trigger: [...manifest.trigger] })); }
|
|
30
|
+
return { register, get, match, list };
|
|
31
|
+
}
|