@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
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
export const SHELL_READINESS_BUDGET_MS = 10_000;
|
|
2
|
+
export const SHELL_READINESS_INTERVAL_MS = 100;
|
|
3
|
+
export const SHELL_READINESS_MAX_SAMPLES = 100;
|
|
4
|
+
const MAX_CAPTURED_PROCESSES = 8;
|
|
5
|
+
const SHELL_NAMES = new Set([
|
|
6
|
+
'sh',
|
|
7
|
+
'bash',
|
|
8
|
+
'dash',
|
|
9
|
+
'zsh',
|
|
10
|
+
'fish',
|
|
11
|
+
'ksh',
|
|
12
|
+
'mksh',
|
|
13
|
+
'csh',
|
|
14
|
+
'tcsh',
|
|
15
|
+
'elvish',
|
|
16
|
+
'xonsh',
|
|
17
|
+
'nu',
|
|
18
|
+
'pwsh',
|
|
19
|
+
'powershell',
|
|
20
|
+
'cmd',
|
|
21
|
+
]);
|
|
22
|
+
const AGENT_NAMES = new Set([
|
|
23
|
+
'pi',
|
|
24
|
+
'codex',
|
|
25
|
+
'claude',
|
|
26
|
+
'gemini',
|
|
27
|
+
'cursor',
|
|
28
|
+
'devin',
|
|
29
|
+
'agy',
|
|
30
|
+
'cline',
|
|
31
|
+
'omp',
|
|
32
|
+
'opencode',
|
|
33
|
+
'copilot',
|
|
34
|
+
'kimi',
|
|
35
|
+
'kiro',
|
|
36
|
+
'droid',
|
|
37
|
+
'amp',
|
|
38
|
+
'grok',
|
|
39
|
+
'hermes',
|
|
40
|
+
'kilo',
|
|
41
|
+
'qodercli',
|
|
42
|
+
'qwen',
|
|
43
|
+
'letta',
|
|
44
|
+
'maki',
|
|
45
|
+
'muse',
|
|
46
|
+
]);
|
|
47
|
+
function normalizedName(value) {
|
|
48
|
+
const tail = value.split(/[\\/]/).at(-1) ?? value;
|
|
49
|
+
return tail
|
|
50
|
+
.replace(/^-+/, '')
|
|
51
|
+
.replace(/\.exe$/i, '')
|
|
52
|
+
.toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
function validNumber(value) {
|
|
55
|
+
return value !== null && Number.isSafeInteger(value) && value > 0;
|
|
56
|
+
}
|
|
57
|
+
function identityCode(pane, expected) {
|
|
58
|
+
if (pane.paneId !== expected.paneId)
|
|
59
|
+
return 'pane_id_mismatch';
|
|
60
|
+
if (pane.workspaceId !== expected.workspaceId)
|
|
61
|
+
return 'workspace_id_mismatch';
|
|
62
|
+
if (pane.terminalId !== expected.terminalId)
|
|
63
|
+
return 'terminal_id_mismatch';
|
|
64
|
+
if (pane.cwd === null)
|
|
65
|
+
return 'pane_cwd_missing';
|
|
66
|
+
return pane.cwd === expected.cwd ? null : 'pane_cwd_mismatch';
|
|
67
|
+
}
|
|
68
|
+
function sampleProcessFacts(processInfo) {
|
|
69
|
+
if (!Array.isArray(processInfo.foregroundProcesses))
|
|
70
|
+
return {
|
|
71
|
+
processes: [],
|
|
72
|
+
truncated: 0,
|
|
73
|
+
error: 'foreground_processes_malformed',
|
|
74
|
+
};
|
|
75
|
+
const processes = [];
|
|
76
|
+
for (const process of processInfo.foregroundProcesses) {
|
|
77
|
+
if (!validNumber(process.pid) ||
|
|
78
|
+
typeof process.name !== 'string' ||
|
|
79
|
+
process.name.length === 0)
|
|
80
|
+
return {
|
|
81
|
+
processes: [],
|
|
82
|
+
truncated: 0,
|
|
83
|
+
error: 'foreground_process_fact_malformed',
|
|
84
|
+
};
|
|
85
|
+
processes.push({ pid: process.pid, name: normalizedName(process.name) });
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
processes: processes.slice(0, MAX_CAPTURED_PROCESSES),
|
|
89
|
+
truncated: Math.max(0, processes.length - MAX_CAPTURED_PROCESSES),
|
|
90
|
+
error: null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function classifyProcessInfo(processInfo) {
|
|
94
|
+
const shellPid = processInfo.shellPid;
|
|
95
|
+
const group = processInfo.foregroundProcessGroupId;
|
|
96
|
+
const facts = sampleProcessFacts(processInfo);
|
|
97
|
+
const stateBase = {
|
|
98
|
+
shellPid: validNumber(shellPid) ? shellPid : null,
|
|
99
|
+
group: validNumber(group) ? group : null,
|
|
100
|
+
shellName: null,
|
|
101
|
+
processes: facts.processes,
|
|
102
|
+
processesTruncated: facts.truncated,
|
|
103
|
+
};
|
|
104
|
+
if (shellPid !== null && !validNumber(shellPid))
|
|
105
|
+
return { ...stateBase, state: 'failed', code: 'shell_pid_malformed' };
|
|
106
|
+
if (group !== null && !validNumber(group))
|
|
107
|
+
return {
|
|
108
|
+
...stateBase,
|
|
109
|
+
state: 'failed',
|
|
110
|
+
code: 'foreground_group_malformed',
|
|
111
|
+
};
|
|
112
|
+
if (facts.error !== null)
|
|
113
|
+
return { ...stateBase, state: 'failed', code: facts.error };
|
|
114
|
+
if (shellPid === null)
|
|
115
|
+
return { ...stateBase, state: 'unknown', code: 'shell_pid_missing' };
|
|
116
|
+
if (group === null)
|
|
117
|
+
return { ...stateBase, state: 'unknown', code: 'foreground_group_missing' };
|
|
118
|
+
if (group !== shellPid)
|
|
119
|
+
return {
|
|
120
|
+
...stateBase,
|
|
121
|
+
state: 'not_ready',
|
|
122
|
+
code: 'foreground_group_not_shell',
|
|
123
|
+
};
|
|
124
|
+
if (processInfo.foregroundProcesses.length === 0)
|
|
125
|
+
return {
|
|
126
|
+
...stateBase,
|
|
127
|
+
state: 'unknown',
|
|
128
|
+
code: 'foreground_processes_empty',
|
|
129
|
+
};
|
|
130
|
+
if (processInfo.foregroundProcesses.some((process) => process.pid !== shellPid)) {
|
|
131
|
+
const agent = facts.processes.find((process) => AGENT_NAMES.has(process.name));
|
|
132
|
+
return agent === undefined
|
|
133
|
+
? { ...stateBase, state: 'not_ready', code: 'extra_foreground_process' }
|
|
134
|
+
: { ...stateBase, state: 'failed', code: `existing_agent:${agent.name}` };
|
|
135
|
+
}
|
|
136
|
+
const shellName = facts.processes[0]?.name ?? null;
|
|
137
|
+
if (shellName === null || !SHELL_NAMES.has(shellName))
|
|
138
|
+
return {
|
|
139
|
+
...stateBase,
|
|
140
|
+
state: 'not_ready',
|
|
141
|
+
code: 'unrecognized_shell',
|
|
142
|
+
shellName,
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
...stateBase,
|
|
146
|
+
state: 'ready',
|
|
147
|
+
code: null,
|
|
148
|
+
shellName,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Verifies the installed Herdr available-shell predicate for one already owned pane.
|
|
153
|
+
* Process readiness is bounded local polling in the caller's single apply operation;
|
|
154
|
+
* it is not an event signal, a model wakeup, or an agent-completion wait.
|
|
155
|
+
*/
|
|
156
|
+
export async function verifyAvailableShell(ops, identity, options = {}) {
|
|
157
|
+
const clock = options.clock ?? Date.now;
|
|
158
|
+
const sleeper = options.sleeper ??
|
|
159
|
+
((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
160
|
+
const shouldAbort = options.shouldAbort ?? (() => false);
|
|
161
|
+
const budgetMs = options.budgetMs ?? SHELL_READINESS_BUDGET_MS;
|
|
162
|
+
const intervalMs = options.intervalMs ?? SHELL_READINESS_INTERVAL_MS;
|
|
163
|
+
const maxSamples = options.maxSamples ?? SHELL_READINESS_MAX_SAMPLES;
|
|
164
|
+
if (!Number.isSafeInteger(budgetMs) ||
|
|
165
|
+
budgetMs < 1 ||
|
|
166
|
+
budgetMs > SHELL_READINESS_BUDGET_MS)
|
|
167
|
+
return immediateFailure(identity, 'budget_invalid');
|
|
168
|
+
if (!Number.isSafeInteger(intervalMs) ||
|
|
169
|
+
intervalMs < 1 ||
|
|
170
|
+
intervalMs > SHELL_READINESS_INTERVAL_MS)
|
|
171
|
+
return immediateFailure(identity, 'interval_invalid');
|
|
172
|
+
if (!Number.isSafeInteger(maxSamples) ||
|
|
173
|
+
maxSamples < 1 ||
|
|
174
|
+
maxSamples > SHELL_READINESS_MAX_SAMPLES)
|
|
175
|
+
return immediateFailure(identity, 'sample_limit_invalid');
|
|
176
|
+
const started = clock();
|
|
177
|
+
const deadline = started + budgetMs;
|
|
178
|
+
let attempts = 0;
|
|
179
|
+
let pinnedShellPid = null;
|
|
180
|
+
let latest = null;
|
|
181
|
+
const samples = [];
|
|
182
|
+
const result = (state, code, pane) => ({
|
|
183
|
+
status: state,
|
|
184
|
+
code,
|
|
185
|
+
evidence: {
|
|
186
|
+
state,
|
|
187
|
+
code,
|
|
188
|
+
attempts,
|
|
189
|
+
elapsed_ms: Math.max(0, clock() - started),
|
|
190
|
+
pane_id: pane.paneId,
|
|
191
|
+
workspace_id: pane.workspaceId,
|
|
192
|
+
terminal_id: pane.terminalId,
|
|
193
|
+
cwd: pane.cwd,
|
|
194
|
+
shell_pid: pinnedShellPid ?? latest?.shell_pid ?? null,
|
|
195
|
+
foreground_process_group_id: latest?.foreground_process_group_id ?? null,
|
|
196
|
+
shell_name: latest?.shell_name ?? null,
|
|
197
|
+
last_state: latest?.state ?? 'not_observed',
|
|
198
|
+
last_code: latest?.code ?? null,
|
|
199
|
+
limits: {
|
|
200
|
+
budget_ms: budgetMs,
|
|
201
|
+
interval_ms: intervalMs,
|
|
202
|
+
max_samples: maxSamples,
|
|
203
|
+
samples_truncated: Math.max(0, attempts - samples.length),
|
|
204
|
+
},
|
|
205
|
+
samples: [...samples],
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
const recordSample = (sample) => {
|
|
209
|
+
latest = sample;
|
|
210
|
+
if (samples.length < maxSamples)
|
|
211
|
+
samples.push(sample);
|
|
212
|
+
};
|
|
213
|
+
const terminal = (state, code) => result(state, code, identity);
|
|
214
|
+
const checkAbort = () => shouldAbort() ? terminal('aborted', 'abort_requested') : null;
|
|
215
|
+
const checkDeadline = (code) => clock() >= deadline ? terminal('timeout', code) : null;
|
|
216
|
+
const initialAbort = checkAbort();
|
|
217
|
+
if (initialAbort !== null)
|
|
218
|
+
return initialAbort;
|
|
219
|
+
const beforeDeadline = checkDeadline('deadline_before_identity');
|
|
220
|
+
if (beforeDeadline !== null)
|
|
221
|
+
return beforeDeadline;
|
|
222
|
+
const initial = await ops.paneGet(identity.paneId, Math.max(1, deadline - clock()));
|
|
223
|
+
const afterInitialAbort = checkAbort();
|
|
224
|
+
if (afterInitialAbort !== null)
|
|
225
|
+
return afterInitialAbort;
|
|
226
|
+
const afterInitialDeadline = checkDeadline('deadline_after_initial_identity');
|
|
227
|
+
if (afterInitialDeadline !== null)
|
|
228
|
+
return afterInitialDeadline;
|
|
229
|
+
if (!initial.ok)
|
|
230
|
+
return terminal(initial.status === 'unknown' ? 'unknown' : 'failed', `initial_identity:${initial.code}`);
|
|
231
|
+
const initialIdentity = identityCode(initial.value, identity);
|
|
232
|
+
if (initialIdentity !== null)
|
|
233
|
+
return terminal('failed', initialIdentity);
|
|
234
|
+
if (initial.value.agent !== null)
|
|
235
|
+
return terminal('failed', 'existing_agent');
|
|
236
|
+
for (;;) {
|
|
237
|
+
const beforeAbort = checkAbort();
|
|
238
|
+
if (beforeAbort !== null)
|
|
239
|
+
return beforeAbort;
|
|
240
|
+
const beforeSampleDeadline = checkDeadline('deadline_before_process_info');
|
|
241
|
+
if (beforeSampleDeadline !== null)
|
|
242
|
+
return beforeSampleDeadline;
|
|
243
|
+
if (attempts >= maxSamples)
|
|
244
|
+
return terminal('timeout', 'sample_limit_reached');
|
|
245
|
+
attempts += 1;
|
|
246
|
+
const sampleStart = clock();
|
|
247
|
+
const snapshot = await ops.paneProcessInfo(identity.paneId, Math.max(1, deadline - clock()));
|
|
248
|
+
const afterSampleAbort = checkAbort();
|
|
249
|
+
if (afterSampleAbort !== null)
|
|
250
|
+
return afterSampleAbort;
|
|
251
|
+
const afterSampleDeadline = checkDeadline('deadline_after_process_info');
|
|
252
|
+
if (afterSampleDeadline !== null)
|
|
253
|
+
return afterSampleDeadline;
|
|
254
|
+
if (!snapshot.ok) {
|
|
255
|
+
const status = snapshot.status === 'unknown' ? 'unknown' : 'failed';
|
|
256
|
+
recordSample({
|
|
257
|
+
elapsed_ms: Math.max(0, sampleStart - started),
|
|
258
|
+
state: status === 'unknown' ? 'unknown' : 'failed',
|
|
259
|
+
code: snapshot.code,
|
|
260
|
+
shell_pid: pinnedShellPid,
|
|
261
|
+
foreground_process_group_id: null,
|
|
262
|
+
shell_name: null,
|
|
263
|
+
foreground_processes: [],
|
|
264
|
+
processes_truncated: 0,
|
|
265
|
+
});
|
|
266
|
+
return terminal(status, `process_info:${snapshot.code}`);
|
|
267
|
+
}
|
|
268
|
+
const processInfo = snapshot.value;
|
|
269
|
+
if (processInfo.paneId !== identity.paneId)
|
|
270
|
+
return terminal('failed', 'pane_id_mismatch');
|
|
271
|
+
const classified = classifyProcessInfo(processInfo);
|
|
272
|
+
if (classified.shellPid !== null) {
|
|
273
|
+
if (pinnedShellPid === null)
|
|
274
|
+
pinnedShellPid = classified.shellPid;
|
|
275
|
+
else if (pinnedShellPid !== classified.shellPid) {
|
|
276
|
+
recordSample({
|
|
277
|
+
elapsed_ms: Math.max(0, sampleStart - started),
|
|
278
|
+
state: 'failed',
|
|
279
|
+
code: 'shell_incarnation_drift',
|
|
280
|
+
shell_pid: classified.shellPid,
|
|
281
|
+
foreground_process_group_id: classified.group,
|
|
282
|
+
shell_name: classified.shellName,
|
|
283
|
+
foreground_processes: classified.processes,
|
|
284
|
+
processes_truncated: classified.processesTruncated,
|
|
285
|
+
});
|
|
286
|
+
return terminal('failed', 'shell_incarnation_drift');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const sample = {
|
|
290
|
+
elapsed_ms: Math.max(0, sampleStart - started),
|
|
291
|
+
state: classified.state,
|
|
292
|
+
code: classified.code,
|
|
293
|
+
shell_pid: classified.shellPid,
|
|
294
|
+
foreground_process_group_id: classified.group,
|
|
295
|
+
shell_name: classified.shellName,
|
|
296
|
+
foreground_processes: classified.processes,
|
|
297
|
+
processes_truncated: classified.processesTruncated,
|
|
298
|
+
};
|
|
299
|
+
recordSample(sample);
|
|
300
|
+
if (classified.state === 'failed')
|
|
301
|
+
return terminal('failed', classified.code ?? 'process_facts_invalid');
|
|
302
|
+
if (classified.state === 'ready') {
|
|
303
|
+
const beforeFinalAbort = checkAbort();
|
|
304
|
+
if (beforeFinalAbort !== null)
|
|
305
|
+
return beforeFinalAbort;
|
|
306
|
+
const beforeFinalDeadline = checkDeadline('deadline_before_final_identity');
|
|
307
|
+
if (beforeFinalDeadline !== null)
|
|
308
|
+
return beforeFinalDeadline;
|
|
309
|
+
const finalIdentity = await ops.paneGet(identity.paneId, Math.max(1, deadline - clock()));
|
|
310
|
+
const afterFinalAbort = checkAbort();
|
|
311
|
+
if (afterFinalAbort !== null)
|
|
312
|
+
return afterFinalAbort;
|
|
313
|
+
const afterFinalDeadline = checkDeadline('deadline_after_final_identity');
|
|
314
|
+
if (afterFinalDeadline !== null)
|
|
315
|
+
return afterFinalDeadline;
|
|
316
|
+
if (!finalIdentity.ok)
|
|
317
|
+
return terminal(finalIdentity.status === 'unknown' ? 'unknown' : 'failed', `final_identity:${finalIdentity.code}`);
|
|
318
|
+
const finalCode = identityCode(finalIdentity.value, identity);
|
|
319
|
+
if (finalCode !== null)
|
|
320
|
+
return terminal('failed', finalCode);
|
|
321
|
+
if (finalIdentity.value.agent !== null)
|
|
322
|
+
return terminal('failed', 'agent_appeared_before_start');
|
|
323
|
+
return result('ready', null, identity);
|
|
324
|
+
}
|
|
325
|
+
if (attempts >= maxSamples)
|
|
326
|
+
return terminal('timeout', 'sample_limit_reached');
|
|
327
|
+
const remaining = deadline - clock();
|
|
328
|
+
if (remaining <= 0)
|
|
329
|
+
return terminal('timeout', classified.state === 'unknown'
|
|
330
|
+
? 'deadline_unknown'
|
|
331
|
+
: 'deadline_not_ready');
|
|
332
|
+
await sleeper(Math.min(intervalMs, remaining));
|
|
333
|
+
const afterSleepAbort = checkAbort();
|
|
334
|
+
if (afterSleepAbort !== null)
|
|
335
|
+
return afterSleepAbort;
|
|
336
|
+
const afterSleepDeadline = checkDeadline('deadline_after_wait');
|
|
337
|
+
if (afterSleepDeadline !== null)
|
|
338
|
+
return afterSleepDeadline;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function immediateFailure(identity, code) {
|
|
342
|
+
return {
|
|
343
|
+
status: 'failed',
|
|
344
|
+
code,
|
|
345
|
+
evidence: {
|
|
346
|
+
state: 'failed',
|
|
347
|
+
code,
|
|
348
|
+
attempts: 0,
|
|
349
|
+
elapsed_ms: 0,
|
|
350
|
+
pane_id: identity.paneId,
|
|
351
|
+
workspace_id: identity.workspaceId,
|
|
352
|
+
terminal_id: identity.terminalId,
|
|
353
|
+
cwd: identity.cwd,
|
|
354
|
+
shell_pid: null,
|
|
355
|
+
foreground_process_group_id: null,
|
|
356
|
+
shell_name: null,
|
|
357
|
+
last_state: 'not_observed',
|
|
358
|
+
last_code: null,
|
|
359
|
+
limits: {
|
|
360
|
+
budget_ms: 0,
|
|
361
|
+
interval_ms: 0,
|
|
362
|
+
max_samples: 0,
|
|
363
|
+
samples_truncated: 0,
|
|
364
|
+
},
|
|
365
|
+
samples: [],
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
}
|
package/dist/app/task-pin.d.ts
CHANGED
|
@@ -1,23 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
readonly schema: typeof TASK_PIN_SCHEMA;
|
|
4
|
-
readonly taskId: string;
|
|
5
|
-
readonly pinnedAt: string;
|
|
6
|
-
}
|
|
7
|
-
export interface TaskPinFailure {
|
|
8
|
-
readonly ok: false;
|
|
9
|
-
readonly message: string;
|
|
10
|
-
}
|
|
11
|
-
export declare function readTaskPin(root: string): Promise<TaskPin | null>;
|
|
12
|
-
export declare function pinTask(root: string, taskId: string): Promise<TaskPin | TaskPinFailure>;
|
|
13
|
-
export declare function unpinTask(root: string): Promise<{
|
|
14
|
-
ok: true;
|
|
15
|
-
}>;
|
|
16
|
-
/**
|
|
17
|
-
* Toggles the workspace-level auto-resume switch. The hook only injects a resume
|
|
18
|
-
* packet when this switch is on and a task pin exists; neither is inferred.
|
|
19
|
-
*/
|
|
20
|
-
export declare function setAutoResume(root: string, enabled: boolean): Promise<{
|
|
21
|
-
ok: true;
|
|
22
|
-
} | TaskPinFailure>;
|
|
23
|
-
export declare function readAutoResume(root: string): Promise<boolean>;
|
|
1
|
+
/** Workspace task selection is a continuity capability. */
|
|
2
|
+
export * from '../continuity/task-pin.js';
|
package/dist/app/task-pin.js
CHANGED
|
@@ -1,113 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { dirname, join } from 'node:path';
|
|
4
|
-
import { readTextFileBounded } from '../infra/fs.js';
|
|
5
|
-
import { workspaceConfigSchema } from '../schemas/workspace.js';
|
|
6
|
-
export const TASK_PIN_SCHEMA = 'pappra/task-pin/1';
|
|
7
|
-
const TASK_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
8
|
-
function pinPath(root) {
|
|
9
|
-
return join(root, '.pappra', 'local', 'current-task');
|
|
10
|
-
}
|
|
11
|
-
export async function readTaskPin(root) {
|
|
12
|
-
const read = await readTextFileBounded(pinPath(root), 64 * 1024);
|
|
13
|
-
if (!read.ok)
|
|
14
|
-
return null;
|
|
15
|
-
try {
|
|
16
|
-
const value = JSON.parse(read.value.text);
|
|
17
|
-
if (typeof value !== 'object' ||
|
|
18
|
-
value === null ||
|
|
19
|
-
!('schema' in value) ||
|
|
20
|
-
value.schema !== TASK_PIN_SCHEMA ||
|
|
21
|
-
!('taskId' in value) ||
|
|
22
|
-
typeof value.taskId !== 'string' ||
|
|
23
|
-
!TASK_ID.test(value.taskId)) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
export async function pinTask(root, taskId) {
|
|
33
|
-
if (!TASK_ID.test(taskId)) {
|
|
34
|
-
return {
|
|
35
|
-
ok: false,
|
|
36
|
-
message: 'task id must be a short identifier (letters, digits, dot, dash, underscore).',
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
const pin = {
|
|
40
|
-
schema: TASK_PIN_SCHEMA,
|
|
41
|
-
taskId,
|
|
42
|
-
pinnedAt: new Date().toISOString(),
|
|
43
|
-
};
|
|
44
|
-
const target = pinPath(root);
|
|
45
|
-
await mkdir(dirname(target), { recursive: true, mode: 0o700 });
|
|
46
|
-
const temporary = `${target}.tmp-${randomUUID()}`;
|
|
47
|
-
await writeFile(temporary, `${JSON.stringify(pin, null, 2)}\n`, {
|
|
48
|
-
mode: 0o600,
|
|
49
|
-
});
|
|
50
|
-
await rename(temporary, target);
|
|
51
|
-
return pin;
|
|
52
|
-
}
|
|
53
|
-
export async function unpinTask(root) {
|
|
54
|
-
const target = pinPath(root);
|
|
55
|
-
const { rm } = await import('node:fs/promises');
|
|
56
|
-
await rm(target, { force: true });
|
|
57
|
-
return { ok: true };
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Toggles the workspace-level auto-resume switch. The hook only injects a resume
|
|
61
|
-
* packet when this switch is on and a task pin exists; neither is inferred.
|
|
62
|
-
*/
|
|
63
|
-
export async function setAutoResume(root, enabled) {
|
|
64
|
-
const configPath = join(root, '.pappra', 'workspace.json');
|
|
65
|
-
const read = await readTextFileBounded(configPath, 1024 * 1024);
|
|
66
|
-
if (!read.ok) {
|
|
67
|
-
return { ok: false, message: read.message };
|
|
68
|
-
}
|
|
69
|
-
let raw;
|
|
70
|
-
try {
|
|
71
|
-
raw = JSON.parse(read.value.text);
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
return {
|
|
75
|
-
ok: false,
|
|
76
|
-
message: `workspace.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
const parsed = workspaceConfigSchema.safeParse({
|
|
80
|
-
...(typeof raw === 'object' && raw !== null ? raw : {}),
|
|
81
|
-
continuity: { autoResume: enabled },
|
|
82
|
-
});
|
|
83
|
-
if (!parsed.success) {
|
|
84
|
-
return {
|
|
85
|
-
ok: false,
|
|
86
|
-
message: `workspace.json rejected the continuity switch: ${parsed.error.issues[0]?.message ?? 'invalid'}`,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
const backup = join(root, '.pappra', 'local', 'backups', `task-pin-${randomUUID()}`, 'workspace.json');
|
|
90
|
-
await mkdir(dirname(backup), { recursive: true, mode: 0o700 });
|
|
91
|
-
await writeFile(backup, read.value.text, { mode: 0o600 });
|
|
92
|
-
const temporary = `${configPath}.tmp-${randomUUID()}`;
|
|
93
|
-
await writeFile(temporary, `${JSON.stringify(parsed.data, null, 2)}\n`);
|
|
94
|
-
await rename(temporary, configPath);
|
|
95
|
-
return { ok: true };
|
|
96
|
-
}
|
|
97
|
-
export async function readAutoResume(root) {
|
|
98
|
-
const read = await readFile(join(root, '.pappra', 'workspace.json'), 'utf8');
|
|
99
|
-
try {
|
|
100
|
-
const raw = JSON.parse(read);
|
|
101
|
-
if (typeof raw !== 'object' || raw === null || !('continuity' in raw)) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
const continuity = raw.continuity;
|
|
105
|
-
return (typeof continuity === 'object' &&
|
|
106
|
-
continuity !== null &&
|
|
107
|
-
'autoResume' in continuity &&
|
|
108
|
-
continuity.autoResume === true);
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
1
|
+
/** Workspace task selection is a continuity capability. */
|
|
2
|
+
export * from '../continuity/task-pin.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CliOutput } from '../output.js';
|
|
2
|
+
export declare const collaborationBindingUsage: string;
|
|
3
|
+
export type BindingAction = 'list' | 'resolve' | 'bind' | 'unbind';
|
|
4
|
+
export interface CollaborationBindingOptions {
|
|
5
|
+
readonly action: BindingAction | null;
|
|
6
|
+
readonly root: string | null;
|
|
7
|
+
readonly taskId: string | null;
|
|
8
|
+
readonly record: string | null;
|
|
9
|
+
readonly roots: string | null;
|
|
10
|
+
readonly apply: boolean;
|
|
11
|
+
readonly json: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function runCollaborationBindingCommand(options: CollaborationBindingOptions, output: CliOutput): Promise<number>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { COLLAB_BINDING_RESULT_SCHEMA, bindAlias, listBindings, resolveBinding, unbindAlias, } from '../../app/collab-bindings.js';
|
|
2
|
+
export const collaborationBindingUsage = 'pappra collab binding list [--root <abs workspace>] [--json]\n' +
|
|
3
|
+
'pappra collab binding resolve --task-id <alias> [--root <abs>] [--roots <abs roots.json>] [--json]\n' +
|
|
4
|
+
'pappra collab binding bind --task-id <alias> --record <abs task.json> [--root <abs>] [--apply] [--json]\n' +
|
|
5
|
+
'pappra collab binding unbind --task-id <alias> [--root <abs>] [--apply] [--json]\n\n' +
|
|
6
|
+
'Workspace-local alias pointers to authoritative collaboration records. The index\n' +
|
|
7
|
+
'contains pointer metadata only; it never copies protocol content,\n' +
|
|
8
|
+
'starts/adopts an execution or treats a target as live. Mutations plan by default.';
|
|
9
|
+
function outputFailure(output, options, value, exitCode) {
|
|
10
|
+
const body = {
|
|
11
|
+
schema: COLLAB_BINDING_RESULT_SCHEMA,
|
|
12
|
+
ok: false,
|
|
13
|
+
status: exitCode === 2 ? 'invalid' : 'needs_input',
|
|
14
|
+
action: options.action,
|
|
15
|
+
code: value.code,
|
|
16
|
+
message: value.message,
|
|
17
|
+
nextAction: value.nextAction,
|
|
18
|
+
...(value.details === undefined ? {} : { details: value.details }),
|
|
19
|
+
};
|
|
20
|
+
if (options.json)
|
|
21
|
+
output.stdout(`${JSON.stringify(body)}\n`);
|
|
22
|
+
else {
|
|
23
|
+
output.stdout(`Binding ${exitCode === 2 ? 'invalid' : 'needs input'}: ${value.message}\n`);
|
|
24
|
+
if (value.nextAction)
|
|
25
|
+
output.stdout(` ${value.nextAction}\n`);
|
|
26
|
+
for (const detail of value.details ?? [])
|
|
27
|
+
output.stdout(` - ${detail}\n`);
|
|
28
|
+
}
|
|
29
|
+
return exitCode;
|
|
30
|
+
}
|
|
31
|
+
function invalid(output, options, message) {
|
|
32
|
+
return outputFailure(output, options, {
|
|
33
|
+
ok: false,
|
|
34
|
+
code: 'invalid_options',
|
|
35
|
+
message,
|
|
36
|
+
nextAction: `Use ${collaborationBindingUsage.split('\n')[0]}.`,
|
|
37
|
+
}, 2);
|
|
38
|
+
}
|
|
39
|
+
function emitList(value, output) {
|
|
40
|
+
const counts = value.counts;
|
|
41
|
+
output.stdout(`Bindings: ${String(counts.bindings)} binding(s), ${String(counts.unbound)} tombstone(s).\n`);
|
|
42
|
+
for (const entry of value.bindings) {
|
|
43
|
+
output.stdout(` ${String(entry.alias)} -> ${String(entry.record)} [${String(entry.status)}]\n`);
|
|
44
|
+
}
|
|
45
|
+
if (value.truncated === true) {
|
|
46
|
+
const omitted = value.omitted;
|
|
47
|
+
output.stdout(` omitted: ${String(omitted.bindings)} binding(s), ${String(omitted.unbound)} tombstone(s)\n`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function emitResolve(value, output) {
|
|
51
|
+
output.stdout(`Alias ${String(value.alias)}: ${String(value.status)} (usable ${String(value.usable)}).\n`);
|
|
52
|
+
const target = value.target;
|
|
53
|
+
if (target) {
|
|
54
|
+
output.stdout(` record: ${String(target.record)} revision ${String(target.state_revision)}; coordinator epoch ${String(target.coordinator_epoch)}; frozen ${String(target.dispatch_frozen)}\n`);
|
|
55
|
+
output.stdout(` attempts: ${String(target.attempt_count)}; scope: ${String(value.scope)}; live status: ${String(value.liveStatus)}\n`);
|
|
56
|
+
}
|
|
57
|
+
for (const candidate of value.candidates)
|
|
58
|
+
output.stdout(` candidate: ${candidate}\n`);
|
|
59
|
+
for (const error of value.errors)
|
|
60
|
+
output.stdout(` ! ${error}\n`);
|
|
61
|
+
}
|
|
62
|
+
function emitMutation(value, action, output) {
|
|
63
|
+
if (value.status === 'planned') {
|
|
64
|
+
output.stdout(action === 'bind'
|
|
65
|
+
? 'Bind plan validates the alias, target and index; nothing was written.\n'
|
|
66
|
+
: 'Unbind plan validates the alias and index; nothing was written.\n');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
output.stdout(`${action === 'bind' ? 'Bind' : 'Unbind'} ${String(value.status)}; applied ${String(value.applied)}.\n`);
|
|
70
|
+
const target = value.target;
|
|
71
|
+
if (target)
|
|
72
|
+
output.stdout(` target: ${String(target.record)}\n`);
|
|
73
|
+
}
|
|
74
|
+
export async function runCollaborationBindingCommand(options, output) {
|
|
75
|
+
if (options.action === null)
|
|
76
|
+
return invalid(output, options, 'binding requires list/resolve/bind/unbind.');
|
|
77
|
+
const serviceOptions = {
|
|
78
|
+
root: options.root,
|
|
79
|
+
taskId: options.taskId,
|
|
80
|
+
record: options.record,
|
|
81
|
+
roots: options.roots,
|
|
82
|
+
apply: options.apply,
|
|
83
|
+
};
|
|
84
|
+
const result = options.action === 'list'
|
|
85
|
+
? await listBindings(serviceOptions)
|
|
86
|
+
: options.action === 'resolve'
|
|
87
|
+
? await resolveBinding(serviceOptions)
|
|
88
|
+
: options.action === 'bind'
|
|
89
|
+
? await bindAlias(serviceOptions)
|
|
90
|
+
: await unbindAlias(serviceOptions);
|
|
91
|
+
if (!result.ok) {
|
|
92
|
+
const exitCode = result.code === 'invalid_options' ||
|
|
93
|
+
result.code === 'workspace_unavailable' ||
|
|
94
|
+
result.code === 'bindings_invalid' ||
|
|
95
|
+
result.code === 'roots_invalid'
|
|
96
|
+
? 2
|
|
97
|
+
: 1;
|
|
98
|
+
return outputFailure(output, options, result, exitCode);
|
|
99
|
+
}
|
|
100
|
+
if (options.json)
|
|
101
|
+
output.stdout(`${JSON.stringify(result.value)}\n`);
|
|
102
|
+
else if (options.action === 'list')
|
|
103
|
+
emitList(result.value, output);
|
|
104
|
+
else if (options.action === 'resolve')
|
|
105
|
+
emitResolve(result.value, output);
|
|
106
|
+
else
|
|
107
|
+
emitMutation(result.value, options.action, output);
|
|
108
|
+
if (options.action === 'resolve') {
|
|
109
|
+
const status = result.value.status;
|
|
110
|
+
return status === 'binding' ||
|
|
111
|
+
status === 'discovery-unique' ||
|
|
112
|
+
status === 'unbound' ||
|
|
113
|
+
status === 'none'
|
|
114
|
+
? 0
|
|
115
|
+
: 1;
|
|
116
|
+
}
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type BootstrapAction, type VisibleBootstrapDependencies } from '../../app/collab-bootstrap.js';
|
|
2
|
+
import type { CliOutput } from '../output.js';
|
|
3
|
+
export declare const collaborationBootstrapUsage: string;
|
|
4
|
+
export interface CollaborationBootstrapOptions {
|
|
5
|
+
readonly action: BootstrapAction | null;
|
|
6
|
+
readonly taskRoot: string | null;
|
|
7
|
+
readonly requestFile: string | null;
|
|
8
|
+
readonly json: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function runCollaborationBootstrapCommand(options: CollaborationBootstrapOptions, output: CliOutput, dependencies?: VisibleBootstrapDependencies): Promise<number>;
|