create-walle 0.10.2 → 0.10.3
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 +3 -1
- package/lib/release-schema.json +2 -2
- package/lib/runtime-closure-policy.json +8 -7
- package/package.json +1 -1
- package/template/coding-task-manager/api-prompts.js +45 -7
- package/template/coding-task-manager/bin/claude-hook.js +55 -1
- package/template/coding-task-manager/bin/ctm-relay-client.js +35 -12
- package/template/coding-task-manager/bin/land.js +2069 -0
- package/template/coding-task-manager/bin/memory-census.js +4 -2
- package/template/coding-task-manager/bin/memory-sync-verify.js +151 -0
- package/template/coding-task-manager/bin/probe-refusal-scope.js +530 -0
- package/template/coding-task-manager/db.js +156 -32
- package/template/coding-task-manager/lib/acp/acp-client.js +383 -0
- package/template/coding-task-manager/lib/acp/dsh-context-truth.js +176 -0
- package/template/coding-task-manager/lib/acp/dsh-launch-model.js +302 -0
- package/template/coding-task-manager/lib/acp/dsh-mcp-servers.js +194 -0
- package/template/coding-task-manager/lib/acp/dsh-prompt-lane.js +616 -0
- package/template/coding-task-manager/lib/acp/dsh-route-patch.js +308 -0
- package/template/coding-task-manager/lib/acp/dsh-session-runtime.js +522 -0
- package/template/coding-task-manager/lib/acp/dsh-transcript.js +395 -0
- package/template/coding-task-manager/lib/acp/dsh-usage-log.js +162 -0
- package/template/coding-task-manager/lib/acp/ndjson-stdio-transport.js +225 -0
- package/template/coding-task-manager/lib/adopted-host-redial.js +47 -11
- package/template/coding-task-manager/lib/adopted-launch-environment.js +123 -0
- package/template/coding-task-manager/lib/agent-auto-update-runtime.js +0 -3
- package/template/coding-task-manager/lib/agent-auto-update.js +26 -10
- package/template/coding-task-manager/lib/agent-bundle/build.js +657 -0
- package/template/coding-task-manager/lib/agent-bundle/codex-install.js +558 -0
- package/template/coding-task-manager/lib/agent-bundle/drift.js +120 -0
- package/template/coding-task-manager/lib/agent-bundle/gc.js +522 -0
- package/template/coding-task-manager/lib/agent-bundle/inventory.js +346 -0
- package/template/coding-task-manager/lib/agent-bundle/push.js +717 -0
- package/template/coding-task-manager/lib/agent-bundle/skill-transfer.js +724 -0
- package/template/coding-task-manager/lib/agent-bundle/spawn.js +119 -0
- package/template/coding-task-manager/lib/agent-bundle/store.js +568 -0
- package/template/coding-task-manager/lib/agent-capabilities.js +142 -12
- package/template/coding-task-manager/lib/agent-cli-cache.js +30 -5
- package/template/coding-task-manager/lib/agent-definitions.js +19 -0
- package/template/coding-task-manager/lib/agent-hooks-codex-toml.js +28 -16
- package/template/coding-task-manager/lib/agent-hooks-codex-workspace.js +9 -1
- package/template/coding-task-manager/lib/agent-hooks-installer.js +23 -8
- package/template/coding-task-manager/lib/agent-hooks-prune.js +37 -11
- package/template/coding-task-manager/lib/agent-hooks-shim.js +105 -0
- package/template/coding-task-manager/lib/agent-presets.js +84 -1
- package/template/coding-task-manager/lib/agent-upgrade-jobs.js +12 -1
- package/template/coding-task-manager/lib/agent-version-service.js +74 -11
- package/template/coding-task-manager/lib/all-sessions-query.js +6 -2
- package/template/coding-task-manager/lib/api/agent-bundles.js +436 -0
- package/template/coding-task-manager/lib/api/backup-retention.js +83 -0
- package/template/coding-task-manager/lib/api/conversation-sync-routes.js +124 -0
- package/template/coding-task-manager/lib/api/diagnostics.js +549 -69
- package/template/coding-task-manager/lib/api/fleet.js +118 -0
- package/template/coding-task-manager/lib/api/hub-bridge.js +243 -0
- package/template/coding-task-manager/lib/api/ingress.js +9 -1
- package/template/coding-task-manager/lib/api/landing.js +403 -0
- package/template/coding-task-manager/lib/api/machines.js +691 -2
- package/template/coding-task-manager/lib/api/memory-sync-routes.js +335 -0
- package/template/coding-task-manager/lib/api/misc-ops.js +8 -3
- package/template/coding-task-manager/lib/api/remote-sessions.js +1326 -0
- package/template/coding-task-manager/lib/api/session-attach.js +623 -0
- package/template/coding-task-manager/lib/api/session-messages.js +1384 -0
- package/template/coding-task-manager/lib/api/sessions.js +77 -51
- package/template/coding-task-manager/lib/api/setup-network.js +544 -16
- package/template/coding-task-manager/lib/api/setup-wizard-handlers.js +884 -0
- package/template/coding-task-manager/lib/api/setup.js +829 -10
- package/template/coding-task-manager/lib/api/walle-bridge.js +9 -1
- package/template/coding-task-manager/lib/attachment-reconciler-wiring.js +5 -2
- package/template/coding-task-manager/lib/auth-rate-limit.js +23 -0
- package/template/coding-task-manager/lib/auth-rules.js +292 -21
- package/template/coding-task-manager/lib/background-stop.js +369 -0
- package/template/coding-task-manager/lib/best-of-attempts.js +4 -1
- package/template/coding-task-manager/lib/best-of-launch-plan.js +52 -0
- package/template/coding-task-manager/lib/box-keep-awake.js +89 -0
- package/template/coding-task-manager/lib/broadcast-reason-stats.js +152 -0
- package/template/coding-task-manager/lib/broadcast-serialize.js +5 -0
- package/template/coding-task-manager/lib/build-info.js +4 -2
- package/template/coding-task-manager/lib/claude-model-truth.js +13 -2
- package/template/coding-task-manager/lib/claude-transcript-terminal-renderer.js +17 -13
- package/template/coding-task-manager/lib/client-diag-handler.js +64 -2
- package/template/coding-task-manager/lib/codex-app-server-client.js +14 -16
- package/template/coding-task-manager/lib/codex-configuration-history.js +55 -0
- package/template/coding-task-manager/lib/codex-harness-qualification.js +43 -20
- package/template/coding-task-manager/lib/codex-rollout-history.js +743 -0
- package/template/coding-task-manager/lib/coding-agent-models.js +27 -12
- package/template/coding-task-manager/lib/coding-cli-skill-installer.js +44 -14
- package/template/coding-task-manager/lib/coding-session-model-adapters.js +43 -2
- package/template/coding-task-manager/lib/coding-session-model-owner.js +1 -1
- package/template/coding-task-manager/lib/coding-session-model-public.js +7 -1
- package/template/coding-task-manager/lib/coding-session-model-runtime.js +23 -4
- package/template/coding-task-manager/lib/coding-session-model-scan.js +31 -0
- package/template/coding-task-manager/lib/coding-session-model-state.js +7 -0
- package/template/coding-task-manager/lib/conversation-freshness-diagnostics.js +280 -0
- package/template/coding-task-manager/lib/conversation-item-clock.js +261 -0
- package/template/coding-task-manager/lib/conversation-source-registry.js +5 -0
- package/template/coding-task-manager/lib/conversation-store-v2.js +44 -0
- package/template/coding-task-manager/lib/conversation-sync.js +355 -0
- package/template/coding-task-manager/lib/create/resolve-launch-params.js +96 -12
- package/template/coding-task-manager/lib/create/worker-role-profile.js +4 -4
- package/template/coding-task-manager/lib/create-intent-lease.js +51 -0
- package/template/coding-task-manager/lib/ctm-session-context-api.js +97 -6
- package/template/coding-task-manager/lib/db-domain-tables.js +1 -0
- package/template/coding-task-manager/lib/db-domains.js +1 -0
- package/template/coding-task-manager/lib/db-owner-dispatch-probe.js +32 -0
- package/template/coding-task-manager/lib/db-owner-request-counts.js +178 -0
- package/template/coding-task-manager/lib/db-owner-worker-client.js +68 -14
- package/template/coding-task-manager/lib/db-schema-source-files.js +5 -0
- package/template/coding-task-manager/lib/deferred-restore-attempt.js +6 -2
- package/template/coding-task-manager/lib/feature-manifest.js +4 -0
- package/template/coding-task-manager/lib/fleet-conversation-route.js +97 -0
- package/template/coding-task-manager/lib/fleet-fanout.js +78 -0
- package/template/coding-task-manager/lib/fleet-hub.js +762 -0
- package/template/coding-task-manager/lib/fleet-origin-follower.js +252 -0
- package/template/coding-task-manager/lib/fleet-resolver-effects.js +147 -0
- package/template/coding-task-manager/lib/fleet-resolver-tick.js +148 -0
- package/template/coding-task-manager/lib/fleet-resolver.js +454 -0
- package/template/coding-task-manager/lib/fleet-session-list-routes.js +114 -0
- package/template/coding-task-manager/lib/fleet-session-lists.js +672 -0
- package/template/coding-task-manager/lib/fleet-update.js +798 -0
- package/template/coding-task-manager/lib/fs-watcher-root-watch.js +141 -0
- package/template/coding-task-manager/lib/fs-watcher.js +58 -42
- package/template/coding-task-manager/lib/harness-history-transition.js +50 -9
- package/template/coding-task-manager/lib/headless-term-service.js +84 -8
- package/template/coding-task-manager/lib/hermetic-agent-launch-guard.js +10 -0
- package/template/coding-task-manager/lib/history-transition-read-op.js +175 -8
- package/template/coding-task-manager/lib/host-cpu-categories.js +310 -0
- package/template/coding-task-manager/lib/host-fseventsd-sampler.js +399 -0
- package/template/coding-task-manager/lib/host-handoff.js +16 -19
- package/template/coding-task-manager/lib/host-stats.js +150 -3
- package/template/coding-task-manager/lib/hosted-sync-worker.js +43 -320
- package/template/coding-task-manager/lib/hub-bridge-access.js +798 -0
- package/template/coding-task-manager/lib/hub-bridge-box.js +409 -0
- package/template/coding-task-manager/lib/hub-bridge-mcp.js +380 -0
- package/template/coding-task-manager/lib/hub-bridge-mesh.js +313 -0
- package/template/coding-task-manager/lib/hub-bridge-ops.js +721 -0
- package/template/coding-task-manager/lib/hub-bridge.js +398 -0
- package/template/coding-task-manager/lib/idle-notify.js +491 -0
- package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +8 -0
- package/template/coding-task-manager/lib/instance-manifest.js +5 -1
- package/template/coding-task-manager/lib/instance-sync-status.js +18 -8
- package/template/coding-task-manager/lib/landing/agent-rule.txt +1 -0
- package/template/coding-task-manager/lib/landing/batch-runner.js +366 -0
- package/template/coding-task-manager/lib/landing/config.js +536 -0
- package/template/coding-task-manager/lib/landing/ctm-view.js +256 -0
- package/template/coding-task-manager/lib/landing/gate.js +360 -0
- package/template/coding-task-manager/lib/landing/git.js +344 -0
- package/template/coding-task-manager/lib/landing/lease.js +247 -0
- package/template/coding-task-manager/lib/landing/merge-delta.js +380 -0
- package/template/coding-task-manager/lib/landing/proof.js +223 -0
- package/template/coding-task-manager/lib/landing/queue.js +998 -0
- package/template/coding-task-manager/lib/landing/radar.js +489 -0
- package/template/coding-task-manager/lib/landing/resolve-cli.js +424 -0
- package/template/coding-task-manager/lib/landing/state.js +263 -0
- package/template/coding-task-manager/lib/landing/stop.js +49 -0
- package/template/coding-task-manager/lib/landing/sync-cli.js +475 -0
- package/template/coding-task-manager/lib/landing/sync.js +456 -0
- package/template/coding-task-manager/lib/landing/train.js +885 -0
- package/template/coding-task-manager/lib/landing/window.js +57 -0
- package/template/coding-task-manager/lib/launch-environment.js +121 -0
- package/template/coding-task-manager/lib/launch-model-override.js +36 -10
- package/template/coding-task-manager/lib/launch-plan-pool.js +2 -1
- package/template/coding-task-manager/lib/launch-plan-warm-cache.js +6 -0
- package/template/coding-task-manager/lib/lead-model-observer.js +57 -1
- package/template/coding-task-manager/lib/lead-model-options.js +160 -3
- package/template/coding-task-manager/lib/lead-model-restore-reconciliation.js +106 -24
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +352 -68
- package/template/coding-task-manager/lib/lead-model-switch.js +114 -10
- package/template/coding-task-manager/lib/lead-model-transition-runtime.js +14 -2
- package/template/coding-task-manager/lib/lead-model-transition-store.js +24 -6
- package/template/coding-task-manager/lib/lead-route-preflight.js +179 -0
- package/template/coding-task-manager/lib/lead-switch-history-mode.js +110 -0
- package/template/coding-task-manager/lib/lead-wire-agent.js +7 -0
- package/template/coding-task-manager/lib/lifecycle-wake-experiment.js +9 -2
- package/template/coding-task-manager/lib/live-defer-announce.js +45 -0
- package/template/coding-task-manager/lib/machine-registry.js +750 -5
- package/template/coding-task-manager/lib/macos-capabilities.js +32 -15
- package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +433 -53
- package/template/coding-task-manager/lib/mainloop-observability.js +14 -9
- package/template/coding-task-manager/lib/media-draft-owner.js +16 -3
- package/template/coding-task-manager/lib/memory-join.js +337 -0
- package/template/coding-task-manager/lib/memory-sync-verify.js +393 -0
- package/template/coding-task-manager/lib/memory-sync.js +565 -0
- package/template/coding-task-manager/lib/migrations/v57-delegate-pref-mirror-opencode.js +68 -0
- package/template/coding-task-manager/lib/migrations/v58-subagent-launch-native-subagents.js +42 -0
- package/template/coding-task-manager/lib/migrations/v59-coding-session-model-state-dsh.js +73 -0
- package/template/coding-task-manager/lib/migrations/v60-delegate-pref-mirror-dsh.js +78 -0
- package/template/coding-task-manager/lib/migrations/v61-conversation-item-clock.js +71 -0
- package/template/coding-task-manager/lib/model-availability.js +727 -0
- package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +4 -6
- package/template/coding-task-manager/lib/model-host-receipt.js +6 -3
- package/template/coding-task-manager/lib/model-id-rule.js +19 -0
- package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
- package/template/coding-task-manager/lib/model-proxy/auto-mode-review.js +110 -23
- package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
- package/template/coding-task-manager/lib/model-proxy/generation-probe.js +60 -17
- package/template/coding-task-manager/lib/model-proxy/historical-tool-declarations.js +37 -0
- package/template/coding-task-manager/lib/model-proxy/lead-request-validation.js +1 -0
- package/template/coding-task-manager/lib/model-proxy/lead-token-admin.js +130 -0
- package/template/coding-task-manager/lib/model-proxy/refusal-scope.js +151 -0
- package/template/coding-task-manager/lib/model-proxy/request-budget-store.js +11 -2
- package/template/coding-task-manager/lib/model-proxy/tokens.js +69 -5
- package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
- package/template/coding-task-manager/lib/native-agent-model-args.js +25 -22
- package/template/coding-task-manager/lib/native-history-control.js +6 -4
- package/template/coding-task-manager/lib/native-lead-settings-client.js +44 -4
- package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +173 -36
- package/template/coding-task-manager/lib/online-backup-worker-client.js +2 -0
- package/template/coding-task-manager/lib/opencode-cli.js +480 -0
- package/template/coding-task-manager/lib/opencode-launch-model.js +471 -0
- package/template/coding-task-manager/lib/opencode-v1-plugin/model-carrier.mjs +35 -0
- package/template/coding-task-manager/lib/opencode-v2-plugin/server.js +118 -0
- package/template/coding-task-manager/lib/owner-action-manifest-data.json +1 -1
- package/template/coding-task-manager/lib/park-composer-draft.js +54 -0
- package/template/coding-task-manager/lib/park-fresh-relaunch.js +41 -10
- package/template/coding-task-manager/lib/perf-phase-trace.js +72 -0
- package/template/coding-task-manager/lib/perf-summary.js +7 -0
- package/template/coding-task-manager/lib/perf-tracker.js +63 -2
- package/template/coding-task-manager/lib/project-config.js +7 -0
- package/template/coding-task-manager/lib/prompt-purge-v2.js +52 -1
- package/template/coding-task-manager/lib/provider-login-broker.js +72 -15
- package/template/coding-task-manager/lib/provider-login-tab.js +10 -2
- package/template/coding-task-manager/lib/read-pool-client.js +89 -2
- package/template/coding-task-manager/lib/read-pool-worker-handle.js +29 -4
- package/template/coding-task-manager/lib/recent-catalog-query.js +15 -0
- package/template/coding-task-manager/lib/recent-session-catalog-response.js +67 -10
- package/template/coding-task-manager/lib/recovery-evidence.js +26 -11
- package/template/coding-task-manager/lib/relay-client.js +57 -3
- package/template/coding-task-manager/lib/relink-audit.js +34 -5
- package/template/coding-task-manager/lib/remote-machine-catalog.js +321 -26
- package/template/coding-task-manager/lib/remote-machine-create.js +576 -36
- package/template/coding-task-manager/lib/remote-machine-link.js +546 -59
- package/template/coding-task-manager/lib/remote-machine-proxy.js +143 -9
- package/template/coding-task-manager/lib/remote-session-forward.js +65 -7
- package/template/coding-task-manager/lib/remote-terminal-target.js +9 -2
- package/template/coding-task-manager/lib/restart-guard.js +18 -6
- package/template/coding-task-manager/lib/restore-seed-modes.js +87 -0
- package/template/coding-task-manager/lib/restored-scrollback-hydration-runtime.js +22 -2
- package/template/coding-task-manager/lib/resume-admission.js +17 -3
- package/template/coding-task-manager/lib/review-repo-registry.js +58 -19
- package/template/coding-task-manager/lib/root-delivery-runtime.js +2 -2
- package/template/coding-task-manager/lib/root-prompt-flow.js +1 -1
- package/template/coding-task-manager/lib/runtime-contract.js +3 -1
- package/template/coding-task-manager/lib/schema-indexes.js +1 -0
- package/template/coding-task-manager/lib/session-context-locator.js +742 -0
- package/template/coding-task-manager/lib/session-create/attachment.js +2 -2
- package/template/coding-task-manager/lib/session-create/create-phases.js +118 -0
- package/template/coding-task-manager/lib/session-create/delegate-worker.js +86 -16
- package/template/coding-task-manager/lib/session-create/dsh-session.js +930 -0
- package/template/coding-task-manager/lib/session-create/lead-route-launch.js +186 -13
- package/template/coding-task-manager/lib/session-create/native-lead-lifetime.js +55 -3
- package/template/coding-task-manager/lib/session-create/opencode-lead-provider.js +50 -0
- package/template/coding-task-manager/lib/session-create/prepared.js +162 -38
- package/template/coding-task-manager/lib/session-create/provider-process.js +160 -28
- package/template/coding-task-manager/lib/session-create/response.js +7 -7
- package/template/coding-task-manager/lib/session-create/structured-session.js +27 -0
- package/template/coding-task-manager/lib/session-create/subagent-pick-table.js +3 -1
- package/template/coding-task-manager/lib/session-create/subagent-pick.js +458 -9
- package/template/coding-task-manager/lib/session-create/transcript.js +1 -1
- package/template/coding-task-manager/lib/session-create/worker-budget-setup.js +21 -2
- package/template/coding-task-manager/lib/session-create/worker-continuation.js +7 -1
- package/template/coding-task-manager/lib/session-create/worker-launch-sandbox.js +39 -10
- package/template/coding-task-manager/lib/session-create/worker-observability.js +2 -2
- package/template/coding-task-manager/lib/session-empty.js +13 -10
- package/template/coding-task-manager/lib/session-host-manager.js +60 -0
- package/template/coding-task-manager/lib/session-jobs.js +3 -1
- package/template/coding-task-manager/lib/session-lifecycle.js +10 -4
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +6 -2
- package/template/coding-task-manager/lib/session-model-intent-contract.js +11 -8
- package/template/coding-task-manager/lib/session-park.js +738 -0
- package/template/coding-task-manager/lib/session-path-grant.js +13 -4
- package/template/coding-task-manager/lib/session-status-ledger.js +106 -4
- package/template/coding-task-manager/lib/session-transport.js +17 -0
- package/template/coding-task-manager/lib/session-workspace-projection.js +54 -5
- package/template/coding-task-manager/lib/session-workspace-tracking-runtime.js +213 -1
- package/template/coding-task-manager/lib/skill-autocomplete-query.js +120 -3
- package/template/coding-task-manager/lib/skill-autocomplete.js +15 -1
- package/template/coding-task-manager/lib/sqlite-file-set.js +39 -0
- package/template/coding-task-manager/lib/standup-search-adapter.js +16 -2
- package/template/coding-task-manager/lib/state-sync/client-ledger.js +12 -0
- package/template/coding-task-manager/lib/state-sync/frame-emitter.js +319 -100
- package/template/coding-task-manager/lib/storage/coding-session-model-store.js +4 -1
- package/template/coding-task-manager/lib/storage/delegate-pref-mirror-store.js +32 -9
- package/template/coding-task-manager/lib/structured-session-boot-sweep.js +235 -0
- package/template/coding-task-manager/lib/submission-ack.js +90 -2
- package/template/coding-task-manager/lib/systemd-scope.js +115 -0
- package/template/coding-task-manager/lib/telemetry-receiver.js +39 -0
- package/template/coding-task-manager/lib/terminal-attach-geometry.js +985 -0
- package/template/coding-task-manager/lib/terminal-bootstrap.js +30 -0
- package/template/coding-task-manager/lib/terminal-draft-operation.js +357 -0
- package/template/coding-task-manager/lib/terminal-input-admission.js +17 -1
- package/template/coding-task-manager/lib/terminal-input-prepared.js +563 -0
- package/template/coding-task-manager/lib/transcript-cache-temps.js +171 -0
- package/template/coding-task-manager/lib/transcript-frame-cache.js +8 -3
- package/template/coding-task-manager/lib/transcript-source.js +36 -4
- package/template/coding-task-manager/lib/transcript-store.js +9 -0
- package/template/coding-task-manager/lib/typing-interaction-trace.js +106 -3
- package/template/coding-task-manager/lib/usage-ledger/backfill.js +40 -5
- package/template/coding-task-manager/lib/usage-ledger/client.js +4 -1
- package/template/coding-task-manager/lib/usage-ledger/dsh-parse.js +152 -0
- package/template/coding-task-manager/lib/usage-ledger/dsh-read.js +73 -0
- package/template/coding-task-manager/lib/usage-ledger/ingest.js +34 -6
- package/template/coding-task-manager/lib/usage-ledger/live.js +35 -8
- package/template/coding-task-manager/lib/usage-ledger/read.js +29 -20
- package/template/coding-task-manager/lib/usage-ledger/schema.js +61 -1
- package/template/coding-task-manager/lib/usage-ledger/task-coordinator.js +4 -2
- package/template/coding-task-manager/lib/wake-skip-reasons.js +1 -0
- package/template/coding-task-manager/lib/walle/turn.js +1 -1
- package/template/coding-task-manager/lib/walle-history-serve.js +838 -0
- package/template/coding-task-manager/lib/walle-kill-authority.js +2 -2
- package/template/coding-task-manager/lib/walle-log-rotation.js +164 -1
- package/template/coding-task-manager/lib/walle-mcp-auto-config.js +56 -2
- package/template/coding-task-manager/lib/walle-row-history.js +495 -0
- package/template/coding-task-manager/lib/walle-supervisor.js +33 -14
- package/template/coding-task-manager/lib/watch-worker-client.js +203 -0
- package/template/coding-task-manager/lib/worker-terminal-output.js +5 -2
- package/template/coding-task-manager/lib/ws/hub-bridge.js +35 -0
- package/template/coding-task-manager/lib/ws/lifecycle.js +6 -11
- package/template/coding-task-manager/lib/ws/misc.js +1 -1
- package/template/coding-task-manager/lib/ws/state-sync.js +106 -4
- package/template/coding-task-manager/lib/ws/terminal-attach.js +91 -0
- package/template/coding-task-manager/oauth-proxy.js +112 -115
- package/template/coding-task-manager/package-lock.json +10 -94
- package/template/coding-task-manager/package.json +4 -3
- package/template/coding-task-manager/protocol/message-table.json +10 -0
- package/template/coding-task-manager/protocol/messages.js +10 -0
- package/template/coding-task-manager/protocol/ws-schema.json +20 -0
- package/template/coding-task-manager/protocol/ws-schema.md +2 -0
- package/template/coding-task-manager/public/css/app.css +170 -0
- package/template/coding-task-manager/public/css/walle-session.css +4 -0
- package/template/coding-task-manager/public/index.html +473 -243
- package/template/coding-task-manager/public/js/agent-command.js +86 -0
- package/template/coding-task-manager/public/js/app/backups-panel.js +111 -5
- package/template/coding-task-manager/public/js/app/coding-agent-upgrade.js +103 -4
- package/template/coding-task-manager/public/js/app/machines-page.js +1328 -8
- package/template/coding-task-manager/public/js/app/models-tab.js +113 -38
- package/template/coding-task-manager/public/js/app/restart-banner.js +53 -0
- package/template/coding-task-manager/public/js/app/worktrees-panel.js +184 -2
- package/template/coding-task-manager/public/js/background-work.js +196 -27
- package/template/coding-task-manager/public/js/coding-session-model-presentation.js +138 -10
- package/template/coding-task-manager/public/js/core/active-session-groups.js +179 -0
- package/template/coding-task-manager/public/js/core/pending-create.js +238 -0
- package/template/coding-task-manager/public/js/core/recent-machine-source.js +228 -0
- package/template/coding-task-manager/public/js/core/session-commands.js +180 -14
- package/template/coding-task-manager/public/js/dashboard-client-function-order.json +8 -0
- package/template/coding-task-manager/public/js/document-review-links.js +9 -1
- package/template/coding-task-manager/public/js/file-resolution-client.js +52 -1
- package/template/coding-task-manager/public/js/history-app-seam.js +22 -2
- package/template/coding-task-manager/public/js/machine-pool-shared.js +558 -3
- package/template/coding-task-manager/public/js/message-renderer.js +4 -3
- package/template/coding-task-manager/public/js/model-picker.js +3 -1
- package/template/coding-task-manager/public/js/native-subagent-review.js +15 -2
- package/template/coding-task-manager/public/js/new-session-agent-grid.js +346 -0
- package/template/coding-task-manager/public/js/pasted-content.js +236 -0
- package/template/coding-task-manager/public/js/pe-private-content-lifetime.js +39 -7
- package/template/coding-task-manager/public/js/private-model-lifetime.js +6 -2
- package/template/coding-task-manager/public/js/prompts.js +11 -10
- package/template/coding-task-manager/public/js/protocol.js +10 -0
- package/template/coding-task-manager/public/js/restart-banner-policy.js +29 -1
- package/template/coding-task-manager/public/js/session-model-picker.js +447 -57
- package/template/coding-task-manager/public/js/session-search-utils.js +18 -2
- package/template/coding-task-manager/public/js/session-usage-view.js +2 -1
- package/template/coding-task-manager/public/js/session-workspace-tracking.js +51 -1
- package/template/coding-task-manager/public/js/state-sync-client.js +10 -1
- package/template/coding-task-manager/public/js/stream-view.js +6 -5
- package/template/coding-task-manager/public/js/terminal/conv-only-session.js +287 -0
- package/template/coding-task-manager/public/js/terminal/runtime-09.js +2 -0
- package/template/coding-task-manager/public/js/terminal/runtime-10.js +5 -5
- package/template/coding-task-manager/public/js/terminal/runtime-12.js +8 -1
- package/template/coding-task-manager/public/js/terminal/runtime-16.js +4 -0
- package/template/coding-task-manager/public/js/terminal/runtime-20.js +1 -1
- package/template/coding-task-manager/public/js/terminal-attachment-draft.js +67 -13
- package/template/coding-task-manager/public/js/terminal-image-orchestration.js +52 -6
- package/template/coding-task-manager/public/js/terminal-wheel.js +23 -3
- package/template/coding-task-manager/public/js/walle/chat-conversation.js +4 -4
- package/template/coding-task-manager/public/js/walle.js +6 -0
- package/template/coding-task-manager/public/js/ws/live-dispatch.js +10 -0
- package/template/coding-task-manager/public/m/app.css +140 -0
- package/template/coding-task-manager/public/m/app.js +507 -68
- package/template/coding-task-manager/public/m/index.html +17 -0
- package/template/coding-task-manager/public/m/machines.js +838 -4
- package/template/coding-task-manager/public/m/private-api.js +31 -1
- package/template/coding-task-manager/public/usage.html +1 -0
- package/template/coding-task-manager/scripts/dev-route-fixture/synthetic-route-upstream.js +104 -0
- package/template/coding-task-manager/scripts/dev-route-fixture.js +339 -0
- package/template/coding-task-manager/scripts/opencode-lead-route-probe.js +195 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe/fake-anthropic.js +168 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe/probe-plugin/server.js +95 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe.js +673 -0
- package/template/coding-task-manager/server.js +1950 -11679
- package/template/coding-task-manager/session-integrity.js +64 -4
- package/template/coding-task-manager/workers/db-owner-worker.js +19 -2
- package/template/coding-task-manager/workers/online-backup-worker.js +1 -0
- package/template/coding-task-manager/workers/read-pool-model-collector.js +51 -3
- package/template/coding-task-manager/workers/read-pool-worker.js +76 -6
- package/template/coding-task-manager/workers/session-host-pool-process.js +6 -6
- package/template/coding-task-manager/workers/session-host-process.js +5 -5
- package/template/coding-task-manager/workers/watch-worker.js +105 -0
- package/template/coding-task-manager/workers/worktree-worker.js +168 -33
- package/template/ctm-relay/lib/protocol.js +14 -1
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/shared/backup-retention.js +82 -0
- package/template/shared/conversation-admission.js +2 -0
- package/template/shared/conversation-provenance.js +3 -7
- package/template/shared/conversation-storage-v2-contract.js +5 -0
- package/template/shared/linked-worktree.js +51 -0
- package/template/shared/macos-fda-target.js +57 -0
- package/template/shared/orphan-parents.js +55 -0
- package/template/shared/release-schema.json +2 -2
- package/template/shared/storage/contract.js +3 -0
- package/template/wall-e/adapters/imessage.js +12 -0
- package/template/wall-e/agent.js +71 -4
- package/template/wall-e/api-walle.js +21 -1
- package/template/wall-e/bin/train-gemma-e4b-tooluse.js +2 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +5 -5
- package/template/wall-e/brain/brain-join.js +859 -0
- package/template/wall-e/brain/brain-sync-membership.js +20 -0
- package/template/wall-e/brain/brain-sync-store.js +131 -77
- package/template/wall-e/brain/brain-sync-v2-schema.js +203 -0
- package/template/wall-e/brain/brain-sync-v2.js +578 -0
- package/template/wall-e/brain/delegate-model-prefs-store.js +13 -4
- package/template/wall-e/brain/deterministic-id.js +51 -0
- package/template/wall-e/brain/memories.js +2 -2
- package/template/wall-e/brain/migrations.js +40 -6
- package/template/wall-e/brain/model-identity-migration.js +1 -1
- package/template/wall-e/brain/owner-call-whitelist.js +23 -7
- package/template/wall-e/brain/schema.js +45 -85
- package/template/wall-e/brain.js +171 -35
- package/template/wall-e/chat.js +3 -3
- package/template/wall-e/coding/agent-shell-env.js +7 -2
- package/template/wall-e/coding/prompt-bundle.js +66 -21
- package/template/wall-e/config/main-loop-exemptions.json +2 -2
- package/template/wall-e/http/admin-api.js +37 -0
- package/template/wall-e/http/gws-api.js +4 -0
- package/template/wall-e/http/helper-rules-api.js +7 -1
- package/template/wall-e/http/model-admin.js +8 -1
- package/template/wall-e/http/model-delegate-pref-admin.js +3 -3
- package/template/wall-e/http/model-proxy-providers.js +174 -1
- package/template/wall-e/http/model-route-health.js +157 -0
- package/template/wall-e/http/sync-api.js +278 -464
- package/template/wall-e/jobs/job-runner.js +28 -1
- package/template/wall-e/lib/brain-owner-op-priority.js +9 -1
- package/template/wall-e/lib/ctm-data-dir.js +26 -0
- package/template/wall-e/lib/derivation-home.js +147 -0
- package/template/wall-e/lib/helper-rules/evaluate.js +50 -9
- package/template/wall-e/lib/helper-rules/grade.js +51 -8
- package/template/wall-e/lib/helper-rules/lead-call-hook.js +38 -5
- package/template/wall-e/lib/helper-rules/store.js +4 -2
- package/template/wall-e/lib/landing-mcp-tools.js +170 -0
- package/template/wall-e/lib/mcp-client-config.js +133 -14
- package/template/wall-e/lib/mcp-gateway-migrate.js +28 -64
- package/template/wall-e/lib/mcp-integration.js +58 -13
- package/template/wall-e/lib/parent-death.js +7 -1
- package/template/wall-e/lib/scheduler-worker-jobs.js +6 -4
- package/template/wall-e/llm/delegate-lead-prefs.js +16 -1
- package/template/wall-e/llm/delegate-vendor-default.js +12 -5
- package/template/wall-e/llm/model-roles.js +1 -1
- package/template/wall-e/llm/native-delegate-selection.js +56 -8
- package/template/wall-e/llm/worker-delegate-args.js +72 -7
- package/template/wall-e/llm/worker-mcp-routing.js +66 -17
- package/template/wall-e/loops/brain-custom-writes.js +19 -0
- package/template/wall-e/loops/ingest.js +17 -1
- package/template/wall-e/loops/think.js +19 -4
- package/template/wall-e/mcp-contract.js +222 -6
- package/template/wall-e/mcp-protocol-contract.js +40 -5
- package/template/wall-e/mcp-server.js +128 -29
- package/template/wall-e/mcp-worker-schemas.js +5 -5
- package/template/wall-e/memory/ctm-context-client.js +85 -0
- package/template/wall-e/memory/ctm-session-context.js +9 -3
- package/template/wall-e/memory/hub-bridge-client.js +388 -0
- package/template/wall-e/memory/replica-origin.js +56 -0
- package/template/wall-e/memory/session-pool.js +135 -0
- package/template/wall-e/runtime/live-capabilities.js +8 -3
- package/template/wall-e/server.js +4 -2
- package/template/wall-e/shared/release-schema.json +2 -2
- package/template/wall-e/shared/sqlite-file-set.js +39 -0
- package/template/wall-e/shared/storage/contract.js +3 -0
- package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
- package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +7 -1
- package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +4 -4
- package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +6 -6
- package/template/wall-e/skills/_templates/fleet-sync/VERSION +1 -0
- package/template/wall-e/skills/_templates/fleet-sync/released-sha256.json +16 -0
- package/template/wall-e/skills/_templates/fleet-sync/templates/claude/SKILL.md +64 -0
- package/template/wall-e/skills/_templates/fleet-sync/templates/codex/SKILL.md +64 -0
- package/template/wall-e/tools/chrome-automation.js +6 -0
- package/template/wall-e/training/setup-cuda-train-env.sh +2 -2
- package/template/wall-e/workers/brain-owner-worker.js +18 -4
- package/template/wall-e/workers/mcp-gateway-process.js +5 -5
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
//@provides: ACTIVE_SORT_MODES, ACTIVE_LOCAL_COMPUTER_LABEL, normalizeActiveSort, activeSortIsGrouped, activeComputerGroupKey, activeComputerGroupLabels, compareActiveComputerGroups, activeComputerFilterPasses, activeComputerMenuModel, applyActiveComputerOptgroup, parseActiveComputerOptionValue, activeComputerFilterChipHtml, activeProjectHeaderHtml, activeGroupHeaderHtml, sortActiveGroupedSessions
|
|
2
|
+
// Active Sessions grouping (docs/active-sessions-group-by-computer-2026-10-30.html §5 D-1, §6).
|
|
3
|
+
// ONE module owns the sort-mode allow-list, the grouped mode (by_project), its group ordering and
|
|
4
|
+
// header markup, and (rev 2) the computer FILTER helpers, so index.html holds no copy of the mode list.
|
|
5
|
+
// No module state; pure except applyActiveComputerOptgroup, which writes the passed-in select. Dual browser/node (worker-tree.js pattern): browser globals, plus
|
|
6
|
+
// module.exports for tests. Loaded AFTER js/core/session-commands.js, whose sessionMachineFacts and
|
|
7
|
+
// MACHINE_HEALTH_UNREACHABLE are the ONLY readers of the machine fields (no hostname parsing).
|
|
8
|
+
|
|
9
|
+
const ACTIVE_SORT_MODES = ['by_activity', 'tab_order', 'by_project'];
|
|
10
|
+
const ACTIVE_LOCAL_COMPUTER_LABEL = 'Local';
|
|
11
|
+
|
|
12
|
+
// A member of ACTIVE_SORT_MODES, else 'by_activity' (D-11).
|
|
13
|
+
function normalizeActiveSort(mode) {
|
|
14
|
+
return ACTIVE_SORT_MODES.includes(mode) ? mode : 'by_activity';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function activeSortIsGrouped(mode) {
|
|
18
|
+
return mode === 'by_project';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// entries are the renderer's [id, session] pairs; a bare session row is accepted too.
|
|
22
|
+
function _activeGroupRow(entry) {
|
|
23
|
+
return Array.isArray(entry) ? entry[1] : entry;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// D-2: the hub-stamped instanceId; '' is the local machine.
|
|
27
|
+
function activeComputerGroupKey(s) {
|
|
28
|
+
return sessionMachineFacts(s).machine;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// D-3: '' -> 'Local'; else the first non-empty machineName among the key's rows, else the key.
|
|
32
|
+
// Labels that collide get ' · ' + key.slice(0, 6) so two machines are never indistinguishable.
|
|
33
|
+
function activeComputerGroupLabels(entries) {
|
|
34
|
+
const names = new Map();
|
|
35
|
+
for (const entry of entries || []) {
|
|
36
|
+
const facts = sessionMachineFacts(_activeGroupRow(entry));
|
|
37
|
+
if (!names.has(facts.machine)) names.set(facts.machine, '');
|
|
38
|
+
if (!names.get(facts.machine) && facts.name) names.set(facts.machine, facts.name);
|
|
39
|
+
}
|
|
40
|
+
const labels = new Map();
|
|
41
|
+
for (const [key, name] of names) {
|
|
42
|
+
labels.set(key, key === '' ? ACTIVE_LOCAL_COMPUTER_LABEL : (name || key));
|
|
43
|
+
}
|
|
44
|
+
const seen = new Map();
|
|
45
|
+
for (const label of labels.values()) seen.set(label, (seen.get(label) || 0) + 1);
|
|
46
|
+
for (const [key, label] of labels) {
|
|
47
|
+
if (seen.get(label) > 1) labels.set(key, label + ' · ' + key.slice(0, 6));
|
|
48
|
+
}
|
|
49
|
+
return labels;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// D-4: Local first, then by label (case-insensitive, numeric-aware), ties broken by key.
|
|
53
|
+
function compareActiveComputerGroups(a, b, labels) {
|
|
54
|
+
if (a === b) return 0;
|
|
55
|
+
if (a === '') return -1;
|
|
56
|
+
if (b === '') return 1;
|
|
57
|
+
const la = (labels && labels.get(a)) || a;
|
|
58
|
+
const lb = (labels && labels.get(b)) || b;
|
|
59
|
+
const byLabel = la.localeCompare(lb, undefined, { sensitivity: 'base', numeric: true });
|
|
60
|
+
if (byLabel !== 0) return byLabel;
|
|
61
|
+
return a < b ? -1 : 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// F-6: a session passes when the filter is null (all computers) or its machine key equals the filter.
|
|
65
|
+
function activeComputerFilterPasses(s, filter) {
|
|
66
|
+
return filter === null || filter === undefined || activeComputerGroupKey(s) === filter;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// F-3/F-4/F-5: the Computers optgroup model. entries are [id, session] pairs (or bare rows).
|
|
70
|
+
// options: All computers, Local, then machines; "✓ " prefixes exactly the active one.
|
|
71
|
+
function activeComputerMenuModel(entries, filter, lastLabels) {
|
|
72
|
+
const active = typeof filter === 'string' ? filter : null;
|
|
73
|
+
const labels = activeComputerGroupLabels(entries);
|
|
74
|
+
const keys = new Set(labels.keys());
|
|
75
|
+
if (active !== null && !keys.has(active)) {
|
|
76
|
+
keys.add(active);
|
|
77
|
+
labels.set(active, (lastLabels && lastLabels.get(active)) || (active === '' ? ACTIVE_LOCAL_COMPUTER_LABEL : active));
|
|
78
|
+
}
|
|
79
|
+
const show = keys.size > 1 || active !== null || (keys.size === 1 && !keys.has(''));
|
|
80
|
+
const ordered = Array.from(keys).sort((a, b) => compareActiveComputerGroups(a, b, labels));
|
|
81
|
+
const mark = (on, text) => (on ? '✓ ' : '') + text;
|
|
82
|
+
const options = [{ value: 'computer:*', text: mark(active === null, 'All computers') }];
|
|
83
|
+
const sigRows = [];
|
|
84
|
+
for (const k of ordered) {
|
|
85
|
+
const label = labels.get(k);
|
|
86
|
+
sigRows.push([k, label]);
|
|
87
|
+
options.push({ value: 'computer:' + k, text: mark(active === k, label) });
|
|
88
|
+
}
|
|
89
|
+
return { show, signature: JSON.stringify([active, sigRows]), options, labels };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// F-5: rewrite the Computers optgroup of `sel` only when the model signature differs from prevSig; returns the
|
|
93
|
+
// signature now in effect. Option text goes through new Option (no HTML parsing).
|
|
94
|
+
function applyActiveComputerOptgroup(sel, model, prevSig) {
|
|
95
|
+
const sig = (model.show ? '1' : '0') + model.signature;
|
|
96
|
+
if (sig === prevSig) return prevSig;
|
|
97
|
+
const old = sel.querySelector('optgroup[label="Computers"]');
|
|
98
|
+
if (old) old.remove();
|
|
99
|
+
if (!model.show) return sig;
|
|
100
|
+
const grp = document.createElement('optgroup');
|
|
101
|
+
grp.label = 'Computers';
|
|
102
|
+
for (const o of model.options) grp.appendChild(new Option(o.text, o.value));
|
|
103
|
+
sel.appendChild(grp);
|
|
104
|
+
return sig;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 'computer:*' -> null (all), 'computer:' -> '' (Local), 'computer:<id>' -> '<id>', anything else -> undefined.
|
|
108
|
+
function parseActiveComputerOptionValue(v) {
|
|
109
|
+
if (typeof v !== 'string' || !v.startsWith('computer:')) return undefined;
|
|
110
|
+
const rest = v.slice('computer:'.length);
|
|
111
|
+
return rest === '*' ? null : rest;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// The Projects header, moved here verbatim from renderSessionList. opts adds iconHtml (the folder glyph,
|
|
115
|
+
// which stays in index.html). Its markup, data-project-key and aria-label text are frozen.
|
|
116
|
+
function activeProjectHeaderHtml(opts) {
|
|
117
|
+
const { key, count, collapsed, hasActive, iconHtml, escHtml, jsArg } = opts;
|
|
118
|
+
const label = opts.label || '(no project)';
|
|
119
|
+
const title = key || 'No project directory';
|
|
120
|
+
const verb = collapsed ? 'Expand' : 'Collapse';
|
|
121
|
+
return `<button type="button" class="session-group-sep project-sep${collapsed ? ' collapsed' : ''}${hasActive ? ' has-active' : ''}" data-project-key="${escHtml(key)}" title="${escHtml(title)}" aria-expanded="${collapsed ? 'false' : 'true'}" aria-label="${verb} ${escHtml(label)} project group, ${count} active session${count === 1 ? '' : 's'}" onclick="toggleActiveProjectGroup('${jsArg(key)}', event)"><span class="project-sep-chevron" aria-hidden="true">▾</span>${iconHtml}<span class="project-sep-label">${escHtml(label)}</span><span class="project-sep-count">${count}</span></button>`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// One entry point for the grouped header (project only since rev 2). opts as activeProjectHeaderHtml.
|
|
125
|
+
function activeGroupHeaderHtml(mode, opts) {
|
|
126
|
+
return activeProjectHeaderHtml(opts);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Sorts `sessions` ([id, session] pairs) IN PLACE for a grouped mode and returns
|
|
130
|
+
// { keyOf, labels, unitPrefix } for the caller's header emission. deps:
|
|
131
|
+
// bucketOf(id, s) -> status bucket, projectKey(s), projectLabels(keys) -> Map, lastActivityMs(s),
|
|
132
|
+
// compareRows(a, b) -> the within-group comparator (status, then display order).
|
|
133
|
+
// by_project: the project with the most urgent session (best status rank, then most recent) floats
|
|
134
|
+
// up.
|
|
135
|
+
function sortActiveGroupedSessions(mode, sessions, deps) {
|
|
136
|
+
const ranks = (typeof SessionActivityUtils !== 'undefined' && SessionActivityUtils.ACTIVE_STATUS_RANK)
|
|
137
|
+
? SessionActivityUtils.ACTIVE_STATUS_RANK
|
|
138
|
+
: { running: 0, resuming: 1, waiting: 2, idle: 3, exited: 4 };
|
|
139
|
+
const projectAgg = new Map(); // key -> { rank, recent }
|
|
140
|
+
for (const [id, s] of sessions) {
|
|
141
|
+
const k = deps.projectKey(s);
|
|
142
|
+
const rank = ranks[deps.bucketOf(id, s)] ?? 4;
|
|
143
|
+
const recent = deps.lastActivityMs(s);
|
|
144
|
+
const cur = projectAgg.get(k) || { rank: 99, recent: 0 };
|
|
145
|
+
if (rank < cur.rank) cur.rank = rank;
|
|
146
|
+
if (recent > cur.recent) cur.recent = recent;
|
|
147
|
+
projectAgg.set(k, cur);
|
|
148
|
+
}
|
|
149
|
+
const labels = deps.projectLabels(Array.from(projectAgg.keys()));
|
|
150
|
+
sessions.sort((a, b) => {
|
|
151
|
+
const ka = deps.projectKey(a[1]);
|
|
152
|
+
const kb = deps.projectKey(b[1]);
|
|
153
|
+
if (ka !== kb) {
|
|
154
|
+
const ga = projectAgg.get(ka);
|
|
155
|
+
const gb = projectAgg.get(kb);
|
|
156
|
+
if (ga.rank !== gb.rank) return ga.rank - gb.rank;
|
|
157
|
+
if (ga.recent !== gb.recent) return gb.recent - ga.recent;
|
|
158
|
+
return ka < kb ? -1 : 1;
|
|
159
|
+
}
|
|
160
|
+
return deps.compareRows(a, b);
|
|
161
|
+
});
|
|
162
|
+
return { keyOf: deps.projectKey, labels, unitPrefix: 'psep:' };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// G-3: removable filter chip markup; the label is escaped here, never trusted.
|
|
166
|
+
function activeComputerFilterChipHtml({ label, escHtml }) {
|
|
167
|
+
const l = escHtml(String(label == null ? '' : label));
|
|
168
|
+
return '<button type="button" class="computer-filter-chip" aria-label="Remove computer filter: ' + l +
|
|
169
|
+
'" title="Showing only ' + l + ' \u2014 click to show all computers" onclick="setActiveComputerFilter(null)">\u25c8 ' + l +
|
|
170
|
+
' <span aria-hidden="true">\u2715</span></button>';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
174
|
+
module.exports = {
|
|
175
|
+
ACTIVE_SORT_MODES, ACTIVE_LOCAL_COMPUTER_LABEL, normalizeActiveSort, activeSortIsGrouped, activeComputerGroupKey,
|
|
176
|
+
activeComputerGroupLabels, compareActiveComputerGroups, activeComputerFilterPasses, activeComputerMenuModel,
|
|
177
|
+
activeComputerFilterChipHtml, applyActiveComputerOptgroup, parseActiveComputerOptionValue, activeProjectHeaderHtml, activeGroupHeaderHtml, sortActiveGroupedSessions,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
//@provides: PendingCreate
|
|
2
|
+
//@needs: toast, clearToastByKey, send, activateTab
|
|
3
|
+
// WP-1 (coding-task-manager/docs/session-create-accept-then-complete-2026-11-01.html §5 D-1/D-7,
|
|
4
|
+
// §6.4, §8 WP-1, §9 S-11/S-12).
|
|
5
|
+
//
|
|
6
|
+
// The 2026-11-01 15:33Z incident: the New Session flow sent `create` with no id, so when the hub's
|
|
7
|
+
// 15 s phase-A wait expired and reported `machine_unreachable` "timeout" the client had no way to
|
|
8
|
+
// match the box's later, successful session to the click — the tab is activated only inside
|
|
9
|
+
// onCreated, and the orphaned success never opened one. This module owns the client half of the
|
|
10
|
+
// fix: the id the client mints (D-1) and the pending entry that reconciles the click with whatever
|
|
11
|
+
// arrives later (D-7 — a `created`, an `error`, the session appearing in a sessions list, or the TTL).
|
|
12
|
+
// Adoption from a list is deliberately restricted to entries whose `created` cannot be coming
|
|
13
|
+
// ('unconfirmed' / 'orphaned'): a list can race ahead of the `created` frame, and adopting a
|
|
14
|
+
// still-'pending' entry would activate its lazy stub and then let onCreated activate the id again.
|
|
15
|
+
//
|
|
16
|
+
// It reads the global `state` object (public/index.html:3627) at CALL time only, and calls the
|
|
17
|
+
// page's toast / clearToastByKey / send / activateTab globals, so loading it right after
|
|
18
|
+
// 02-toast.js is enough. `_now` is the clock seam the unit tests pin.
|
|
19
|
+
var PendingCreate = (function buildCtmPendingCreate() {
|
|
20
|
+
const TTL_MS = 600000; // D-7
|
|
21
|
+
// id -> { id, machine, machineName, label, activeTabAtClick, clickedAt, acceptedAt, state }
|
|
22
|
+
const entries = new Map();
|
|
23
|
+
|
|
24
|
+
const createKey = (id) => 'create-pending:' + id;
|
|
25
|
+
const readyKey = (id) => 'create-ready:' + id;
|
|
26
|
+
const nowMs = () => Number(PendingCreate._now()) || 0;
|
|
27
|
+
const machineClause = (entry) => (entry.machine ? ' on ' + entry.machineName : '');
|
|
28
|
+
|
|
29
|
+
function showToast(type, text, key, duration) {
|
|
30
|
+
if (typeof toast !== 'function') return;
|
|
31
|
+
const opts = { type: type, key: key };
|
|
32
|
+
if (duration) opts.duration = duration;
|
|
33
|
+
toast(text, opts);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function clearPendingToast(id) {
|
|
37
|
+
if (typeof clearToastByKey === 'function') clearToastByKey(createKey(id));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function settleTimings(entry, settledAt) {
|
|
41
|
+
return {
|
|
42
|
+
clickToAcceptedMs: entry.acceptedAt >= 0 ? Math.max(0, entry.acceptedAt - entry.clickedAt) : -1,
|
|
43
|
+
clickToSettledMs: Math.max(0, settledAt - entry.clickedAt),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const HEX = '0123456789abcdef';
|
|
48
|
+
|
|
49
|
+
// RFC 4122 §4.4: version 4 in the high nibble of byte 6, variant 10 in the top bits of byte 8.
|
|
50
|
+
function formatUuidV4(bytes) {
|
|
51
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
52
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
53
|
+
let out = '';
|
|
54
|
+
for (let i = 0; i < 16; i += 1) {
|
|
55
|
+
out += HEX[(bytes[i] >> 4) & 0x0f] + HEX[bytes[i] & 0x0f];
|
|
56
|
+
if (i === 3 || i === 5 || i === 7 || i === 9) out += '-';
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// One `client-diag` / `create-ready` row per settled entry (§6.4). The server's
|
|
62
|
+
// lib/client-diag-handler.js turns it into one `[create-client]` line.
|
|
63
|
+
function emitDiag(entry, outcome) {
|
|
64
|
+
if (typeof send !== 'function') return;
|
|
65
|
+
const timings = settleTimings(entry, nowMs());
|
|
66
|
+
send({
|
|
67
|
+
type: 'client-diag',
|
|
68
|
+
event: 'create-ready',
|
|
69
|
+
id: entry.id,
|
|
70
|
+
details: {
|
|
71
|
+
machine: entry.machine,
|
|
72
|
+
outcome: outcome,
|
|
73
|
+
clickToAcceptedMs: timings.clickToAcceptedMs,
|
|
74
|
+
clickToSettledMs: timings.clickToSettledMs,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function drop(id) {
|
|
80
|
+
entries.delete(id);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
TTL_MS: TTL_MS,
|
|
85
|
+
// Test seam: the unit tests replace this with a fake clock. Nothing else may.
|
|
86
|
+
_now: function () { return Date.now(); },
|
|
87
|
+
|
|
88
|
+
/** The create id the client mints (D-1). `crypto.randomUUID` is SECURE-CONTEXT ONLY, so a hub
|
|
89
|
+
* opened over plain http on a LAN address has no such function — it falls back to
|
|
90
|
+
* `crypto.getRandomValues`, which is available in every context, and a sandbox with no crypto
|
|
91
|
+
* at all falls back to Math.random. Always the 36-char 8-4-4-4-12 lowercase v4 form. */
|
|
92
|
+
newId: function () {
|
|
93
|
+
const cryptoApi = (typeof crypto === 'object' && crypto) ? crypto : null;
|
|
94
|
+
if (cryptoApi && typeof cryptoApi.randomUUID === 'function') return String(cryptoApi.randomUUID());
|
|
95
|
+
const bytes = new Array(16);
|
|
96
|
+
if (cryptoApi && typeof cryptoApi.getRandomValues === 'function' && typeof Uint8Array === 'function') {
|
|
97
|
+
const seed = new Uint8Array(16);
|
|
98
|
+
cryptoApi.getRandomValues(seed);
|
|
99
|
+
for (let i = 0; i < 16; i += 1) bytes[i] = seed[i];
|
|
100
|
+
} else {
|
|
101
|
+
for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);
|
|
102
|
+
}
|
|
103
|
+
return formatUuidV4(bytes);
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
/** Called by _doCreate before send(). Shows toast(info) key 'create-pending:'+id,
|
|
107
|
+
* text "Starting <label> on <machineName>…" (or "Starting <label>…" for a local create),
|
|
108
|
+
* duration TTL_MS. */
|
|
109
|
+
begin: function (opts) {
|
|
110
|
+
const options = opts || {};
|
|
111
|
+
const id = String(options.id || '');
|
|
112
|
+
if (!id) return null;
|
|
113
|
+
const machine = String(options.machine || '');
|
|
114
|
+
const entry = {
|
|
115
|
+
id: id,
|
|
116
|
+
machine: machine,
|
|
117
|
+
machineName: String(options.machineName || machine || 'This Mac'),
|
|
118
|
+
label: String(options.label || '').trim() || 'New session',
|
|
119
|
+
activeTabAtClick: options.activeTabAtClick === undefined ? null : options.activeTabAtClick,
|
|
120
|
+
clickedAt: nowMs(),
|
|
121
|
+
acceptedAt: -1,
|
|
122
|
+
state: 'pending',
|
|
123
|
+
};
|
|
124
|
+
entries.set(id, entry);
|
|
125
|
+
showToast('info', 'Starting ' + entry.label + machineClause(entry) + '…', createKey(id), TTL_MS);
|
|
126
|
+
return entry;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/** 'create-accepted' with a pending id: replaces the toast text with
|
|
130
|
+
* "Starting <label> on <machineName> (load <load1> on <cores> cores)…"; records acceptedAt.
|
|
131
|
+
* Returns true if pending. */
|
|
132
|
+
accepted: function (msg) {
|
|
133
|
+
const entry = msg ? entries.get(String(msg.id || '')) : null;
|
|
134
|
+
if (!entry) return false;
|
|
135
|
+
entry.state = 'accepted';
|
|
136
|
+
if (entry.acceptedAt < 0) entry.acceptedAt = nowMs();
|
|
137
|
+
const hasLoad = msg.load1 !== undefined && msg.load1 !== null && msg.cores !== undefined
|
|
138
|
+
&& msg.cores !== null && Number.isFinite(Number(msg.load1)) && Number.isFinite(Number(msg.cores));
|
|
139
|
+
const loadClause = hasLoad ? ' (load ' + Number(msg.load1) + ' on ' + Number(msg.cores) + ' cores)' : '';
|
|
140
|
+
showToast('info', 'Starting ' + entry.label + machineClause(entry) + loadClause + '…',
|
|
141
|
+
createKey(entry.id), TTL_MS);
|
|
142
|
+
return true;
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/** 'created' with a pending id: clears toast + entry, emits diag outcome 'created'. Returns true
|
|
146
|
+
* if it was pending. Activation stays in onCreated, unchanged. */
|
|
147
|
+
settleCreated: function (msg) {
|
|
148
|
+
const id = msg ? String(msg.id || '') : '';
|
|
149
|
+
const entry = id ? entries.get(id) : null;
|
|
150
|
+
if (!entry) return false;
|
|
151
|
+
drop(id);
|
|
152
|
+
clearPendingToast(id);
|
|
153
|
+
emitDiag(entry, 'created');
|
|
154
|
+
return true;
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/** 'error' with a pending id. code === 'machine_create_unconfirmed': toast(warning) key
|
|
158
|
+
* 'create-pending:'+id, "<label> is still starting on <machineName>; it will open when ready",
|
|
159
|
+
* keeps the entry, returns true (caller skips the Machine error toast). Any other code: clears
|
|
160
|
+
* entry + pending toast, emits diag 'failed', returns false (caller's existing toast runs).
|
|
161
|
+
*
|
|
162
|
+
* An id-less `machine_*` error naming a machine is the OLD hub's shape (its phase-A timeout
|
|
163
|
+
* carries no id, §8 "new client, old hub"): every entry still waiting on that machine becomes
|
|
164
|
+
* 'orphaned', which is what lets the next sessions list adopt it. Always returns false, so the
|
|
165
|
+
* caller's existing Machine error toast still runs. */
|
|
166
|
+
settleError: function (msg) {
|
|
167
|
+
const id = msg ? String(msg.id || '') : '';
|
|
168
|
+
const entry = id ? entries.get(id) : null;
|
|
169
|
+
if (entry) {
|
|
170
|
+
if (msg.code === 'machine_create_unconfirmed') {
|
|
171
|
+
entry.state = 'unconfirmed';
|
|
172
|
+
showToast('warning',
|
|
173
|
+
entry.label + ' is still starting' + machineClause(entry) + '; it will open when ready',
|
|
174
|
+
createKey(id));
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
drop(id);
|
|
178
|
+
clearPendingToast(id);
|
|
179
|
+
emitDiag(entry, 'failed');
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (!id && msg && typeof msg.machine === 'string' && msg.machine !== ''
|
|
183
|
+
&& /^machine_/.test(String(msg.code || ''))) {
|
|
184
|
+
for (const candidate of entries.values()) {
|
|
185
|
+
if (candidate.machine === msg.machine
|
|
186
|
+
&& (candidate.state === 'pending' || candidate.state === 'accepted')) {
|
|
187
|
+
candidate.state = 'orphaned';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
/** Adopts only an entry whose `created` is known not to be coming — state 'unconfirmed'
|
|
195
|
+
* (machine_create_unconfirmed) or 'orphaned' (an id-less machine error). An entry that is still
|
|
196
|
+
* 'pending'/'accepted' is left alone even when the list already carries its id: the server can
|
|
197
|
+
* broadcast a sessions list BEFORE the `created` frame, and adopting it here would activate the
|
|
198
|
+
* lazy stub and then let onCreated activate the same id again.
|
|
199
|
+
*
|
|
200
|
+
* For each adoptable id present in sessions: if state.activeTab === entry.activeTabAtClick →
|
|
201
|
+
* activateTab(id), else toast(info) "<label> is ready on <machineName>" key 'create-ready:'+id;
|
|
202
|
+
* clears entry, emits diag 'adopted'. Also drops entries older than TTL_MS (diag 'expired'). */
|
|
203
|
+
adoptFromList: function (sessions) {
|
|
204
|
+
const list = Array.isArray(sessions) ? sessions : [];
|
|
205
|
+
const present = new Set();
|
|
206
|
+
for (const row of list) {
|
|
207
|
+
if (row && row.id !== undefined && row.id !== null && row.id !== '') present.add(String(row.id));
|
|
208
|
+
}
|
|
209
|
+
const settledAt = nowMs();
|
|
210
|
+
const ttl = Number(PendingCreate.TTL_MS) || 0;
|
|
211
|
+
for (const id of Array.from(entries.keys())) {
|
|
212
|
+
const entry = entries.get(id);
|
|
213
|
+
if (!entry) continue;
|
|
214
|
+
if (present.has(id) && (entry.state === 'unconfirmed' || entry.state === 'orphaned')) {
|
|
215
|
+
// Drop the entry BEFORE activating: activation happens once, ever, even if activateTab
|
|
216
|
+
// synchronously provokes another sessions list.
|
|
217
|
+
drop(id);
|
|
218
|
+
clearPendingToast(id);
|
|
219
|
+
const activeTab = (typeof state === 'object' && state) ? state.activeTab : undefined;
|
|
220
|
+
if (activeTab === entry.activeTabAtClick) {
|
|
221
|
+
if (typeof activateTab === 'function') activateTab(id);
|
|
222
|
+
} else {
|
|
223
|
+
showToast('info', entry.label + ' is ready on ' + entry.machineName, readyKey(id));
|
|
224
|
+
}
|
|
225
|
+
emitDiag(entry, 'adopted');
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
// A 'pending'/'accepted' entry is never adopted, so its TTL is the only other way out.
|
|
229
|
+
if (settledAt - entry.clickedAt > ttl) {
|
|
230
|
+
// D-7: dropped silently — the session, if any, is already a normal list row by now.
|
|
231
|
+
drop(id);
|
|
232
|
+
clearPendingToast(id);
|
|
233
|
+
emitDiag(entry, 'expired');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
})();
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
//@provides: recentSourceKey, recentSourceForFilter, recentRequestUrl, stampRecentRowsFromMachine, recentMachineUnavailableText, recentMachineLoad, beginRecentLoad, recentSourceChanged, fetchMachineRecentRows, stampRecentBodyFromSource, recentNoteFleetBody, recentFleetStatusText, renderRecentFleetStatus
|
|
2
|
+
// Recent Sessions machine routing (docs/recent-sessions-follow-computer-filter-2026-10-31.html §5 D-1..D-4, §6,
|
|
3
|
+
// superseded for the All/Local mapping by docs/fleet-session-lists-hub-search-2026-11-01.html §5 D-11).
|
|
4
|
+
// ONE module owns the decisions the Recent list makes once the Active Sessions "Computers" filter names a
|
|
5
|
+
// paired box: which source answers (D-1 — that box's own list, never a hub merge), how the request is
|
|
6
|
+
// spelled (D-2 — the existing ?machine= hub proxy), how the box's rows are stamped with their provenance
|
|
7
|
+
// (D-3), what is said when the box cannot answer (D-4 — named, never "empty"), and which response is
|
|
8
|
+
// still current (D-5 — a request token, never timing). Dual browser/node (active-session-groups.js
|
|
9
|
+
// pattern): browser globals, plus module.exports for tests. Loaded AFTER js/core/active-session-groups.js.
|
|
10
|
+
|
|
11
|
+
// D-11 (fleet-session-lists-hub-search-2026-11-01.html §5 D-11, supersedes the 10-31 D-1 mapping for
|
|
12
|
+
// null): null = All computers -> the FLEET source (this hub fans out to every box, D-1..D-6); '' =
|
|
13
|
+
// Local -> the hub's own list; a non-empty string = that box's own list. Any other non-string value
|
|
14
|
+
// stays the hub, exactly as before.
|
|
15
|
+
function recentSourceForFilter(filter) {
|
|
16
|
+
if (filter === null) return { kind: 'fleet' };
|
|
17
|
+
return typeof filter === 'string' && filter !== ''
|
|
18
|
+
? { kind: 'machine', machine: filter }
|
|
19
|
+
: { kind: 'hub' };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// D-2/D-11: the query fragment that names the source. A fleet source adds &scope=fleet (this is the
|
|
23
|
+
// one place the parameter is spelled); a hub source appends nothing, so Local/All-but-fleet keeps its
|
|
24
|
+
// exact current URL. A box is ALSO asked for owner=self — without it the box answers with every row
|
|
25
|
+
// it holds, including its copies of the hub's own sessions (151 of Spark's top 200, §1.1), which then
|
|
26
|
+
// render as the box's. The box filters before its limit, so the list is that box's own top-N.
|
|
27
|
+
function recentRequestUrl(base, source) {
|
|
28
|
+
if (source && source.kind === 'fleet') return base + '&scope=fleet';
|
|
29
|
+
return source && source.kind === 'machine'
|
|
30
|
+
? base + '&machine=' + encodeURIComponent(source.machine) + '&owner=self'
|
|
31
|
+
: base;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// D-3: a box's own rows carry no machine key — they are local to the box. Stamping is by PROVENANCE
|
|
35
|
+
// (which request returned the row), never inferred from the row's content. Returns a new array.
|
|
36
|
+
function stampRecentRowsFromMachine(rows, machine, machineName) {
|
|
37
|
+
return (rows || []).map((row) => Object.assign({}, row, { machine, machineName }));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// D-4: the Recent empty state's copy when the box did not answer. 2xx -> '' (nothing to say). 404 /
|
|
41
|
+
// machine_unknown -> the pairing is gone. Anything else (502 machine_unreachable, 503
|
|
42
|
+
// peer_credential_missing, 0 / a fetch timeout) -> unreachable. A failed source is NEVER rendered as
|
|
43
|
+
// an empty hub list (agentic-dev-framework#10, §3).
|
|
44
|
+
function recentMachineUnavailableText(status, code, machineName) {
|
|
45
|
+
const name = machineName || 'This computer';
|
|
46
|
+
const s = Number(status) || 0;
|
|
47
|
+
if (s >= 200 && s < 300) return '';
|
|
48
|
+
if (s === 404 || code === 'machine_unknown') return name + ' is no longer paired.';
|
|
49
|
+
return name + " is unreachable — its recent sessions can't be listed right now.";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// D-5 + D-4: the load epoch. `machine` is the box the CURRENT load answers for (undefined when the
|
|
53
|
+
// source is not a box), `key` is the full source identity (kind + box id) a re-render compares
|
|
54
|
+
// against, `token` its epoch, `unavailable` the named failure the empty state shows ('' = none).
|
|
55
|
+
// Kept here, not in index.html, so the inline script gains call sites only.
|
|
56
|
+
const recentMachineLoad = { token: 0, machine: undefined, key: '', unavailable: '' };
|
|
57
|
+
|
|
58
|
+
/** D-11: one comparable identity per source — All, Local and box A are now three distinct sources. */
|
|
59
|
+
function recentSourceKey(source) {
|
|
60
|
+
return source.kind + ':' + (source.machine || '');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Every Recent load (hub, box or fleet) opens a new epoch and clears the previous failure text.
|
|
64
|
+
// Returns the source it answers for plus the token a response must still match to be applied.
|
|
65
|
+
function beginRecentLoad(filter) {
|
|
66
|
+
const source = recentSourceForFilter(filter);
|
|
67
|
+
recentMachineLoad.token += 1;
|
|
68
|
+
recentMachineLoad.machine = source.machine;
|
|
69
|
+
recentMachineLoad.key = recentSourceKey(source);
|
|
70
|
+
recentMachineLoad.unavailable = '';
|
|
71
|
+
return { source, token: recentMachineLoad.token };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// D-5/D-11: did the filter move the SOURCE? All (fleet) <-> Local (hub) is a source change now that
|
|
75
|
+
// null is the fleet, while box A <-> box B still is — so a comparison on the machine alone would
|
|
76
|
+
// silently keep the previous list. Compare the source KEY instead.
|
|
77
|
+
function recentSourceChanged(filter) {
|
|
78
|
+
return recentSourceKey(recentSourceForFilter(filter)) !== recentMachineLoad.key;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// D-7/D-11: one status line per member that did NOT answer `ok` (self is always `ok`, so it never
|
|
82
|
+
// produces a line). A silent empty list for a box that failed is the bug this copy exists to kill
|
|
83
|
+
// (§2.6). `status` values come from the fleet envelope (lib/fleet-session-lists.js FLEET_STATUSES).
|
|
84
|
+
const FLEET_STATUS_UNIT_MIN = 60;
|
|
85
|
+
const FLEET_STATUS_UNIT_HOUR = 60;
|
|
86
|
+
const FLEET_STATUS_UNIT_DAY = 24;
|
|
87
|
+
|
|
88
|
+
/** D-7: a short, floor-rounded age — `N s` / `N min` / `N h` / `N d`, never "just now", min 1 unit. */
|
|
89
|
+
function relativeAge(ms) {
|
|
90
|
+
const value = Number(ms);
|
|
91
|
+
const seconds = Math.floor((Number.isFinite(value) ? value : 0) / 1000);
|
|
92
|
+
if (seconds < FLEET_STATUS_UNIT_MIN) return Math.max(1, seconds) + ' s';
|
|
93
|
+
const minutes = Math.floor(seconds / FLEET_STATUS_UNIT_MIN);
|
|
94
|
+
if (minutes < FLEET_STATUS_UNIT_HOUR) return minutes + ' min';
|
|
95
|
+
const hours = Math.floor(minutes / FLEET_STATUS_UNIT_HOUR);
|
|
96
|
+
if (hours < FLEET_STATUS_UNIT_DAY) return hours + ' h';
|
|
97
|
+
return Math.floor(hours / FLEET_STATUS_UNIT_DAY) + ' d';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* D-7: the fleet status copy. `machines` is the envelope's `machines` (self first, so self is
|
|
102
|
+
* skipped by its `ok` status). Returns [] when every member answered, so the caller can hide the
|
|
103
|
+
* element instead of rendering an empty line.
|
|
104
|
+
* @param {object[]} machines
|
|
105
|
+
* @param {number} now epoch ms; `as_of` is compared against it (injectable for tests)
|
|
106
|
+
* @returns {string[]}
|
|
107
|
+
*/
|
|
108
|
+
function recentFleetStatusText(machines, now = Date.now()) {
|
|
109
|
+
const lines = [];
|
|
110
|
+
for (const machine of Array.isArray(machines) ? machines : []) {
|
|
111
|
+
if (!machine || machine.status === 'ok') continue;
|
|
112
|
+
const name = machine.machine_name || machine.machine_id;
|
|
113
|
+
if (machine.status === 'stale') {
|
|
114
|
+
lines.push(name + ': showing its list from ' + relativeAge(Number(now) - Number(machine.as_of)) + ' ago');
|
|
115
|
+
} else if (machine.status === 'timeout') {
|
|
116
|
+
lines.push(name + " didn't answer in time — its sessions are missing");
|
|
117
|
+
} else if (machine.status === 'unreachable' || machine.status === 'skipped') {
|
|
118
|
+
lines.push(name + ' is unreachable — its sessions are missing');
|
|
119
|
+
} else {
|
|
120
|
+
lines.push(name + ' returned an error — its sessions are missing');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return lines;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* D-7: the ONE owner of `<div id="recent-fleet-status">`, created lazily right after #recent-search.
|
|
128
|
+
* Each line is a child <div> set with textContent (never innerHTML — the strings carry machine
|
|
129
|
+
* names). Empty `lines` hides the element. A no-op without a DOM (node tests).
|
|
130
|
+
*/
|
|
131
|
+
function renderRecentFleetStatus(lines) {
|
|
132
|
+
if (typeof document === 'undefined' || !document) return;
|
|
133
|
+
const list = Array.isArray(lines) ? lines : [];
|
|
134
|
+
const search = document.getElementById('recent-search');
|
|
135
|
+
let el = document.getElementById('recent-fleet-status');
|
|
136
|
+
if (!el) {
|
|
137
|
+
// Below the whole search row: #recent-search sits in a flex row (.search-input-wrap inside
|
|
138
|
+
// .search-row), so inserting beside the input would shrink it instead of adding a line.
|
|
139
|
+
const anchor = (search && typeof search.closest === 'function' && search.closest('.search-row')) || search;
|
|
140
|
+
if (!anchor || !anchor.parentNode) return;
|
|
141
|
+
el = document.createElement('div');
|
|
142
|
+
el.id = 'recent-fleet-status';
|
|
143
|
+
el.className = 'recent-fleet-status';
|
|
144
|
+
el.setAttribute('role', 'status');
|
|
145
|
+
el.style.cssText = 'font-size:11px;color:var(--fg-dim);padding:2px 8px;';
|
|
146
|
+
anchor.parentNode.insertBefore(el, anchor.nextSibling);
|
|
147
|
+
}
|
|
148
|
+
el.textContent = '';
|
|
149
|
+
for (const line of list) {
|
|
150
|
+
const lineEl = document.createElement('div');
|
|
151
|
+
lineEl.textContent = String(line);
|
|
152
|
+
el.appendChild(lineEl);
|
|
153
|
+
}
|
|
154
|
+
el.hidden = list.length === 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// D-7: the status line a parsed list body implies, rendered inline so index.html gains no lines.
|
|
158
|
+
// A fleet body was stamped (and its `machines` filled) by the server; a hub or machine body carries
|
|
159
|
+
// no `machines`, so the element is cleared rather than left showing a previous fleet's failure.
|
|
160
|
+
function recentNoteFleetBody(body) {
|
|
161
|
+
renderRecentFleetStatus(recentFleetStatusText((body && body.machines) || []));
|
|
162
|
+
return body;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// The box half of a Recent load: GET through the hub proxy, stamped on arrival. NEVER throws — a
|
|
166
|
+
// failed box is an empty, NAMED list (D-4), never a hub fallback. Only a still-current load (token)
|
|
167
|
+
// may publish the failure text.
|
|
168
|
+
async function fetchMachineRecentRows(fetchImpl, base, load, labels) {
|
|
169
|
+
const machine = load.source.machine;
|
|
170
|
+
const name = (labels && labels.get ? labels.get(machine) : '') || machine;
|
|
171
|
+
let status = 0;
|
|
172
|
+
let code;
|
|
173
|
+
try {
|
|
174
|
+
const res = await fetchImpl(recentRequestUrl(base, load.source));
|
|
175
|
+
status = res && Number(res.status) || 0;
|
|
176
|
+
let body = null;
|
|
177
|
+
try {
|
|
178
|
+
body = await res.json();
|
|
179
|
+
} catch {
|
|
180
|
+
/* expected: an unreadable proxy body is a failed source, not a thrown load */
|
|
181
|
+
}
|
|
182
|
+
if (status >= 200 && status < 300) {
|
|
183
|
+
const rows = Array.isArray(body) ? body : (Array.isArray(body?.sessions) ? body.sessions : []);
|
|
184
|
+
return stampRecentRowsFromMachine(rows, machine, name);
|
|
185
|
+
}
|
|
186
|
+
code = body?.code || body?.error;
|
|
187
|
+
} catch {
|
|
188
|
+
/* expected: a transport failure (proxy timeout, refused connection) is a named empty list */
|
|
189
|
+
status = 0;
|
|
190
|
+
}
|
|
191
|
+
if (load.token === recentMachineLoad.token) {
|
|
192
|
+
recentMachineLoad.unavailable = recentMachineUnavailableText(status, code, name);
|
|
193
|
+
}
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// D-3, search flavour: `/api/sessions/search` answers `{results: [...]}`. A box's hits are its own
|
|
198
|
+
// rows, so they are stamped the same way, and the body it returns carries the source machine as
|
|
199
|
+
// `_machine` (provenance, not content) so the caller can tell whether the filter moved while the
|
|
200
|
+
// search was in flight (D-5) without holding a variable of its own. A hub body is handed back
|
|
201
|
+
// untouched — `_machine` is then undefined. `filterOrSource` accepts either a filter value or a
|
|
202
|
+
// source object from recentSourceForFilter().
|
|
203
|
+
function stampRecentBodyFromSource(body, filterOrSource, labels) {
|
|
204
|
+
const source = filterOrSource && filterOrSource.kind ? filterOrSource : recentSourceForFilter(filterOrSource);
|
|
205
|
+
// D-7/D-11: the fleet status line is rendered from the body itself, so index.html's search call
|
|
206
|
+
// site needs no extra line. A hub or machine body has no `machines`, so this clears the element.
|
|
207
|
+
recentNoteFleetBody(body);
|
|
208
|
+
if (!body) return body;
|
|
209
|
+
// D-5: every body names the SOURCE it answers (kind + machine), so the caller can drop a response
|
|
210
|
+
// whose source is no longer the current one — including All (fleet) <-> Local (hub), which share
|
|
211
|
+
// no machine id.
|
|
212
|
+
if (source.kind !== 'machine') return Object.assign({}, body, { _sourceKey: recentSourceKey(source) });
|
|
213
|
+
const machine = source.machine;
|
|
214
|
+
const name = (labels && labels.get ? labels.get(machine) : '') || machine;
|
|
215
|
+
return Object.assign({}, body, {
|
|
216
|
+
_sourceKey: recentSourceKey(source),
|
|
217
|
+
_machine: machine,
|
|
218
|
+
results: Array.isArray(body.results) ? stampRecentRowsFromMachine(body.results, machine, name) : body.results,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
223
|
+
module.exports = {
|
|
224
|
+
recentSourceKey, recentSourceForFilter, recentRequestUrl, stampRecentRowsFromMachine, recentMachineUnavailableText,
|
|
225
|
+
recentMachineLoad, beginRecentLoad, recentSourceChanged, fetchMachineRecentRows, stampRecentBodyFromSource,
|
|
226
|
+
recentNoteFleetBody, recentFleetStatusText, renderRecentFleetStatus,
|
|
227
|
+
};
|
|
228
|
+
}
|