dexterm-cli 0.0.0-stage → 0.1.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/LICENSE +661 -0
- package/dist/packages/agents/src/audit.js +122 -0
- package/dist/packages/agents/src/bus.js +394 -0
- package/dist/packages/agents/src/executor.js +202 -0
- package/dist/packages/agents/src/gate.js +179 -0
- package/dist/packages/agents/src/identity.js +218 -0
- package/dist/packages/agents/src/index.js +15 -0
- package/dist/packages/agents/src/proposals.js +153 -0
- package/dist/packages/agents/src/schema.js +87 -0
- package/dist/packages/agents/src/sdk.js +140 -0
- package/dist/packages/agents/src/tiers.js +119 -0
- package/dist/packages/agents/src/tools.js +72 -0
- package/dist/packages/agents/src/ws.js +158 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
- package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
- package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
- package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +75 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +74 -0
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
- package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
- package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
- package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
- package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
- package/dist/packages/cli/bin/dexterm.mjs +11 -0
- package/dist/packages/cli/src/agent-cmd.js +336 -0
- package/dist/packages/cli/src/agent-roster.js +26 -0
- package/dist/packages/cli/src/alerts/adapters/desktop.js +40 -0
- package/dist/packages/cli/src/alerts/adapters/discord.js +36 -0
- package/dist/packages/cli/src/alerts/adapters/telegram.js +43 -0
- package/dist/packages/cli/src/alerts/adapters/toast.js +16 -0
- package/dist/packages/cli/src/alerts/creds.js +83 -0
- package/dist/packages/cli/src/alerts/format.js +41 -0
- package/dist/packages/cli/src/alerts/index.js +10 -0
- package/dist/packages/cli/src/alerts/notifier.js +53 -0
- package/dist/packages/cli/src/alerts/route.js +19 -0
- package/dist/packages/cli/src/alerts/types.js +1 -0
- package/dist/packages/cli/src/alerts-cmd.js +212 -0
- package/dist/packages/cli/src/analyst/backend.js +526 -0
- package/dist/packages/cli/src/analyst/results.js +38 -0
- package/dist/packages/cli/src/analyst/session.js +319 -0
- package/dist/packages/cli/src/audit.js +51 -0
- package/dist/packages/cli/src/backtest-cmd.js +208 -0
- package/dist/packages/cli/src/boot-state.js +62 -0
- package/dist/packages/cli/src/coin-target.js +2 -0
- package/dist/packages/cli/src/custom.js +261 -0
- package/dist/packages/cli/src/display.js +191 -0
- package/dist/packages/cli/src/doctor.js +375 -0
- package/dist/packages/cli/src/export.js +235 -0
- package/dist/packages/cli/src/feed.js +634 -0
- package/dist/packages/cli/src/fixtures.js +275 -0
- package/dist/packages/cli/src/follow-cmd.js +221 -0
- package/dist/packages/cli/src/images-config.js +59 -0
- package/dist/packages/cli/src/index.js +31 -0
- package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
- package/dist/packages/cli/src/larp-record.js +21 -0
- package/dist/packages/cli/src/live/copy.js +80 -0
- package/dist/packages/cli/src/live/flow.js +233 -0
- package/dist/packages/cli/src/live/index.js +511 -0
- package/dist/packages/cli/src/live/intel.js +227 -0
- package/dist/packages/cli/src/live/newpairs.js +334 -0
- package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
- package/dist/packages/cli/src/live/screens/token-live.js +459 -0
- package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
- package/dist/packages/cli/src/live/token.js +585 -0
- package/dist/packages/cli/src/live/trade.js +929 -0
- package/dist/packages/cli/src/live/watchlist.js +178 -0
- package/dist/packages/cli/src/main.js +5785 -0
- package/dist/packages/cli/src/mcp-cmd.js +326 -0
- package/dist/packages/cli/src/narrative-cmd.js +76 -0
- package/dist/packages/cli/src/pack-cmd.js +323 -0
- package/dist/packages/cli/src/plan-cmd.js +278 -0
- package/dist/packages/cli/src/popup.js +810 -0
- package/dist/packages/cli/src/rules-cmd.js +131 -0
- package/dist/packages/cli/src/rules.js +358 -0
- package/dist/packages/cli/src/scan/command.js +57 -0
- package/dist/packages/cli/src/scan/engine.js +419 -0
- package/dist/packages/cli/src/scan/observe.js +64 -0
- package/dist/packages/cli/src/scan/ui.js +98 -0
- package/dist/packages/cli/src/screener/filter.js +241 -0
- package/dist/packages/cli/src/screener/presets.js +80 -0
- package/dist/packages/cli/src/screener/rows.js +146 -0
- package/dist/packages/cli/src/screener/rug.js +50 -0
- package/dist/packages/cli/src/screener/sort.js +52 -0
- package/dist/packages/cli/src/screener/view.js +183 -0
- package/dist/packages/cli/src/screens/analyst-session.js +343 -0
- package/dist/packages/cli/src/screens/analyst.js +326 -0
- package/dist/packages/cli/src/screens/boot.js +74 -0
- package/dist/packages/cli/src/screens/bubbles.js +255 -0
- package/dist/packages/cli/src/screens/coin.js +33 -0
- package/dist/packages/cli/src/screens/follows.js +201 -0
- package/dist/packages/cli/src/screens/kol.js +90 -0
- package/dist/packages/cli/src/screens/narrative.js +90 -0
- package/dist/packages/cli/src/screens/newpairs.js +150 -0
- package/dist/packages/cli/src/screens/overlay.js +257 -0
- package/dist/packages/cli/src/screens/plans.js +126 -0
- package/dist/packages/cli/src/screens/positions.js +176 -0
- package/dist/packages/cli/src/screens/rules.js +170 -0
- package/dist/packages/cli/src/screens/screener.js +459 -0
- package/dist/packages/cli/src/screens/social-panel.js +107 -0
- package/dist/packages/cli/src/screens/ticket.js +342 -0
- package/dist/packages/cli/src/screens/token.js +312 -0
- package/dist/packages/cli/src/screens/trending.js +181 -0
- package/dist/packages/cli/src/screens/watchlist.js +104 -0
- package/dist/packages/cli/src/skill-eval-build.js +109 -0
- package/dist/packages/cli/src/social/demo.js +96 -0
- package/dist/packages/cli/src/social/view.js +250 -0
- package/dist/packages/cli/src/state.js +463 -0
- package/dist/packages/cli/src/theme-cmd.js +126 -0
- package/dist/packages/cli/src/trade.js +319 -0
- package/dist/packages/cli/src/update.js +294 -0
- package/dist/packages/cli/src/version.js +3 -0
- package/dist/packages/cli/src/voice-cmd.js +119 -0
- package/dist/packages/cli/src/voice-hud.js +42 -0
- package/dist/packages/cli/src/wallet-cmd.js +440 -0
- package/dist/packages/cli/src/wallet-command.js +31 -0
- package/dist/packages/cli/src/watch-cmd.js +190 -0
- package/dist/packages/cli/src/widgets.js +208 -0
- package/dist/packages/cli/src/workspace-cmd.js +68 -0
- package/dist/packages/cli/src/x-cmd.js +154 -0
- package/dist/packages/custom/src/config.js +213 -0
- package/dist/packages/custom/src/index.js +10 -0
- package/dist/packages/custom/src/keymap.js +107 -0
- package/dist/packages/custom/src/layout.js +235 -0
- package/dist/packages/custom/src/packs.js +633 -0
- package/dist/packages/custom/src/rules.js +704 -0
- package/dist/packages/custom/src/schema.js +538 -0
- package/dist/packages/custom/src/themes.js +176 -0
- package/dist/packages/custom/src/widget-html.js +222 -0
- package/dist/packages/custom/src/workspace.js +141 -0
- package/dist/packages/data/src/analyst-actions.js +133 -0
- package/dist/packages/data/src/budget.js +88 -0
- package/dist/packages/data/src/cache.js +142 -0
- package/dist/packages/data/src/candles.js +168 -0
- package/dist/packages/data/src/clock.js +64 -0
- package/dist/packages/data/src/coin-target.js +82 -0
- package/dist/packages/data/src/credits.js +137 -0
- package/dist/packages/data/src/detail.js +97 -0
- package/dist/packages/data/src/fast-lane.js +321 -0
- package/dist/packages/data/src/fixtures.js +135 -0
- package/dist/packages/data/src/health.js +245 -0
- package/dist/packages/data/src/http.js +277 -0
- package/dist/packages/data/src/images/cache.js +141 -0
- package/dist/packages/data/src/images/decode-worker.js +28 -0
- package/dist/packages/data/src/images/decode.js +81 -0
- package/dist/packages/data/src/images/fetch.js +162 -0
- package/dist/packages/data/src/images/gif.js +214 -0
- package/dist/packages/data/src/images/index.js +10 -0
- package/dist/packages/data/src/images/jpeg.js +544 -0
- package/dist/packages/data/src/images/png.js +310 -0
- package/dist/packages/data/src/images/service.js +334 -0
- package/dist/packages/data/src/images/types.js +27 -0
- package/dist/packages/data/src/index.js +211 -0
- package/dist/packages/data/src/intel/helius-history.js +70 -0
- package/dist/packages/data/src/intel/holder-class.js +125 -0
- package/dist/packages/data/src/intel/rules.js +421 -0
- package/dist/packages/data/src/intel/scan.js +170 -0
- package/dist/packages/data/src/larp/evidence.js +288 -0
- package/dist/packages/data/src/larp/link-baseline.js +196 -0
- package/dist/packages/data/src/larp/links.js +137 -0
- package/dist/packages/data/src/larp/robots.js +90 -0
- package/dist/packages/data/src/larp/safe-fetch.js +389 -0
- package/dist/packages/data/src/limiter.js +341 -0
- package/dist/packages/data/src/model.js +12 -0
- package/dist/packages/data/src/paper.js +331 -0
- package/dist/packages/data/src/rpc-config.js +126 -0
- package/dist/packages/data/src/rpc-pool.js +166 -0
- package/dist/packages/data/src/social/chain.js +83 -0
- package/dist/packages/data/src/social/dedupe.js +205 -0
- package/dist/packages/data/src/social/feed.js +462 -0
- package/dist/packages/data/src/social/firewall.js +110 -0
- package/dist/packages/data/src/social/index.js +11 -0
- package/dist/packages/data/src/social/kol.js +269 -0
- package/dist/packages/data/src/social/model.js +11 -0
- package/dist/packages/data/src/social/rank.js +184 -0
- package/dist/packages/data/src/social/rss.js +113 -0
- package/dist/packages/data/src/social/summary.js +104 -0
- package/dist/packages/data/src/social/venues.js +186 -0
- package/dist/packages/data/src/sources/dexscreener.js +184 -0
- package/dist/packages/data/src/sources/geckoterminal.js +133 -0
- package/dist/packages/data/src/sources/goplus.js +161 -0
- package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
- package/dist/packages/data/src/sources/jupiter.js +198 -0
- package/dist/packages/data/src/sources/pda.js +113 -0
- package/dist/packages/data/src/sources/pump-curve.js +58 -0
- package/dist/packages/data/src/sources/pumpportal.js +252 -0
- package/dist/packages/data/src/sources/rugcheck.js +71 -0
- package/dist/packages/data/src/sources/solana-rpc.js +299 -0
- package/dist/packages/data/src/sources/solana-ws.js +287 -0
- package/dist/packages/data/src/store.js +289 -0
- package/dist/packages/data/src/trade/bridge.js +464 -0
- package/dist/packages/data/src/trade/caps.js +323 -0
- package/dist/packages/data/src/trade/copy/auto.js +385 -0
- package/dist/packages/data/src/trade/copy/follows.js +103 -0
- package/dist/packages/data/src/trade/copy/index.js +9 -0
- package/dist/packages/data/src/trade/copy/mirror.js +91 -0
- package/dist/packages/data/src/trade/copy/stats.js +120 -0
- package/dist/packages/data/src/trade/copy/tracker.js +158 -0
- package/dist/packages/data/src/trade/copy/trades.js +87 -0
- package/dist/packages/data/src/trade/copy/types.js +6 -0
- package/dist/packages/data/src/trade/executor.js +933 -0
- package/dist/packages/data/src/trade/kill-switch.js +47 -0
- package/dist/packages/data/src/trade/link-store.js +52 -0
- package/dist/packages/data/src/trade/plans/confirm.js +157 -0
- package/dist/packages/data/src/trade/plans/engine.js +784 -0
- package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
- package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
- package/dist/packages/data/src/trade/plans/executor.js +11 -0
- package/dist/packages/data/src/trade/plans/index.js +12 -0
- package/dist/packages/data/src/trade/plans/journal.js +77 -0
- package/dist/packages/data/src/trade/plans/mandate.js +129 -0
- package/dist/packages/data/src/trade/plans/plan.js +179 -0
- package/dist/packages/data/src/trade/plans/service.js +168 -0
- package/dist/packages/data/src/trade/plans/types.js +6 -0
- package/dist/packages/data/src/trade/rpc-exec.js +229 -0
- package/dist/packages/data/src/trade/signer/client.js +197 -0
- package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
- package/dist/packages/data/src/trade/signer/host.js +97 -0
- package/dist/packages/data/src/trade/signer/main.js +121 -0
- package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
- package/dist/packages/data/src/trade/signer/protocol.js +92 -0
- package/dist/packages/data/src/trade/signer/service.js +400 -0
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
- package/dist/packages/data/src/trade/sol-usd.js +86 -0
- package/dist/packages/data/src/trade/swap-build.js +108 -0
- package/dist/packages/data/src/trade/token-delta.js +201 -0
- package/dist/packages/data/src/trade/tx-decode.js +632 -0
- package/dist/packages/data/src/util.js +24 -0
- package/dist/packages/data/src/watchlist.js +176 -0
- package/dist/packages/data/src/x/api.js +256 -0
- package/dist/packages/data/src/x/budget.js +66 -0
- package/dist/packages/data/src/x/config.js +49 -0
- package/dist/packages/data/src/x/index.js +10 -0
- package/dist/packages/data/src/x/mock-server.js +217 -0
- package/dist/packages/data/src/x/oauth.js +242 -0
- package/dist/packages/data/src/x/pkce.js +21 -0
- package/dist/packages/data/src/x/source.js +285 -0
- package/dist/packages/data/src/x/token-store.js +134 -0
- package/dist/packages/data/src/x/transport.js +71 -0
- package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
- package/dist/packages/mcp/assets/cases.json +1194 -0
- package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
- package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
- package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
- package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
- package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
- package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
- package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
- package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
- package/dist/packages/mcp/src/dossier.js +78 -0
- package/dist/packages/mcp/src/firewall.js +21 -0
- package/dist/packages/mcp/src/index.js +22 -0
- package/dist/packages/mcp/src/server.js +936 -0
- package/dist/packages/mcp/src/user-content.js +434 -0
- package/dist/packages/packs/src/backtest.js +183 -0
- package/dist/packages/packs/src/digest.js +95 -0
- package/dist/packages/packs/src/firewall.js +57 -0
- package/dist/packages/packs/src/index.js +20 -0
- package/dist/packages/packs/src/larp-eval.js +100 -0
- package/dist/packages/packs/src/narrative.js +309 -0
- package/dist/packages/packs/src/reason.js +135 -0
- package/dist/packages/packs/src/rug-model.js +137 -0
- package/dist/packages/packs/src/schema.js +54 -0
- package/dist/packages/packs/src/skill-backtest.js +300 -0
- package/dist/packages/packs/src/token-dossier.js +174 -0
- package/dist/packages/packs/src/verdicts.js +17 -0
- package/dist/packages/packs/src/wallet-dossier.js +105 -0
- package/dist/packages/popup/src/config.js +71 -0
- package/dist/packages/popup/src/dock.js +271 -0
- package/dist/packages/popup/src/host.js +210 -0
- package/dist/packages/popup/src/index.js +12 -0
- package/dist/packages/popup/src/loopback.js +53 -0
- package/dist/packages/popup/src/open.js +183 -0
- package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
- package/dist/packages/popup/src/pages/bubbles.css +23 -0
- package/dist/packages/popup/src/pages/bubbles.html +36 -0
- package/dist/packages/popup/src/pages/bubbles.js +214 -0
- package/dist/packages/popup/src/pages/chart-lib.js +262 -0
- package/dist/packages/popup/src/pages/chart.css +50 -0
- package/dist/packages/popup/src/pages/chart.html +56 -0
- package/dist/packages/popup/src/pages/chart.js +554 -0
- package/dist/packages/popup/src/pages/common.css +34 -0
- package/dist/packages/popup/src/pages/common.js +187 -0
- package/dist/packages/popup/src/pages/feeds.html +20 -0
- package/dist/packages/popup/src/pages/feeds.js +77 -0
- package/dist/packages/popup/src/pages/narrative.html +20 -0
- package/dist/packages/popup/src/pages/narrative.js +61 -0
- package/dist/packages/popup/src/pages/plans.css +30 -0
- package/dist/packages/popup/src/pages/plans.html +26 -0
- package/dist/packages/popup/src/pages/plans.js +82 -0
- package/dist/packages/popup/src/pages/roster.css +41 -0
- package/dist/packages/popup/src/pages/roster.html +25 -0
- package/dist/packages/popup/src/pages/roster.js +75 -0
- package/dist/packages/popup/src/pages/token.html +20 -0
- package/dist/packages/popup/src/pages/token.js +112 -0
- package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
- package/dist/packages/popup/src/pages/widget.css +5 -0
- package/dist/packages/popup/src/pages/widget.html +21 -0
- package/dist/packages/popup/src/pages/widget.js +85 -0
- package/dist/packages/popup/src/protocol.js +31 -0
- package/dist/packages/popup/src/selftest.js +156 -0
- package/dist/packages/popup/src/server.js +582 -0
- package/dist/packages/popup/src/sinkproxy.js +167 -0
- package/dist/packages/popup/src/widget-html.js +231 -0
- package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
- package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
- package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
- package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
- package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
- package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
- package/dist/packages/recorder/src/cli.js +113 -0
- package/dist/packages/recorder/src/export.js +71 -0
- package/dist/packages/recorder/src/index.js +20 -0
- package/dist/packages/recorder/src/labels.js +151 -0
- package/dist/packages/recorder/src/open.js +29 -0
- package/dist/packages/recorder/src/recorder.js +553 -0
- package/dist/packages/recorder/src/schedule.js +43 -0
- package/dist/packages/recorder/src/schema.js +135 -0
- package/dist/packages/recorder/src/snapshot.js +191 -0
- package/dist/packages/recorder/src/sweep.js +52 -0
- package/dist/packages/recorder/src/verdicts.js +52 -0
- package/dist/packages/tui/src/bubbles.js +415 -0
- package/dist/packages/tui/src/canvas.js +370 -0
- package/dist/packages/tui/src/chrome.js +85 -0
- package/dist/packages/tui/src/color.js +133 -0
- package/dist/packages/tui/src/flush.js +62 -0
- package/dist/packages/tui/src/fmt.js +188 -0
- package/dist/packages/tui/src/fx-trade.js +278 -0
- package/dist/packages/tui/src/fx.js +527 -0
- package/dist/packages/tui/src/images/blocks.js +141 -0
- package/dist/packages/tui/src/images/coin.js +22 -0
- package/dist/packages/tui/src/images/detect.js +140 -0
- package/dist/packages/tui/src/images/index.js +7 -0
- package/dist/packages/tui/src/images/layer.js +146 -0
- package/dist/packages/tui/src/images/pixels.js +157 -0
- package/dist/packages/tui/src/images/protocols.js +87 -0
- package/dist/packages/tui/src/index.js +18 -0
- package/dist/packages/tui/src/keymap.js +357 -0
- package/dist/packages/tui/src/layout.js +11 -0
- package/dist/packages/tui/src/raster-font.js +195 -0
- package/dist/packages/tui/src/raster.js +213 -0
- package/dist/packages/tui/src/sound.js +177 -0
- package/dist/packages/tui/src/theme.js +182 -0
- package/dist/packages/tui/src/widgets.js +598 -0
- package/dist/packages/voice/src/controller.js +407 -0
- package/dist/packages/voice/src/doctor.js +131 -0
- package/dist/packages/voice/src/download.js +184 -0
- package/dist/packages/voice/src/engines.js +84 -0
- package/dist/packages/voice/src/index.js +20 -0
- package/dist/packages/voice/src/router.js +502 -0
- package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
- package/dist/packages/voice/src/stt/apple.js +179 -0
- package/dist/packages/voice/src/stt/fake.js +34 -0
- package/dist/packages/voice/src/stt/whisper.js +112 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/espeak.js +131 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/piper.js +206 -0
- package/dist/packages/voice/src/tts/say.js +132 -0
- package/dist/packages/voice/src/tts/voices.js +206 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +38 -4
- package/README.md +0 -3
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
// `dexterm pack <list|verify|add|remove|export|lock|sign|trust|signers|keymap|widget> …` (task-1358 Tier 4).
|
|
2
|
+
// A pack is a directory of plain files (workspaces, themes, rules, a keymap, popup widgets) with a sha256 lock
|
|
3
|
+
// and an optional `ssh-keygen -Y` signature. No network, no model, never a key file. Everything a pack can do to
|
|
4
|
+
// this machine is decided HERE, by the user, through the config journal (actor user):
|
|
5
|
+
// list installed packs (trust, hash, what they contribute, widget consent)
|
|
6
|
+
// verify <dir> the install card: integrity, signature, trust tier, contents, refusals (exit 1 on a problem)
|
|
7
|
+
// add <dir> [--accept-unsigned] install under ~/.dexterm/packs/<name>@<version>; unsigned needs the flag (the risk card)
|
|
8
|
+
// remove <name> delete the pack directory + record + its widget registrations
|
|
9
|
+
// export <dir> --name <n> [--publisher <p>] [--description <d>] [--workspace <w>]… [--theme <t>]… [--rules] [--keymap]
|
|
10
|
+
// write a pack from YOUR files (+ pack.lock); secrets are refused
|
|
11
|
+
// lock <dir> (re)write pack.lock
|
|
12
|
+
// sign <dir> --key <ssh private key> sign pack.lock with ssh-keygen -Y (namespace dexterm-pack)
|
|
13
|
+
// trust <identity> "<ssh pubkey>" pin a publisher key in ~/.dexterm/allowed_signers
|
|
14
|
+
// signers the pinned publisher keys
|
|
15
|
+
// keymap <name> [--merge] show a pack's keymap; --merge layers it over config.keymap (reserved keys refused; all or nothing)
|
|
16
|
+
// widget <pack> <name> --enable|--disable
|
|
17
|
+
// consent for a pack widget page (installed DISABLED; the popup host serves it only when enabled)
|
|
18
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
19
|
+
import { resolve, join } from 'node:path';
|
|
20
|
+
import { DEFAULT_KEYMAP } from '../../tui/src/index.js';
|
|
21
|
+
import { dextermHome, loadConfig, applyAndJournal, listWorkspaces, listUserThemes, packsDir, inspectPack, installPack, removePack, exportPack, writeLock, signPack, pinSigner, listSigners, loadPacks, setWidgetEnabled, packWidgetKey, checkIntegrity, applyKeymapConfig, collisions, PackError, } from '../../custom/src/index.js';
|
|
22
|
+
export const PACK_USAGE = `usage: dexterm pack list | verify <dir> | add <dir> [--accept-unsigned] | remove <name>
|
|
23
|
+
dexterm pack export <dir> --name <n> [--publisher <p>] [--description <d>] [--workspace <w>]… [--theme <t>]… [--rules] [--keymap]
|
|
24
|
+
dexterm pack lock <dir> | sign <dir> --key <ssh-private-key> | trust <identity> "<ssh pubkey>" | signers
|
|
25
|
+
dexterm pack keymap <name> [--merge] | widget <pack> <widget> --enable|--disable
|
|
26
|
+
a pack is plain files + pack.lock (sha256) + an optional ssh signature; it never runs code: widgets land DISABLED,
|
|
27
|
+
rules from an unsigned pack load disabled, a pack keymap is merged only by "pack keymap <name> --merge"`;
|
|
28
|
+
/** Parse `--flag value` / `--flag` (boolean) / repeated flags; positionals in order. */
|
|
29
|
+
function parse(argv, bools, multi = []) {
|
|
30
|
+
const pos = [];
|
|
31
|
+
const flags = {};
|
|
32
|
+
for (let i = 0; i < argv.length; i++) {
|
|
33
|
+
const a = argv[i];
|
|
34
|
+
if (!a.startsWith('--')) {
|
|
35
|
+
pos.push(a);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const name = a.slice(2);
|
|
39
|
+
if (bools.includes(name)) {
|
|
40
|
+
flags[name] = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const v = argv[++i];
|
|
44
|
+
if (v === undefined)
|
|
45
|
+
throw new Error(`--${name} needs a value`);
|
|
46
|
+
if (multi.includes(name))
|
|
47
|
+
(flags[name] = (flags[name] ?? [])).push(v);
|
|
48
|
+
else
|
|
49
|
+
flags[name] = v;
|
|
50
|
+
}
|
|
51
|
+
return { pos, flags };
|
|
52
|
+
}
|
|
53
|
+
const trustWord = (t) => (t === 'reviewed' ? 'REVIEWED (hash on the shipped list)' : t === 'signed' ? 'SIGNED by a pinned publisher key' : 'UNSIGNED / not reviewed');
|
|
54
|
+
/** The install card: what the pack is, what it contributes, what was refused, what the user is accepting. */
|
|
55
|
+
export function renderPackCard(insp) {
|
|
56
|
+
const m = insp.manifest;
|
|
57
|
+
const lines = [];
|
|
58
|
+
lines.push(`${m.name}@${m.version} · publisher ${m.publisher}${m.description ? ` · ${m.description}` : ''}`);
|
|
59
|
+
lines.push(` integrity ${insp.integrity.ok ? `ok ${insp.integrity.hash}` : `✗ ${insp.integrity.reason}`}`);
|
|
60
|
+
lines.push(` signature ${insp.signature.ok ? `ok (identity ${insp.signature.identity})` : insp.signature.reason}`);
|
|
61
|
+
lines.push(` trust ${trustWord(insp.trust)}`);
|
|
62
|
+
for (const w of insp.workspaces)
|
|
63
|
+
lines.push(` workspace ${w.workspace ? `${m.name}/${w.workspace.name}${w.workspace.popups?.length ? ` (${w.workspace.popups.length} popups)` : ''}` : `✗ ${w.path}: ${w.error}`}`);
|
|
64
|
+
for (const t of insp.themes)
|
|
65
|
+
lines.push(` theme ${t.doc ? `${m.name}-${t.name} (extends ${t.doc.extends})` : `✗ ${t.path}: ${t.error}`}`);
|
|
66
|
+
for (const f of insp.rules) {
|
|
67
|
+
for (const r of f.rules)
|
|
68
|
+
lines.push(` rule ${r.id}: when ${r.when} → ${r.then.map((a) => a.action).join(' ')} (loads ${insp.trust === 'unsigned' ? 'DISABLED' : 'enabled'})`);
|
|
69
|
+
for (const e of f.errors)
|
|
70
|
+
lines.push(` rule ✗ ${f.path}:${e.line} ${e.msg}`);
|
|
71
|
+
}
|
|
72
|
+
if (insp.keymap)
|
|
73
|
+
lines.push(` keymap ${insp.keymap.keymap ? `${insp.keymap.path} (NOT merged; dexterm pack keymap ${m.name} to review)` : `✗ ${insp.keymap.error}`}`);
|
|
74
|
+
for (const w of insp.widgets)
|
|
75
|
+
lines.push(` widget ${w.manifest ? `${m.name}/${w.name}: ${w.manifest.widgets.map((x) => `${x.id} "${x.title}" topics ${x.topics.join(',')}`).join('; ')} (installed DISABLED, no capabilities)` : `✗ ${w.name}: ${w.error}`}`);
|
|
76
|
+
for (const w of insp.warnings)
|
|
77
|
+
lines.push(` ⚠ ${w}`);
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
}
|
|
80
|
+
export async function packCommand(argv, io) {
|
|
81
|
+
const home = dextermHome(io.env);
|
|
82
|
+
const cmd = argv[0];
|
|
83
|
+
const bad = (s) => { io.stderr.write(`dexterm pack: ${s}\n${PACK_USAGE}\n`); return 2; };
|
|
84
|
+
const fail = (e) => { io.stderr.write(`dexterm pack: ${e instanceof PackError ? `${e.code}: ` : ''}${e.message}\n`); return 1; };
|
|
85
|
+
const persist = (ops, why) => applyAndJournal(home, ops, { actor: 'user', why }).config;
|
|
86
|
+
let p;
|
|
87
|
+
try {
|
|
88
|
+
p = parse(argv.slice(1), ['accept-unsigned', 'merge', 'enable', 'disable', 'rules', 'keymap'], ['workspace', 'theme']);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
return bad(e.message);
|
|
92
|
+
}
|
|
93
|
+
const [a, b] = p.pos;
|
|
94
|
+
try {
|
|
95
|
+
switch (cmd) {
|
|
96
|
+
case undefined:
|
|
97
|
+
case 'list': {
|
|
98
|
+
const cfg = loadConfig(home);
|
|
99
|
+
for (const e of cfg.errors)
|
|
100
|
+
io.stderr.write(`dexterm pack: config: ${e}\n`);
|
|
101
|
+
const packs = cfg.config.packs ?? [];
|
|
102
|
+
io.stdout.write(`${cfg.path} · ${packs.length} pack(s) installed\n`);
|
|
103
|
+
if (!packs.length)
|
|
104
|
+
return 0;
|
|
105
|
+
const loaded = loadPacks(home, cfg.config);
|
|
106
|
+
for (const r of packs) {
|
|
107
|
+
const ws = loaded.workspaces.filter((w) => w.pack === r.name).map((w) => w.name);
|
|
108
|
+
const th = loaded.themes.filter((t) => t.pack === r.name).map((t) => t.name);
|
|
109
|
+
const ru = loaded.rules.filter((x) => x.source === `pack:${r.name}`);
|
|
110
|
+
const wi = loaded.widgets.filter((w) => w.pack === r.name);
|
|
111
|
+
io.stdout.write(` ${r.name}@${r.version} ${r.trust.padEnd(8)} ${r.hash.slice(0, 23)}… publisher ${r.publisher ?? '?'}\n`);
|
|
112
|
+
if (ws.length)
|
|
113
|
+
io.stdout.write(` workspaces ${ws.join(', ')}\n`);
|
|
114
|
+
if (th.length)
|
|
115
|
+
io.stdout.write(` themes ${th.join(', ')}\n`);
|
|
116
|
+
if (ru.length)
|
|
117
|
+
io.stdout.write(` rules ${ru.map((x) => `${x.id} (${x.enabled ? 'on' : 'off'})`).join(', ')}\n`);
|
|
118
|
+
for (const w of wi)
|
|
119
|
+
io.stdout.write(` widget ${w.pack}/${w.name} ${w.enabled ? 'ENABLED' : 'disabled'} ${w.manifest.widgets.map((x) => x.id).join(', ')}\n`);
|
|
120
|
+
if (loaded.keymaps.some((k) => k.pack === r.name))
|
|
121
|
+
io.stdout.write(` keymap not merged (dexterm pack keymap ${r.name} --merge)\n`);
|
|
122
|
+
}
|
|
123
|
+
for (const e of loaded.errors)
|
|
124
|
+
io.stdout.write(` ⚠ ${e}\n`);
|
|
125
|
+
for (const n of loaded.notes)
|
|
126
|
+
io.stdout.write(` · ${n}\n`);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
case 'verify': {
|
|
130
|
+
if (!a)
|
|
131
|
+
return bad('verify needs a pack directory');
|
|
132
|
+
const insp = inspectPack(resolve(a), { home });
|
|
133
|
+
io.stdout.write(renderPackCard(insp) + '\n');
|
|
134
|
+
const refused = insp.warnings.filter((w) => / refused: /.test(w));
|
|
135
|
+
if (!insp.integrity.ok || refused.length) {
|
|
136
|
+
io.stdout.write(`✗ ${!insp.integrity.ok ? 'integrity failed' : `${refused.length} item(s) refused`}; this pack would not install\n`);
|
|
137
|
+
return 1;
|
|
138
|
+
}
|
|
139
|
+
io.stdout.write(`ok: installs as ${trustWord(insp.trust)}${insp.trust === 'unsigned' ? ' (needs --accept-unsigned)' : ''}\n`);
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
case 'add': {
|
|
143
|
+
if (!a)
|
|
144
|
+
return bad('add needs a pack directory');
|
|
145
|
+
const dir = resolve(a);
|
|
146
|
+
const cfg = loadConfig(home);
|
|
147
|
+
if (!cfg.ok)
|
|
148
|
+
return fail(new Error(cfg.errors[0]));
|
|
149
|
+
const insp = inspectPack(dir, { home });
|
|
150
|
+
io.stdout.write(renderPackCard(insp) + '\n');
|
|
151
|
+
// integrity first: a pack whose files do not match its lock is refused whatever its signature or the flag says
|
|
152
|
+
if (!insp.integrity.ok)
|
|
153
|
+
return fail(new PackError(`integrity: ${insp.integrity.reason}${'changed' in insp.integrity && insp.integrity.changed?.length ? ` (${insp.integrity.changed.join(', ')})` : ''}`, 'integrity'));
|
|
154
|
+
if (insp.trust === 'unsigned' && !p.flags['accept-unsigned']) {
|
|
155
|
+
io.stderr.write(`dexterm pack: ${insp.manifest.name} is not reviewed or signed — do you trust its source? re-run with --accept-unsigned to install it anyway\n (its rules will load DISABLED, its widgets stay disabled, its keymap is not merged)\n`);
|
|
156
|
+
return 1;
|
|
157
|
+
}
|
|
158
|
+
const r = installPack(home, dir, cfg.config, { acceptUnsigned: !!p.flags['accept-unsigned'] });
|
|
159
|
+
persist([{ op: 'replace', path: '/packs', value: r.config.packs ?? [] }, { op: 'replace', path: '/plugins', value: r.config.plugins ?? {} }], `dexterm pack add ${r.manifest.name}@${r.manifest.version} (${r.trust})`);
|
|
160
|
+
io.stdout.write(`installed ${r.manifest.name}@${r.manifest.version} → ${r.dest} (${r.trust})\n`);
|
|
161
|
+
if (r.replaced)
|
|
162
|
+
io.stdout.write(` upgraded: removed ${r.replaced}; widget consent starts over (the pages may have changed)\n`);
|
|
163
|
+
for (const w of r.inspection.widgets)
|
|
164
|
+
if (w.manifest)
|
|
165
|
+
io.stdout.write(` widget ${r.manifest.name}/${w.name} is DISABLED: read ${w.pages.join(', ')} then "dexterm pack widget ${r.manifest.name} ${w.name} --enable"\n`);
|
|
166
|
+
if (r.inspection.keymap?.keymap)
|
|
167
|
+
io.stdout.write(` keymap not merged: "dexterm pack keymap ${r.manifest.name}" shows it, --merge applies it\n`);
|
|
168
|
+
return 0;
|
|
169
|
+
}
|
|
170
|
+
case 'remove': {
|
|
171
|
+
if (!a)
|
|
172
|
+
return bad('remove needs a pack name');
|
|
173
|
+
const cfg = loadConfig(home);
|
|
174
|
+
if (!cfg.ok)
|
|
175
|
+
return fail(new Error(cfg.errors[0]));
|
|
176
|
+
if (!(cfg.config.packs ?? []).some((x) => x.name === a)) {
|
|
177
|
+
io.stderr.write(`dexterm pack: no installed pack "${a}"\n`);
|
|
178
|
+
return 1;
|
|
179
|
+
}
|
|
180
|
+
const r = removePack(home, cfg.config, a);
|
|
181
|
+
persist([{ op: 'replace', path: '/packs', value: r.config.packs ?? [] }, { op: 'replace', path: '/plugins', value: r.config.plugins ?? {} }], `dexterm pack remove ${a}`);
|
|
182
|
+
io.stdout.write(`removed ${a}${r.removed ? ` (${r.removed})` : ''}\n`);
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
case 'export': {
|
|
186
|
+
if (!a)
|
|
187
|
+
return bad('export needs an output directory');
|
|
188
|
+
const name = p.flags.name;
|
|
189
|
+
if (!name)
|
|
190
|
+
return bad('export needs --name <pack-name>');
|
|
191
|
+
const cfg = loadConfig(home);
|
|
192
|
+
const workspaces = [];
|
|
193
|
+
for (const w of p.flags.workspace ?? []) {
|
|
194
|
+
const f = listWorkspaces(home).find((x) => x.name === w && !x.error);
|
|
195
|
+
if (!f)
|
|
196
|
+
return fail(new Error(`no workspace "${w}" (dexterm workspace list)`));
|
|
197
|
+
if (f.pack)
|
|
198
|
+
return fail(new Error(`workspace "${w}" belongs to pack ${f.pack}; export your own files`));
|
|
199
|
+
workspaces.push(f.workspace);
|
|
200
|
+
}
|
|
201
|
+
const themes = [];
|
|
202
|
+
for (const t of p.flags.theme ?? []) {
|
|
203
|
+
const f = listUserThemes(home, cfg.config).find((x) => x.name === t && !x.error);
|
|
204
|
+
if (!f)
|
|
205
|
+
return fail(new Error(`no user theme "${t}" (dexterm theme list)`));
|
|
206
|
+
themes.push(f.doc);
|
|
207
|
+
}
|
|
208
|
+
const rules = p.flags.rules ? (cfg.config.rules ?? []).filter((r) => !r.source || r.source === 'user') : [];
|
|
209
|
+
const keymap = p.flags.keymap ? cfg.config.keymap : undefined;
|
|
210
|
+
const lock = exportPack(resolve(a), { name, version: p.flags.version, publisher: p.flags.publisher ?? io.env.USER ?? 'me', description: p.flags.description, workspaces, themes, rules, keymap });
|
|
211
|
+
io.stdout.write(`wrote ${resolve(a)} · ${lock.files.length} file(s) · ${lock.hash}\n share the directory; sign it with "dexterm pack sign ${a} --key ~/.ssh/id_ed25519" so others can pin your key\n`);
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
case 'lock': {
|
|
215
|
+
if (!a)
|
|
216
|
+
return bad('lock needs a pack directory');
|
|
217
|
+
const lock = writeLock(resolve(a));
|
|
218
|
+
io.stdout.write(`${lock.hash} · ${lock.files.length} file(s)\n`);
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
case 'sign': {
|
|
222
|
+
if (!a)
|
|
223
|
+
return bad('sign needs a pack directory');
|
|
224
|
+
const key = p.flags.key;
|
|
225
|
+
if (!key)
|
|
226
|
+
return bad('sign needs --key <ssh private key file> (e.g. ~/.ssh/id_ed25519)');
|
|
227
|
+
if (!existsSync(key))
|
|
228
|
+
return fail(new Error(`${key}: no such key file`));
|
|
229
|
+
const sig = signPack(resolve(a), { keyFile: key });
|
|
230
|
+
io.stdout.write(`signed → ${sig}\n publish your PUBLIC key (${key}.pub) so users can run: dexterm pack trust <your-identity> "$(cat ${key}.pub)"\n`);
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
case 'trust': {
|
|
234
|
+
if (!a || !b)
|
|
235
|
+
return bad('trust needs <identity> "<ssh pubkey line>"');
|
|
236
|
+
const path = pinSigner(home, a, b);
|
|
237
|
+
io.stdout.write(`pinned ${a} in ${path}\n`);
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
case 'signers': {
|
|
241
|
+
const s = listSigners(home);
|
|
242
|
+
io.stdout.write(s.length ? s.map((x) => ` ${x.identity.padEnd(32)} ${x.key}`).join('\n') + '\n' : 'no pinned publisher keys (dexterm pack trust <identity> "<pubkey>")\n');
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
case 'keymap': {
|
|
246
|
+
if (!a)
|
|
247
|
+
return bad('keymap needs a pack name');
|
|
248
|
+
const cfg = loadConfig(home);
|
|
249
|
+
if (!cfg.ok)
|
|
250
|
+
return fail(new Error(cfg.errors[0]));
|
|
251
|
+
const k = loadPacks(home, cfg.config).keymaps.find((x) => x.pack === a);
|
|
252
|
+
if (!k) {
|
|
253
|
+
io.stderr.write(`dexterm pack: no installed pack "${a}" with a keymap\n`);
|
|
254
|
+
return 1;
|
|
255
|
+
}
|
|
256
|
+
const entries = [];
|
|
257
|
+
for (const [action, keys] of Object.entries(k.keymap.global ?? {}))
|
|
258
|
+
entries.push([null, action, keys]);
|
|
259
|
+
for (const [screen, t] of Object.entries(k.keymap.screens ?? {}))
|
|
260
|
+
for (const [action, keys] of Object.entries(t))
|
|
261
|
+
entries.push([screen, action, keys]);
|
|
262
|
+
const own = cfg.config.keymap ?? {};
|
|
263
|
+
const conflicts = entries.filter(([s, action]) => (s ? own.screens?.[s]?.[action] : own.global?.[action]) !== undefined);
|
|
264
|
+
io.stdout.write(`${a} keymap (${entries.length} binding(s); ${p.flags.merge ? 'merging' : 'NOT merged'}):\n`);
|
|
265
|
+
for (const [s, action, keys] of entries)
|
|
266
|
+
io.stdout.write(` ${(s ?? 'global').padEnd(10)} ${action.padEnd(22)} ${keys.join(' / ')}${conflicts.some(([cs, ca]) => cs === s && ca === action) ? ' (replaces your override)' : ''}\n`);
|
|
267
|
+
// validate the MERGED result (defaults + yours + the pack's): reserved keys/actions and collisions refuse the whole merge
|
|
268
|
+
const merged = { global: { ...(own.global ?? {}), ...(k.keymap.global ?? {}) }, screens: { ...(own.screens ?? {}) } };
|
|
269
|
+
for (const [screen, t] of Object.entries(k.keymap.screens ?? {}))
|
|
270
|
+
merged.screens[screen] = { ...(merged.screens[screen] ?? {}), ...t };
|
|
271
|
+
const res = applyKeymapConfig(DEFAULT_KEYMAP.bindings, merged);
|
|
272
|
+
const errs = res.errors.length ? res.errors : collisions(res.bindings);
|
|
273
|
+
if (errs.length) {
|
|
274
|
+
for (const e of errs)
|
|
275
|
+
io.stdout.write(` ✗ ${e.path} ${e.msg}\n`);
|
|
276
|
+
io.stdout.write(`refused: this keymap cannot be merged (${errs.length} problem(s)); your keys are unchanged\n`);
|
|
277
|
+
return 1;
|
|
278
|
+
}
|
|
279
|
+
if (!p.flags.merge) {
|
|
280
|
+
io.stdout.write(`ok: would merge cleanly · dexterm pack keymap ${a} --merge\n`);
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
persist([{ op: 'replace', path: '/keymap', value: merged }], `dexterm pack keymap ${a} --merge`);
|
|
284
|
+
io.stdout.write(`merged ${entries.length} binding(s) into config.keymap (ctrl+z in the app or the journal undoes it)\n`);
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
case 'widget': {
|
|
288
|
+
if (!a || !b)
|
|
289
|
+
return bad('widget needs <pack> <widget> --enable|--disable');
|
|
290
|
+
const on = p.flags.enable ? true : p.flags.disable ? false : null;
|
|
291
|
+
if (on === null)
|
|
292
|
+
return bad('widget needs --enable or --disable');
|
|
293
|
+
const cfg = loadConfig(home);
|
|
294
|
+
if (!cfg.ok)
|
|
295
|
+
return fail(new Error(cfg.errors[0]));
|
|
296
|
+
const key = packWidgetKey(a, b);
|
|
297
|
+
if (on) {
|
|
298
|
+
// consent is informed: the pack must still hash to its record, and the page is named so the user has read it
|
|
299
|
+
const rec = (cfg.config.packs ?? []).find((x) => x.name === a);
|
|
300
|
+
if (!rec)
|
|
301
|
+
return fail(new Error(`no installed pack "${a}" (dexterm pack list)`));
|
|
302
|
+
const integ = checkIntegrity(join(packsDir(home), `${rec.name}@${rec.version}`));
|
|
303
|
+
if (!integ.ok || integ.hash !== rec.hash)
|
|
304
|
+
return fail(new Error(`pack ${a} changed since install (${integ.ok ? 'hash differs' : integ.reason}); re-install before enabling a widget`));
|
|
305
|
+
const w = loadPacks(home, cfg.config).widgets.find((x) => x.pack === a && x.name === b);
|
|
306
|
+
if (!w)
|
|
307
|
+
return fail(new Error(`no installed widget ${key} (dexterm pack list)`));
|
|
308
|
+
io.stdout.write(`enabling ${key}: the popup host will serve ${w.manifest.widgets.map((x) => `${w.dir}/${x.page}`).join(', ')} in a loopback window (hash-only CSP: only the inline script/style you just read runs — no remote script, style, frame or import, no fetch, no eval; topics ${[...new Set(w.manifest.widgets.flatMap((x) => x.topics))].join(', ')})\n`);
|
|
309
|
+
}
|
|
310
|
+
const next = setWidgetEnabled(cfg.config, a, b, on);
|
|
311
|
+
persist([{ op: 'replace', path: '/plugins', value: next.plugins ?? {} }], `dexterm pack widget ${a} ${b} ${on ? '--enable' : '--disable'}`);
|
|
312
|
+
io.stdout.write(`${key} ${on ? 'ENABLED (open it from a workspace popup {"kind":"widget","widget":"' + key + '"} or ctrl+w o)' : 'disabled'}\n`);
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
default: return bad(`unknown subcommand "${cmd}"`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (e) {
|
|
319
|
+
return fail(e);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/** For tests + the install card: the text of a pack page, size-capped (the popup host applies its own cap too). */
|
|
323
|
+
export function readPackPage(path, max = 512_000) { const t = readFileSync(path, 'utf8'); return t.length > max ? t.slice(0, max) : t; }
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// `dexterm plan …` and `dexterm mandate …` — typed order-plan + standing-mandate CLI (task-1355).
|
|
2
|
+
//
|
|
3
|
+
// dexterm plan propose --mint M --symbol S --size 20 --entry mc-below:300k [--tp mc-above:600k:50%]…
|
|
4
|
+
// [--stop mc-below:150k] [--decimals 6] [--supply 1b] [--slippage 300]
|
|
5
|
+
// dexterm plan list [--json]
|
|
6
|
+
// dexterm plan arm <planId> --mandate <id> # requires a PHYSICAL keypress on a TTY (refuses pipes)
|
|
7
|
+
// dexterm plan amend <planId> [--size 25] [--entry …] [--tp …]… [--stop …|none] [--slippage N] # task-1363
|
|
8
|
+
// dexterm plan cancel <planId>
|
|
9
|
+
// dexterm plan journal [--verify] [--export]
|
|
10
|
+
// dexterm mandate draft [--total 100] [--per-trade 20] [--per-coin 40] [--daily-loss 50]
|
|
11
|
+
// [--max-tier keypress|ptt-held|hands-free] [--ttl-hours 4]
|
|
12
|
+
// dexterm mandate list [--json] | arm <id> | revoke <id>
|
|
13
|
+
//
|
|
14
|
+
// Arming is the only gate: `arm` reads ONE real keystroke in raw mode (ttyKeypress) and refuses a pipe,
|
|
15
|
+
// a subprocess, a voice router or an agent — exactly like `dexterm wallet resume`. Proposing never arms.
|
|
16
|
+
// Default runs are PAPER (no key, no wallet, no mainnet); the journal under ~/.dexterm/plans.journal is
|
|
17
|
+
// the hash-chained audit log + recovery source.
|
|
18
|
+
import { PlanService, defaultPlansJournalPath, parseAmount, parseTrigger, parseExit, planStatusLabel, fmtUsd, PlanError, PolicyRefusal, MandateError, ConfirmError, } from '../../data/src/trade/plans/index.js';
|
|
19
|
+
import { ttyKeypress } from "./wallet-cmd.js";
|
|
20
|
+
function flag(rest, name) { const i = rest.indexOf(name); return i >= 0 ? rest[i + 1] : undefined; }
|
|
21
|
+
function has(rest, name) { return rest.includes(name); }
|
|
22
|
+
/** Collect every value after each occurrence of `name` (for repeatable flags like --tp). */
|
|
23
|
+
function flags(rest, name) { const out = []; for (let i = 0; i < rest.length; i++)
|
|
24
|
+
if (rest[i] === name && rest[i + 1] !== undefined)
|
|
25
|
+
out.push(rest[i + 1]); return out; }
|
|
26
|
+
function openService(rest, io) {
|
|
27
|
+
const journalPath = flag(rest, '--journal') ?? defaultPlansJournalPath(io.env);
|
|
28
|
+
return new PlanService({ journalPath });
|
|
29
|
+
}
|
|
30
|
+
function errMsg(e) {
|
|
31
|
+
if (e instanceof PlanError || e instanceof PolicyRefusal || e instanceof MandateError || e instanceof ConfirmError)
|
|
32
|
+
return `${e.reason}: ${e.message}`;
|
|
33
|
+
return e instanceof Error ? e.message : String(e);
|
|
34
|
+
}
|
|
35
|
+
const PLAN_USAGE = `dexterm plan <command>
|
|
36
|
+
propose --mint M --size 20 --entry mc-below:300k [--symbol S] [--decimals 6] [--supply 1b]
|
|
37
|
+
[--tp mc-above:600k:50%]… [--stop mc-below:150k] [--slippage 300] [--side buy|sell] [--json]
|
|
38
|
+
list [--json]
|
|
39
|
+
arm <planId> --mandate <id> (needs a physical keypress on a TTY; a pipe/agent is refused)
|
|
40
|
+
amend <planId> [--size 25] [--entry mc-below:280k] [--tp mc-above:600k:50%]… [--stop mc-below:150k|none] [--slippage N]
|
|
41
|
+
(a PROPOSED plan only: the edit is a NEW plan with a new hash; the old one is
|
|
42
|
+
superseded and every earlier confirmation is void; re-arm by keypress)
|
|
43
|
+
cancel <planId>
|
|
44
|
+
journal [--verify] [--export]`;
|
|
45
|
+
const MANDATE_USAGE = `dexterm mandate <command>
|
|
46
|
+
draft [--total 100] [--per-trade 20] [--per-coin 40] [--daily-loss 50]
|
|
47
|
+
[--max-tier keypress|ptt-held|hands-free] [--ttl-hours 4] [--json]
|
|
48
|
+
list [--json]
|
|
49
|
+
arm <id> (needs a physical keypress on a TTY; a pipe/agent is refused)
|
|
50
|
+
revoke <id>`;
|
|
51
|
+
export async function runPlan(rest, io) {
|
|
52
|
+
const cmd = rest[0];
|
|
53
|
+
const out = (s) => io.stdout.write(s + '\n');
|
|
54
|
+
const errOut = (s) => io.stderr.write(s + '\n');
|
|
55
|
+
try {
|
|
56
|
+
switch (cmd) {
|
|
57
|
+
case 'propose': {
|
|
58
|
+
const mint = flag(rest, '--mint');
|
|
59
|
+
if (!mint) {
|
|
60
|
+
errOut('dexterm plan propose: --mint is required');
|
|
61
|
+
return 2;
|
|
62
|
+
}
|
|
63
|
+
const sizeStr = flag(rest, '--size');
|
|
64
|
+
if (!sizeStr) {
|
|
65
|
+
errOut('dexterm plan propose: --size is required (USD)');
|
|
66
|
+
return 2;
|
|
67
|
+
}
|
|
68
|
+
const entryStr = flag(rest, '--entry') ?? 'immediate';
|
|
69
|
+
const supply = parseAmount(flag(rest, '--supply') ?? '1b');
|
|
70
|
+
const input = {
|
|
71
|
+
mint, symbol: flag(rest, '--symbol'), decimals: Number(flag(rest, '--decimals') ?? 6),
|
|
72
|
+
side: flag(rest, '--side') ?? 'buy', sizeUsd: parseAmount(sizeStr),
|
|
73
|
+
entry: parseTrigger(entryStr),
|
|
74
|
+
exits: flags(rest, '--tp').map(parseExit),
|
|
75
|
+
stop: flag(rest, '--stop') ? parseTrigger(flag(rest, '--stop')) : null,
|
|
76
|
+
slippageBps: flag(rest, '--slippage') ? Number(flag(rest, '--slippage')) : undefined,
|
|
77
|
+
};
|
|
78
|
+
const svc = openService(rest, io);
|
|
79
|
+
const plan = svc.propose(input, { supply, by: 'user' });
|
|
80
|
+
if (has(rest, '--json')) {
|
|
81
|
+
out(JSON.stringify({ id: plan.id, hash: plan.hash, readBack: svc.readBack(plan.id) }));
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
out(`proposed ${plan.id}`);
|
|
85
|
+
out(` ${svc.readBack(plan.id)}`);
|
|
86
|
+
out(' PROPOSAL only — not armed. Arm with: dexterm plan arm ' + plan.id + ' --mandate <id> (physical keypress)');
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
case 'list': {
|
|
90
|
+
const svc = openService(rest, io);
|
|
91
|
+
const rows = svc.listPlans();
|
|
92
|
+
if (has(rest, '--json')) {
|
|
93
|
+
out(JSON.stringify(rows));
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
if (!rows.length) {
|
|
97
|
+
out('no plans');
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
for (const r of rows)
|
|
101
|
+
out(`${r.id.padEnd(22)} ${planStatusLabel(r)}`);
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
case 'arm': {
|
|
105
|
+
const planId = rest[1];
|
|
106
|
+
const mandateId = flag(rest, '--mandate');
|
|
107
|
+
if (!planId || !mandateId) {
|
|
108
|
+
errOut('usage: dexterm plan arm <planId> --mandate <id>');
|
|
109
|
+
return 2;
|
|
110
|
+
}
|
|
111
|
+
const svc = openService(rest, io);
|
|
112
|
+
const plan = svc.engine.plan(planId);
|
|
113
|
+
if (!plan) {
|
|
114
|
+
errOut(`no such plan ${planId}`);
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
out(`Arm this plan into ${mandateId}?`);
|
|
118
|
+
out(` ${svc.readBack(planId)}`);
|
|
119
|
+
out('Hold any key to confirm (physical keypress). A pipe or agent cannot arm. Waiting 15s…');
|
|
120
|
+
const key = await ttyKeypress(io.stdin, 15_000);
|
|
121
|
+
if (key === null) {
|
|
122
|
+
errOut('refused: no physical keypress (stdin is not a TTY, or timed out). Voice/agents can never arm.');
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
svc.armPlanByKeypress(planId, mandateId);
|
|
126
|
+
out(`armed ${planId} into ${mandateId}`);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
case 'amend': {
|
|
130
|
+
// task-1363 (card item 3): edit a PROPOSED plan without the cancel + re-propose dance. The engine's amend
|
|
131
|
+
// is the existing primitive: a mutated plan is a NEW plan (new id, new hash), the old proposal is
|
|
132
|
+
// superseded, and nothing armed can be edited (cancel it first). Arming the new plan is a new keypress.
|
|
133
|
+
const planId = rest[1];
|
|
134
|
+
if (!planId) {
|
|
135
|
+
errOut('usage: dexterm plan amend <planId> [--size USD] [--entry TRIGGER] [--tp EXIT]… [--stop TRIGGER|none] [--slippage BPS]');
|
|
136
|
+
return 2;
|
|
137
|
+
}
|
|
138
|
+
const patch = {};
|
|
139
|
+
if (flag(rest, '--size'))
|
|
140
|
+
patch.sizeUsd = parseAmount(flag(rest, '--size'));
|
|
141
|
+
if (flag(rest, '--entry'))
|
|
142
|
+
patch.entry = parseTrigger(flag(rest, '--entry'));
|
|
143
|
+
if (flags(rest, '--tp').length)
|
|
144
|
+
patch.exits = flags(rest, '--tp').map(parseExit);
|
|
145
|
+
if (flag(rest, '--stop'))
|
|
146
|
+
patch.stop = flag(rest, '--stop') === 'none' ? null : parseTrigger(flag(rest, '--stop'));
|
|
147
|
+
if (flag(rest, '--slippage'))
|
|
148
|
+
patch.slippageBps = Number(flag(rest, '--slippage'));
|
|
149
|
+
if (!Object.keys(patch).length) {
|
|
150
|
+
errOut('dexterm plan amend: nothing to change (pass --size / --entry / --tp / --stop / --slippage)');
|
|
151
|
+
return 2;
|
|
152
|
+
}
|
|
153
|
+
const svc = openService(rest, io);
|
|
154
|
+
const next = svc.engine.amend(planId, patch, { by: 'user' });
|
|
155
|
+
if (has(rest, '--json')) {
|
|
156
|
+
out(JSON.stringify({ id: next.id, hash: next.hash, supersedes: planId, readBack: svc.readBack(next.id) }));
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
out(`amended: ${planId} → ${next.id} (superseded; every earlier confirmation is void)`);
|
|
160
|
+
out(` ${svc.readBack(next.id)}`);
|
|
161
|
+
out(' PROPOSAL only — not armed. Arm with: dexterm plan arm ' + next.id + ' --mandate <id> (physical keypress)');
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
case 'cancel': {
|
|
165
|
+
const planId = rest[1];
|
|
166
|
+
if (!planId) {
|
|
167
|
+
errOut('usage: dexterm plan cancel <planId>');
|
|
168
|
+
return 2;
|
|
169
|
+
}
|
|
170
|
+
const svc = openService(rest, io);
|
|
171
|
+
const status = svc.cancelPlan(planId, 'user');
|
|
172
|
+
out(`${planId}: ${status}`);
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
case 'journal': {
|
|
176
|
+
const svc = openService(rest, io);
|
|
177
|
+
if (has(rest, '--export')) {
|
|
178
|
+
out(svc.exportJournal());
|
|
179
|
+
return 0;
|
|
180
|
+
}
|
|
181
|
+
const v = svc.verifyJournal();
|
|
182
|
+
out(v.ok ? `audit log OK — ${v.count} events, hash chain verified` : `audit log BROKEN at seq ${v.atSeq}: ${v.why}`);
|
|
183
|
+
return v.ok ? 0 : 1;
|
|
184
|
+
}
|
|
185
|
+
default:
|
|
186
|
+
(cmd ? errOut(`unknown subcommand: ${cmd}`) : out(PLAN_USAGE));
|
|
187
|
+
return cmd ? 2 : 0;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
errOut(`dexterm plan: ${errMsg(e)}`);
|
|
192
|
+
return 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
export async function runMandate(rest, io) {
|
|
196
|
+
const cmd = rest[0];
|
|
197
|
+
const out = (s) => io.stdout.write(s + '\n');
|
|
198
|
+
const errOut = (s) => io.stderr.write(s + '\n');
|
|
199
|
+
try {
|
|
200
|
+
switch (cmd) {
|
|
201
|
+
case 'draft': {
|
|
202
|
+
const svc = openService(rest, io);
|
|
203
|
+
const num = (n) => (flag(rest, n) !== undefined ? parseAmount(flag(rest, n)) : undefined);
|
|
204
|
+
const maxTier = flag(rest, '--max-tier');
|
|
205
|
+
const ttlHours = flag(rest, '--ttl-hours') ? Number(flag(rest, '--ttl-hours')) : undefined;
|
|
206
|
+
const m = svc.draftMandate({
|
|
207
|
+
totalUsd: num('--total'), perTradeUsd: num('--per-trade'), perCoinUsd: num('--per-coin'),
|
|
208
|
+
dailyLossUsd: num('--daily-loss'), maxTier, ttlMs: ttlHours ? ttlHours * 3600_000 : undefined,
|
|
209
|
+
});
|
|
210
|
+
if (has(rest, '--json')) {
|
|
211
|
+
out(JSON.stringify({ id: m.id, budget: m.budget, maxTier: m.maxTier }));
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
out(`drafted ${m.id}`);
|
|
215
|
+
out(` total ${fmtUsd(m.budget.totalUsd)} · per-trade ${fmtUsd(m.budget.perTradeUsd)} · per-coin ${fmtUsd(m.budget.perCoinUsd)} · daily-loss ${fmtUsd(m.budget.dailyLossUsd)} · maxTier ${m.maxTier}`);
|
|
216
|
+
out(' DRAFT only — not armed. Arm with: dexterm mandate arm ' + m.id + ' (physical keypress)');
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
case 'list': {
|
|
220
|
+
const svc = openService(rest, io);
|
|
221
|
+
const rows = svc.listMandates();
|
|
222
|
+
if (has(rest, '--json')) {
|
|
223
|
+
out(JSON.stringify(rows));
|
|
224
|
+
return 0;
|
|
225
|
+
}
|
|
226
|
+
if (!rows.length) {
|
|
227
|
+
out('no mandates');
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
for (const r of rows)
|
|
231
|
+
out(`${r.id.padEnd(24)} ${r.status.padEnd(7)} total ${fmtUsd(r.totalUsd)} · reserved ${fmtUsd(r.reservedUsd)} · spent ${fmtUsd(r.spentUsd)} · maxTier ${r.maxTier}`);
|
|
232
|
+
return 0;
|
|
233
|
+
}
|
|
234
|
+
case 'arm': {
|
|
235
|
+
const id = rest[1];
|
|
236
|
+
if (!id) {
|
|
237
|
+
errOut('usage: dexterm mandate arm <id>');
|
|
238
|
+
return 2;
|
|
239
|
+
}
|
|
240
|
+
const svc = openService(rest, io);
|
|
241
|
+
const row = svc.listMandates().find((m) => m.id === id);
|
|
242
|
+
if (!row) {
|
|
243
|
+
errOut(`no such mandate ${id}`);
|
|
244
|
+
return 1;
|
|
245
|
+
}
|
|
246
|
+
out(`Arm standing mandate ${id}? This lets armed plans fire at machine speed from the capped fast wallet.`);
|
|
247
|
+
out(` total ${fmtUsd(row.totalUsd)} · per-trade ${fmtUsd(row.perTradeUsd)} · per-coin ${fmtUsd(row.perCoinUsd)} · maxTier ${row.maxTier}`);
|
|
248
|
+
out('Hold any key to confirm (physical keypress). A pipe or agent cannot arm. Waiting 15s…');
|
|
249
|
+
const key = await ttyKeypress(io.stdin, 15_000);
|
|
250
|
+
if (key === null) {
|
|
251
|
+
errOut('refused: no physical keypress (stdin is not a TTY, or timed out). Voice/agents can never arm a mandate.');
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
254
|
+
svc.armMandateByKeypress(id);
|
|
255
|
+
out(`armed mandate ${id}`);
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
case 'revoke': {
|
|
259
|
+
const id = rest[1];
|
|
260
|
+
if (!id) {
|
|
261
|
+
errOut('usage: dexterm mandate revoke <id>');
|
|
262
|
+
return 2;
|
|
263
|
+
}
|
|
264
|
+
const svc = openService(rest, io);
|
|
265
|
+
svc.revokeMandate(id, 'user');
|
|
266
|
+
out(`revoked ${id}`);
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
default:
|
|
270
|
+
(cmd ? errOut(`unknown subcommand: ${cmd}`) : out(MANDATE_USAGE));
|
|
271
|
+
return cmd ? 2 : 0;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch (e) {
|
|
275
|
+
errOut(`dexterm mandate: ${errMsg(e)}`);
|
|
276
|
+
return 1;
|
|
277
|
+
}
|
|
278
|
+
}
|