@phyzess/pappra 0.1.0-beta.13 → 0.1.0-beta.18
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 +134 -15
- package/assets/integrations/skills/pappra/SKILL.md +28 -7
- package/assets/integrations/skills/pappra/references/collaboration.md +633 -0
- package/assets/integrations/skills/pappra/references/environment.md +360 -0
- package/assets/integrations/skills/pappra/references/hosts.md +20 -0
- package/assets/integrations/skills/pappra/references/operations.md +36 -5
- package/dist/adapters/pi/recovery-extension.d.ts +65 -0
- package/dist/adapters/pi/recovery-extension.js +238 -0
- package/dist/adapters/pi/session-transition-extension.d.ts +30 -0
- package/dist/adapters/pi/session-transition-extension.js +265 -0
- package/dist/app/automatic-handoff-events.d.ts +26 -0
- package/dist/app/automatic-handoff-events.js +134 -0
- package/dist/app/bounded-file.d.ts +2 -0
- package/dist/app/bounded-file.js +2 -0
- package/dist/app/capability-evidence/claude.d.ts +37 -0
- package/dist/app/capability-evidence/claude.js +206 -0
- package/dist/app/capability-evidence/codex.d.ts +35 -0
- package/dist/app/capability-evidence/codex.js +193 -0
- package/dist/app/capability-evidence/index.d.ts +33 -0
- package/dist/app/capability-evidence/index.js +117 -0
- package/dist/app/capability-evidence/pi.d.ts +73 -0
- package/dist/app/capability-evidence/pi.js +669 -0
- package/dist/app/capability-evidence/shared.d.ts +69 -0
- package/dist/app/capability-evidence/shared.js +195 -0
- package/dist/app/collab-auto-handoff.d.ts +55 -0
- package/dist/app/collab-auto-handoff.js +384 -0
- package/dist/app/collab-bindings.d.ts +57 -0
- package/dist/app/collab-bindings.js +1006 -0
- package/dist/app/collab-bootstrap-transport.d.ts +163 -0
- package/dist/app/collab-bootstrap-transport.js +769 -0
- package/dist/app/collab-bootstrap.d.ts +137 -0
- package/dist/app/collab-bootstrap.js +3455 -0
- package/dist/app/collab-handoff.d.ts +12 -0
- package/dist/app/collab-handoff.js +511 -0
- package/dist/app/collab-messages.d.ts +2 -0
- package/dist/app/collab-messages.js +2 -0
- package/dist/app/collab-notify.d.ts +156 -0
- package/dist/app/collab-notify.js +1208 -0
- package/dist/app/collab-preflight.d.ts +34 -0
- package/dist/app/collab-preflight.js +96 -0
- package/dist/app/collab-recovery-executor.d.ts +161 -0
- package/dist/app/collab-recovery-executor.js +1153 -0
- package/dist/app/collab-recovery-projection.d.ts +103 -0
- package/dist/app/collab-recovery-projection.js +659 -0
- package/dist/app/collab-recovery.d.ts +25 -0
- package/dist/app/collab-recovery.js +429 -0
- package/dist/app/collab-resources.d.ts +16 -0
- package/dist/app/collab-resources.js +278 -0
- package/dist/app/collab-role-events.d.ts +22 -0
- package/dist/app/collab-role-events.js +329 -0
- package/dist/app/collab-role-orchestration.d.ts +53 -0
- package/dist/app/collab-role-orchestration.js +601 -0
- package/dist/app/collab-role-plan.d.ts +81 -0
- package/dist/app/collab-role-plan.js +504 -0
- package/dist/app/collab-role-repair.d.ts +109 -0
- package/dist/app/collab-role-repair.js +821 -0
- package/dist/app/collab-rollover.d.ts +263 -0
- package/dist/app/collab-rollover.js +1112 -0
- package/dist/app/collab-route.d.ts +64 -0
- package/dist/app/collab-route.js +240 -0
- package/dist/app/collab-session-transition.d.ts +221 -0
- package/dist/app/collab-session-transition.js +3334 -0
- package/dist/app/collab-state.d.ts +7 -0
- package/dist/app/collab-state.js +162 -0
- package/dist/app/collab-task-store.d.ts +22 -0
- package/dist/app/collab-task-store.js +204 -0
- package/dist/app/collab-work-execution.d.ts +127 -0
- package/dist/app/collab-work-execution.js +1027 -0
- package/dist/app/collab-work-plan.d.ts +87 -0
- package/dist/app/collab-work-plan.js +437 -0
- package/dist/app/collaboration.d.ts +8 -2
- package/dist/app/collaboration.js +101 -12
- package/dist/app/doctor.js +108 -5
- package/dist/app/host-mcp.js +34 -8
- package/dist/app/host-probe.js +4 -4
- package/dist/app/init.d.ts +58 -4
- package/dist/app/init.js +768 -155
- package/dist/app/managed-pi-bootstrap-admission.d.ts +14 -0
- package/dist/app/managed-pi-bootstrap-admission.js +104 -0
- package/dist/app/managed-pi-bootstrap-refresh.d.ts +10 -0
- package/dist/app/managed-pi-bootstrap-refresh.js +78 -0
- package/dist/app/managed-pi-delivery.d.ts +8 -0
- package/dist/app/managed-pi-delivery.js +110 -0
- package/dist/app/managed-pi-handoff-receiver.d.ts +15 -0
- package/dist/app/managed-pi-handoff-receiver.js +263 -0
- package/dist/app/managed-pi-handoff-reference.d.ts +3 -0
- package/dist/app/managed-pi-handoff-reference.js +60 -0
- package/dist/app/managed-pi-herdr.d.ts +21 -0
- package/dist/app/managed-pi-herdr.js +185 -0
- package/dist/app/managed-pi-initial-admission.d.ts +28 -0
- package/dist/app/managed-pi-initial-admission.js +416 -0
- package/dist/app/managed-pi-initial-launch.d.ts +5 -0
- package/dist/app/managed-pi-initial-launch.js +61 -0
- package/dist/app/managed-pi-native-observation.d.ts +36 -0
- package/dist/app/managed-pi-native-observation.js +191 -0
- package/dist/app/managed-pi-readiness.d.ts +29 -0
- package/dist/app/managed-pi-readiness.js +288 -0
- package/dist/app/managed-pi-repair-stop.d.ts +21 -0
- package/dist/app/managed-pi-repair-stop.js +371 -0
- package/dist/app/managed-pi-resume-admission.d.ts +32 -0
- package/dist/app/managed-pi-resume-admission.js +618 -0
- package/dist/app/managed-pi-retirement.d.ts +28 -0
- package/dist/app/managed-pi-retirement.js +112 -0
- package/dist/app/managed-pi-sdk-control.d.ts +7 -0
- package/dist/app/managed-pi-sdk-control.js +169 -0
- package/dist/app/managed-pi-sdk-runtime.d.ts +3 -0
- package/dist/app/managed-pi-sdk-runtime.js +483 -0
- package/dist/app/managed-pi-source-admission.d.ts +14 -0
- package/dist/app/managed-pi-source-admission.js +323 -0
- package/dist/app/managed-pi-transition-host.d.ts +36 -0
- package/dist/app/managed-pi-transition-host.js +292 -0
- package/dist/app/managed-pi.d.ts +54 -0
- package/dist/app/managed-pi.js +248 -0
- package/dist/app/shell-readiness.d.ts +66 -0
- package/dist/app/shell-readiness.js +368 -0
- package/dist/app/task-pin.d.ts +2 -23
- package/dist/app/task-pin.js +2 -113
- package/dist/cli/commands/collaboration-binding.d.ts +13 -0
- package/dist/cli/commands/collaboration-binding.js +118 -0
- package/dist/cli/commands/collaboration-bootstrap.d.ts +10 -0
- package/dist/cli/commands/collaboration-bootstrap.js +84 -0
- package/dist/cli/commands/collaboration-capabilities.d.ts +13 -0
- package/dist/cli/commands/collaboration-capabilities.js +127 -0
- package/dist/cli/commands/collaboration-handoff.d.ts +13 -0
- package/dist/cli/commands/collaboration-handoff.js +102 -0
- package/dist/cli/commands/collaboration-message.d.ts +17 -0
- package/dist/cli/commands/collaboration-message.js +177 -0
- package/dist/cli/commands/collaboration-plan.d.ts +8 -0
- package/dist/cli/commands/collaboration-plan.js +108 -0
- package/dist/cli/commands/collaboration-preflight.d.ts +10 -0
- package/dist/cli/commands/collaboration-preflight.js +167 -0
- package/dist/cli/commands/collaboration-recovery-run.d.ts +46 -0
- package/dist/cli/commands/collaboration-recovery-run.js +224 -0
- package/dist/cli/commands/collaboration-recovery.d.ts +11 -0
- package/dist/cli/commands/collaboration-recovery.js +104 -0
- package/dist/cli/commands/collaboration-resources.d.ts +11 -0
- package/dist/cli/commands/collaboration-resources.js +96 -0
- package/dist/cli/commands/collaboration-role-plan.d.ts +9 -0
- package/dist/cli/commands/collaboration-role-plan.js +69 -0
- package/dist/cli/commands/collaboration-role-run.d.ts +11 -0
- package/dist/cli/commands/collaboration-role-run.js +128 -0
- package/dist/cli/commands/collaboration-rollover.d.ts +10 -0
- package/dist/cli/commands/collaboration-rollover.js +103 -0
- package/dist/cli/commands/collaboration-state.d.ts +10 -0
- package/dist/cli/commands/collaboration-state.js +96 -0
- package/dist/cli/commands/collaboration-transition.d.ts +10 -0
- package/dist/cli/commands/collaboration-transition.js +75 -0
- package/dist/cli/commands/collaboration-work-run.d.ts +34 -0
- package/dist/cli/commands/collaboration-work-run.js +178 -0
- package/dist/cli/commands/collaboration.d.ts +4 -0
- package/dist/cli/commands/collaboration.js +39 -4
- package/dist/cli/commands/continuity.js +2 -6
- package/dist/cli/commands/docs.d.ts +2 -0
- package/dist/cli/commands/docs.js +89 -15
- package/dist/cli/commands/host.d.ts +7 -1
- package/dist/cli/commands/host.js +122 -7
- package/dist/cli/commands/init.d.ts +20 -3
- package/dist/cli/commands/init.js +897 -232
- package/dist/cli/commands/mcp.d.ts +1 -0
- package/dist/cli/commands/mcp.js +11 -5
- package/dist/cli/commands/repair.d.ts +12 -0
- package/dist/cli/commands/repair.js +41 -0
- package/dist/cli/commands/runtime.d.ts +1 -1
- package/dist/cli/commands/runtime.js +139 -38
- package/dist/cli/commands/session.d.ts +7 -0
- package/dist/cli/commands/session.js +82 -0
- package/dist/cli/commands/task.js +19 -5
- package/dist/cli/run.js +866 -24
- package/dist/code/identity.d.ts +7 -0
- package/dist/code/identity.js +7 -3
- package/dist/code/registration.js +7 -0
- package/dist/code/service.d.ts +9 -0
- package/dist/code/service.js +16 -3
- package/dist/code/text.js +19 -7
- package/dist/collab/admission.d.ts +91 -66
- package/dist/collab/admission.js +21 -6
- package/dist/collab/canonical.d.ts +1 -1
- package/dist/collab/canonical.js +44 -3
- package/dist/collab/envelope.d.ts +2 -2
- package/dist/collab/envelope.js +12 -12
- package/dist/collab/first-work.d.ts +3 -0
- package/dist/collab/first-work.js +15 -0
- package/dist/collab/handoff.d.ts +16 -0
- package/dist/collab/handoff.js +39 -5
- package/dist/collab/index.d.ts +97 -1
- package/dist/collab/index.js +9 -1
- package/dist/collab/messages.js +126 -3
- package/dist/collab/persisted-messages.d.ts +123 -0
- package/dist/collab/persisted-messages.js +496 -0
- package/dist/collab/preflight.d.ts +1 -1
- package/dist/collab/resources.d.ts +13 -3
- package/dist/collab/resources.js +37 -6
- package/dist/collab/rollover.d.ts +311 -0
- package/dist/collab/rollover.js +1024 -0
- package/dist/collab/store.d.ts +1 -0
- package/dist/collab/store.js +1 -4
- package/dist/collab/types.d.ts +2 -354
- package/dist/collab/validation.d.ts +2 -10
- package/dist/collab/validation.js +2 -303
- package/dist/collab/work-plan.d.ts +105 -0
- package/dist/collab/work-plan.js +843 -0
- package/dist/continuity/context.d.ts +11 -2
- package/dist/continuity/context.js +100 -13
- package/dist/continuity/handoff-bundle.d.ts +296 -0
- package/dist/continuity/handoff-bundle.js +477 -0
- package/dist/continuity/index.d.ts +2 -0
- package/dist/continuity/index.js +1 -0
- package/dist/continuity/paths.js +10 -2
- package/dist/continuity/service.js +91 -15
- package/dist/continuity/task-pin.d.ts +24 -0
- package/dist/continuity/task-pin.js +590 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +6 -0
- package/dist/experimental/index.d.ts +13 -0
- package/dist/experimental/index.js +25 -0
- package/dist/experimental/pi.d.ts +4 -0
- package/dist/experimental/pi.js +12 -0
- package/dist/hosts/claude/native.js +1 -1
- package/dist/hosts/codex/native.js +1 -1
- package/dist/hosts/codex/protocol.js +1 -1
- package/dist/hosts/integration/bootstrap-config.d.ts +58 -0
- package/dist/hosts/integration/bootstrap-config.js +254 -0
- package/dist/hosts/integration/bundle-export.d.ts +44 -0
- package/dist/hosts/integration/bundle-export.js +366 -3
- package/dist/hosts/integration/bundle.js +3 -0
- package/dist/hosts/integration/hook.js +36 -9
- package/dist/hosts/integration/pappra-bootstrap-extension.mjs +80 -0
- package/dist/hosts/integration/pappra-extension-runtime.mjs +59 -24631
- package/dist/hosts/integration/pappra-hook.mjs +61 -24749
- package/dist/hosts/integration/pappra-recovery-extension.mjs +259 -0
- package/dist/hosts/integration/pappra-session-transition-extension.mjs +276 -0
- package/dist/hosts/integration/pi-bootstrap-extension.d.ts +36 -0
- package/dist/hosts/integration/pi-bootstrap-extension.js +101 -0
- package/dist/hosts/integration/pi-bootstrap-guard.d.ts +125 -0
- package/dist/hosts/integration/pi-bootstrap-guard.js +438 -0
- package/dist/hosts/integration/pi-bootstrap-runtime.d.ts +63 -0
- package/dist/hosts/integration/pi-bootstrap-runtime.js +223 -0
- package/dist/hosts/integration/pi-native-events.d.ts +112 -0
- package/dist/hosts/integration/pi-native-events.js +348 -0
- package/dist/hosts/integration/pi-rollover-extension.d.ts +166 -0
- package/dist/hosts/integration/pi-rollover-extension.js +431 -0
- package/dist/hosts/integration/recovery-config.d.ts +48 -0
- package/dist/hosts/integration/recovery-config.js +117 -0
- package/dist/hosts/integration/service.d.ts +6 -1
- package/dist/hosts/integration/service.js +485 -35
- package/dist/hosts/integration/support.d.ts +7 -0
- package/dist/hosts/integration/support.js +9 -1
- package/dist/hosts/integration/templates.d.ts +3 -0
- package/dist/hosts/integration/templates.js +26 -3
- package/dist/hosts/pi/managed-host-identity.d.ts +2 -0
- package/dist/hosts/pi/managed-host-identity.js +40 -0
- package/dist/hosts/pi/managed-provider-diagnostics.d.ts +6 -0
- package/dist/hosts/pi/managed-provider-diagnostics.js +184 -0
- package/dist/hosts/pi/managed-sdk-session.d.ts +335 -0
- package/dist/hosts/pi/managed-sdk-session.js +1639 -0
- package/dist/hosts/pi/managed-session-extension.d.ts +20 -0
- package/dist/hosts/pi/managed-session-extension.js +84 -0
- package/dist/hosts/pi/managed-session.d.ts +177 -0
- package/dist/hosts/pi/managed-session.js +1557 -0
- package/dist/hosts/pi/protocol.js +1 -1
- package/dist/hosts/pi/query-result.js +1 -1
- package/dist/hosts/pi/resume-capability.d.ts +41 -0
- package/dist/hosts/pi/resume-capability.js +33 -0
- package/dist/hosts/pi/rpc.d.ts +182 -134
- package/dist/hosts/pi/rpc.js +230 -37
- package/dist/hosts/pilot.d.ts +97 -74
- package/dist/hosts/pilot.js +5 -1
- package/dist/hosts/version.d.ts +3 -0
- package/dist/hosts/version.js +18 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/bounded-file.d.ts +19 -0
- package/dist/infra/bounded-file.js +66 -0
- package/dist/infra/fs.js +10 -2
- package/dist/infra/git.d.ts +29 -0
- package/dist/infra/git.js +45 -9
- package/dist/infra/json-property.d.ts +2 -0
- package/dist/infra/json-property.js +69 -0
- package/dist/infra/json.d.ts +4 -0
- package/dist/infra/json.js +24 -0
- package/dist/infra/owned-process-group.js +1 -1
- package/dist/infra/private-lock.d.ts +16 -0
- package/dist/infra/private-lock.js +148 -0
- package/dist/infra/private-state.d.ts +7 -0
- package/dist/infra/private-state.js +146 -0
- package/dist/infra/process-identity.d.ts +12 -0
- package/dist/infra/process-identity.js +142 -0
- package/dist/infra/validation.d.ts +5 -0
- package/dist/infra/validation.js +15 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +6 -0
- package/dist/install/apply.d.ts +9 -1
- package/dist/install/apply.js +176 -12
- package/dist/install/dependencies.d.ts +38 -0
- package/dist/install/dependencies.js +282 -0
- package/dist/install/ownership.js +30 -1
- package/dist/install/references.d.ts +31 -0
- package/dist/install/references.js +208 -0
- package/dist/install/repair.d.ts +37 -0
- package/dist/install/repair.js +798 -0
- package/dist/install/reset-local.d.ts +16 -0
- package/dist/install/reset-local.js +304 -0
- package/dist/install/rollback.d.ts +3 -0
- package/dist/install/rollback.js +9 -3
- package/dist/install/runtime.d.ts +10 -6
- package/dist/install/runtime.js +299 -59
- package/dist/install/source-fingerprint.d.ts +2 -0
- package/dist/install/source-fingerprint.js +103 -0
- package/dist/knowledge/catalog.d.ts +1 -0
- package/dist/knowledge/catalog.js +143 -10
- package/dist/knowledge/journal.js +17 -7
- package/dist/knowledge/paths.js +2 -1
- package/dist/knowledge/refs.js +4 -0
- package/dist/knowledge/register-batch.d.ts +4 -0
- package/dist/knowledge/register-batch.js +225 -0
- package/dist/knowledge/relocate.d.ts +5 -0
- package/dist/knowledge/relocate.js +92 -0
- package/dist/knowledge/scan.js +103 -47
- package/dist/knowledge/service.d.ts +3 -0
- package/dist/knowledge/service.js +33 -1
- package/dist/knowledge/write.d.ts +4 -1
- package/dist/knowledge/write.js +1 -1
- package/dist/mcp/server.d.ts +4 -2
- package/dist/mcp/server.js +87 -136
- package/dist/schemas/collaboration-state-validation.d.ts +6 -0
- package/dist/schemas/collaboration-state-validation.js +342 -0
- package/dist/schemas/collaboration-state.d.ts +436 -0
- package/dist/schemas/collaboration-state.js +1 -0
- package/dist/schemas/collaboration.d.ts +335 -0
- package/dist/schemas/collaboration.js +272 -0
- package/dist/schemas/continuity.d.ts +8 -0
- package/dist/schemas/continuity.js +13 -3
- package/dist/schemas/doctor.d.ts +1 -0
- package/dist/schemas/doctor.js +1 -0
- package/dist/schemas/document.d.ts +61 -0
- package/dist/schemas/document.js +9 -0
- package/dist/schemas/errors.d.ts +1 -0
- package/dist/schemas/errors.js +1 -0
- package/dist/schemas/host.d.ts +24 -0
- package/dist/schemas/host.js +19 -0
- package/dist/schemas/install.d.ts +8 -2
- package/dist/schemas/install.js +11 -1
- package/dist/schemas/managed-pi-execution.d.ts +16 -0
- package/dist/schemas/managed-pi-execution.js +2 -0
- package/dist/schemas/outcome.d.ts +9 -5
- package/dist/workspace/config-file.js +1 -3
- package/dist/workspace/documents.js +12 -2
- package/dist/workspace/repositories.d.ts +7 -0
- package/dist/workspace/repositories.js +14 -5
- package/package.json +35 -35
- package/skills/pappra/SKILL.md +28 -7
- package/skills/pappra/references/collaboration.md +633 -0
- package/skills/pappra/references/environment.md +360 -0
- package/skills/pappra/references/hosts.md +20 -0
- package/skills/pappra/references/operations.md +36 -5
- package/dist/app/collab-from.d.ts +0 -35
- package/dist/app/collab-from.js +0 -84
|
@@ -7,6 +7,9 @@ import { errorMessage, failed, succeeded } from '../schemas/outcome.js';
|
|
|
7
7
|
import { fileIdentitySchema, installJournalSchema, installLockSchema, workspaceInstallManifestSchema, } from '../schemas/install.js';
|
|
8
8
|
import { isPathInside, samePath } from '../schemas/paths.js';
|
|
9
9
|
import { observePath, readTextFileBounded, resolveRealpath, sha256Digest, } from '../infra/fs.js';
|
|
10
|
+
import { ensurePrivateState } from '../infra/private-state.js';
|
|
11
|
+
import { publishPrivateLock } from '../infra/private-lock.js';
|
|
12
|
+
import { captureProcessIdentity } from '../infra/process-identity.js';
|
|
10
13
|
export function workspaceInstallPaths(root) {
|
|
11
14
|
const localDir = join(root, '.pappra', 'local');
|
|
12
15
|
return {
|
|
@@ -143,6 +146,11 @@ export async function writeBytesAtomic(path, bytes, boundaryRoot) {
|
|
|
143
146
|
const boundary = await ensureStatePathBoundary(boundaryRoot, path);
|
|
144
147
|
if (!boundary.ok)
|
|
145
148
|
return boundary;
|
|
149
|
+
if (isPathInside(workspaceInstallPaths(boundaryRoot).localDir, path)) {
|
|
150
|
+
const prepared = await ensurePrivateState(boundaryRoot);
|
|
151
|
+
if (!prepared.ok)
|
|
152
|
+
return prepared;
|
|
153
|
+
}
|
|
146
154
|
}
|
|
147
155
|
const temporary = `${path}.tmp-${randomUUID()}`;
|
|
148
156
|
try {
|
|
@@ -182,6 +190,18 @@ export async function acquireInstallLock(path, operation, target, boundaryRoot)
|
|
|
182
190
|
existing: null,
|
|
183
191
|
};
|
|
184
192
|
}
|
|
193
|
+
if (isPathInside(workspaceInstallPaths(boundaryRoot).localDir, path)) {
|
|
194
|
+
const prepared = await ensurePrivateState(boundaryRoot);
|
|
195
|
+
if (!prepared.ok)
|
|
196
|
+
return {
|
|
197
|
+
ok: false,
|
|
198
|
+
status: 'failed',
|
|
199
|
+
lock: null,
|
|
200
|
+
path,
|
|
201
|
+
message: prepared.message,
|
|
202
|
+
existing: null,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
185
205
|
}
|
|
186
206
|
try {
|
|
187
207
|
await mkdir(dirname(path), { recursive: true });
|
|
@@ -202,10 +222,19 @@ export async function acquireInstallLock(path, operation, target, boundaryRoot)
|
|
|
202
222
|
operation,
|
|
203
223
|
target,
|
|
204
224
|
pid: process.pid,
|
|
225
|
+
...(captureProcessIdentity() === null
|
|
226
|
+
? {}
|
|
227
|
+
: { process_identity: captureProcessIdentity() }),
|
|
205
228
|
createdAt: new Date().toISOString(),
|
|
206
229
|
};
|
|
207
230
|
try {
|
|
208
|
-
await
|
|
231
|
+
await publishPrivateLock(path, Buffer.from(`${JSON.stringify(lock, null, 2)}\n`), async () => {
|
|
232
|
+
if (boundaryRoot !== undefined) {
|
|
233
|
+
const boundary = await ensureStatePathBoundary(boundaryRoot, path);
|
|
234
|
+
if (!boundary.ok)
|
|
235
|
+
throw new Error(boundary.message);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
209
238
|
return {
|
|
210
239
|
ok: true,
|
|
211
240
|
status: 'acquired',
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RuntimeManifest } from '../schemas/install.js';
|
|
2
|
+
import { type Outcome } from '../schemas/outcome.js';
|
|
3
|
+
export interface RuntimeReferenceReport {
|
|
4
|
+
readonly coverage: 'registered-workspaces-only';
|
|
5
|
+
readonly active: readonly {
|
|
6
|
+
workspaceRoot: string;
|
|
7
|
+
workspaceId: string;
|
|
8
|
+
executable: string;
|
|
9
|
+
}[];
|
|
10
|
+
readonly unknown: readonly string[];
|
|
11
|
+
/** A registry never proves that no unregistered workspace exists. */
|
|
12
|
+
readonly unregisteredCoverage: 'unknown';
|
|
13
|
+
}
|
|
14
|
+
export declare function runtimeReferencesDirectory(dataDir: string): string;
|
|
15
|
+
/** Lexical and real-path aliases of the same global data directory are one runtime. */
|
|
16
|
+
export declare function runtimeBindingMatches(dataDir: string, version: string, binding: {
|
|
17
|
+
readonly version: string;
|
|
18
|
+
readonly executable: string;
|
|
19
|
+
} | null | undefined): Promise<boolean>;
|
|
20
|
+
/** Reads only explicitly registered roots. No home-directory discovery occurs. */
|
|
21
|
+
export declare function inspectRuntimeReferences(dataDir: string, version: string): Promise<RuntimeReferenceReport>;
|
|
22
|
+
export interface RuntimeReferenceLease {
|
|
23
|
+
readonly path: string;
|
|
24
|
+
readonly lockPath: string;
|
|
25
|
+
readonly release: () => Promise<Outcome<null>>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Publish before machine binding while holding the runtime's deletion/install
|
|
29
|
+
* lock. A crash cannot create a new binding absent from the explicit registry.
|
|
30
|
+
*/
|
|
31
|
+
export declare function beginRuntimeReference(root: string, runtime: RuntimeManifest): Promise<Outcome<RuntimeReferenceLease>>;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { observePath, resolveRealpath, sha256Digest } from '../infra/fs.js';
|
|
5
|
+
import { inspectMachineConfig, inspectWorkspaceConfig, } from '../workspace/config.js';
|
|
6
|
+
import { isPathInside } from '../schemas/paths.js';
|
|
7
|
+
import { failed, succeeded } from '../schemas/outcome.js';
|
|
8
|
+
import { diagnosticCodes } from '../schemas/errors.js';
|
|
9
|
+
import { acquireInstallLock, ensureStatePathBoundary, readJsonDocument, releaseInstallLock, writeJsonAtomic, } from './ownership.js';
|
|
10
|
+
const referenceSchema = z.strictObject({
|
|
11
|
+
schema: z.literal('pappra/runtime-reference/1'),
|
|
12
|
+
workspaceRoot: z.string().min(1).max(4096).refine(isAbsolute),
|
|
13
|
+
workspaceId: z.uuid(),
|
|
14
|
+
rootIdentity: z.strictObject({
|
|
15
|
+
dev: z.number().int().nonnegative(),
|
|
16
|
+
ino: z.number().int().nonnegative(),
|
|
17
|
+
}),
|
|
18
|
+
intendedRuntime: z.strictObject({
|
|
19
|
+
version: z.string().min(1).max(128),
|
|
20
|
+
executable: z.string().min(1).max(4096),
|
|
21
|
+
runtimeRoot: z.string().min(1).max(4096),
|
|
22
|
+
}),
|
|
23
|
+
registeredAt: z.iso.datetime({ offset: true }),
|
|
24
|
+
});
|
|
25
|
+
export function runtimeReferencesDirectory(dataDir) {
|
|
26
|
+
return join(resolve(dataDir), 'runtime-references');
|
|
27
|
+
}
|
|
28
|
+
/** Lexical and real-path aliases of the same global data directory are one runtime. */
|
|
29
|
+
export async function runtimeBindingMatches(dataDir, version, binding) {
|
|
30
|
+
if (binding?.version !== version)
|
|
31
|
+
return false;
|
|
32
|
+
const target = join(resolve(dataDir), 'runtimes', version);
|
|
33
|
+
const executable = resolve(binding.executable);
|
|
34
|
+
if (isPathInside(target, executable))
|
|
35
|
+
return true;
|
|
36
|
+
const realTarget = await resolveRealpath(target);
|
|
37
|
+
const realExecutable = await resolveRealpath(executable);
|
|
38
|
+
return (realTarget !== null &&
|
|
39
|
+
realExecutable !== null &&
|
|
40
|
+
isPathInside(realTarget, realExecutable));
|
|
41
|
+
}
|
|
42
|
+
/** Reads only explicitly registered roots. No home-directory discovery occurs. */
|
|
43
|
+
export async function inspectRuntimeReferences(dataDir, version) {
|
|
44
|
+
const active = [];
|
|
45
|
+
const unknown = [];
|
|
46
|
+
const canonicalDataDir = (await resolveRealpath(dataDir)) ?? resolve(dataDir);
|
|
47
|
+
const directory = runtimeReferencesDirectory(canonicalDataDir);
|
|
48
|
+
const report = () => ({
|
|
49
|
+
coverage: 'registered-workspaces-only',
|
|
50
|
+
active,
|
|
51
|
+
unknown,
|
|
52
|
+
unregisteredCoverage: 'unknown',
|
|
53
|
+
});
|
|
54
|
+
const boundary = await ensureStatePathBoundary(canonicalDataDir, directory);
|
|
55
|
+
if (!boundary.ok) {
|
|
56
|
+
unknown.push('registry_path_unsafe');
|
|
57
|
+
return report();
|
|
58
|
+
}
|
|
59
|
+
const presence = await observePath(directory).catch(() => null);
|
|
60
|
+
if (presence?.kind === 'missing') {
|
|
61
|
+
unknown.push('registry_missing');
|
|
62
|
+
return report();
|
|
63
|
+
}
|
|
64
|
+
if (presence?.kind !== 'directory') {
|
|
65
|
+
unknown.push('registry_unreadable_or_redirected');
|
|
66
|
+
return report();
|
|
67
|
+
}
|
|
68
|
+
let names;
|
|
69
|
+
try {
|
|
70
|
+
names = (await readdir(directory)).sort();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
unknown.push('registry_unreadable');
|
|
74
|
+
return report();
|
|
75
|
+
}
|
|
76
|
+
if (names.length > 4096) {
|
|
77
|
+
unknown.push('registry_entry_limit');
|
|
78
|
+
return report();
|
|
79
|
+
}
|
|
80
|
+
for (const name of names) {
|
|
81
|
+
if (!/^sha256-[0-9a-f]{64}\.json$/.test(name)) {
|
|
82
|
+
unknown.push('unexpected_registry_entry');
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const read = await readJsonDocument(join(directory, name), referenceSchema);
|
|
86
|
+
if (!read.value) {
|
|
87
|
+
unknown.push(`reference_unreadable:${name}`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const entry = read.value;
|
|
91
|
+
const root = await observePath(entry.workspaceRoot).catch(() => null);
|
|
92
|
+
const canonical = await resolveRealpath(entry.workspaceRoot);
|
|
93
|
+
if (root?.kind !== 'directory' ||
|
|
94
|
+
!root.stats ||
|
|
95
|
+
canonical !== entry.workspaceRoot ||
|
|
96
|
+
root.stats.dev !== entry.rootIdentity.dev ||
|
|
97
|
+
root.stats.ino !== entry.rootIdentity.ino) {
|
|
98
|
+
unknown.push(`workspace_missing_or_replaced:${name}`);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const workspace = await inspectWorkspaceConfig(entry.workspaceRoot);
|
|
102
|
+
if (workspace.value?.workspaceId !== entry.workspaceId) {
|
|
103
|
+
unknown.push(`workspace_identity_unknown:${name}`);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const machine = await inspectMachineConfig(entry.workspaceRoot);
|
|
107
|
+
if (!machine.value) {
|
|
108
|
+
unknown.push(`machine_binding_unknown:${name}`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const binding = machine.value.runtime;
|
|
112
|
+
if (binding && (await runtimeBindingMatches(dataDir, version, binding)))
|
|
113
|
+
active.push({
|
|
114
|
+
workspaceRoot: entry.workspaceRoot,
|
|
115
|
+
workspaceId: entry.workspaceId,
|
|
116
|
+
executable: binding.executable,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return report();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Publish before machine binding while holding the runtime's deletion/install
|
|
123
|
+
* lock. A crash cannot create a new binding absent from the explicit registry.
|
|
124
|
+
*/
|
|
125
|
+
export async function beginRuntimeReference(root, runtime) {
|
|
126
|
+
const canonical = await resolveRealpath(root);
|
|
127
|
+
const observation = canonical === null ? null : await observePath(canonical).catch(() => null);
|
|
128
|
+
const workspace = canonical === null ? null : await inspectWorkspaceConfig(canonical);
|
|
129
|
+
if (!canonical ||
|
|
130
|
+
observation?.kind !== 'directory' ||
|
|
131
|
+
!observation.stats ||
|
|
132
|
+
!workspace?.value)
|
|
133
|
+
return failed(diagnosticCodes.initPlanStale, 'Cannot register a runtime reference for an unverified workspace root.');
|
|
134
|
+
const selectedDataDir = dirname(dirname(resolve(runtime.runtimeRoot)));
|
|
135
|
+
if (join(selectedDataDir, 'runtimes', runtime.runtimeVersion) !==
|
|
136
|
+
resolve(runtime.runtimeRoot) ||
|
|
137
|
+
!isPathInside(runtime.runtimeRoot, runtime.executable))
|
|
138
|
+
return failed(diagnosticCodes.initTargetUnsafe, 'Runtime reference is not inside its exact version directory.');
|
|
139
|
+
const dataDir = (await resolveRealpath(selectedDataDir)) ?? selectedDataDir;
|
|
140
|
+
const lockPath = join(dataDir, 'runtimes', '.locks', `${runtime.runtimeVersion}.lock`);
|
|
141
|
+
const entry = {
|
|
142
|
+
schema: 'pappra/runtime-reference/1',
|
|
143
|
+
workspaceRoot: canonical,
|
|
144
|
+
workspaceId: workspace.value.workspaceId,
|
|
145
|
+
rootIdentity: { dev: observation.stats.dev, ino: observation.stats.ino },
|
|
146
|
+
intendedRuntime: {
|
|
147
|
+
version: runtime.runtimeVersion,
|
|
148
|
+
executable: runtime.executable,
|
|
149
|
+
runtimeRoot: runtime.runtimeRoot,
|
|
150
|
+
},
|
|
151
|
+
registeredAt: new Date().toISOString(),
|
|
152
|
+
};
|
|
153
|
+
const key = sha256Digest(JSON.stringify([
|
|
154
|
+
canonical,
|
|
155
|
+
entry.workspaceId,
|
|
156
|
+
entry.rootIdentity.dev,
|
|
157
|
+
entry.rootIdentity.ino,
|
|
158
|
+
])).replace(':', '-');
|
|
159
|
+
const path = join(runtimeReferencesDirectory(dataDir), `${key}.json`);
|
|
160
|
+
const boundary = await ensureStatePathBoundary(dataDir, path);
|
|
161
|
+
if (!boundary.ok)
|
|
162
|
+
return boundary;
|
|
163
|
+
let existing = await readJsonDocument(path, referenceSchema);
|
|
164
|
+
const unchanged = existing.value &&
|
|
165
|
+
existing.value.workspaceRoot === canonical &&
|
|
166
|
+
existing.value.workspaceId === entry.workspaceId &&
|
|
167
|
+
existing.value.rootIdentity.dev === entry.rootIdentity.dev &&
|
|
168
|
+
existing.value.rootIdentity.ino === entry.rootIdentity.ino &&
|
|
169
|
+
JSON.stringify(existing.value.intendedRuntime) ===
|
|
170
|
+
JSON.stringify(entry.intendedRuntime);
|
|
171
|
+
if (unchanged && (await observePath(lockPath)).kind === 'missing') {
|
|
172
|
+
const machine = await inspectMachineConfig(canonical);
|
|
173
|
+
if (machine.value?.runtime?.version === runtime.runtimeVersion &&
|
|
174
|
+
machine.value.runtime.executable === runtime.executable) {
|
|
175
|
+
// This binding is already published and protected from deletion by its
|
|
176
|
+
// verified registry entry. A read-only repeat needs no registration lease.
|
|
177
|
+
return succeeded({
|
|
178
|
+
path,
|
|
179
|
+
lockPath,
|
|
180
|
+
release: async () => succeeded(null),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const lock = await acquireInstallLock(lockPath, 'update-machine-runtime', runtime.runtimeRoot, dataDir);
|
|
185
|
+
if (!lock.ok || !lock.lock)
|
|
186
|
+
return failed(diagnosticCodes.initApplyResidual, lock.message ?? 'Runtime reference registration is locked.');
|
|
187
|
+
const release = () => releaseInstallLock(lockPath, lock.lock.token);
|
|
188
|
+
existing = await readJsonDocument(path, referenceSchema);
|
|
189
|
+
if (existing.exists &&
|
|
190
|
+
(!existing.value ||
|
|
191
|
+
existing.value.workspaceRoot !== canonical ||
|
|
192
|
+
existing.value.workspaceId !== entry.workspaceId ||
|
|
193
|
+
existing.value.rootIdentity.dev !== entry.rootIdentity.dev ||
|
|
194
|
+
existing.value.rootIdentity.ino !== entry.rootIdentity.ino)) {
|
|
195
|
+
await release();
|
|
196
|
+
return failed(diagnosticCodes.initApplyResidual, 'Existing runtime reference is unreadable or belongs to another workspace identity; it was preserved.');
|
|
197
|
+
}
|
|
198
|
+
if (existing.value &&
|
|
199
|
+
JSON.stringify(existing.value.intendedRuntime) ===
|
|
200
|
+
JSON.stringify(entry.intendedRuntime))
|
|
201
|
+
return succeeded({ path, lockPath, release });
|
|
202
|
+
const written = await writeJsonAtomic(path, entry, dataDir);
|
|
203
|
+
if (!written.ok) {
|
|
204
|
+
await release();
|
|
205
|
+
return written;
|
|
206
|
+
}
|
|
207
|
+
return succeeded({ path, lockPath, release });
|
|
208
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type PrivateLockObservation } from '../infra/private-lock.js';
|
|
2
|
+
import { type LocalResetResult } from './reset-local.js';
|
|
3
|
+
export interface RepairOptions {
|
|
4
|
+
readonly root: string;
|
|
5
|
+
readonly runtimeVersion?: string;
|
|
6
|
+
readonly installId?: string;
|
|
7
|
+
readonly dataDir?: string;
|
|
8
|
+
readonly apply?: boolean;
|
|
9
|
+
/** Explicit operator assertion that every potentially affected writer has stopped. */
|
|
10
|
+
readonly quarantineUnownedLocks?: boolean;
|
|
11
|
+
readonly resetLocal?: boolean;
|
|
12
|
+
readonly allWritersStopped?: boolean;
|
|
13
|
+
/** Test-only dependency seam; never supplied by the CLI. */
|
|
14
|
+
readonly afterLocalArchive?: () => Promise<void> | void;
|
|
15
|
+
}
|
|
16
|
+
export interface RepairEntry {
|
|
17
|
+
readonly journal: string;
|
|
18
|
+
readonly installId: string;
|
|
19
|
+
readonly operation: string;
|
|
20
|
+
readonly status: string;
|
|
21
|
+
}
|
|
22
|
+
export interface RepairResult {
|
|
23
|
+
readonly ok: boolean;
|
|
24
|
+
readonly mode: 'plan' | 'apply';
|
|
25
|
+
readonly entries: readonly RepairEntry[];
|
|
26
|
+
readonly repaired: readonly string[];
|
|
27
|
+
readonly locks: readonly string[];
|
|
28
|
+
readonly errors: readonly string[];
|
|
29
|
+
readonly lockStates: readonly Pick<PrivateLockObservation, 'file' | 'state'>[];
|
|
30
|
+
readonly preserved: readonly string[];
|
|
31
|
+
readonly quarantined: readonly string[];
|
|
32
|
+
readonly reset: LocalResetResult | null;
|
|
33
|
+
}
|
|
34
|
+
/** Bounded private lock inventory shared by doctor and explicit repair. */
|
|
35
|
+
export declare function inspectWorkspaceLocks(root: string): Promise<readonly PrivateLockObservation[]>;
|
|
36
|
+
/** Explicit, bounded journal recovery. Preview never removes a lock or changes state. */
|
|
37
|
+
export declare function repairInstall(options: RepairOptions): Promise<RepairResult>;
|