@songsid/agend 2.1.12 → 2.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/README.zh-TW.md +4 -2
- package/dist/agent-cli.js +6 -1
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +68 -12
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-token-header.d.ts +13 -0
- package/dist/agent-token-header.js +16 -0
- package/dist/agent-token-header.js.map +1 -0
- package/dist/auth/one-time-code.d.ts +19 -0
- package/dist/auth/one-time-code.js +49 -0
- package/dist/auth/one-time-code.js.map +1 -0
- package/dist/auth-api.d.ts +52 -0
- package/dist/auth-api.js +346 -0
- package/dist/auth-api.js.map +1 -0
- package/dist/backend/antigravity.js +4 -3
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +4 -0
- package/dist/backend/claude-code.js +97 -0
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.js +59 -30
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok.js +1 -1
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro-agent.d.ts +66 -0
- package/dist/backend/kiro-agent.js +302 -0
- package/dist/backend/kiro-agent.js.map +1 -0
- package/dist/backend/kiro-identity.d.ts +80 -0
- package/dist/backend/kiro-identity.js +537 -0
- package/dist/backend/kiro-identity.js.map +1 -0
- package/dist/backend/kiro-v2-store-worker.d.ts +1 -0
- package/dist/backend/kiro-v2-store-worker.js +87 -0
- package/dist/backend/kiro-v2-store-worker.js.map +1 -0
- package/dist/backend/kiro-v2-store.d.ts +54 -0
- package/dist/backend/kiro-v2-store.js +131 -0
- package/dist/backend/kiro-v2-store.js.map +1 -0
- package/dist/backend/kiro-v3-identity.d.ts +5 -0
- package/dist/backend/kiro-v3-identity.js +22 -0
- package/dist/backend/kiro-v3-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +90 -7
- package/dist/backend/kiro.js +500 -124
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/muse.js +1 -1
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +51 -3
- package/dist/backend/types.js.map +1 -1
- package/dist/cache-analysis.d.ts +92 -0
- package/dist/cache-analysis.js +147 -0
- package/dist/cache-analysis.js.map +1 -0
- package/dist/cache-ledger.d.ts +108 -0
- package/dist/cache-ledger.js +230 -0
- package/dist/cache-ledger.js.map +1 -0
- package/dist/cache-prices.d.ts +32 -0
- package/dist/cache-prices.js +73 -0
- package/dist/cache-prices.js.map +1 -0
- package/dist/cache-scan.d.ts +39 -0
- package/dist/cache-scan.js +115 -0
- package/dist/cache-scan.js.map +1 -0
- package/dist/cache-service.d.ts +115 -0
- package/dist/cache-service.js +417 -0
- package/dist/cache-service.js.map +1 -0
- package/dist/channel/adapters/discord.d.ts +1 -0
- package/dist/channel/adapters/discord.js +29 -10
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +1 -0
- package/dist/channel/adapters/telegram.js +13 -1
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/types.d.ts +9 -1
- package/dist/classic-channel-manager.d.ts +18 -10
- package/dist/classic-channel-manager.js +45 -18
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli-entry.d.ts +19 -0
- package/dist/cli-entry.js +36 -0
- package/dist/cli-entry.js.map +1 -0
- package/dist/cli.js +471 -146
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +5 -3
- package/dist/command-table.js +29 -27
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +46 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -1
- package/dist/connection-secrets.d.ts +6 -0
- package/dist/connection-secrets.js +4 -0
- package/dist/connection-secrets.js.map +1 -1
- package/dist/daemon.d.ts +95 -4
- package/dist/daemon.js +748 -111
- package/dist/daemon.js.map +1 -1
- package/dist/darwin-memory.d.ts +6 -1
- package/dist/darwin-memory.js +17 -6
- package/dist/darwin-memory.js.map +1 -1
- package/dist/delivery-consumed-watch.d.ts +85 -0
- package/dist/delivery-consumed-watch.js +244 -0
- package/dist/delivery-consumed-watch.js.map +1 -0
- package/dist/delivery-outbox.d.ts +61 -0
- package/dist/delivery-outbox.js +120 -2
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +14 -5
- package/dist/delivery-reconciliation.js +27 -4
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/delivery-reconciliation.test.js +35 -1
- package/dist/delivery-reconciliation.test.js.map +1 -1
- package/dist/event-log.d.ts +4 -1
- package/dist/event-log.js +6 -3
- package/dist/event-log.js.map +1 -1
- package/dist/event-loop-watch.d.ts +28 -0
- package/dist/event-loop-watch.js +75 -3
- package/dist/event-loop-watch.js.map +1 -1
- package/dist/fleet-context.d.ts +22 -2
- package/dist/fleet-control-audit.d.ts +5 -0
- package/dist/fleet-control-audit.js +7 -0
- package/dist/fleet-control-audit.js.map +1 -1
- package/dist/fleet-level-config.d.ts +2 -2
- package/dist/fleet-level-config.js +28 -1
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +339 -12
- package/dist/fleet-manager.js +2623 -461
- package/dist/fleet-manager.js.map +1 -1
- package/dist/full-restart.js +2 -3
- package/dist/full-restart.js.map +1 -1
- package/dist/general-knowledge/skills/persona-emoji/SKILL.md +3 -1
- package/dist/host-memory.d.ts +4 -0
- package/dist/host-memory.js.map +1 -1
- package/dist/install-lock.d.ts +49 -0
- package/dist/install-lock.js +200 -0
- package/dist/install-lock.js.map +1 -0
- package/dist/instance-config-impact.js +8 -2
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +10 -5
- package/dist/instance-lifecycle.js +162 -53
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +164 -16
- package/dist/locale.js.map +1 -1
- package/dist/login-controller.d.ts +2 -0
- package/dist/login-controller.js +17 -5
- package/dist/login-controller.js.map +1 -1
- package/dist/memory-pressure.d.ts +7 -8
- package/dist/memory-pressure.js +76 -15
- package/dist/memory-pressure.js.map +1 -1
- package/dist/monotonic-budget.d.ts +2 -0
- package/dist/monotonic-budget.js +28 -0
- package/dist/monotonic-budget.js.map +1 -0
- package/dist/needs-you-hub.d.ts +114 -0
- package/dist/needs-you-hub.js +370 -0
- package/dist/needs-you-hub.js.map +1 -0
- package/dist/needs-you-live.d.ts +116 -0
- package/dist/needs-you-live.js +228 -0
- package/dist/needs-you-live.js.map +1 -0
- package/dist/needs-you.d.ts +132 -0
- package/dist/needs-you.js +164 -0
- package/dist/needs-you.js.map +1 -0
- package/dist/node-version-guard.d.ts +30 -0
- package/dist/node-version-guard.js +50 -0
- package/dist/node-version-guard.js.map +1 -0
- package/dist/operator-control-paths.d.ts +4 -0
- package/dist/operator-control-paths.js +24 -0
- package/dist/operator-control-paths.js.map +1 -0
- package/dist/outbound-handlers.d.ts +2 -0
- package/dist/outbound-handlers.js +9 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +5 -5
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/package-preimage.d.ts +26 -0
- package/dist/package-preimage.js +96 -0
- package/dist/package-preimage.js.map +1 -0
- package/dist/profile-control.js +4 -20
- package/dist/profile-control.js.map +1 -1
- package/dist/public-web-gateway.d.ts +18 -0
- package/dist/public-web-gateway.js +141 -0
- package/dist/public-web-gateway.js.map +1 -0
- package/dist/public-web-link.d.ts +53 -0
- package/dist/public-web-link.js +175 -0
- package/dist/public-web-link.js.map +1 -0
- package/dist/quickstart-api.d.ts +8 -1
- package/dist/quickstart-api.js +86 -48
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +1 -0
- package/dist/quickstart.js +16 -6
- package/dist/quickstart.js.map +1 -1
- package/dist/restart-guard.d.ts +52 -0
- package/dist/restart-guard.js +109 -0
- package/dist/restart-guard.js.map +1 -0
- package/dist/scheduler/db.d.ts +7 -1
- package/dist/scheduler/db.js +44 -2
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/index.d.ts +1 -1
- package/dist/scheduler/retry-label.d.ts +11 -0
- package/dist/scheduler/retry-label.js +24 -0
- package/dist/scheduler/retry-label.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +55 -2
- package/dist/scheduler/scheduler.js +220 -8
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/types.d.ts +22 -0
- package/dist/scheduler/types.js.map +1 -1
- package/dist/secret-store.d.ts +5 -0
- package/dist/secret-store.js +20 -0
- package/dist/secret-store.js.map +1 -1
- package/dist/service-activation.d.ts +152 -0
- package/dist/service-activation.js +407 -0
- package/dist/service-activation.js.map +1 -0
- package/dist/service-installer.d.ts +63 -1
- package/dist/service-installer.js +141 -16
- package/dist/service-installer.js.map +1 -1
- package/dist/service-plan.d.ts +65 -0
- package/dist/service-plan.js +108 -0
- package/dist/service-plan.js.map +1 -0
- package/dist/service-restart-selection.d.ts +6 -5
- package/dist/service-restart-selection.js +6 -5
- package/dist/service-restart-selection.js.map +1 -1
- package/dist/settings-api.d.ts +10 -2
- package/dist/settings-api.js +216 -72
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-baseline.d.ts +20 -0
- package/dist/settings-baseline.js +104 -0
- package/dist/settings-baseline.js.map +1 -0
- package/dist/settings-change.d.ts +21 -0
- package/dist/settings-change.js +161 -0
- package/dist/settings-change.js.map +1 -0
- package/dist/settings-confirmation.d.ts +105 -0
- package/dist/settings-confirmation.js +232 -0
- package/dist/settings-confirmation.js.map +1 -0
- package/dist/settings-control.d.ts +41 -0
- package/dist/settings-control.js +294 -0
- package/dist/settings-control.js.map +1 -0
- package/dist/settings-effect.d.ts +24 -0
- package/dist/settings-effect.js +169 -0
- package/dist/settings-effect.js.map +1 -0
- package/dist/settings-http-confirmation.d.ts +29 -0
- package/dist/settings-http-confirmation.js +123 -0
- package/dist/settings-http-confirmation.js.map +1 -0
- package/dist/settings-instance-patch.d.ts +4 -0
- package/dist/settings-instance-patch.js +26 -0
- package/dist/settings-instance-patch.js.map +1 -0
- package/dist/settings-request-capability.d.ts +16 -0
- package/dist/settings-request-capability.js +32 -0
- package/dist/settings-request-capability.js.map +1 -0
- package/dist/settings-transaction.d.ts +65 -0
- package/dist/settings-transaction.js +223 -0
- package/dist/settings-transaction.js.map +1 -0
- package/dist/setup-auth.d.ts +10 -18
- package/dist/setup-auth.js +14 -38
- package/dist/setup-auth.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +75 -18
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-guide.d.ts +13 -0
- package/dist/setup-guide.js +25 -0
- package/dist/setup-guide.js.map +1 -0
- package/dist/setup-host.d.ts +16 -0
- package/dist/setup-host.js +199 -35
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +5 -1
- package/dist/setup-wizard.js.map +1 -1
- package/dist/slash-authz.d.ts +17 -3
- package/dist/slash-authz.js +5 -0
- package/dist/slash-authz.js.map +1 -1
- package/dist/spawn-gate.js +3 -3
- package/dist/spawn-gate.js.map +1 -1
- package/dist/sqlite-open-errors.d.ts +12 -0
- package/dist/sqlite-open-errors.js +13 -0
- package/dist/sqlite-open-errors.js.map +1 -0
- package/dist/statusline-watcher.d.ts +5 -0
- package/dist/statusline-watcher.js +2 -0
- package/dist/statusline-watcher.js.map +1 -1
- package/dist/steer-capability.d.ts +6 -0
- package/dist/steer-capability.js +8 -0
- package/dist/steer-capability.js.map +1 -1
- package/dist/sync-work-attribution.d.ts +14 -0
- package/dist/sync-work-attribution.js +43 -8
- package/dist/sync-work-attribution.js.map +1 -1
- package/dist/tips.js +10 -5
- package/dist/tips.js.map +1 -1
- package/dist/tmux-control.d.ts +57 -5
- package/dist/tmux-control.js +379 -61
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.d.ts +20 -6
- package/dist/tmux-manager.js +90 -47
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.d.ts +59 -0
- package/dist/tmux-read.js +213 -0
- package/dist/tmux-read.js.map +1 -0
- package/dist/topic-commands.d.ts +21 -10
- package/dist/topic-commands.js +205 -63
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.js +2 -1
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/tunnel/cloudflared-install.d.ts +2 -0
- package/dist/tunnel/cloudflared-install.js +1 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/purpose-lane.d.ts +14 -0
- package/dist/tunnel/purpose-lane.js +43 -0
- package/dist/tunnel/purpose-lane.js.map +1 -0
- package/dist/turn-reply-guard.d.ts +2 -0
- package/dist/turn-reply-guard.js +10 -1
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/ui/app.html +18 -0
- package/dist/ui/chat-commands.js +43 -0
- package/dist/ui/chat-render.js +478 -0
- package/dist/ui/chat-store.js +318 -0
- package/dist/ui/chat-strings.js +89 -0
- package/dist/ui/chat-thread.js +246 -0
- package/dist/ui/chat-tour.js +116 -0
- package/dist/ui/fleet-cache.js +168 -0
- package/dist/ui/fleet-org.js +142 -0
- package/dist/ui/panel-chat.js +504 -0
- package/dist/ui/panel-fleet.js +427 -0
- package/dist/ui/panel-needs.js +202 -0
- package/dist/ui/panel-settings.js +754 -0
- package/dist/ui/preview-panel.js +202 -0
- package/dist/ui/preview.js +270 -0
- package/dist/ui/settings-apply.js +239 -0
- package/dist/ui/settings-confirm.js +186 -0
- package/dist/ui/settings-dialogs.js +546 -0
- package/dist/ui/settings-model.js +277 -0
- package/dist/ui/settings-strings.js +654 -0
- package/dist/ui/settings-wizard.js +140 -0
- package/dist/ui/shared/agend-auth.js +93 -0
- package/dist/ui/shared/app-ctx.js +80 -0
- package/dist/ui/shared/app-html.js +9 -0
- package/dist/ui/shared/app-i18n.js +95 -0
- package/dist/ui/shared/app-nav.js +73 -0
- package/dist/ui/shared/app-needs.js +114 -0
- package/dist/ui/shared/app-route.js +73 -0
- package/dist/ui/shared/app-session.js +66 -0
- package/dist/ui/shared/app-shell.js +346 -0
- package/dist/ui/shared/app-store.js +51 -0
- package/dist/ui/shared/app-stream.js +201 -0
- package/dist/ui/shared/app.css +820 -0
- package/dist/ui/shared/app.js +88 -0
- package/dist/ui/shared/fonts/OFL.txt +92 -0
- package/dist/ui/shared/fonts/README.md +21 -0
- package/dist/ui/shared/fonts/inter.woff2 +0 -0
- package/dist/ui/shared/htm.module.js +1 -0
- package/dist/ui/shared/panel-view.js +655 -0
- package/dist/ui/shared/preact-hooks.module.js +2 -0
- package/dist/ui/shared/preact.module.js +2 -0
- package/dist/ui/shared/signin.js +142 -0
- package/dist/ui/shared/theme.js +29 -0
- package/dist/ui/shared/tokens.css +153 -0
- package/dist/ui/shared/ui-confirm.js +43 -0
- package/dist/ui/shared/ui-dialog.js +45 -0
- package/dist/ui/shared/ui-icons.js +63 -0
- package/dist/ui/shared/ui-menu.js +39 -0
- package/dist/ui/shared/ui-states.js +20 -0
- package/dist/ui/shared/ui-toast.js +18 -0
- package/dist/ui/shared/vendor/htm.LICENSE +202 -0
- package/dist/ui/shared/vendor/preact.LICENSE +21 -0
- package/dist/ui/shared/vendor/vendor.json +39 -0
- package/dist/ui/shared/view-strings.js +309 -0
- package/dist/ui/signin.html +52 -0
- package/dist/update-check.d.ts +6 -2
- package/dist/update-check.js +12 -3
- package/dist/update-check.js.map +1 -1
- package/dist/update-dispatch.d.ts +28 -0
- package/dist/update-dispatch.js +73 -0
- package/dist/update-dispatch.js.map +1 -0
- package/dist/update-install.d.ts +129 -0
- package/dist/update-install.js +262 -0
- package/dist/update-install.js.map +1 -0
- package/dist/upgrade-notices.d.ts +17 -0
- package/dist/upgrade-notices.js +93 -0
- package/dist/upgrade-notices.js.map +1 -0
- package/dist/usage/statusline-usage.d.ts +2 -0
- package/dist/usage/statusline-usage.js +14 -10
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/view-api.d.ts +6 -1
- package/dist/view-api.js +62 -51
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +85 -14
- package/dist/web-api.js +557 -77
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +71 -27
- package/dist/web-auth.js +169 -69
- package/dist/web-auth.js.map +1 -1
- package/dist/web-body.d.ts +3 -0
- package/dist/web-body.js +55 -0
- package/dist/web-body.js.map +1 -0
- package/dist/web-channel-echo.d.ts +9 -0
- package/dist/web-channel-echo.js +55 -0
- package/dist/web-channel-echo.js.map +1 -0
- package/dist/web-chat-history.d.ts +125 -0
- package/dist/web-chat-history.js +184 -0
- package/dist/web-chat-history.js.map +1 -0
- package/dist/web-commands.d.ts +77 -0
- package/dist/web-commands.js +81 -0
- package/dist/web-commands.js.map +1 -0
- package/dist/web-continuation.d.ts +7 -0
- package/dist/web-continuation.js +22 -0
- package/dist/web-continuation.js.map +1 -0
- package/dist/web-host-guard.d.ts +34 -0
- package/dist/web-host-guard.js +57 -1
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-login.d.ts +72 -0
- package/dist/web-login.js +132 -0
- package/dist/web-login.js.map +1 -0
- package/dist/web-org.d.ts +55 -0
- package/dist/web-org.js +67 -0
- package/dist/web-org.js.map +1 -0
- package/dist/web-preview.d.ts +77 -0
- package/dist/web-preview.js +240 -0
- package/dist/web-preview.js.map +1 -0
- package/dist/web-request-context.d.ts +10 -0
- package/dist/web-request-context.js +13 -0
- package/dist/web-request-context.js.map +1 -0
- package/dist/web-session.d.ts +167 -0
- package/dist/web-session.js +389 -0
- package/dist/web-session.js.map +1 -0
- package/dist/web-shell-routes.d.ts +59 -0
- package/dist/web-shell-routes.js +96 -0
- package/dist/web-shell-routes.js.map +1 -0
- package/dist/web-upload.d.ts +183 -0
- package/dist/web-upload.js +468 -0
- package/dist/web-upload.js.map +1 -0
- package/dist/website-tips-renderer.d.ts +2 -2
- package/dist/website-tips-renderer.js +3 -3
- package/dist/website-tips-renderer.js.map +1 -1
- package/launcher/agend +110 -0
- package/launcher/agend-agent +110 -0
- package/launcher/agend-agent.cjs +4 -0
- package/launcher/agend.cjs +4 -0
- package/launcher/install-admission.cjs +105 -0
- package/launcher/launch.cjs +64 -0
- package/launcher/postinstall.cjs +114 -0
- package/launcher/run-in-process.cjs +13 -0
- package/launcher/runtime-platform.cjs +86 -0
- package/launcher/runtime-select.cjs +217 -0
- package/package.json +18 -7
- package/scripts/preinstall-guard.cjs +55 -0
- package/templates/launchd.plist.ejs +2 -2
- package/templates/systemd.service.ejs +1 -1
- package/dist/ui/dashboard.html +0 -774
- package/dist/ui/settings.html +0 -2165
- package/dist/ui/view.html +0 -1139
package/launcher/agend
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# #1450: the `agend` and `agend-agent` npm bins (this file and launcher/agend-agent are identical). POSIX sh, so AgEnD
|
|
3
|
+
# starts with no Node on PATH at all: it execs this release's bundled Node on the JS launcher, which is the one place
|
|
4
|
+
# that decides and verifies the interpreter (launcher/runtime-select.cjs). A `node` on PATH is used only when the
|
|
5
|
+
# bundled Node is not installed; the JS launcher then decides whether that Node qualifies. Native Windows is not
|
|
6
|
+
# supported (WSL is), as with tmux.
|
|
7
|
+
|
|
8
|
+
# This file's real location: npm's global bin is a symlink, possibly relative, possibly to another link. A readlink
|
|
9
|
+
# loop, not `readlink -f` (older macOS lacks it; GNU is not assumed).
|
|
10
|
+
self=$0
|
|
11
|
+
while [ -L "$self" ]; do
|
|
12
|
+
link=$(readlink "$self") || break
|
|
13
|
+
case $link in
|
|
14
|
+
/*) self=$link ;;
|
|
15
|
+
*) self=$(dirname "$self")/$link ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
dir=$(cd "$(dirname "$self")" && pwd -P) || exit 1
|
|
19
|
+
pkg=$(dirname "$dir")
|
|
20
|
+
launcher=$dir/$(basename "$self").cjs
|
|
21
|
+
|
|
22
|
+
sys=$(uname -s) || sys=unknown
|
|
23
|
+
mach=$(uname -m) || mach=unknown
|
|
24
|
+
case $sys in
|
|
25
|
+
Linux) os=linux ;;
|
|
26
|
+
Darwin) os=darwin ;;
|
|
27
|
+
*) os=unsupported ;;
|
|
28
|
+
esac
|
|
29
|
+
case $mach in
|
|
30
|
+
x86_64 | amd64) cpu=x64 ;;
|
|
31
|
+
aarch64 | arm64) cpu=arm64 ;;
|
|
32
|
+
*) cpu=unsupported ;;
|
|
33
|
+
esac
|
|
34
|
+
node_pkg=$pkg/node_modules/@songsid/agend-node-$os-$cpu
|
|
35
|
+
node=$node_pkg/bin/node
|
|
36
|
+
receipt=$pkg/.agend-runtime.json
|
|
37
|
+
repair="npm install -g @songsid/agend"
|
|
38
|
+
refuse() { echo "AgEnD cannot start: $1." >&2; echo "To repair: $2" >&2; exit "${3:-1}"; }
|
|
39
|
+
|
|
40
|
+
# 1. AGEND_NODE, the operator's explicit choice: absolute and executable, then it runs the JS launcher, whose probe
|
|
41
|
+
# refuses one that does not qualify. Never a silent fallback to another Node.
|
|
42
|
+
if [ -n "${AGEND_NODE:-}" ]; then
|
|
43
|
+
case $AGEND_NODE in
|
|
44
|
+
/*) ;;
|
|
45
|
+
*) refuse "AGEND_NODE must be an absolute path (got $AGEND_NODE)" "unset AGEND_NODE, or point it at a Node 22.14+" ;;
|
|
46
|
+
esac
|
|
47
|
+
[ -f "$AGEND_NODE" ] && [ -x "$AGEND_NODE" ] || refuse "AGEND_NODE ($AGEND_NODE) is not an executable file" "unset AGEND_NODE, or point it at a Node 22.14+"
|
|
48
|
+
exec "$AGEND_NODE" "$launcher" "$@"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 2. The bundled Node, only when its ADMISSION KEY (launcher/runtime-select.cjs, "THE ADMISSION KEY") is exactly what
|
|
52
|
+
# this shell measures now — nothing parsed, the bytes rebuilt and compared: the host's os and cpu (never its
|
|
53
|
+
# version: an OS update must not lock AgEnD out), the Node's realpath, size and mtime, and the POSIX cksum of both
|
|
54
|
+
# manifests and of the receipt. The JS selection requires the same key, so a
|
|
55
|
+
# change to any bound file keeps BOTH from running the candidate.
|
|
56
|
+
# Every measurement must itself complete: a value printed by a command that then fails is no measurement (#1460 r4).
|
|
57
|
+
admissible() {
|
|
58
|
+
[ -f "$pkg/.agend-runtime.key" ] && [ -f "$node" ] && [ ! -L "$node" ] && [ -x "$node" ] || return 1
|
|
59
|
+
case $os in linux | darwin) ;; *) return 1 ;; esac
|
|
60
|
+
real=$(cd "${node%/*}" 2>/dev/null && pwd -P) || return 1
|
|
61
|
+
real=$real/node
|
|
62
|
+
size=$(wc -c < "$node") || return 1
|
|
63
|
+
set -- $size
|
|
64
|
+
size=${1:-}
|
|
65
|
+
case $size in '' | *[!0-9]*) return 1 ;; esac
|
|
66
|
+
if mtime=$(stat -c %Y "$node" 2>/dev/null); then :; elif mtime=$(stat -f %m "$node" 2>/dev/null); then :; else return 1; fi
|
|
67
|
+
case $mtime in '' | *[!0-9]*) return 1 ;; esac
|
|
68
|
+
sum_package=$(cksum < "$pkg/package.json") || return 1
|
|
69
|
+
sum_runtime=$(cksum < "$node_pkg/package.json") || return 1
|
|
70
|
+
sum_receipt=$(cksum < "$receipt") || return 1
|
|
71
|
+
printf 'agend-runtime-key 2\nos %s\ncpu %s\nnode %s\nsize %s\nmtime %s\npackage %s\nruntime %s\nreceipt %s\n' \
|
|
72
|
+
"$os" "$cpu" "$real" "$size" "$mtime" "$sum_package" "$sum_runtime" "$sum_receipt" | cmp -s - "$pkg/.agend-runtime.key"
|
|
73
|
+
}
|
|
74
|
+
# This host, measured fresh, must still be one the bundled Node supports (launcher/runtime-platform.cjs
|
|
75
|
+
# runtimeSupport: glibc >= 2.28 on Linux, Darwin kernel >= 20) — minimums, never a pinned version. A stored or copied
|
|
76
|
+
# runtime on a host that no longer qualifies never runs; the JS launcher then picks a system Node or refuses.
|
|
77
|
+
supported() {
|
|
78
|
+
case $os in
|
|
79
|
+
linux)
|
|
80
|
+
v=$(getconf GNU_LIBC_VERSION 2>/dev/null) || return 1
|
|
81
|
+
case $v in "glibc "*) v=${v#glibc } ;; *) return 1 ;; esac
|
|
82
|
+
major=${v%%.*}
|
|
83
|
+
minor=${v#*.}
|
|
84
|
+
minor=${minor%%.*}
|
|
85
|
+
case $major in '' | *[!0-9]*) return 1 ;; esac
|
|
86
|
+
case $minor in '' | *[!0-9]*) return 1 ;; esac
|
|
87
|
+
[ "$major" -gt 2 ] || { [ "$major" -eq 2 ] && [ "$minor" -ge 28 ]; }
|
|
88
|
+
;;
|
|
89
|
+
darwin)
|
|
90
|
+
r=$(uname -r) || return 1
|
|
91
|
+
kernel=${r%%.*}
|
|
92
|
+
case $kernel in '' | *[!0-9]*) return 1 ;; esac
|
|
93
|
+
[ "$kernel" -ge 20 ]
|
|
94
|
+
;;
|
|
95
|
+
*) return 1 ;;
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
98
|
+
if supported && admissible; then
|
|
99
|
+
exec "$node" "$launcher" "$@"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# 3. Not admitted here, so that candidate never runs. A node on PATH runs the JS launcher: it explains a refusal, or
|
|
103
|
+
# is itself the choice when the runtime was skipped altogether (and it qualifies).
|
|
104
|
+
if command -v node >/dev/null 2>&1; then
|
|
105
|
+
exec node "$launcher" "$@"
|
|
106
|
+
fi
|
|
107
|
+
if [ -e "$receipt" ] || [ -e "$node" ] || [ -L "$node" ]; then
|
|
108
|
+
refuse "its bundled Node ($node) is missing, incomplete or changed since it was verified, and there is no node on PATH" "$repair"
|
|
109
|
+
fi
|
|
110
|
+
refuse "its bundled Node is not installed and there is no node on PATH" "$repair" 127
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# #1450: the `agend` and `agend-agent` npm bins (this file and launcher/agend-agent are identical). POSIX sh, so AgEnD
|
|
3
|
+
# starts with no Node on PATH at all: it execs this release's bundled Node on the JS launcher, which is the one place
|
|
4
|
+
# that decides and verifies the interpreter (launcher/runtime-select.cjs). A `node` on PATH is used only when the
|
|
5
|
+
# bundled Node is not installed; the JS launcher then decides whether that Node qualifies. Native Windows is not
|
|
6
|
+
# supported (WSL is), as with tmux.
|
|
7
|
+
|
|
8
|
+
# This file's real location: npm's global bin is a symlink, possibly relative, possibly to another link. A readlink
|
|
9
|
+
# loop, not `readlink -f` (older macOS lacks it; GNU is not assumed).
|
|
10
|
+
self=$0
|
|
11
|
+
while [ -L "$self" ]; do
|
|
12
|
+
link=$(readlink "$self") || break
|
|
13
|
+
case $link in
|
|
14
|
+
/*) self=$link ;;
|
|
15
|
+
*) self=$(dirname "$self")/$link ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
dir=$(cd "$(dirname "$self")" && pwd -P) || exit 1
|
|
19
|
+
pkg=$(dirname "$dir")
|
|
20
|
+
launcher=$dir/$(basename "$self").cjs
|
|
21
|
+
|
|
22
|
+
sys=$(uname -s) || sys=unknown
|
|
23
|
+
mach=$(uname -m) || mach=unknown
|
|
24
|
+
case $sys in
|
|
25
|
+
Linux) os=linux ;;
|
|
26
|
+
Darwin) os=darwin ;;
|
|
27
|
+
*) os=unsupported ;;
|
|
28
|
+
esac
|
|
29
|
+
case $mach in
|
|
30
|
+
x86_64 | amd64) cpu=x64 ;;
|
|
31
|
+
aarch64 | arm64) cpu=arm64 ;;
|
|
32
|
+
*) cpu=unsupported ;;
|
|
33
|
+
esac
|
|
34
|
+
node_pkg=$pkg/node_modules/@songsid/agend-node-$os-$cpu
|
|
35
|
+
node=$node_pkg/bin/node
|
|
36
|
+
receipt=$pkg/.agend-runtime.json
|
|
37
|
+
repair="npm install -g @songsid/agend"
|
|
38
|
+
refuse() { echo "AgEnD cannot start: $1." >&2; echo "To repair: $2" >&2; exit "${3:-1}"; }
|
|
39
|
+
|
|
40
|
+
# 1. AGEND_NODE, the operator's explicit choice: absolute and executable, then it runs the JS launcher, whose probe
|
|
41
|
+
# refuses one that does not qualify. Never a silent fallback to another Node.
|
|
42
|
+
if [ -n "${AGEND_NODE:-}" ]; then
|
|
43
|
+
case $AGEND_NODE in
|
|
44
|
+
/*) ;;
|
|
45
|
+
*) refuse "AGEND_NODE must be an absolute path (got $AGEND_NODE)" "unset AGEND_NODE, or point it at a Node 22.14+" ;;
|
|
46
|
+
esac
|
|
47
|
+
[ -f "$AGEND_NODE" ] && [ -x "$AGEND_NODE" ] || refuse "AGEND_NODE ($AGEND_NODE) is not an executable file" "unset AGEND_NODE, or point it at a Node 22.14+"
|
|
48
|
+
exec "$AGEND_NODE" "$launcher" "$@"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 2. The bundled Node, only when its ADMISSION KEY (launcher/runtime-select.cjs, "THE ADMISSION KEY") is exactly what
|
|
52
|
+
# this shell measures now — nothing parsed, the bytes rebuilt and compared: the host's os and cpu (never its
|
|
53
|
+
# version: an OS update must not lock AgEnD out), the Node's realpath, size and mtime, and the POSIX cksum of both
|
|
54
|
+
# manifests and of the receipt. The JS selection requires the same key, so a
|
|
55
|
+
# change to any bound file keeps BOTH from running the candidate.
|
|
56
|
+
# Every measurement must itself complete: a value printed by a command that then fails is no measurement (#1460 r4).
|
|
57
|
+
admissible() {
|
|
58
|
+
[ -f "$pkg/.agend-runtime.key" ] && [ -f "$node" ] && [ ! -L "$node" ] && [ -x "$node" ] || return 1
|
|
59
|
+
case $os in linux | darwin) ;; *) return 1 ;; esac
|
|
60
|
+
real=$(cd "${node%/*}" 2>/dev/null && pwd -P) || return 1
|
|
61
|
+
real=$real/node
|
|
62
|
+
size=$(wc -c < "$node") || return 1
|
|
63
|
+
set -- $size
|
|
64
|
+
size=${1:-}
|
|
65
|
+
case $size in '' | *[!0-9]*) return 1 ;; esac
|
|
66
|
+
if mtime=$(stat -c %Y "$node" 2>/dev/null); then :; elif mtime=$(stat -f %m "$node" 2>/dev/null); then :; else return 1; fi
|
|
67
|
+
case $mtime in '' | *[!0-9]*) return 1 ;; esac
|
|
68
|
+
sum_package=$(cksum < "$pkg/package.json") || return 1
|
|
69
|
+
sum_runtime=$(cksum < "$node_pkg/package.json") || return 1
|
|
70
|
+
sum_receipt=$(cksum < "$receipt") || return 1
|
|
71
|
+
printf 'agend-runtime-key 2\nos %s\ncpu %s\nnode %s\nsize %s\nmtime %s\npackage %s\nruntime %s\nreceipt %s\n' \
|
|
72
|
+
"$os" "$cpu" "$real" "$size" "$mtime" "$sum_package" "$sum_runtime" "$sum_receipt" | cmp -s - "$pkg/.agend-runtime.key"
|
|
73
|
+
}
|
|
74
|
+
# This host, measured fresh, must still be one the bundled Node supports (launcher/runtime-platform.cjs
|
|
75
|
+
# runtimeSupport: glibc >= 2.28 on Linux, Darwin kernel >= 20) — minimums, never a pinned version. A stored or copied
|
|
76
|
+
# runtime on a host that no longer qualifies never runs; the JS launcher then picks a system Node or refuses.
|
|
77
|
+
supported() {
|
|
78
|
+
case $os in
|
|
79
|
+
linux)
|
|
80
|
+
v=$(getconf GNU_LIBC_VERSION 2>/dev/null) || return 1
|
|
81
|
+
case $v in "glibc "*) v=${v#glibc } ;; *) return 1 ;; esac
|
|
82
|
+
major=${v%%.*}
|
|
83
|
+
minor=${v#*.}
|
|
84
|
+
minor=${minor%%.*}
|
|
85
|
+
case $major in '' | *[!0-9]*) return 1 ;; esac
|
|
86
|
+
case $minor in '' | *[!0-9]*) return 1 ;; esac
|
|
87
|
+
[ "$major" -gt 2 ] || { [ "$major" -eq 2 ] && [ "$minor" -ge 28 ]; }
|
|
88
|
+
;;
|
|
89
|
+
darwin)
|
|
90
|
+
r=$(uname -r) || return 1
|
|
91
|
+
kernel=${r%%.*}
|
|
92
|
+
case $kernel in '' | *[!0-9]*) return 1 ;; esac
|
|
93
|
+
[ "$kernel" -ge 20 ]
|
|
94
|
+
;;
|
|
95
|
+
*) return 1 ;;
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
98
|
+
if supported && admissible; then
|
|
99
|
+
exec "$node" "$launcher" "$@"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# 3. Not admitted here, so that candidate never runs. A node on PATH runs the JS launcher: it explains a refusal, or
|
|
103
|
+
# is itself the choice when the runtime was skipped altogether (and it qualifies).
|
|
104
|
+
if command -v node >/dev/null 2>&1; then
|
|
105
|
+
exec node "$launcher" "$@"
|
|
106
|
+
fi
|
|
107
|
+
if [ -e "$receipt" ] || [ -e "$node" ] || [ -L "$node" ]; then
|
|
108
|
+
refuse "its bundled Node ($node) is missing, incomplete or changed since it was verified, and there is no node on PATH" "$repair"
|
|
109
|
+
fi
|
|
110
|
+
refuse "its bundled Node is not installed and there is no node on PATH" "$repair" 127
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #1450 C1: may this install of @songsid/agend proceed? Called by the postinstall, inside npm's transaction (a refusal
|
|
3
|
+
// makes npm roll the install back). The lock is the updater's (src/install-lock.ts): `<prefix>/.agend-install.lock`,
|
|
4
|
+
// next to the package it guards. The prefix comes from WHERE this package is — never from the environment.
|
|
5
|
+
// - no lock, no token → a plain external install: proceed (the documented restriction covers the rest);
|
|
6
|
+
// - a live lock, its own token → the updater's own npm child: proceed;
|
|
7
|
+
// - a live lock, other/no token → a foreign install while AgEnD owns the prefix: refuse;
|
|
8
|
+
// - a token, but no live lock → late or stale (it cannot authorise a successor): refuse;
|
|
9
|
+
// - a lock that cannot be read → refuse, naming the file (unknown ownership blocks).
|
|
10
|
+
// Same syntax constraints as runtime-platform.cjs: this may run on an old Node.
|
|
11
|
+
var fs = require("fs");
|
|
12
|
+
var path = require("path");
|
|
13
|
+
var childProcess = require("child_process");
|
|
14
|
+
|
|
15
|
+
var LOCK = ".agend-install.lock";
|
|
16
|
+
var TOKEN_ENV = "AGEND_INSTALL_TOKEN";
|
|
17
|
+
|
|
18
|
+
/** `<prefix>/lib/node_modules/@songsid/agend` → `<prefix>`; null for any other layout (a local install, a checkout). */
|
|
19
|
+
function globalPrefix(pkgDir) {
|
|
20
|
+
var scope = path.dirname(pkgDir);
|
|
21
|
+
var nodeModules = path.dirname(scope);
|
|
22
|
+
var lib = path.dirname(nodeModules);
|
|
23
|
+
if (path.basename(pkgDir) !== "agend" || path.basename(scope) !== "@songsid" || path.basename(nodeModules) !== "node_modules" || path.basename(lib) !== "lib") return null;
|
|
24
|
+
return path.dirname(lib);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** ps by absolute path when it is where the OS keeps it: a service's or a test's PATH may not reach it. */
|
|
28
|
+
var PS = ["/bin/ps", "/usr/bin/ps"].filter(function (p) { try { fs.accessSync(p, fs.constants.X_OK); return true; } catch (e) { return false; } })[0] || "ps";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A process's start time as one canonical text: `ps -o lstart=` under LC_ALL=C and TZ=UTC, so every caller — whatever
|
|
32
|
+
* its own locale or time zone — reads the same text for the same process. null: it could not be read.
|
|
33
|
+
*/
|
|
34
|
+
function processStart(pid) {
|
|
35
|
+
try {
|
|
36
|
+
var env = {};
|
|
37
|
+
Object.keys(process.env).forEach(function (k) { env[k] = process.env[k]; });
|
|
38
|
+
env.LC_ALL = "C";
|
|
39
|
+
env.TZ = "UTC";
|
|
40
|
+
var r = childProcess.spawnSync(PS, ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", timeout: 5000, env: env, stdio: ["ignore", "pipe", "ignore"] });
|
|
41
|
+
var text = String(r.stdout || "").trim();
|
|
42
|
+
return r.status === 0 && text ? text : null;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The record, exactly as the updater validates it (src/install-lock.ts parseInstallLock), or null. */
|
|
49
|
+
/** Does `pid` exist? Only a definite "no such process" is no. */
|
|
50
|
+
function exists(pid) {
|
|
51
|
+
try { process.kill(pid, 0); return true; } catch (e) { return e.code !== "ESRCH"; }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The lock holder's state: "live" (that very process), "stale" (PROVEN gone: no such pid, or the pid is now a process
|
|
56
|
+
* that started at another time), or "unknown" (it exists but its start time cannot be read) — which blocks, never
|
|
57
|
+
* counts as stale. deps (tests): exists, processStart.
|
|
58
|
+
*/
|
|
59
|
+
function holderState(lock, deps) {
|
|
60
|
+
var d = deps || {};
|
|
61
|
+
if (!(d.exists || exists)(lock.pid)) return "stale";
|
|
62
|
+
var seen = (d.processStart || processStart)(lock.pid);
|
|
63
|
+
if (seen === null || seen === undefined) return "unknown";
|
|
64
|
+
return seen === lock.processStart ? "live" : "stale";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parse(text) {
|
|
68
|
+
var r;
|
|
69
|
+
try { r = JSON.parse(text); } catch (e) { return null; }
|
|
70
|
+
return r && typeof r === "object" && Number.isSafeInteger(r.pid) && r.pid > 0 && typeof r.processStart === "string" && r.processStart
|
|
71
|
+
&& typeof r.prefix === "string" && typeof r.token === "string" && /^[0-9a-f]{32,}$/.test(r.token)
|
|
72
|
+
&& typeof r.targetSpec === "string" && typeof r.agendHome === "string" && typeof r.createdAt === "string" ? r : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** { ok: true, why } or { ok: false, reason }. deps (tests): env, processStart. */
|
|
76
|
+
function admit(pkgDir, deps) {
|
|
77
|
+
var d = deps || {};
|
|
78
|
+
var env = d.env || process.env;
|
|
79
|
+
var token = env[TOKEN_ENV] || "";
|
|
80
|
+
var real;
|
|
81
|
+
try { real = fs.realpathSync(pkgDir); } catch (e) { real = pkgDir; }
|
|
82
|
+
var prefix = globalPrefix(real);
|
|
83
|
+
if (!prefix) return token ? { ok: false, reason: "an AgEnD install token was presented, but this is not a global install it could belong to" } : { ok: true, why: "not a global install" };
|
|
84
|
+
try { prefix = fs.realpathSync(prefix); } catch (e) { /* as derived */ }
|
|
85
|
+
var lockPath = path.join(prefix, LOCK);
|
|
86
|
+
var text = null;
|
|
87
|
+
try { text = fs.readFileSync(lockPath, "utf8"); } catch (e) {
|
|
88
|
+
if (e.code !== "ENOENT") return { ok: false, reason: "the install lock " + lockPath + " cannot be read (" + e.code + ")" };
|
|
89
|
+
}
|
|
90
|
+
if (text === null) {
|
|
91
|
+
return token ? { ok: false, reason: "an AgEnD install token was presented, but no update holds " + lockPath + " (a late or stale token)" } : { ok: true, why: "no lock: an external install" };
|
|
92
|
+
}
|
|
93
|
+
var lock = parse(text);
|
|
94
|
+
if (!lock) return { ok: false, reason: "the install lock " + lockPath + " cannot be read as a lock; if no `agend update` is running, remove it and retry" };
|
|
95
|
+
var state = holderState(lock, d);
|
|
96
|
+
if (state === "unknown") return { ok: false, reason: "whether the AgEnD update that holds " + lockPath + " (pid " + lock.pid + ") is still running cannot be told; retry" };
|
|
97
|
+
if (state === "stale") {
|
|
98
|
+
return token ? { ok: false, reason: "the AgEnD update that held " + lockPath + " is gone, so its token cannot authorise this install" } : { ok: true, why: "a stale lock and no token: an external install" };
|
|
99
|
+
}
|
|
100
|
+
if (lock.prefix !== prefix) return { ok: false, reason: "the install lock " + lockPath + " names another prefix (" + lock.prefix + ")" };
|
|
101
|
+
if (token && token === lock.token) return { ok: true, why: "the updater's own install" };
|
|
102
|
+
return { ok: false, reason: "an `agend update` (pid " + lock.pid + ") is installing into " + prefix + " right now; wait for it, then retry" };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = { LOCK: LOCK, TOKEN_ENV: TOKEN_ENV, admit: admit, globalPrefix: globalPrefix, processStart: processStart, exists: exists, holderState: holderState, parse: parse };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #1450 C2: the launcher body shared by the `agend` and `agend-agent` bins. It picks the interpreter BEFORE any module
|
|
3
|
+
// of the CLI's import graph loads, never downloads, and never changes PATH. Syntax old Nodes still parse.
|
|
4
|
+
var fs = require("fs");
|
|
5
|
+
var path = require("path");
|
|
6
|
+
var childProcess = require("child_process");
|
|
7
|
+
var select = require("./runtime-select.cjs");
|
|
8
|
+
|
|
9
|
+
var FORWARDED = ["SIGTERM", "SIGHUP", "SIGUSR1", "SIGUSR2"];
|
|
10
|
+
|
|
11
|
+
function realpath(file) {
|
|
12
|
+
try { return fs.realpathSync(file); } catch (e) { return null; }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** `entryRelative`: the CLI inside the package, e.g. "dist/cli.js". */
|
|
16
|
+
module.exports = function launch(entryRelative, launcherDir) {
|
|
17
|
+
var args = process.argv.slice(2);
|
|
18
|
+
var chosen = select.selectRuntime(launcherDir);
|
|
19
|
+
|
|
20
|
+
// For the updater (C4): which Node and package would this install run on? Printed, nothing started.
|
|
21
|
+
if (args[0] === "--agend-select-json") {
|
|
22
|
+
process.stdout.write(JSON.stringify(chosen.ok
|
|
23
|
+
? { node: chosen.node, source: chosen.source, pkgDir: chosen.pkgDir, entry: path.join(chosen.pkgDir, entryRelative), warning: chosen.warning || null }
|
|
24
|
+
: { error: chosen.reason, recovery: chosen.recovery }) + "\n");
|
|
25
|
+
process.exit(chosen.ok ? 0 : 1);
|
|
26
|
+
}
|
|
27
|
+
if (!chosen.ok) {
|
|
28
|
+
process.stderr.write("\n AgEnD cannot start: " + chosen.reason + ".\n To repair: " + chosen.recovery + "\n\n");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
if (chosen.warning) process.stderr.write(" ⚠ AgEnD: " + chosen.warning + "\n");
|
|
32
|
+
|
|
33
|
+
var entry = path.join(chosen.pkgDir, entryRelative);
|
|
34
|
+
if (chosen.node === realpath(process.execPath)) {
|
|
35
|
+
require("./run-in-process.cjs")(entry);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var env = {};
|
|
40
|
+
Object.keys(process.env).forEach(function (key) { env[key] = process.env[key]; });
|
|
41
|
+
env.AGEND_NODE_SELECTED = "1";
|
|
42
|
+
// The handlers are in place BEFORE the spawn: a signal delivered between spawn() and their registration would kill
|
|
43
|
+
// this launcher by default and orphan the CLI. (JS handlers cannot run during this synchronous block, so `child` is
|
|
44
|
+
// always set by the time one does.)
|
|
45
|
+
var child = null;
|
|
46
|
+
FORWARDED.forEach(function (signal) {
|
|
47
|
+
process.on(signal, function () { try { child.kill(signal); } catch (e) { /* gone */ } });
|
|
48
|
+
});
|
|
49
|
+
// A terminal's Ctrl+C reaches the whole process group, the child included: the child decides, this waits.
|
|
50
|
+
process.on("SIGINT", function () {});
|
|
51
|
+
child = childProcess.spawn(chosen.node, [entry].concat(args), { stdio: "inherit", env: env });
|
|
52
|
+
child.on("error", function (err) {
|
|
53
|
+
process.stderr.write("\n AgEnD cannot start " + chosen.node + ": " + err.message + "\n\n");
|
|
54
|
+
process.exit(1);
|
|
55
|
+
});
|
|
56
|
+
child.on("exit", function (code, signal) {
|
|
57
|
+
if (signal) {
|
|
58
|
+
process.removeAllListeners(signal);
|
|
59
|
+
process.kill(process.pid, signal);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
process.exit(code === null ? 1 : code);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// #1450: npm's `postinstall` for @songsid/agend. It runs under whatever Node runs npm (possibly an old one) and
|
|
4
|
+
// decides, inside the install transaction, whether this install can run at all — refusing (exit 1) makes npm roll the
|
|
5
|
+
// install back (proven by the npm-rollback-proof gate, #1455):
|
|
6
|
+
// - this release's bundled Node is present (supported host): prove it — exact version, N-API ≥ 10, platform/arch,
|
|
7
|
+
// and a better-sqlite3 database opened in the main thread AND in a worker — then write the receipt the launcher
|
|
8
|
+
// checks on every start;
|
|
9
|
+
// - no bundled Node (unsupported host, none pinned yet, optional dependencies omitted): the Node running npm must
|
|
10
|
+
// qualify itself.
|
|
11
|
+
var fs = require("fs");
|
|
12
|
+
var path = require("path");
|
|
13
|
+
var crypto = require("crypto");
|
|
14
|
+
var childProcess = require("child_process");
|
|
15
|
+
var platform = require("./runtime-platform.cjs");
|
|
16
|
+
var select = require("./runtime-select.cjs");
|
|
17
|
+
var admission = require("./install-admission.cjs");
|
|
18
|
+
|
|
19
|
+
/** Runs on the candidate Node with argv[1] = the package dir: version, N-API, and a real DB open in both threads. */
|
|
20
|
+
var PROOF = [
|
|
21
|
+
"const { createRequire } = require('node:module');",
|
|
22
|
+
"const { Worker } = require('node:worker_threads');",
|
|
23
|
+
"const dir = process.argv[1];",
|
|
24
|
+
"const open = () => { const D = createRequire(require('node:path').join(dir, 'package.json'))('better-sqlite3'); const db = new D(':memory:'); const one = db.prepare('select 1 as one').get().one; db.close(); return one; };",
|
|
25
|
+
"if (open() !== 1) process.exit(3);",
|
|
26
|
+
"const w = new Worker(`const { createRequire } = require('node:module'); const D = createRequire(require('node:path').join(${JSON.stringify(dir)}, 'package.json'))('better-sqlite3'); const db = new D(':memory:'); const one = db.prepare('select 1 as one').get().one; db.close(); require('node:worker_threads').parentPort.postMessage(one);`, { eval: true });",
|
|
27
|
+
// The worker's answer AND its clean exit (the database closed inside it): any other end fails the proof.
|
|
28
|
+
"let answer = null;",
|
|
29
|
+
"w.on('message', one => { answer = one; });",
|
|
30
|
+
"w.on('error', () => process.exit(5));",
|
|
31
|
+
"w.on('exit', code => { if (code !== 0 || answer !== 1) process.exit(4); process.stdout.write(JSON.stringify({ node: process.versions.node, napi: Number(process.versions.napi), platform: process.platform, arch: process.arch })); process.exit(0); });",
|
|
32
|
+
].join("\n");
|
|
33
|
+
|
|
34
|
+
function refuse(reason, recovery) {
|
|
35
|
+
process.stderr.write("\n AgEnD cannot be installed here: " + reason + ".\n npm is rolling this install back; the previous install, if any, stays.\n " + recovery + "\n\n");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function sha256(file) {
|
|
40
|
+
return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function main(deps) {
|
|
44
|
+
var d = deps || {};
|
|
45
|
+
var pkg = select.packageDir(d.launcherDir || __dirname);
|
|
46
|
+
if (!pkg) refuse("the installed package is not @songsid/agend", "npm install -g @songsid/agend");
|
|
47
|
+
// A receipt describes THIS install's verification and nothing else: whatever is there now (one npm left behind, one
|
|
48
|
+
// copied in) goes first, so selection and installation see the same state (runtime-select.cjs).
|
|
49
|
+
[select.KEY, select.RECEIPT].forEach(function (name) {
|
|
50
|
+
var file = path.join(pkg.dir, name);
|
|
51
|
+
if (select.absent(file)) return;
|
|
52
|
+
try { fs.unlinkSync(file); } catch (e) { /* checked below */ }
|
|
53
|
+
if (!select.absent(file)) refuse("an old runtime receipt at " + file + " cannot be removed", "remove it, then: npm install -g @songsid/agend@" + pkg.manifest.version);
|
|
54
|
+
});
|
|
55
|
+
// C1: an `agend update` owns this prefix right now → only its own npm child may install (install-admission.cjs).
|
|
56
|
+
var admitted = admission.admit(pkg.dir, { env: d.env, processStart: d.processStart });
|
|
57
|
+
if (!admitted.ok) refuse(admitted.reason, "Never run two installs of @songsid/agend into one prefix at once, or one while `agend update` runs.");
|
|
58
|
+
var host = d.host || platform.hostPlatform();
|
|
59
|
+
var running = d.versions || { node: process.versions.node, napi: Number(process.versions.napi) };
|
|
60
|
+
var engines = (pkg.manifest.engines && pkg.manifest.engines.node) || ">=22.14.0";
|
|
61
|
+
var pin = select.pinnedRuntime(pkg.manifest, host);
|
|
62
|
+
var support = platform.runtimeSupport(host);
|
|
63
|
+
var log = d.log || function (line) { process.stdout.write(line + "\n"); };
|
|
64
|
+
|
|
65
|
+
if (pin && support.supported) {
|
|
66
|
+
var candidate = select.runtimeCandidate(pkg.dir, pin);
|
|
67
|
+
if (candidate.exists) {
|
|
68
|
+
if (!candidate.manifest || candidate.manifest.name !== pin.name || candidate.manifest.version !== pin.version) {
|
|
69
|
+
refuse("the bundled Node package is not " + pin.name + "@" + pin.version, "npm install -g @songsid/agend@" + pkg.manifest.version);
|
|
70
|
+
}
|
|
71
|
+
var st;
|
|
72
|
+
try { st = fs.lstatSync(candidate.node); } catch (e) { st = null; }
|
|
73
|
+
if (!st || !st.isFile()) refuse("the bundled Node binary is missing or not a regular file", "npm install -g @songsid/agend@" + pkg.manifest.version);
|
|
74
|
+
var r = childProcess.spawnSync(candidate.node, ["-e", PROOF, pkg.dir], { encoding: "utf8", timeout: 30000, stdio: ["ignore", "pipe", "pipe"] });
|
|
75
|
+
var seen = null;
|
|
76
|
+
try { seen = r.status === 0 ? JSON.parse(String(r.stdout).trim()) : null; } catch (e) { seen = null; }
|
|
77
|
+
var wantNode = pin.version.replace(/-agend\.\d+$/, "");
|
|
78
|
+
if (!seen || seen.node !== wantNode || !(seen.napi >= select.MIN_NAPI) || seen.platform !== host.platform || seen.arch !== host.arch) {
|
|
79
|
+
refuse("the bundled Node did not pass its check (" + (seen ? "Node " + seen.node + ", N-API " + seen.napi + ", " + seen.platform + "-" + seen.arch : "exit " + (r.status === null ? r.signal : r.status) + ": " + String(r.stderr || "").trim().split("\n").pop()) + ")", "npm install -g @songsid/agend@" + pkg.manifest.version);
|
|
80
|
+
}
|
|
81
|
+
var real = fs.realpathSync(candidate.node);
|
|
82
|
+
// The receipt contract (runtime-select.cjs) keeps every string plain, so a shell can check it exactly too.
|
|
83
|
+
if (!select.PLAIN.test(real)) refuse("AgEnD's install path " + JSON.stringify(real) + " contains a quote, backslash or control character", "install it under a plain path");
|
|
84
|
+
var finalStat = fs.statSync(real);
|
|
85
|
+
var receipt = {
|
|
86
|
+
receipt: 2, pinnedVersion: pin.version, nodePath: real, size: finalStat.size, mtime: Math.floor(finalStat.mtimeMs / 1000), sha256: sha256(real),
|
|
87
|
+
napi: seen.napi, platform: host.platform, arch: host.arch, libc: host.glibc ? "glibc" : null, verifiedAt: new Date().toISOString(),
|
|
88
|
+
};
|
|
89
|
+
var tmp = path.join(pkg.dir, select.RECEIPT + "." + process.pid + ".tmp");
|
|
90
|
+
fs.writeFileSync(tmp, select.receiptText(receipt));
|
|
91
|
+
fs.renameSync(tmp, path.join(pkg.dir, select.RECEIPT));
|
|
92
|
+
// The admission key goes LAST: it binds the receipt just written (runtime-select.cjs).
|
|
93
|
+
var key = select.runtimeKey(pkg.dir, candidate, host);
|
|
94
|
+
if (key === null) refuse("the bundled Node's admission key cannot be computed", "npm install -g @songsid/agend@" + pkg.manifest.version);
|
|
95
|
+
var keyTmp = path.join(pkg.dir, select.KEY + "." + process.pid + ".tmp");
|
|
96
|
+
fs.writeFileSync(keyTmp, key);
|
|
97
|
+
fs.renameSync(keyTmp, path.join(pkg.dir, select.KEY));
|
|
98
|
+
log(" ✓ AgEnD will run on its bundled Node " + seen.node + " (verified: N-API " + seen.napi + ", a database opened in the main thread and a worker)");
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
if (select.qualifies(running, engines)) {
|
|
102
|
+
log(" ⚠ AgEnD's bundled Node was not installed (--omit=optional?); it will run on this Node " + running.node);
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
refuse("its bundled Node (" + pin.name + ") was not installed, and this Node " + running.node + " is older than AgEnD needs (" + engines + ")",
|
|
106
|
+
"If optional dependencies were omitted or a registry mirror blocks " + pin.name + ", allow it and retry; or install Node " + engines + " first.");
|
|
107
|
+
}
|
|
108
|
+
if (select.qualifies(running, engines)) return 0;
|
|
109
|
+
refuse((pin ? support.reason : "this release bundles no Node") + ", and this Node " + running.node + " is older than AgEnD needs (" + engines + ")",
|
|
110
|
+
"Install Node " + engines + ", then: npm install -g @songsid/agend@" + pkg.manifest.version);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = { main: main, PROOF: PROOF };
|
|
114
|
+
if (require.main === module) process.exit(main() || 0);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Loaded only when the running Node is the selected one — a Node new enough to parse `import()`; older Nodes never
|
|
3
|
+
// load this file, so their parse of the launcher stays clean. process.argv[1] becomes the inner entry, as when the
|
|
4
|
+
// CLI is run directly (service install, completion and restart read it).
|
|
5
|
+
var url = require("url");
|
|
6
|
+
|
|
7
|
+
module.exports = function runInProcess(entry) {
|
|
8
|
+
process.argv[1] = entry;
|
|
9
|
+
return import(url.pathToFileURL(entry).href).catch(function (err) {
|
|
10
|
+
process.stderr.write(String((err && err.stack) || err) + "\n");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #1450 C3: the one answer to "can this host run AgEnD's bundled Node?", shared by preinstall, postinstall and the
|
|
3
|
+
// launcher. CommonJS that very old Nodes still parse (var, function, no optional chaining): it may run on the host's
|
|
4
|
+
// own old Node, before anything decides which Node AgEnD itself will use.
|
|
5
|
+
var os = require("os");
|
|
6
|
+
|
|
7
|
+
/** The runtime packages AgEnD ships: official glibc Linux builds and macOS builds (no musl, #1450). */
|
|
8
|
+
var SHIPPED = ["linux-x64", "linux-arm64", "darwin-x64", "darwin-arm64"];
|
|
9
|
+
/** The official Node 22 builds need glibc ≥ 2.28 on Linux and macOS 11 (Darwin kernel 20) or newer. */
|
|
10
|
+
var MIN_GLIBC = [2, 28];
|
|
11
|
+
var MIN_DARWIN_KERNEL = 20;
|
|
12
|
+
|
|
13
|
+
function numbers(text) {
|
|
14
|
+
return String(text).split(".").map(function (part) { return parseInt(part, 10); });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function atLeast(have, want) {
|
|
18
|
+
for (var i = 0; i < want.length; i++) {
|
|
19
|
+
var h = have[i] || 0;
|
|
20
|
+
if (h > want[i]) return true;
|
|
21
|
+
if (h < want[i]) return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** glibc's version when this Node runs on glibc; null on musl or when it cannot be told. */
|
|
27
|
+
function glibcVersion(report) {
|
|
28
|
+
try {
|
|
29
|
+
var r = report || (process.report && process.report.getReport && process.report.getReport());
|
|
30
|
+
var v = r && r.header && r.header.glibcVersionRuntime;
|
|
31
|
+
return typeof v === "string" && v ? v : null;
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The host as the runtime packages see it. `probe` is injectable for tests:
|
|
39
|
+
* { platform, arch, glibc (string|null), darwinRelease (string|null) }.
|
|
40
|
+
*/
|
|
41
|
+
function hostPlatform(probe) {
|
|
42
|
+
var p = probe || {};
|
|
43
|
+
var platform = p.platform || process.platform;
|
|
44
|
+
var arch = p.arch || process.arch;
|
|
45
|
+
return {
|
|
46
|
+
platform: platform,
|
|
47
|
+
arch: arch,
|
|
48
|
+
id: platform + "-" + arch,
|
|
49
|
+
glibc: "glibc" in p ? p.glibc : (platform === "linux" ? glibcVersion() : null),
|
|
50
|
+
darwinRelease: "darwinRelease" in p ? p.darwinRelease : (platform === "darwin" ? os.release() : null),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Whether a bundled runtime can run here, and why not. */
|
|
55
|
+
function runtimeSupport(host) {
|
|
56
|
+
if (SHIPPED.indexOf(host.id) < 0) return { supported: false, reason: "no AgEnD runtime is published for " + host.id };
|
|
57
|
+
if (host.platform === "linux") {
|
|
58
|
+
if (!host.glibc) return { supported: false, reason: "this Linux is not glibc-based (musl?), and the bundled Node needs glibc" };
|
|
59
|
+
if (!atLeast(numbers(host.glibc), MIN_GLIBC)) return { supported: false, reason: "glibc " + host.glibc + " is older than the 2.28 the bundled Node needs" };
|
|
60
|
+
}
|
|
61
|
+
if (host.platform === "darwin") {
|
|
62
|
+
var kernel = numbers(host.darwinRelease || "0")[0];
|
|
63
|
+
if (!(kernel >= MIN_DARWIN_KERNEL)) return { supported: false, reason: "macOS older than 11 (Darwin " + host.darwinRelease + ") cannot run the bundled Node" };
|
|
64
|
+
}
|
|
65
|
+
return { supported: true, reason: null };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Does a Node version satisfy an `engines.node` range? Only the forms AgEnD uses: alternatives joined by `||`, each
|
|
70
|
+
* `^x.y.z` (same major, at least x.y.z) or `>=x[.y[.z]]`. The version must be a full stable `x.y.z`: as in npm's
|
|
71
|
+
* semver, a prerelease (`24.0.0-nightly…`, `22.14.0-rc.1`) satisfies no stable alternative.
|
|
72
|
+
*/
|
|
73
|
+
function satisfiesEngines(version, range) {
|
|
74
|
+
var full = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(String(version));
|
|
75
|
+
if (!full) return false;
|
|
76
|
+
var v = [+full[1], +full[2], +full[3]];
|
|
77
|
+
return String(range).split("||").some(function (alt) {
|
|
78
|
+
var a = alt.trim();
|
|
79
|
+
var m;
|
|
80
|
+
if ((m = /^\^(\d+)\.(\d+)\.(\d+)$/.exec(a))) return v[0] === +m[1] && atLeast(v, [+m[1], +m[2], +m[3]]);
|
|
81
|
+
if ((m = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/.exec(a))) return atLeast(v, [+m[1], +(m[2] || 0), +(m[3] || 0)]);
|
|
82
|
+
return false;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { SHIPPED: SHIPPED, hostPlatform: hostPlatform, runtimeSupport: runtimeSupport, satisfiesEngines: satisfiesEngines, glibcVersion: glibcVersion };
|