heyspex 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/bin/hs.mjs +931 -98
- package/bin/spexcode.mjs +262 -0
- package/dev-factory/engine/worktree.mjs +740 -0
- package/dev-factory/lib/code-repo-root.mjs +67 -0
- package/dev-factory/lib/context-budget.mjs +84 -0
- package/dev-factory/lib/epic-detect.mjs +190 -0
- package/dev-factory/lib/fleet-queue-file.mjs +846 -0
- package/dev-factory/lib/fleet-queue.mjs +426 -0
- package/dev-factory/lib/fleet-state.mjs +14 -18
- package/dev-factory/lib/model-catalog.mjs +338 -0
- package/dev-factory/lib/seat-context.mjs +199 -0
- package/dev-factory/lib/spec-io.mjs +93 -0
- package/dev-factory/lib/sufficiency-score.mjs +8 -2
- package/dev-factory/lib/worktree-allocator.mjs +744 -0
- package/dev-factory/model-catalog.json +222 -0
- package/dist/cli/hs-check.mjs +1085 -103
- package/dist/cli/hs-claim.mjs +936 -63
- package/dist/cli/hs-connect.mjs +9786 -1910
- package/dist/cli/hs-defer.mjs +5267 -3053
- package/dist/cli/hs-dev.mjs +564 -0
- package/dist/cli/hs-env.mjs +385 -299
- package/dist/cli/hs-index-rebuild.mjs +7129 -0
- package/dist/cli/hs-index-reconcile.mjs +7852 -0
- package/dist/cli/hs-init.mjs +777 -138
- package/dist/cli/hs-install.mjs +6583 -485
- package/dist/cli/hs-join.mjs +10137 -2368
- package/dist/cli/hs-vault-pull.mjs +4659 -5882
- package/dist/cli/hs-vault.mjs +3793 -1537
- package/dist/cli/hs-workspace.mjs +2550 -524
- package/dist/cli-templates/customer-vault/specs/entity-model.md +594 -601
- package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +110 -177
- package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -0
- package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +13 -15
- package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +91 -0
- package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +150 -168
- package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -0
- package/dist/cli-templates/harness-commands/claude/hs-auto.md +36 -0
- package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -0
- package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -0
- package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -0
- package/dist/cli-templates/harness-commands/claude/hs-dev.md +20 -0
- package/dist/cli-templates/harness-commands/claude/hs-doctor.md +32 -0
- package/dist/cli-templates/harness-commands/claude/hs-ground.md +28 -0
- package/dist/cli-templates/harness-commands/claude/hs-handoff.md +33 -0
- package/dist/cli-templates/harness-commands/claude/hs-log.md +27 -0
- package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -0
- package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -0
- package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -0
- package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -0
- package/dist/cli-templates/harness-commands/claude/hs-res.md +63 -0
- package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -0
- package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -0
- package/dist/cli-templates/harness-commands/claude/hs-view.md +26 -0
- package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -0
- package/dist/cli-templates/harness-commands/claude/prototype.md +91 -0
- package/dist/cli-templates/harness-commands/cursor/hs-auto.md +40 -0
- package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-capture.md +65 -0
- package/dist/cli-templates/harness-commands/cursor/hs-defer.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/hs-dev.md +24 -0
- package/dist/cli-templates/harness-commands/cursor/hs-doctor.md +36 -0
- package/dist/cli-templates/harness-commands/cursor/hs-ground.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-handoff.md +37 -0
- package/dist/cli-templates/harness-commands/cursor/hs-log.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-next.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/hs-promote.md +73 -0
- package/dist/cli-templates/harness-commands/cursor/hs-push.md +38 -0
- package/dist/cli-templates/harness-commands/cursor/hs-qc.md +30 -0
- package/dist/cli-templates/harness-commands/cursor/hs-res.md +67 -0
- package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +26 -0
- package/dist/cli-templates/harness-commands/cursor/hs-review.md +26 -0
- package/dist/cli-templates/harness-commands/cursor/hs-view.md +30 -0
- package/dist/cli-templates/harness-commands/cursor/kill-ports.md +35 -0
- package/dist/cli-templates/harness-commands/cursor/prototype.md +95 -0
- package/docs/mcp-client-config.md +259 -6
- package/package.json +231 -124
- package/plugins/heyspex/.claude-plugin/plugin.json +12 -0
- package/plugins/heyspex/.cursor-plugin/plugin.json +15 -0
- package/plugins/heyspex/.mcp.json +12 -0
- package/plugins/heyspex/bin/launch-mcp.mjs +95 -0
- package/plugins/heyspex/bin/run-hook.mjs +62 -0
- package/plugins/heyspex/hooks/cursor-hooks.json +227 -0
- package/plugins/heyspex/hooks/hooks.json +361 -0
- package/plugins/heyspex/mcp.json +13 -0
- package/scripts/check-mcp-freshness.mjs +17 -9
- package/scripts/claude-hooks/_env-vault-events.mjs +68 -0
- package/scripts/claude-hooks/_guardrail-events.mjs +96 -0
- package/scripts/claude-hooks/_guardrail-loader.mjs +494 -0
- package/scripts/claude-hooks/_intent-classifier-router.mjs +410 -0
- package/scripts/claude-hooks/_intent-classifier-semantic.mjs +351 -0
- package/scripts/claude-hooks/_run.mjs +120 -0
- package/scripts/claude-hooks/_transcript-text.mjs +60 -0
- package/scripts/claude-hooks/_vault-retrieval.mjs +322 -0
- package/scripts/claude-hooks/agent-spawn-delegation-gate.mjs +133 -0
- package/scripts/claude-hooks/capture-workflow-gate.mjs +11 -0
- package/scripts/claude-hooks/check-auto-memory-budget.mjs +396 -0
- package/scripts/claude-hooks/check-claude-md-budget.mjs +327 -0
- package/scripts/claude-hooks/check-mcp-freshness-hook.mjs +14 -0
- package/scripts/claude-hooks/claim-before-code-gate.mjs +11 -0
- package/scripts/claude-hooks/claim-liveness-shared.mjs +575 -0
- package/scripts/claude-hooks/close-terminal-on-ship.mjs +64 -0
- package/scripts/claude-hooks/coo-denylist-gate.mjs +11 -0
- package/scripts/claude-hooks/coo-guard-check.mjs +11 -0
- package/scripts/claude-hooks/deep-research-cost-gate.mjs +11 -0
- package/scripts/claude-hooks/dev-server-task-reminder.mjs +15 -0
- package/scripts/claude-hooks/emit-claim-status.mjs +41 -0
- package/scripts/claude-hooks/enforce-session-sequence.mjs +11 -0
- package/scripts/claude-hooks/enforce-vault-naming.mjs +11 -0
- package/scripts/claude-hooks/env-vault-guard.mjs +11 -0
- package/scripts/claude-hooks/epic-milestone-phrasing-gate.mjs +11 -0
- package/scripts/claude-hooks/git-identity-guard.mjs +11 -0
- package/scripts/claude-hooks/git-reflog-audit.mjs +15 -0
- package/scripts/claude-hooks/git-stash-gate.mjs +11 -0
- package/scripts/claude-hooks/guard-auto-memory-writes.mjs +137 -0
- package/scripts/claude-hooks/id-display-format-gate.mjs +11 -0
- package/scripts/claude-hooks/inject-communication-style.mjs +66 -0
- package/scripts/claude-hooks/inject-coo-seat.mjs +78 -0
- package/scripts/claude-hooks/inject-dev-server-awareness.mjs +11 -0
- package/scripts/claude-hooks/inject-doctrine.mjs +13 -0
- package/scripts/claude-hooks/inject-vault-context.mjs +14 -0
- package/scripts/claude-hooks/intent-classifier-trigger.mjs +247 -0
- package/scripts/claude-hooks/log-session-activity.mjs +341 -0
- package/scripts/claude-hooks/naughty-list-gate.mjs +11 -0
- package/scripts/claude-hooks/notify-status-flip.mjs +72 -0
- package/scripts/claude-hooks/plan-exit-ingest-gate.mjs +170 -0
- package/scripts/claude-hooks/plan-ingest-close-reminder.mjs +66 -0
- package/scripts/claude-hooks/plan-review-gate.mjs +11 -0
- package/scripts/claude-hooks/preflight-reminder.mjs +262 -0
- package/scripts/claude-hooks/protected-action-intent.mjs +221 -0
- package/scripts/claude-hooks/research-method-guard.mjs +398 -0
- package/scripts/claude-hooks/research-protocol-gate.mjs +11 -0
- package/scripts/claude-hooks/session-end.mjs +449 -0
- package/scripts/claude-hooks/settings-write-gate.mjs +11 -0
- package/scripts/claude-hooks/settlement-check-gate.mjs +39 -0
- package/scripts/claude-hooks/status-flip-detect.mjs +17 -0
- package/scripts/claude-hooks/terminal-close-shared.mjs +426 -0
- package/scripts/claude-hooks/terminal-close-watcher.mjs +122 -0
- package/scripts/claude-hooks/wip-checkpoint.mjs +298 -0
- package/scripts/cursor-hooks/manifest.mjs +235 -0
- package/scripts/dev-server-status.mjs +34 -13
- package/scripts/dev-server-stop.mjs +313 -0
- package/scripts/heyspex-mode.mjs +268 -0
- package/scripts/hs-auto-resolve.mjs +240 -0
- package/scripts/hs-auto-source.mjs +398 -0
- package/scripts/hs-auto.mjs +134 -0
- package/scripts/hs-capture-cli.mjs +326 -0
- package/scripts/hs-classifier.mjs +136 -0
- package/scripts/hs-companion.mjs +88 -0
- package/scripts/hs-gateway.mjs +115 -0
- package/scripts/hs-mode.mjs +125 -0
- package/scripts/hs-next.mjs +441 -0
- package/scripts/hs-settings.mjs +338 -0
- package/scripts/hs-view.mjs +333 -0
- package/scripts/hs-worktree.mjs +201 -0
- package/scripts/install-claude-code-hooks.mjs +270 -0
- package/scripts/install-cursor-hooks.mjs +471 -0
- package/scripts/lib/agent-env-markers.mjs +38 -0
- package/scripts/lib/console-url-resolver.mjs +197 -60
- package/scripts/lib/customer-kit.mjs +886 -0
- package/scripts/lib/dev-mode.mjs +244 -28
- package/scripts/lib/heyspex-home.mjs +72 -0
- package/scripts/lib/hs-command.mjs +277 -0
- package/scripts/lib/hs-companion-settings.mjs +472 -0
- package/scripts/lib/hs-source-commit.mjs +59 -5
- package/scripts/lib/hs-source-identity.mjs +526 -0
- package/scripts/lib/hs-source-log.mjs +11 -21
- package/scripts/lib/hs-source-push.mjs +107 -58
- package/scripts/lib/hs-worktree-args.mjs +115 -0
- package/scripts/lib/shared-path-helpers.mjs +134 -0
- package/scripts/lib/standalone-doctor.mjs +540 -3
- package/scripts/lib/subagent-git-lock.mjs +109 -0
- package/scripts/lib/template-pin.mjs +34 -0
- package/scripts/provenance-lib.mjs +91 -0
- package/scripts/stamp-provenance-on-edit.mjs +222 -0
- package/scripts/wip-ledger-health.mjs +653 -0
- package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -0
- package/spexharness/src/adapters/audit.mjs +197 -0
- package/spexharness/src/adapters/claude-code.mjs +441 -0
- package/spexharness/src/chat/tui/ansi.mjs +448 -0
- package/spexharness/src/chat/tui/palette.mjs +22 -0
- package/spexharness/src/chat/tui/select.mjs +208 -0
- package/spexharness/src/config-root.mjs +54 -0
- package/spexharness/src/context-budget.mjs +141 -0
- package/spexharness/src/dev-server-control.mjs +389 -0
- package/spexharness/src/doctrine/behaviour.mjs +371 -0
- package/spexharness/src/doctrine/corpus.mjs +733 -0
- package/spexharness/src/doctrine/inject.mjs +338 -0
- package/spexharness/src/doctrine/merge.mjs +184 -0
- package/spexharness/src/doctrine/sources.mjs +252 -0
- package/spexharness/src/doctrine/synthesize.mjs +401 -0
- package/spexharness/src/doctrine/vault-source.mjs +574 -0
- package/spexharness/src/doctrine/writer.mjs +176 -0
- package/spexharness/src/events.mjs +84 -0
- package/spexharness/src/gates/capture-workflow-gate.mjs +202 -0
- package/spexharness/src/gates/claim-before-code-gate.decide.mjs +33 -0
- package/spexharness/src/gates/claim-before-code-gate.mjs +129 -0
- package/spexharness/src/gates/deep-research-cost-gate.mjs +274 -0
- package/spexharness/src/gates/enforce-session-sequence.mjs +895 -0
- package/spexharness/src/gates/enforce-vault-naming.mjs +215 -0
- package/spexharness/src/gates/epic-milestone-phrasing-gate.mjs +233 -0
- package/spexharness/src/gates/git-identity-guard.decide.mjs +436 -0
- package/spexharness/src/gates/git-identity-guard.mjs +61 -0
- package/spexharness/src/gates/git-reflog-audit.mjs +151 -0
- package/spexharness/src/gates/git-stash-gate.decide.mjs +361 -0
- package/spexharness/src/gates/git-stash-gate.mjs +159 -0
- package/spexharness/src/gates/id-display-format-gate.mjs +190 -0
- package/spexharness/src/gates/inject-doctrine-gate.mjs +89 -0
- package/spexharness/src/gates/inject-vault-context.mjs +239 -0
- package/spexharness/src/gates/naughty-list-gate.mjs +213 -0
- package/spexharness/src/gates/plan-review-gate.mjs +233 -0
- package/spexharness/src/gates/preflight-reminder-gate.mjs +265 -0
- package/spexharness/src/gates/research-protocol-gate.mjs +175 -0
- package/spexharness/src/gates/settings-write-gate.decide.mjs +96 -0
- package/spexharness/src/gates/settings-write-gate.mjs +104 -0
- package/spexharness/src/git-reflog-audit.mjs +294 -0
- package/spexharness/src/intent-classifier.mjs +194 -0
- package/spexharness/src/intent-directive.mjs +541 -0
- package/spexharness/src/profile/learn.mjs +551 -0
- package/spexharness/src/profile/proposals.mjs +357 -0
- package/spexharness/src/profile/vault-inclusion.mjs +386 -0
- package/spexharness/src/redact.mjs +151 -0
- package/spexharness/src/rule-table.mjs +819 -0
- package/spexharness/src/seat-context.mjs +122 -0
- package/spexharness/src/session-bootstrap.mjs +512 -0
- package/spexharness/src/session-record.mjs +815 -0
- package/spexharness/src/token-inventory.mjs +207 -0
- package/vendor/heyspex-mcp/account-switch.mjs +95 -0
- package/vendor/heyspex-mcp/alignment-gate.mjs +315 -1
- package/vendor/heyspex-mcp/blocker-resolver.mjs +37 -2
- package/vendor/heyspex-mcp/breakdown-ladder.mjs +119 -4
- package/vendor/heyspex-mcp/breakdown-source.mjs +271 -0
- package/vendor/heyspex-mcp/breakdown-writer.mjs +23 -3
- package/vendor/heyspex-mcp/capture-buffer-patches.mjs +46 -5
- package/vendor/heyspex-mcp/capture-channel.mjs +55 -5
- package/vendor/heyspex-mcp/capture-console-client.mjs +80 -1
- package/vendor/heyspex-mcp/capture-focus.mjs +1035 -29
- package/vendor/heyspex-mcp/capture-outbox.mjs +5 -1
- package/vendor/heyspex-mcp/capture-tools.mjs +1219 -0
- package/vendor/heyspex-mcp/claim-tools.mjs +1124 -0
- package/vendor/heyspex-mcp/cli-verbs.mjs +2076 -0
- package/vendor/heyspex-mcp/cli.mjs +201 -15524
- package/vendor/heyspex-mcp/cloud-tool-surface.mjs +128 -0
- package/vendor/heyspex-mcp/cloud-vault-writes.mjs +412 -0
- package/vendor/heyspex-mcp/config-seed.yaml +15 -0
- package/vendor/heyspex-mcp/config-settings.mjs +290 -7
- package/vendor/heyspex-mcp/console-fetch.mjs +657 -16
- package/vendor/heyspex-mcp/console-transport.mjs +166 -0
- package/vendor/heyspex-mcp/credentials.mjs +584 -0
- package/vendor/heyspex-mcp/defer-spec.mjs +44 -4
- package/vendor/heyspex-mcp/deferred-reclaim.mjs +29 -3
- package/vendor/heyspex-mcp/design-handlers.mjs +31 -0
- package/vendor/heyspex-mcp/design-tools.mjs +4 -1
- package/vendor/heyspex-mcp/dev-factory-bridge.mjs +4 -4
- package/vendor/heyspex-mcp/dev-mode-core.mjs +45 -0
- package/vendor/heyspex-mcp/dev-tools.mjs +124 -0
- package/vendor/heyspex-mcp/diagnose-handlers.mjs +54 -0
- package/vendor/heyspex-mcp/events-tools.mjs +207 -0
- package/vendor/heyspex-mcp/filing-tools.mjs +688 -0
- package/vendor/heyspex-mcp/fleet-handlers.mjs +37 -0
- package/vendor/heyspex-mcp/fleet-queue-tools.mjs +69 -14
- package/vendor/heyspex-mcp/focus-tools.mjs +190 -0
- package/vendor/heyspex-mcp/folder-trust-store.mjs +59 -0
- package/vendor/heyspex-mcp/http-transport.mjs +300 -0
- package/vendor/heyspex-mcp/list-tools.mjs +257 -0
- package/vendor/heyspex-mcp/login-handoff.mjs +15 -1
- package/vendor/heyspex-mcp/mcp-config.mjs +94 -0
- package/vendor/heyspex-mcp/memory-tools.mjs +523 -0
- package/vendor/heyspex-mcp/mvp-mode.mjs +53 -6
- package/vendor/heyspex-mcp/occupied-keys.mjs +141 -0
- package/vendor/heyspex-mcp/on-deck-brief.mjs +3360 -0
- package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -0
- package/vendor/heyspex-mcp/package-root.mjs +32 -0
- package/vendor/heyspex-mcp/peer-presence.mjs +8 -1
- package/vendor/heyspex-mcp/plan-ground.mjs +621 -652
- package/vendor/heyspex-mcp/plan-push.mjs +906 -21
- package/vendor/heyspex-mcp/plan-review.mjs +124 -30
- package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
- package/vendor/heyspex-mcp/procedure-handlers.mjs +325 -0
- package/vendor/heyspex-mcp/procedure-tools.mjs +540 -351
- package/vendor/heyspex-mcp/promote-capture.mjs +386 -69
- package/vendor/heyspex-mcp/promote-diagnose.mjs +74 -0
- package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +9 -2
- package/vendor/heyspex-mcp/promote-research.mjs +660 -53
- package/vendor/heyspex-mcp/promotion-helpers.mjs +251 -0
- package/vendor/heyspex-mcp/prompts.mjs +1460 -0
- package/vendor/heyspex-mcp/read-path.mjs +29 -9
- package/vendor/heyspex-mcp/receipt-shape.mjs +146 -0
- package/vendor/heyspex-mcp/recipe-handlers.mjs +117 -0
- package/vendor/heyspex-mcp/regen-tasks.mjs +210 -3
- package/vendor/heyspex-mcp/register-spec.mjs +266 -54
- package/vendor/heyspex-mcp/request-context.mjs +59 -0
- package/vendor/heyspex-mcp/research-focus.mjs +207 -4
- package/vendor/heyspex-mcp/research-handlers.mjs +623 -0
- package/vendor/heyspex-mcp/research-tools.mjs +4 -4
- package/vendor/heyspex-mcp/response-budget.mjs +348 -0
- package/vendor/heyspex-mcp/review-focus.mjs +25 -1
- package/vendor/heyspex-mcp/search-specs.mjs +280 -0
- package/vendor/heyspex-mcp/seat-block-ledger.mjs +113 -0
- package/vendor/heyspex-mcp/seat-identity.mjs +2 -6
- package/vendor/heyspex-mcp/session-owner.mjs +108 -0
- package/vendor/heyspex-mcp/session-tools.mjs +152 -0
- package/vendor/heyspex-mcp/settings-registry.mjs +232 -0
- package/vendor/heyspex-mcp/settings-tools.mjs +85 -12
- package/vendor/heyspex-mcp/spec-edit-tools.mjs +446 -0
- package/vendor/heyspex-mcp/spec-id-lib.mjs +93 -17
- package/vendor/heyspex-mcp/split-research.mjs +301 -56
- package/vendor/heyspex-mcp/stdio-rpc.mjs +22 -0
- package/vendor/heyspex-mcp/submit-for-review.mjs +132 -6
- package/vendor/heyspex-mcp/summary-contract.mjs +105 -0
- package/vendor/heyspex-mcp/summary-write-tools.mjs +224 -0
- package/vendor/heyspex-mcp/symptom-query.mjs +146 -0
- package/vendor/heyspex-mcp/test-helpers.mjs +108 -1
- package/vendor/heyspex-mcp/tool-catalog.mjs +2813 -0
- package/vendor/heyspex-mcp/tool-dispatch.mjs +639 -0
- package/vendor/heyspex-mcp/tool-helpers.mjs +325 -0
- package/vendor/heyspex-mcp/validate-linkage.mjs +414 -27
- package/vendor/heyspex-mcp/vault-io.mjs +724 -0
- package/vendor/heyspex-mcp/vault-less-endpoints.mjs +52 -0
- package/vendor/heyspex-mcp/vault-resolve.mjs +366 -68
- package/vendor/heyspex-mcp/verify-reproduction.mjs +270 -30
- package/vendor/heyspex-mcp/whoami.mjs +91 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import { createRequire as __hsCreateRequire } from 'node:module';
|
|
2
|
+
const require = __hsCreateRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// spex-console/bin/hs-dev.ts
|
|
5
|
+
import process3 from "node:process";
|
|
6
|
+
|
|
7
|
+
// spex-console/src/lib/dev-mode.ts
|
|
8
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
9
|
+
import { homedir as homedir2 } from "node:os";
|
|
10
|
+
import { join as join2 } from "node:path";
|
|
11
|
+
|
|
12
|
+
// vendor/heyspex-mcp/credentials.mjs
|
|
13
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
14
|
+
import os, { homedir } from "node:os";
|
|
15
|
+
import { dirname, join, resolve } from "node:path";
|
|
16
|
+
function underTestRunner(env = process.env) {
|
|
17
|
+
return Boolean(env.VITEST) || Boolean(env.NODE_TEST_CONTEXT);
|
|
18
|
+
}
|
|
19
|
+
function samePathLoose(a, b) {
|
|
20
|
+
const norm = (p) => {
|
|
21
|
+
const r = resolve(p).replace(/[\\/]+$/, "");
|
|
22
|
+
return process.platform === "win32" ? r.toLowerCase() : r;
|
|
23
|
+
};
|
|
24
|
+
return norm(a) === norm(b);
|
|
25
|
+
}
|
|
26
|
+
function realUserHome() {
|
|
27
|
+
try {
|
|
28
|
+
const h = os.userInfo().homedir;
|
|
29
|
+
if (typeof h === "string" && h) return h;
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function assertNotRealHomeUnderTest(filePath) {
|
|
35
|
+
if (!underTestRunner()) return;
|
|
36
|
+
const real = realUserHome();
|
|
37
|
+
if (real && samePathLoose(dirname(filePath), join(real, ".heyspex"))) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`credentials: refusing to write ${filePath} under a test runner \u2014 that is the real ~/.heyspex directory. Point the test at a temp home.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// spex-console/src/lib/dev-mode.ts
|
|
45
|
+
var CLOUD_CONSOLE_URL = "https://app.heyspex.com";
|
|
46
|
+
var LOCAL_CONSOLE_URL = "http://localhost:3000";
|
|
47
|
+
function normalizeMode(raw) {
|
|
48
|
+
const v = String(raw ?? "").trim().toLowerCase();
|
|
49
|
+
if (v === "prod" || v === "production") return { mode: "prod", reliance: "cloud" };
|
|
50
|
+
if (v === "dev") return { mode: "dev", reliance: null };
|
|
51
|
+
if (v === "local") return { mode: "dev", reliance: "local" };
|
|
52
|
+
if (v === "cloud") return { mode: "dev", reliance: "cloud" };
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function normalizeReliance(raw) {
|
|
56
|
+
const v = String(raw ?? "").trim().toLowerCase();
|
|
57
|
+
return v === "local" || v === "cloud" ? v : null;
|
|
58
|
+
}
|
|
59
|
+
function normalizeDevModeInput(mode, reliance) {
|
|
60
|
+
const parsed = normalizeMode(mode);
|
|
61
|
+
if (!parsed) return null;
|
|
62
|
+
return {
|
|
63
|
+
mode: parsed.mode,
|
|
64
|
+
reliance: parsed.mode === "prod" ? "cloud" : parsed.reliance ?? normalizeReliance(reliance) ?? "local"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function deriveSemantics(mode, reliance) {
|
|
68
|
+
const effectiveReliance = mode === "prod" ? "cloud" : reliance ?? "local";
|
|
69
|
+
const local = mode === "dev" && effectiveReliance === "local";
|
|
70
|
+
return {
|
|
71
|
+
reliance: effectiveReliance,
|
|
72
|
+
consoleUrl: local ? LOCAL_CONSOLE_URL : CLOUD_CONSOLE_URL,
|
|
73
|
+
readTier: local ? "disk" : "neon",
|
|
74
|
+
vaultWriteMode: local ? "local" : "cloud"
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function devModeFilePath(homeDir = homedir2()) {
|
|
78
|
+
return join2(homeDir, ".heyspex", "dev-mode.json");
|
|
79
|
+
}
|
|
80
|
+
var defaultReadFile = (p) => readFileSync2(p, "utf8");
|
|
81
|
+
function readLocalDevModeRecord({
|
|
82
|
+
homeDir = homedir2(),
|
|
83
|
+
readFile = defaultReadFile
|
|
84
|
+
} = {}) {
|
|
85
|
+
let raw;
|
|
86
|
+
try {
|
|
87
|
+
raw = readFile(devModeFilePath(homeDir));
|
|
88
|
+
} catch {
|
|
89
|
+
return { record: null, parseError: null };
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(raw);
|
|
93
|
+
const mode = parsed?.mode === "prod" || parsed?.mode === "dev" ? parsed.mode : null;
|
|
94
|
+
const reliance = normalizeReliance(parsed?.reliance);
|
|
95
|
+
if (!mode || mode === "dev" && !reliance) {
|
|
96
|
+
return { record: null, parseError: `invalid dev-mode record: ${JSON.stringify(parsed)}` };
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
record: {
|
|
100
|
+
mode,
|
|
101
|
+
reliance: mode === "prod" ? "cloud" : reliance,
|
|
102
|
+
console_url: typeof parsed.console_url === "string" ? parsed.console_url : null,
|
|
103
|
+
updated_at: typeof parsed.updated_at === "string" ? parsed.updated_at : null,
|
|
104
|
+
source: typeof parsed.source === "string" ? parsed.source : null
|
|
105
|
+
},
|
|
106
|
+
parseError: null
|
|
107
|
+
};
|
|
108
|
+
} catch (err) {
|
|
109
|
+
return {
|
|
110
|
+
record: null,
|
|
111
|
+
parseError: `unparseable dev-mode.json: ${err instanceof Error ? err.message : String(err)}`
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function resolveDevMode({
|
|
116
|
+
env = process.env,
|
|
117
|
+
homeDir = homedir2(),
|
|
118
|
+
readFile = defaultReadFile,
|
|
119
|
+
cloudSetting = void 0
|
|
120
|
+
} = {}) {
|
|
121
|
+
const local = readLocalDevModeRecord({ homeDir, readFile });
|
|
122
|
+
const cloudKnown = cloudSetting !== void 0;
|
|
123
|
+
const cloudValue = cloudKnown && cloudSetting ? {
|
|
124
|
+
mode: cloudSetting.mode === "dev" ? "dev" : "prod",
|
|
125
|
+
reliance: normalizeReliance(cloudSetting.reliance) ?? "cloud"
|
|
126
|
+
} : null;
|
|
127
|
+
let mode;
|
|
128
|
+
let reliance;
|
|
129
|
+
let source;
|
|
130
|
+
let relianceSource = null;
|
|
131
|
+
const envParsed = normalizeMode(env.HEYSPEX_DEV_MODE);
|
|
132
|
+
if (envParsed) {
|
|
133
|
+
mode = envParsed.mode;
|
|
134
|
+
const envReliance = envParsed.reliance ?? normalizeReliance(env.HEYSPEX_DEV_RELIANCE);
|
|
135
|
+
reliance = envReliance ?? "local";
|
|
136
|
+
source = "env";
|
|
137
|
+
relianceSource = envReliance ? "env" : "assumed-local";
|
|
138
|
+
} else if (local.record) {
|
|
139
|
+
mode = local.record.mode;
|
|
140
|
+
reliance = local.record.reliance;
|
|
141
|
+
source = "local-override";
|
|
142
|
+
} else if (cloudValue) {
|
|
143
|
+
mode = cloudValue.mode;
|
|
144
|
+
reliance = cloudValue.reliance;
|
|
145
|
+
source = "cloud-setting";
|
|
146
|
+
} else {
|
|
147
|
+
mode = "prod";
|
|
148
|
+
reliance = "cloud";
|
|
149
|
+
source = "default";
|
|
150
|
+
}
|
|
151
|
+
const semantics = deriveSemantics(mode, reliance);
|
|
152
|
+
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
153
|
+
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
154
|
+
return {
|
|
155
|
+
mode,
|
|
156
|
+
reliance: semantics.reliance,
|
|
157
|
+
consoleUrl,
|
|
158
|
+
readTier: semantics.readTier,
|
|
159
|
+
vaultWriteMode: semantics.vaultWriteMode,
|
|
160
|
+
sources: {
|
|
161
|
+
mode: source,
|
|
162
|
+
reliance: relianceSource ?? source,
|
|
163
|
+
consoleUrl: source === "local-override" && local.record?.console_url ? "local-override" : "derived",
|
|
164
|
+
readTier: "derived",
|
|
165
|
+
vaultWriteMode: "derived"
|
|
166
|
+
},
|
|
167
|
+
localOverride: {
|
|
168
|
+
present: local.record !== null,
|
|
169
|
+
record: local.record,
|
|
170
|
+
parseError: local.parseError,
|
|
171
|
+
staleAgainstCloud
|
|
172
|
+
},
|
|
173
|
+
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function modeOverrideOf(resolved) {
|
|
177
|
+
if (!resolved) return null;
|
|
178
|
+
const cloud = resolved.cloudSetting.value;
|
|
179
|
+
if (resolved.sources.mode === "env") {
|
|
180
|
+
const disagrees = cloud !== null && (cloud.mode !== resolved.mode || resolved.mode === "dev" && cloud.reliance !== resolved.reliance);
|
|
181
|
+
return {
|
|
182
|
+
source: "env",
|
|
183
|
+
written_at: null,
|
|
184
|
+
written_by: "HEYSPEX_DEV_MODE",
|
|
185
|
+
disagrees_with_cloud: disagrees
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (resolved.localOverride.present) {
|
|
189
|
+
const record = resolved.localOverride.record;
|
|
190
|
+
return {
|
|
191
|
+
source: "dev-mode.json",
|
|
192
|
+
written_at: record?.updated_at ?? null,
|
|
193
|
+
written_by: record?.source ?? null,
|
|
194
|
+
disagrees_with_cloud: resolved.localOverride.staleAgainstCloud === true
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
function buildDevStatusJson(resolved, cloudError) {
|
|
200
|
+
return { resolved, cloudError, mode_override: modeOverrideOf(resolved) };
|
|
201
|
+
}
|
|
202
|
+
function upsertCredentialsYamlKeys(homeDir, updates, deps = {}) {
|
|
203
|
+
const readFile = deps.readFile ?? defaultReadFile;
|
|
204
|
+
const writeFile = deps.writeFile ?? ((p, c) => writeFileSync2(p, c, { mode: 384 }));
|
|
205
|
+
const mkdir = deps.mkdir ?? ((p) => mkdirSync2(p, { recursive: true, mode: 448 }));
|
|
206
|
+
const dir = join2(homeDir, ".heyspex");
|
|
207
|
+
const filePath = join2(dir, "credentials.yaml");
|
|
208
|
+
if (!deps.writeFile) assertNotRealHomeUnderTest(filePath);
|
|
209
|
+
mkdir(dir);
|
|
210
|
+
let content = "";
|
|
211
|
+
try {
|
|
212
|
+
content = readFile(filePath);
|
|
213
|
+
} catch {
|
|
214
|
+
content = "";
|
|
215
|
+
}
|
|
216
|
+
const lines = content.length > 0 ? content.split(/\r?\n/) : [];
|
|
217
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
218
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
219
|
+
const pattern = new RegExp(`^${escapedKey}:.*$`);
|
|
220
|
+
const serialized = `${key}: ${value}`;
|
|
221
|
+
const idx = lines.findIndex((line) => pattern.test(line));
|
|
222
|
+
if (idx >= 0) lines[idx] = serialized;
|
|
223
|
+
else lines.push(serialized);
|
|
224
|
+
}
|
|
225
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
226
|
+
writeFile(filePath, `${lines.join("\n")}
|
|
227
|
+
`);
|
|
228
|
+
return filePath;
|
|
229
|
+
}
|
|
230
|
+
function readCredentialsMcpToken({
|
|
231
|
+
homeDir = homedir2(),
|
|
232
|
+
readFile = defaultReadFile
|
|
233
|
+
} = {}) {
|
|
234
|
+
try {
|
|
235
|
+
const raw = readFile(join2(homeDir, ".heyspex", "credentials.yaml"));
|
|
236
|
+
const match = raw.match(/^mcp_token:\s*["']?([^"'\r\n#]+)/m);
|
|
237
|
+
return match?.[1]?.trim() || null;
|
|
238
|
+
} catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async function withRequestTimeout(timeoutMs, fn) {
|
|
243
|
+
const controller = new AbortController();
|
|
244
|
+
const state = { timedOut: false };
|
|
245
|
+
const timer = setTimeout(() => {
|
|
246
|
+
state.timedOut = true;
|
|
247
|
+
controller.abort();
|
|
248
|
+
}, timeoutMs);
|
|
249
|
+
timer.unref?.();
|
|
250
|
+
try {
|
|
251
|
+
return await fn(controller.signal);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
if (state.timedOut) throw new Error(`timed out after ${timeoutMs}ms`);
|
|
254
|
+
throw err;
|
|
255
|
+
} finally {
|
|
256
|
+
clearTimeout(timer);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function discardResponseBody(res) {
|
|
260
|
+
try {
|
|
261
|
+
await res?.body?.cancel();
|
|
262
|
+
} catch {
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function fetchCloudDevMode({
|
|
266
|
+
consoleUrl,
|
|
267
|
+
fetchImpl = fetch,
|
|
268
|
+
homeDir = homedir2(),
|
|
269
|
+
readFile = defaultReadFile,
|
|
270
|
+
timeoutMs = 4e3
|
|
271
|
+
}) {
|
|
272
|
+
try {
|
|
273
|
+
const token = readCredentialsMcpToken({ homeDir, readFile });
|
|
274
|
+
const res = await withRequestTimeout(
|
|
275
|
+
timeoutMs,
|
|
276
|
+
(signal) => fetchImpl(`${consoleUrl.replace(/\/+$/, "")}/api/admin/dev-mode`, {
|
|
277
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
278
|
+
signal
|
|
279
|
+
})
|
|
280
|
+
);
|
|
281
|
+
if (!res.ok) {
|
|
282
|
+
await discardResponseBody(res);
|
|
283
|
+
return { known: false, value: null, error: `HTTP ${res.status}` };
|
|
284
|
+
}
|
|
285
|
+
const body = await res.json();
|
|
286
|
+
if (body.cloudKnown === false)
|
|
287
|
+
return { known: false, value: null, error: "storage unavailable" };
|
|
288
|
+
const cloud = body.cloud;
|
|
289
|
+
return {
|
|
290
|
+
known: true,
|
|
291
|
+
value: cloud ? normalizeDevModeInput(cloud.mode, cloud.reliance) : null,
|
|
292
|
+
error: null
|
|
293
|
+
};
|
|
294
|
+
} catch (err) {
|
|
295
|
+
return { known: false, value: null, error: err instanceof Error ? err.message : String(err) };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
var DevModeNotAdminError = class extends Error {
|
|
299
|
+
constructor() {
|
|
300
|
+
super(
|
|
301
|
+
"This account runs on HeySpex Cloud. Switching to local-disk mode is reserved for the HeySpex team's own build machines."
|
|
302
|
+
);
|
|
303
|
+
this.name = "DevModeNotAdminError";
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
async function applyDevMode(targetMode, targetReliance, deps = {}) {
|
|
307
|
+
const {
|
|
308
|
+
homeDir = homedir2(),
|
|
309
|
+
fetchImpl = fetch,
|
|
310
|
+
source = "cli",
|
|
311
|
+
now = () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
312
|
+
syncCloud = true
|
|
313
|
+
} = deps;
|
|
314
|
+
const writeFile = deps.writeFile ?? ((p, c) => writeFileSync2(p, c, "utf8"));
|
|
315
|
+
const mkdir = deps.mkdir ?? ((p) => mkdirSync2(p, { recursive: true, mode: 448 }));
|
|
316
|
+
const parsed = normalizeMode(targetMode);
|
|
317
|
+
if (!parsed) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`applyDevMode: invalid mode "${targetMode}" (expected prod | dev | local | cloud)`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const mode = parsed.mode;
|
|
323
|
+
const reliance = parsed.reliance ?? normalizeReliance(targetReliance) ?? "local";
|
|
324
|
+
const semantics = deriveSemantics(mode, reliance);
|
|
325
|
+
if (!deps.writeFile) assertNotRealHomeUnderTest(devModeFilePath(homeDir));
|
|
326
|
+
async function postCloud(token) {
|
|
327
|
+
return withRequestTimeout(
|
|
328
|
+
5e3,
|
|
329
|
+
(signal) => fetchImpl(`${semantics.consoleUrl}/api/admin/dev-mode`, {
|
|
330
|
+
method: "POST",
|
|
331
|
+
headers: {
|
|
332
|
+
"content-type": "application/json",
|
|
333
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
334
|
+
},
|
|
335
|
+
body: JSON.stringify({ mode, reliance: semantics.reliance, source }),
|
|
336
|
+
signal
|
|
337
|
+
})
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
let cloud = { attempted: false, updated: false, error: null };
|
|
341
|
+
if (syncCloud && semantics.reliance === "local") {
|
|
342
|
+
const token = readCredentialsMcpToken({ homeDir, readFile: deps.readFile });
|
|
343
|
+
if (token) {
|
|
344
|
+
try {
|
|
345
|
+
const res = await postCloud(token);
|
|
346
|
+
await discardResponseBody(res);
|
|
347
|
+
if (res.status === 403) throw new DevModeNotAdminError();
|
|
348
|
+
cloud = { attempted: true, updated: res.ok, error: res.ok ? null : `HTTP ${res.status}` };
|
|
349
|
+
} catch (err) {
|
|
350
|
+
if (err instanceof DevModeNotAdminError) throw err;
|
|
351
|
+
cloud = {
|
|
352
|
+
attempted: true,
|
|
353
|
+
updated: false,
|
|
354
|
+
error: err instanceof Error ? err.message : String(err)
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const record = {
|
|
360
|
+
mode,
|
|
361
|
+
reliance: semantics.reliance,
|
|
362
|
+
console_url: semantics.consoleUrl,
|
|
363
|
+
updated_at: now(),
|
|
364
|
+
source
|
|
365
|
+
};
|
|
366
|
+
mkdir(join2(homeDir, ".heyspex"));
|
|
367
|
+
const recordPath = devModeFilePath(homeDir);
|
|
368
|
+
writeFile(recordPath, `${JSON.stringify(record, null, 2)}
|
|
369
|
+
`);
|
|
370
|
+
const credentialsPath = upsertCredentialsYamlKeys(
|
|
371
|
+
homeDir,
|
|
372
|
+
{ console_url: semantics.consoleUrl },
|
|
373
|
+
{ readFile: deps.readFile, writeFile: deps.writeFile, mkdir: deps.mkdir }
|
|
374
|
+
);
|
|
375
|
+
if (syncCloud && !cloud.attempted) {
|
|
376
|
+
cloud = { attempted: true, updated: false, error: null };
|
|
377
|
+
try {
|
|
378
|
+
const token = readCredentialsMcpToken({ homeDir, readFile: deps.readFile });
|
|
379
|
+
const res = await postCloud(token);
|
|
380
|
+
if (res.ok) cloud.updated = true;
|
|
381
|
+
else cloud.error = `HTTP ${res.status}`;
|
|
382
|
+
await discardResponseBody(res);
|
|
383
|
+
} catch (err) {
|
|
384
|
+
cloud.error = err instanceof Error ? err.message : String(err);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
applied: {
|
|
389
|
+
mode,
|
|
390
|
+
reliance: semantics.reliance,
|
|
391
|
+
consoleUrl: semantics.consoleUrl,
|
|
392
|
+
readTier: semantics.readTier,
|
|
393
|
+
vaultWriteMode: semantics.vaultWriteMode
|
|
394
|
+
},
|
|
395
|
+
record,
|
|
396
|
+
recordPath,
|
|
397
|
+
credentialsPath,
|
|
398
|
+
cloud
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// spex-console/bin/run-entry.ts
|
|
403
|
+
import process2 from "node:process";
|
|
404
|
+
var BinExit = class extends Error {
|
|
405
|
+
code;
|
|
406
|
+
constructor(code) {
|
|
407
|
+
super(`exit ${code}`);
|
|
408
|
+
this.name = "BinExit";
|
|
409
|
+
this.code = code;
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
function runEntry(name, main2, options = {}) {
|
|
413
|
+
const { fallbackCode = 1, unhandledLabel = "unhandled" } = options;
|
|
414
|
+
void (async () => {
|
|
415
|
+
try {
|
|
416
|
+
await main2();
|
|
417
|
+
} catch (err) {
|
|
418
|
+
if (err instanceof BinExit) {
|
|
419
|
+
process2.exitCode = err.code;
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
423
|
+
process2.stderr.write(`${name}: ${unhandledLabel}: ${message}
|
|
424
|
+
`);
|
|
425
|
+
process2.exitCode = fallbackCode;
|
|
426
|
+
}
|
|
427
|
+
})();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// spex-console/bin/hs-dev.ts
|
|
431
|
+
var HELP = `hs dev \u2014 dev/prod mode + local/cloud reliance
|
|
432
|
+
|
|
433
|
+
Usage:
|
|
434
|
+
hs dev [--json] Show the resolved mode and the source of each value
|
|
435
|
+
hs dev mode:local Switch to dev mode, local reliance (localhost console, disk reads, local writes)
|
|
436
|
+
hs dev mode:cloud Switch to dev mode, cloud reliance (cloud console, Neon reads, cloud writes)
|
|
437
|
+
hs dev mode:prod Switch to prod (cloud console, Neon reads, cloud writes)
|
|
438
|
+
hs dev local|cloud|prod Shorthand for the mode:* forms
|
|
439
|
+
|
|
440
|
+
Resolution order: env (HEYSPEX_DEV_MODE) \u2192 ~/.heyspex/dev-mode.json \u2192
|
|
441
|
+
cloud admin setting (account_settings.dev_mode) \u2192 default prod.
|
|
442
|
+
|
|
443
|
+
Switching writes ~/.heyspex/dev-mode.json and console_url in
|
|
444
|
+
~/.heyspex/credentials.yaml (which repoints MCP/hooks/dev-factory via the
|
|
445
|
+
shared console-url resolver), and best-effort updates the cloud admin setting.
|
|
446
|
+
Never touches repo-tracked files.`;
|
|
447
|
+
var SOURCE_LABEL = {
|
|
448
|
+
env: "env override",
|
|
449
|
+
"local-override": "local override (~/.heyspex/dev-mode.json)",
|
|
450
|
+
"cloud-setting": "cloud admin setting",
|
|
451
|
+
default: "default",
|
|
452
|
+
derived: "derived",
|
|
453
|
+
// HS-EVVI AC1: HEYSPEX_DEV_MODE=dev with no HEYSPEX_DEV_RELIANCE is never
|
|
454
|
+
// silently local any more — the row says the reliance was assumed.
|
|
455
|
+
"assumed-local": "ASSUMED local \u2014 HEYSPEX_DEV_MODE=dev names no reliance; set HEYSPEX_DEV_RELIANCE"
|
|
456
|
+
};
|
|
457
|
+
function describeCloudFailure(cloudError) {
|
|
458
|
+
if (cloudError === "HTTP 401") {
|
|
459
|
+
return "not signed in on this machine (HTTP 401) \u2014 run `hs login`";
|
|
460
|
+
}
|
|
461
|
+
return `unavailable${cloudError ? ` (${cloudError})` : ""}`;
|
|
462
|
+
}
|
|
463
|
+
function printStatus(resolved, cloudError) {
|
|
464
|
+
const rows = [
|
|
465
|
+
["mode", resolved.mode, resolved.sources.mode],
|
|
466
|
+
["reliance", resolved.reliance, resolved.sources.reliance],
|
|
467
|
+
["console URL", resolved.consoleUrl, resolved.sources.consoleUrl],
|
|
468
|
+
["read tier", resolved.readTier, resolved.sources.readTier],
|
|
469
|
+
["vault writes", resolved.vaultWriteMode, resolved.sources.vaultWriteMode]
|
|
470
|
+
];
|
|
471
|
+
const out = ["hs dev \u2014 resolved mode", ""];
|
|
472
|
+
for (const [label, value, source] of rows) {
|
|
473
|
+
out.push(` ${label.padEnd(13)}${value.padEnd(28)}${SOURCE_LABEL[source] ?? source}`);
|
|
474
|
+
}
|
|
475
|
+
out.push("");
|
|
476
|
+
if (resolved.sources.mode === "env") {
|
|
477
|
+
out.push(
|
|
478
|
+
" env override: active (HEYSPEX_DEV_MODE) \u2014 hides the local record and the cloud setting"
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
if (resolved.localOverride.present) {
|
|
482
|
+
const rec = resolved.localOverride.record;
|
|
483
|
+
out.push(
|
|
484
|
+
` local override: active (written ${rec?.updated_at ?? "?"} by ${rec?.source ?? "?"})` + (resolved.localOverride.staleAgainstCloud ? " \u2014 DISAGREES with the cloud setting (stale override?)" : "")
|
|
485
|
+
);
|
|
486
|
+
} else if (resolved.localOverride.parseError) {
|
|
487
|
+
out.push(` local override: UNREADABLE, ignored \u2014 ${resolved.localOverride.parseError}`);
|
|
488
|
+
} else {
|
|
489
|
+
out.push(" local override: none");
|
|
490
|
+
}
|
|
491
|
+
if (resolved.cloudSetting.known) {
|
|
492
|
+
const v = resolved.cloudSetting.value;
|
|
493
|
+
out.push(
|
|
494
|
+
v ? ` cloud setting: ${v.mode}${v.mode === "dev" ? ` (${v.reliance})` : ""}` : " cloud setting: unset"
|
|
495
|
+
);
|
|
496
|
+
} else {
|
|
497
|
+
out.push(` cloud setting: ${describeCloudFailure(cloudError)}`);
|
|
498
|
+
}
|
|
499
|
+
process3.stdout.write(`${out.join("\n")}
|
|
500
|
+
`);
|
|
501
|
+
}
|
|
502
|
+
async function main() {
|
|
503
|
+
const argv = process3.argv.slice(3);
|
|
504
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
505
|
+
process3.stdout.write(`${HELP}
|
|
506
|
+
`);
|
|
507
|
+
process3.exitCode = 0;
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const json = argv.includes("--json");
|
|
511
|
+
const positional = argv.filter((a) => !a.startsWith("--"));
|
|
512
|
+
const token = positional[0]?.toLowerCase();
|
|
513
|
+
if (token === void 0 || token === "status") {
|
|
514
|
+
const pre = resolveDevMode();
|
|
515
|
+
const cloud = await fetchCloudDevMode({ consoleUrl: pre.consoleUrl });
|
|
516
|
+
const resolved = resolveDevMode({ cloudSetting: cloud.known ? cloud.value : void 0 });
|
|
517
|
+
if (json) {
|
|
518
|
+
process3.stdout.write(
|
|
519
|
+
`${JSON.stringify(buildDevStatusJson(resolved, cloud.error), null, 2)}
|
|
520
|
+
`
|
|
521
|
+
);
|
|
522
|
+
} else {
|
|
523
|
+
printStatus(resolved, cloud.error);
|
|
524
|
+
}
|
|
525
|
+
process3.exitCode = 0;
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const modeToken = token.startsWith("mode:") ? token.slice("mode:".length) : token;
|
|
529
|
+
if (!["local", "cloud", "prod", "dev"].includes(modeToken)) {
|
|
530
|
+
process3.stderr.write(`hs dev: unknown subcommand \`${token}\`
|
|
531
|
+
|
|
532
|
+
${HELP}
|
|
533
|
+
`);
|
|
534
|
+
process3.exitCode = 2;
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
try {
|
|
538
|
+
const result = await applyDevMode(modeToken, positional[1] ?? null, { source: "cli" });
|
|
539
|
+
if (json) {
|
|
540
|
+
process3.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
541
|
+
`);
|
|
542
|
+
} else {
|
|
543
|
+
const a = result.applied;
|
|
544
|
+
const lines = [
|
|
545
|
+
`Switched to ${a.mode}${a.mode === "dev" ? ` (${a.reliance})` : ""}:`,
|
|
546
|
+
` console URL ${a.consoleUrl}`,
|
|
547
|
+
` read tier ${a.readTier}`,
|
|
548
|
+
` vault writes ${a.vaultWriteMode}`,
|
|
549
|
+
` wrote ${result.recordPath}`,
|
|
550
|
+
` wrote console_url in ${result.credentialsPath}`,
|
|
551
|
+
result.cloud.attempted ? result.cloud.updated ? " cloud admin setting updated" : ` cloud admin setting NOT updated (${result.cloud.error}) \u2014 local files still switched` : " cloud sync skipped"
|
|
552
|
+
];
|
|
553
|
+
process3.stdout.write(`${lines.join("\n")}
|
|
554
|
+
`);
|
|
555
|
+
}
|
|
556
|
+
process3.exitCode = 0;
|
|
557
|
+
} catch (err) {
|
|
558
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
559
|
+
process3.stderr.write(`hs dev: ${message}
|
|
560
|
+
`);
|
|
561
|
+
process3.exitCode = 1;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
runEntry("hs dev", main, { fallbackCode: 1 });
|