harnery 0.40.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commander.js +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +34 -20
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +113 -12
- package/dist/commands/browse-ai.d.ts.map +1 -1
- package/dist/commands/browse-ai.js +4 -0
- package/dist/commands/browse.d.ts +164 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +388 -45
- package/dist/commands/checkpoint.js +8 -12
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +52 -0
- package/dist/commands/deinit.d.ts +1 -1
- package/dist/commands/deinit.d.ts.map +1 -1
- package/dist/commands/deinit.js +8 -4
- package/dist/commands/disk.d.ts +6 -0
- package/dist/commands/disk.d.ts.map +1 -0
- package/dist/commands/disk.js +80 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +2 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +58 -3
- package/dist/commands/fetch.d.ts.map +1 -1
- package/dist/commands/fetch.js +10 -0
- package/dist/commands/files.d.ts +16 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -2
- package/dist/commands/init.d.ts +21 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +271 -109
- package/dist/commands/instructions.js +5 -4
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +2 -1
- package/dist/commands/rm.d.ts +4 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +87 -0
- package/dist/commands/servers.d.ts +7 -0
- package/dist/commands/servers.d.ts.map +1 -0
- package/dist/commands/servers.js +394 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +82 -2
- package/dist/commands/webview.d.ts +4 -0
- package/dist/commands/webview.d.ts.map +1 -0
- package/dist/commands/webview.js +94 -0
- package/dist/core/adapter.d.ts +50 -1
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +59 -1
- package/dist/core/adapters/attestation.d.ts.map +1 -1
- package/dist/core/adapters/attestation.js +3 -2
- package/dist/core/adapters/profiles.d.ts +29 -3
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +53 -1
- package/dist/core/adapters/registry.d.ts.map +1 -1
- package/dist/core/adapters/registry.js +33 -0
- package/dist/core/adapters/types.d.ts +9 -0
- package/dist/core/adapters/types.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.d.ts +1 -1
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -7
- package/dist/core/agents/cli.js +25 -16
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +7 -2
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +2 -0
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +3 -9
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +2 -8
- package/dist/core/agents/rules/stop-hook.d.ts +2 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +1 -1
- package/dist/core/agents/session-events.d.ts +6 -2
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +105 -26
- package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -1
- package/dist/core/agents/session-finalization-state-v3.js +2 -1
- package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
- package/dist/core/agents/session-finalizer-v3.js +5 -4
- package/dist/core/agents/session-name-display.d.ts +28 -0
- package/dist/core/agents/session-name-display.d.ts.map +1 -1
- package/dist/core/agents/session-name-display.js +43 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.d.ts +10 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +21 -2
- package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +61 -0
- package/dist/core/agents/state/live-coordination-writer.js +3 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +2 -10
- package/dist/core/agents/turn-changes.d.ts +17 -0
- package/dist/core/agents/turn-changes.d.ts.map +1 -0
- package/dist/core/agents/turn-changes.js +76 -0
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +11 -3
- package/dist/core/artifacts/index.d.ts +140 -4
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +526 -52
- package/dist/core/config.d.ts +73 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +142 -3
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +2 -1
- package/dist/core/conversations/archive.d.ts.map +1 -1
- package/dist/core/conversations/archive.js +4 -3
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +5 -4
- package/dist/core/events/legacy-storage/canary.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/canary.js +5 -4
- package/dist/core/events/legacy-storage/compression.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/compression.js +2 -1
- package/dist/core/events/v3/adapter-id.d.ts +10 -2
- package/dist/core/events/v3/adapter-id.d.ts.map +1 -1
- package/dist/core/events/v3/adapter-id.js +16 -0
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +2 -1
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
- package/dist/core/events/v3/bootstrap.js +5 -4
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +17 -0
- package/dist/core/events/v3/contract.d.ts +5 -0
- package/dist/core/events/v3/contract.d.ts.map +1 -1
- package/dist/core/events/v3/contract.js +7 -1
- package/dist/core/events/v3/control-witness.d.ts.map +1 -1
- package/dist/core/events/v3/control-witness.js +3 -2
- package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -1
- package/dist/core/events/v3/coordination-transaction-recovery.js +4 -3
- package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -1
- package/dist/core/events/v3/fingerprint-keys.js +7 -6
- package/dist/core/events/v3/generated.d.ts +1 -1
- package/dist/core/events/v3/generated.js +1 -1
- package/dist/core/events/v3/live-feed.d.ts.map +1 -1
- package/dist/core/events/v3/live-feed.js +5 -4
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/command-recorder.js +8 -7
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +8 -7
- package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -1
- package/dist/core/events/v3/producers/diagnostic-summaries.js +5 -4
- package/dist/core/events/v3/producers/hook-base.d.ts +1 -0
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +10 -4
- package/dist/core/events/v3/producers/hook.d.ts +2 -0
- package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook.js +16 -1
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +16 -6
- package/dist/core/events/v3/producers/recorder.d.ts +2 -4
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +125 -101
- package/dist/core/events/v3/projection.js +9 -1
- package/dist/core/events/v3/recovery.d.ts.map +1 -1
- package/dist/core/events/v3/recovery.js +4 -3
- package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/inventory.js +2 -1
- package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/maintenance.js +4 -3
- package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-reader.js +4 -3
- package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-writer.js +6 -5
- package/dist/core/events/v3/tool-line-changes.d.ts +8 -0
- package/dist/core/events/v3/tool-line-changes.d.ts.map +1 -0
- package/dist/core/events/v3/tool-line-changes.js +135 -0
- package/dist/core/events/v3/writer.d.ts.map +1 -1
- package/dist/core/events/v3/writer.js +8 -7
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +6 -5
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +12 -11
- package/dist/core/governor/state.d.ts.map +1 -1
- package/dist/core/governor/state.js +7 -6
- package/dist/core/guard/evaluator.js +1 -0
- package/dist/core/guard/storage.d.ts.map +1 -1
- package/dist/core/guard/storage.js +3 -2
- package/dist/core/hooks/adapter/behaviors/claude-code.d.ts +4 -0
- package/dist/core/hooks/adapter/behaviors/claude-code.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/claude-code.js +31 -0
- package/dist/core/hooks/adapter/behaviors/codex.d.ts +15 -0
- package/dist/core/hooks/adapter/behaviors/codex.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/codex.js +82 -0
- package/dist/core/hooks/adapter/behaviors/cursor.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/cursor.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/cursor.js +35 -0
- package/dist/core/hooks/adapter/behaviors/index.d.ts +11 -0
- package/dist/core/hooks/adapter/behaviors/index.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/index.js +20 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.js +34 -0
- package/dist/core/hooks/adapter/behaviors/opencode.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/opencode.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/opencode.js +34 -0
- package/dist/core/hooks/adapter/behaviors/types.d.ts +89 -0
- package/dist/core/hooks/adapter/behaviors/types.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/types.js +1 -0
- package/dist/core/hooks/adapter/detect.d.ts +1 -1
- package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
- package/dist/core/hooks/adapter/detect.js +2 -3
- package/dist/core/hooks/adapter/events.d.ts +46 -1
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +36 -0
- package/dist/core/hooks/adapter/opencode-plugin.d.ts +60 -0
- package/dist/core/hooks/adapter/opencode-plugin.d.ts.map +1 -0
- package/dist/core/hooks/adapter/opencode-plugin.js +129 -0
- package/dist/core/hooks/adapter/output.d.ts +7 -0
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +8 -1
- package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
- package/dist/core/hooks/adapter/wiring.d.ts +52 -8
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +133 -12
- package/dist/core/hooks/cli.js +676 -531
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +2 -1
- package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -1
- package/dist/core/hooks/prompt-context/contract.js +1 -0
- package/dist/core/hooks/resolve/transcript.d.ts +1 -1
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +35 -4
- package/dist/core/hooks/run-markers.d.ts +37 -0
- package/dist/core/hooks/run-markers.d.ts.map +1 -0
- package/dist/core/hooks/run-markers.js +109 -0
- package/dist/core/hooks/session-name-presence.d.ts +3 -1
- package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
- package/dist/core/hooks/session-name-presence.js +7 -2
- package/dist/core/hooks/shell-waiter.d.ts +2 -0
- package/dist/core/hooks/shell-waiter.d.ts.map +1 -0
- package/dist/core/hooks/shell-waiter.js +173 -0
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +5 -4
- package/dist/core/semantic/review.d.ts.map +1 -1
- package/dist/core/semantic/review.js +5 -4
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +15 -14
- package/dist/core/semantic/storage.d.ts.map +1 -1
- package/dist/core/semantic/storage.js +5 -4
- package/dist/core/servers/index.d.ts +211 -0
- package/dist/core/servers/index.d.ts.map +1 -0
- package/dist/core/servers/index.js +560 -0
- package/dist/core/servers/net.d.ts +58 -0
- package/dist/core/servers/net.d.ts.map +1 -0
- package/dist/core/servers/net.js +270 -0
- package/dist/core/servers/tunnels.d.ts +14 -0
- package/dist/core/servers/tunnels.d.ts.map +1 -0
- package/dist/core/servers/tunnels.js +33 -0
- package/dist/core/storage/atomic-json.d.ts.map +1 -1
- package/dist/core/storage/atomic-json.js +7 -3
- package/dist/core/storage/builtins.js +1 -0
- package/dist/core/storage/durable-history.d.ts.map +1 -1
- package/dist/core/storage/durable-history.js +6 -5
- package/dist/core/storage/maintenance-providers.d.ts.map +1 -1
- package/dist/core/storage/maintenance-providers.js +2 -1
- package/dist/core/storage/maintenance.d.ts.map +1 -1
- package/dist/core/storage/maintenance.js +7 -6
- package/dist/core/storage/metrics.d.ts.map +1 -1
- package/dist/core/storage/metrics.js +5 -1
- package/dist/core/storage/modes.d.ts +17 -0
- package/dist/core/storage/modes.d.ts.map +1 -0
- package/dist/core/storage/modes.js +47 -0
- package/dist/core/storage/process-log.d.ts.map +1 -1
- package/dist/core/storage/process-log.js +3 -2
- package/dist/core/storage/receipts.d.ts.map +1 -1
- package/dist/core/storage/receipts.js +4 -3
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +7 -6
- package/dist/core/supervisor/hook-health.d.ts +2 -1
- package/dist/core/supervisor/hook-health.d.ts.map +1 -1
- package/dist/core/supervisor/hook-health.js +4 -3
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +7 -6
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +8 -7
- package/dist/core/workflow/adapters.d.ts +7 -0
- package/dist/core/workflow/adapters.d.ts.map +1 -1
- package/dist/core/workflow/adapters.js +9 -0
- package/dist/core/workflow/approvals.d.ts.map +1 -1
- package/dist/core/workflow/approvals.js +6 -5
- package/dist/core/workflow/billing.d.ts.map +1 -1
- package/dist/core/workflow/billing.js +33 -4
- package/dist/core/workflow/durable-record.d.ts.map +1 -1
- package/dist/core/workflow/durable-record.js +4 -3
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +30 -1
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +2 -3
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +3 -2
- package/dist/core/workflow/sandbox-projection.d.ts +13 -2
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +23 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +6 -1
- package/dist/core/workflow/spawn-codex.d.ts +2 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +20 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +6 -1
- package/dist/core/workflow/spawn-opencode.d.ts +36 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -0
- package/dist/core/workflow/spawn-opencode.js +135 -0
- package/dist/core/workflow/transcript.d.ts.map +1 -1
- package/dist/core/workflow/transcript.js +3 -2
- package/dist/core/workflow/types.d.ts +47 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/worker-access.d.ts +92 -0
- package/dist/core/workflow/worker-access.d.ts.map +1 -0
- package/dist/core/workflow/worker-access.js +450 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +5 -4
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +3 -2
- package/dist/core/workflow/workspaces/paths.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/paths.js +2 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +10 -9
- package/dist/lazy-commands.js +8 -1
- package/dist/lib/agent-browser/client.d.ts +9 -0
- package/dist/lib/agent-browser/client.d.ts.map +1 -1
- package/dist/lib/agent-browser/client.js +13 -0
- package/dist/lib/browser/batch-steps.d.ts +18 -0
- package/dist/lib/browser/batch-steps.d.ts.map +1 -0
- package/dist/lib/browser/batch-steps.js +82 -0
- package/dist/lib/browser/client.d.ts +35 -1
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +117 -8
- package/dist/lib/browser/collect/collector.d.ts +99 -0
- package/dist/lib/browser/collect/collector.d.ts.map +1 -0
- package/dist/lib/browser/collect/collector.js +202 -0
- package/dist/lib/browser/collect/fields.d.ts +31 -0
- package/dist/lib/browser/collect/fields.d.ts.map +1 -0
- package/dist/lib/browser/collect/fields.js +75 -0
- package/dist/lib/browser/collect/index.d.ts +6 -0
- package/dist/lib/browser/collect/index.d.ts.map +1 -0
- package/dist/lib/browser/collect/index.js +5 -0
- package/dist/lib/browser/collect/page-driver.d.ts +14 -0
- package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
- package/dist/lib/browser/collect/page-driver.js +306 -0
- package/dist/lib/browser/collect/presets.d.ts +52 -0
- package/dist/lib/browser/collect/presets.d.ts.map +1 -0
- package/dist/lib/browser/collect/presets.js +176 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
- package/dist/lib/browser/collect/scroll-plan.js +118 -0
- package/dist/lib/browser/identity.d.ts +25 -0
- package/dist/lib/browser/identity.d.ts.map +1 -0
- package/dist/lib/browser/identity.js +66 -0
- package/dist/lib/browser/index.d.ts +3 -2
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -1
- package/dist/lib/browser/launch-args.d.ts +26 -0
- package/dist/lib/browser/launch-args.d.ts.map +1 -1
- package/dist/lib/browser/launch-args.js +41 -1
- package/dist/lib/browser/page-review-judge.d.ts +2 -0
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +13 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +1 -0
- package/dist/lib/browser/user-agent.d.ts +53 -0
- package/dist/lib/browser/user-agent.d.ts.map +1 -0
- package/dist/lib/browser/user-agent.js +79 -0
- package/dist/lib/browser/windows-chrome.d.ts +59 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
- package/dist/lib/browser/windows-chrome.js +179 -0
- package/dist/lib/claude-desktop-share.d.ts +136 -0
- package/dist/lib/claude-desktop-share.d.ts.map +1 -0
- package/dist/lib/claude-desktop-share.js +394 -0
- package/dist/lib/disk-usage.d.ts +50 -0
- package/dist/lib/disk-usage.d.ts.map +1 -0
- package/dist/lib/disk-usage.js +303 -0
- package/dist/lib/docs-lint.d.ts +6 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +8 -3
- package/dist/lib/docs-metadata-managed.d.ts +1 -0
- package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
- package/dist/lib/docs-metadata-managed.js +17 -1
- package/dist/lib/guarded-remove.d.ts +34 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -0
- package/dist/lib/guarded-remove.js +310 -0
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -9
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +2 -0
- package/dist/lib/instructions/templates.js +8 -8
- package/dist/lib/pace/index.d.ts +2 -0
- package/dist/lib/pace/index.d.ts.map +1 -0
- package/dist/lib/pace/index.js +1 -0
- package/dist/lib/pace/pace.d.ts +95 -0
- package/dist/lib/pace/pace.d.ts.map +1 -0
- package/dist/lib/pace/pace.js +313 -0
- package/dist/lib/tunnel/error-page.d.ts +1 -1
- package/dist/lib/tunnel/error-page.d.ts.map +1 -1
- package/dist/lib/tunnel/error-page.js +19 -8
- package/dist/lib/tunnel/forward-headers.d.ts +11 -0
- package/dist/lib/tunnel/forward-headers.d.ts.map +1 -0
- package/dist/lib/tunnel/forward-headers.js +17 -0
- package/dist/lib/tunnel/gate.js +34 -10
- package/dist/lib/tunnel/path-scope.d.ts +22 -0
- package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
- package/dist/lib/tunnel/path-scope.js +62 -0
- package/dist/lib/tunnel/state.d.ts +5 -0
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +17 -3
- package/dist/lib/webview/index.d.ts +2 -0
- package/dist/lib/webview/index.d.ts.map +1 -0
- package/dist/lib/webview/index.js +1 -0
- package/dist/lib/webview/webview.d.ts +116 -0
- package/dist/lib/webview/webview.d.ts.map +1 -0
- package/dist/lib/webview/webview.js +289 -0
- package/opencode-plugin/package.json +19 -0
- package/opencode-plugin/src/index.ts +791 -0
- package/package.json +16 -4
- package/schemas/config.schema.json +104 -0
- package/schemas/event-v3.schema.json +164 -0
- package/src/commander.ts +26 -0
- package/src/commands/agents.ts +47 -18
- package/src/commands/artifacts.ts +171 -19
- package/src/commands/browse-ai.ts +8 -0
- package/src/commands/browse.ts +570 -48
- package/src/commands/checkpoint.ts +7 -9
- package/src/commands/claude-desktop.ts +61 -0
- package/src/commands/deinit.ts +9 -4
- package/src/commands/disk.ts +114 -0
- package/src/commands/docs.ts +2 -0
- package/src/commands/doctor.ts +63 -3
- package/src/commands/fetch.ts +14 -0
- package/src/commands/files.ts +93 -2
- package/src/commands/init.ts +334 -122
- package/src/commands/instructions.ts +8 -4
- package/src/commands/ledger-v3.ts +2 -1
- package/src/commands/rm.ts +115 -0
- package/src/commands/servers.ts +506 -0
- package/src/commands/tunnel.ts +102 -2
- package/src/commands/webview.ts +159 -0
- package/src/core/adapter.ts +102 -1
- package/src/core/adapters/attestation.ts +3 -2
- package/src/core/adapters/profiles.ts +63 -1
- package/src/core/adapters/registry.ts +37 -0
- package/src/core/adapters/types.ts +10 -0
- package/src/core/agents/canonical-emit.ts +2 -5
- package/src/core/agents/cli.ts +26 -17
- package/src/core/agents/coord-client.ts +9 -3
- package/src/core/agents/render/session-context.ts +1 -0
- package/src/core/agents/rules/claim-conflict.ts +3 -8
- package/src/core/agents/rules/commit-conflict.ts +2 -7
- package/src/core/agents/rules/stop-hook.ts +3 -1
- package/src/core/agents/session-events.ts +120 -26
- package/src/core/agents/session-finalization-state-v3.ts +2 -1
- package/src/core/agents/session-finalizer-v3.ts +5 -4
- package/src/core/agents/session-name-display.ts +48 -0
- package/src/core/agents/state/heartbeat-reader.ts +7 -0
- package/src/core/agents/state/heartbeat-writer.ts +24 -2
- package/src/core/agents/state/live-coordination-view.ts +74 -0
- package/src/core/agents/state/live-coordination-writer.ts +3 -0
- package/src/core/agents/state/stale-sweep.ts +2 -9
- package/src/core/agents/turn-changes.ts +97 -0
- package/src/core/artifacts/delivery-card.ts +12 -3
- package/src/core/artifacts/index.ts +701 -47
- package/src/core/config.ts +206 -4
- package/src/core/context/index.ts +2 -1
- package/src/core/conversations/archive.ts +4 -3
- package/src/core/diagnostics/bundle.ts +5 -3
- package/src/core/events/legacy-storage/canary.ts +5 -4
- package/src/core/events/legacy-storage/compression.ts +2 -1
- package/src/core/events/v3/adapter-id.ts +29 -1
- package/src/core/events/v3/authority-outbox.ts +2 -1
- package/src/core/events/v3/bootstrap.ts +5 -4
- package/src/core/events/v3/capabilities.ts +17 -0
- package/src/core/events/v3/contract.ts +9 -1
- package/src/core/events/v3/control-witness.ts +3 -2
- package/src/core/events/v3/coordination-transaction-recovery.ts +4 -3
- package/src/core/events/v3/fingerprint-keys.ts +7 -6
- package/src/core/events/v3/generated.ts +1 -1
- package/src/core/events/v3/live-feed.ts +5 -4
- package/src/core/events/v3/producers/command-recorder.ts +8 -6
- package/src/core/events/v3/producers/coordination-recorder.ts +8 -7
- package/src/core/events/v3/producers/diagnostic-summaries.ts +5 -4
- package/src/core/events/v3/producers/hook-base.ts +11 -4
- package/src/core/events/v3/producers/hook.ts +19 -1
- package/src/core/events/v3/producers/intake.ts +18 -6
- package/src/core/events/v3/producers/recorder.ts +139 -114
- package/src/core/events/v3/projection.ts +11 -1
- package/src/core/events/v3/recovery.ts +4 -3
- package/src/core/events/v3/support-storage/inventory.ts +2 -1
- package/src/core/events/v3/support-storage/maintenance.ts +8 -3
- package/src/core/events/v3/support-storage/pack-reader.ts +4 -3
- package/src/core/events/v3/support-storage/pack-writer.ts +6 -5
- package/src/core/events/v3/tool-line-changes.ts +139 -0
- package/src/core/events/v3/writer.ts +8 -7
- package/src/core/governor/planning.ts +6 -5
- package/src/core/governor/service.ts +12 -11
- package/src/core/governor/state.ts +7 -6
- package/src/core/guard/evaluator.ts +1 -0
- package/src/core/guard/storage.ts +3 -2
- package/src/core/hooks/adapter/behaviors/claude-code.ts +33 -0
- package/src/core/hooks/adapter/behaviors/codex.ts +96 -0
- package/src/core/hooks/adapter/behaviors/cursor.ts +37 -0
- package/src/core/hooks/adapter/behaviors/index.ts +31 -0
- package/src/core/hooks/adapter/behaviors/openclaw.ts +36 -0
- package/src/core/hooks/adapter/behaviors/opencode.ts +36 -0
- package/src/core/hooks/adapter/behaviors/types.ts +110 -0
- package/src/core/hooks/adapter/detect.ts +2 -3
- package/src/core/hooks/adapter/events.ts +72 -1
- package/src/core/hooks/adapter/opencode-plugin.ts +177 -0
- package/src/core/hooks/adapter/output.ts +8 -1
- package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
- package/src/core/hooks/adapter/wiring.ts +198 -16
- package/src/core/hooks/cli.ts +776 -539
- package/src/core/hooks/effects/image-capture.ts +2 -1
- package/src/core/hooks/prompt-context/contract.ts +1 -0
- package/src/core/hooks/resolve/transcript.ts +42 -5
- package/src/core/hooks/run-markers.ts +132 -0
- package/src/core/hooks/session-name-presence.ts +10 -3
- package/src/core/hooks/shell-waiter.ts +166 -0
- package/src/core/resources/service.ts +5 -4
- package/src/core/semantic/review.ts +5 -4
- package/src/core/semantic/service.ts +15 -14
- package/src/core/semantic/storage.ts +5 -4
- package/src/core/servers/index.ts +712 -0
- package/src/core/servers/net.ts +283 -0
- package/src/core/servers/tunnels.ts +39 -0
- package/src/core/storage/atomic-json.ts +7 -3
- package/src/core/storage/builtins.ts +1 -0
- package/src/core/storage/durable-history.ts +6 -5
- package/src/core/storage/maintenance-providers.ts +2 -1
- package/src/core/storage/maintenance.ts +7 -6
- package/src/core/storage/metrics.ts +5 -1
- package/src/core/storage/modes.ts +53 -0
- package/src/core/storage/process-log.ts +3 -2
- package/src/core/storage/receipts.ts +4 -3
- package/src/core/storage/segments.ts +7 -6
- package/src/core/supervisor/hook-health.ts +4 -4
- package/src/core/supervisor/service.ts +7 -6
- package/src/core/work/state.ts +8 -7
- package/src/core/workflow/adapters.ts +15 -0
- package/src/core/workflow/approvals.ts +6 -5
- package/src/core/workflow/billing.ts +34 -4
- package/src/core/workflow/durable-record.ts +4 -3
- package/src/core/workflow/engine.ts +38 -1
- package/src/core/workflow/live-session-v3.ts +2 -3
- package/src/core/workflow/run-state.ts +3 -2
- package/src/core/workflow/sandbox-projection.ts +38 -2
- package/src/core/workflow/spawn-claude.ts +5 -1
- package/src/core/workflow/spawn-codex.ts +18 -1
- package/src/core/workflow/spawn-cursor.ts +5 -1
- package/src/core/workflow/spawn-opencode.ts +153 -0
- package/src/core/workflow/transcript.ts +3 -2
- package/src/core/workflow/types.ts +62 -2
- package/src/core/workflow/worker-access.ts +525 -0
- package/src/core/workflow/workspaces/leases.ts +5 -4
- package/src/core/workflow/workspaces/local-git.ts +3 -2
- package/src/core/workflow/workspaces/paths.ts +2 -1
- package/src/core/workflow/workspaces/state.ts +10 -9
- package/src/lazy-commands.ts +11 -1
- package/src/lib/agent-browser/client.ts +18 -0
- package/src/lib/browser/batch-steps.ts +79 -0
- package/src/lib/browser/client.ts +147 -9
- package/src/lib/browser/collect/collector.ts +306 -0
- package/src/lib/browser/collect/fields.ts +87 -0
- package/src/lib/browser/collect/index.ts +47 -0
- package/src/lib/browser/collect/page-driver.ts +327 -0
- package/src/lib/browser/collect/presets.ts +237 -0
- package/src/lib/browser/collect/scroll-plan.ts +179 -0
- package/src/lib/browser/identity.ts +64 -0
- package/src/lib/browser/index.ts +16 -1
- package/src/lib/browser/launch-args.ts +53 -1
- package/src/lib/browser/page-review-judge.ts +16 -0
- package/src/lib/browser/qa-run-contracts.ts +11 -0
- package/src/lib/browser/qa-run.ts +1 -0
- package/src/lib/browser/user-agent.ts +130 -0
- package/src/lib/browser/windows-chrome.ts +227 -0
- package/src/lib/claude-desktop-share.ts +511 -0
- package/src/lib/disk-usage.ts +347 -0
- package/src/lib/docs-lint.ts +8 -3
- package/src/lib/docs-metadata-managed.ts +21 -0
- package/src/lib/guarded-remove.ts +376 -0
- package/src/lib/identities/assume.ts +7 -7
- package/src/lib/instructions/apply.ts +2 -0
- package/src/lib/instructions/templates.ts +8 -8
- package/src/lib/pace/index.ts +14 -0
- package/src/lib/pace/pace.ts +370 -0
- package/src/lib/tunnel/error-page.ts +25 -9
- package/src/lib/tunnel/forward-headers.ts +16 -0
- package/src/lib/tunnel/gate.ts +39 -10
- package/src/lib/tunnel/path-scope.ts +73 -0
- package/src/lib/tunnel/state.ts +20 -3
- package/src/lib/webview/index.ts +13 -0
- package/src/lib/webview/webview.ts +457 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local socket and process observation for the server registry.
|
|
3
|
+
*
|
|
4
|
+
* Linux reads procfs directly (no `ss`/`lsof` dependency, works under WSL).
|
|
5
|
+
* macOS shells out to `lsof` and `ps`. Other platforms report `unsupported`
|
|
6
|
+
* so callers can say the scan did not run rather than implying it found
|
|
7
|
+
* nothing.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { readdirSync, readFileSync, readlinkSync } from "node:fs";
|
|
12
|
+
|
|
13
|
+
export type ScanSupport = "procfs" | "lsof" | "unsupported";
|
|
14
|
+
|
|
15
|
+
export interface ListeningSocket {
|
|
16
|
+
pid: number;
|
|
17
|
+
port: number;
|
|
18
|
+
address: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ProcessInfo {
|
|
22
|
+
pid: number;
|
|
23
|
+
ppid: number | null;
|
|
24
|
+
command: string;
|
|
25
|
+
cwd: string | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function scanSupport(): ScanSupport {
|
|
29
|
+
if (process.platform === "linux") return "procfs";
|
|
30
|
+
if (process.platform === "darwin") return "lsof";
|
|
31
|
+
return "unsupported";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ProcSocketRow {
|
|
35
|
+
localAddress: string;
|
|
36
|
+
localPort: number;
|
|
37
|
+
state: string;
|
|
38
|
+
inode: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Parse `/proc/net/tcp` or `/proc/net/tcp6` content. */
|
|
42
|
+
export function parseProcNetTcp(content: string): ProcSocketRow[] {
|
|
43
|
+
const rows: ProcSocketRow[] = [];
|
|
44
|
+
for (const line of content.split("\n").slice(1)) {
|
|
45
|
+
const fields = line.trim().split(/\s+/);
|
|
46
|
+
if (fields.length < 10) continue;
|
|
47
|
+
const [address, portHex] = (fields[1] ?? "").split(":");
|
|
48
|
+
if (!address || !portHex) continue;
|
|
49
|
+
rows.push({
|
|
50
|
+
localAddress: decodeProcAddress(address),
|
|
51
|
+
localPort: Number.parseInt(portHex, 16),
|
|
52
|
+
state: fields[3] ?? "",
|
|
53
|
+
inode: fields[9] ?? "",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return rows;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function decodeProcAddress(hex: string): string {
|
|
60
|
+
if (hex.length === 8) {
|
|
61
|
+
const bytes = hex.match(/../g)?.map((pair) => Number.parseInt(pair, 16)) ?? [];
|
|
62
|
+
return bytes.reverse().join(".");
|
|
63
|
+
}
|
|
64
|
+
if (/^0+$/.test(hex)) return "::";
|
|
65
|
+
if (hex === "00000000000000000000000001000000") return "::1";
|
|
66
|
+
if (hex.startsWith("0000000000000000FFFF0000")) return decodeProcAddress(hex.slice(24));
|
|
67
|
+
return `ipv6:${hex.toLowerCase()}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function readProcSockets(): ProcSocketRow[] {
|
|
71
|
+
const rows: ProcSocketRow[] = [];
|
|
72
|
+
for (const file of ["/proc/net/tcp", "/proc/net/tcp6"]) {
|
|
73
|
+
try {
|
|
74
|
+
rows.push(...parseProcNetTcp(readFileSync(file, "utf8")));
|
|
75
|
+
} catch {
|
|
76
|
+
// tcp6 is absent on IPv6-disabled kernels.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return rows;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Map socket inodes to the pids holding them (only processes we may read). */
|
|
83
|
+
function socketOwners(inodes: Set<string>): Map<string, number> {
|
|
84
|
+
const owners = new Map<string, number>();
|
|
85
|
+
if (inodes.size === 0) return owners;
|
|
86
|
+
let entries: string[];
|
|
87
|
+
try {
|
|
88
|
+
entries = readdirSync("/proc");
|
|
89
|
+
} catch {
|
|
90
|
+
return owners;
|
|
91
|
+
}
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
if (!/^\d+$/.test(entry)) continue;
|
|
94
|
+
let fds: string[];
|
|
95
|
+
try {
|
|
96
|
+
fds = readdirSync(`/proc/${entry}/fd`);
|
|
97
|
+
} catch {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
for (const fd of fds) {
|
|
101
|
+
let target: string;
|
|
102
|
+
try {
|
|
103
|
+
target = readlinkSync(`/proc/${entry}/fd/${fd}`);
|
|
104
|
+
} catch {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const match = /^socket:\[(\d+)\]$/.exec(target);
|
|
108
|
+
if (match?.[1] && inodes.has(match[1]) && !owners.has(match[1])) {
|
|
109
|
+
owners.set(match[1], Number(entry));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (owners.size === inodes.size) break;
|
|
113
|
+
}
|
|
114
|
+
return owners;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Listening TCP sockets whose owning process this user can see. */
|
|
118
|
+
export function listListeningSockets(): ListeningSocket[] {
|
|
119
|
+
const support = scanSupport();
|
|
120
|
+
if (support === "procfs") {
|
|
121
|
+
const listening = readProcSockets().filter((row) => row.state === "0A");
|
|
122
|
+
const owners = socketOwners(new Set(listening.map((row) => row.inode)));
|
|
123
|
+
const seen = new Set<string>();
|
|
124
|
+
const sockets: ListeningSocket[] = [];
|
|
125
|
+
for (const row of listening) {
|
|
126
|
+
const pid = owners.get(row.inode);
|
|
127
|
+
if (!pid) continue;
|
|
128
|
+
const key = `${pid}:${row.localPort}`;
|
|
129
|
+
if (seen.has(key)) continue;
|
|
130
|
+
seen.add(key);
|
|
131
|
+
sockets.push({ pid, port: row.localPort, address: row.localAddress });
|
|
132
|
+
}
|
|
133
|
+
return sockets.sort((a, b) => a.port - b.port);
|
|
134
|
+
}
|
|
135
|
+
if (support === "lsof") return lsofListeningSockets();
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Every listening TCP port on this machine, including ports whose owner this
|
|
141
|
+
* user cannot see (containers, other users). Used to avoid port collisions.
|
|
142
|
+
*/
|
|
143
|
+
export function listListeningPorts(): Set<number> {
|
|
144
|
+
const support = scanSupport();
|
|
145
|
+
if (support === "procfs")
|
|
146
|
+
return new Set(
|
|
147
|
+
readProcSockets()
|
|
148
|
+
.filter((row) => row.state === "0A")
|
|
149
|
+
.map((row) => row.localPort),
|
|
150
|
+
);
|
|
151
|
+
if (support === "lsof") return new Set(listListeningSockets().map((socket) => socket.port));
|
|
152
|
+
return new Set();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Parse `lsof -Fpn` output for listening sockets. */
|
|
156
|
+
export function parseLsofListen(output: string): ListeningSocket[] {
|
|
157
|
+
const sockets: ListeningSocket[] = [];
|
|
158
|
+
const seen = new Set<string>();
|
|
159
|
+
let pid = 0;
|
|
160
|
+
for (const line of output.split("\n")) {
|
|
161
|
+
if (line.startsWith("p")) pid = Number(line.slice(1));
|
|
162
|
+
else if (line.startsWith("n") && pid > 0) {
|
|
163
|
+
const match = /^(.*):(\d+)$/.exec(line.slice(1));
|
|
164
|
+
if (!match?.[2]) continue;
|
|
165
|
+
const port = Number(match[2]);
|
|
166
|
+
const key = `${pid}:${port}`;
|
|
167
|
+
if (seen.has(key)) continue;
|
|
168
|
+
seen.add(key);
|
|
169
|
+
sockets.push({ pid, port, address: (match[1] ?? "").replace(/^\[|\]$/g, "") });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return sockets.sort((a, b) => a.port - b.port);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Count established server-side connections per local port. A browser tab
|
|
177
|
+
* holding a live-reload socket or keep-alive connection counts as activity.
|
|
178
|
+
*/
|
|
179
|
+
export function establishedConnectionCounts(ports: number[]): Map<number, number> {
|
|
180
|
+
const counts = new Map<number, number>();
|
|
181
|
+
const wanted = new Set(ports.filter((port) => Number.isInteger(port) && port > 0));
|
|
182
|
+
if (wanted.size === 0) return counts;
|
|
183
|
+
const support = scanSupport();
|
|
184
|
+
if (support === "procfs") {
|
|
185
|
+
for (const row of readProcSockets()) {
|
|
186
|
+
// 01 ESTABLISHED; 03 SYN_RECV, where a deferred-accept server parks a
|
|
187
|
+
// connected client until its first bytes arrive.
|
|
188
|
+
if ((row.state !== "01" && row.state !== "03") || !wanted.has(row.localPort)) continue;
|
|
189
|
+
counts.set(row.localPort, (counts.get(row.localPort) ?? 0) + 1);
|
|
190
|
+
}
|
|
191
|
+
return counts;
|
|
192
|
+
}
|
|
193
|
+
if (support === "lsof") return lsofConnectionCounts(wanted);
|
|
194
|
+
return counts;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Listening sockets via `lsof` (the macOS path; also runs wherever lsof exists). */
|
|
198
|
+
export function lsofListeningSockets(): ListeningSocket[] {
|
|
199
|
+
return parseLsofListen(run("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-Fpn"]));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Server-side established connections per wanted local port, via `lsof`. */
|
|
203
|
+
export function lsofConnectionCounts(wanted: Set<number>): Map<number, number> {
|
|
204
|
+
const counts = new Map<number, number>();
|
|
205
|
+
const output = run("lsof", ["-nP", "-iTCP", "-sTCP:ESTABLISHED", "-Fn"]);
|
|
206
|
+
for (const line of output.split("\n")) {
|
|
207
|
+
const match = /^n.*?:(\d+)->/.exec(line);
|
|
208
|
+
const port = match?.[1] ? Number(match[1]) : 0;
|
|
209
|
+
if (wanted.has(port)) counts.set(port, (counts.get(port) ?? 0) + 1);
|
|
210
|
+
}
|
|
211
|
+
return counts;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Parent, command line, and working directory via `ps` and `lsof` (the macOS path). */
|
|
215
|
+
export function psProcessInfo(pid: number): ProcessInfo | null {
|
|
216
|
+
const ps = run("ps", ["-o", "ppid=,command=", "-p", String(pid)]).trim();
|
|
217
|
+
if (!ps) return null;
|
|
218
|
+
const match = /^(\d+)\s+(.*)$/.exec(ps);
|
|
219
|
+
const cwdLine = run("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"])
|
|
220
|
+
.split("\n")
|
|
221
|
+
.find((line) => line.startsWith("n"));
|
|
222
|
+
return {
|
|
223
|
+
pid,
|
|
224
|
+
ppid: match?.[1] ? Number(match[1]) : null,
|
|
225
|
+
command: match?.[2] ?? ps,
|
|
226
|
+
cwd: cwdLine ? cwdLine.slice(1) : null,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function readProcessInfo(pid: number): ProcessInfo | null {
|
|
231
|
+
if (process.platform === "linux") {
|
|
232
|
+
try {
|
|
233
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
234
|
+
const afterComm = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
|
|
235
|
+
const ppid = Number(afterComm[1]);
|
|
236
|
+
const command = readFileSync(`/proc/${pid}/cmdline`, "utf8").split("\0").join(" ").trim();
|
|
237
|
+
let cwd: string | null = null;
|
|
238
|
+
try {
|
|
239
|
+
cwd = readlinkSync(`/proc/${pid}/cwd`);
|
|
240
|
+
} catch {
|
|
241
|
+
cwd = null;
|
|
242
|
+
}
|
|
243
|
+
return { pid, ppid: Number.isInteger(ppid) ? ppid : null, command, cwd };
|
|
244
|
+
} catch {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (process.platform === "darwin") return psProcessInfo(pid);
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Parse `ps -o etime=` output (`[[dd-]hh:]mm:ss`) into seconds. */
|
|
253
|
+
export function parseElapsed(value: string): number | null {
|
|
254
|
+
const match = /^\s*(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)\s*$/.exec(value);
|
|
255
|
+
if (!match) return null;
|
|
256
|
+
const [, days, hours, minutes, seconds] = match;
|
|
257
|
+
return (
|
|
258
|
+
Number(days ?? 0) * 86_400 + Number(hours ?? 0) * 3_600 + Number(minutes) * 60 + Number(seconds)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** When a process started, from its elapsed run time; null when unavailable. */
|
|
263
|
+
export function processStartedAt(pid: number, now = Date.now()): string | null {
|
|
264
|
+
if (process.platform !== "linux" && process.platform !== "darwin") return null;
|
|
265
|
+
const elapsed = parseElapsed(run("ps", ["-o", "etime=", "-p", String(pid)]));
|
|
266
|
+
return elapsed === null ? null : new Date(now - elapsed * 1_000).toISOString();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** The pid followed by its ancestors, nearest first (bounded). */
|
|
270
|
+
export function ancestry(pid: number, maxHops = 32): number[] {
|
|
271
|
+
const chain: number[] = [];
|
|
272
|
+
let current: number | null = pid;
|
|
273
|
+
while (current && current > 1 && chain.length < maxHops && !chain.includes(current)) {
|
|
274
|
+
chain.push(current);
|
|
275
|
+
current = readProcessInfo(current)?.ppid ?? null;
|
|
276
|
+
}
|
|
277
|
+
return chain;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function run(command: string, args: string[]): string {
|
|
281
|
+
const result = spawnSync(command, args, { encoding: "utf8", timeout: 10_000 });
|
|
282
|
+
return result.status === 0 || result.stdout ? (result.stdout ?? "") : "";
|
|
283
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tunnels as registry services.
|
|
3
|
+
*
|
|
4
|
+
* `tunnel up` and `tunnel reload` register the instance; `tunnel down`
|
|
5
|
+
* removes it. Tunnels started before the registry existed are adopted the next
|
|
6
|
+
* time the registry is read, so a long-lived tunnel never shows as an
|
|
7
|
+
* unregistered listener.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
import { gateLogFile, type TunnelState } from "../../lib/tunnel/state.ts";
|
|
12
|
+
import { resolveBinName } from "../config.ts";
|
|
13
|
+
import type { RegisterServerInput } from "./index.ts";
|
|
14
|
+
|
|
15
|
+
export function tunnelServerId(name: string): string {
|
|
16
|
+
return `tunnel-${name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-")}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The registry input for one tunnel instance. */
|
|
20
|
+
export function tunnelServerInput(state: TunnelState, coordRoot: string): RegisterServerInput {
|
|
21
|
+
const helpers = [state.cloudflared_pid, state.provider_pid].filter(
|
|
22
|
+
(pid): pid is number => typeof pid === "number" && pid > 0,
|
|
23
|
+
);
|
|
24
|
+
return {
|
|
25
|
+
id: tunnelServerId(state.name),
|
|
26
|
+
kind: "service",
|
|
27
|
+
type: "tunnel",
|
|
28
|
+
label: `Tunnel ${state.name} → ${state.target}${state.vhost ? ` (${state.vhost})` : ""}`,
|
|
29
|
+
url: state.url,
|
|
30
|
+
port: state.gate_port,
|
|
31
|
+
pid: state.gate_pid,
|
|
32
|
+
...(helpers.length ? { pids: helpers } : {}),
|
|
33
|
+
log: resolve(coordRoot, ".cache", "tunnel", gateLogFile(state.name)),
|
|
34
|
+
cwd: coordRoot,
|
|
35
|
+
stop_argv: [resolveBinName(coordRoot), "tunnel", "down", "--name", state.name],
|
|
36
|
+
started_at: state.started_at,
|
|
37
|
+
owner: null,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { chmodSync, mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
+
import { stateDirMode, stateFileMode } from "./modes.ts";
|
|
3
4
|
|
|
4
5
|
/** Write one private JSON projection without exposing a partial frame to readers. */
|
|
5
6
|
export function writePrivateJsonAtomic(path: string, value: unknown): void {
|
|
6
|
-
mkdirSync(dirname(path), { recursive: true, mode:
|
|
7
|
+
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
7
8
|
const temporary = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
8
|
-
writeFileSync(temporary, `${JSON.stringify(value)}\n`, {
|
|
9
|
+
writeFileSync(temporary, `${JSON.stringify(value)}\n`, {
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
mode: stateFileMode(),
|
|
12
|
+
});
|
|
9
13
|
try {
|
|
10
|
-
chmodSync(temporary,
|
|
14
|
+
chmodSync(temporary, stateFileMode());
|
|
11
15
|
} catch {
|
|
12
16
|
// Windows does not expose POSIX modes. The atomic write still applies.
|
|
13
17
|
}
|
|
@@ -476,6 +476,7 @@ function cacheFamilies(): HarneryStorageFamily[] {
|
|
|
476
476
|
["pid-map-cache", ".harnery/pid-map", "active native process observations"],
|
|
477
477
|
["remote-presence-cache", ".harnery/presence/remote", "presence relay observations"],
|
|
478
478
|
["event-v3-live-display", ".harnery/live/v3", "Event Ledger V3 canonical events"],
|
|
479
|
+
["local-server-registry", ".harnery/servers", "running local server processes"],
|
|
479
480
|
] as const;
|
|
480
481
|
return [
|
|
481
482
|
...definitions.map(([id, root, source]) =>
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import type { FileHandle } from "node:fs/promises";
|
|
21
21
|
import { open, rename, rm } from "node:fs/promises";
|
|
22
22
|
import { basename, dirname, isAbsolute, join, relative } from "node:path";
|
|
23
|
+
import { stateDirMode, stateFileMode } from "./modes.ts";
|
|
23
24
|
|
|
24
25
|
export const HARNERY_DURABLE_HISTORY_SCHEMA = "harnery.durable-history/v1" as const;
|
|
25
26
|
|
|
@@ -271,7 +272,7 @@ export async function rewriteCrashSafeJsonlFile(
|
|
|
271
272
|
const handle = await open(
|
|
272
273
|
temp,
|
|
273
274
|
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollowFlag(),
|
|
274
|
-
|
|
275
|
+
stateFileMode(),
|
|
275
276
|
);
|
|
276
277
|
try {
|
|
277
278
|
await writeAll(handle, Buffer.from(body, "utf8"));
|
|
@@ -325,7 +326,7 @@ function appendAndSync(
|
|
|
325
326
|
const fd = openSync(
|
|
326
327
|
path,
|
|
327
328
|
fsConstants.O_RDWR | fsConstants.O_APPEND | fsConstants.O_CREAT | noFollowFlag(),
|
|
328
|
-
|
|
329
|
+
stateFileMode(),
|
|
329
330
|
);
|
|
330
331
|
try {
|
|
331
332
|
assertOpenFileIdentity(path, fd, parent);
|
|
@@ -348,7 +349,7 @@ interface DirectoryIdentity {
|
|
|
348
349
|
}
|
|
349
350
|
|
|
350
351
|
function ensurePrivateDirectory(path: string): DirectoryIdentity {
|
|
351
|
-
mkdirSync(path, { recursive: true, mode:
|
|
352
|
+
mkdirSync(path, { recursive: true, mode: stateDirMode() });
|
|
352
353
|
return captureDirectoryIdentity(path);
|
|
353
354
|
}
|
|
354
355
|
|
|
@@ -522,7 +523,7 @@ function acquireLease(lease: string): LeaseOwner {
|
|
|
522
523
|
};
|
|
523
524
|
let claimed: DirectoryIdentity | undefined;
|
|
524
525
|
try {
|
|
525
|
-
mkdirSync(lease, { mode:
|
|
526
|
+
mkdirSync(lease, { mode: stateDirMode() });
|
|
526
527
|
assertDirectoryWithinParent(lease, parent);
|
|
527
528
|
claimed = captureDirectoryIdentity(lease);
|
|
528
529
|
writeOwner(join(lease, "owner.json"), owner);
|
|
@@ -549,7 +550,7 @@ function writeOwner(path: string, owner: LeaseOwner): void {
|
|
|
549
550
|
const fd = openSync(
|
|
550
551
|
path,
|
|
551
552
|
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollowFlag(),
|
|
552
|
-
|
|
553
|
+
stateFileMode(),
|
|
553
554
|
);
|
|
554
555
|
try {
|
|
555
556
|
assertOpenFileIdentity(path, fd, parent);
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
readMaintenanceTransaction,
|
|
13
13
|
runAutomaticMaintenanceSlice,
|
|
14
14
|
} from "./maintenance.ts";
|
|
15
|
+
import { stateFileMode } from "./modes.ts";
|
|
15
16
|
|
|
16
17
|
const STALE_RUNNING_CLAIM_MS = 10 * 60 * 1_000;
|
|
17
18
|
|
|
@@ -191,7 +192,7 @@ function recoverInterruptedDailyClaim(coordRoot: string, now: Date): void {
|
|
|
191
192
|
|
|
192
193
|
function atomicCursor(path: string, cursor: DailyCursor): void {
|
|
193
194
|
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
194
|
-
writeFileSync(temporary, `${JSON.stringify(cursor, null, 2)}\n`, { mode:
|
|
195
|
+
writeFileSync(temporary, `${JSON.stringify(cursor, null, 2)}\n`, { mode: stateFileMode() });
|
|
195
196
|
renameSync(temporary, path);
|
|
196
197
|
}
|
|
197
198
|
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
HARNERY_STRUCTURED_LOG_PROVIDER_ID,
|
|
21
21
|
type HarneryRegisteredStorageFamily,
|
|
22
22
|
} from "./contract.ts";
|
|
23
|
+
import { stateDirMode, stateFileMode } from "./modes.ts";
|
|
23
24
|
|
|
24
25
|
export const HARNERY_MAINTENANCE_TRANSACTION_SCHEMA =
|
|
25
26
|
"harnery.storage-maintenance-transaction/v1" as const;
|
|
@@ -641,7 +642,7 @@ function writeReceipt(
|
|
|
641
642
|
committed_at: now.toISOString(),
|
|
642
643
|
};
|
|
643
644
|
const path = receiptPath(coordRoot, transactionId, action.action_id);
|
|
644
|
-
mkdirSync(dirname(path), { recursive: true, mode:
|
|
645
|
+
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
645
646
|
writeExclusiveDurable(path, `${JSON.stringify(receipt, null, 2)}\n`);
|
|
646
647
|
}
|
|
647
648
|
|
|
@@ -678,7 +679,7 @@ function writeRunOutcome(
|
|
|
678
679
|
now: Date,
|
|
679
680
|
): void {
|
|
680
681
|
const root = join(resolve(coordRoot), ".harnery", "logs", "storage-maintenance");
|
|
681
|
-
mkdirSync(root, { recursive: true, mode:
|
|
682
|
+
mkdirSync(root, { recursive: true, mode: stateDirMode() });
|
|
682
683
|
const active = join(root, "active.jsonl");
|
|
683
684
|
if (existsSync(active) && statSync(active).size > 1_048_576) {
|
|
684
685
|
renameSync(active, join(root, `sealed-${now.toISOString().replaceAll(/[:.]/g, "-")}.jsonl`));
|
|
@@ -686,7 +687,7 @@ function writeRunOutcome(
|
|
|
686
687
|
appendFileSync(
|
|
687
688
|
active,
|
|
688
689
|
`${JSON.stringify({ ts: now.toISOString(), transaction_id: transaction.transaction_id, state: transaction.state, actions: transaction.actions.length, files: transaction.actions.reduce((n, action) => n + action.files, 0), bytes: transaction.actions.reduce((n, action) => n + action.bytes, 0), reason_codes: transaction.reason_codes })}\n`,
|
|
689
|
-
{ encoding: "utf8", mode:
|
|
690
|
+
{ encoding: "utf8", mode: stateFileMode() },
|
|
690
691
|
);
|
|
691
692
|
}
|
|
692
693
|
|
|
@@ -871,9 +872,9 @@ function readJsonFile(path: string): unknown {
|
|
|
871
872
|
}
|
|
872
873
|
|
|
873
874
|
function atomicJson(path: string, value: unknown, fsync: boolean): void {
|
|
874
|
-
mkdirSync(dirname(path), { recursive: true, mode:
|
|
875
|
+
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
875
876
|
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
876
|
-
const fd = openSync(temporary, "wx",
|
|
877
|
+
const fd = openSync(temporary, "wx", stateFileMode());
|
|
877
878
|
try {
|
|
878
879
|
writeFileSync(fd, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
879
880
|
if (fsync) fsyncSync(fd);
|
|
@@ -884,7 +885,7 @@ function atomicJson(path: string, value: unknown, fsync: boolean): void {
|
|
|
884
885
|
}
|
|
885
886
|
|
|
886
887
|
function writeExclusiveDurable(path: string, value: string): void {
|
|
887
|
-
const fd = openSync(path, "wx",
|
|
888
|
+
const fd = openSync(path, "wx", stateFileMode());
|
|
888
889
|
try {
|
|
889
890
|
writeFileSync(fd, value, "utf8");
|
|
890
891
|
fsyncSync(fd);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { stateFileMode } from "./modes.ts";
|
|
3
4
|
|
|
4
5
|
export interface HarneryLogMetricsDelta {
|
|
5
6
|
accepted?: number;
|
|
@@ -109,7 +110,10 @@ export function mergeMetricsSidecar(
|
|
|
109
110
|
const merged = mergeMetrics(current, delta, now);
|
|
110
111
|
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
111
112
|
try {
|
|
112
|
-
writeFileSync(temporary, `${JSON.stringify(merged)}\n`, {
|
|
113
|
+
writeFileSync(temporary, `${JSON.stringify(merged)}\n`, {
|
|
114
|
+
encoding: "utf8",
|
|
115
|
+
mode: stateFileMode(),
|
|
116
|
+
});
|
|
113
117
|
renameSync(temporary, path);
|
|
114
118
|
return { merged: true, generation_reset: generationReset };
|
|
115
119
|
} catch {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The permission modes for everything Harnery writes as project state under `.harnery/`.
|
|
3
|
+
*
|
|
4
|
+
* `private` (the default) is owner-only: 0700 directories and 0600 files, exactly as before this
|
|
5
|
+
* module existed. `group` is for a project several Unix users share through one group, such as a
|
|
6
|
+
* workspace that several people's agents work in at once: 2770 (setgid) and 0660, with nothing ever granted
|
|
7
|
+
* to other users. Every state write takes its mode from here, and every integrity check that
|
|
8
|
+
* rejects a loosely permissioned file asks `stateModeTooOpen`, so the two cannot disagree.
|
|
9
|
+
*
|
|
10
|
+
* Personal state outside the project (browser cookies, sessions, pacing ledgers in the user's home)
|
|
11
|
+
* is not project state and stays owner-only regardless.
|
|
12
|
+
*/
|
|
13
|
+
import { storageSharing } from "../config.ts";
|
|
14
|
+
|
|
15
|
+
let resolved: "private" | "group" | undefined;
|
|
16
|
+
|
|
17
|
+
/** The sharing mode for this process, resolved once. Tests reset it with `resetStorageSharing`. */
|
|
18
|
+
export function stateSharing(): "private" | "group" {
|
|
19
|
+
if (resolved === undefined) {
|
|
20
|
+
resolved = storageSharing();
|
|
21
|
+
// A umask that strips group bits would quietly turn every group file back into an owner-only
|
|
22
|
+
// one, and the next user's write would fail. Keep the process's other-bits mask as it was.
|
|
23
|
+
if (resolved === "group" && typeof process.umask === "function")
|
|
24
|
+
process.umask(process.umask() & ~0o070);
|
|
25
|
+
}
|
|
26
|
+
return resolved;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resetStorageSharing(): void {
|
|
30
|
+
resolved = undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Mode for a directory of project state. Group mode includes setgid: Harnery chmods directories
|
|
35
|
+
* after creating them, and a plain 0770 would clear the bit, so files made inside would take the
|
|
36
|
+
* writer's own group instead of the project's, and other users could not read them.
|
|
37
|
+
*/
|
|
38
|
+
export function stateDirMode(): number {
|
|
39
|
+
return stateSharing() === "group" ? 0o2770 : 0o700;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Mode for a file of project state. */
|
|
43
|
+
export function stateFileMode(): number {
|
|
44
|
+
return stateSharing() === "group" ? 0o660 : 0o600;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* True when a state file or directory is readable or writable by someone it should not be: anyone
|
|
49
|
+
* but the owner in private mode, anyone outside the owner and group in group mode.
|
|
50
|
+
*/
|
|
51
|
+
export function stateModeTooOpen(mode: number): boolean {
|
|
52
|
+
return (mode & (stateSharing() === "group" ? 0o007 : 0o077)) !== 0;
|
|
53
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { basename, dirname, join, resolve } from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { createStorageCatalog } from "./catalog.ts";
|
|
15
|
+
import { stateDirMode, stateFileMode } from "./modes.ts";
|
|
15
16
|
import { familyLogDirectory } from "./segments.ts";
|
|
16
17
|
|
|
17
18
|
export interface RotatingTextSinkOptions {
|
|
@@ -79,7 +80,7 @@ export class RotatingTextSink {
|
|
|
79
80
|
append(text: string): void {
|
|
80
81
|
if (this.#closed) throw new Error("text sink is closed");
|
|
81
82
|
const bytes = Buffer.from(text, "utf8");
|
|
82
|
-
mkdirSync(dirname(this.#options.path), { recursive: true, mode:
|
|
83
|
+
mkdirSync(dirname(this.#options.path), { recursive: true, mode: stateDirMode() });
|
|
83
84
|
let offset = 0;
|
|
84
85
|
while (offset < bytes.byteLength) {
|
|
85
86
|
let current = existsSync(this.#options.path) ? statSync(this.#options.path).size : 0;
|
|
@@ -88,7 +89,7 @@ export class RotatingTextSink {
|
|
|
88
89
|
current = 0;
|
|
89
90
|
}
|
|
90
91
|
const length = Math.min(bytes.byteLength - offset, this.#options.max_bytes - current);
|
|
91
|
-
const fd = openSync(this.#options.path, "a",
|
|
92
|
+
const fd = openSync(this.#options.path, "a", stateFileMode());
|
|
92
93
|
try {
|
|
93
94
|
writeSync(fd, bytes.subarray(offset, offset + length));
|
|
94
95
|
if (this.#options.durable) fdatasyncSync(fd);
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
HarneryMaintenanceError,
|
|
19
19
|
type HarneryMaintenanceReceipt,
|
|
20
20
|
} from "./maintenance.ts";
|
|
21
|
+
import { stateDirMode, stateFileMode } from "./modes.ts";
|
|
21
22
|
|
|
22
23
|
export const HARNERY_RECEIPT_SEGMENT_MANIFEST_SCHEMA =
|
|
23
24
|
"harnery.storage-maintenance-receipt-segment/v1" as const;
|
|
@@ -85,7 +86,7 @@ export function consolidateMutationReceipts(
|
|
|
85
86
|
);
|
|
86
87
|
}
|
|
87
88
|
const segments = join(root, "segments");
|
|
88
|
-
mkdirSync(segments, { recursive: true, mode:
|
|
89
|
+
mkdirSync(segments, { recursive: true, mode: stateDirMode() });
|
|
89
90
|
const payloadPath = join(segments, `${segmentId}.jsonl`);
|
|
90
91
|
const manifestPath = join(segments, `${segmentId}.manifest.json`);
|
|
91
92
|
writeExclusiveDurable(payloadPath, payload);
|
|
@@ -144,9 +145,9 @@ function readReceipt(path: string): {
|
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
function writeExclusiveDurable(path: string, bytes: string): void {
|
|
147
|
-
mkdirSync(dirname(path), { recursive: true, mode:
|
|
148
|
+
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
148
149
|
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
149
|
-
const fd = openSync(temporary, "wx",
|
|
150
|
+
const fd = openSync(temporary, "wx", stateFileMode());
|
|
150
151
|
try {
|
|
151
152
|
writeFileSync(fd, bytes, "utf8");
|
|
152
153
|
fsyncSync(fd);
|
|
@@ -23,6 +23,7 @@ import type { HarneryRegisteredStorageFamily } from "./contract.ts";
|
|
|
23
23
|
import { parseLogRecord } from "./jsonl.ts";
|
|
24
24
|
import type { HarneryLogMetricsDelta } from "./metrics.ts";
|
|
25
25
|
import { mergeMetricsSidecar } from "./metrics.ts";
|
|
26
|
+
import { stateDirMode, stateFileMode } from "./modes.ts";
|
|
26
27
|
|
|
27
28
|
export interface HarneryLogSegmentV1 {
|
|
28
29
|
sequence: number;
|
|
@@ -165,7 +166,7 @@ export class FileSegmentSink {
|
|
|
165
166
|
const fd = openSync(
|
|
166
167
|
active,
|
|
167
168
|
fsConstants.O_RDWR | fsConstants.O_APPEND | fsConstants.O_CREAT | noFollowFlag(),
|
|
168
|
-
|
|
169
|
+
stateFileMode(),
|
|
169
170
|
);
|
|
170
171
|
let bytes = 0;
|
|
171
172
|
try {
|
|
@@ -337,7 +338,7 @@ export async function withFamilyLease<T>(
|
|
|
337
338
|
let owner: LeaseOwner | undefined;
|
|
338
339
|
while (true) {
|
|
339
340
|
try {
|
|
340
|
-
mkdirSync(lease, { mode:
|
|
341
|
+
mkdirSync(lease, { mode: stateDirMode() });
|
|
341
342
|
const leaseIdentity = captureDirectoryIdentity(lease);
|
|
342
343
|
owner = {
|
|
343
344
|
owner_id: randomUUID(),
|
|
@@ -397,7 +398,7 @@ function sealActive(active: string, options: ResolvedSegmentOptions, now: Date):
|
|
|
397
398
|
const sequence = manifest.next_sequence;
|
|
398
399
|
const name = `${now.toISOString().slice(0, 10).replaceAll("-", "")}-${String(sequence).padStart(8, "0")}.jsonl.gz`;
|
|
399
400
|
const target = join(directory, "segments", name);
|
|
400
|
-
mkdirSync(dirname(target), { recursive: true, mode:
|
|
401
|
+
mkdirSync(dirname(target), { recursive: true, mode: stateDirMode() });
|
|
401
402
|
const temporary = `${target}.tmp-${process.pid}-${randomUUID()}`;
|
|
402
403
|
const compressed = gzipSync(content);
|
|
403
404
|
writeNewFile(temporary, compressed);
|
|
@@ -425,7 +426,7 @@ function sealActive(active: string, options: ResolvedSegmentOptions, now: Date):
|
|
|
425
426
|
],
|
|
426
427
|
};
|
|
427
428
|
const sealedManifest = join(directory, "manifests", `${String(sequence).padStart(8, "0")}.json`);
|
|
428
|
-
mkdirSync(dirname(sealedManifest), { recursive: true, mode:
|
|
429
|
+
mkdirSync(dirname(sealedManifest), { recursive: true, mode: stateDirMode() });
|
|
429
430
|
writeNewFile(sealedManifest, Buffer.from(`${JSON.stringify(next)}\n`));
|
|
430
431
|
atomicJson(join(directory, "manifest.json"), next);
|
|
431
432
|
truncateRegularFile(active);
|
|
@@ -539,7 +540,7 @@ interface DirectoryIdentity {
|
|
|
539
540
|
}
|
|
540
541
|
|
|
541
542
|
function ensurePrivateDirectory(path: string): DirectoryIdentity {
|
|
542
|
-
mkdirSync(path, { recursive: true, mode:
|
|
543
|
+
mkdirSync(path, { recursive: true, mode: stateDirMode() });
|
|
543
544
|
return captureDirectoryIdentity(path);
|
|
544
545
|
}
|
|
545
546
|
|
|
@@ -632,7 +633,7 @@ function writeNewFile(path: string, content: Buffer): void {
|
|
|
632
633
|
const fd = openSync(
|
|
633
634
|
path,
|
|
634
635
|
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollowFlag(),
|
|
635
|
-
|
|
636
|
+
stateFileMode(),
|
|
636
637
|
);
|
|
637
638
|
try {
|
|
638
639
|
assertOpenFileIdentity(path, fd, parent);
|