dexterm-cli 0.0.0-stage → 0.1.1
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 +379 -0
- package/dist/packages/agents/src/executor.js +177 -0
- package/dist/packages/agents/src/gate.js +176 -0
- package/dist/packages/agents/src/identity.js +179 -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 +114 -0
- package/dist/packages/agents/src/tools.js +69 -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 +67 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +64 -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 +303 -0
- package/dist/packages/cli/src/agent-roster.js +26 -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 +149 -0
- package/dist/packages/cli/src/boot-state.js +62 -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 +361 -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 +509 -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 +5583 -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 +339 -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/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/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/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 +515 -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/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 +208 -0
- package/dist/packages/data/src/intel/helius-history.js +59 -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/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 +170 -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 +247 -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 +849 -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 +19 -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/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 +15 -0
- package/dist/packages/tui/src/keymap.js +355 -0
- package/dist/packages/tui/src/layout.js +11 -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 +395 -0
- package/dist/packages/voice/src/doctor.js +63 -0
- package/dist/packages/voice/src/engines.js +44 -0
- package/dist/packages/voice/src/index.js +16 -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 +102 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/say.js +96 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +42 -4
- package/README.md +0 -3
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// `dexterm narrative [--window 1h|24h] [--summary] [--db PATH]` (task-1356, AI-SUITE-DESIGN §7).
|
|
2
|
+
// Computes the narrative board from the history recorder's snapshots and prints it (or the deterministic
|
|
3
|
+
// 90-second morning summary with --summary). No network, no model. Read-only on the DB.
|
|
4
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
5
|
+
import { defaultDbPath } from '../../data/src/index.js';
|
|
6
|
+
import { computeBoard, recentNarrativeRows, loadDefaultLexicon, renderBoardText, summaryText } from '../../packs/src/index.js';
|
|
7
|
+
export const NARRATIVE_HELP = `dexterm narrative: the narrative board over recorded history (task-1356). No network, no model.
|
|
8
|
+
|
|
9
|
+
dexterm narrative [--window 1h|24h] [--summary] [--db PATH]
|
|
10
|
+
|
|
11
|
+
--window 1h (default) or 24h: which window the "what is working" score uses.
|
|
12
|
+
--summary print the deterministic 90-second spoken summary instead of the table (keyless, no model).
|
|
13
|
+
--db defaults to DEXTERM_DB_PATH, else ~/.local/share/dexterm/dexterm.db.
|
|
14
|
+
|
|
15
|
+
Narratives are clustered from token names against a versioned lexicon (owner: maintainers); X/Telegram-only
|
|
16
|
+
narratives are missed and the honest unclassified share is shown. The board needs the app to have been
|
|
17
|
+
running with the recorder on (switch off with --no-recorder / DEXTERM_RECORDER=0).`;
|
|
18
|
+
function parse(argv) {
|
|
19
|
+
const opts = {};
|
|
20
|
+
for (let i = 0; i < argv.length; i++) {
|
|
21
|
+
const a = argv[i];
|
|
22
|
+
if (!a.startsWith('--'))
|
|
23
|
+
continue;
|
|
24
|
+
const k = a.slice(2);
|
|
25
|
+
const v = argv[i + 1];
|
|
26
|
+
if (v !== undefined && !v.startsWith('--') && k !== 'summary') {
|
|
27
|
+
opts[k] = v;
|
|
28
|
+
i++;
|
|
29
|
+
}
|
|
30
|
+
else
|
|
31
|
+
opts[k] = true;
|
|
32
|
+
}
|
|
33
|
+
return opts;
|
|
34
|
+
}
|
|
35
|
+
export function narrativeCommand(argv, io) {
|
|
36
|
+
const opts = parse(argv);
|
|
37
|
+
if (opts.help || opts.h) {
|
|
38
|
+
io.stdout.write(NARRATIVE_HELP + '\n');
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
const path = typeof opts.db === 'string' ? opts.db : (io.env.DEXTERM_DB_PATH || defaultDbPath(io.env));
|
|
42
|
+
if (path === ':memory:') {
|
|
43
|
+
io.stderr.write('dexterm narrative: an in-memory DB has no history\n');
|
|
44
|
+
return 2;
|
|
45
|
+
}
|
|
46
|
+
const window = opts.window === '24h' ? '24h' : '1h';
|
|
47
|
+
let db = null;
|
|
48
|
+
try {
|
|
49
|
+
db = new DatabaseSync(path, { readOnly: true });
|
|
50
|
+
const hasTable = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'token_snapshots'").get();
|
|
51
|
+
if (!hasTable) {
|
|
52
|
+
io.stderr.write(`dexterm narrative: no recorded history in ${path} (run the app with the recorder on first)\n`);
|
|
53
|
+
return 2;
|
|
54
|
+
}
|
|
55
|
+
const rows = recentNarrativeRows(db, {});
|
|
56
|
+
const board = computeBoard(rows, loadDefaultLexicon(), { window });
|
|
57
|
+
if (opts.summary) {
|
|
58
|
+
const s = summaryText(board);
|
|
59
|
+
io.stdout.write(`${s.text}\n\n[${s.words} words ≈ ${s.approx_sec}s at 150 wpm; deterministic template, no model]\n`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
io.stdout.write(renderBoardText(board));
|
|
63
|
+
}
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
io.stderr.write(`dexterm narrative: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
try {
|
|
72
|
+
db?.close();
|
|
73
|
+
}
|
|
74
|
+
catch { /* ignore */ }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -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; }
|