pi-oracle 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +8 -5
- package/docs/ORACLE_DESIGN.md +10 -7
- package/docs/ORACLE_ISOLATED_PI_VALIDATION.md +7 -7
- package/docs/platform-smoke.md +23 -20
- package/extensions/oracle/index.ts +10 -4
- package/extensions/oracle/lib/config.ts +63 -9
- package/extensions/oracle/lib/poller.ts +1 -0
- package/extensions/oracle/lib/tools.ts +2 -2
- package/package.json +5 -5
- package/platform-smoke.config.mjs +10 -3
- package/scripts/oracle-real-smoke.mjs +7 -3
- package/scripts/platform-smoke/crabbox-runner.mjs +6 -6
- package/scripts/platform-smoke/invariants.mjs +17 -1
- package/scripts/platform-smoke/platform-build-windows.ps1 +2 -2
- package/scripts/platform-smoke/targets.mjs +4 -4
- package/scripts/platform-smoke.mjs +14 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.7.7 - 2026-06-08
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- updated the local pi development baseline to `@earendil-works/pi-coding-agent` / `@earendil-works/pi-ai` `0.79.0` and regenerated the npm lockfile
|
|
9
|
+
- documented `pi` `0.79.0+` as the suggested tested floor while keeping pi runtime packages as optional wildcard peers so npm peer ranges do not block users from trying newer pi releases
|
|
10
|
+
- updated isolated local-extension and packed package validation workflows to pass explicit `--approve` when they intentionally trust their temporary project fixtures under Pi 0.79.0 project-trust rules
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- made project-local `.pi/extensions/oracle.json` overrides honor explicit Pi project-trust opt-outs (`--no-approve` or a saved “do not trust” decision) while preserving the historical default of loading safe project overrides for existing oracle users
|
|
14
|
+
|
|
15
|
+
### Compatibility
|
|
16
|
+
- reviewed the pi `0.79.0` changelog, project-trust docs, extension docs, package docs, prompt-template docs, SDK/RPC exports, and matching examples; the oracle extension remains compatible with current extension lifecycle and package install/update behavior
|
|
17
|
+
|
|
18
|
+
## 0.7.6 - 2026-06-04
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- updated the local pi development baseline to `@earendil-works/pi-coding-agent` / `@earendil-works/pi-ai` `0.78.1` and regenerated the npm lockfile
|
|
22
|
+
- documented `pi` `0.78.1+` as the suggested tested floor while keeping pi runtime packages as optional wildcard peers so newer pi releases are not blocked by npm peer ranges
|
|
23
|
+
- added Ant Ling, NVIDIA NIM, and MiniMax China provider env mappings to the oracle real-smoke/platform-smoke provider metadata
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- made startup poller and wake-up routing mode-aware with Pi 0.78.1 `ctx.mode`, so print and JSON one-shot runs do not start background pollers or publish oracle UI status
|
|
27
|
+
- guarded oracle startup status and warning notifications with `ctx.hasUI` for non-UI modes
|
|
28
|
+
|
|
29
|
+
### Compatibility
|
|
30
|
+
- reviewed the pi `0.78.1` changelog, extension docs, package docs, prompt-template docs, and matching examples; the oracle extension remains compatible with current extension lifecycle and package install/update behavior
|
|
31
|
+
|
|
5
32
|
## 0.7.5 - 2026-06-02
|
|
6
33
|
|
|
7
34
|
### Added
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`pi-oracle` lets a `pi` agent send hard, long-running work to ChatGPT.com or Grok through the web app, with repo archives, background execution, saved results, and a best-effort wake-up back into `pi` when the answer is ready.
|
|
4
4
|
|
|
5
|
-
> Status: experimental public beta. Validated on macOS, Linux, and Windows native with Chromium-family browsers and the
|
|
5
|
+
> Status: experimental public beta. Validated on macOS, Linux, and Windows native with Chromium-family browsers and pi `0.79.0`. Pi `0.79.0+` is the suggested tested floor for project-trust-aware package/runtime validation, but pi-bundled runtime packages remain optional wildcard peers so npm peer ranges do not block users from trying newer pi releases. Normal oracle jobs run in an isolated browser profile, not your active browser window.
|
|
6
6
|
|
|
7
7
|
## What a successful run looks like
|
|
8
8
|
|
|
@@ -77,7 +77,7 @@ You need:
|
|
|
77
77
|
|
|
78
78
|
- macOS, Linux, or Windows native
|
|
79
79
|
- Node.js 22 or newer
|
|
80
|
-
- `pi` 0.
|
|
80
|
+
- Suggested tested floor: `pi` 0.79.0 or newer; older pi versions are not blocked by package metadata but are outside the current validation baseline
|
|
81
81
|
- Google Chrome/Chromium or another Chromium-family browser
|
|
82
82
|
- ChatGPT or Grok already signed in to the configured local browser profile for the provider you plan to use
|
|
83
83
|
- `agent-browser`, `tar`, and `zstd` available on the machine
|
|
@@ -179,6 +179,8 @@ Agent-facing tools:
|
|
|
179
179
|
|
|
180
180
|
Most users can start with defaults. Set an agent-level config only when you need a non-default provider, mode, preset, or browser profile.
|
|
181
181
|
|
|
182
|
+
Pi 0.79.0 gates project-local inputs behind project trust. `pi-oracle` preserves its historical risk-on extension behavior for existing users: project-local `.pi/extensions/oracle.json` safe overrides still load by default for compatibility. They are ignored when you explicitly opt out of project-local inputs with `--no-approve` or save a “do not trust” decision for the project. Privileged browser/auth settings still come only from the agent-level config.
|
|
183
|
+
|
|
182
184
|
`~/.pi/agent/extensions/oracle.json`
|
|
183
185
|
|
|
184
186
|
```json
|
|
@@ -383,12 +385,13 @@ Use the narrowest validation workflow that proves the change:
|
|
|
383
385
|
| --- | --- |
|
|
384
386
|
| Everyday local iteration | `npm run verify:oracle` |
|
|
385
387
|
| Platform-sensitive changes | `npm run smoke:platform:doctor`, then a focused `node scripts/platform-smoke.mjs run --target <target> --suite <suite>` |
|
|
386
|
-
|
|
|
388
|
+
| Platform matrix proof | `npm run smoke:platform:all` |
|
|
389
|
+
| Publish/release gate | `npm run release:check` |
|
|
387
390
|
|
|
388
|
-
For macOS, Ubuntu, and Windows native package/build plus packed runtime validation, use [`docs/platform-smoke.md`](docs/platform-smoke.md). The full release
|
|
391
|
+
For macOS, Ubuntu, and Windows native package/build plus packed runtime validation, use [`docs/platform-smoke.md`](docs/platform-smoke.md). The full release gate is:
|
|
389
392
|
|
|
390
393
|
```bash
|
|
391
|
-
npm run
|
|
394
|
+
npm run release:check
|
|
392
395
|
```
|
|
393
396
|
|
|
394
397
|
The real runtime suite defaults to deterministic installed-tool execution so platform proof stays bounded. Provider/model defaults remain `zai/glm-5.1` for doctor/config and for optional model-agent debugging; override with `PI_ORACLE_REAL_TEST_PROVIDER` and `PI_ORACLE_REAL_TEST_MODEL` when needed. For inner-loop source loading only, use `npm run smoke:real:source`; it is not release proof. Set `PI_ORACLE_REAL_TEST_MODEL_AGENT=1` only when debugging the slower model-agent path. The optional second real-agent negative symlink check is opt-in via `PI_ORACLE_REAL_TEST_NEGATIVE_SYMLINK=1`; `npm run sanity:oracle` covers archive/symlink rejection by default without adding another model-agent turn to the platform release gate.
|
package/docs/ORACLE_DESIGN.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# pi-oracle design
|
|
2
2
|
|
|
3
|
-
Status: isolated-profile concurrency architecture implemented in code
|
|
4
|
-
Date: 2026-04
|
|
3
|
+
Status: isolated-profile concurrency architecture implemented in code and validated against the current pi baseline.
|
|
4
|
+
Date: 2026-06-04
|
|
5
5
|
|
|
6
6
|
Companion doc:
|
|
7
7
|
- `docs/ORACLE_RECOVERY_DRILL.md` — safe expired-auth recovery validation drill
|
|
8
8
|
|
|
9
9
|
Compatibility target:
|
|
10
|
-
- `pi` 0.
|
|
10
|
+
- `pi` 0.79.0+ is the suggested tested floor for current project-trust-aware package/runtime validation
|
|
11
|
+
- package metadata keeps pi runtime packages as optional wildcard peers, so this suggested floor is not enforced as a hard npm install requirement
|
|
11
12
|
- current extension lifecycle only; no backward-compatibility shims for removed `session_switch` / `session_fork` events
|
|
12
13
|
|
|
13
14
|
## Goal
|
|
@@ -228,9 +229,7 @@ Merged config locations:
|
|
|
228
229
|
- global: `~/.pi/agent/extensions/oracle.json`
|
|
229
230
|
- project: `.pi/extensions/oracle.json`
|
|
230
231
|
|
|
231
|
-
Project config remains restricted to safe overrides only.
|
|
232
|
-
|
|
233
|
-
Browser/auth settings are global-only because they control local privileged browser state.
|
|
232
|
+
Project config remains restricted to safe overrides only. On Pi 0.79.0+, pi itself gates project-local inputs behind project trust, but `pi-oracle` keeps its historical risk-on extension behavior for this package-specific safe override file: `.pi/extensions/oracle.json` loads by default for compatibility, and is ignored only when the run passes `--no-approve` or the project has a saved “do not trust” decision. This preserves the existing extension experience while still honoring explicit opt-out/distrust decisions. Browser/auth settings remain global-only because they control local privileged browser state.
|
|
234
233
|
|
|
235
234
|
### Current config shape
|
|
236
235
|
|
|
@@ -632,11 +631,15 @@ Remaining non-blocking hardening work:
|
|
|
632
631
|
- keep hardening model-selection verification against future ChatGPT UI variation
|
|
633
632
|
|
|
634
633
|
Recent proof points:
|
|
634
|
+
- Pi 0.79.0 release gate: `npm run release:check` passed on 2026-06-08, including `verify:oracle` plus Crabbox macOS, Ubuntu, and Windows native `platform-build` and `real-extension` suites
|
|
635
|
+
- Pi 0.79.0 platform artifacts: `.artifacts/platform-smoke/run-1780938522145-50q2f2` (macOS platform-build), `.artifacts/platform-smoke/run-1780938572090-bi87g5` (macOS real-extension), `.artifacts/platform-smoke/run-1780938542847-quridb` (Ubuntu platform-build), `.artifacts/platform-smoke/run-1780938587248-c8uo4c` (Ubuntu real-extension), `.artifacts/platform-smoke/run-1780938585007-l0xapp` (Windows native platform-build), `.artifacts/platform-smoke/run-1780938820527-c1j8tt` (Windows native real-extension)
|
|
636
|
+
- Pi 0.79.0 isolated local-extension model-agent smoke: `.artifacts/real-smoke/run-1780935835596-pfbn5o` passed with `PI_ORACLE_REAL_TEST_MODEL_AGENT=1 npm run smoke:real:source`
|
|
637
|
+
- Pi 0.79.0 packed-install smoke: `.artifacts/real-smoke/run-1780935825537-pmna07` passed with `npm run smoke:real:packed`
|
|
635
638
|
- expired-auth drill fail path: `a2460bc1-7d89-4041-b67d-39680d310325`
|
|
636
639
|
- `/oracle-auth` repair evidence: the per-run `/tmp/pi-oracle-auth-*/oracle-auth.log` bundle path printed by `/oracle-auth`
|
|
637
640
|
- expired-auth drill post-repair success: `fa26a2a7-0057-4a21-b3e0-71c1d020facf`
|
|
638
641
|
- successful multi-artifact completion: `b6b3599c-6b91-4315-adfa-8a83aa5eda9b`
|
|
639
642
|
- repo-owned sanity harness: `npm run sanity:oracle`
|
|
640
643
|
- real installed-extension smoke source of truth: `scripts/oracle-real-smoke.mjs`; required release proof runs packed-install mode (`npm run smoke:real:packed`) and executes installed-package `oracle_submit` deterministically, with optional slower model-agent debugging via `PI_ORACLE_REAL_TEST_MODEL_AGENT=1`; source mode (`npm run smoke:real:source`) is inner-loop/debug only
|
|
641
|
-
- macOS, Ubuntu, and Windows native package/build/runtime smoke source of truth: `docs/platform-smoke.md`; use `npm run verify:oracle` for everyday local iteration, `npm run smoke:platform:doctor` plus a focused target/suite run for platform-sensitive changes,
|
|
644
|
+
- macOS, Ubuntu, and Windows native package/build/runtime smoke source of truth: `docs/platform-smoke.md`; use `npm run verify:oracle` for everyday local iteration, `npm run smoke:platform:doctor` plus a focused target/suite run for platform-sensitive changes, `npm run smoke:platform:all` for doctor-first platform matrix evidence, and `npm run release:check` for the full local-plus-platform release gate
|
|
642
645
|
- release gate: `npm run release:check`, also used by `prepublishOnly`, combines static verification and all required Crabbox platform smokes
|
|
@@ -24,15 +24,15 @@ That keeps the validation run from reusing your normal `pi` agent state.
|
|
|
24
24
|
The extension is loaded from the local checkout with:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
pi --no-extensions -e "$REPO/extensions/oracle/index.ts"
|
|
27
|
+
pi --approve --no-extensions -e "$REPO/extensions/oracle/index.ts"
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
That ensures the session is exercising the in-repo code, not a globally installed package.
|
|
30
|
+
That ensures the session is exercising the in-repo code, not a globally installed package. `--approve` is intentional for this isolated workflow on Pi 0.79.0+: the test fixture is this trusted checkout, and non-interactive/scripted validation must not block on the project-trust prompt.
|
|
31
31
|
|
|
32
32
|
If you also need the in-repo `/oracle` prompt template, load it explicitly instead of installing this repository as a project-local package:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
pi --no-extensions -e "$REPO/extensions/oracle/index.ts" \
|
|
35
|
+
pi --approve --no-extensions -e "$REPO/extensions/oracle/index.ts" \
|
|
36
36
|
--no-prompt-templates --prompt-template "$REPO/prompts/oracle.md"
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -96,7 +96,7 @@ cleanup() {
|
|
|
96
96
|
trap cleanup EXIT
|
|
97
97
|
cleanup
|
|
98
98
|
|
|
99
|
-
TMUX_CMD1="cd '$REPO' && env PI_CODING_AGENT_DIR='$TEST1_AGENT' PI_ORACLE_JOBS_DIR='$TEST1_JOBS' PATH='$PATH' pi --session-dir '$TEST1_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
99
|
+
TMUX_CMD1="cd '$REPO' && env PI_CODING_AGENT_DIR='$TEST1_AGENT' PI_ORACLE_JOBS_DIR='$TEST1_JOBS' PATH='$PATH' pi --approve --session-dir '$TEST1_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
100
100
|
tmux new-session -d -s "$SESSION1" "$TMUX_CMD1"
|
|
101
101
|
sleep 8
|
|
102
102
|
tmux send-keys -t "$SESSION1":0.0 "$PROMPT1" Enter
|
|
@@ -129,7 +129,7 @@ PY
|
|
|
129
129
|
rm -f "$LIST"
|
|
130
130
|
fi
|
|
131
131
|
|
|
132
|
-
TMUX_CMD2="cd '$FIXTURE' && env PI_CODING_AGENT_DIR='$TEST2_AGENT' PI_ORACLE_JOBS_DIR='$TEST2_JOBS' PATH='$PATH' pi --session-dir '$TEST2_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
132
|
+
TMUX_CMD2="cd '$FIXTURE' && env PI_CODING_AGENT_DIR='$TEST2_AGENT' PI_ORACLE_JOBS_DIR='$TEST2_JOBS' PATH='$PATH' pi --approve --session-dir '$TEST2_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
133
133
|
tmux new-session -d -s "$SESSION2" "$TMUX_CMD2"
|
|
134
134
|
sleep 8
|
|
135
135
|
tmux send-keys -t "$SESSION2":0.0 "$PROMPT2" Enter
|
|
@@ -178,7 +178,7 @@ Expected behavior:
|
|
|
178
178
|
The main smoke test above calls `oracle_submit` directly, so it only needs the local extension entrypoint. If you also changed `prompts/oracle.md`, start the isolated session with the local prompt template explicitly loaded:
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
|
-
LOCAL_ORACLE_PI_CMD="pi --session-dir '$TEST1_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts' --no-prompt-templates --prompt-template '$REPO/prompts/oracle.md'"
|
|
181
|
+
LOCAL_ORACLE_PI_CMD="pi --approve --session-dir '$TEST1_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts' --no-prompt-templates --prompt-template '$REPO/prompts/oracle.md'"
|
|
182
182
|
TMUX_CMD1="cd '$REPO' && env PI_CODING_AGENT_DIR='$TEST1_AGENT' PI_ORACLE_JOBS_DIR='$TEST1_JOBS' PATH='$PATH' $LOCAL_ORACLE_PI_CMD"
|
|
183
183
|
```
|
|
184
184
|
|
|
@@ -226,7 +226,7 @@ cleanup() {
|
|
|
226
226
|
trap 'cleanup; rm -rf "$TEST_ROOT"' EXIT
|
|
227
227
|
cleanup
|
|
228
228
|
|
|
229
|
-
TMUX_CMD="cd '$REPO' && env PI_CODING_AGENT_DIR='$AGENT_DIR' PI_ORACLE_JOBS_DIR='$JOBS_DIR' AGENT_BROWSER_PATH='$FAKE_BROWSER' PI_ORACLE_AUTH_AGENT_BROWSER_TIMEOUT_MS='250' PI_ORACLE_AUTH_CLOSE_TIMEOUT_MS='250' PI_ORACLE_AUTH_KILL_GRACE_MS='100' PATH='$PATH' pi --session-dir '$SESSION_DIR' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
229
|
+
TMUX_CMD="cd '$REPO' && env PI_CODING_AGENT_DIR='$AGENT_DIR' PI_ORACLE_JOBS_DIR='$JOBS_DIR' AGENT_BROWSER_PATH='$FAKE_BROWSER' PI_ORACLE_AUTH_AGENT_BROWSER_TIMEOUT_MS='250' PI_ORACLE_AUTH_CLOSE_TIMEOUT_MS='250' PI_ORACLE_AUTH_KILL_GRACE_MS='100' PATH='$PATH' pi --approve --session-dir '$SESSION_DIR' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
230
230
|
|
|
231
231
|
tmux new-session -d -s "$SESSION_NAME" "$TMUX_CMD"
|
|
232
232
|
sleep 8
|
package/docs/platform-smoke.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
Required targets: `macos`, `ubuntu`, `windows-native`.
|
|
15
15
|
Required suites: `platform-build`, `real-extension`.
|
|
16
|
-
Crabbox baseline: `0.
|
|
16
|
+
Crabbox baseline: `0.26.0` or newer.
|
|
17
17
|
|
|
18
18
|
## Required local setup
|
|
19
19
|
|
|
@@ -25,7 +25,7 @@ crabbox --version
|
|
|
25
25
|
crabbox providers
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
`PI_ORACLE_SMOKE_CRABBOX` is
|
|
28
|
+
`PLATFORM_SMOKE_CRABBOX` is the reusable binary override. `PI_ORACLE_SMOKE_CRABBOX` is a project-specific alias and wins when both are set.
|
|
29
29
|
|
|
30
30
|
Target setup:
|
|
31
31
|
|
|
@@ -48,13 +48,14 @@ Use the narrowest workflow that proves the change. Do not run the full platform
|
|
|
48
48
|
| --- | --- | --- |
|
|
49
49
|
| Everyday local iteration | `npm run verify:oracle` | Syntax, bundle, platform-smoke invariants, type checks, oracle sanity, and package dry-run pass locally. |
|
|
50
50
|
| Platform-sensitive change | `npm run smoke:platform:doctor`, then `node scripts/platform-smoke.mjs run --target <target> --suite <suite>` | Target setup is ready and the affected platform/suite works without paying for unrelated targets. |
|
|
51
|
-
|
|
|
51
|
+
| Platform matrix proof | `npm run smoke:platform:all` | Doctor-first packed-install proof passes on every required target and suite. |
|
|
52
|
+
| Publish/release gate | `npm run release:check` | Local verification (`verify:oracle`) passes, then the doctor-first platform matrix passes. |
|
|
52
53
|
|
|
53
54
|
Platform-sensitive changes include archive behavior, process cleanup, runtime/browser profile handling, package metadata, Crabbox harness code, or anything that may differ across macOS/Linux/Windows.
|
|
54
55
|
|
|
55
56
|
## Commands
|
|
56
57
|
|
|
57
|
-
Doctor is mandatory before the full
|
|
58
|
+
Doctor is mandatory before the full platform matrix. The canonical all-target platform command enforces that:
|
|
58
59
|
|
|
59
60
|
```bash
|
|
60
61
|
npm run smoke:platform:all
|
|
@@ -70,13 +71,13 @@ npm run smoke:platform:windows-native
|
|
|
70
71
|
node scripts/platform-smoke.mjs run --target windows-native --suite real-extension
|
|
71
72
|
```
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
Full release gate:
|
|
74
75
|
|
|
75
76
|
```bash
|
|
76
77
|
npm run release:check
|
|
77
78
|
```
|
|
78
79
|
|
|
79
|
-
`prepublishOnly` runs `npm run release:check`.
|
|
80
|
+
`release:check` runs `verify:oracle` before `smoke:platform:all`, matching the Crabbox doctor-first release order: cheap harness checks, doctor, full matrix, then artifact review. `prepublishOnly` runs `npm run release:check`.
|
|
80
81
|
|
|
81
82
|
## What `platform-build` proves
|
|
82
83
|
|
|
@@ -89,8 +90,8 @@ On each required target, `platform-build`:
|
|
|
89
90
|
5. runs `npm pack`;
|
|
90
91
|
6. creates a fresh target-local pi project;
|
|
91
92
|
7. runs `npm install --no-save <packed tarball>`;
|
|
92
|
-
8. runs `pi install -l ./node_modules/pi-oracle
|
|
93
|
-
9. runs `pi list`;
|
|
93
|
+
8. runs `pi install -l ./node_modules/pi-oracle --approve` so Pi 0.79.0 project-trust gating intentionally trusts the temporary fixture;
|
|
94
|
+
9. runs `pi list --approve`;
|
|
94
95
|
10. asserts the installed package came from `node_modules/pi-oracle` and did not use `pi -e` / source-extension shortcuts.
|
|
95
96
|
|
|
96
97
|
## What `real-extension` proves
|
|
@@ -99,8 +100,8 @@ On each required target, `platform-build`:
|
|
|
99
100
|
|
|
100
101
|
1. packs this checkout with `npm pack`;
|
|
101
102
|
2. installs the tarball into a clean pi project;
|
|
102
|
-
3. runs `pi install -l ./node_modules/pi-oracle`;
|
|
103
|
-
4. asserts `pi list` shows the packed install path;
|
|
103
|
+
3. runs `pi install -l ./node_modules/pi-oracle --approve`;
|
|
104
|
+
4. asserts `pi list --approve` shows the packed install path;
|
|
104
105
|
5. executes `oracle_submit` from the installed package path, not source `pi -e`;
|
|
105
106
|
6. asserts whole-project archive creation and default exclusions.
|
|
106
107
|
|
|
@@ -112,7 +113,7 @@ For inner-loop/debug only, use:
|
|
|
112
113
|
npm run smoke:real:source
|
|
113
114
|
```
|
|
114
115
|
|
|
115
|
-
That source-mode smoke loads `extensions/oracle/index.ts` with `pi --no-extensions -e`; it is useful while developing but is not release proof.
|
|
116
|
+
That source-mode smoke loads `extensions/oracle/index.ts` with `pi --approve --no-extensions -e`; it is useful while developing but is not release proof.
|
|
116
117
|
|
|
117
118
|
## Artifacts
|
|
118
119
|
|
|
@@ -142,12 +143,14 @@ Artifacts are local evidence only. Do not commit or share them without redaction
|
|
|
142
143
|
|
|
143
144
|
## Windows template maintenance
|
|
144
145
|
|
|
145
|
-
When Windows lacks a reusable tool such as `zstd` or `agent-browser`, update `pi-extension-windows-template` rather than adding a per-run installer:
|
|
146
|
-
|
|
147
|
-
1.
|
|
148
|
-
2.
|
|
149
|
-
3.
|
|
150
|
-
4.
|
|
151
|
-
5.
|
|
152
|
-
6.
|
|
153
|
-
7.
|
|
146
|
+
When Windows lacks a reusable tool such as `zstd` or `agent-browser`, update the shared `pi-extension-windows-template` infrastructure rather than adding a per-run installer:
|
|
147
|
+
|
|
148
|
+
1. revert/switch `pi-extension-windows-template` to the current canonical `crabbox-ready` snapshot;
|
|
149
|
+
2. boot the template;
|
|
150
|
+
3. install/update the reusable tool globally without secrets;
|
|
151
|
+
4. verify from a fresh SSH session: `node --version`, `npm --version`, `git --version`, `tar --version`, `zstd --version`, `agent-browser --version`, and `agent-browser install`;
|
|
152
|
+
5. remove downloads, caches, checkouts, `.pi`, `.artifacts`, `.debug`, browser auth/session state, and secrets;
|
|
153
|
+
6. shut down cleanly;
|
|
154
|
+
7. create/promote the configured power-off `crabbox-ready` snapshot;
|
|
155
|
+
8. run `npm run smoke:platform:doctor` and `npm run smoke:platform:windows-native` against the promoted snapshot;
|
|
156
|
+
9. clean stale clones/leases.
|
|
@@ -39,9 +39,13 @@ export default function oracleExtension(pi: ExtensionAPI) {
|
|
|
39
39
|
function startPollerForContext(ctx: ExtensionContext) {
|
|
40
40
|
try {
|
|
41
41
|
const sessionFile = getSessionFile(ctx);
|
|
42
|
+
if (ctx.mode === "print" || ctx.mode === "json") {
|
|
43
|
+
stopPoller(ctx);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
42
46
|
if (!hasPersistedSessionFile(sessionFile)) {
|
|
43
47
|
stopPoller(ctx);
|
|
44
|
-
ctx.ui.setStatus("oracle", ctx.ui.theme.fg("accent", "oracle: unavailable"));
|
|
48
|
+
if (ctx.hasUI) ctx.ui.setStatus("oracle", ctx.ui.theme.fg("accent", "oracle: unavailable"));
|
|
45
49
|
return;
|
|
46
50
|
}
|
|
47
51
|
|
|
@@ -49,15 +53,17 @@ export default function oracleExtension(pi: ExtensionAPI) {
|
|
|
49
53
|
void runStartupMaintenance(ctx).catch((error) => {
|
|
50
54
|
const message = `Oracle startup maintenance failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
51
55
|
console.error(message);
|
|
52
|
-
ctx.ui.notify(message, "warning");
|
|
56
|
+
if (ctx.hasUI) ctx.ui.notify(message, "warning");
|
|
53
57
|
});
|
|
54
58
|
startPoller(pi, ctx, config.poller.intervalMs, workerPath);
|
|
55
59
|
refreshOracleStatus(ctx);
|
|
56
60
|
} catch (error) {
|
|
57
61
|
const message = error instanceof Error ? error.message : String(error);
|
|
58
62
|
stopPoller(ctx);
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
if (ctx.hasUI) {
|
|
64
|
+
ctx.ui.setStatus("oracle", ctx.ui.theme.fg("error", "oracle: config error"));
|
|
65
|
+
ctx.ui.notify(message, "warning");
|
|
66
|
+
}
|
|
61
67
|
}
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { execFileSync } from "node:child_process";
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
-
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import { getAgentDir, hasProjectTrustInputs, ProjectTrustStore } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import { isAbsolute, join, normalize } from "node:path";
|
|
11
11
|
import {
|
|
12
12
|
assertNotKnownBrowserUserDataPath,
|
|
@@ -313,27 +313,76 @@ const detectedChromeUserAgent = detectDefaultChromeUserAgent(detectedChromeExecu
|
|
|
313
313
|
const agentExtensionsDir = join(getAgentDir(), "extensions");
|
|
314
314
|
const detectedChromeProfileName = detectDefaultBrowserProfileSource(process.platform);
|
|
315
315
|
|
|
316
|
+
export interface OracleConfigLoadOptions {
|
|
317
|
+
/**
|
|
318
|
+
* Whether project-local oracle config may be loaded. Omit for the runtime
|
|
319
|
+
* policy that preserves oracle's historical project-config behavior while
|
|
320
|
+
* respecting explicit --no-approve and saved distrust decisions.
|
|
321
|
+
*/
|
|
322
|
+
projectConfigTrusted?: boolean;
|
|
323
|
+
/** Session cwd used for Pi's saved project-trust decision when config lookup is anchored to a derived project root. */
|
|
324
|
+
projectConfigTrustCwd?: string;
|
|
325
|
+
}
|
|
326
|
+
|
|
316
327
|
export interface OracleConfigLoadDetails {
|
|
317
328
|
agentDir: string;
|
|
318
329
|
agentConfigPath: string;
|
|
319
330
|
agentConfigExists: boolean;
|
|
320
331
|
projectConfigPath: string;
|
|
321
332
|
projectConfigExists: boolean;
|
|
333
|
+
projectConfigTrusted: boolean;
|
|
334
|
+
projectConfigLoaded: boolean;
|
|
335
|
+
projectConfigSkippedReason?: string;
|
|
322
336
|
effectiveAuthConfigPath: string;
|
|
323
337
|
effectiveAuthScope: "agent";
|
|
324
338
|
}
|
|
325
339
|
|
|
326
|
-
|
|
340
|
+
function getProjectTrustCliOverride(argv = process.argv): boolean | undefined {
|
|
341
|
+
let trusted: boolean | undefined;
|
|
342
|
+
for (const arg of argv.slice(2)) {
|
|
343
|
+
if (arg === "--approve" || arg === "-a") trusted = true;
|
|
344
|
+
if (arg === "--no-approve" || arg === "-na") trusted = false;
|
|
345
|
+
}
|
|
346
|
+
return trusted;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function isProjectConfigTrusted(cwd: string, agentDir: string, projectConfigExists: boolean, options?: OracleConfigLoadOptions): boolean {
|
|
350
|
+
if (options?.projectConfigTrusted !== undefined) return options.projectConfigTrusted;
|
|
351
|
+
const trustCwd = options?.projectConfigTrustCwd ?? cwd;
|
|
352
|
+
const cliOverride = getProjectTrustCliOverride();
|
|
353
|
+
if (cliOverride !== undefined) return cliOverride;
|
|
354
|
+
if (!projectConfigExists && !hasProjectTrustInputs(trustCwd)) return true;
|
|
355
|
+
try {
|
|
356
|
+
const trustStore = new ProjectTrustStore(agentDir);
|
|
357
|
+
const trustDecision = trustStore.get(trustCwd);
|
|
358
|
+
const rootDecision = trustCwd !== cwd ? trustStore.get(cwd) : null;
|
|
359
|
+
if (trustDecision !== null) return trustDecision;
|
|
360
|
+
if (rootDecision !== null) return rootDecision;
|
|
361
|
+
} catch {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function getOracleConfigLoadDetails(cwd: string, options?: OracleConfigLoadOptions): OracleConfigLoadDetails {
|
|
327
368
|
const agentDir = getAgentDir();
|
|
328
369
|
const projectRoot = getProjectId(cwd);
|
|
329
370
|
const agentConfigPath = join(agentDir, "extensions", "oracle.json");
|
|
330
371
|
const projectConfigPath = join(projectRoot, ".pi", "extensions", "oracle.json");
|
|
372
|
+
const projectConfigExists = existsSync(projectConfigPath);
|
|
373
|
+
const projectConfigTrusted = isProjectConfigTrusted(projectRoot, agentDir, projectConfigExists, options);
|
|
374
|
+
const projectConfigLoaded = projectConfigExists && projectConfigTrusted;
|
|
331
375
|
return {
|
|
332
376
|
agentDir,
|
|
333
377
|
agentConfigPath,
|
|
334
378
|
agentConfigExists: existsSync(agentConfigPath),
|
|
335
379
|
projectConfigPath,
|
|
336
|
-
projectConfigExists
|
|
380
|
+
projectConfigExists,
|
|
381
|
+
projectConfigTrusted,
|
|
382
|
+
projectConfigLoaded,
|
|
383
|
+
projectConfigSkippedReason: projectConfigExists && !projectConfigTrusted
|
|
384
|
+
? "Project oracle config is ignored because this run used --no-approve or the project has a saved untrusted decision."
|
|
385
|
+
: undefined,
|
|
337
386
|
effectiveAuthConfigPath: agentConfigPath,
|
|
338
387
|
effectiveAuthScope: "agent",
|
|
339
388
|
};
|
|
@@ -341,8 +390,11 @@ export function getOracleConfigLoadDetails(cwd: string): OracleConfigLoadDetails
|
|
|
341
390
|
|
|
342
391
|
export function formatOracleAuthConfigRemediation(details: OracleConfigLoadDetails): string {
|
|
343
392
|
const authFields = "auth.chromeProfile / auth.chromeCookiePath / auth.chromiumKeychain";
|
|
344
|
-
if (!details.
|
|
345
|
-
|
|
393
|
+
if (!details.projectConfigLoaded) {
|
|
394
|
+
const projectNote = details.projectConfigSkippedReason
|
|
395
|
+
? ` Project config at ${details.projectConfigPath} is present but not loaded because this run explicitly does not trust project-local inputs.`
|
|
396
|
+
: "";
|
|
397
|
+
return `Set ${authFields} in ${details.effectiveAuthConfigPath}.${projectNote}`;
|
|
346
398
|
}
|
|
347
399
|
return (
|
|
348
400
|
`Set ${authFields} in ${details.effectiveAuthConfigPath}. ` +
|
|
@@ -354,11 +406,13 @@ export function formatOracleAuthConfigSummary(details: OracleConfigLoadDetails):
|
|
|
354
406
|
const lines = [
|
|
355
407
|
`Effective oracle auth config: ${details.effectiveAuthConfigPath} (agent dir: ${details.agentDir}${details.agentConfigExists ? "" : "; create this file to override auth.*"})`,
|
|
356
408
|
];
|
|
357
|
-
if (details.
|
|
409
|
+
if (details.projectConfigLoaded) {
|
|
358
410
|
lines.push(
|
|
359
411
|
`Project oracle config also loaded: ${details.projectConfigPath} ` +
|
|
360
412
|
`(project scope can override ${[...PROJECT_OVERRIDE_KEYS].join("/")} only; auth.* still comes from ${details.effectiveAuthConfigPath}).`,
|
|
361
413
|
);
|
|
414
|
+
} else if (details.projectConfigSkippedReason) {
|
|
415
|
+
lines.push(`Project oracle config present but not loaded: ${details.projectConfigPath}. ${details.projectConfigSkippedReason}`);
|
|
362
416
|
}
|
|
363
417
|
return lines.join("\n");
|
|
364
418
|
}
|
|
@@ -665,9 +719,9 @@ function validateOracleConfig(value: unknown): OracleConfig {
|
|
|
665
719
|
};
|
|
666
720
|
}
|
|
667
721
|
|
|
668
|
-
export function loadOracleConfig(cwd: string): OracleConfig {
|
|
669
|
-
const details = getOracleConfigLoadDetails(cwd);
|
|
722
|
+
export function loadOracleConfig(cwd: string, options?: OracleConfigLoadOptions): OracleConfig {
|
|
723
|
+
const details = getOracleConfigLoadDetails(cwd, options);
|
|
670
724
|
const globalConfig = readJson(details.agentConfigPath);
|
|
671
|
-
const projectConfig = filterProjectConfig(readJson(details.projectConfigPath));
|
|
725
|
+
const projectConfig = details.projectConfigLoaded ? filterProjectConfig(readJson(details.projectConfigPath)) : undefined;
|
|
672
726
|
return validateOracleConfig(deepMerge(deepMerge(DEFAULT_CONFIG, globalConfig), projectConfig));
|
|
673
727
|
}
|
|
@@ -165,6 +165,7 @@ function getJobCountsForSession(sessionFile: string | undefined, cwd: string): {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
function refreshOracleStatusSnapshot(snapshot: OraclePollerContextSnapshot): void {
|
|
168
|
+
if (!snapshot.hasUI) return;
|
|
168
169
|
if (!snapshot.sessionFile) {
|
|
169
170
|
snapshot.ui.setStatus("oracle", snapshot.ui.theme.fg("accent", "oracle: unavailable"));
|
|
170
171
|
return;
|
|
@@ -1264,7 +1264,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1264
1264
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1265
1265
|
try {
|
|
1266
1266
|
const projectCwd = getProjectId(ctx.cwd);
|
|
1267
|
-
const baseConfig = loadOracleConfig(projectCwd);
|
|
1267
|
+
const baseConfig = loadOracleConfig(projectCwd, { projectConfigTrustCwd: ctx.cwd });
|
|
1268
1268
|
const provider = normalizeOracleProvider(params.provider, baseConfig.defaults.provider, "oracle_auth");
|
|
1269
1269
|
const message = await runOracleAuthBootstrap(authWorkerPath, projectCwd, provider);
|
|
1270
1270
|
return {
|
|
@@ -1311,7 +1311,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1311
1311
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1312
1312
|
try {
|
|
1313
1313
|
const projectCwd = getProjectId(ctx.cwd);
|
|
1314
|
-
const baseConfig = loadOracleConfig(projectCwd);
|
|
1314
|
+
const baseConfig = loadOracleConfig(projectCwd, { projectConfigTrustCwd: ctx.cwd });
|
|
1315
1315
|
const originSessionFile = requirePersistedSessionFile(getSessionFile(ctx), "submit oracle jobs");
|
|
1316
1316
|
const projectId = getProjectId(projectCwd);
|
|
1317
1317
|
const sessionId = getSessionId(originSessionFile, projectId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-oracle",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "ChatGPT and Grok web-oracle extension for pi with isolated browser auth, async jobs, and project-context archives.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
]
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
|
-
"check:oracle-extension": "node --check extensions/oracle/shared/browser-profile-helpers.mjs && node --check extensions/oracle/shared/process-helpers.mjs && node --check extensions/oracle/shared/state-coordination-helpers.mjs && node --check extensions/oracle/shared/job-coordination-helpers.mjs && node --check extensions/oracle/shared/job-lifecycle-helpers.mjs && node --check extensions/oracle/shared/job-observability-helpers.mjs && node --check extensions/oracle/worker/run-job.mjs && node --check extensions/oracle/worker/state-locks.mjs && node --check extensions/oracle/worker/artifact-heuristics.mjs && node --check extensions/oracle/worker/chatgpt-ui-helpers.mjs && node --check extensions/oracle/worker/chatgpt-flow-helpers.mjs && node --check extensions/oracle/worker/auth-flow-helpers.mjs && node --check extensions/oracle/worker/auth-cookie-policy.mjs && node --check extensions/oracle/worker/chromium-cookie-source.mjs && node --check extensions/oracle/worker/auth-bootstrap.mjs && esbuild extensions/oracle/index.ts --bundle --platform=node --format=esm --external:@earendil-works/pi-coding-agent --external
|
|
50
|
+
"check:oracle-extension": "node --check extensions/oracle/shared/browser-profile-helpers.mjs && node --check extensions/oracle/shared/process-helpers.mjs && node --check extensions/oracle/shared/state-coordination-helpers.mjs && node --check extensions/oracle/shared/job-coordination-helpers.mjs && node --check extensions/oracle/shared/job-lifecycle-helpers.mjs && node --check extensions/oracle/shared/job-observability-helpers.mjs && node --check extensions/oracle/worker/run-job.mjs && node --check extensions/oracle/worker/state-locks.mjs && node --check extensions/oracle/worker/artifact-heuristics.mjs && node --check extensions/oracle/worker/chatgpt-ui-helpers.mjs && node --check extensions/oracle/worker/chatgpt-flow-helpers.mjs && node --check extensions/oracle/worker/auth-flow-helpers.mjs && node --check extensions/oracle/worker/auth-cookie-policy.mjs && node --check extensions/oracle/worker/chromium-cookie-source.mjs && node --check extensions/oracle/worker/auth-bootstrap.mjs && esbuild extensions/oracle/index.ts --bundle --platform=node --format=esm --external:@earendil-works/pi-coding-agent --external:typebox --outfile=/tmp/pi-oracle-extension-check.js",
|
|
51
51
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
52
52
|
"typecheck:worker-helpers": "tsc --noEmit -p tsconfig.worker-helpers.json",
|
|
53
53
|
"sanity:oracle": "node scripts/oracle-sanity-runner.mjs",
|
|
@@ -83,9 +83,9 @@
|
|
|
83
83
|
"protobufjs": "7.6.1"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@earendil-works/pi-ai": "^0.
|
|
87
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
88
|
-
"@types/node": "^
|
|
86
|
+
"@earendil-works/pi-ai": "^0.79.0",
|
|
87
|
+
"@earendil-works/pi-coding-agent": "^0.79.0",
|
|
88
|
+
"@types/node": "^22.19.19",
|
|
89
89
|
"esbuild": "^0.28.0",
|
|
90
90
|
"tsx": "^4.22.3",
|
|
91
91
|
"typebox": "^1.1.39",
|
|
@@ -18,14 +18,18 @@ export default {
|
|
|
18
18
|
"node scripts/platform-smoke.mjs run --target <target> --suite <suite>",
|
|
19
19
|
],
|
|
20
20
|
},
|
|
21
|
-
|
|
22
|
-
description: "Doctor-first packed-install macOS/Ubuntu/Windows
|
|
21
|
+
platformMatrix: {
|
|
22
|
+
description: "Doctor-first packed-install macOS/Ubuntu/Windows platform proof.",
|
|
23
23
|
commands: ["npm run smoke:platform:all"],
|
|
24
24
|
},
|
|
25
|
+
release: {
|
|
26
|
+
description: "Full release gate: local verification plus the doctor-first platform matrix.",
|
|
27
|
+
commands: ["npm run release:check"],
|
|
28
|
+
},
|
|
25
29
|
},
|
|
26
30
|
requiredCrabbox: {
|
|
27
31
|
source: "https://github.com/openclaw/crabbox",
|
|
28
|
-
minVersion: "0.
|
|
32
|
+
minVersion: "0.26.0",
|
|
29
33
|
},
|
|
30
34
|
ubuntuContainerImage: "pi-oracle-platform-smoke:node24",
|
|
31
35
|
ubuntuContainerBaseImage: "cimg/node:24.16",
|
|
@@ -52,6 +56,9 @@ export default {
|
|
|
52
56
|
ai_gateway: ["AI_GATEWAY_API_KEY"],
|
|
53
57
|
mistral: ["MISTRAL_API_KEY"],
|
|
54
58
|
minimax: ["MINIMAX_API_KEY"],
|
|
59
|
+
"minimax-cn": ["MINIMAX_CN_API_KEY"],
|
|
60
|
+
"ant-ling": ["ANT_LING_API_KEY"],
|
|
61
|
+
nvidia: ["NVIDIA_API_KEY"],
|
|
55
62
|
moonshot: ["MOONSHOT_API_KEY"],
|
|
56
63
|
kimi: ["KIMI_API_KEY"],
|
|
57
64
|
},
|
|
@@ -22,7 +22,7 @@ function usage() {
|
|
|
22
22
|
console.log(`Usage: node scripts/oracle-real-smoke.mjs <doctor|run> [--mode packed|source]
|
|
23
23
|
|
|
24
24
|
Modes:
|
|
25
|
-
packed Release proof. npm pack -> clean pi project -> npm install tarball -> pi install -l -> run through installed package. Default.
|
|
25
|
+
packed Release proof. npm pack -> clean pi project -> npm install tarball -> pi install -l --approve -> run through installed package. Default.
|
|
26
26
|
source Inner-loop/debug only. Loads this checkout with pi --no-extensions -e extensions/oracle/index.ts.
|
|
27
27
|
|
|
28
28
|
Environment:
|
|
@@ -85,6 +85,9 @@ function apiKeyNameForProvider(provider) {
|
|
|
85
85
|
ai_gateway: "AI_GATEWAY_API_KEY",
|
|
86
86
|
mistral: "MISTRAL_API_KEY",
|
|
87
87
|
minimax: "MINIMAX_API_KEY",
|
|
88
|
+
"minimax-cn": "MINIMAX_CN_API_KEY",
|
|
89
|
+
"ant-ling": "ANT_LING_API_KEY",
|
|
90
|
+
nvidia: "NVIDIA_API_KEY",
|
|
88
91
|
moonshot: "MOONSHOT_API_KEY",
|
|
89
92
|
opencode: "OPENCODE_API_KEY",
|
|
90
93
|
kimi: "KIMI_API_KEY",
|
|
@@ -254,12 +257,12 @@ async function preparePackedProject({ runDir, provider, model, timeoutMs }) {
|
|
|
254
257
|
|
|
255
258
|
await mustRun(installDir, "npm-init", npm, ["init", "-y"], { cwd: piProject, env: process.env, timeoutMs: 60_000 });
|
|
256
259
|
await mustRun(installDir, "packed-node-install", npm, ["install", "--no-save", tarballPath], { cwd: piProject, env: process.env, timeoutMs: 120_000 });
|
|
257
|
-
await mustRun(installDir, "pi-install", piCommand(), ["install", "-l", `./node_modules/${PACKAGE_NAME}
|
|
260
|
+
await mustRun(installDir, "pi-install", piCommand(), ["install", "-l", `./node_modules/${PACKAGE_NAME}`, "--approve"], {
|
|
258
261
|
cwd: piProject,
|
|
259
262
|
env: { ...process.env, PI_OFFLINE: "1" },
|
|
260
263
|
timeoutMs: 120_000,
|
|
261
264
|
});
|
|
262
|
-
const piList = await mustRun(installDir, "pi-list", piCommand(), ["list"], {
|
|
265
|
+
const piList = await mustRun(installDir, "pi-list", piCommand(), ["list", "--approve"], {
|
|
263
266
|
cwd: piProject,
|
|
264
267
|
env: { ...process.env, PI_OFFLINE: "1" },
|
|
265
268
|
timeoutMs: 60_000,
|
|
@@ -339,6 +342,7 @@ console.log(JSON.stringify(result, null, 2));
|
|
|
339
342
|
async function runPiAgent({ prepared, agentDir, sessionDir, jobsDir, prompt, outDir, label, timeoutMs, stopAfterJobArchive = false }) {
|
|
340
343
|
mkdirSync(outDir, { recursive: true });
|
|
341
344
|
const args = [
|
|
345
|
+
"--approve",
|
|
342
346
|
"--print",
|
|
343
347
|
"--provider", prepared.provider,
|
|
344
348
|
"--model", prepared.model,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Thin Crabbox CLI wrapper for pi-oracle's
|
|
2
|
+
* Thin Crabbox CLI wrapper for pi-oracle's local platform smoke targets.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
|
|
7
|
-
const CRABBOX_BIN = process.env.PI_ORACLE_SMOKE_CRABBOX || "crabbox";
|
|
7
|
+
const CRABBOX_BIN = process.env.PI_ORACLE_SMOKE_CRABBOX || process.env.PLATFORM_SMOKE_CRABBOX || "crabbox";
|
|
8
8
|
|
|
9
9
|
function env(name) {
|
|
10
10
|
return process.env[name] ?? "";
|
|
@@ -58,9 +58,9 @@ export function execCrabbox(args, opts = {}) {
|
|
|
58
58
|
export function buildTargetBaseArgs(targetName, config = {}) {
|
|
59
59
|
switch (targetName) {
|
|
60
60
|
case "macos": {
|
|
61
|
-
const host = env("PI_ORACLE_SMOKE_MAC_HOST") || "localhost";
|
|
62
|
-
const user = env("PI_ORACLE_SMOKE_MAC_USER") || env("USER");
|
|
63
|
-
const workRoot = env("PI_ORACLE_SMOKE_MAC_WORK_ROOT") || `/Users/${env("USER")}/crabbox
|
|
61
|
+
const host = env("PI_ORACLE_SMOKE_MAC_HOST") || env("PLATFORM_SMOKE_MAC_HOST") || "localhost";
|
|
62
|
+
const user = env("PI_ORACLE_SMOKE_MAC_USER") || env("PLATFORM_SMOKE_MAC_USER") || env("USER");
|
|
63
|
+
const workRoot = env("PI_ORACLE_SMOKE_MAC_WORK_ROOT") || env("PLATFORM_SMOKE_MAC_WORK_ROOT") || `/Users/${env("USER")}/crabbox/${config.packageName ?? "pi-oracle"}`;
|
|
64
64
|
return [
|
|
65
65
|
"--provider", "ssh",
|
|
66
66
|
"--target", "macos",
|
|
@@ -81,7 +81,7 @@ export function buildTargetBaseArgs(targetName, config = {}) {
|
|
|
81
81
|
case "windows-native": {
|
|
82
82
|
const vm = env("PI_ORACLE_SMOKE_WINDOWS_VM") || env("PLATFORM_SMOKE_WINDOWS_VM") || config.windowsParallels?.sourceVm || "pi-extension-windows-template";
|
|
83
83
|
const snapshot = env("PI_ORACLE_SMOKE_WINDOWS_SNAPSHOT") || env("PLATFORM_SMOKE_WINDOWS_SNAPSHOT") || config.windowsParallels?.snapshot || "crabbox-ready";
|
|
84
|
-
const user = env("PI_ORACLE_SMOKE_WINDOWS_USER") || env("USER");
|
|
84
|
+
const user = env("PI_ORACLE_SMOKE_WINDOWS_USER") || env("PLATFORM_SMOKE_WINDOWS_USER") || env("USER");
|
|
85
85
|
const workRoot = env("PI_ORACLE_SMOKE_WINDOWS_NATIVE_WORK_ROOT") || env("PLATFORM_SMOKE_WINDOWS_WORK_ROOT") || `C:\\crabbox\\${config.packageName ?? "pi-oracle"}`;
|
|
86
86
|
return [
|
|
87
87
|
"--provider", "parallels",
|
|
@@ -17,6 +17,15 @@ function runNode(args) {
|
|
|
17
17
|
return spawnSync(process.execPath, args, { cwd: repoRoot, encoding: "utf8" });
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function testHelpTextIncludesTargetsAndExamples() {
|
|
21
|
+
const result = runNode(["scripts/platform-smoke.mjs", "--help"]);
|
|
22
|
+
assert.equal(result.status, 0, `help should exit cleanly: ${result.stderr}`);
|
|
23
|
+
assert.match(result.stdout, /Supported: macos,ubuntu,windows-native/, "help should list supported targets");
|
|
24
|
+
assert.match(result.stdout, /--suite\s+Suite name\. Supported: platform-build,real-extension/, "help should list supported suites");
|
|
25
|
+
assert.match(result.stdout, /npm run release:check/, "help should name the full release gate");
|
|
26
|
+
assert.match(result.stdout, /PLATFORM_SMOKE_CRABBOX/, "help should document reusable platform-smoke env knobs");
|
|
27
|
+
}
|
|
28
|
+
|
|
20
29
|
function testTargetSelection() {
|
|
21
30
|
const result = runNode(["scripts/platform-smoke.mjs", "run", "--target", "not-a-target", "--suite", "platform-build"]);
|
|
22
31
|
assert.notEqual(result.status, 0, "unsupported targets should fail before Crabbox runs");
|
|
@@ -79,9 +88,15 @@ function testCanonicalWorkflowConfig() {
|
|
|
79
88
|
assert.deepEqual(config.workflows?.everyday?.commands, ["npm run verify:oracle"], "everyday workflow should use the local verification gate");
|
|
80
89
|
assert(config.workflows?.platformSensitive?.commands?.includes("npm run smoke:platform:doctor"), "platform-sensitive workflow should start with doctor");
|
|
81
90
|
assert(config.workflows?.platformSensitive?.commands?.some((command) => command.includes("--target <target> --suite <suite>")), "platform-sensitive workflow should document focused target/suite runs");
|
|
82
|
-
assert.deepEqual(config.workflows?.
|
|
91
|
+
assert.deepEqual(config.workflows?.platformMatrix?.commands, ["npm run smoke:platform:all"], "platform matrix workflow should use the full target matrix");
|
|
92
|
+
assert.deepEqual(config.workflows?.release?.commands, ["npm run release:check"], "release workflow should use the full local-plus-platform release gate");
|
|
93
|
+
assert.equal(config.requiredCrabbox?.minVersion, "0.26.0", "Crabbox baseline should match the documented provider contract");
|
|
83
94
|
assert.equal(pkg.scripts["smoke:platform:all"], "npm run smoke:platform:doctor && node scripts/platform-smoke.mjs run --target macos,ubuntu,windows-native", "full platform smoke should remain doctor-first and cover all required targets");
|
|
84
95
|
assert.match(pkg.scripts["release:check"], /npm run verify:oracle && npm run smoke:platform:all/, "release check should combine local verification and full platform smoke");
|
|
96
|
+
const runnerSource = readFileSync(new URL("./crabbox-runner.mjs", import.meta.url), "utf8");
|
|
97
|
+
assert.match(runnerSource, /PLATFORM_SMOKE_CRABBOX/, "runner should honor reusable Crabbox binary override");
|
|
98
|
+
assert.match(runnerSource, /PLATFORM_SMOKE_MAC_WORK_ROOT/, "runner should honor reusable macOS work-root override");
|
|
99
|
+
assert.match(runnerSource, /PLATFORM_SMOKE_WINDOWS_WORK_ROOT/, "runner should honor reusable Windows work-root override");
|
|
85
100
|
}
|
|
86
101
|
|
|
87
102
|
function testRealSmokeExpensiveAgentPathsAreOptIn() {
|
|
@@ -96,6 +111,7 @@ function testRealSmokeExpensiveAgentPathsAreOptIn() {
|
|
|
96
111
|
assert.match(source, /truthy\(env\("PI_ORACLE_REAL_TEST_NEGATIVE_SYMLINK"\)\)/, "negative symlink real-agent check should be opt-in");
|
|
97
112
|
}
|
|
98
113
|
|
|
114
|
+
testHelpTextIncludesTargetsAndExamples();
|
|
99
115
|
testTargetSelection();
|
|
100
116
|
testPackedInstallCommandRendering();
|
|
101
117
|
testRealExtensionPackedInstallRendering();
|
|
@@ -122,7 +122,7 @@ if ($PackTarball -and $PiCli -and (Test-Path -LiteralPath $TarballPath)) {
|
|
|
122
122
|
if ($PACKED_NODE_INSTALL_EXIT -eq 0) {
|
|
123
123
|
$PreviousPiOffline = $env:PI_OFFLINE
|
|
124
124
|
$env:PI_OFFLINE = "1"
|
|
125
|
-
& $PiCli install -l (Join-Path ".\node_modules" $PackageName) 1> $PiInstallOut 2> $PiInstallErr
|
|
125
|
+
& $PiCli install -l (Join-Path ".\node_modules" $PackageName) --approve 1> $PiInstallOut 2> $PiInstallErr
|
|
126
126
|
$PI_INSTALL_EXIT = Exit-CodeFromLastCommand
|
|
127
127
|
if ($null -eq $PreviousPiOffline) { Remove-Item Env:\PI_OFFLINE -ErrorAction SilentlyContinue } else { $env:PI_OFFLINE = $PreviousPiOffline }
|
|
128
128
|
} else {
|
|
@@ -148,7 +148,7 @@ if ($PiCli) {
|
|
|
148
148
|
Push-Location $PiProject
|
|
149
149
|
$PreviousPiOffline = $env:PI_OFFLINE
|
|
150
150
|
$env:PI_OFFLINE = "1"
|
|
151
|
-
& $PiCli list 1> $PiListOut 2> $PiListErr
|
|
151
|
+
& $PiCli list --approve 1> $PiListOut 2> $PiListErr
|
|
152
152
|
$PI_LIST_EXIT = Exit-CodeFromLastCommand
|
|
153
153
|
if ($null -eq $PreviousPiOffline) { Remove-Item Env:\PI_OFFLINE -ErrorAction SilentlyContinue } else { $env:PI_OFFLINE = $PreviousPiOffline }
|
|
154
154
|
Pop-Location
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* The
|
|
2
|
+
* Cross-platform smoke suites for pi-oracle.
|
|
3
|
+
* The suites prove the package builds, packs, installs, loads, and runs through pi's package path.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
@@ -194,13 +194,13 @@ export function buildPlatformBuildCommand(targetName = "ubuntu", packageName = "
|
|
|
194
194
|
lines.push('echo "PLATFORM_PACKED_NODE_INSTALL_EXIT=$PACKED_NODE_INSTALL_EXIT"');
|
|
195
195
|
lines.push(...posixSection("PACKED_NODE_INSTALL_STDOUT", 'cat "$PACK_DIR/packed-node-install.stdout.txt" 2>/dev/null || true'));
|
|
196
196
|
lines.push(...posixSection("PACKED_NODE_INSTALL_STDERR", 'cat "$PACK_DIR/packed-node-install.stderr.txt" 2>/dev/null || true'));
|
|
197
|
-
lines.push(`if [ "$PACKED_NODE_INSTALL_EXIT" -eq 0 ] && [ -n "$PI_CLI" ]; then (cd "$PI_PROJECT" && PI_OFFLINE=1 "$PI_CLI" install -l ./node_modules/${packageName} >"$PACK_DIR/pi-install.stdout.txt" 2>"$PACK_DIR/pi-install.stderr.txt"); PI_INSTALL_EXIT=$?; else echo "packed npm install failed or missing pi cli" >"$PACK_DIR/pi-install.stderr.txt"; PI_INSTALL_EXIT=1; fi`);
|
|
197
|
+
lines.push(`if [ "$PACKED_NODE_INSTALL_EXIT" -eq 0 ] && [ -n "$PI_CLI" ]; then (cd "$PI_PROJECT" && PI_OFFLINE=1 "$PI_CLI" install -l ./node_modules/${packageName} --approve >"$PACK_DIR/pi-install.stdout.txt" 2>"$PACK_DIR/pi-install.stderr.txt"); PI_INSTALL_EXIT=$?; else echo "packed npm install failed or missing pi cli" >"$PACK_DIR/pi-install.stderr.txt"; PI_INSTALL_EXIT=1; fi`);
|
|
198
198
|
lines.push('echo "PLATFORM_PI_INSTALL_EXIT=$PI_INSTALL_EXIT"');
|
|
199
199
|
lines.push(...posixSection("PI_INSTALL_STDOUT", 'cat "$PACK_DIR/pi-install.stdout.txt" 2>/dev/null || true'));
|
|
200
200
|
lines.push(...posixSection("PI_INSTALL_STDERR", 'cat "$PACK_DIR/pi-install.stderr.txt" 2>/dev/null || true'));
|
|
201
201
|
lines.push("");
|
|
202
202
|
lines.push('echo "=== pi list ==="');
|
|
203
|
-
lines.push('if [ -n "$PI_CLI" ]; then (cd "$PI_PROJECT" && PI_OFFLINE=1 "$PI_CLI" list >"$PACK_DIR/pi-list.stdout.txt" 2>"$PACK_DIR/pi-list.stderr.txt"); PI_LIST_EXIT=$?; else echo "missing pi cli" >"$PACK_DIR/pi-list.stderr.txt"; PI_LIST_EXIT=1; fi');
|
|
203
|
+
lines.push('if [ -n "$PI_CLI" ]; then (cd "$PI_PROJECT" && PI_OFFLINE=1 "$PI_CLI" list --approve >"$PACK_DIR/pi-list.stdout.txt" 2>"$PACK_DIR/pi-list.stderr.txt"); PI_LIST_EXIT=$?; else echo "missing pi cli" >"$PACK_DIR/pi-list.stderr.txt"; PI_LIST_EXIT=1; fi');
|
|
204
204
|
lines.push('echo "PLATFORM_PI_LIST_EXIT=$PI_LIST_EXIT"');
|
|
205
205
|
lines.push(...posixSection("PI_LIST_STDOUT", 'cat "$PACK_DIR/pi-list.stdout.txt" 2>/dev/null || true'));
|
|
206
206
|
lines.push(...posixSection("PI_LIST_STDERR", 'cat "$PACK_DIR/pi-list.stderr.txt" 2>/dev/null || true'));
|
|
@@ -41,21 +41,24 @@ Examples:
|
|
|
41
41
|
Canonical workflows:
|
|
42
42
|
Everyday local iteration: npm run verify:oracle
|
|
43
43
|
Platform-sensitive changes: npm run smoke:platform:doctor, then focused run --target <target> --suite <suite>
|
|
44
|
-
|
|
44
|
+
Platform matrix proof: npm run smoke:platform:all
|
|
45
|
+
Full release gate: npm run release:check
|
|
45
46
|
|
|
46
47
|
Environment:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
PLATFORM_SMOKE_CRABBOX Reusable Crabbox binary override (defaults to PATH)
|
|
49
|
+
PI_ORACLE_SMOKE_CRABBOX Project-specific Crabbox binary override (wins over PLATFORM_SMOKE_CRABBOX)
|
|
50
|
+
PLATFORM_SMOKE_MAC_HOST macOS SSH host (default: localhost)
|
|
51
|
+
PLATFORM_SMOKE_MAC_USER macOS SSH user (default: $USER)
|
|
52
|
+
PLATFORM_SMOKE_MAC_WORK_ROOT macOS Crabbox work root
|
|
53
|
+
PLATFORM_SMOKE_UBUNTU_IMAGE Optional local-container image override
|
|
54
|
+
PLATFORM_SMOKE_WINDOWS_VM Parallels source VM (default from config)
|
|
55
|
+
PLATFORM_SMOKE_WINDOWS_SNAPSHOT Parallels snapshot (default from config)
|
|
56
|
+
PLATFORM_SMOKE_WINDOWS_USER Windows SSH user (default: $USER)
|
|
57
|
+
PLATFORM_SMOKE_WINDOWS_WORK_ROOT Windows work root
|
|
58
|
+
PI_ORACLE_SMOKE_* Project-specific aliases for the PLATFORM_SMOKE_* knobs above
|
|
56
59
|
PI_ORACLE_REAL_TEST_PROVIDER Real smoke provider (default: zai)
|
|
57
60
|
PI_ORACLE_REAL_TEST_MODEL Real smoke model (default: glm-5.1)
|
|
58
|
-
ZAI_API_KEY Default real-smoke provider API key
|
|
61
|
+
ZAI_API_KEY Default real-smoke provider API key for optional model-agent debugging
|
|
59
62
|
`);
|
|
60
63
|
}
|
|
61
64
|
|