clearotron 0.4.0-beta.0 → 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/.env.example +9 -0
- package/INSTALL.md +4 -31
- package/README.md +5 -5
- package/bin/brandowner.mjs +1 -1
- package/bin/onboard.mjs +18 -74
- package/bin/start.mjs +58 -14
- package/bin/update.mjs +10 -2
- package/build-info.json +2 -2
- package/demo/full-country-search/run/_driver/search-policy.json +1 -1
- package/demo/full-country-search/run/status.json +1 -1
- package/demo/global-preliminary-search/run/_driver/search-policy.json +1 -1
- package/demo/global-preliminary-search/run/status.json +1 -1
- package/demo/knockout-search/run/_driver/search-policy.json +1 -1
- package/demo/knockout-search/run/status.json +1 -1
- package/demo/multi-country-focus-search/run/_driver/search-policy.json +1 -1
- package/demo/multi-country-focus-search/run/status.json +1 -1
- package/docs/README.md +1 -0
- package/docs/SECURITY-OWASP.md +298 -0
- package/docs/SECURITY.md +18 -13
- package/docs/architecture/02-architecture.md +5 -5
- package/docs/architecture/03-run-lifecycle.md +28 -29
- package/docs/architecture/04-configuration-reference.md +5 -10
- package/docs/architecture/05-config-governance.md +5 -7
- package/docs/architecture/06-operations-runbook.md +0 -12
- package/docs/architecture/07-quality-and-audit.md +12 -13
- package/docs/architecture/09-security-and-data.md +9 -4
- package/driver/CHANGELOG.md +70 -0
- package/driver/ask-ledger.mjs +4 -2
- package/driver/authority-trees.mjs +90 -8
- package/driver/band-shape.mjs +13 -13
- package/driver/blind-frame-model.mjs +1 -1
- package/driver/case-law-ledger.mjs +19 -3
- package/driver/claim-liveness.mjs +30 -1
- package/driver/clearance-variants-record.mjs +10 -3
- package/driver/common-law-receipts.mjs +64 -14
- package/driver/commonlaw-carry.mjs +2 -2
- package/driver/compare.mjs +2 -2
- package/driver/connotation-search.mjs +33 -104
- package/driver/contract-arm2-baseline.json +6 -5
- package/driver/contract-e3-backlog.mjs +62 -62
- package/driver/contract-vocabulary.mjs +38 -31
- package/driver/coverage-form-io.mjs +13 -1
- package/driver/coverage-form.mjs +42 -4
- package/driver/coverage-ledger.mjs +3 -5
- package/driver/cross-check-wait.mjs +43 -0
- package/driver/declination-call.mjs +34 -10
- package/driver/declination-tool.mjs +27 -15
- package/driver/deferral-row.mjs +139 -0
- package/driver/degraded-parts.mjs +377 -0
- package/driver/deliver-trigger.sh +1 -1
- package/driver/demo-container.mjs +4 -1
- package/driver/dev-portal.mjs +5 -5
- package/driver/dispatch-record.mjs +2 -2
- package/driver/driver.config.mjs +193 -96
- package/driver/e2e/README.md +1 -1
- package/driver/effective-scope.mjs +58 -2
- package/driver/engine/anthropic-agent.mjs +103 -27
- package/driver/engine/auth.mjs +2 -2
- package/driver/engine/child-record.mjs +11 -0
- package/driver/engine/cli-version.mjs +4 -3
- package/driver/engine/common.mjs +17 -2
- package/driver/engine/deny-authority-write.mjs +3 -1
- package/driver/engine/engine-env.mjs +174 -0
- package/driver/engine/engine-spawn.mjs +179 -0
- package/driver/engine/mcp/band-server.mjs +1 -1
- package/driver/engine/mcp/clarivate-server.mjs +2 -1
- package/driver/engine/mcp/codex-config.mjs +112 -3
- package/driver/engine/mcp/corsearch-server.mjs +1 -0
- package/driver/engine/mcp/declination-server.mjs +9 -8
- package/driver/engine/mcp/dispositions-server.mjs +2 -1
- package/driver/engine/mcp/euipo-server.mjs +1 -0
- package/driver/engine/mcp/fetch-server.mjs +14 -11
- package/driver/engine/mcp/free-tier-server.mjs +1 -1
- package/driver/engine/mcp/gather-config.mjs +23 -11
- package/driver/engine/mcp/perplexity-server.mjs +97 -17
- package/driver/engine/mcp/probe-server.mjs +43 -21
- package/driver/engine/mcp/public-fetch.mjs +195 -0
- package/driver/engine/mcp/recording-server.mjs +79 -14
- package/driver/engine/mcp/signa-server.mjs +1 -0
- package/driver/engine/mcp/supplemental.mjs +14 -14
- package/driver/engine/mcp/unit-note-server.mjs +50 -1
- package/driver/engine/mcp/uspto-local-server.mjs +1 -0
- package/driver/engine/openai-agent.mjs +109 -28
- package/driver/engine/probe.mjs +152 -19
- package/driver/enqueue-schema.mjs +5 -5
- package/driver/feedback-issues.mjs +1 -1
- package/driver/feedback-store.mjs +1 -1
- package/driver/findings-model.mjs +67 -16
- package/driver/flag-snapshot.mjs +1 -1
- package/driver/floor-duty.mjs +23 -7
- package/driver/form-neighbourhood.mjs +52 -17
- package/driver/frame-diff-model.mjs +3 -3
- package/driver/framework-method.mjs +304 -0
- package/driver/gateway.mjs +49 -21
- package/driver/hand-off-exits.mjs +129 -0
- package/driver/jx-lanes.mjs +1 -1
- package/driver/jx.mjs +8 -5
- package/driver/knockout-assess-record.mjs +7 -1
- package/driver/knockout-frame-record.mjs +96 -34
- package/driver/knockout-review-record.mjs +7 -2
- package/driver/log.mjs +2 -2
- package/driver/matter-frame-record.mjs +31 -2
- package/driver/methodology-witness.mjs +6 -3
- package/driver/named-band.mjs +1 -1
- package/driver/owner-use-check.mjs +45 -10
- package/driver/package.json +1 -1
- package/driver/partial-payload-baseline.json +4 -0
- package/driver/phase0.mjs +2 -2
- package/driver/pipeline-knockout.mjs +256 -129
- package/driver/pipeline.mjs +417 -614
- package/driver/placement-carry.mjs +22 -3
- package/driver/placement-form-io.mjs +18 -4
- package/driver/placement-form.mjs +53 -4
- package/driver/placement-union.mjs +32 -5
- package/driver/portal-report.mjs +3 -3
- package/driver/portal-service.mjs +6 -5
- package/driver/portal-static.mjs +11 -18
- package/driver/predelivery-lint.mjs +10 -10
- package/driver/profile-page.html +5 -5
- package/driver/profiles.mjs +5 -5
- package/driver/progress.mjs +6 -4
- package/driver/provider-usage.mjs +25 -2
- package/driver/publish/index.mjs +97 -25
- package/driver/publish/knockout.mjs +136 -14
- package/driver/publish/profiles-page.mjs +2 -0
- package/driver/publish/publish-inputs.mjs +10 -4
- package/driver/publish/render-knockout.mjs +48 -21
- package/driver/publish/render.mjs +13 -8
- package/driver/publish/report-data.mjs +2 -0
- package/driver/publish/search-depth.mjs +77 -4
- package/driver/publish/templates/report.css +1 -1
- package/driver/publish/xlsx.mjs +34 -15
- package/driver/reasoning-tripwires.mjs +2 -160
- package/driver/recall-receipt.mjs +27 -0
- package/driver/recall-reconciliation.mjs +1 -1
- package/driver/record-carry.mjs +14 -14
- package/driver/recording-agreement.mjs +2 -2
- package/driver/reference-score.mjs +169 -47
- package/driver/register-count.mjs +61 -4
- package/driver/register-digest-record.mjs +14 -1
- package/driver/register-plan.mjs +161 -80
- package/driver/register-records.mjs +37 -4
- package/driver/registration-scripts.mjs +22 -0
- package/driver/registry-fidelity.mjs +4 -4
- package/driver/repair-composers.mjs +23 -7
- package/driver/repair-contract.mjs +1 -1
- package/driver/repairs.mjs +5 -2
- package/driver/reviewer-open-points.mjs +33 -0
- package/driver/rule-shape.mjs +8 -8
- package/driver/run-economics.mjs +3 -3
- package/driver/run-integrity.mjs +2 -2
- package/driver/runner.mjs +17 -5
- package/driver/scope-facts.mjs +89 -12
- package/driver/score-redaction.mjs +247 -0
- package/driver/screen-gate.mjs +1 -1
- package/driver/search-policy.mjs +17 -0
- package/driver/skills/README.md +2 -2
- package/driver/skills/blind-frame/SKILL.md +2 -2
- package/driver/skills/clearance-common-law/SKILL.md +29 -36
- package/driver/skills/clearance-common-law/perplexity-prompts.md +8 -30
- package/driver/skills/clearance-register/SKILL.md +15 -16
- package/driver/skills/clearance-register/digest.md +15 -31
- package/driver/skills/clearance-register/register-recipes.md +14 -56
- package/driver/skills/clearance-register/unit.md +37 -48
- package/driver/skills/clearance-search/SKILL.md +6 -8
- package/driver/skills/clearance-search/delivery-contract.md +0 -7
- package/driver/skills/clearance-search/firm-wide-reasoning.md +5 -4
- package/driver/skills/clearance-search/phase2-execution.md +5 -6
- package/driver/skills/clearance-search/report-prose.md +5 -5
- package/driver/skills/clearance-search/risk-framework-triage.md +2 -2
- package/driver/skills/clearance-search/synthesis-rules.md +9 -9
- package/driver/skills/clearance-search/template-formatting.md +2 -2
- package/driver/skills/clearance-variants/SKILL.md +8 -8
- package/driver/skills/clearance-variants/transliteration-scripts.md +2 -2
- package/driver/skills/frame-diff/SKILL.md +2 -2
- package/driver/skills/knockout-assess/SKILL.md +40 -10
- package/driver/skills/knockout-frame/SKILL.md +42 -10
- package/driver/skills/matter-frame/SKILL.md +12 -5
- package/driver/skills/matter-frame/watchlist-reference.md +1 -1
- package/driver/skills/narrative-refutation/SKILL.md +5 -3
- package/driver/skills/placement-inquiry/SKILL.md +5 -1
- package/driver/stage-context.mjs +27 -6
- package/driver/stages-knockout.mjs +92 -79
- package/driver/stages.mjs +111 -68
- package/driver/stray-artifacts.mjs +9 -5
- package/driver/suite-census.json +696 -150
- package/driver/synthesis-record.mjs +26 -4
- package/driver/systemd/README.md +5 -6
- package/driver/systemd/clearotron-client-mcp.service +24 -0
- package/driver/systemd/clearotron-mcp-face.service +24 -0
- package/driver/systemd/clearotron-portal.service +24 -0
- package/driver/systemd/clearotron-worker.service +29 -0
- package/driver/tokens.mjs +10 -9
- package/driver/turnaround-bands.mjs +1 -1
- package/driver/unit-inventory.mjs +24 -43
- package/driver/variant-manifest-model.mjs +22 -3
- package/driver/verify-knockout.mjs +163 -4
- package/driver/verify.mjs +42 -16
- package/driver/web-grid.mjs +150 -0
- package/driver/whatif-memo-run.mjs +1 -1
- package/driver/withheld-families.mjs +113 -4
- package/driver/worker-heartbeat.mjs +13 -2
- package/mcp-server/CHANGELOG.md +8 -0
- package/mcp-server/README.md +1 -1
- package/mcp-server/lib/knockout.mjs +1 -1
- package/mcp-server/lib/ops.mjs +10 -10
- package/mcp-server/lib/scrub.mjs +1 -1
- package/mcp-server/lib/trace.mjs +3 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/server.mjs +3 -3
- package/package.json +1 -1
- package/portal-ui/dist/assets/{index-DVtz44vH.js → index-BPAUjcI0.js} +3 -2
- package/portal-ui/dist/assets/{index-5CCwiJG7.css → index-D2wrw9cH.css} +18 -2
- package/portal-ui/dist/assets/plus-jakarta-sans-BUCHxqJ-.woff2 +0 -0
- package/portal-ui/dist/index.html +2 -15
- package/portal-ui/package.json +1 -1
- package/providers/_shared/README.md +1 -1
- package/providers/_shared/answer-memory.mjs +199 -0
- package/providers/_shared/enumerate.mjs +155 -50
- package/providers/_shared/execute-plan.mjs +62 -16
- package/providers/_shared/ledger-path.mjs +1 -1
- package/providers/_shared/ledger.mjs +48 -6
- package/providers/_shared/plan-guards.mjs +7 -0
- package/providers/_shared/script-form.mjs +24 -5
- package/providers/_shared/term-shape.mjs +5 -5
- package/providers/clarivate/src/capabilities.js +11 -0
- package/providers/clarivate/src/core.js +193 -18
- package/providers/corsearch/README.md +1 -2
- package/providers/corsearch/src/core.js +2 -2
- package/providers/jx-subclass/lookup.mjs +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/README.md +8 -7
- package/providers/oauth-mcp-bridge/bridge.mjs +21 -5
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/oauth-mcp-bridge/systemd/courtlistener-mcp.service +1 -1
- package/providers/oauth-mcp-bridge/warm-server.mjs +16 -5
- package/providers/perplexity/src/core.js +246 -21
- package/providers/signa/src/capabilities.js +24 -0
- package/providers/signa/src/core.js +93 -25
- package/scripts/README.md +2 -1
- package/scripts/added-reference-check.mjs +2 -1
- package/scripts/backup-recall-stores.mjs +5 -5
- package/scripts/demo-evidence.mjs +2 -1
- package/scripts/deprecate-below.mjs +114 -2
- package/scripts/drive-env-check.mjs +2 -1
- package/scripts/e2e-first-time.mjs +469 -0
- package/scripts/e2e-scenario-ops.mjs +441 -0
- package/scripts/e2e.mjs +104 -16
- package/scripts/env-audit.mjs +8 -0
- package/scripts/freeze-example-run.mjs +7 -7
- package/scripts/generated-files-are-current.mjs +2 -1
- package/scripts/hand-off-exits-probe.mjs +75 -0
- package/scripts/import-cycle-check.mjs +2 -1
- package/scripts/merge-shape-check.mjs +2 -1
- package/scripts/package-size-budget.mjs +2 -1
- package/scripts/record-carry-probe.mjs +1 -1
- package/scripts/release-entry-catch-up.mjs +77 -8
- package/scripts/release-install-check.mjs +4 -1
- package/scripts/release-note-required.mjs +139 -9
- package/scripts/release-rehearsal-version.mjs +60 -0
- package/scripts/release-sbom.mjs +104 -0
- package/scripts/release-visible-check.mjs +7 -5
- package/scripts/render-check.mjs +5 -2
- package/scripts/report-offline-render-check.mjs +169 -0
- package/scripts/score.mjs +79 -8
- package/scripts/strip-titles-and-attributions.mjs +2 -1
- package/scripts/strip-tracker-citations.mjs +2 -1
- package/scripts/test-full.mjs +2 -1
- package/scripts/test-run.mjs +10 -3
- package/scripts/third-party-notices.mjs +3 -1
- package/scripts/travelling-predicates.mjs +1 -1
- package/scripts/writing-standard-check.mjs +2 -1
- package/shared/brand-fonts.mjs +59 -0
- package/shared/brand.mjs +6 -6
- package/shared/browser-temp-root.mjs +3 -2
- package/shared/doctrine-overlay.mjs +1 -1
- package/shared/driver-dir.mjs +47 -11
- package/shared/fonts/OFL-fira-code.txt +93 -0
- package/shared/fonts/OFL-plus-jakarta-sans.txt +93 -0
- package/shared/fonts/README.md +37 -0
- package/shared/fonts/fira-code.woff2 +0 -0
- package/shared/fonts/plus-jakarta-sans.woff2 +0 -0
- package/shared/names-in-force.mjs +2 -0
- package/shared/npm-cli.mjs +23 -0
- package/shared/os-advice.mjs +14 -2
- package/shared/path-seps.mjs +42 -0
- package/shared/process-table.mjs +71 -3
- package/shared/reference-guard-classes.mjs +30 -6
- package/shared/root-doc-commands.mjs +6 -2
- package/shared/running-start.mjs +24 -7
- package/shared/scope.mjs +2 -2
- package/shared/wsl.mjs +1 -1
- package/driver/known-conflicts.mjs +0 -327
package/.env.example
CHANGED
|
@@ -265,6 +265,15 @@ CLARIVATE_API_KEY=changeme
|
|
|
265
265
|
# Signa.
|
|
266
266
|
SIGNA_API_KEY=changeme
|
|
267
267
|
#
|
|
268
|
+
# The run memory: within one run, reuse an answer the register already gave instead of asking again.
|
|
269
|
+
# Signa's starts off: `watch` still sends every request and records whether a held answer would have
|
|
270
|
+
# matched, and `on` reuses held answers. Clarivate's starts on, and `off` asks every time. Read when a run
|
|
271
|
+
# starts, by providers/_shared/answer-memory.mjs.
|
|
272
|
+
# effect: tuning
|
|
273
|
+
CLEAROTRON_SIGNA_ANSWER_MEMORY=off
|
|
274
|
+
# effect: tuning
|
|
275
|
+
CLEAROTRON_CLARIVATE_ANSWER_MEMORY=on
|
|
276
|
+
#
|
|
268
277
|
# USPTO, local — the free US source, and the only one here that is not a paid vendor. TWO
|
|
269
278
|
# variables, and they are needed at different moments, which is why neither substitutes for the other.
|
|
270
279
|
#
|
package/INSTALL.md
CHANGED
|
@@ -45,23 +45,10 @@ run is [mcp-server/CONNECT.md](mcp-server/CONNECT.md), and why something is the
|
|
|
45
45
|
Node 20, or on 22.0 through 22.12, the install succeeds and the first US search fails with
|
|
46
46
|
`ERR_UNKNOWN_BUILTIN_MODULE`, saying nothing about Node. `package.json` declares the floor, the
|
|
47
47
|
install refuses below it before writing anything, and `nvm use` picks the pin up.
|
|
48
|
-
- **macOS, Linux
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
release. Until then, on Windows,
|
|
53
|
-
`wsl --install -d Ubuntu`, then `wsl -d Ubuntu`, and work through this page
|
|
54
|
-
from **inside** that distribution. Name it: plain `wsl` can open a minimal image with no apt, no
|
|
55
|
-
curl and no bash, and everything below assumes Ubuntu. A fresh Ubuntu has no Node at all, and
|
|
56
|
-
apt's package is below the floor above, so `npx` answers "not found" before anything of ours runs.
|
|
57
|
-
From the Ubuntu prompt:
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
sudo apt update && sudo apt install -y curl
|
|
61
|
-
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
|
|
62
|
-
. "$HOME/.nvm/nvm.sh" && nvm install 22 # 22.13 or newer, per the floor above
|
|
63
|
-
npx clearotron install # offers to install the reasoning program if the machine has none, and shows you how to sign it in
|
|
64
|
-
```
|
|
48
|
+
- **macOS, Linux or Windows.** On Windows everything runs natively in PowerShell, with no WSL2, no Git
|
|
49
|
+
and no administrator rights: `npm install --global clearotron`, then `clearotron install`, which installs
|
|
50
|
+
the AI program you choose into your own user folder. On Windows and macOS Clearotron runs in the window
|
|
51
|
+
you start it from, and closing the window stops it.
|
|
65
52
|
|
|
66
53
|
Run from `npx`, the install first installs Clearotron under `~/.local`, as `npm install -g --prefix
|
|
67
54
|
~/.local` would, and finishes from there. That way the `clearotron` command and your assistant's connection
|
|
@@ -163,20 +150,6 @@ with `sudo`; install under your home instead: `npm install -g clearotron --prefi
|
|
|
163
150
|
**Upgrade production to stables only.** What each channel promises and how often one is cut:
|
|
164
151
|
**[docs/RELEASES.md](docs/RELEASES.md)**.
|
|
165
152
|
|
|
166
|
-
**Upgrading an install made before 0.2.2: pin the agent id first.** The default agent id changed from
|
|
167
|
-
`clawdi` to `localagent`, and that id is part of a path — your runs live under
|
|
168
|
-
`<workspaceRoot>/workspace-<agent>/studio/clearance-search/`. If you never set an agent id, the upgraded
|
|
169
|
-
install reads a workspace that does not exist yet, and an empty workspace looks like an account with no
|
|
170
|
-
runs rather than like a misconfiguration. Set **both** names in your environment file before starting
|
|
171
|
-
it, because the register-search servers read their own:
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
CLEAROTRON_DEFAULT_AGENT=clawdi
|
|
175
|
-
CLEAROTRON_GATHER_AGENT=clawdi
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
If your environment file already sets them, nothing changes. A first-time install needs neither.
|
|
179
|
-
|
|
180
153
|
The rest of this section is about the two ways a package reaches you, which is a separate question from
|
|
181
154
|
which version it is.
|
|
182
155
|
|
package/README.md
CHANGED
|
@@ -42,14 +42,14 @@ exits, and keeps the folder the same way.
|
|
|
42
42
|
npx clearotron install
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Node 22.13 or newer, on macOS or
|
|
45
|
+
Node 22.13 or newer, on macOS, Linux or Windows. It needs no root: it puts the program under `~/.local`, with the
|
|
46
46
|
`clearotron` command in `~/.local/bin`, then asks one question at a time and checks each credential
|
|
47
47
|
before it saves it. Every command below uses the `clearotron` it installed — the same channel you
|
|
48
48
|
installed from — so if `~/.local/bin` is not on your `PATH` yet, type the full path,
|
|
49
|
-
`~/.local/bin/clearotron`, instead. **On Windows
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
`~/.local/bin/clearotron`, instead. **On Windows it runs natively, from PowerShell: no WSL2, no Git and
|
|
50
|
+
no administrator rights.** Install it with `npm install --global clearotron`, then run `clearotron install`.
|
|
51
|
+
It runs in the window you start it from, as on a Mac: closing the window stops a search, and the next
|
|
52
|
+
`clearotron start` picks it up again.
|
|
53
53
|
|
|
54
54
|
Installing with npm's own global form instead is covered in [INSTALL.md](INSTALL.md) §2.
|
|
55
55
|
|
package/bin/brandowner.mjs
CHANGED
|
@@ -112,7 +112,7 @@ const USAGE = `
|
|
|
112
112
|
--name the brand owner's legal name (required)
|
|
113
113
|
--domains comma-separated email domains that resolve to this owner
|
|
114
114
|
--platforms comma-separated marketplaces their searches cover
|
|
115
|
-
omitted ⇒ the
|
|
115
|
+
omitted ⇒ none — searches use the general web, plus any stores chosen for each matter
|
|
116
116
|
--framework their risk framework, as skills/clearance-search/<file>.md
|
|
117
117
|
omitted ⇒ the Generic default is applied and named in the output
|
|
118
118
|
--industry free text, shown on their profile
|
package/bin/onboard.mjs
CHANGED
|
@@ -103,6 +103,8 @@ import { resolveAuthMode, CLOUD_SWITCH, CLOUD_SETTINGS, CLOUD_SECRETS, cloudsSwi
|
|
|
103
103
|
import { isInsideCheckout } from "../shared/inside-checkout.mjs"; // — one copy of the rule, and it is testable
|
|
104
104
|
import { packagedBuild as sharedPackagedBuild } from "../shared/packaged-build.mjs"; // — one reader of build-info.json, reachable from the driver
|
|
105
105
|
import { processTable } from "../shared/process-table.mjs"; // — /proc is not the only box
|
|
106
|
+
import { hasSep } from "../shared/path-seps.mjs"; // a Windows path is written with "\"
|
|
107
|
+
import { npmInvocation } from "../shared/npm-cli.mjs"; // npm without a shell, which Windows needs
|
|
106
108
|
import { programsFromAnotherCheckout } from "../shared/checkout-move.mjs";
|
|
107
109
|
import { entrypointOf } from "../driver/systemd/install-census.mjs"; // one ExecStart parser
|
|
108
110
|
import { overlayReport, renderOverlayReport } from "../shared/doctrine-overlay.mjs"; // — the doctor reports the overlay
|
|
@@ -790,7 +792,8 @@ export async function askSignIn(io, { localAccount = "user", existing = null } =
|
|
|
790
792
|
* Was `resolveClaudeBin`. The body never had anything claude-specific in it; the NAME was the last place
|
|
791
793
|
* this file still assumed one engine, and a name that lies is how the second adapter stayed invisible.
|
|
792
794
|
*/
|
|
793
|
-
export function resolveEngineBin(bin, { env = process.env, wsl = null, onWindowsDrive = null, engine = null, enginesDir = undefined
|
|
795
|
+
export function resolveEngineBin(bin, { env = process.env, wsl = null, onWindowsDrive = null, engine = null, enginesDir = undefined,
|
|
796
|
+
platform = process.platform } = {}) {
|
|
794
797
|
// A VIEW OVER THE DRIVER'S ONE RESOLVER (driver.config.mjs resolveEngineProgram), which every other
|
|
795
798
|
// reader asks too: the run door, the inventory the portal reads, and both adapters. This used to be a
|
|
796
799
|
// second PATH walk of its own, and it was the only one that knew to pass over a Windows copy under WSL;
|
|
@@ -803,14 +806,14 @@ export function resolveEngineBin(bin, { env = process.env, wsl = null, onWindows
|
|
|
803
806
|
// Linux runner without root, so an arm that could only supply a PATH could never drive the skip.
|
|
804
807
|
const id = engine ?? Object.keys(ENGINE_BINARIES).find((k) => ENGINE_BINARIES[k].fallback === bin) ?? DEFAULT_ENGINE_ID;
|
|
805
808
|
const spec = ENGINE_BINARIES[id];
|
|
806
|
-
const r = resolveEngineProgram(id, { env: { ...env, [spec.env]: bin }, wsl, onWindowsDrive, enginesDir });
|
|
809
|
+
const r = resolveEngineProgram(id, { env: { ...env, [spec.env]: bin }, wsl, onWindowsDrive, enginesDir, platform });
|
|
807
810
|
const found = { source: r.source, version: r.version, rejected: r.rejected, skipped: r.skipped, windowsShim: r.windowsShim };
|
|
808
811
|
// A RELATIVE path is the trap for both adapters: stage subprocesses run with cwd set to the RUN
|
|
809
812
|
// DIRECTORY (driver/engine/common.mjs resolveSpawnCwd), so it resolves against a directory that did not
|
|
810
813
|
// exist at setup time. Reported with its absolute form from here, which is what the wizard then uses.
|
|
811
814
|
if (r.relative) { const abs = resolve(bin); return { path: abs, executable: isExec(abs), relative: true, ...found }; }
|
|
812
815
|
// A PATH SOMEBODY TYPED IS NOT OVERRULED, only reported: the resolver never falls through from it.
|
|
813
|
-
if (!r.resolved && r.explicit && bin
|
|
816
|
+
if (!r.resolved && r.explicit && hasSep(bin, platform)) return { path: resolve(bin), executable: false, relative: false, ...found };
|
|
814
817
|
return { path: r.resolved, executable: Boolean(r.resolved), relative: false, ...found };
|
|
815
818
|
}
|
|
816
819
|
|
|
@@ -927,30 +930,11 @@ export function windowsShimNote(skipped, bin) {
|
|
|
927
930
|
const isExec = (p) => { try { accessSync(p, constants.X_OK); return statSync(p).isFile(); } catch { return false; } };
|
|
928
931
|
|
|
929
932
|
/**
|
|
930
|
-
*
|
|
931
|
-
*
|
|
933
|
+
* How to leave demo mode. The first line is the same everywhere; the second differs by whether the
|
|
934
|
+
* machine has a background form. Windows has none, so there it names closing the window and starting
|
|
935
|
+
* again, where elsewhere it names restarting the services and the file background services read.
|
|
932
936
|
*
|
|
933
|
-
*
|
|
934
|
-
* population this protects is the one that cannot run this suite to find out. An arm on a Linux runner
|
|
935
|
-
* has to be able to ask what a Windows reader is shown, or the Windows text is asserted by nobody —
|
|
936
|
-
* and reading `process.platform` inside the caller would make that arm read source instead of driving
|
|
937
|
-
* the answer.
|
|
938
|
-
*/
|
|
939
|
-
export function platformEngineRefusal({ platform = process.platform } = {}) {
|
|
940
|
-
if (platform !== "win32") return null;
|
|
941
|
-
return "this engine does not run on native Windows — stage subprocesses are spawned with POSIX path "
|
|
942
|
-
+ "and process semantics, so the run door refuses before it reads PATH. Run it under WSL2, or in "
|
|
943
|
-
+ "the devcontainer. The demo works here as it is: it replays finished reports and needs no engine.";
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
/**
|
|
947
|
-
* How to leave demo mode, which is not the same instruction everywhere.
|
|
948
|
-
*
|
|
949
|
-
* On the platform the run door refuses, the standard advice is a loop: install a CLI the reader may
|
|
950
|
-
* already have, then restart a service — neither of which can change the answer, because the refusal
|
|
951
|
-
* is about the platform rather than the program. Naming WSL2 is the only instruction that ends it.
|
|
952
|
-
*
|
|
953
|
-
* ELSEWHERE IT NAMES SETUP, WHICH DOES THE WORK NOW. This used to say to install the CLI by hand with
|
|
937
|
+
* IT NAMES SETUP, WHICH DOES THE WORK NOW. This used to say to install the CLI by hand with
|
|
954
938
|
* `npm install -g`, then run `claude` to sign in, and to restart the engine service so it re-read its PATH.
|
|
955
939
|
* Setup offers to install the program itself, into a folder that is not on PATH, so the bare `claude` it
|
|
956
940
|
* named was a command the reader's shell does not have; and a run finds that copy without re-reading PATH.
|
|
@@ -961,16 +945,16 @@ export function platformEngineRefusal({ platform = process.platform } = {}) {
|
|
|
961
945
|
*/
|
|
962
946
|
export function leaveDemoAdvice(engSpec, { platform = process.platform, command = reachableCommand("install"),
|
|
963
947
|
startCommand = reachableCommand("start") } = {}) {
|
|
948
|
+
const first = `To leave demo: run \`${command}\`. It offers to install ${engSpec.vendor}'s CLI if this machine has none, `
|
|
949
|
+
+ "asks how it is paid for, and proves it with one turn; if the CLI is not signed in, it names the command that signs it in.";
|
|
950
|
+
// WINDOWS HAS NO BACKGROUND FORM, so the second line there names the one restart it has: the window.
|
|
964
951
|
if (platform === "win32") {
|
|
965
|
-
return [
|
|
966
|
-
+ `${engSpec.vendor}'s CLI natively will not change this — the run door refuses on the platform, `
|
|
967
|
-
+ "not on the program."];
|
|
952
|
+
return [first, "If Clearotron is already running, close its window and start it again: it reads the settings setup writes when it starts."];
|
|
968
953
|
}
|
|
969
954
|
// BACKGROUND SERVICES ARE THE EXCEPTION to "restart them": they read `~/.env`, which setup does not write,
|
|
970
955
|
// and `start --background` only adds lines to it (see programDisagreement below, which says the same).
|
|
971
956
|
return [
|
|
972
|
-
|
|
973
|
-
+ "asks how it is paid for, and proves it with one turn; if the CLI is not signed in, it names the command that signs it in.",
|
|
957
|
+
first,
|
|
974
958
|
"If Clearotron's services are already running, restart them afterwards: they read the settings setup writes "
|
|
975
959
|
+ "when they start, and the portal reports what the engine saw when it last started. Background services "
|
|
976
960
|
+ `read \`~/.env\` instead, which setup does not write, and \`${startCommand} --background\` adds to it only the `
|
|
@@ -1813,27 +1797,6 @@ export async function runCheck() {
|
|
|
1813
1797
|
const seen = versionOf(bin);
|
|
1814
1798
|
const copyWords = (b) => `${b.source === "installed" ? "the copy Clearotron installed"
|
|
1815
1799
|
: b.source === "explicit" ? `set in ${engSpec.env}` : "on PATH"}${seen ? `, version ${seen}` : ", version not read"}`;
|
|
1816
|
-
// ── NATIVE WINDOWS IS ANSWERED HERE, BEFORE ANY PATH IS RESOLVED OR REPORTED ──────────────────
|
|
1817
|
-
//
|
|
1818
|
-
// `resolveEngineBin` tests a candidate with `accessSync(X_OK)` and `isFile()`. Windows has no
|
|
1819
|
-
// execute bit, so X_OK is satisfied by any file that exists — and an npm global install writes
|
|
1820
|
-
// BOTH `claude`, an extensionless shell script for Git Bash, and `claude.cmd`. The POSIX test
|
|
1821
|
-
// passes on the shell script, so this line reported the engine FOUND at a path CreateProcess
|
|
1822
|
-
// cannot start, and the probe then failed with `spawn claude ENOENT`. Reported from a real
|
|
1823
|
-
// Windows run, 2026-09-09.
|
|
1824
|
-
//
|
|
1825
|
-
// RESOLVING `claude.cmd` INSTEAD WOULD MOVE THE CONTRADICTION, NOT REMOVE IT.
|
|
1826
|
-
// `preflightEngineBinary` refuses native Windows by name, and the run door calls it
|
|
1827
|
-
// unconditionally — so a clearance cannot run here whatever this line finds. Making the probe
|
|
1828
|
-
// succeed would produce a doctor that says ready, a probe that passes, and a run that refuses
|
|
1829
|
-
// anyway: the same disagreement one step later, and more convincing for having spawned something.
|
|
1830
|
-
//
|
|
1831
|
-
// So doctor says what the run door says. That is the property — the two agree — and the reader
|
|
1832
|
-
// gets one refusal they can act on instead of a found-then-failed sequence that sends them
|
|
1833
|
-
// looking for a PATH problem they do not have. The demo is unaffected: it replays finished runs
|
|
1834
|
-
// and needs no engine, which is why four reports published on that same Windows box.
|
|
1835
|
-
const platformRefusal = platformEngineRefusal();
|
|
1836
|
-
if (platformRefusal) problem(platformRefusal);
|
|
1837
1800
|
// ── AND WHETHER THAT COPY IS NEW ENOUGH FOR THE MODELS IT WILL BE ASKED FOR ──────────────────
|
|
1838
1801
|
//
|
|
1839
1802
|
// The floor governed the copy setup INSTALLS and nothing else, and a copy already on the machine
|
|
@@ -1845,7 +1808,7 @@ export async function runCheck() {
|
|
|
1845
1808
|
// compared. The last is said rather than passed over — doctor's own contract is that a failure to
|
|
1846
1809
|
// look is not a clean result — and it is the ordinary state for a copy the machine installed by
|
|
1847
1810
|
// another route, where there is no package.json to read and doctor spawns nothing to ask.
|
|
1848
|
-
|
|
1811
|
+
if (bin.executable && !bin.relative && olderThanFloor(seen, engSpec.floor) === true)
|
|
1849
1812
|
problem(`${bin.path} — ${copyWords(bin)}. Clearotron needs ${engSpec.floor} or newer. ${belowFloorTail(engSpec, bin)}`);
|
|
1850
1813
|
else if (bin.executable && !bin.relative) {
|
|
1851
1814
|
ok(`${bin.path} — ${copyWords(bin)}`);
|
|
@@ -3872,26 +3835,6 @@ try {
|
|
|
3872
3835
|
if (!pick.id) { sayNoEngine(); break; }
|
|
3873
3836
|
const eng = ENGINE_BINARIES[pick.id];
|
|
3874
3837
|
|
|
3875
|
-
// ── NATIVE WINDOWS IS ANSWERED HERE TOO, AND THE ESCAPE IS OFFERED RATHER THAN LEFT ON A MENU ──
|
|
3876
|
-
//
|
|
3877
|
-
// Doctor learned this and the wizard did not, and the gap is worse than it sounds: the two "no
|
|
3878
|
-
// engine" escapes below both sit behind "no usable binary", and on Windows that is FALSE. X_OK is
|
|
3879
|
-
// satisfied by any file that exists, so the wizard says "found C:\...\claude", walks past both
|
|
3880
|
-
// escapes, asks which lane pays, spends a proof turn, and meets `spawn claude ENOENT`. The only
|
|
3881
|
-
// prompt left then defaults to No and returns to the engine menu — with nothing changed between
|
|
3882
|
-
// iterations, on either lane. Reported from a real Windows run: the menu's last row was the one
|
|
3883
|
-
// way out and nothing on screen said so.
|
|
3884
|
-
//
|
|
3885
|
-
// So the refusal is stated before a candidate is resolved, and finishing with no engine is OFFERED
|
|
3886
|
-
// rather than being a row the reader has to notice. Declining leaves them on the menu exactly as
|
|
3887
|
-
// before, which is the branch that was already there.
|
|
3888
|
-
const wizardRefusal = platformEngineRefusal();
|
|
3889
|
-
if (wizardRefusal) {
|
|
3890
|
-
problem(wizardRefusal);
|
|
3891
|
-
if (await confirm("Finish setup with no engine configured?", true)) { sayNoEngine(); break engine; }
|
|
3892
|
-
continue;
|
|
3893
|
-
}
|
|
3894
|
-
|
|
3895
3838
|
let bin = resolveEngineBin(process.env[eng.env] || eng.fallback, { engine: pick.id });
|
|
3896
3839
|
// Under WSL the Windows build on the appended PATH has been passed over. Said before the install
|
|
3897
3840
|
// offer below, because otherwise a reader whose Linux install is genuinely missing is asked to
|
|
@@ -3929,7 +3872,8 @@ try {
|
|
|
3929
3872
|
say("");
|
|
3930
3873
|
if (await confirm(`Run \`${engineInstallCommand(eng, dir)}\` now?`, false)) {
|
|
3931
3874
|
say(` $ ${engineInstallCommand(eng, dir)}`);
|
|
3932
|
-
const
|
|
3875
|
+
const npm = npmInvocation(engineInstallArgs(eng, dir));
|
|
3876
|
+
const r = spawnSync(npm.command, npm.args, { stdio: "inherit" });
|
|
3933
3877
|
// THE EXIT CODE IS NOT THE ANSWER, and this is the issue's own rule. An install that exits 0 may
|
|
3934
3878
|
// have left the vendor's placeholder (npm told to skip install scripts), and one that exits
|
|
3935
3879
|
// non-zero may still have left a usable program. So what decides is the same resolution the
|
package/bin/start.mjs
CHANGED
|
@@ -87,6 +87,7 @@ import { writeSecretFile } from "../shared/secret-file.mjs"; // one atomic wri
|
|
|
87
87
|
import { runRequiredNames, missingRequirements, CLOUD_ROUTES } from "../driver/run-requirements.mjs";
|
|
88
88
|
import { CLOUD_SWITCH, cloudsSwitchedOn } from "../driver/engine/auth.mjs"; // a switch compared the way the program reads it: on or off
|
|
89
89
|
import { ENGINE_BINARIES, DEFAULT_ENGINE_ID as RUN_DEFAULT_ENGINE, resolveEngineProgram } from "../driver/driver.config.mjs";
|
|
90
|
+
import { spawnsDetached, killWindowsTreesNow } from "../driver/engine/engine-spawn.mjs"; // Windows has no process group to stop
|
|
90
91
|
import { unitEnvironment, unitValue } from "../driver/unit-environment.mjs"; // the PATH the worker unit will run with, read the way doctor reads it
|
|
91
92
|
|
|
92
93
|
/**
|
|
@@ -149,7 +150,7 @@ import { addressRefusal } from "../shared/staff-domain.mjs";
|
|
|
149
150
|
// entry is written through them, so this command cannot produce a shape of its own.
|
|
150
151
|
import { withPerson, withOrganisation, withCompany } from "../shared/grants-edit.mjs";
|
|
151
152
|
import { assertGrantsShape, resolvePerson } from "../shared/scope.mjs";
|
|
152
|
-
import { backgroundManager } from "../shared/os-advice.mjs";
|
|
153
|
+
import { backgroundManager, backgroundRefusal } from "../shared/os-advice.mjs";
|
|
153
154
|
import { recordRunning } from "../shared/running-start.mjs";
|
|
154
155
|
import { frontingVariablesSet } from "../shared/install-auth.mjs"; // — one owner for what counts as a proxy in front of a door
|
|
155
156
|
|
|
@@ -713,6 +714,26 @@ export const BACKGROUND_EXCLUDED = Object.freeze({
|
|
|
713
714
|
"profile-service.service": "the portal constructs the profile service IN-PROCESS (driver/portal-service.mjs); the standalone unit is the separate-editor deployment shape and running both double-serves the store",
|
|
714
715
|
});
|
|
715
716
|
|
|
717
|
+
/**
|
|
718
|
+
* The key that signs every access token, and the one it replaces while a rotation is under way. Only the
|
|
719
|
+
* processes that mint or check keys hold them: the portal and the two doors. The worker holds neither,
|
|
720
|
+
* because it starts the AI program for every stage.
|
|
721
|
+
*/
|
|
722
|
+
export const SIGNING_KEY_NAMES = Object.freeze(["TRADEMARK_MCP_TOKEN_SECRET", "TRADEMARK_MCP_TOKEN_SECRET_PREVIOUS"]);
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* A child's environment: this supervisor's, then the child's own block over it. The signing keys are the
|
|
726
|
+
* exception: a child whose own block does not carry the current key gets neither of them. This process
|
|
727
|
+
* sets the key on itself to mint the portal's key, so without the exception every child would inherit it,
|
|
728
|
+
* whatever its block said. A child that is handed the key keeps the previous one too, from this process,
|
|
729
|
+
* so a rotation under way still checks the keys signed before it.
|
|
730
|
+
*/
|
|
731
|
+
export function childProcessEnv(parent = process.env, own = {}) {
|
|
732
|
+
const env = { ...parent, ...own };
|
|
733
|
+
if (!Object.hasOwn(own, SIGNING_KEY_NAMES[0])) for (const k of SIGNING_KEY_NAMES) delete env[k];
|
|
734
|
+
return env;
|
|
735
|
+
}
|
|
736
|
+
|
|
716
737
|
export function childEnv({ ports, paths, user, portalSecret, tokenSecret, opsToken, host = HOST, localWorker = false, demo = false, clientFence = null, credential = null, env = process.env }) {
|
|
717
738
|
// ONE AUTHOR FOR THIS EXPRESSION. The hosted install path composes the same
|
|
718
739
|
// origin, and the near-miss is specific: this is an ORIGIN, the portal's client appends `/mcp`
|
|
@@ -811,8 +832,10 @@ export function childEnv({ ports, paths, user, portalSecret, tokenSecret, opsTok
|
|
|
811
832
|
CLIENT_MCP_HTTP_PORT: String(ports.client),
|
|
812
833
|
},
|
|
813
834
|
// The worker needs the install's PATHS and nothing else — no ports, no secrets, no door config. It
|
|
814
|
-
// talks to the queue and the pool, not to either listener.
|
|
815
|
-
worker
|
|
835
|
+
// talks to the queue and the pool, not to either listener. `shared` carries the signing key for the two
|
|
836
|
+
// doors and the portal, which mint and check keys; the worker does neither, and it starts the AI program
|
|
837
|
+
// for every stage, so the key is taken out here and withheld again at the spawn (childProcessEnv).
|
|
838
|
+
worker: Object.fromEntries(Object.entries(shared).filter(([k]) => !SIGNING_KEY_NAMES.includes(k))),
|
|
816
839
|
// ── THE CLIENT DOOR, ON BOTH PATHS ( — F26) ──────────────────────────────
|
|
817
840
|
//
|
|
818
841
|
// Ruling, restated several times in session: START BOTH. It already held on the systemd path
|
|
@@ -976,6 +999,9 @@ if (isMain) {
|
|
|
976
999
|
// exactly how to drain when they do.
|
|
977
1000
|
const wantWorker = !argv.includes("--no-worker");
|
|
978
1001
|
const wantBackground = argv.includes("--background");
|
|
1002
|
+
// ON WINDOWS THERE IS NO BACKGROUND FORM: it is systemd units. Answered here, before anything is written
|
|
1003
|
+
// or systemctl is called, in the owner's sentence and nothing else, so no `start:` prefix either.
|
|
1004
|
+
if (wantBackground && backgroundRefusal()) { err(backgroundRefusal()); process.exit(1); }
|
|
979
1005
|
|
|
980
1006
|
// A flag with its value forgotten is a MISTAKE, not a request for the default. `--base` swallowing the
|
|
981
1007
|
// next flag, or falling through to ~/trademark, would put an install somewhere nobody asked for.
|
|
@@ -2187,8 +2213,9 @@ if (isMain) {
|
|
|
2187
2213
|
cwd: REPO,
|
|
2188
2214
|
// Its OWN process group, so Ctrl-C reaches this supervisor alone and teardown is one ordered
|
|
2189
2215
|
// sequence rather than a race between the terminal's signal and ours. It also means the group
|
|
2190
|
-
// kill below reaches anything a child spawns, not just the child.
|
|
2191
|
-
|
|
2216
|
+
// kill below reaches anything a child spawns, not just the child. NOT ON WINDOWS, where a detached
|
|
2217
|
+
// child has no console and outlives the window it was started from (engine/engine-spawn.mjs).
|
|
2218
|
+
detached: spawnsDetached(),
|
|
2192
2219
|
// — STDERR IS TEED RATHER THAN INHERITED, AND ONLY STDERR.
|
|
2193
2220
|
//
|
|
2194
2221
|
// These children announce a refusal as one FATAL line on stderr and exit 1. With `inherit` the
|
|
@@ -2202,7 +2229,7 @@ if (isMain) {
|
|
|
2202
2229
|
// progress rendering intact. Piping a stream costs the child its TTY, so only the stream that has to
|
|
2203
2230
|
// be read is piped.
|
|
2204
2231
|
stdio: ["ignore", "inherit", "pipe"],
|
|
2205
|
-
env:
|
|
2232
|
+
env: childProcessEnv(process.env, env),
|
|
2206
2233
|
});
|
|
2207
2234
|
// The last lines of that child's stderr, forwarded on as they arrive so nothing is delayed or
|
|
2208
2235
|
// swallowed, and kept so the failure can QUOTE them rather than refer to them.
|
|
@@ -2252,7 +2279,6 @@ if (isMain) {
|
|
|
2252
2279
|
return rec;
|
|
2253
2280
|
};
|
|
2254
2281
|
|
|
2255
|
-
const signalGroup = (rec, sig) => { try { process.kill(-rec.child.pid, sig); } catch { /* already gone */ } };
|
|
2256
2282
|
|
|
2257
2283
|
async function shutdown(code) {
|
|
2258
2284
|
if (stopping) return;
|
|
@@ -2260,15 +2286,14 @@ if (isMain) {
|
|
|
2260
2286
|
const live = children.filter((c) => c.alive);
|
|
2261
2287
|
if (live.length) {
|
|
2262
2288
|
say(`\n stopping ${live.map((c) => c.name).join(" and ")}…`);
|
|
2263
|
-
|
|
2289
|
+
stopChildren(live.map((c) => c.child), "SIGTERM");
|
|
2264
2290
|
// A grace window, then the hammer. Nothing this command starts holds unflushed client state, so
|
|
2265
2291
|
// the window is short; it exists so a service gets to close its listener and log its own line.
|
|
2266
2292
|
const deadline = Date.now() + 5000;
|
|
2267
2293
|
while (children.some((c) => c.alive) && Date.now() < deadline) await new Promise((r) => setTimeout(r, 100));
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
}
|
|
2294
|
+
const stuck = children.filter((x) => x.alive);
|
|
2295
|
+
for (const c of stuck) err(` ${c.name} did not stop on SIGTERM — killing it.`);
|
|
2296
|
+
stopChildren(stuck.map((c) => c.child), "SIGKILL");
|
|
2272
2297
|
await new Promise((r) => setTimeout(r, 200));
|
|
2273
2298
|
}
|
|
2274
2299
|
// ── THE DEMO TAKES ITS FOLDER WITH IT ───────────────────────────────────────────────────────────
|
|
@@ -2589,8 +2614,7 @@ if (isMain) {
|
|
|
2589
2614
|
// `--background` installs and enables service units. There are none on Windows, so both the offer
|
|
2590
2615
|
// and the sentence naming what manages them were wrong there — a reader was told to run a flag that
|
|
2591
2616
|
// cannot succeed and given a service manager that is not on the machine and cannot be put there.
|
|
2592
|
-
// Reported from a real run.
|
|
2593
|
-
// does not have.
|
|
2617
|
+
// Reported from a real run. The rule: do not name a route this platform does not have.
|
|
2594
2618
|
for (const line of backgroundOfferLines({ demo: DEMO, keep: DEMO_KEEP || READER_BASE, manager: backgroundManager(), start: invoke("start") })) say(line);
|
|
2595
2619
|
say("");
|
|
2596
2620
|
}
|
|
@@ -2693,6 +2717,26 @@ export function windowCloseSignals() {
|
|
|
2693
2717
|
return ["SIGHUP"];
|
|
2694
2718
|
}
|
|
2695
2719
|
|
|
2720
|
+
/**
|
|
2721
|
+
* Stop children of this command, and whatever they started.
|
|
2722
|
+
*
|
|
2723
|
+
* On Linux and macOS each child leads its own process group, so the signal goes to the group, and to the
|
|
2724
|
+
* child alone when the group is already gone. Windows has neither groups nor signals a Node child can
|
|
2725
|
+
* catch, so there every child's process tree is ended at once, from one process listing: closing the
|
|
2726
|
+
* window leaves only seconds, and a listing takes most of one. The second call finds nothing left.
|
|
2727
|
+
* `platform` and the two effects are parameters so both branches run on a Linux CI.
|
|
2728
|
+
*/
|
|
2729
|
+
export function stopChildren(children, sig, { platform = process.platform, kill = (pid, s) => process.kill(pid, s),
|
|
2730
|
+
endTrees = killWindowsTreesNow } = {}) {
|
|
2731
|
+
const live = (children ?? []).filter((c) => c?.pid);
|
|
2732
|
+
if (platform === "win32") {
|
|
2733
|
+
const pids = live.filter((c) => c.exitCode === null && c.signalCode === null).map((c) => c.pid);
|
|
2734
|
+
if (pids.length) endTrees(pids);
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
for (const c of live) { try { kill(-c.pid, sig); } catch { try { c.kill(sig); } catch { /* already gone */ } } }
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2696
2740
|
/**
|
|
2697
2741
|
* What a reader is told when a child this install cannot run without exits.
|
|
2698
2742
|
*
|
package/bin/update.mjs
CHANGED
|
@@ -53,6 +53,7 @@ import { fileURLToPath } from "node:url";
|
|
|
53
53
|
|
|
54
54
|
import { config, ENGINE_BINARIES, enginesFolder, engineInstallArgs } from "../driver/driver.config.mjs";
|
|
55
55
|
import { isInsideCheckout } from "../shared/inside-checkout.mjs"; // — one copy of the rule
|
|
56
|
+
import { npmInvocation } from "../shared/npm-cli.mjs"; // npm without a shell, which Windows needs
|
|
56
57
|
import { overlayReport, renderOverlayReport, treeFiles } from "../shared/doctrine-overlay.mjs";
|
|
57
58
|
import { liveRunHolds } from "../driver/deploy-live-run-guard.mjs"; // — one live-run test, shared with deploy-preflight
|
|
58
59
|
import { isEntrypoint } from "../shared/is-entrypoint.mjs"; // — one entry-point test, all spellings
|
|
@@ -210,7 +211,9 @@ function refreshEngines(engines, dir = enginesFolder()) {
|
|
|
210
211
|
|
|
211
212
|
function runInCheckout(cmd, args) {
|
|
212
213
|
say(`\n $ ${cmd} ${args.join(" ")}`);
|
|
213
|
-
|
|
214
|
+
// npm is a batch file on Windows, which Node will not start without a shell (shared/npm-cli.mjs).
|
|
215
|
+
const run = cmd === "npm" ? npmInvocation(args) : { command: cmd, args };
|
|
216
|
+
const r = spawnSync(run.command, run.args, { cwd: REPO, stdio: "inherit" });
|
|
214
217
|
if (r.error) { console.error(` could not run ${cmd}: ${r.error.message}`); return 70; }
|
|
215
218
|
if (r.signal) { console.error(` ${cmd} was killed by ${r.signal}`); return 70; }
|
|
216
219
|
return r.status ?? 0;
|
|
@@ -383,7 +386,12 @@ export async function update(argv = process.argv.slice(2)) {
|
|
|
383
386
|
return pulled;
|
|
384
387
|
}
|
|
385
388
|
|
|
386
|
-
|
|
389
|
+
// NO INSTALL SCRIPTS. A dependency's install script runs with everything this account can reach, and an
|
|
390
|
+
// update fetches whatever the registry serves that day. None is needed here: the only dependency that
|
|
391
|
+
// carries one is optional and macOS-only, and this package's own two are the Node floor, which the CLI
|
|
392
|
+
// checks at every start, and a contributor's git hooks. Dev dependencies stay, because the portal
|
|
393
|
+
// bundle below is rebuilt from them.
|
|
394
|
+
const installed = runInCheckout("npm", ["ci", "--ignore-scripts"]);
|
|
387
395
|
if (installed !== 0) {
|
|
388
396
|
console.error("\n npm ci failed. The code is updated; its dependencies are not, so this install is");
|
|
389
397
|
console.error(" in a half-updated state and should not be run until that command succeeds.\n");
|
package/build-info.json
CHANGED
package/docs/README.md
CHANGED
|
@@ -40,6 +40,7 @@ the vendor flags approximate is UNKNOWN, never a number.
|
|
|
40
40
|
| [`E2E.md`](E2E.md) | Proving a deployment works end to end |
|
|
41
41
|
| [`GLOSSARY.md`](GLOSSARY.md) | The words this codebase uses in a particular way — one line each, for a contributor meeting them for the first time |
|
|
42
42
|
| [`SECURITY.md`](SECURITY.md) | The security envelope — what protects what, and where it is enforced in code. To report a vulnerability, use [`../SECURITY.md`](../SECURITY.md) |
|
|
43
|
+
| [`SECURITY-OWASP.md`](SECURITY-OWASP.md) | Each risk on OWASP's two lists for AI systems, what Clearotron does about it, and the file on `main` where that lives |
|
|
43
44
|
|
|
44
45
|
Access control is stated once, in [`SECURITY.md`](SECURITY.md): who may see which runs, what an unset
|
|
45
46
|
guest list means on each face, and which door proves identity. Issuing and rotating grants and ops
|