@huynle/hub 0.6.1 → 0.7.2
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/CHANGELOG.md +870 -0
- package/Dockerfile +33 -0
- package/README.md +1007 -66
- package/package.json +47 -2
- package/packages/claude-code/package.json +12 -0
- package/packages/claude-code/src/bridge.ts +79 -0
- package/packages/claude-code/src/daemon.ts +339 -0
- package/packages/claude-code/src/hook-input.ts +44 -0
- package/packages/claude-code/src/ids.ts +27 -0
- package/packages/claude-code/src/index.ts +18 -0
- package/packages/claude-code/src/launcher.ts +265 -0
- package/packages/claude-code/src/main.ts +127 -0
- package/packages/claude-code/src/mcp.ts +109 -0
- package/packages/claude-code/src/paths.ts +34 -0
- package/packages/claude-code/src/plugin.ts +99 -0
- package/packages/claude-code/src/references.ts +51 -0
- package/packages/claude-code/src/sessions.ts +600 -0
- package/packages/claude-code/src/tools.ts +66 -0
- package/packages/claude-code/src/wire.ts +71 -0
- package/packages/claude-code/src/wrapper.ts +156 -0
- package/packages/cli/dist/hub-cli.js +21647 -0
- package/packages/cli/package.json +11 -0
- package/packages/cli/src/acting.ts +70 -0
- package/packages/cli/src/args.ts +91 -0
- package/packages/cli/src/bootstrap/adapters/archive-stream.ts +276 -0
- package/packages/cli/src/bootstrap/adapters/claude/README.md +63 -0
- package/packages/cli/src/bootstrap/adapters/claude/adapter.ts +250 -0
- package/packages/cli/src/bootstrap/adapters/claude/index.ts +42 -0
- package/packages/cli/src/bootstrap/adapters/claude/node.ts +99 -0
- package/packages/cli/src/bootstrap/adapters/claude/openpgp.ts +221 -0
- package/packages/cli/src/bootstrap/adapters/claude/ports.ts +44 -0
- package/packages/cli/src/bootstrap/adapters/claude/source.ts +160 -0
- package/packages/cli/src/bootstrap/adapters/claude/verify.ts +165 -0
- package/packages/cli/src/bootstrap/adapters/codex/archive.ts +103 -0
- package/packages/cli/src/bootstrap/adapters/codex/index.ts +60 -0
- package/packages/cli/src/bootstrap/adapters/codex/plan.ts +129 -0
- package/packages/cli/src/bootstrap/adapters/codex/sigstore.ts +298 -0
- package/packages/cli/src/bootstrap/adapters/codex/source.ts +102 -0
- package/packages/cli/src/bootstrap/adapters/codex/verify.ts +298 -0
- package/packages/cli/src/bootstrap/adapters/inflate.ts +433 -0
- package/packages/cli/src/bootstrap/adapters/node-services/control.ts +392 -0
- package/packages/cli/src/bootstrap/adapters/node-services/index.ts +23 -0
- package/packages/cli/src/bootstrap/adapters/node-services/launch.ts +223 -0
- package/packages/cli/src/bootstrap/adapters/node-services/plan.ts +222 -0
- package/packages/cli/src/bootstrap/adapters/node-services/render.ts +183 -0
- package/packages/cli/src/bootstrap/adapters/node-services/validate.ts +197 -0
- package/packages/cli/src/bootstrap/adapters/opencode/README.md +204 -0
- package/packages/cli/src/bootstrap/adapters/opencode/apply.ts +205 -0
- package/packages/cli/src/bootstrap/adapters/opencode/archive.ts +91 -0
- package/packages/cli/src/bootstrap/adapters/opencode/config.ts +212 -0
- package/packages/cli/src/bootstrap/adapters/opencode/index.ts +81 -0
- package/packages/cli/src/bootstrap/adapters/opencode/plan.ts +266 -0
- package/packages/cli/src/bootstrap/adapters/opencode/verify.ts +208 -0
- package/packages/cli/src/bootstrap/adapters/readiness.ts +19 -0
- package/packages/cli/src/bootstrap/ansible/index.ts +2 -0
- package/packages/cli/src/bootstrap/ansible/render.ts +279 -0
- package/packages/cli/src/bootstrap/contracts/actions.ts +470 -0
- package/packages/cli/src/bootstrap/contracts/artifact.ts +323 -0
- package/packages/cli/src/bootstrap/contracts/canonical.ts +54 -0
- package/packages/cli/src/bootstrap/contracts/credentials.ts +108 -0
- package/packages/cli/src/bootstrap/contracts/discovery.ts +215 -0
- package/packages/cli/src/bootstrap/contracts/envelope.ts +143 -0
- package/packages/cli/src/bootstrap/contracts/exec.ts +74 -0
- package/packages/cli/src/bootstrap/contracts/harness.ts +202 -0
- package/packages/cli/src/bootstrap/contracts/index.ts +28 -0
- package/packages/cli/src/bootstrap/contracts/journal.ts +198 -0
- package/packages/cli/src/bootstrap/contracts/paths.ts +91 -0
- package/packages/cli/src/bootstrap/contracts/plan.ts +155 -0
- package/packages/cli/src/bootstrap/contracts/platform.ts +62 -0
- package/packages/cli/src/bootstrap/contracts/preview.ts +190 -0
- package/packages/cli/src/bootstrap/contracts/profile.ts +105 -0
- package/packages/cli/src/bootstrap/contracts/secrets.ts +52 -0
- package/packages/cli/src/bootstrap/contracts/validate.ts +212 -0
- package/packages/cli/src/bootstrap/discovery/discover.ts +146 -0
- package/packages/cli/src/bootstrap/discovery/executables.ts +134 -0
- package/packages/cli/src/bootstrap/discovery/host.ts +96 -0
- package/packages/cli/src/bootstrap/discovery/index.ts +6 -0
- package/packages/cli/src/bootstrap/discovery/node-probe.ts +97 -0
- package/packages/cli/src/bootstrap/discovery/probe.ts +41 -0
- package/packages/cli/src/bootstrap/discovery/util.ts +13 -0
- package/packages/cli/src/bootstrap/enrollment/enroll.ts +226 -0
- package/packages/cli/src/bootstrap/enrollment/index.ts +7 -0
- package/packages/cli/src/bootstrap/enrollment/policy.ts +54 -0
- package/packages/cli/src/bootstrap/enrollment/result.ts +63 -0
- package/packages/cli/src/bootstrap/enrollment/status.ts +119 -0
- package/packages/cli/src/bootstrap/enrollment/store.ts +173 -0
- package/packages/cli/src/bootstrap/preview/index.ts +5 -0
- package/packages/cli/src/bootstrap/preview/links.ts +31 -0
- package/packages/cli/src/bootstrap/preview/plan.ts +242 -0
- package/packages/cli/src/bootstrap/preview/preview.ts +104 -0
- package/packages/cli/src/bootstrap/preview/render.ts +30 -0
- package/packages/cli/src/bootstrap/profiles/audit.ts +87 -0
- package/packages/cli/src/bootstrap/profiles/catalog.ts +49 -0
- package/packages/cli/src/bootstrap/profiles/desired.ts +96 -0
- package/packages/cli/src/bootstrap/profiles/drift.ts +286 -0
- package/packages/cli/src/bootstrap/profiles/history.ts +263 -0
- package/packages/cli/src/bootstrap/profiles/index.ts +11 -0
- package/packages/cli/src/bootstrap/profiles/upgrade.ts +276 -0
- package/packages/cli/src/bootstrap/profiles/versions.ts +50 -0
- package/packages/cli/src/bootstrap/release/README.md +115 -0
- package/packages/cli/src/bootstrap/release/assets.ts +39 -0
- package/packages/cli/src/bootstrap/release/cli.ts +332 -0
- package/packages/cli/src/bootstrap/release/harness-pins.json +94 -0
- package/packages/cli/src/bootstrap/release/manifest.ts +118 -0
- package/packages/cli/src/bootstrap/release/pins.ts +192 -0
- package/packages/cli/src/bootstrap/release/text-assets.d.ts +9 -0
- package/packages/cli/src/bootstrap/release/trust-root.json +13 -0
- package/packages/cli/src/bootstrap/release/trust-root.ts +39 -0
- package/packages/cli/src/bootstrap/release/verify.ts +88 -0
- package/packages/cli/src/bootstrap/transaction/builtin.ts +312 -0
- package/packages/cli/src/bootstrap/transaction/engine.ts +994 -0
- package/packages/cli/src/bootstrap/transaction/executor.ts +112 -0
- package/packages/cli/src/bootstrap/transaction/fs.ts +194 -0
- package/packages/cli/src/bootstrap/transaction/index.ts +12 -0
- package/packages/cli/src/bootstrap/transaction/json-merge.ts +403 -0
- package/packages/cli/src/bootstrap/transaction/lock.ts +132 -0
- package/packages/cli/src/bootstrap/transaction/resolve.ts +48 -0
- package/packages/cli/src/bootstrap/transaction/result.ts +60 -0
- package/packages/cli/src/bootstrap/transaction/state.ts +88 -0
- package/packages/cli/src/bootstrap/transaction/store.ts +189 -0
- package/packages/cli/src/bootstrap/transaction/stream.ts +254 -0
- package/packages/cli/src/cli.ts +814 -0
- package/packages/cli/src/credentials.ts +75 -0
- package/packages/cli/src/decisions.ts +106 -0
- package/packages/cli/src/format.ts +185 -0
- package/packages/cli/src/grants.ts +105 -0
- package/packages/cli/src/http.ts +86 -0
- package/packages/cli/src/launches.ts +157 -0
- package/packages/cli/src/login.ts +160 -0
- package/packages/cli/src/main.ts +63 -0
- package/packages/cli/src/mcp.ts +879 -0
- package/packages/cli/src/messaging.ts +108 -0
- package/packages/cli/src/node.ts +279 -0
- package/packages/cli/src/openshell.ts +166 -0
- package/packages/cli/src/setup/commands.ts +809 -0
- package/packages/cli/src/setup/contributors.ts +171 -0
- package/packages/cli/src/setup/dispatch.ts +74 -0
- package/packages/cli/src/setup/launch-driver.ts +109 -0
- package/packages/cli/src/setup/runtime.ts +456 -0
- package/packages/cli/src/setup/spoke-probe.ts +120 -0
- package/packages/cli/src/setup/state.ts +244 -0
- package/packages/cli/src/setup/user-bin.ts +39 -0
- package/packages/cli/src/spoke-services.ts +109 -0
- package/packages/cli/src/watch.ts +28 -0
- package/packages/codex/package.json +12 -0
- package/packages/codex/src/app-server.ts +44 -0
- package/packages/codex/src/ids.ts +28 -0
- package/packages/codex/src/index.ts +11 -0
- package/packages/codex/src/jsonrpc.ts +168 -0
- package/packages/codex/src/main.ts +123 -0
- package/packages/codex/src/paths.ts +65 -0
- package/packages/codex/src/sessions.ts +578 -0
- package/packages/codex/src/spoke.ts +252 -0
- package/packages/codex/src/stdio.ts +101 -0
- package/packages/codex/src/websocket.ts +206 -0
- package/packages/codex/src/wrapper.ts +104 -0
- package/packages/node/package.json +12 -0
- package/packages/node/src/config.ts +184 -0
- package/packages/node/src/connection.ts +283 -0
- package/packages/node/src/daemon.ts +281 -0
- package/packages/node/src/git.ts +107 -0
- package/packages/node/src/harnesses.ts +95 -0
- package/packages/node/src/host.ts +319 -0
- package/packages/node/src/index.ts +17 -0
- package/packages/node/src/main.ts +11 -0
- package/packages/node/src/openshell.ts +73 -0
- package/packages/node/src/run.ts +106 -0
- package/packages/preset/package.json +12 -0
- package/packages/preset/presets/aider.json +8 -0
- package/packages/preset/presets/goose.json +8 -0
- package/packages/preset/src/index.ts +2 -0
- package/packages/preset/src/main.ts +195 -0
- package/packages/preset/src/presets.ts +87 -0
- package/packages/preset/src/spoke.ts +251 -0
- package/packages/preset/src/tmux.ts +160 -0
- package/packages/protocol/package.json +12 -0
- package/packages/protocol/src/harness-events.ts +207 -0
- package/packages/protocol/src/index.ts +1215 -0
- package/packages/protocol/src/launch.ts +172 -0
- package/packages/protocol/src/node.ts +409 -0
- package/packages/protocol/src/preset.ts +144 -0
- package/packages/protocol/src/webhook.ts +62 -0
- package/packages/protocol/src/wire.ts +54 -0
- package/packages/runtime-openshell/image/Dockerfile +36 -0
- package/packages/runtime-openshell/image/README.md +78 -0
- package/packages/runtime-openshell/image/build.ts +203 -0
- package/packages/runtime-openshell/image/entry.sh +163 -0
- package/packages/runtime-openshell/image/image.ts +119 -0
- package/packages/runtime-openshell/image/start.sh +24 -0
- package/packages/runtime-openshell/package.json +12 -0
- package/packages/runtime-openshell/src/cli.ts +584 -0
- package/packages/runtime-openshell/src/index.ts +6 -0
- package/packages/runtime-openshell/src/policy.ts +155 -0
- package/packages/runtime-openshell/src/profiles.ts +181 -0
- package/packages/runtime-openshell/src/runtime.ts +475 -0
- package/packages/runtime-sdk/package.json +12 -0
- package/packages/runtime-sdk/src/index.ts +87 -0
- package/packages/sdk/package.json +12 -0
- package/packages/sdk/src/address.ts +25 -0
- package/packages/sdk/src/client.ts +553 -0
- package/packages/sdk/src/envelope.ts +108 -0
- package/packages/sdk/src/errors.ts +107 -0
- package/packages/sdk/src/index.ts +10 -0
- package/packages/sdk/src/sse.ts +70 -0
- package/packages/sdk/src/types.ts +517 -0
- package/packages/sdk/src/webhook.ts +102 -0
- package/packages/spoke-sdk/package.json +12 -0
- package/packages/spoke-sdk/src/activity.ts +3 -0
- package/packages/spoke-sdk/src/connection.ts +295 -0
- package/packages/spoke-sdk/src/delivery.ts +119 -0
- package/packages/spoke-sdk/src/enroll.ts +101 -0
- package/packages/spoke-sdk/src/harness-hello.ts +79 -0
- package/packages/spoke-sdk/src/harness-spoke.ts +263 -0
- package/packages/spoke-sdk/src/index.ts +12 -0
- package/packages/spoke-sdk/src/prompt-refs.ts +154 -0
- package/packages/spoke-sdk/src/prompt-source.ts +8 -0
- package/packages/spoke-sdk/src/session-ids.ts +49 -0
- package/packages/spoke-sdk/src/session-tokens.ts +78 -0
- package/scripts/hub-claude-code.service +34 -0
- package/scripts/hub-codex-spoke.service +34 -0
- package/scripts/hub-node.service +35 -0
- package/scripts/io.github.huynle.hub-claude-code.plist +53 -0
- package/scripts/io.github.huynle.hub-codex-spoke.plist +53 -0
- package/scripts/io.github.huynle.hub-node.plist +52 -0
- package/src/central/actions.ts +233 -0
- package/src/central/agent/acting-routes.ts +133 -0
- package/src/central/agent/acting-switch.ts +93 -0
- package/src/central/agent/acting.ts +599 -0
- package/src/central/agent/actor.ts +193 -0
- package/src/central/agent/app-tokens.ts +152 -0
- package/src/central/agent/archive-inactive.ts +66 -0
- package/src/central/agent/audit.ts +185 -0
- package/src/central/agent/decisions.ts +530 -0
- package/src/central/agent/delivery.ts +678 -0
- package/src/central/agent/directory-routes.ts +328 -0
- package/src/central/agent/directory.ts +473 -0
- package/src/central/agent/envelope.ts +51 -0
- package/src/central/agent/errors.ts +74 -0
- package/src/central/agent/escalation.ts +219 -0
- package/src/central/agent/event-cursor.ts +49 -0
- package/src/central/agent/event-writers.ts +181 -0
- package/src/central/agent/events-routes.ts +148 -0
- package/src/central/agent/events.ts +277 -0
- package/src/central/agent/grant-requests.ts +221 -0
- package/src/central/agent/grant-routes.ts +70 -0
- package/src/central/agent/grants.ts +235 -0
- package/src/central/agent/launch-engine.ts +1025 -0
- package/src/central/agent/launch-routes.ts +252 -0
- package/src/central/agent/launch-spec.ts +289 -0
- package/src/central/agent/launch-store.ts +264 -0
- package/src/central/agent/launch-tokens.ts +206 -0
- package/src/central/agent/message-limits.ts +123 -0
- package/src/central/agent/message-routes.ts +385 -0
- package/src/central/agent/messages.ts +442 -0
- package/src/central/agent/names.ts +86 -0
- package/src/central/agent/placement.ts +419 -0
- package/src/central/agent/policy.ts +378 -0
- package/src/central/agent/read-routes.ts +385 -0
- package/src/central/agent/routes.ts +281 -0
- package/src/central/agent/session-tokens.ts +227 -0
- package/src/central/agent/versioned.ts +26 -0
- package/src/central/agent/watch.ts +112 -0
- package/src/central/agent/webhook-routes.ts +86 -0
- package/src/central/agent/webhooks.ts +493 -0
- package/src/central/agent-data.ts +177 -0
- package/src/central/auth.ts +350 -121
- package/src/central/backend/file-state.ts +240 -0
- package/src/central/backend/index.ts +116 -0
- package/src/central/backend/memory-bus.ts +93 -0
- package/src/central/backend/memory-catalog.ts +90 -0
- package/src/central/backend/memory-counters.ts +102 -0
- package/src/central/backend/memory-events.ts +45 -0
- package/src/central/backend/memory-kv.ts +38 -0
- package/src/central/backend/memory-state.ts +77 -0
- package/src/central/backend/postgres-migrate.ts +100 -0
- package/src/central/backend/postgres-state.ts +197 -0
- package/src/central/backend/types.ts +246 -0
- package/src/central/backend/valkey-bus.ts +425 -0
- package/src/central/backend/valkey-catalog.ts +190 -0
- package/src/central/backend/valkey-client.ts +36 -0
- package/src/central/backend/valkey-counters.ts +38 -0
- package/src/central/backend/valkey-events.ts +168 -0
- package/src/central/backend/valkey-kv.ts +54 -0
- package/src/central/backend/valkey-locks.ts +92 -0
- package/src/central/cluster/directory.ts +141 -0
- package/src/central/cluster/feed.ts +84 -0
- package/src/central/cluster/leader.ts +232 -0
- package/src/central/cluster/live.ts +151 -0
- package/src/central/cluster/recommend.ts +59 -0
- package/src/central/cluster/rpc.ts +150 -0
- package/src/central/cluster/view.ts +232 -0
- package/src/central/config.ts +197 -3
- package/src/central/done.ts +96 -0
- package/src/central/events-log.ts +100 -0
- package/src/central/http.ts +1149 -170
- package/src/central/import.ts +318 -0
- package/src/central/main.ts +211 -12
- package/src/central/mcp-http.ts +116 -0
- package/src/central/nodes.ts +314 -0
- package/src/central/oidc.ts +34 -14
- package/src/central/push-target.ts +118 -18
- package/src/central/push.ts +256 -82
- package/src/central/registry.ts +902 -92
- package/src/central/say-stream.ts +128 -0
- package/src/central/session-token-push.ts +233 -0
- package/src/central/speech/openai.ts +21 -14
- package/src/central/speech/openrouter.ts +27 -10
- package/src/central/speech/provider.ts +54 -0
- package/src/central/speech/service.ts +22 -7
- package/src/central/spoke-ws.ts +522 -44
- package/src/central/store-shared.ts +518 -0
- package/src/central/store.ts +625 -189
- package/src/central/token-keys.ts +165 -0
- package/src/config.ts +118 -7
- package/src/index.ts +195 -43
- package/src/protocol.ts +3 -650
- package/src/rpc.ts +27 -2
- package/src/spoke/client.ts +3 -272
- package/src/spoke/conversation.ts +16 -12
- package/src/spoke/deliver.ts +75 -0
- package/src/spoke/enroll.ts +2 -79
- package/src/spoke/env-inject.ts +160 -0
- package/src/spoke/hub-rpc.ts +115 -0
- package/src/spoke/hub-tool.ts +409 -0
- package/src/spoke/launch-source.ts +22 -0
- package/src/spoke/launchers.ts +174 -0
- package/src/spoke/locations.ts +3 -2
- package/src/spoke/node-roots.ts +61 -0
- package/src/spoke/prompt-refs.ts +116 -0
- package/src/spoke/session-tokens.ts +2 -0
- package/src/spoke/spoke.ts +465 -28
- package/src/spoke/taskboard.ts +243 -0
- package/src/spoke/timeline.ts +184 -6
- package/src/spoke/tracker.ts +79 -4
- package/src/status.ts +203 -10
- package/src/tui-commands.ts +13 -6
- package/src/tui-messaging.ts +93 -0
- package/src/tui.ts +15 -0
- package/web/dist/apple-touch-icon.png +0 -0
- package/web/dist/assets/highlight-y4rJE6Tt.js +5 -0
- package/web/dist/assets/index-DWnMhXk9.js +20 -0
- package/web/dist/assets/index-DmUXEnw9.css +1 -0
- package/web/dist/assets/ort-wasm-simd-threaded-CxTQ5xH-.wasm +0 -0
- package/web/dist/assets/silero-worker-C8-8vLVS.js +11 -0
- package/web/dist/assets/silero_vad-Dt9-bTDn.onnx +0 -0
- package/web/dist/favicon.ico +0 -0
- package/web/dist/icon-192.png +0 -0
- package/web/dist/icon-512.png +0 -0
- package/web/dist/icon-maskable-512.png +0 -0
- package/web/dist/icon.svg +5 -5
- package/web/dist/index.html +17 -4
- package/web/dist/manifest.webmanifest +2 -2
- package/web/dist/sw.js +79 -15
- package/web/dist/assets/index-DClBvfLL.css +0 -1
- package/web/dist/assets/index-Djo_yF0a.js +0 -12
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// The per-action executor interface. The engine (engine.ts) owns everything transactional: the lock, preview
|
|
2
|
+
// revalidation, consent, path resolution and symlink refusal, the journal, backups, staging, atomic replacement,
|
|
3
|
+
// rollback and resume. An executor only says, for one action kind:
|
|
4
|
+
//
|
|
5
|
+
// plan which journal class the step is and which path it changes (pure, from the action alone);
|
|
6
|
+
// check whether the step may run on this host now (read-only: preconditions, pins, versions);
|
|
7
|
+
// change what the target should become (read-only: bytes for a file, a symlink target, a directory tree,
|
|
8
|
+
// or an `effect` for steps with no file of their own to stage: enrollment, verification, activation,
|
|
9
|
+
// launch). Large content (a harness, hubctl) is never passed as bytes: it is streamed into the step's
|
|
10
|
+
// StagingArea (stream.ts) and named as a StagedFile, which the engine moves or copies into place;
|
|
11
|
+
// undo how to reverse a done effect that has no backup (clear a stored credential, unload a service).
|
|
12
|
+
//
|
|
13
|
+
// Executors never write files themselves (except an effect, through its own typed client such as the
|
|
14
|
+
// CredentialStore), never receive a shell, and never see the journal's internals. Adapter tasks supply the
|
|
15
|
+
// harness, MCP-registration, service-activation, enrollment and verification executors through this interface.
|
|
16
|
+
import type { Action, ActionKind, EntryClass, EvidenceResult, ExecutionContext, FileState, PathRef, RootMapping } from "../contracts/index.ts"
|
|
17
|
+
import type { TxFs } from "./fs.ts"
|
|
18
|
+
import type { StagedFile, StagingArea } from "./stream.ts"
|
|
19
|
+
import type { ResolveOptions } from "./resolve.ts"
|
|
20
|
+
import type { TxResult } from "./result.ts"
|
|
21
|
+
|
|
22
|
+
export type ActionOf<K extends ActionKind> = Extract<Action, { kind: K }>
|
|
23
|
+
|
|
24
|
+
/** The host apply runs on (computed fresh by the caller: hostFingerprint() and targetOf()). */
|
|
25
|
+
export interface HostInfo {
|
|
26
|
+
readonly fingerprint: string
|
|
27
|
+
readonly target: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* One file of a directory tree an executor wants staged (a harness unpacked from its verified archive): small
|
|
32
|
+
* content as bytes, large content as a file of the step's staging area.
|
|
33
|
+
*/
|
|
34
|
+
export type TreeFile = {
|
|
35
|
+
/** Relative, "/"-separated, inside the tree (isSafeRel). */
|
|
36
|
+
readonly rel: string
|
|
37
|
+
readonly mode: number
|
|
38
|
+
} & ({ readonly bytes: Uint8Array } | { readonly staged: StagedFile })
|
|
39
|
+
|
|
40
|
+
export type Change =
|
|
41
|
+
/**
|
|
42
|
+
* Write these bytes (or this staged file: one the step's staging area wrote is moved, any other is copied, its
|
|
43
|
+
* digest checked while it is copied). `refuse`: an existing different file is a conflict. `backup`: it is backed
|
|
44
|
+
* up and replaced.
|
|
45
|
+
*/
|
|
46
|
+
| ({ readonly type: "file"; readonly mode: number; readonly replace: "refuse" | "backup" } & ({ readonly bytes: Uint8Array } | { readonly staged: StagedFile }))
|
|
47
|
+
| { readonly type: "directory"; readonly mode: number }
|
|
48
|
+
| { readonly type: "symlink"; readonly target: string; readonly replace: "refuse" | "backup" }
|
|
49
|
+
/** A fresh, immutable tree (a versioned install root). An existing different tree is a conflict. */
|
|
50
|
+
| { readonly type: "tree"; readonly files: readonly TreeFile[]; readonly mode: number }
|
|
51
|
+
/**
|
|
52
|
+
* v1.1: remove the target file (a service unit of a removed component). The engine backs it up, journals it and
|
|
53
|
+
* unlinks it; a rollback restores the backup. A missing target is skipped.
|
|
54
|
+
*/
|
|
55
|
+
| { readonly type: "remove" }
|
|
56
|
+
/** A step without a file to stage. The engine journals the target's state before and after `perform`. */
|
|
57
|
+
| { readonly type: "effect"; readonly perform: () => Promise<TxResult<void>> }
|
|
58
|
+
/** Already exactly as wanted: journaled as skipped. */
|
|
59
|
+
| { readonly type: "none"; readonly reason: string }
|
|
60
|
+
|
|
61
|
+
export interface StepPlan {
|
|
62
|
+
readonly class: EntryClass
|
|
63
|
+
/** The path the step changes; required for every class except external. */
|
|
64
|
+
readonly path?: PathRef
|
|
65
|
+
readonly resolve?: ResolveOptions
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface StepContext {
|
|
69
|
+
readonly fs: TxFs
|
|
70
|
+
readonly roots: readonly RootMapping[]
|
|
71
|
+
/** v1.1: where apply runs; paths resolve for it. */
|
|
72
|
+
readonly context: ExecutionContext
|
|
73
|
+
readonly host: HostInfo
|
|
74
|
+
readonly now: () => number
|
|
75
|
+
/** The resolved, symlink-checked absolute path of the step's target (when it has one). */
|
|
76
|
+
readonly target?: string
|
|
77
|
+
/** The target's state now. */
|
|
78
|
+
readonly current: FileState
|
|
79
|
+
/**
|
|
80
|
+
* The target's state before this transaction touched it: `current`, unless an earlier step of this transaction
|
|
81
|
+
* changed the same path and left it exactly so. Preconditions are checked against this.
|
|
82
|
+
*/
|
|
83
|
+
readonly original: FileState
|
|
84
|
+
/** Resolves another PathRef with the same symlink rules. */
|
|
85
|
+
readonly resolve: (ref: PathRef, options?: ResolveOptions) => TxResult<string>
|
|
86
|
+
/** Records upstream evidence for the apply report (never into the journal). */
|
|
87
|
+
readonly recordEvidence: (results: readonly EvidenceResult[]) => void
|
|
88
|
+
/** The step's private scratch space for large downloads and what they unpack to (removed after the step). */
|
|
89
|
+
readonly staging: StagingArea
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface UndoContext {
|
|
93
|
+
readonly fs: TxFs
|
|
94
|
+
readonly roots: readonly RootMapping[]
|
|
95
|
+
readonly context: ExecutionContext
|
|
96
|
+
readonly host: HostInfo
|
|
97
|
+
readonly now: () => number
|
|
98
|
+
readonly target?: string
|
|
99
|
+
/** Why the engine undoes: a credential to clear, or a done external step to reverse. */
|
|
100
|
+
readonly reason: "clear-credential" | "revert-external"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ActionExecutor<K extends ActionKind = ActionKind> {
|
|
104
|
+
readonly kind: K
|
|
105
|
+
plan(action: ActionOf<K>): StepPlan
|
|
106
|
+
check?(action: ActionOf<K>, ctx: StepContext): Promise<TxResult<void>>
|
|
107
|
+
change(action: ActionOf<K>, ctx: StepContext): Promise<TxResult<Change>>
|
|
108
|
+
/** Required for class credential (clear it; a stale credential is never restored). Optional for external. */
|
|
109
|
+
undo?(action: ActionOf<K>, ctx: UndoContext): Promise<TxResult<void>>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type ExecutorMap = { readonly [K in ActionKind]?: ActionExecutor<K> }
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// The filesystem operations the applier uses, and nothing more: no exec, no spawn, no network. Everything the
|
|
2
|
+
// transaction layer does to disk goes through a TxFs, so tests can inject faults (ENOSPC, a kill at any step
|
|
3
|
+
// boundary) and the engine stays a pure function of what this interface reports.
|
|
4
|
+
//
|
|
5
|
+
// Writes are only ever "create a new file exclusively" (O_CREAT|O_EXCL|O_NOFOLLOW, then fsync); replacing a file is
|
|
6
|
+
// always stage-then-rename, so a reader (or a crash) sees the old file or the new one, never a torn mix. Large files
|
|
7
|
+
// (a harness binary, hubctl itself) are written and read in chunks (createExclusive, readRange), never whole.
|
|
8
|
+
import {
|
|
9
|
+
chmodSync,
|
|
10
|
+
closeSync,
|
|
11
|
+
constants,
|
|
12
|
+
fchmodSync,
|
|
13
|
+
fsyncSync,
|
|
14
|
+
linkSync,
|
|
15
|
+
lstatSync,
|
|
16
|
+
mkdirSync,
|
|
17
|
+
openSync,
|
|
18
|
+
readdirSync,
|
|
19
|
+
readFileSync,
|
|
20
|
+
readlinkSync,
|
|
21
|
+
readSync,
|
|
22
|
+
renameSync,
|
|
23
|
+
rmdirSync,
|
|
24
|
+
symlinkSync,
|
|
25
|
+
unlinkSync,
|
|
26
|
+
writeSync,
|
|
27
|
+
} from "node:fs"
|
|
28
|
+
import type { EntryKind } from "../contracts/index.ts"
|
|
29
|
+
|
|
30
|
+
export interface EntryInfo {
|
|
31
|
+
readonly kind: EntryKind
|
|
32
|
+
/** Permission bits (0..0o7777). */
|
|
33
|
+
readonly mode: number
|
|
34
|
+
readonly size: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A file being created exclusively, written in chunks (createExclusive). */
|
|
38
|
+
export interface TxWriter {
|
|
39
|
+
write(chunk: Uint8Array): void
|
|
40
|
+
/** Sets the mode, fsyncs and closes: the file is complete. */
|
|
41
|
+
close(): void
|
|
42
|
+
/** Closes and removes the incomplete file. */
|
|
43
|
+
abort(): void
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface TxFs {
|
|
47
|
+
/** Does not follow symlinks; undefined when nothing is there. */
|
|
48
|
+
lstat(path: string): EntryInfo | undefined
|
|
49
|
+
readFile(path: string): Uint8Array
|
|
50
|
+
/** At most `length` bytes from `offset` (fewer at the end of the file). Does not follow a final symlink. */
|
|
51
|
+
readRange(path: string, offset: number, length: number): Uint8Array
|
|
52
|
+
readlink(path: string): string
|
|
53
|
+
readdir(path: string): string[]
|
|
54
|
+
/** Creates a new file (fails if anything exists there, a symlink included), writes it fully, sets `mode`, fsyncs. */
|
|
55
|
+
writeExclusive(path: string, data: Uint8Array | string, mode: number): void
|
|
56
|
+
/** Like writeExclusive, for content that arrives in chunks: the file is created now and completed by close(). */
|
|
57
|
+
createExclusive(path: string, mode: number): TxWriter
|
|
58
|
+
/** Atomic replace of `to`'s directory entry. */
|
|
59
|
+
rename(from: string, to: string): void
|
|
60
|
+
/** Hard link; fails if `to` exists (an atomic create-if-absent of complete content). */
|
|
61
|
+
link(from: string, to: string): void
|
|
62
|
+
unlink(path: string): void
|
|
63
|
+
/** One level; fails if it exists. */
|
|
64
|
+
mkdir(path: string, mode: number): void
|
|
65
|
+
/** Creates every missing level with `mode`. */
|
|
66
|
+
mkdirp(path: string, mode: number): void
|
|
67
|
+
/** Fails if not empty. */
|
|
68
|
+
rmdir(path: string): void
|
|
69
|
+
symlink(target: string, path: string): void
|
|
70
|
+
chmod(path: string, mode: number): void
|
|
71
|
+
/** Best effort: makes a rename in `path` durable. */
|
|
72
|
+
fsyncDir(path: string): void
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function kindOf(mode: number): EntryKind {
|
|
76
|
+
switch (mode & constants.S_IFMT) {
|
|
77
|
+
case constants.S_IFREG:
|
|
78
|
+
return "file"
|
|
79
|
+
case constants.S_IFDIR:
|
|
80
|
+
return "directory"
|
|
81
|
+
case constants.S_IFLNK:
|
|
82
|
+
return "symlink"
|
|
83
|
+
default:
|
|
84
|
+
return "other"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const errno = (error: unknown) => (error as NodeJS.ErrnoException | undefined)?.code
|
|
89
|
+
|
|
90
|
+
export const nodeFs: TxFs = {
|
|
91
|
+
lstat(path) {
|
|
92
|
+
try {
|
|
93
|
+
const s = lstatSync(path)
|
|
94
|
+
return { kind: kindOf(s.mode), mode: s.mode & 0o7777, size: s.size }
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (errno(error) === "ENOENT" || errno(error) === "ENOTDIR") return undefined
|
|
97
|
+
throw error
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
readFile: (path) => new Uint8Array(readFileSync(path)),
|
|
101
|
+
readRange(path, offset, length) {
|
|
102
|
+
const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW)
|
|
103
|
+
try {
|
|
104
|
+
const out = new Uint8Array(length)
|
|
105
|
+
let got = 0
|
|
106
|
+
while (got < length) {
|
|
107
|
+
const n = readSync(fd, out, got, length - got, offset + got)
|
|
108
|
+
if (n === 0) break
|
|
109
|
+
got += n
|
|
110
|
+
}
|
|
111
|
+
return got === length ? out : out.subarray(0, got)
|
|
112
|
+
} finally {
|
|
113
|
+
closeSync(fd)
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
readlink: (path) => readlinkSync(path),
|
|
117
|
+
readdir: (path) => readdirSync(path),
|
|
118
|
+
writeExclusive(path, data, mode) {
|
|
119
|
+
const fd = openSync(path, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, mode)
|
|
120
|
+
let ok = false
|
|
121
|
+
try {
|
|
122
|
+
const bytes = typeof data === "string" ? Buffer.from(data, "utf8") : data
|
|
123
|
+
let offset = 0
|
|
124
|
+
while (offset < bytes.byteLength) offset += writeSync(fd, bytes, offset, bytes.byteLength - offset)
|
|
125
|
+
fchmodSync(fd, mode)
|
|
126
|
+
fsyncSync(fd)
|
|
127
|
+
ok = true
|
|
128
|
+
} finally {
|
|
129
|
+
closeSync(fd)
|
|
130
|
+
if (!ok) {
|
|
131
|
+
try {
|
|
132
|
+
unlinkSync(path)
|
|
133
|
+
} catch {}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
createExclusive(path, mode) {
|
|
138
|
+
const fd = openSync(path, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, mode)
|
|
139
|
+
let open = true
|
|
140
|
+
const finish = (complete: boolean) => {
|
|
141
|
+
if (!open) return
|
|
142
|
+
open = false
|
|
143
|
+
try {
|
|
144
|
+
if (complete) {
|
|
145
|
+
fchmodSync(fd, mode)
|
|
146
|
+
fsyncSync(fd)
|
|
147
|
+
}
|
|
148
|
+
} finally {
|
|
149
|
+
closeSync(fd)
|
|
150
|
+
if (!complete) {
|
|
151
|
+
try {
|
|
152
|
+
unlinkSync(path)
|
|
153
|
+
} catch {}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
write(chunk) {
|
|
159
|
+
if (!open) throw new Error(`${path} is already closed`)
|
|
160
|
+
let offset = 0
|
|
161
|
+
while (offset < chunk.byteLength) offset += writeSync(fd, chunk, offset, chunk.byteLength - offset)
|
|
162
|
+
},
|
|
163
|
+
close() {
|
|
164
|
+
try {
|
|
165
|
+
finish(true)
|
|
166
|
+
} catch (error) {
|
|
167
|
+
try {
|
|
168
|
+
unlinkSync(path)
|
|
169
|
+
} catch {}
|
|
170
|
+
throw error
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
abort: () => finish(false),
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
rename: (from, to) => renameSync(from, to),
|
|
177
|
+
link: (from, to) => linkSync(from, to),
|
|
178
|
+
unlink: (path) => unlinkSync(path),
|
|
179
|
+
mkdir: (path, mode) => mkdirSync(path, { mode }),
|
|
180
|
+
mkdirp: (path, mode) => void mkdirSync(path, { recursive: true, mode }),
|
|
181
|
+
rmdir: (path) => rmdirSync(path),
|
|
182
|
+
symlink: (target, path) => symlinkSync(target, path),
|
|
183
|
+
chmod: (path, mode) => chmodSync(path, mode),
|
|
184
|
+
fsyncDir(path) {
|
|
185
|
+
try {
|
|
186
|
+
const fd = openSync(path, constants.O_RDONLY)
|
|
187
|
+
try {
|
|
188
|
+
fsyncSync(fd)
|
|
189
|
+
} finally {
|
|
190
|
+
closeSync(fd)
|
|
191
|
+
}
|
|
192
|
+
} catch {}
|
|
193
|
+
},
|
|
194
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// The transactional local applier for bootstrap setup plans (see engine.ts for the guarantees and executor.ts for
|
|
2
|
+
// the per-action executor interface adapters implement). Built on the frozen v1 contracts in ../contracts.
|
|
3
|
+
export * from "./result.ts"
|
|
4
|
+
export * from "./fs.ts"
|
|
5
|
+
export * from "./state.ts"
|
|
6
|
+
export * from "./resolve.ts"
|
|
7
|
+
export * from "./lock.ts"
|
|
8
|
+
export * from "./store.ts"
|
|
9
|
+
export * from "./json-merge.ts"
|
|
10
|
+
export * from "./executor.ts"
|
|
11
|
+
export * from "./builtin.ts"
|
|
12
|
+
export * from "./engine.ts"
|