rovecode 0.3.3 → 0.4.0-beta.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/CHANGELOG.md +512 -0
- package/README.md +33 -48
- package/bin/rovecode.js +24 -0
- package/dist/cli/app-dybnr56b.js +2 -0
- package/dist/cli/ask-user-p8hq4xgj.js +2 -0
- package/dist/cli/auth-login-ewpgw5sm.js +2 -0
- package/dist/cli/auth-m8p9grty.js +2 -0
- package/dist/cli/bench-xv3ypwev.js +9 -0
- package/dist/cli/catalog-737wb2s0.js +2 -0
- package/dist/cli/cli-arhg40m0.js +2 -0
- package/dist/cli/client-cf2pxx8q.js +2 -0
- package/dist/cli/commands-3p7e4xxs.js +2 -0
- package/dist/cli/connect-3q93d7cb.js +2 -0
- package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
- package/dist/cli/context-report-hbw9zfes.js +2 -0
- package/dist/cli/count-remote-mby98cd0.js +2 -0
- package/dist/cli/design-122y0axd.js +2 -0
- package/dist/cli/dispatch-b4egzvvh.js +2 -0
- package/dist/cli/doctor-x4jkv72e.js +3 -0
- package/dist/cli/executor-ftvg6tsy.js +2 -0
- package/dist/cli/export-pdgdhkch.js +2 -0
- package/dist/cli/files-cez9a96p.js +2 -0
- package/dist/cli/gauntlet-r3xxaszc.js +2 -0
- package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
- package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
- package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
- package/dist/cli/hashline-ewg5hbe3.js +2 -0
- package/dist/cli/http-n0kehsk8.js +5 -0
- package/dist/cli/index-z5qt1s76.js +2 -0
- package/dist/cli/install-80mp63kx.js +2 -0
- package/dist/cli/loop-12twjcat.js +2 -0
- package/dist/cli/main-01pv9206.js +4 -0
- package/dist/cli/main-0jys2ccn.js +3 -0
- package/dist/cli/main-1ztz6fkj.js +10 -0
- package/dist/cli/main-23q7cmww.js +9 -0
- package/dist/cli/main-2rzbexn2.js +3 -0
- package/dist/cli/main-2wyax8k9.js +9 -0
- package/dist/cli/main-2yeveeve.js +6 -0
- package/dist/cli/main-2z3dek0b.js +3 -0
- package/dist/cli/main-2zgsknth.js +3 -0
- package/dist/cli/main-45ejth3a.js +4 -0
- package/dist/cli/main-45rn3trk.js +22 -0
- package/dist/cli/main-4p4e2w7x.js +4 -0
- package/dist/cli/main-4y0tnfpa.js +16 -0
- package/dist/cli/main-5py0rkmc.js +4 -0
- package/dist/cli/main-6dtqmbt6.js +7 -0
- package/dist/cli/main-6h9x282m.js +4 -0
- package/dist/cli/main-6vjeds42.js +3 -0
- package/dist/cli/main-78gq4bt9.js +6 -0
- package/dist/cli/main-7jd5vh3x.js +4 -0
- package/dist/cli/main-7kt6r53y.js +4 -0
- package/dist/cli/main-8c1tbazx.js +58 -0
- package/dist/cli/main-9a9rnh47.js +19 -0
- package/dist/cli/main-9ht36z12.js +3 -0
- package/dist/cli/main-a2yfvcy9.js +7 -0
- package/dist/cli/main-a3f51n0x.js +5 -0
- package/dist/cli/main-b8zq261k.js +3 -0
- package/dist/cli/main-bxtvnf6d.js +13 -0
- package/dist/cli/main-edxc3yzt.js +4 -0
- package/dist/cli/main-evgz4mp5.js +21 -0
- package/dist/cli/main-f33fc5je.js +9 -0
- package/dist/cli/main-fvnpq46y.js +12 -0
- package/dist/cli/main-gbbty4d4.js +3 -0
- package/dist/cli/main-gth53dnt.js +25 -0
- package/dist/cli/main-hqbz10aw.js +9 -0
- package/dist/cli/main-hrrvcfan.js +38 -0
- package/dist/cli/main-hzwtsb2m.js +5 -0
- package/dist/cli/main-j7ttv0sd.js +34 -0
- package/dist/cli/main-jak598k9.js +5 -0
- package/dist/cli/main-kba6zeyd.js +6 -0
- package/dist/cli/main-kwwsz6rq.js +3 -0
- package/dist/cli/main-m8vm17zq.js +3 -0
- package/dist/cli/main-mg4f96e1.js +3 -0
- package/dist/cli/main-mg9b20ac.js +18 -0
- package/dist/cli/main-mgb9ccnx.js +3 -0
- package/dist/cli/main-mjt2p7aj.js +3 -0
- package/dist/cli/main-n6qrdbmy.js +3 -0
- package/dist/cli/main-na7wse0x.js +5 -0
- package/dist/cli/main-nqveez48.js +4 -0
- package/dist/cli/main-ntqef02r.js +10 -0
- package/dist/cli/main-nvc3yjay.js +136 -0
- package/dist/cli/main-p0cfn6nr.js +16 -0
- package/dist/cli/main-qj2djy17.js +19 -0
- package/dist/cli/main-qsevpgsv.js +3 -0
- package/dist/cli/main-qvarybsp.js +3 -0
- package/dist/cli/main-rebtt91r.js +5 -0
- package/dist/cli/main-rpg7h8mb.js +3 -0
- package/dist/cli/main-rsy72qmw.js +15 -0
- package/dist/cli/main-rvetps99.js +18 -0
- package/dist/cli/main-s4bb0jav.js +3 -0
- package/dist/cli/main-s9v8k74e.js +3 -0
- package/dist/cli/main-tjvwmscs.js +3 -0
- package/dist/cli/main-tkgarpjj.js +4 -0
- package/dist/cli/main-v8y60bb2.js +3 -0
- package/dist/cli/main-vhrrq337.js +3 -0
- package/dist/cli/main-vp2dfb7s.js +4 -0
- package/dist/cli/main-vqbr22sz.js +8 -0
- package/dist/cli/main-vxnwe5xx.js +18 -0
- package/dist/cli/main-wgph00xf.js +5 -0
- package/dist/cli/main-wk2csfnj.js +5 -0
- package/dist/cli/main-wm997zjx.js +3 -0
- package/dist/cli/main-wpkyraxh.js +3 -0
- package/dist/cli/main-wqt32p5x.js +4 -0
- package/dist/cli/main-x9ct6y1a.js +3 -0
- package/dist/cli/main-xfekqh9m.js +7 -0
- package/dist/cli/main-xt9zc3n6.js +7 -0
- package/dist/cli/main-xx2z3zh5.js +4 -0
- package/dist/cli/main-y5c82rxr.js +3 -0
- package/dist/cli/main-yrjt2sqt.js +14 -0
- package/dist/cli/main-ys6zj3yr.js +3 -0
- package/dist/cli/main-ywbxshqc.js +8 -0
- package/dist/cli/main-z13755t8.js +25 -0
- package/dist/cli/main-zc7pyrbj.js +4 -0
- package/dist/cli/main.js +279 -0
- package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
- package/dist/cli/mcp-login-bthtfpt7.js +2 -0
- package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
- package/dist/cli/notify-54v5z9dz.js +2 -0
- package/dist/cli/oauth-g5gme95c.js +2 -0
- package/dist/cli/output-satndjap.js +16 -0
- package/dist/cli/profiles-sfhpbq3m.js +2 -0
- package/dist/cli/provider-config-hv3xtdt4.js +2 -0
- package/dist/cli/provider-kwzq6g84.js +2 -0
- package/dist/cli/registry-fh0hdnyn.js +2 -0
- package/dist/cli/registry-y1y8e94r.js +2 -0
- package/dist/cli/repl-t4z03mqq.js +11 -0
- package/dist/cli/resume-fqt4chg8.js +2 -0
- package/dist/cli/run-flags-rysbag9t.js +2 -0
- package/dist/cli/runtime-j19fjbsa.js +2 -0
- package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
- package/dist/cli/server-r0b6bksk.js +5 -0
- package/dist/cli/session-arg-txmn5g4x.js +2 -0
- package/dist/cli/session-ed250d9j.js +2 -0
- package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
- package/dist/cli/settings-y9rzcqx8.js +2 -0
- package/dist/cli/setup-jmbr11j0.js +2 -0
- package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
- package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
- package/dist/cli/smoke-1bg937kx.js +8 -0
- package/dist/cli/start-chat-p01cdks3.js +12 -0
- package/dist/cli/stream-4wmyaypz.js +2 -0
- package/dist/cli/task-eg4s093s.js +2 -0
- package/dist/cli/tasks-12v9rr9k.js +2 -0
- package/dist/cli/thinking-a5ngvqyh.js +2 -0
- package/dist/cli/todo-1wxpcecx.js +2 -0
- package/dist/cli/tools-2ftsya7w.js +2 -0
- package/dist/cli/tools-x1tj4fxm.js +2 -0
- package/dist/cli/trust-cmd-hccxehzb.js +2 -0
- package/dist/cli/update-check-ygt3vd7m.js +2 -0
- package/dist/cli/update-cmd-v23qhr8c.js +2 -0
- package/dist/cli/voice-g1gtck92.js +2 -0
- package/dist/cli/webfetch-0nnrjgb5.js +2 -0
- package/dist/cli/websearch-f0vr2p7d.js +2 -0
- package/dist/cli/workspace-9rq1w4ta.js +2 -0
- package/dist/lib/index.js +62 -0
- package/dist/lib/models-index.json +1 -0
- package/dist/lib/plugins.js +6 -0
- package/dist/lib/providers.js +17 -0
- package/dist/lib/public-api.js +20 -0
- package/dist/rovecode.exe +4 -0
- package/package.json +83 -72
- package/bin/rovecode.ts +0 -21
- package/src/acp/server.ts +0 -374
- package/src/cli/auth-login.ts +0 -122
- package/src/cli/connect.ts +0 -244
- package/src/cli/context-cmd.ts +0 -199
- package/src/cli/dispatch.ts +0 -82
- package/src/cli/doctor.ts +0 -362
- package/src/cli/export.ts +0 -276
- package/src/cli/help.ts +0 -293
- package/src/cli/is-tui-invocation.ts +0 -8
- package/src/cli/main.ts +0 -583
- package/src/cli/market-cmd.ts +0 -658
- package/src/cli/mcp-login.ts +0 -141
- package/src/cli/mcp-market-cmd.ts +0 -302
- package/src/cli/output.ts +0 -382
- package/src/cli/repl.ts +0 -250
- package/src/cli/repomap-root.ts +0 -14
- package/src/cli/resume.ts +0 -57
- package/src/cli/run-flags.ts +0 -43
- package/src/cli/run-limits.ts +0 -78
- package/src/cli/runtime.ts +0 -931
- package/src/cli/session-arg.ts +0 -30
- package/src/cli/sessions-cmd.ts +0 -145
- package/src/cli/setup.ts +0 -153
- package/src/cli/skills-cmd.ts +0 -194
- package/src/cli/start-chat.ts +0 -65
- package/src/cli/trust-cmd.ts +0 -52
- package/src/coding/bash.ts +0 -148
- package/src/coding/checkpoints.ts +0 -327
- package/src/coding/diff.ts +0 -138
- package/src/coding/files.ts +0 -341
- package/src/coding/hashline.ts +0 -274
- package/src/coding/lsp-gate.ts +0 -254
- package/src/coding/lsp-servers.ts +0 -147
- package/src/coding/lsp.ts +0 -283
- package/src/coding/repomap-cache.ts +0 -99
- package/src/coding/repomap-files.ts +0 -192
- package/src/coding/repomap.ts +0 -481
- package/src/core/agents.ts +0 -255
- package/src/core/compaction.ts +0 -259
- package/src/core/config.ts +0 -289
- package/src/core/context-report.ts +0 -228
- package/src/core/context.ts +0 -60
- package/src/core/count-remote.ts +0 -107
- package/src/core/execpolicy-rules.ts +0 -196
- package/src/core/execpolicy.ts +0 -385
- package/src/core/executor.ts +0 -454
- package/src/core/guardrails.ts +0 -400
- package/src/core/hooks.ts +0 -411
- package/src/core/images.ts +0 -230
- package/src/core/intro.ts +0 -266
- package/src/core/loop.ts +0 -567
- package/src/core/modes.ts +0 -372
- package/src/core/orchestrator.ts +0 -245
- package/src/core/proc-group.ts +0 -48
- package/src/core/project-trust.ts +0 -98
- package/src/core/reflection.ts +0 -165
- package/src/core/sandbox-config.ts +0 -186
- package/src/core/session-id.ts +0 -24
- package/src/core/session-images.ts +0 -73
- package/src/core/session-ops.ts +0 -183
- package/src/core/session-text.ts +0 -29
- package/src/core/session.ts +0 -469
- package/src/core/settings.ts +0 -170
- package/src/core/tasks.ts +0 -646
- package/src/core/token-scale.ts +0 -108
- package/src/core/tools.ts +0 -309
- package/src/core/trust.ts +0 -104
- package/src/core/types.ts +0 -330
- package/src/core/update-check.ts +0 -171
- package/src/core/usage.ts +0 -204
- package/src/core/validate.ts +0 -121
- package/src/core/verify-gate.ts +0 -159
- package/src/core/verify.ts +0 -236
- package/src/core/voice.ts +0 -158
- package/src/core/win-job.ts +0 -183
- package/src/core/workspace.ts +0 -184
- package/src/design/audit.ts +0 -797
- package/src/design/direction.ts +0 -190
- package/src/design/rules.ts +0 -157
- package/src/eval/bench.ts +0 -150
- package/src/eval/gauntlet-runner.ts +0 -215
- package/src/eval/gauntlet-support.ts +0 -84
- package/src/eval/gauntlet-wave3.ts +0 -269
- package/src/eval/gauntlet-wave4.ts +0 -217
- package/src/eval/gauntlet.ts +0 -253
- package/src/index.ts +0 -17
- package/src/lanes/agy.ts +0 -95
- package/src/lanes/approval.ts +0 -24
- package/src/lanes/claude.ts +0 -129
- package/src/lanes/codex.ts +0 -127
- package/src/lanes/events.ts +0 -130
- package/src/lanes/job.ts +0 -142
- package/src/lanes/opencode.ts +0 -122
- package/src/lanes/process.ts +0 -184
- package/src/lanes/progress.ts +0 -183
- package/src/lanes/registry.ts +0 -178
- package/src/lanes/runner.ts +0 -124
- package/src/lanes/types.ts +0 -112
- package/src/market/catalogs/mcp-docs.json +0 -111
- package/src/market/catalogs/plugins.json +0 -111
- package/src/market/catalogs/skills.json +0 -478
- package/src/market/clone.ts +0 -72
- package/src/market/context-cost.ts +0 -121
- package/src/market/digest.ts +0 -106
- package/src/market/index.ts +0 -22
- package/src/market/install.ts +0 -578
- package/src/market/manifest.ts +0 -187
- package/src/market/prereq.ts +0 -145
- package/src/market/registry.ts +0 -363
- package/src/market/resolve.ts +0 -111
- package/src/market/types.ts +0 -236
- package/src/market/validate.ts +0 -227
- package/src/mcp/client.ts +0 -449
- package/src/mcp/config.ts +0 -252
- package/src/mcp/local-package.ts +0 -211
- package/src/mcp/market-catalog.ts +0 -84
- package/src/mcp/market-install.ts +0 -289
- package/src/mcp/market.ts +0 -362
- package/src/mcp/oauth.ts +0 -251
- package/src/mcp/prompts-resources.ts +0 -249
- package/src/mcp/shared.ts +0 -149
- package/src/mcp/status.ts +0 -67
- package/src/mcp/tools.ts +0 -275
- package/src/mcp/transport.ts +0 -122
- package/src/mcp/trust.ts +0 -25
- package/src/memory/blocks.ts +0 -278
- package/src/memory/recall.ts +0 -355
- package/src/memory/scope.ts +0 -182
- package/src/memory/store.ts +0 -105
- package/src/memory/tools.ts +0 -99
- package/src/plugins/cli.ts +0 -119
- package/src/plugins/discover.ts +0 -108
- package/src/plugins/index.ts +0 -50
- package/src/plugins/install.ts +0 -184
- package/src/plugins/load.ts +0 -124
- package/src/plugins/manifest.ts +0 -92
- package/src/plugins/state.ts +0 -83
- package/src/providers/auth.ts +0 -408
- package/src/providers/cache.ts +0 -223
- package/src/providers/catalog-local.ts +0 -160
- package/src/providers/catalog.ts +0 -421
- package/src/providers/middleware-context.ts +0 -86
- package/src/providers/middleware.ts +0 -373
- package/src/providers/model-list.ts +0 -23
- package/src/providers/oauth/common.ts +0 -105
- package/src/providers/oauth/device-code.ts +0 -107
- package/src/providers/oauth/github-copilot.ts +0 -146
- package/src/providers/oauth/loopback.ts +0 -158
- package/src/providers/oauth/openai.ts +0 -163
- package/src/providers/oauth/openrouter.ts +0 -89
- package/src/providers/oauth/pkce.ts +0 -45
- package/src/providers/oauth/registry.ts +0 -39
- package/src/providers/oauth/seam.ts +0 -89
- package/src/providers/profile-glm53.ts +0 -111
- package/src/providers/profile-sonnet5-persona.ts +0 -65
- package/src/providers/profile-sonnet5-voice.ts +0 -23
- package/src/providers/profiles.ts +0 -156
- package/src/providers/provider-config.ts +0 -311
- package/src/providers/registry.ts +0 -333
- package/src/providers/responses.ts +0 -209
- package/src/providers/retry.ts +0 -234
- package/src/providers/router.ts +0 -294
- package/src/providers/sse.ts +0 -26
- package/src/providers/stream-errors.ts +0 -117
- package/src/providers/stream.ts +0 -566
- package/src/providers/thinking.ts +0 -189
- package/src/providers/wire-messages.ts +0 -129
- package/src/providers/wire-responses.ts +0 -79
- package/src/providers/wire-select.ts +0 -53
- package/src/server/http.ts +0 -291
- package/src/server/openapi.ts +0 -246
- package/src/sextant/card-hits.ts +0 -102
- package/src/sextant/card-keys.ts +0 -55
- package/src/sextant/context-source.ts +0 -157
- package/src/sextant/crew-cards.ts +0 -350
- package/src/sextant/draw-agents.ts +0 -273
- package/src/sextant/draw-code.ts +0 -388
- package/src/sextant/draw-context.ts +0 -222
- package/src/sextant/draw-frame.ts +0 -164
- package/src/sextant/draw-market.ts +0 -573
- package/src/sextant/draw-messages.ts +0 -386
- package/src/sextant/draw-pet.ts +0 -230
- package/src/sextant/draw-plan.ts +0 -187
- package/src/sextant/draw-tabs.ts +0 -85
- package/src/sextant/draw-util.ts +0 -65
- package/src/sextant/draw-wizard.ts +0 -378
- package/src/sextant/engine.ts +0 -230
- package/src/sextant/frame-hits.ts +0 -25
- package/src/sextant/frame.ts +0 -101
- package/src/sextant/git-status.ts +0 -197
- package/src/sextant/grid.ts +0 -59
- package/src/sextant/input.ts +0 -119
- package/src/sextant/keys.ts +0 -521
- package/src/sextant/layout.ts +0 -86
- package/src/sextant/local-commands.ts +0 -169
- package/src/sextant/market-source.ts +0 -287
- package/src/sextant/mentions.ts +0 -200
- package/src/sextant/message-hits.ts +0 -26
- package/src/sextant/model.ts +0 -387
- package/src/sextant/overlays.ts +0 -456
- package/src/sextant/panel-hits.ts +0 -38
- package/src/sextant/pet.ts +0 -399
- package/src/sextant/screen.ts +0 -324
- package/src/sextant/scroll-hits.ts +0 -66
- package/src/sextant/scrollbar.ts +0 -82
- package/src/sextant/sextant-bridge.ts +0 -174
- package/src/sextant/sextant-cards.ts +0 -142
- package/src/sextant/sextant-diff-base.ts +0 -63
- package/src/sextant/sextant-files.ts +0 -154
- package/src/sextant/sextant-frame-loop.ts +0 -335
- package/src/sextant/sextant-renderer.ts +0 -574
- package/src/sextant/sextant-repo.ts +0 -140
- package/src/sextant/theme.ts +0 -66
- package/src/sextant/tool-rows.ts +0 -189
- package/src/sextant/types.ts +0 -493
- package/src/skills/index.ts +0 -387
- package/src/skills/pack.ts +0 -220
- package/src/skills/spec.ts +0 -162
- package/src/skills/tools.ts +0 -69
- package/src/skills/versioned.ts +0 -227
- package/src/telemetry/otel-export.ts +0 -122
- package/src/telemetry/otel-lanes.ts +0 -89
- package/src/telemetry/otel-logs.ts +0 -131
- package/src/telemetry/otel-metrics.ts +0 -136
- package/src/telemetry/otel.ts +0 -397
- package/src/telemetry/otlp.ts +0 -76
- package/src/tools/ask-user.ts +0 -156
- package/src/tools/bash-bg.ts +0 -94
- package/src/tools/bash-jobs.ts +0 -237
- package/src/tools/design.ts +0 -151
- package/src/tools/evalcell.ts +0 -338
- package/src/tools/html-text.ts +0 -139
- package/src/tools/provider.ts +0 -149
- package/src/tools/task.ts +0 -250
- package/src/tools/todo.ts +0 -320
- package/src/tools/webfetch.ts +0 -332
- package/src/tools/websearch.ts +0 -359
- package/src/tui/agents-cmd.ts +0 -41
- package/src/tui/app.ts +0 -749
- package/src/tui/attach.ts +0 -127
- package/src/tui/boot-notes.ts +0 -41
- package/src/tui/builtin-prompts.ts +0 -59
- package/src/tui/checkpoints-cmd.ts +0 -70
- package/src/tui/clipboard-image.ts +0 -81
- package/src/tui/clipboard.ts +0 -78
- package/src/tui/commands.ts +0 -283
- package/src/tui/config-view.ts +0 -53
- package/src/tui/context-cmds.ts +0 -282
- package/src/tui/cost.ts +0 -108
- package/src/tui/crash-guard.ts +0 -173
- package/src/tui/focus-terminal.ts +0 -34
- package/src/tui/git-cmds.ts +0 -273
- package/src/tui/git-plain.ts +0 -58
- package/src/tui/info-cmd.ts +0 -150
- package/src/tui/input-plain.ts +0 -76
- package/src/tui/mcp-cmd.ts +0 -128
- package/src/tui/memory-note.ts +0 -77
- package/src/tui/modes-cmd.ts +0 -45
- package/src/tui/notify-seq.ts +0 -100
- package/src/tui/notify.ts +0 -318
- package/src/tui/overlays.ts +0 -97
- package/src/tui/pi-renderer.ts +0 -428
- package/src/tui/providers-cmd.ts +0 -377
- package/src/tui/reasoning-view.ts +0 -56
- package/src/tui/renderer.ts +0 -128
- package/src/tui/replay-marker.ts +0 -29
- package/src/tui/session-cmd.ts +0 -148
- package/src/tui/session-manage.ts +0 -95
- package/src/tui/sextant-attach.ts +0 -102
- package/src/tui/sextant-io.ts +0 -202
- package/src/tui/sextant-smoke.ts +0 -110
- package/src/tui/shell-cmd.ts +0 -158
- package/src/tui/smoke.ts +0 -72
- package/src/tui/staged-terminal.ts +0 -50
- package/src/tui/startup.ts +0 -12
- package/src/tui/theme.ts +0 -59
- package/src/tui/todo-label.ts +0 -7
- package/src/tui/trust-card.ts +0 -107
- package/src/tui/tui-commands.ts +0 -87
- package/tsconfig.json +0 -30
- package/vendor/pi-tui/LICENSE +0 -21
- package/vendor/pi-tui/PATCHES.md +0 -12
- package/vendor/pi-tui/PROVENANCE.md +0 -12
- package/vendor/pi-tui/README.upstream.md +0 -854
- package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
- package/vendor/pi-tui/src/autocomplete.ts +0 -827
- package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
- package/vendor/pi-tui/src/components/box.ts +0 -138
- package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
- package/vendor/pi-tui/src/components/editor.ts +0 -2364
- package/vendor/pi-tui/src/components/h-stack.ts +0 -45
- package/vendor/pi-tui/src/components/image.ts +0 -128
- package/vendor/pi-tui/src/components/input.ts +0 -448
- package/vendor/pi-tui/src/components/loader.ts +0 -93
- package/vendor/pi-tui/src/components/markdown.ts +0 -1016
- package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
- package/vendor/pi-tui/src/components/select-list.ts +0 -230
- package/vendor/pi-tui/src/components/settings-list.ts +0 -277
- package/vendor/pi-tui/src/components/spacer.ts +0 -29
- package/vendor/pi-tui/src/components/stack.ts +0 -155
- package/vendor/pi-tui/src/components/text.ts +0 -108
- package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
- package/vendor/pi-tui/src/components/v-stack.ts +0 -34
- package/vendor/pi-tui/src/editor-component.ts +0 -75
- package/vendor/pi-tui/src/fuzzy.ts +0 -138
- package/vendor/pi-tui/src/index.ts +0 -149
- package/vendor/pi-tui/src/keybindings.ts +0 -321
- package/vendor/pi-tui/src/keys.ts +0 -1402
- package/vendor/pi-tui/src/kill-ring.ts +0 -47
- package/vendor/pi-tui/src/latex.ts +0 -1381
- package/vendor/pi-tui/src/layout-node.ts +0 -52
- package/vendor/pi-tui/src/layout.ts +0 -411
- package/vendor/pi-tui/src/native-modifiers.ts +0 -60
- package/vendor/pi-tui/src/native-module-path.ts +0 -32
- package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
- package/vendor/pi-tui/src/terminal-colors.ts +0 -74
- package/vendor/pi-tui/src/terminal-image.ts +0 -701
- package/vendor/pi-tui/src/terminal.ts +0 -554
- package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
- package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
- package/vendor/pi-tui/src/tui.ts +0 -1264
- package/vendor/pi-tui/src/undo-stack.ts +0 -29
- package/vendor/pi-tui/src/utils.ts +0 -1327
- package/vendor/pi-tui/src/word-navigation.ts +0 -118
- package/vendor/pi-tui/test/test-themes.ts +0 -39
- package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
- /package/{src/providers → dist/cli}/models-index.json +0 -0
package/src/skills/spec.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/** agentskills.io-compatible SKILL.md frontmatter (port #72): the spec field set, a lenient | strict validator and
|
|
2
|
-
* the skill-specific frontmatter walker. Pattern sources (no code copied): pi packages/coding-agent/src/core/skills.ts
|
|
3
|
-
* (MIT — validateName :92-115, validateDescription :117-127, warn-and-load :276-345, name → parent-dir fallback :319),
|
|
4
|
-
* opencode-2026 skill/index.ts:53-59 (MIT — type-only checks; unknown fields ignored, skills.mdx:45), hermes
|
|
5
|
-
* agent/skill_utils.py:1182-1198 (MIT — the 60-char cut was a PROMPT truncation, never a load rule). The field set
|
|
6
|
-
* and limits follow the agentskills.io specification (verified 2026-09-04): name ≤ 64, lower-case [a-z0-9] joined by
|
|
7
|
-
* single hyphens, equal to the parent directory; description 1–1024; compatibility ≤ 500; metadata string → string;
|
|
8
|
-
* allowed-tools one space-separated string (experimental — parsed as DATA, never enforced). The spec defines NO
|
|
9
|
-
* archive format and says nothing about unknown keys: rovecode warns once and loads (`version` is its own known key).
|
|
10
|
-
*
|
|
11
|
-
* Why a second walker beside index.ts parseFrontmatter: that helper drops every colon-less line — core/agents.ts
|
|
12
|
-
* checkFrontmatterLines and tui/commands.ts depend on exactly that, so it stays byte-identical — and therefore
|
|
13
|
-
* cannot carry an indented `metadata:` map or a `>` / `|` block-scalar description. parseSkillFrontmatter reads the
|
|
14
|
-
* flat `key: value` files the old loader read the same way (same quote strip, same body slice) and additionally:
|
|
15
|
-
* nested pairs under `metadata:` become the metadata map; indented lines under any other key are continuation text
|
|
16
|
-
* joined by ONE space (`>`, `>-`, `|` scalars read as one line); a colon inside an unquoted value is kept; BOM and
|
|
17
|
-
* CRLF tolerated; a key whose children are `- item` lines is recorded as a list (allowed-tools must be a string). */
|
|
18
|
-
|
|
19
|
-
export const NAME_MAX = 64;
|
|
20
|
-
export const NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
21
|
-
export const DESCRIPTION_MAX = 1024;
|
|
22
|
-
export const COMPATIBILITY_MAX = 500;
|
|
23
|
-
/** the spec's six fields + rovecode's own `version` (never warned about) */
|
|
24
|
-
export const KNOWN_KEYS: ReadonlySet<string> = new Set(["name", "description", "license", "compatibility", "metadata", "allowed-tools", "version"]);
|
|
25
|
-
|
|
26
|
-
export type FieldShape = "scalar" | "list" | "map";
|
|
27
|
-
export type ValidateMode = "lenient" | "strict";
|
|
28
|
-
|
|
29
|
-
export interface SkillFrontmatter {
|
|
30
|
-
/** top-level values: inline text, or continuation lines joined by one space; quotes stripped */
|
|
31
|
-
fields: Record<string, string>;
|
|
32
|
-
/** how every top-level key was written (metadata is a map when it has indented children) */
|
|
33
|
-
shapes: Record<string, FieldShape>;
|
|
34
|
-
/** the `metadata:` map, string values only */
|
|
35
|
-
metadata: Record<string, string>;
|
|
36
|
-
/** metadata keys whose value was a nested map, a list or empty — not a string */
|
|
37
|
-
metadataNonString: string[];
|
|
38
|
-
body: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** the validated field set of one SKILL.md */
|
|
42
|
-
export interface SkillMeta {
|
|
43
|
-
name: string;
|
|
44
|
-
description: string;
|
|
45
|
-
/** top-level `version`, else `metadata.version`, else "" */
|
|
46
|
-
version: string;
|
|
47
|
-
license?: string;
|
|
48
|
-
compatibility?: string;
|
|
49
|
-
metadata: Record<string, string>;
|
|
50
|
-
/** `allowed-tools` whitespace-split — data only */
|
|
51
|
-
allowedTools: string[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface Validation {
|
|
55
|
-
meta: SkillMeta;
|
|
56
|
-
/** the file must not load (lenient) / the check fails (strict) */
|
|
57
|
-
errors: string[];
|
|
58
|
-
/** lenient mode: loaded anyway, reported */
|
|
59
|
-
warnings: string[];
|
|
60
|
-
/** strict mode: unknown keys — printed, never a failure */
|
|
61
|
-
notes: string[];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const BLOCK_SCALAR = /^[>|][+-]?$/;
|
|
65
|
-
/** `key: value` where the key is identifier-shaped — a nested-map line, not a sentence holding a colon */
|
|
66
|
-
const MAP_LINE = /^[A-Za-z0-9_-]+:(\s|$)/;
|
|
67
|
-
const unquote = (v: string): string => v.trim().replace(/^["']|["']$/g, "");
|
|
68
|
-
|
|
69
|
-
/** null when the text has no `---` block or the block is unterminated (the two INVALID shapes). */
|
|
70
|
-
export function parseSkillFrontmatter(raw: string): SkillFrontmatter | null {
|
|
71
|
-
const text = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
|
|
72
|
-
const firstEnd = text.indexOf("\n");
|
|
73
|
-
if (firstEnd === -1 || text.slice(0, firstEnd).replace(/\r$/, "") !== "---") return null;
|
|
74
|
-
const close = /\r?\n---[ \t]*(?=\r?\n|$)/g;
|
|
75
|
-
close.lastIndex = firstEnd;
|
|
76
|
-
const m = close.exec(text);
|
|
77
|
-
if (m === null) return null;
|
|
78
|
-
const block = text.slice(firstEnd + 1, m.index);
|
|
79
|
-
// past the closing `---` line, then one blank separator line — the parseFrontmatter body slice
|
|
80
|
-
const body = text.slice(m.index + m[0].length).replace(/^\r?\n/, "").replace(/^\r?\n/, "");
|
|
81
|
-
|
|
82
|
-
const fields: Record<string, string> = {};
|
|
83
|
-
const shapes: Record<string, FieldShape> = {};
|
|
84
|
-
const metadata: Record<string, string> = {};
|
|
85
|
-
const metadataNonString: string[] = [];
|
|
86
|
-
const markNonString = (k: string): void => { delete metadata[k]; if (!metadataNonString.includes(k)) metadataNonString.push(k); };
|
|
87
|
-
let key: string | undefined; // the open top-level key
|
|
88
|
-
let blockScalar = false;
|
|
89
|
-
let metaKey: string | undefined; // the open metadata sub-key
|
|
90
|
-
let metaIndent = -1;
|
|
91
|
-
for (const line of block.split(/\r?\n/)) {
|
|
92
|
-
const t = line.trim();
|
|
93
|
-
if (t === "" || t.startsWith("#")) continue;
|
|
94
|
-
const indent = line.length - line.trimStart().length;
|
|
95
|
-
if (indent === 0) {
|
|
96
|
-
const i = line.indexOf(":");
|
|
97
|
-
if (i <= 0) { key = undefined; continue; } // not a `key: value` line — skipped, as parseFrontmatter skips it
|
|
98
|
-
key = line.slice(0, i).trim();
|
|
99
|
-
const rest = line.slice(i + 1).trim();
|
|
100
|
-
metaKey = undefined; metaIndent = -1;
|
|
101
|
-
blockScalar = BLOCK_SCALAR.test(rest);
|
|
102
|
-
if (key === "metadata") { shapes[key] = rest === "" ? "map" : "scalar"; if (rest !== "") fields[key] = unquote(rest); continue; }
|
|
103
|
-
shapes[key] = "scalar";
|
|
104
|
-
fields[key] = blockScalar ? "" : unquote(rest);
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (key === undefined) continue;
|
|
108
|
-
if (key === "metadata" && shapes[key] === "map") {
|
|
109
|
-
if (metaKey !== undefined && indent > metaIndent) { markNonString(metaKey); continue; } // nested map / list under a sub-key
|
|
110
|
-
const i = t.indexOf(":");
|
|
111
|
-
if (t.startsWith("- ") || i <= 0) { shapes[key] = "list"; continue; } // `metadata:` holding a list
|
|
112
|
-
metaKey = t.slice(0, i).trim(); metaIndent = indent;
|
|
113
|
-
const v = t.slice(i + 1).trim();
|
|
114
|
-
if (v === "") markNonString(metaKey); else metadata[metaKey] = unquote(v);
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
const cont = fields[key] ?? "";
|
|
118
|
-
if (!blockScalar && shapes[key] === "scalar" && cont === "") {
|
|
119
|
-
if (t.startsWith("- ")) shapes[key] = "list"; else if (MAP_LINE.test(t)) shapes[key] = "map";
|
|
120
|
-
}
|
|
121
|
-
fields[key] = cont === "" ? t : `${cont} ${t}`;
|
|
122
|
-
}
|
|
123
|
-
return { fields, shapes, metadata, metadataNonString, body };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** The spec checks. Lenient (the loader): an empty or missing description is the ONE error, everything else a
|
|
127
|
-
* warning and the file loads (a missing name falls back to the directory name — pi:319). Strict (`validate`,
|
|
128
|
-
* `pack`): every spec violation is an error; unknown keys are notes in both modes' spirit — never a failure. */
|
|
129
|
-
export function validateSkillMeta(fm: SkillFrontmatter, dirBasename: string, mode: ValidateMode): Validation {
|
|
130
|
-
const errors: string[] = [], warnings: string[] = [], notes: string[] = [];
|
|
131
|
-
const flag = (msg: string): void => { (mode === "strict" ? errors : warnings).push(msg); };
|
|
132
|
-
const f = fm.fields;
|
|
133
|
-
const description = f["description"] ?? "";
|
|
134
|
-
if (description === "") errors.push(`description is required (1–${DESCRIPTION_MAX} characters)`);
|
|
135
|
-
else if (description.length > DESCRIPTION_MAX) flag(`description is ${description.length} characters (max ${DESCRIPTION_MAX})`);
|
|
136
|
-
let name = f["name"] ?? "";
|
|
137
|
-
if (name === "") {
|
|
138
|
-
if (mode === "strict") errors.push("name is required");
|
|
139
|
-
else warnings.push(`name missing — using the directory name "${dirBasename}"`);
|
|
140
|
-
name = dirBasename;
|
|
141
|
-
} else {
|
|
142
|
-
if (name.length > NAME_MAX) flag(`name is ${name.length} characters (max ${NAME_MAX})`);
|
|
143
|
-
if (!NAME_RE.test(name)) flag(`name "${name}" must be lower-case letters, digits and single hyphens (no leading, trailing or double hyphen)`);
|
|
144
|
-
if (name !== dirBasename) flag(`name "${name}" does not match its directory "${dirBasename}"`);
|
|
145
|
-
}
|
|
146
|
-
const compatibility = f["compatibility"] || undefined;
|
|
147
|
-
if (compatibility !== undefined && compatibility.length > COMPATIBILITY_MAX) flag(`compatibility is ${compatibility.length} characters (max ${COMPATIBILITY_MAX})`);
|
|
148
|
-
const metaShape = fm.shapes["metadata"];
|
|
149
|
-
if (metaShape !== undefined && metaShape !== "map") flag("metadata must be a map of string keys to string values");
|
|
150
|
-
if (fm.metadataNonString.length > 0) flag(`metadata values must be strings: ${fm.metadataNonString.join(", ")}`);
|
|
151
|
-
const toolsShape = fm.shapes["allowed-tools"];
|
|
152
|
-
if (toolsShape !== undefined && toolsShape !== "scalar") flag("allowed-tools must be one space-separated string");
|
|
153
|
-
const unknown = Object.keys(fm.shapes).filter((k) => !KNOWN_KEYS.has(k));
|
|
154
|
-
if (unknown.length > 0) (mode === "strict" ? notes : warnings).push(`unknown frontmatter keys: ${unknown.join(", ")}`);
|
|
155
|
-
const version = f["version"] || fm.metadata["version"] || "";
|
|
156
|
-
const license = f["license"] || undefined;
|
|
157
|
-
const allowedTools = (f["allowed-tools"] ?? "").split(/\s+/).filter((t) => t !== "");
|
|
158
|
-
const meta: SkillMeta = { name, description, version, metadata: fm.metadata, allowedTools };
|
|
159
|
-
if (license !== undefined) meta.license = license;
|
|
160
|
-
if (compatibility !== undefined) meta.compatibility = compatibility;
|
|
161
|
-
return { meta, errors, warnings, notes };
|
|
162
|
-
}
|
package/src/skills/tools.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/** Skill tools (hermes pattern): `skill_view` returns a body and bumps the
|
|
2
|
-
* usage sidecar; `skills_list` returns the index. Above 50 skills the index
|
|
3
|
-
* is too big for the system prompt, so `skills_list` is the only access path
|
|
4
|
-
* and `buildSkillsIndex` returns "" (prompt_builder.py:2083 reject rule). */
|
|
5
|
-
|
|
6
|
-
import type { Tool, ToolContext, ToolOutput } from "../core/types.ts";
|
|
7
|
-
import { SkillStore, skillLifecycle, type Skill, type SkillUsage } from "./index.ts";
|
|
8
|
-
|
|
9
|
-
export const INDEX_PROMPT_LIMIT = 50; // hermes: >50 skills → skills_list tool only
|
|
10
|
-
|
|
11
|
-
function renderIndexLine(s: Skill, usage: SkillUsage | undefined, now: number): string {
|
|
12
|
-
const state = skillLifecycle(s, usage, now);
|
|
13
|
-
return `- ${s.name}${s.version ? ` (v${s.version})` : ""}: ${s.description}${state === "stale" ? " [stale]" : ""}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** System-prompt index. Empty string when the catalogue outgrows the prompt. */
|
|
17
|
-
export function buildSkillsIndex(store: SkillStore, now: number = Date.now()): string {
|
|
18
|
-
const skills = store.list();
|
|
19
|
-
if (skills.length === 0 || skills.length > INDEX_PROMPT_LIMIT) return "";
|
|
20
|
-
return skills.map((s) => renderIndexLine(s, store.usage(s.name), now)).join("\n");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function createSkillTools(store: SkillStore): Tool[] {
|
|
24
|
-
const skillView: Tool = {
|
|
25
|
-
schema: {
|
|
26
|
-
name: "skill_view",
|
|
27
|
-
description:
|
|
28
|
-
"Read one skill's full instructions. Use when a skill in the index matches the task.",
|
|
29
|
-
args: {
|
|
30
|
-
type: "object",
|
|
31
|
-
properties: { name: { type: "string", description: "skill name from the index" } },
|
|
32
|
-
required: ["name"],
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
kind: "read",
|
|
36
|
-
sequential: true, // sidecar write
|
|
37
|
-
execute(args): Promise<ToolOutput> {
|
|
38
|
-
const raw = args as { name?: unknown };
|
|
39
|
-
const name = typeof raw.name === "string" ? raw.name : "";
|
|
40
|
-
const skill = store.get(name);
|
|
41
|
-
if (!skill) return Promise.resolve({ ok: false, output: `no skill named '${name}'` });
|
|
42
|
-
const usage = store.bumpUsage(skill);
|
|
43
|
-
// the FULL description, not the index's clipped line: the model opened this skill to read it, so
|
|
44
|
-
// the sentence saying when to use it belongs here in one piece
|
|
45
|
-
const header = `# ${skill.name} (v${skill.version})\n\n${skill.fullDescription}\n\n`;
|
|
46
|
-
return Promise.resolve({ ok: true, output: header + skill.body, data: usage });
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const skillsList: Tool = {
|
|
51
|
-
schema: {
|
|
52
|
-
name: "skills_list",
|
|
53
|
-
description:
|
|
54
|
-
"List available skills (name, version, description, staleness). Use to discover skills when the system prompt has no skill index.",
|
|
55
|
-
args: { type: "object", properties: {} },
|
|
56
|
-
},
|
|
57
|
-
kind: "read",
|
|
58
|
-
execute(_args: unknown, _ctx: ToolContext): Promise<ToolOutput> {
|
|
59
|
-
store.scan(); // fresh catalogue even when the caller forgot to rescan
|
|
60
|
-
const skills = store.list();
|
|
61
|
-
if (skills.length === 0) return Promise.resolve({ ok: true, output: "no skills installed" });
|
|
62
|
-
const now = Date.now();
|
|
63
|
-
const lines = skills.map((s) => renderIndexLine(s, store.usage(s.name), now));
|
|
64
|
-
return Promise.resolve({ ok: true, output: lines.join("\n"), data: { count: skills.length } });
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
return [skillView, skillsList];
|
|
69
|
-
}
|
package/src/skills/versioned.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/** Versioned edit ledger for skill/memory files (PORT #16, prime-agent MIT).
|
|
2
|
-
*
|
|
3
|
-
* Port of the refinement/versioned-state machinery in prime-agent
|
|
4
|
-
* packages/coding-agent/src/core/refinement/refinement.ts:
|
|
5
|
-
* - per-edit before/after snapshots + version bump (AppliedRefinementEdit:85,
|
|
6
|
-
* `version = before ? before.version + 1 : 1`:770, record push:788)
|
|
7
|
-
* - optimistic-concurrency baseline check → typed reject, no write (735-749)
|
|
8
|
-
* - rollback built from recorded before/after, itself a new history entry
|
|
9
|
-
* (rollbackProposal:813-845, rollbackOf:100)
|
|
10
|
-
* - append-only JSONL history beside the store; malformed lines skipped so a
|
|
11
|
-
* bad append can never break rollback (refinements.jsonl:25, 374-400)
|
|
12
|
-
*
|
|
13
|
-
* Deliberately NOT ported (BLUEPRINT §5 + prime's reward-hacking findings):
|
|
14
|
-
* no auto-refinement loop (prime's reviewAutoRefine LLM gate stays out), no
|
|
15
|
-
* LLM proposal pass, and no grader/eval surface anywhere in this API
|
|
16
|
-
* (ouroboros rule: graders stay hidden from agent-facing code). Edits happen
|
|
17
|
-
* only when a human or an explicit tool call invokes commit()/rollback().
|
|
18
|
-
*
|
|
19
|
-
* Storage: sidecar JSONL beside the target file — `<target>.versions.jsonl`
|
|
20
|
-
* (mirrors the house `SKILL.md.usage.json` sidecar). One line per edit:
|
|
21
|
-
* {version, baseVersion, before, after, reason, timestamp, rollbackTo?}.
|
|
22
|
-
* Every record carries FULL before/after content, so record v restores both
|
|
23
|
-
* content@v (`after`) and content@v-1 (`before`). With the newest K records
|
|
24
|
-
* retained, rollback covers versions [oldest.baseVersion .. newest.version].
|
|
25
|
-
* History is bounded: past `maxEdits` records the oldest are evicted, and
|
|
26
|
-
* versions older than the window become unreachable (typed reject).
|
|
27
|
-
* Versions are monotonic and never reused after eviction. */
|
|
28
|
-
|
|
29
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
30
|
-
import { dirname } from "node:path";
|
|
31
|
-
|
|
32
|
-
export const LEDGER_SUFFIX = ".versions.jsonl";
|
|
33
|
-
export const DEFAULT_MAX_EDITS = 20;
|
|
34
|
-
|
|
35
|
-
export interface VersionedEdit {
|
|
36
|
-
/** version this edit produced (monotonic; = baseVersion + 1) */
|
|
37
|
-
version: number;
|
|
38
|
-
/** version the editor read before making the change */
|
|
39
|
-
baseVersion: number;
|
|
40
|
-
/** full target content before the edit (content at baseVersion) */
|
|
41
|
-
before: string;
|
|
42
|
-
/** full target content after the edit (content at version) */
|
|
43
|
-
after: string;
|
|
44
|
-
reason: string;
|
|
45
|
-
/** ms epoch */
|
|
46
|
-
timestamp: number;
|
|
47
|
-
/** set when this edit was produced by rollback(): the restored version */
|
|
48
|
-
rollbackTo?: number;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Inclusive range of versions rollback can currently restore. */
|
|
52
|
-
export interface VersionRange { from: number; to: number }
|
|
53
|
-
|
|
54
|
-
export type CommitResult =
|
|
55
|
-
| { ok: true; edit: VersionedEdit }
|
|
56
|
-
| { ok: false; code: "version-conflict"; baseVersion: number; currentVersion: number; message: string };
|
|
57
|
-
|
|
58
|
-
export type RollbackResult =
|
|
59
|
-
| { ok: true; edit: VersionedEdit }
|
|
60
|
-
| { ok: false; code: "unknown-version"; requested: number; available: VersionRange | null; message: string };
|
|
61
|
-
|
|
62
|
-
// ---------- internals ----------
|
|
63
|
-
|
|
64
|
-
/** Atomic write (house pattern, skills/index.ts writeUsageAtomic): tmp + rename. */
|
|
65
|
-
function atomicWrite(path: string, text: string): void {
|
|
66
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
67
|
-
const tmp = `${path}.${process.pid}.tmp`;
|
|
68
|
-
writeFileSync(tmp, text);
|
|
69
|
-
renameSync(tmp, path);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Parse one JSONL line into an edit, or null when malformed (prime: skip, never throw). */
|
|
73
|
-
function parseEdit(line: string): VersionedEdit | null {
|
|
74
|
-
let raw: unknown;
|
|
75
|
-
try {
|
|
76
|
-
raw = JSON.parse(line);
|
|
77
|
-
} catch {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
81
|
-
const r = raw as Record<string, unknown>;
|
|
82
|
-
if (
|
|
83
|
-
typeof r["version"] !== "number" || typeof r["baseVersion"] !== "number" ||
|
|
84
|
-
typeof r["before"] !== "string" || typeof r["after"] !== "string" ||
|
|
85
|
-
typeof r["reason"] !== "string" || typeof r["timestamp"] !== "number"
|
|
86
|
-
) return null;
|
|
87
|
-
const edit: VersionedEdit = {
|
|
88
|
-
version: r["version"], baseVersion: r["baseVersion"],
|
|
89
|
-
before: r["before"], after: r["after"],
|
|
90
|
-
reason: r["reason"], timestamp: r["timestamp"],
|
|
91
|
-
};
|
|
92
|
-
if (typeof r["rollbackTo"] === "number") edit.rollbackTo = r["rollbackTo"];
|
|
93
|
-
return edit;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// ---------- ledger ----------
|
|
97
|
-
|
|
98
|
-
export interface VersionLedgerOptions {
|
|
99
|
-
/** sidecar path (default: `<target>${LEDGER_SUFFIX}`) */
|
|
100
|
-
ledgerPath?: string;
|
|
101
|
-
/** bounded snapshot count; oldest records evicted past this (default 20, min 1) */
|
|
102
|
-
maxEdits?: number;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export class VersionLedger {
|
|
106
|
-
readonly ledgerPath: string;
|
|
107
|
-
private readonly maxEdits: number;
|
|
108
|
-
|
|
109
|
-
constructor(readonly targetPath: string, opts: VersionLedgerOptions = {}) {
|
|
110
|
-
this.ledgerPath = opts.ledgerPath ?? targetPath + LEDGER_SUFFIX;
|
|
111
|
-
this.maxEdits = Math.max(1, opts.maxEdits ?? DEFAULT_MAX_EDITS);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** Retained edits, oldest → newest. Malformed lines are skipped; duplicate
|
|
115
|
-
* versions keep the last occurrence (append order wins). */
|
|
116
|
-
history(): VersionedEdit[] {
|
|
117
|
-
if (!existsSync(this.ledgerPath)) return [];
|
|
118
|
-
const byVersion = new Map<number, VersionedEdit>();
|
|
119
|
-
for (const line of readFileSync(this.ledgerPath, "utf8").split("\n")) {
|
|
120
|
-
const trimmed = line.trim();
|
|
121
|
-
if (!trimmed) continue;
|
|
122
|
-
const edit = parseEdit(trimmed);
|
|
123
|
-
if (edit) byVersion.set(edit.version, edit);
|
|
124
|
-
}
|
|
125
|
-
return [...byVersion.values()].sort((a, b) => a.version - b.version);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Current version: 0 until the first recorded edit; unaffected by eviction. */
|
|
129
|
-
version(): number {
|
|
130
|
-
return this.history().at(-1)?.version ?? 0;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** Current on-disk content of the target ("" when the file does not exist). */
|
|
134
|
-
read(): string {
|
|
135
|
-
return existsSync(this.targetPath) ? readFileSync(this.targetPath, "utf8") : "";
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Versions rollback can restore right now, or null before the first edit.
|
|
139
|
-
* Derived from the CONTIGUOUS suffix of retained records: a corruption hole
|
|
140
|
-
* (missing/garbled record) breaks before/after coverage, so a naive
|
|
141
|
-
* first..last span would advertise versions rollback cannot restore. */
|
|
142
|
-
range(): VersionRange | null {
|
|
143
|
-
const edits = this.history();
|
|
144
|
-
const last = edits.at(-1);
|
|
145
|
-
if (!last) return null;
|
|
146
|
-
let start = edits.length - 1;
|
|
147
|
-
while (start > 0 && edits[start - 1]!.version === edits[start]!.baseVersion) start--;
|
|
148
|
-
return { from: edits[start]!.baseVersion, to: last.version };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Content at a recorded version, or undefined when evicted/unknown. Besides a
|
|
152
|
-
* record's own `after`, any record with baseVersion === version restores it via
|
|
153
|
-
* `before` — recoverable even when the record that PRODUCED it was corrupted. */
|
|
154
|
-
contentAt(version: number): string | undefined {
|
|
155
|
-
const edits = this.history();
|
|
156
|
-
const direct = edits.find((e) => e.version === version);
|
|
157
|
-
if (direct) return direct.after;
|
|
158
|
-
return edits.find((e) => e.baseVersion === version)?.before;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** True when the target was modified outside the ledger since the last edit. */
|
|
162
|
-
drifted(): boolean {
|
|
163
|
-
const last = this.history().at(-1);
|
|
164
|
-
return last !== undefined && this.read() !== last.after;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/** Record an edit and write the target. Optimistic concurrency: `baseVersion`
|
|
168
|
-
* must equal the current version or the commit is rejected BEFORE any disk
|
|
169
|
-
* mutation — no partial write. `before` is read from disk here, so the
|
|
170
|
-
* record stays truthful even if the target drifted externally. */
|
|
171
|
-
commit(after: string, reason: string, baseVersion: number, now: number = Date.now()): CommitResult {
|
|
172
|
-
const currentVersion = this.version();
|
|
173
|
-
if (baseVersion !== currentVersion) {
|
|
174
|
-
return {
|
|
175
|
-
ok: false, code: "version-conflict", baseVersion, currentVersion,
|
|
176
|
-
message: `baseVersion ${baseVersion} does not match current version ${currentVersion}; re-read and retry`,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
return { ok: true, edit: this.write(after, reason, currentVersion, now) };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** One-call rollback to any retained version. Recorded as a NEW edit with
|
|
183
|
-
* `rollbackTo` (prime `rollbackOf` pattern) — history is append-only, and
|
|
184
|
-
* the restored content is byte-exact from the stored snapshot. */
|
|
185
|
-
rollback(toVersion: number, reason?: string, now: number = Date.now()): RollbackResult {
|
|
186
|
-
const content = this.contentAt(toVersion);
|
|
187
|
-
if (content === undefined) {
|
|
188
|
-
const available = this.range();
|
|
189
|
-
return {
|
|
190
|
-
ok: false, code: "unknown-version", requested: toVersion, available,
|
|
191
|
-
message: available
|
|
192
|
-
? `version ${toVersion} is not restorable; available: ${available.from}..${available.to}`
|
|
193
|
-
: `version ${toVersion} is not restorable; no recorded edits`,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
const edit = this.write(content, reason ?? `rollback to v${toVersion}`, this.version(), now, toVersion);
|
|
197
|
-
return { ok: true, edit };
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/** Append the record, evict past the bound, then atomically write the target.
|
|
201
|
-
* Ledger-first ordering: a crash between the two leaves an honest record
|
|
202
|
-
* (visible via drifted()) rather than an unrecorded content change. */
|
|
203
|
-
private write(after: string, reason: string, currentVersion: number, now: number, rollbackTo?: number): VersionedEdit {
|
|
204
|
-
const edit: VersionedEdit = {
|
|
205
|
-
version: currentVersion + 1, baseVersion: currentVersion,
|
|
206
|
-
before: this.read(), after, reason, timestamp: now,
|
|
207
|
-
...(rollbackTo !== undefined ? { rollbackTo } : {}),
|
|
208
|
-
};
|
|
209
|
-
mkdirSync(dirname(this.ledgerPath), { recursive: true });
|
|
210
|
-
// Heal a torn trailing append: a crash mid-append can leave the last line
|
|
211
|
-
// without "\n" — appending straight on would concatenate this record into the
|
|
212
|
-
// torn line, making the NEW commit invisible to history()/version().
|
|
213
|
-
let record = JSON.stringify(edit) + "\n";
|
|
214
|
-
if (existsSync(this.ledgerPath)) {
|
|
215
|
-
const raw = readFileSync(this.ledgerPath);
|
|
216
|
-
if (raw.length > 0 && raw[raw.length - 1] !== 0x0a) record = "\n" + record;
|
|
217
|
-
}
|
|
218
|
-
appendFileSync(this.ledgerPath, record, "utf8");
|
|
219
|
-
const edits = this.history();
|
|
220
|
-
if (edits.length > this.maxEdits) {
|
|
221
|
-
const kept = edits.slice(edits.length - this.maxEdits);
|
|
222
|
-
atomicWrite(this.ledgerPath, kept.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
223
|
-
}
|
|
224
|
-
atomicWrite(this.targetPath, after);
|
|
225
|
-
return edit;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/** OTLP/HTTP exporter pipe (port #82) — ONE exporter for the three signals the #39 hook set emits:
|
|
2
|
-
* traces (`<base>/v1/traces`), metrics (`<base>/v1/metrics`) and logs (`<base>/v1/logs`). The base is the
|
|
3
|
-
* configured ROVECODE_OTEL_ENDPOINT with a trailing `/v1/traces` stripped (so a full traces URL still yields
|
|
4
|
-
* its siblings), each POST rides the #39 REF'D timeout (Bun unrefs AbortSignal.timeout — hooks.ts idiom)
|
|
5
|
-
* and is tracked so flush() awaits every outstanding one; the export path never throws.
|
|
6
|
-
*
|
|
7
|
-
* Failure policy: 5xx / timeout / network stay per-export warnings with the #39 texts byte-identical
|
|
8
|
-
* (`OTLP export to <url> failed: HTTP 500` / `… timed out after 5000ms` / `… ECONNREFUSED`) and never
|
|
9
|
-
* disable anything. HTTP 404 / 405 / 501 on a signal mean the collector does not serve that path at all
|
|
10
|
-
* (an old collector without the logs receiver, a traces-only proxy): THAT signal is disabled for the life
|
|
11
|
-
* of this exporter (one per runtime: the process's one hook set on the CLI/TUI, one per session under
|
|
12
|
-
* `rovecode serve`) after ONE note — `OTLP collector <base> does not accept <signal> (HTTP <n>) — <signal>
|
|
13
|
-
* export disabled` — while the other signals continue. A malformed endpoint (`http://`, `host:4318`) makes
|
|
14
|
-
* the exporter unusable: no POST is ever attempted (the #39 one-note rule; otel.ts raises it).
|
|
15
|
-
*
|
|
16
|
-
* Moved verbatim out of otel.ts (post / track / flush / withTimeout / TIMED_OUT / errText / hex /
|
|
17
|
-
* normalizeEndpoint / validEndpoint) so that file stays under the 400-line cap; otel.ts re-exports the
|
|
18
|
-
* public names, so `import … from "telemetry/otel.ts"` is unchanged. OTLP/HTTP paths and JSON field names
|
|
19
|
-
* follow the public opentelemetry-proto spec (hand-encoded; no @opentelemetry dependency). */
|
|
20
|
-
|
|
21
|
-
import { randomBytes } from "node:crypto";
|
|
22
|
-
|
|
23
|
-
export const DEFAULT_EXPORT_TIMEOUT_MS = 5000;
|
|
24
|
-
export const OTLP_TRACES_PATH = "/v1/traces";
|
|
25
|
-
export type OtlpSignal = "traces" | "metrics" | "logs";
|
|
26
|
-
export const OTLP_SIGNALS: readonly OtlpSignal[] = ["traces", "metrics", "logs"];
|
|
27
|
-
/** HTTP statuses that say "this path is not served here" — the signal is switched off, not retried */
|
|
28
|
-
const NOT_SERVED: ReadonlySet<number> = new Set([404, 405, 501]);
|
|
29
|
-
|
|
30
|
-
/** base URL or full traces URL (either, with or without trailing slashes) → <base>/v1/traces */
|
|
31
|
-
export function normalizeEndpoint(endpoint: string): string {
|
|
32
|
-
const base = endpoint.trim().replace(/\/+$/, "");
|
|
33
|
-
return base.endsWith(OTLP_TRACES_PATH) ? base : base + OTLP_TRACES_PATH;
|
|
34
|
-
}
|
|
35
|
-
/** the collector base: the normalized traces URL minus its `/v1/traces` — the siblings hang off it */
|
|
36
|
-
export const baseOf = (endpoint: string): string => normalizeEndpoint(endpoint).slice(0, -OTLP_TRACES_PATH.length);
|
|
37
|
-
export const signalUrl = (endpoint: string, signal: OtlpSignal): string => `${baseOf(endpoint)}/v1/${signal}`;
|
|
38
|
-
|
|
39
|
-
/** an absolute http(s) URL with a host — `http://` does not parse and `host:4318` parses host-less,
|
|
40
|
-
* and both would normalize to `http:/v1/traces` (host "v1") and stall every export to the timeout */
|
|
41
|
-
export function validEndpoint(endpoint: string): boolean {
|
|
42
|
-
try { const u = new URL(endpoint.trim()); return (u.protocol === "http:" || u.protocol === "https:") && u.hostname !== ""; } catch { return false; }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface OtlpExporterOptions {
|
|
46
|
-
endpoint: string;
|
|
47
|
-
headers?: Record<string, string>;
|
|
48
|
-
fetch?: typeof fetch;
|
|
49
|
-
timeoutMs?: number;
|
|
50
|
-
/** every export note (per-export failures, the one-shot per-signal disable) */
|
|
51
|
-
warn: (note: string) => void;
|
|
52
|
-
}
|
|
53
|
-
export interface OtlpExporter {
|
|
54
|
-
readonly base: string;
|
|
55
|
-
/** false = malformed endpoint: nothing is ever POSTed (the caller says so once) */
|
|
56
|
-
readonly usable: boolean;
|
|
57
|
-
url(signal: OtlpSignal): string;
|
|
58
|
-
/** POST one encoded request; fire-and-forget, tracked for flush(); never throws */
|
|
59
|
-
post(signal: OtlpSignal, body: string): void;
|
|
60
|
-
/** await every outstanding POST (posts issued while waiting are awaited too) */
|
|
61
|
-
flush(): Promise<void>;
|
|
62
|
-
/** the signal was switched off by a 404/405/501 (diagnostic seam) */
|
|
63
|
-
disabled(signal: OtlpSignal): boolean;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function createOtlpExporter(opts: OtlpExporterOptions): OtlpExporter {
|
|
67
|
-
const base = baseOf(opts.endpoint);
|
|
68
|
-
const usable = validEndpoint(opts.endpoint);
|
|
69
|
-
const headers = { ...(opts.headers ?? {}), "content-type": "application/json" };
|
|
70
|
-
const fetchFn = opts.fetch ?? fetch;
|
|
71
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_EXPORT_TIMEOUT_MS;
|
|
72
|
-
const pending = new Set<Promise<void>>();
|
|
73
|
-
const off = new Set<OtlpSignal>();
|
|
74
|
-
const url = (signal: OtlpSignal): string => `${base}/v1/${signal}`;
|
|
75
|
-
const send = async (signal: OtlpSignal, body: string): Promise<void> => {
|
|
76
|
-
if (!usable || off.has(signal)) return;
|
|
77
|
-
const target = url(signal);
|
|
78
|
-
const fail = (reason: string): void => opts.warn(`OTLP export to ${target} failed: ${reason}`);
|
|
79
|
-
const ac = new AbortController();
|
|
80
|
-
try {
|
|
81
|
-
const res = await withTimeout(fetchFn(target, { method: "POST", headers, body, signal: ac.signal }), timeoutMs, ac);
|
|
82
|
-
if (res === TIMED_OUT) { fail(`timed out after ${timeoutMs}ms`); return; }
|
|
83
|
-
await res.arrayBuffer().catch(() => undefined); // release the connection; a success body is `{}`
|
|
84
|
-
if (res.ok) return;
|
|
85
|
-
if (NOT_SERVED.has(res.status)) { // one note, then silence for this signal (a second in-flight POST that raced the first says nothing more)
|
|
86
|
-
if (!off.has(signal)) { off.add(signal); opts.warn(`OTLP collector ${base} does not accept ${signal} (HTTP ${res.status}) — ${signal} export disabled`); }
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
fail(`HTTP ${res.status}`);
|
|
90
|
-
} catch (e) { fail(errText(e)); }
|
|
91
|
-
};
|
|
92
|
-
return {
|
|
93
|
-
base, usable, url,
|
|
94
|
-
post(signal, body) { const p = send(signal, body); pending.add(p); void p.then(() => { pending.delete(p); }); },
|
|
95
|
-
async flush() { while (pending.size > 0) await Promise.all([...pending]); },
|
|
96
|
-
disabled: (signal) => off.has(signal),
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ---------- helpers (moved from otel.ts) ----------
|
|
101
|
-
|
|
102
|
-
/** random hex id of `bytes` bytes; the all-zero id is invalid in OTel */
|
|
103
|
-
export function hex(bytes: number): string {
|
|
104
|
-
let h: string;
|
|
105
|
-
do h = randomBytes(bytes).toString("hex"); while (/^0+$/.test(h));
|
|
106
|
-
return h;
|
|
107
|
-
}
|
|
108
|
-
export const TIMED_OUT: unique symbol = Symbol("rovecode.otel.timeout");
|
|
109
|
-
/** resolves TIMED_OUT after ms on a REF'D timer and aborts the request's controller (hooks.ts idiom);
|
|
110
|
-
* the race is independent of the fetch honoring the signal, so a stuck transport still settles */
|
|
111
|
-
export function withTimeout<T>(p: Promise<T>, ms: number, ac: AbortController): Promise<T | typeof TIMED_OUT> {
|
|
112
|
-
return new Promise((resolve, reject) => {
|
|
113
|
-
const timer = setTimeout(() => { ac.abort(); resolve(TIMED_OUT); }, ms);
|
|
114
|
-
(timer as unknown as { ref?: () => void }).ref?.();
|
|
115
|
-
p.then((v) => { clearTimeout(timer); resolve(v); }, (e: unknown) => { clearTimeout(timer); reject(e); });
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
export function errText(e: unknown): string {
|
|
119
|
-
if (e instanceof Error) return e.message;
|
|
120
|
-
if (typeof e === "object" && e !== null && typeof (e as { message?: unknown }).message === "string") return (e as { message: string }).message;
|
|
121
|
-
return String(e);
|
|
122
|
-
}
|