@warlock.js/core 5.32.0 → 5.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -46
- package/esm/application/application-config-types.d.mts +8 -0
- package/esm/application/application-config-types.d.mts.map +1 -1
- package/esm/application/boot-signal.d.mts +8 -8
- package/esm/application/boot-signal.d.mts.map +1 -1
- package/esm/application/boot-signal.mjs +15 -9
- package/esm/application/boot-signal.mjs.map +1 -1
- package/esm/cli/commands/ai/ai-command.action.mjs +140 -0
- package/esm/cli/commands/ai/ai-command.action.mjs.map +1 -0
- package/esm/cli/commands/ai.command.mjs +50 -0
- package/esm/cli/commands/ai.command.mjs.map +1 -0
- package/esm/cli/commands/check/check-command.action.mjs +4 -0
- package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
- package/esm/cli/commands/check.command.mjs +6 -1
- package/esm/cli/commands/check.command.mjs.map +1 -1
- package/esm/cli/commands/e2e.command.mjs +63 -0
- package/esm/cli/commands/e2e.command.mjs.map +1 -0
- package/esm/cli/commands/load-e2e-spec.mjs +47 -0
- package/esm/cli/commands/load-e2e-spec.mjs.map +1 -0
- package/esm/cli/commands/pm2/pm2-command.action.mjs +91 -0
- package/esm/cli/commands/pm2/pm2-command.action.mjs.map +1 -0
- package/esm/cli/commands/pm2/pm2-config.mjs +115 -0
- package/esm/cli/commands/pm2/pm2-config.mjs.map +1 -0
- package/esm/cli/commands/pm2.command.mjs +53 -0
- package/esm/cli/commands/pm2.command.mjs.map +1 -0
- package/esm/cli/framework-cli-commands.mjs +6 -0
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +1 -4
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/shutdown-timeout.mjs +7 -0
- package/esm/connectors/shutdown-timeout.mjs.map +1 -0
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs +8 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs +1 -1
- package/esm/dev-server/health-checker/default-health-checkers.mjs +2 -16
- package/esm/dev-server/health-checker/default-health-checkers.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +1 -2
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.d.mts +1 -1
- package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
- package/esm/e2e/browser.d.mts +4 -0
- package/esm/e2e/browser.d.mts.map +1 -0
- package/esm/e2e/browser.mjs +88 -0
- package/esm/e2e/browser.mjs.map +1 -0
- package/esm/e2e/cdp.d.mts +152 -0
- package/esm/e2e/cdp.d.mts.map +1 -0
- package/esm/e2e/cdp.mjs +106 -0
- package/esm/e2e/cdp.mjs.map +1 -0
- package/esm/e2e/fixture.mjs +195 -0
- package/esm/e2e/fixture.mjs.map +1 -0
- package/esm/e2e/index.d.mts +5 -0
- package/esm/e2e/index.mjs +6 -0
- package/esm/e2e/owned-process.mjs +193 -0
- package/esm/e2e/owned-process.mjs.map +1 -0
- package/esm/e2e/page.d.mts +64 -0
- package/esm/e2e/page.d.mts.map +1 -0
- package/esm/e2e/page.mjs +376 -0
- package/esm/e2e/page.mjs.map +1 -0
- package/esm/e2e/run-e2e.d.mts +17 -0
- package/esm/e2e/run-e2e.d.mts.map +1 -0
- package/esm/e2e/run-e2e.mjs +143 -0
- package/esm/e2e/run-e2e.mjs.map +1 -0
- package/esm/e2e/types.d.mts +123 -0
- package/esm/e2e/types.d.mts.map +1 -0
- package/esm/e2e/types.mjs +24 -0
- package/esm/e2e/types.mjs.map +1 -0
- package/esm/generations/features/index.mjs +2 -0
- package/esm/generations/features/index.mjs.map +1 -1
- package/esm/generations/features/process.feature.mjs +65 -0
- package/esm/generations/features/process.feature.mjs.map +1 -0
- package/esm/http/port-preflight.d.mts.map +1 -1
- package/esm/http/port-preflight.mjs +2 -0
- package/esm/http/port-preflight.mjs.map +1 -1
- package/esm/index.d.mts +7 -2
- package/esm/index.mjs +7 -2
- package/esm/secrets/errors.d.mts +9 -0
- package/esm/secrets/errors.d.mts.map +1 -0
- package/esm/secrets/errors.mjs +24 -0
- package/esm/secrets/errors.mjs.map +1 -0
- package/esm/secrets/file-backend.mjs +167 -0
- package/esm/secrets/file-backend.mjs.map +1 -0
- package/esm/secrets/index.d.mts +6 -0
- package/esm/secrets/index.mjs +6 -0
- package/esm/secrets/keyring-backend.mjs +50 -0
- package/esm/secrets/keyring-backend.mjs.map +1 -0
- package/esm/secrets/memory-backend.d.mts +12 -0
- package/esm/secrets/memory-backend.d.mts.map +1 -0
- package/esm/secrets/memory-backend.mjs +28 -0
- package/esm/secrets/memory-backend.mjs.map +1 -0
- package/esm/secrets/resolve-driver.mjs +14 -0
- package/esm/secrets/resolve-driver.mjs.map +1 -0
- package/esm/secrets/secret-store.d.mts +25 -0
- package/esm/secrets/secret-store.d.mts.map +1 -0
- package/esm/secrets/secret-store.mjs +120 -0
- package/esm/secrets/secret-store.mjs.map +1 -0
- package/esm/secrets/secrets.d.mts +11 -0
- package/esm/secrets/secrets.d.mts.map +1 -0
- package/esm/secrets/secrets.mjs +72 -0
- package/esm/secrets/secrets.mjs.map +1 -0
- package/esm/secrets/types.d.mts +26 -0
- package/esm/secrets/types.d.mts.map +1 -0
- package/esm/socket/index.d.mts +2 -2
- package/esm/socket/index.mjs +2 -2
- package/esm/socket/utils.d.mts +7 -0
- package/esm/socket/utils.d.mts.map +1 -1
- package/esm/socket/utils.mjs +12 -1
- package/esm/socket/utils.mjs.map +1 -1
- package/esm/tests/fakes/index.d.mts +2 -1
- package/esm/tests/fakes/index.mjs +2 -1
- package/esm/tests/fakes/secrets-fake.d.mts +13 -0
- package/esm/tests/fakes/secrets-fake.d.mts.map +1 -0
- package/esm/tests/fakes/secrets-fake.mjs +50 -0
- package/esm/tests/fakes/secrets-fake.mjs.map +1 -0
- package/esm/tests/fakes/test-fakes.d.mts +2 -2
- package/esm/tests/fakes/test-fakes.d.mts.map +1 -1
- package/esm/tests/fakes/test-fakes.mjs +5 -2
- package/esm/tests/fakes/test-fakes.mjs.map +1 -1
- package/esm/tests/index.d.mts +2 -1
- package/esm/tests/index.mjs +2 -1
- package/esm/tests/vitest-setup.d.mts +1 -1
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/tooling/check/lint-step.d.mts +2 -8
- package/esm/tooling/check/lint-step.d.mts.map +1 -1
- package/esm/tooling/check/lint-step.mjs +1 -42
- package/esm/tooling/check/lint-step.mjs.map +1 -1
- package/esm/tooling/check/run-check.d.mts +2 -0
- package/esm/tooling/check/run-check.d.mts.map +1 -1
- package/esm/tooling/check/run-check.mjs +3 -1
- package/esm/tooling/check/run-check.mjs.map +1 -1
- package/esm/tooling/problem.d.mts +1 -1
- package/esm/tooling/problem.d.mts.map +1 -1
- package/esm/tooling/problem.mjs.map +1 -1
- package/esm/updater/package-manager.mjs +5 -1
- package/esm/updater/package-manager.mjs.map +1 -1
- package/esm/upgrade/codemods/index.mjs +3 -0
- package/esm/upgrade/codemods/index.mjs.map +1 -1
- package/esm/upgrade/codemods/oxc-toolchain.mjs +191 -0
- package/esm/upgrade/codemods/oxc-toolchain.mjs.map +1 -0
- package/esm/upgrade/codemods/oxlint-rule-schemas.mjs +6 -0
- package/esm/upgrade/codemods/oxlint-rule-schemas.mjs.map +1 -0
- package/esm/upgrade/codemods/port-eslint-rules.mjs +343 -0
- package/esm/upgrade/codemods/port-eslint-rules.mjs.map +1 -0
- package/esm/upgrade/codemods/toolchain-templates.mjs +244 -0
- package/esm/upgrade/codemods/toolchain-templates.mjs.map +1 -0
- package/esm/upgrade/codemods/validate-oxlint-rule.mjs +60 -0
- package/esm/upgrade/codemods/validate-oxlint-rule.mjs.map +1 -0
- package/esm/upgrade/notes.mjs +14 -7
- package/esm/upgrade/notes.mjs.map +1 -1
- package/esm/upgrade/run-upgrade.mjs +25 -6
- package/esm/upgrade/run-upgrade.mjs.map +1 -1
- package/esm/warlock-config/types.d.mts +1 -1
- package/llms-full.txt +538 -29
- package/llms.txt +5 -3
- package/package.json +29 -20
- package/skills/README.md +2 -1
- package/skills/create-module/SKILL.md +2 -2
- package/skills/index.md +1 -1
- package/skills/run-app/SKILL.md +115 -6
- package/skills/store-secrets/SKILL.md +141 -0
- package/skills/test-browser/SKILL.md +119 -0
- package/skills/test-fakes/SKILL.md +13 -11
- package/skills/upgrade-from-5-29/SKILL.md +85 -7
- package/skills/warlock-check/SKILL.md +7 -3
- package/skills/warlock-doctor/SKILL.md +8 -0
- package/skills/wire-socket/SKILL.md +19 -0
- package/skills/write-cli-command/SKILL.md +21 -0
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs +0 -154
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +0 -1
- package/esm/dev-server/health-checker/workers/eslint-health.worker.d.mts +0 -1
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +0 -192
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,70 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
> ⚠ **Versioning: `@warlock.js/*` does not follow SemVer strictly — breaking changes may ship in a minor.** This is a deliberate decision, not an oversight: the framework is pre-adoption and the cost of a major per behaviour fix currently outweighs the benefit. **Pin an exact version or a tilde range (`~4.13.0`) if you need to opt into changes rather than receive them.** Every breaking change is marked **BREAKING** in its entry and summarised in an _Upgrading_ section at the top of the release. **This policy will change once the framework has consumers beyond its author.**
|
|
8
8
|
|
|
9
|
+
## 5.34.0 - 2026-10-19
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Select an isolated in-memory secrets backend through `SECRETS_DRIVER=memory` or `secrets.driver`, including built-server test deployments and explicit `SecretStore` options. Configuration overrides the environment; unknown drivers fail closed.
|
|
14
|
+
- Refuse the memory driver under `NODE_ENV=production` unless `allowMemoryInProduction: true` is explicitly configured; preserve log masking and namespace isolation without keychain/environment/file access.
|
|
15
|
+
- Add `warlock e2e` and the isolated `@warlock.js/core/e2e` Node subpath: owned app/build fixtures and browser profiles, direct CDP with native WebSocket, stable hit-tested clicks, editable text and navigation waits, responsive screenshots and overflow assertions, console/network allowlists, per-run evidence and PID-verified process-group cleanup. Include fake-CDP coverage, real-browser integration specs and a complete browser-testing guide/skill.
|
|
16
|
+
- `oxc-toolchain-removal` runs on `warlock upgrade --from 5.33 --to 5.34.0`, reusing the preserved 5.33 `oxc-toolchain` migration. It ports literal folder restrictions, native TypeScript rule equivalents, unused-import checks, globals and ignores; unsupported rules/options report their source and file scope without executing config. Native type-aware equivalents enable the optional app engine automatically.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **BREAKING** Migrated template configs enable Oxlint `correctness: error`; stale literal `healthCheckers.eslint` options are removed.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Detect a real lockfile beside `pnpm-workspace.yaml` before treating the directory as pnpm. npm apps with only `package-lock.json` now receive the correct install hint.
|
|
25
|
+
- Validate Postman draft-04 schemas with an explicit development-only Ajv adapter instead of resolving ESLint's private dependency. Generated source formatting checks use Oxfmt.
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
|
|
29
|
+
- **BREAKING** Remove the ESLint development checker, CLI selection, checker config option and production dependency. `warlock check --lint eslint` now fails with the 5.34.0 removal and Oxlint/Oxfmt migration guidance.
|
|
30
|
+
|
|
31
|
+
### Upgrading
|
|
32
|
+
|
|
33
|
+
- Run `npx warlock upgrade --from 5.33 --to 5.34.0 --dry-run`, review the findings, apply the migration and run `npm install`. Remove remaining custom ESLint checkers and formatting scripts after porting them. Existing Oxc rules are retained on conflict. Additional correctness diagnostics must be fixed in newly migrated apps. See `upgrade-from-5-29`, section “5.33 to 5.34 toolchain removal”.
|
|
34
|
+
|
|
35
|
+
## 5.33.0 - 2026-10-19
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- `oxc-toolchain`, a 5.33.0 `warlock upgrade` codemod, migrates generated configs, package scripts/dependencies, editor settings and literal dev health switches to Oxlint/Oxfmt. Customized files are retained with file:line review guidance; additions and deletions honor `--dry-run` and `--json`.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- **BREAKING** ESLint no longer starts from config detection alone in the dev server or `warlock check`. TypeScript and configured Oxlint remain the default health checkers; `check` defaults to Oxlint.
|
|
44
|
+
|
|
45
|
+
### Deprecated
|
|
46
|
+
|
|
47
|
+
- ESLint/Prettier support is removed in 5.34.0. Explicit ESLint opt-ins emit one deprecation warning per process on stderr with migration instructions.
|
|
48
|
+
|
|
49
|
+
### Upgrading
|
|
50
|
+
|
|
51
|
+
- Run `npx warlock upgrade --from 5.32 --to 5.33.0` (preview with `--dry-run`), install dependencies and review customized configs. To retain legacy lint checks temporarily, set `devServer.healthCheckers.eslint: true` and select `warlock check --lint eslint`. See the upgrade skill's "5.32 to 5.33" section.
|
|
52
|
+
- `broadcastSocketEvent(event, ...args)` dispatches through the existing socket connector's adapter and returns `false` when no socket server is available. Web page-cache invalidations use this path to notify clients across processes configured with a shared adapter.
|
|
53
|
+
- Add app-scoped `secrets.get/set/remove/has` and `environmentName`, also exported from `@warlock.js/core/secrets`, with lazy optional OS keychain storage, read-only scoped environment lookup, authenticated encrypted owner-only files, typed errors, logger masking and explicit memory backends.
|
|
54
|
+
- Add `secretsFake` to the default test fakes, with isolated memory storage and assertions that never reveal credentials; opt out with `tests.fakes: { secrets: false }`.
|
|
55
|
+
- `warlock ai measure --model <id> [--tools <module>] [--system <file>] [--skills <dir>] [--json]` lazily loads AI and reports per-part input tokens and priced USD. Supports provider-qualified IDs, configured model modules, and JSON output without app bootstrap.
|
|
56
|
+
- Optional `warlock pm2 init` generates `ecosystem.config.cjs` from app/build config with a `<project>-<app>-<env>` name, readiness URL, `wait_ready`, startup timeout and a kill timeout matched to `app.shutdownTimeout`. PM2 stays outside core's dependencies. Fork mode is the default; cluster mode requires a socket adapter and WebSocket-only transports when sockets are configured.
|
|
57
|
+
- `warlock pm2 reload [--id <pm2-id>]` verifies the exact app name, working directory and bundle path before reloading numeric PM2 ids sequentially. Document rolling reload limits, shared-server stops by PM2 id and logger output formatting.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- Completed application boot now sends PM2's `"ready"` IPC message without disconnecting its channel; the existing `warlock start` readiness protocol remains supported. Cluster workers skip the exclusive port preflight so they can share their primary's listener during rolling reloads.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- Type the socket connector’s raw HTTP(S) server across asynchronous listener startup; keep native-keyring test doubles declaration-safe and cover the documented `check --lint` option.
|
|
66
|
+
- Socket-only connector boot awaits its listener and rejects bind failures before application readiness can be announced.
|
|
67
|
+
|
|
68
|
+
### Upgrading
|
|
69
|
+
|
|
70
|
+
- Rebuild production bundles to include PM2 readiness before using `wait_ready`. Generate with the target `NODE_ENV`, review the ecosystem file, and start the bundle directly under PM2. Cluster sockets require `socket.adapter`, `socket.options.transports: ["websocket"]` and WebSocket-only clients; polling deployments need sticky routing to individual listeners. Fork reloads can interrupt traffic.
|
|
71
|
+
- Add `warlock add process` with exact family dependency, typed supervisor config, application recovery/shutdown hooks, optional devtools registration, next steps and idempotent file generation.
|
|
72
|
+
|
|
9
73
|
## 5.32.0 - 2026-10-18
|
|
10
74
|
|
|
11
75
|
### Added
|
|
@@ -306,18 +370,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
306
370
|
|
|
307
371
|
- `request.input(key)` now sees a route param set with `Request.setParam()`. Before, it updated only `request.params`, so in a multi-site app (where the site dispatcher sets each page param) every dynamic segment read as `undefined` through `input()`, `only()` and `all()`. Params keep their precedence over query and body.
|
|
308
372
|
|
|
309
|
-
## 5.23.2 - 2026-09-26
|
|
310
|
-
|
|
311
|
-
### Changed
|
|
312
|
-
|
|
313
|
-
- Lockstep patch release; package APIs are unchanged.
|
|
314
|
-
|
|
315
|
-
## 5.23.1 - 2026-09-26
|
|
316
|
-
|
|
317
|
-
### Changed
|
|
318
|
-
|
|
319
|
-
- Lockstep patch release; package APIs are unchanged.
|
|
320
|
-
|
|
321
373
|
## 5.23.0 - 2026-09-25
|
|
322
374
|
|
|
323
375
|
### Added
|
|
@@ -332,12 +384,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
332
384
|
- `warlock add` installs with the project's package manager: the nearest `packageManager` field, then a `pnpm-workspace.yaml`, then the nearest lockfile, searched upward so an app inside a workspace uses the workspace's manager.
|
|
333
385
|
- `uniqueExceptCurrentUser` / `uniqueExceptCurrentId` / `existsExceptCurrentUser` / `existsExceptCurrentId` on `StringValidator` now return `this`, so `Infer<>` keeps the concrete output type.
|
|
334
386
|
|
|
335
|
-
## 5.22.1 - 2026-09-25
|
|
336
|
-
|
|
337
|
-
### Changed
|
|
338
|
-
|
|
339
|
-
- Lockstep patch release; package APIs are unchanged.
|
|
340
|
-
|
|
341
387
|
## 5.22.0 - 2026-09-25
|
|
342
388
|
|
|
343
389
|
### Fixed
|
|
@@ -377,10 +423,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
377
423
|
registration for a `GET` route at the same path. GET-only routes retain
|
|
378
424
|
Fastify's implicit HEAD behavior.
|
|
379
425
|
|
|
380
|
-
### Changed
|
|
381
|
-
|
|
382
|
-
- Lockstep release maintenance and dependency refresh.
|
|
383
|
-
|
|
384
426
|
## 5.20.0 - 2026-09-24
|
|
385
427
|
|
|
386
428
|
### Added
|
|
@@ -406,12 +448,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
406
448
|
|
|
407
449
|
- The repository cache is also cleared after the DB transaction commits (cascade `afterCommit`), fixing stale reads when a concurrent read re-cached the old row between the model event and `COMMIT`.
|
|
408
450
|
|
|
409
|
-
## 5.19.1 - 2026-09-23
|
|
410
|
-
|
|
411
|
-
### Changed
|
|
412
|
-
|
|
413
|
-
- Lockstep patch release aligning `@warlock.js/core` with the 5.19.1 family; no Core runtime API change.
|
|
414
|
-
|
|
415
451
|
## 5.19.0 - 2026-09-23
|
|
416
452
|
|
|
417
453
|
### Added
|
|
@@ -546,14 +582,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
546
582
|
|
|
547
583
|
- Backend file changes are picked up sooner in `warlock dev` (event debounce 150ms → 50ms).
|
|
548
584
|
|
|
549
|
-
## 5.10.0 - 2026-09-14
|
|
550
|
-
|
|
551
|
-
_Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.10.0._
|
|
552
|
-
|
|
553
|
-
## 5.9.0 - 2026-09-13
|
|
554
|
-
|
|
555
|
-
_Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.9.0._
|
|
556
|
-
|
|
557
585
|
## 5.8.0 - 2026-09-13
|
|
558
586
|
|
|
559
587
|
### Added
|
|
@@ -873,16 +901,6 @@ _Released in lockstep with the `@warlock.js/*` family; no package-specific chang
|
|
|
873
901
|
growing switch.
|
|
874
902
|
- Fixed 7 mojibake checkmarks in CLI output.
|
|
875
903
|
|
|
876
|
-
## 5.0.2 - 2026-08-25
|
|
877
|
-
|
|
878
|
-
No changes to `@warlock.js/core`. Released in lockstep with the `@warlock.js/web` SSR
|
|
879
|
-
fix (`ssr.noExternal`) — see that package's changelog.
|
|
880
|
-
|
|
881
|
-
## 5.0.1 - 2026-08-25
|
|
882
|
-
|
|
883
|
-
No changes to `@warlock.js/core`. Released in lockstep with the `create-warlock` vite
|
|
884
|
-
resolution pin and the `@warlock.js/web` peer narrowing — see those packages' changelogs.
|
|
885
|
-
|
|
886
904
|
## 5.0.0 - 2026-08-25
|
|
887
905
|
|
|
888
906
|
### Upgrading
|
|
@@ -4,6 +4,14 @@ export type AppConfigurations = {
|
|
|
4
4
|
* App name
|
|
5
5
|
*/
|
|
6
6
|
appName?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Overall connector shutdown budget in milliseconds. PM2 config generation
|
|
9
|
+
* uses the same value for `kill_timeout`. Allow enough time for HTTP draining
|
|
10
|
+
* and the remaining connectors; tune `http.gracefulShutdown.timeout` below it.
|
|
11
|
+
*
|
|
12
|
+
* @default 10000
|
|
13
|
+
*/
|
|
14
|
+
shutdownTimeout?: number;
|
|
7
15
|
/**
|
|
8
16
|
* Default locale code
|
|
9
17
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application-config-types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/application/application-config-types.ts"],"mappings":";YAAY;;;;EAIV;;;;;;EAMA;;;;;;EAMA;;;;;;;;;EASA;;;;EAIA;;;;EAIA"}
|
|
1
|
+
{"version":3,"file":"application-config-types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/application/application-config-types.ts"],"mappings":";YAAY;;;;EAIV;;;;;;;;EAQA;;;;;;EAMA;;;;;;EAMA;;;;;;;;;EASA;;;;EAIA;;;;EAIA"}
|
|
@@ -41,7 +41,7 @@ export type BootSignal = {
|
|
|
41
41
|
* Environment flag `warlock start` sets on the process it supervises.
|
|
42
42
|
*
|
|
43
43
|
* An IPC channel alone is NOT proof that the parent speaks this protocol —
|
|
44
|
-
*
|
|
44
|
+
* a vitest worker fork or a custom supervisor may hand their child a
|
|
45
45
|
* channel they use for their own messages. Writing to one of those, and then
|
|
46
46
|
* closing it, corrupts someone else's protocol and can kill the process. The
|
|
47
47
|
* flag is the handshake: only a parent that set it gets signalled.
|
|
@@ -50,14 +50,14 @@ export declare const BOOT_SIGNAL_ENV_KEY = "WARLOCK_BOOT_SIGNAL";
|
|
|
50
50
|
/**
|
|
51
51
|
* Report a completed boot to the parent process, if one is supervising.
|
|
52
52
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
53
|
+
* The versioned signal requires an IPC channel (`process.send` exists) and
|
|
54
|
+
* the parent's {@link BOOT_SIGNAL_ENV_KEY} handshake. With IPC and `pm_id`,
|
|
55
|
+
* PM2 instead receives its documented `"ready"` message and keeps its channel
|
|
56
|
+
* open for logs and worker coordination.
|
|
57
|
+
* Standalone bundles and unrecognized supervisors remain silent.
|
|
58
58
|
*
|
|
59
|
-
* The channel is closed as soon as
|
|
60
|
-
* holds a `ref` on the child's event loop,
|
|
59
|
+
* The `warlock start` channel is closed as soon as its message is flushed.
|
|
60
|
+
* An open IPC channel holds a `ref` on the child's event loop, keeping a finished or
|
|
61
61
|
* wedged process alive and indistinguishable from a healthy server — the exact
|
|
62
62
|
* confusion this signal exists to remove. One message, then disconnect.
|
|
63
63
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boot-signal.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/application/boot-signal.ts"],"mappings":";;;;;;;YAOY;;;;;;;;qBASC;;;;;;;;;YAUD;EACV,MAAM;EACN;;EAEA;;EAEA;EACA,aAAa;EACb,iBAAiB;EACjB;;EAEA;;EAEA;;;;;;;;;;;qBAYW;;;;;;;;;;;;;;;;;;;wBAoBG,eAAe,QAAQ;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"boot-signal.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/application/boot-signal.ts"],"mappings":";;;;;;;YAOY;;;;;;;;qBASC;;;;;;;;;YAUD;EACV,MAAM;EACN;;EAEA;;EAEA;EACA,aAAa;EACb,iBAAiB;EACjB;;EAEA;;EAEA;;;;;;;;;;;qBAYW;;;;;;;;;;;;;;;;;;;wBAoBG,eAAe,QAAQ;;;;;;;;;;wBA2CvB,aAAa,mBAAmB,WAAW"}
|
|
@@ -11,7 +11,7 @@ const BOOT_SIGNAL_VERSION = 1;
|
|
|
11
11
|
* Environment flag `warlock start` sets on the process it supervises.
|
|
12
12
|
*
|
|
13
13
|
* An IPC channel alone is NOT proof that the parent speaks this protocol —
|
|
14
|
-
*
|
|
14
|
+
* a vitest worker fork or a custom supervisor may hand their child a
|
|
15
15
|
* channel they use for their own messages. Writing to one of those, and then
|
|
16
16
|
* closing it, corrupts someone else's protocol and can kill the process. The
|
|
17
17
|
* flag is the handshake: only a parent that set it gets signalled.
|
|
@@ -20,14 +20,14 @@ const BOOT_SIGNAL_ENV_KEY = "WARLOCK_BOOT_SIGNAL";
|
|
|
20
20
|
/**
|
|
21
21
|
* Report a completed boot to the parent process, if one is supervising.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
23
|
+
* The versioned signal requires an IPC channel (`process.send` exists) and
|
|
24
|
+
* the parent's {@link BOOT_SIGNAL_ENV_KEY} handshake. With IPC and `pm_id`,
|
|
25
|
+
* PM2 instead receives its documented `"ready"` message and keeps its channel
|
|
26
|
+
* open for logs and worker coordination.
|
|
27
|
+
* Standalone bundles and unrecognized supervisors remain silent.
|
|
28
28
|
*
|
|
29
|
-
* The channel is closed as soon as
|
|
30
|
-
* holds a `ref` on the child's event loop,
|
|
29
|
+
* The `warlock start` channel is closed as soon as its message is flushed.
|
|
30
|
+
* An open IPC channel holds a `ref` on the child's event loop, keeping a finished or
|
|
31
31
|
* wedged process alive and indistinguishable from a healthy server — the exact
|
|
32
32
|
* confusion this signal exists to remove. One message, then disconnect.
|
|
33
33
|
*
|
|
@@ -36,7 +36,13 @@ const BOOT_SIGNAL_ENV_KEY = "WARLOCK_BOOT_SIGNAL";
|
|
|
36
36
|
* healthy application.
|
|
37
37
|
*/
|
|
38
38
|
function sendBootSignal(signal) {
|
|
39
|
-
if (typeof process.send !== "function"
|
|
39
|
+
if (typeof process.send !== "function") return;
|
|
40
|
+
if (process.env["WARLOCK_BOOT_SIGNAL"] !== "1") {
|
|
41
|
+
if (process.env.pm_id !== void 0) try {
|
|
42
|
+
process.send("ready", void 0, void 0, () => {});
|
|
43
|
+
} catch {}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
40
46
|
delete process.env[BOOT_SIGNAL_ENV_KEY];
|
|
41
47
|
try {
|
|
42
48
|
process.send(signal, void 0, void 0, () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boot-signal.mjs","names":[],"sources":["../../../../../../../../packages/core/src/application/boot-signal.ts"],"sourcesContent":["import type { Environment, RuntimeStrategy } from \"../utils/environment\";\n\n/**\n * The only message type the boot channel carries today. Named rather than\n * inlined because both the sender (`sendBootSignal`) and the receiver\n * (`warlock start`) must agree on the exact literal.\n */\nexport type BootSignalType = \"warlock:ready\";\n\n/**\n * Schema version of {@link BootSignal}.\n *\n * Present from the first release on purpose: a version field cannot be added to\n * a shipped wire format without a flag day, and this one has to be able to\n * evolve while older CLIs are still supervising newer bundles.\n */\nexport const BOOT_SIGNAL_VERSION = 1;\n\n/**\n * What a booted child process tells its supervising parent over the IPC\n * channel `warlock start` opens for it.\n *\n * This is a wire contract, not an internal detail: `warlock start` prints its\n * success banner **only** when this arrives, so anything watching that banner\n * (a CI gate, a process supervisor) transitively depends on it.\n */\nexport type BootSignal = {\n type: BootSignalType;\n version: number;\n /** The application process, so a supervisor can address it directly. */\n pid: number;\n /** When the boot completed, ISO-8601. */\n at: string;\n environment: Environment;\n runtimeStrategy: RuntimeStrategy;\n bootDurationMs?: number;\n /** The bound http port, absent for an app with no http connector. */\n port?: number;\n /** The roles this process serves — see `Application.roles`. */\n roles: string[];\n};\n\n/**\n * Environment flag `warlock start` sets on the process it supervises.\n *\n * An IPC channel alone is NOT proof that the parent speaks this protocol —\n *
|
|
1
|
+
{"version":3,"file":"boot-signal.mjs","names":[],"sources":["../../../../../../../../packages/core/src/application/boot-signal.ts"],"sourcesContent":["import type { Environment, RuntimeStrategy } from \"../utils/environment\";\n\n/**\n * The only message type the boot channel carries today. Named rather than\n * inlined because both the sender (`sendBootSignal`) and the receiver\n * (`warlock start`) must agree on the exact literal.\n */\nexport type BootSignalType = \"warlock:ready\";\n\n/**\n * Schema version of {@link BootSignal}.\n *\n * Present from the first release on purpose: a version field cannot be added to\n * a shipped wire format without a flag day, and this one has to be able to\n * evolve while older CLIs are still supervising newer bundles.\n */\nexport const BOOT_SIGNAL_VERSION = 1;\n\n/**\n * What a booted child process tells its supervising parent over the IPC\n * channel `warlock start` opens for it.\n *\n * This is a wire contract, not an internal detail: `warlock start` prints its\n * success banner **only** when this arrives, so anything watching that banner\n * (a CI gate, a process supervisor) transitively depends on it.\n */\nexport type BootSignal = {\n type: BootSignalType;\n version: number;\n /** The application process, so a supervisor can address it directly. */\n pid: number;\n /** When the boot completed, ISO-8601. */\n at: string;\n environment: Environment;\n runtimeStrategy: RuntimeStrategy;\n bootDurationMs?: number;\n /** The bound http port, absent for an app with no http connector. */\n port?: number;\n /** The roles this process serves — see `Application.roles`. */\n roles: string[];\n};\n\n/**\n * Environment flag `warlock start` sets on the process it supervises.\n *\n * An IPC channel alone is NOT proof that the parent speaks this protocol —\n * a vitest worker fork or a custom supervisor may hand their child a\n * channel they use for their own messages. Writing to one of those, and then\n * closing it, corrupts someone else's protocol and can kill the process. The\n * flag is the handshake: only a parent that set it gets signalled.\n */\nexport const BOOT_SIGNAL_ENV_KEY = \"WARLOCK_BOOT_SIGNAL\";\n\n/**\n * Report a completed boot to the parent process, if one is supervising.\n *\n * The versioned signal requires an IPC channel (`process.send` exists) and\n * the parent's {@link BOOT_SIGNAL_ENV_KEY} handshake. With IPC and `pm_id`,\n * PM2 instead receives its documented `\"ready\"` message and keeps its channel\n * open for logs and worker coordination.\n * Standalone bundles and unrecognized supervisors remain silent.\n *\n * The `warlock start` channel is closed as soon as its message is flushed.\n * An open IPC channel holds a `ref` on the child's event loop, keeping a finished or\n * wedged process alive and indistinguishable from a healthy server — the exact\n * confusion this signal exists to remove. One message, then disconnect.\n *\n * Failure to send is deliberately swallowed: the parent may have already\n * detached or died, and a supervisor going away must never take down a\n * healthy application.\n */\nexport function sendBootSignal(signal: BootSignal): void {\n if (typeof process.send !== \"function\") {\n return;\n }\n\n // PM2 injects pm_id (including \"0\") in both fork and cluster mode. Its\n // ProcessContainer uses this channel for logs too: never disconnect it.\n // https://github.com/Unitech/pm2/blob/master/lib/ProcessContainer.js\n // https://pm2.keymetrics.io/docs/usage/signals-clean-restart/\n if (process.env[BOOT_SIGNAL_ENV_KEY] !== \"1\") {\n if (process.env.pm_id !== undefined) {\n try {\n process.send(\"ready\", undefined, undefined, () => {});\n } catch {\n // A detached supervisor must not take down an already booted app.\n }\n }\n return;\n }\n\n // Consumed here so it cannot be inherited by anything this app itself spawns\n // and make a grandchild report readiness on a channel meant for someone else.\n delete process.env[BOOT_SIGNAL_ENV_KEY];\n\n try {\n process.send(signal, undefined, undefined, () => {\n process.disconnect?.();\n });\n } catch {\n // The channel closed between the check and the write — the app is up\n // regardless, and there is no longer anyone to tell.\n }\n}\n\n/**\n * Narrow an arbitrary IPC payload to a {@link BootSignal}.\n *\n * The parent receives whatever the child chooses to send, so the message is\n * untrusted input and is validated before it is allowed to flip the parent's\n * \"the server is up\" state. A *newer* version than this CLI knows is still\n * accepted — readiness is readiness, and refusing it would make an old\n * supervisor report a healthy app as failed.\n */\nexport function isBootSignal(message: unknown): message is BootSignal {\n if (typeof message !== \"object\" || message === null) {\n return false;\n }\n\n const candidate = message as { type?: unknown; version?: unknown };\n\n return candidate.type === \"warlock:ready\" && typeof candidate.version === \"number\";\n}\n"],"mappings":";;;;;;;;AAgBA,MAAa,sBAAsB;;;;;;;;;;AAmCnC,MAAa,sBAAsB;;;;;;;;;;;;;;;;;;;AAoBnC,SAAgB,eAAe,QAA0B;CACvD,IAAI,OAAO,QAAQ,SAAS,YAC1B;CAOF,IAAI,QAAQ,+BAA6B,KAAK;EAC5C,IAAI,QAAQ,IAAI,UAAU,QACxB,IAAI;GACF,QAAQ,KAAK,SAAS,QAAW,cAAiB,CAAC,CAAC;EACtD,QAAQ,CAER;EAEF;CACF;CAIA,OAAO,QAAQ,IAAI;CAEnB,IAAI;EACF,QAAQ,KAAK,QAAQ,QAAW,cAAiB;GAC/C,QAAQ,aAAa;EACvB,CAAC;CACH,QAAQ,CAGR;AACF;;;;;;;;;;AAWA,SAAgB,aAAa,SAAyC;CACpE,IAAI,OAAO,YAAY,YAAY,YAAY,MAC7C,OAAO;CAGT,MAAM,YAAY;CAElB,OAAO,UAAU,SAAS,mBAAmB,OAAO,UAAU,YAAY;AAC5E"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { withConsoleOnStderr } from "../routes/with-console-on-stderr.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { readFile, stat } from "node:fs/promises";
|
|
5
|
+
import { resolve as resolve$1 } from "import-meta-resolve";
|
|
6
|
+
|
|
7
|
+
//#region ../../packages/core/src/cli/commands/ai/ai-command.action.ts
|
|
8
|
+
/** Resolve from the caller's project, including optional provider packages. */
|
|
9
|
+
async function loadMeasureModule(specifier, cwd = process.cwd()) {
|
|
10
|
+
const parent = pathToFileURL(path.join(cwd, "package.json")).href;
|
|
11
|
+
return import(resolve$1(specifier, parent));
|
|
12
|
+
}
|
|
13
|
+
function object(value) {
|
|
14
|
+
return value !== null && typeof value === "object";
|
|
15
|
+
}
|
|
16
|
+
function isMeasureModule(value) {
|
|
17
|
+
return typeof value.measure === "function" && typeof value.skills === "function";
|
|
18
|
+
}
|
|
19
|
+
function isModel(value) {
|
|
20
|
+
return object(value) && typeof value.name === "string" && typeof value.provider === "string" && typeof value.complete === "function" && typeof value.stream === "function" && (value.countTokens === void 0 || typeof value.countTokens === "function") && (value.pricing === void 0 || object(value.pricing) && typeof value.pricing.input === "number" && Number.isFinite(value.pricing.input) && value.pricing.input >= 0 && typeof value.pricing.output === "number" && Number.isFinite(value.pricing.output) && value.pricing.output >= 0);
|
|
21
|
+
}
|
|
22
|
+
function isTool(value) {
|
|
23
|
+
return object(value) && typeof value.name === "string" && typeof value.description === "string" && typeof value.execute === "function" && (value.input === void 0 || object(value.input) && object(value.input["~standard"]) && typeof value.input["~standard"].validate === "function");
|
|
24
|
+
}
|
|
25
|
+
/** SDK constructor/model signatures verified against the first-party adapter sources. */
|
|
26
|
+
async function resolveMeasureModel(id, modelModule, load = loadMeasureModule, env = process.env) {
|
|
27
|
+
if (modelModule) {
|
|
28
|
+
const exported = await load(modelModule);
|
|
29
|
+
const model = typeof exported.resolveModel === "function" ? await exported.resolveModel(id) : exported.model ?? exported.default;
|
|
30
|
+
if (!isModel(model)) throw new Error("--model-module must export a model or resolveModel(id) returning a model.");
|
|
31
|
+
return model;
|
|
32
|
+
}
|
|
33
|
+
const separator = id.indexOf(":");
|
|
34
|
+
const provider = separator < 0 ? "openai" : id.slice(0, separator);
|
|
35
|
+
const name = separator < 0 ? id : id.slice(separator + 1);
|
|
36
|
+
const adapters = {
|
|
37
|
+
openai: {
|
|
38
|
+
package: "@warlock.js/ai-openai",
|
|
39
|
+
sdk: "OpenAISDK",
|
|
40
|
+
key: "OPENAI_API_KEY"
|
|
41
|
+
},
|
|
42
|
+
anthropic: {
|
|
43
|
+
package: "@warlock.js/ai-anthropic",
|
|
44
|
+
sdk: "AnthropicSDK",
|
|
45
|
+
key: "ANTHROPIC_API_KEY"
|
|
46
|
+
},
|
|
47
|
+
google: {
|
|
48
|
+
package: "@warlock.js/ai-google",
|
|
49
|
+
sdk: "GoogleSDK",
|
|
50
|
+
key: "GEMINI_API_KEY"
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const adapter = Object.hasOwn(adapters, provider) ? adapters[provider] : void 0;
|
|
54
|
+
if (!adapter || !name) throw new Error("Use a model ID, openai:<id>, anthropic:<id>, google:<id>, or --model-module for a custom provider.");
|
|
55
|
+
const apiKey = env[adapter.key];
|
|
56
|
+
if (!apiKey) throw new Error(`Set ${adapter.key}, or supply a configured model with --model-module.`);
|
|
57
|
+
let exported;
|
|
58
|
+
try {
|
|
59
|
+
exported = await load(adapter.package);
|
|
60
|
+
} catch (cause) {
|
|
61
|
+
throw new Error(`Cannot load ${adapter.package}. Install it in this project or use --model-module.`, { cause });
|
|
62
|
+
}
|
|
63
|
+
const Constructor = exported[adapter.sdk];
|
|
64
|
+
if (typeof Constructor !== "function") throw new Error(`${adapter.package} does not export ${adapter.sdk}.`);
|
|
65
|
+
const model = new Constructor({ apiKey }).model({
|
|
66
|
+
name,
|
|
67
|
+
...provider === "openai" ? { api: "responses" } : {}
|
|
68
|
+
});
|
|
69
|
+
if (!isModel(model)) throw new Error(`${adapter.package} returned an invalid model.`);
|
|
70
|
+
return model;
|
|
71
|
+
}
|
|
72
|
+
function formatMeasurement(report) {
|
|
73
|
+
const lines = [`${report.provider}:${report.model} — input per call`, "Part Tokens Source USD"];
|
|
74
|
+
const row = (name, part) => `${name}\t${part.tokens}\t${part.source}\t${part.costUSD === void 0 ? "unknown" : part.costUSD.toFixed(8)}`;
|
|
75
|
+
for (const [name, part] of Object.entries(report.parts)) lines.push(row(name, part));
|
|
76
|
+
lines.push(row("total", report.total));
|
|
77
|
+
if (report.probe) lines.push("Messages include a user probe: \".\"");
|
|
78
|
+
lines.push("USD uses uncached input pricing; output, cache adjustments and hosted-tool fees are excluded.");
|
|
79
|
+
return lines.join("\n");
|
|
80
|
+
}
|
|
81
|
+
async function aiCommandAction({ args, options }, dependencies = {}) {
|
|
82
|
+
if (args.length !== 1 || args[0] !== "measure") throw new Error("Usage: warlock ai measure --model <id> [--tools <module>] [--system <file>] [--skills <dir>] [--json]");
|
|
83
|
+
const stringOption = (name, required = false) => {
|
|
84
|
+
const value = options[name];
|
|
85
|
+
if (value === void 0 && !required) return void 0;
|
|
86
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`--${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)} requires a value.`);
|
|
87
|
+
return value;
|
|
88
|
+
};
|
|
89
|
+
const id = stringOption("model", true);
|
|
90
|
+
if (!id) throw new Error("--model is required.");
|
|
91
|
+
const modelModule = stringOption("modelModule");
|
|
92
|
+
const toolsModule = stringOption("tools");
|
|
93
|
+
const systemFile = stringOption("system");
|
|
94
|
+
const skillsDir = stringOption("skills");
|
|
95
|
+
const cwd = dependencies.cwd ?? process.cwd();
|
|
96
|
+
const load = dependencies.load ?? ((specifier) => loadMeasureModule(specifier, cwd));
|
|
97
|
+
const calculate = async () => {
|
|
98
|
+
let exported;
|
|
99
|
+
try {
|
|
100
|
+
exported = await load("@warlock.js/ai");
|
|
101
|
+
} catch (cause) {
|
|
102
|
+
throw new Error("Cannot load @warlock.js/ai. Install it in this project to use warlock ai measure.", { cause });
|
|
103
|
+
}
|
|
104
|
+
if (!isMeasureModule(exported)) throw new Error("warlock ai measure requires @warlock.js/ai 5.33.0 or newer.");
|
|
105
|
+
const aiPackage = exported;
|
|
106
|
+
const model = await resolveMeasureModel(id, modelModule, load);
|
|
107
|
+
let tools = [];
|
|
108
|
+
if (toolsModule) {
|
|
109
|
+
const module = await load(toolsModule);
|
|
110
|
+
const entries = module.tools ?? module.default;
|
|
111
|
+
if (!Array.isArray(entries) || !entries.every(isTool)) throw new Error("--tools must export an array of tool configs as tools or default.");
|
|
112
|
+
tools = entries;
|
|
113
|
+
}
|
|
114
|
+
const system = systemFile ? await readFile(path.resolve(cwd, systemFile), "utf8") : void 0;
|
|
115
|
+
let skills;
|
|
116
|
+
if (skillsDir) {
|
|
117
|
+
const directory = path.resolve(cwd, skillsDir);
|
|
118
|
+
if (!(await stat(directory)).isDirectory()) throw new Error("--skills must name a directory.");
|
|
119
|
+
skills = aiPackage.skills({
|
|
120
|
+
name: "measured-skills",
|
|
121
|
+
sources: [{
|
|
122
|
+
type: "directory",
|
|
123
|
+
path: directory
|
|
124
|
+
}]
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return aiPackage.measure({
|
|
128
|
+
model,
|
|
129
|
+
tools,
|
|
130
|
+
system,
|
|
131
|
+
skills
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
const report = options.json === true ? await withConsoleOnStderr(calculate) : await calculate();
|
|
135
|
+
console.log(options.json === true ? JSON.stringify(report, null, 2) : formatMeasurement(report));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//#endregion
|
|
139
|
+
export { aiCommandAction, formatMeasurement, loadMeasureModule, resolveMeasureModel };
|
|
140
|
+
//# sourceMappingURL=ai-command.action.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-command.action.mjs","names":["resolveModule"],"sources":["../../../../../../../../../../packages/core/src/cli/commands/ai/ai-command.action.ts"],"sourcesContent":["import { readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { resolve as resolveModule } from \"import-meta-resolve\";\nimport type { Measurement, ModelContract, ToolConfig } from \"@warlock.js/ai\";\nimport type { CommandActionData } from \"../../../commands/types\";\nimport { withConsoleOnStderr } from \"../routes/with-console-on-stderr\";\n\ntype ModuleExports = Record<string, unknown>;\ntype MeasureModule = Pick<typeof import(\"@warlock.js/ai\"), \"measure\" | \"skills\">;\ntype ModuleLoader = (specifier: string) => Promise<ModuleExports>;\ntype SDKConstructor = new (config: { apiKey: string }) => {\n model(config: { name: string; api?: \"responses\" }): ModelContract;\n};\n\n/** Resolve from the caller's project, including optional provider packages. */\nexport async function loadMeasureModule(\n specifier: string,\n cwd = process.cwd(),\n): Promise<ModuleExports> {\n const parent = pathToFileURL(path.join(cwd, \"package.json\")).href;\n return import(resolveModule(specifier, parent));\n}\n\nfunction object(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\";\n}\n\nfunction isMeasureModule(value: ModuleExports): value is ModuleExports & MeasureModule {\n return typeof value.measure === \"function\" && typeof value.skills === \"function\";\n}\n\nfunction isModel(value: unknown): value is ModelContract {\n return (\n object(value) &&\n typeof value.name === \"string\" &&\n typeof value.provider === \"string\" &&\n typeof value.complete === \"function\" &&\n typeof value.stream === \"function\" &&\n (value.countTokens === undefined || typeof value.countTokens === \"function\") &&\n (value.pricing === undefined ||\n (object(value.pricing) &&\n typeof value.pricing.input === \"number\" &&\n Number.isFinite(value.pricing.input) &&\n value.pricing.input >= 0 &&\n typeof value.pricing.output === \"number\" &&\n Number.isFinite(value.pricing.output) &&\n value.pricing.output >= 0))\n );\n}\n\nfunction isTool(value: unknown): value is ToolConfig<unknown, unknown> {\n return (\n object(value) &&\n typeof value.name === \"string\" &&\n typeof value.description === \"string\" &&\n typeof value.execute === \"function\" &&\n (value.input === undefined ||\n (object(value.input) &&\n object(value.input[\"~standard\"]) &&\n typeof value.input[\"~standard\"].validate === \"function\"))\n );\n}\n\n/** SDK constructor/model signatures verified against the first-party adapter sources. */\nexport async function resolveMeasureModel(\n id: string,\n modelModule: string | undefined,\n load: ModuleLoader = loadMeasureModule,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<ModelContract> {\n if (modelModule) {\n const exported = await load(modelModule);\n const model: unknown =\n typeof exported.resolveModel === \"function\"\n ? await exported.resolveModel(id)\n : (exported.model ?? exported.default);\n if (!isModel(model))\n throw new Error(\"--model-module must export a model or resolveModel(id) returning a model.\");\n return model;\n }\n const separator = id.indexOf(\":\");\n const provider = separator < 0 ? \"openai\" : id.slice(0, separator);\n const name = separator < 0 ? id : id.slice(separator + 1);\n const adapters: Record<string, { package: string; sdk: string; key: string }> = {\n openai: { package: \"@warlock.js/ai-openai\", sdk: \"OpenAISDK\", key: \"OPENAI_API_KEY\" },\n anthropic: {\n package: \"@warlock.js/ai-anthropic\",\n sdk: \"AnthropicSDK\",\n key: \"ANTHROPIC_API_KEY\",\n },\n google: { package: \"@warlock.js/ai-google\", sdk: \"GoogleSDK\", key: \"GEMINI_API_KEY\" },\n };\n const adapter = Object.hasOwn(adapters, provider) ? adapters[provider] : undefined;\n if (!adapter || !name)\n throw new Error(\n \"Use a model ID, openai:<id>, anthropic:<id>, google:<id>, or --model-module for a custom provider.\",\n );\n const apiKey = env[adapter.key];\n if (!apiKey)\n throw new Error(`Set ${adapter.key}, or supply a configured model with --model-module.`);\n let exported: ModuleExports;\n try {\n exported = await load(adapter.package);\n } catch (cause) {\n throw new Error(\n `Cannot load ${adapter.package}. Install it in this project or use --model-module.`,\n { cause },\n );\n }\n const Constructor = exported[adapter.sdk];\n if (typeof Constructor !== \"function\")\n throw new Error(`${adapter.package} does not export ${adapter.sdk}.`);\n const sdk = new (Constructor as SDKConstructor)({ apiKey });\n // The Responses adapter exposes countTokens; the Chat adapter does not.\n const model = sdk.model({\n name,\n ...(provider === \"openai\" ? { api: \"responses\" as const } : {}),\n });\n if (!isModel(model)) throw new Error(`${adapter.package} returned an invalid model.`);\n return model;\n}\n\nexport function formatMeasurement(report: Measurement): string {\n const lines = [\n `${report.provider}:${report.model} — input per call`,\n \"Part\\tTokens\\tSource\\tUSD\",\n ];\n const row = (name: string, part: Measurement[\"total\"]) =>\n `${name}\\t${part.tokens}\\t${part.source}\\t${part.costUSD === undefined ? \"unknown\" : part.costUSD.toFixed(8)}`;\n for (const [name, part] of Object.entries(report.parts)) lines.push(row(name, part));\n lines.push(row(\"total\", report.total));\n if (report.probe) lines.push('Messages include a user probe: \".\"');\n lines.push(\n \"USD uses uncached input pricing; output, cache adjustments and hosted-tool fees are excluded.\",\n );\n return lines.join(\"\\n\");\n}\n\nexport async function aiCommandAction(\n { args, options }: CommandActionData,\n dependencies: { load?: ModuleLoader; cwd?: string } = {},\n): Promise<void> {\n if (args.length !== 1 || args[0] !== \"measure\")\n throw new Error(\n \"Usage: warlock ai measure --model <id> [--tools <module>] [--system <file>] [--skills <dir>] [--json]\",\n );\n const stringOption = (name: string, required = false): string | undefined => {\n const value = options[name];\n if (value === undefined && !required) return undefined;\n if (typeof value !== \"string\" || !value.trim())\n throw new Error(\n `--${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)} requires a value.`,\n );\n return value;\n };\n const id = stringOption(\"model\", true);\n if (!id) throw new Error(\"--model is required.\");\n const modelModule = stringOption(\"modelModule\");\n const toolsModule = stringOption(\"tools\");\n const systemFile = stringOption(\"system\");\n const skillsDir = stringOption(\"skills\");\n const cwd = dependencies.cwd ?? process.cwd();\n const load = dependencies.load ?? ((specifier) => loadMeasureModule(specifier, cwd));\n const calculate = async (): Promise<Measurement> => {\n let exported: ModuleExports;\n try {\n exported = await load(\"@warlock.js/ai\");\n } catch (cause) {\n throw new Error(\n \"Cannot load @warlock.js/ai. Install it in this project to use warlock ai measure.\",\n { cause },\n );\n }\n // The optional peer stays behind the action; unrelated CLI commands never import AI.\n if (!isMeasureModule(exported)) {\n throw new Error(\"warlock ai measure requires @warlock.js/ai 5.33.0 or newer.\");\n }\n const aiPackage = exported;\n const model = await resolveMeasureModel(id, modelModule, load);\n let tools: ToolConfig<unknown, unknown>[] = [];\n if (toolsModule) {\n const module = await load(toolsModule);\n const entries: unknown = module.tools ?? module.default;\n if (!Array.isArray(entries) || !entries.every(isTool))\n throw new Error(\"--tools must export an array of tool configs as tools or default.\");\n tools = entries;\n }\n const system = systemFile ? await readFile(path.resolve(cwd, systemFile), \"utf8\") : undefined;\n let skills: ReturnType<typeof aiPackage.skills> | undefined;\n if (skillsDir) {\n const directory = path.resolve(cwd, skillsDir);\n if (!(await stat(directory)).isDirectory())\n throw new Error(\"--skills must name a directory.\");\n skills = aiPackage.skills({\n name: \"measured-skills\",\n sources: [{ type: \"directory\", path: directory }],\n });\n }\n return aiPackage.measure({ model, tools, system, skills });\n };\n const report = options.json === true ? await withConsoleOnStderr(calculate) : await calculate();\n console.log(options.json === true ? JSON.stringify(report, null, 2) : formatMeasurement(report));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { command } from "../../commands/cli-command.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../../packages/core/src/cli/commands/ai.command.ts
|
|
4
|
+
/** Built in so installing AI does not require cli.commands registration. */
|
|
5
|
+
const aiCommand = command({
|
|
6
|
+
name: "ai <action>",
|
|
7
|
+
description: "Measure AI input tokens and uncached input USD per call",
|
|
8
|
+
preload: {},
|
|
9
|
+
options: [
|
|
10
|
+
{
|
|
11
|
+
text: "--model",
|
|
12
|
+
description: "Model ID (OpenAI), or openai: / anthropic: / google: ID",
|
|
13
|
+
type: "string",
|
|
14
|
+
required: true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
text: "--model-module",
|
|
18
|
+
description: "Module exporting a model or resolveModel(id)",
|
|
19
|
+
type: "string"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
text: "--tools",
|
|
23
|
+
description: "Module exporting a tools array (named tools or default)",
|
|
24
|
+
type: "string"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
text: "--system",
|
|
28
|
+
description: "UTF-8 system prompt file",
|
|
29
|
+
type: "string"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
text: "--skills",
|
|
33
|
+
description: "Directory containing skill folders with SKILL.md files",
|
|
34
|
+
type: "string"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
text: "--json",
|
|
38
|
+
description: "Print one JSON report to stdout",
|
|
39
|
+
type: "boolean"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
action: async (data) => {
|
|
43
|
+
const { aiCommandAction } = await import("./ai/ai-command.action.mjs");
|
|
44
|
+
await aiCommandAction(data);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { aiCommand };
|
|
50
|
+
//# sourceMappingURL=ai.command.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.command.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/cli/commands/ai.command.ts"],"sourcesContent":["import { command } from \"../../commands/cli-command\";\n\n/** Built in so installing AI does not require cli.commands registration. */\nexport const aiCommand = command({\n name: \"ai <action>\",\n description: \"Measure AI input tokens and uncached input USD per call\",\n preload: {},\n options: [\n {\n text: \"--model\",\n description: \"Model ID (OpenAI), or openai: / anthropic: / google: ID\",\n type: \"string\",\n required: true,\n },\n {\n text: \"--model-module\",\n description: \"Module exporting a model or resolveModel(id)\",\n type: \"string\",\n },\n {\n text: \"--tools\",\n description: \"Module exporting a tools array (named tools or default)\",\n type: \"string\",\n },\n { text: \"--system\", description: \"UTF-8 system prompt file\", type: \"string\" },\n {\n text: \"--skills\",\n description: \"Directory containing skill folders with SKILL.md files\",\n type: \"string\",\n },\n { text: \"--json\", description: \"Print one JSON report to stdout\", type: \"boolean\" },\n ],\n action: async (data) => {\n const { aiCommandAction } = await import(\"./ai/ai-command.action\");\n await aiCommandAction(data);\n },\n});\n"],"mappings":";;;;AAGA,MAAa,YAAY,QAAQ;CAC/B,MAAM;CACN,aAAa;CACb,SAAS,CAAC;CACV,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GAAE,MAAM;GAAY,aAAa;GAA4B,MAAM;EAAS;EAC5E;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GAAE,MAAM;GAAU,aAAa;GAAmC,MAAM;EAAU;CACpF;CACA,QAAQ,OAAO,SAAS;EACtB,MAAM,EAAE,oBAAoB,MAAM,OAAO;EACzC,MAAM,gBAAgB,IAAI;CAC5B;AACF,CAAC"}
|
|
@@ -30,6 +30,9 @@ function parseSkippedSteps(value) {
|
|
|
30
30
|
* @param data Parsed CLI args: `changed`, `base`, `skip`, `project`, `json`.
|
|
31
31
|
*/
|
|
32
32
|
async function checkCommandAction({ options }) {
|
|
33
|
+
const lint = stringOption(options.lint);
|
|
34
|
+
if (lint === "eslint") throw new Error("ESLint support was removed in 5.34.0. Oxlint/Oxfmt is the default. Run npx warlock upgrade --from 5.33 --to 5.34.0.");
|
|
35
|
+
if (lint !== void 0 && lint !== "oxlint") throw new Error("Unknown lint tool. Use --lint oxlint.");
|
|
33
36
|
const base = stringOption(options.base);
|
|
34
37
|
const changed = options.changed === true || base !== void 0;
|
|
35
38
|
const project = stringOption(options.project);
|
|
@@ -37,6 +40,7 @@ async function checkCommandAction({ options }) {
|
|
|
37
40
|
const cliEntry = process.argv[1];
|
|
38
41
|
const flags = [...changed ? ["--changed"] : [], ...base ? ["--base", base] : []];
|
|
39
42
|
const report = await runCheck({
|
|
43
|
+
lint,
|
|
40
44
|
changed,
|
|
41
45
|
base,
|
|
42
46
|
skip,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-command.action.mjs","names":[],"sources":["../../../../../../../../../../packages/core/src/cli/commands/check/check-command.action.ts"],"sourcesContent":["import type { CommandActionData } from \"../../../commands/types\";\nimport { CHECK_STEPS, type CheckStepName } from \"../../../tooling/check/check-report.types\";\nimport { runCheck } from \"../../../tooling/check/run-check\";\nimport { flushAndExit } from \"../../flush-and-exit\";\nimport { childTypecheckRunner } from \"./child-typecheck\";\nimport { formatCheckSummary, printProblems } from \"./format-problems\";\n\nfunction stringOption(value: string | number | boolean | undefined): string | undefined {\n return typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction isStepName(value: string): value is CheckStepName {\n return (CHECK_STEPS as readonly string[]).includes(value);\n}\n\n/** Parse `--skip typecheck,lint`; throws on an unknown step name. */\nexport function parseSkippedSteps(value: string | undefined): CheckStepName[] {\n if (!value) return [];\n\n const names = value\n .split(\",\")\n .map((name) => name.trim())\n .filter((name) => name.length > 0);\n const unknown = names.filter((name) => !isStepName(name));\n\n if (unknown.length > 0) {\n throw new Error(\n `Unknown check step(s): ${unknown.join(\", \")}. Valid steps: ${CHECK_STEPS.join(\", \")}.`,\n );\n }\n\n return names.filter(isStepName);\n}\n\n/**\n * Action behind `warlock check`.\n *\n * Runs {@link runCheck} with the typecheck in a child `warlock typecheck`\n * process (so a heap overflow is reported, not fatal), prints the problems and\n * the step summary — or, with `--json`, exactly the report — and exits with\n * the report's code: 0 clean, 1 your code, 2 this machine.\n *\n * @param data Parsed CLI args: `changed`, `base`, `skip`, `project`, `json`.\n */\nexport async function checkCommandAction({ options }: CommandActionData): Promise<void> {\n const base = stringOption(options.base);\n const changed = options.changed === true || base !== undefined;\n const project = stringOption(options.project);\n const skip = parseSkippedSteps(stringOption(options.skip));\n const cliEntry = process.argv[1];\n const flags = [...(changed ? [\"--changed\"] : []), ...(base ? [\"--base\", base] : [])];\n\n const report = await runCheck({\n changed,\n base,\n skip,\n project,\n ...(cliEntry ? { typecheck: childTypecheckRunner(cliEntry, flags) } : {}),\n });\n\n if (options.json === true) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printProblems(report.problems);\n\n if (report.problems.length > 0) console.log(\"\");\n\n for (const line of formatCheckSummary(report)) {\n console.log(line);\n }\n }\n\n if (report.exitCode !== 0) {\n await flushAndExit(report.exitCode);\n }\n}\n"],"mappings":";;;;;;;AAOA,SAAS,aAAa,OAAkE;CACtF,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,SAAS,IAAI,MAAM,KAAK,IAAI;AAC/E;AAEA,SAAS,WAAW,OAAuC;CACzD,OAAQ,YAAkC,SAAS,KAAK;AAC1D;;AAGA,SAAgB,kBAAkB,OAA4C;CAC5E,IAAI,CAAC,OAAO,OAAO,CAAC;CAEpB,MAAM,QAAQ,MACX,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,KAAK,SAAS,CAAC;CACnC,MAAM,UAAU,MAAM,QAAQ,SAAS,CAAC,WAAW,IAAI,CAAC;CAExD,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,0BAA0B,QAAQ,KAAK,IAAI,EAAE,iBAAiB,YAAY,KAAK,IAAI,EAAE,EACvF;CAGF,OAAO,MAAM,OAAO,UAAU;AAChC;;;;;;;;;;;AAYA,eAAsB,mBAAmB,EAAE,WAA6C;CACtF,MAAM,OAAO,aAAa,QAAQ,IAAI;CACtC,MAAM,UAAU,QAAQ,YAAY,QAAQ,SAAS;CACrD,MAAM,UAAU,aAAa,QAAQ,OAAO;CAC5C,MAAM,OAAO,kBAAkB,aAAa,QAAQ,IAAI,CAAC;CACzD,MAAM,WAAW,QAAQ,KAAK;CAC9B,MAAM,QAAQ,CAAC,GAAI,UAAU,CAAC,WAAW,IAAI,CAAC,GAAI,GAAI,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,CAAE;CAEnF,MAAM,SAAS,MAAM,SAAS;EAC5B;EACA;EACA;EACA;EACA,GAAI,WAAW,EAAE,WAAW,qBAAqB,UAAU,KAAK,EAAE,IAAI,CAAC;CACzE,CAAC;CAED,IAAI,QAAQ,SAAS,MACnB,QAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;MACtC;EACL,cAAc,OAAO,QAAQ;EAE7B,IAAI,OAAO,SAAS,SAAS,GAAG,QAAQ,IAAI,EAAE;EAE9C,KAAK,MAAM,QAAQ,mBAAmB,MAAM,GAC1C,QAAQ,IAAI,IAAI;CAEpB;CAEA,IAAI,OAAO,aAAa,GACtB,MAAM,aAAa,OAAO,QAAQ;AAEtC"}
|
|
1
|
+
{"version":3,"file":"check-command.action.mjs","names":[],"sources":["../../../../../../../../../../packages/core/src/cli/commands/check/check-command.action.ts"],"sourcesContent":["import type { CommandActionData } from \"../../../commands/types\";\nimport { CHECK_STEPS, type CheckStepName } from \"../../../tooling/check/check-report.types\";\nimport { runCheck } from \"../../../tooling/check/run-check\";\nimport { flushAndExit } from \"../../flush-and-exit\";\nimport { childTypecheckRunner } from \"./child-typecheck\";\nimport { formatCheckSummary, printProblems } from \"./format-problems\";\n\nfunction stringOption(value: string | number | boolean | undefined): string | undefined {\n return typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction isStepName(value: string): value is CheckStepName {\n return (CHECK_STEPS as readonly string[]).includes(value);\n}\n\n/** Parse `--skip typecheck,lint`; throws on an unknown step name. */\nexport function parseSkippedSteps(value: string | undefined): CheckStepName[] {\n if (!value) return [];\n\n const names = value\n .split(\",\")\n .map((name) => name.trim())\n .filter((name) => name.length > 0);\n const unknown = names.filter((name) => !isStepName(name));\n\n if (unknown.length > 0) {\n throw new Error(\n `Unknown check step(s): ${unknown.join(\", \")}. Valid steps: ${CHECK_STEPS.join(\", \")}.`,\n );\n }\n\n return names.filter(isStepName);\n}\n\n/**\n * Action behind `warlock check`.\n *\n * Runs {@link runCheck} with the typecheck in a child `warlock typecheck`\n * process (so a heap overflow is reported, not fatal), prints the problems and\n * the step summary — or, with `--json`, exactly the report — and exits with\n * the report's code: 0 clean, 1 your code, 2 this machine.\n *\n * @param data Parsed CLI args: `changed`, `base`, `skip`, `project`, `json`.\n */\nexport async function checkCommandAction({ options }: CommandActionData): Promise<void> {\n const lint = stringOption(options.lint);\n if (lint === \"eslint\") {\n throw new Error(\n \"ESLint support was removed in 5.34.0. Oxlint/Oxfmt is the default. Run npx warlock upgrade --from 5.33 --to 5.34.0.\",\n );\n }\n if (lint !== undefined && lint !== \"oxlint\") {\n throw new Error(\"Unknown lint tool. Use --lint oxlint.\");\n }\n const base = stringOption(options.base);\n const changed = options.changed === true || base !== undefined;\n const project = stringOption(options.project);\n const skip = parseSkippedSteps(stringOption(options.skip));\n const cliEntry = process.argv[1];\n const flags = [...(changed ? [\"--changed\"] : []), ...(base ? [\"--base\", base] : [])];\n\n const report = await runCheck({\n lint,\n changed,\n base,\n skip,\n project,\n ...(cliEntry ? { typecheck: childTypecheckRunner(cliEntry, flags) } : {}),\n });\n\n if (options.json === true) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printProblems(report.problems);\n\n if (report.problems.length > 0) console.log(\"\");\n\n for (const line of formatCheckSummary(report)) {\n console.log(line);\n }\n }\n\n if (report.exitCode !== 0) {\n await flushAndExit(report.exitCode);\n }\n}\n"],"mappings":";;;;;;;AAOA,SAAS,aAAa,OAAkE;CACtF,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,SAAS,IAAI,MAAM,KAAK,IAAI;AAC/E;AAEA,SAAS,WAAW,OAAuC;CACzD,OAAQ,YAAkC,SAAS,KAAK;AAC1D;;AAGA,SAAgB,kBAAkB,OAA4C;CAC5E,IAAI,CAAC,OAAO,OAAO,CAAC;CAEpB,MAAM,QAAQ,MACX,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,KAAK,SAAS,CAAC;CACnC,MAAM,UAAU,MAAM,QAAQ,SAAS,CAAC,WAAW,IAAI,CAAC;CAExD,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,0BAA0B,QAAQ,KAAK,IAAI,EAAE,iBAAiB,YAAY,KAAK,IAAI,EAAE,EACvF;CAGF,OAAO,MAAM,OAAO,UAAU;AAChC;;;;;;;;;;;AAYA,eAAsB,mBAAmB,EAAE,WAA6C;CACtF,MAAM,OAAO,aAAa,QAAQ,IAAI;CACtC,IAAI,SAAS,UACX,MAAM,IAAI,MACR,qHACF;CAEF,IAAI,SAAS,UAAa,SAAS,UACjC,MAAM,IAAI,MAAM,uCAAuC;CAEzD,MAAM,OAAO,aAAa,QAAQ,IAAI;CACtC,MAAM,UAAU,QAAQ,YAAY,QAAQ,SAAS;CACrD,MAAM,UAAU,aAAa,QAAQ,OAAO;CAC5C,MAAM,OAAO,kBAAkB,aAAa,QAAQ,IAAI,CAAC;CACzD,MAAM,WAAW,QAAQ,KAAK;CAC9B,MAAM,QAAQ,CAAC,GAAI,UAAU,CAAC,WAAW,IAAI,CAAC,GAAI,GAAI,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,CAAE;CAEnF,MAAM,SAAS,MAAM,SAAS;EAC5B;EACA;EACA;EACA;EACA;EACA,GAAI,WAAW,EAAE,WAAW,qBAAqB,UAAU,KAAK,EAAE,IAAI,CAAC;CACzE,CAAC;CAED,IAAI,QAAQ,SAAS,MACnB,QAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;MACtC;EACL,cAAc,OAAO,QAAQ;EAE7B,IAAI,OAAO,SAAS,SAAS,GAAG,QAAQ,IAAI,EAAE;EAE9C,KAAK,MAAM,QAAQ,mBAAmB,MAAM,GAC1C,QAAQ,IAAI,IAAI;CAEpB;CAEA,IAAI,OAAO,aAAa,GACtB,MAAM,aAAa,OAAO,QAAQ;AAEtC"}
|
|
@@ -4,7 +4,7 @@ import { checkCommandAction } from "./check/check-command.action.mjs";
|
|
|
4
4
|
//#region ../../packages/core/src/cli/commands/check.command.ts
|
|
5
5
|
/**
|
|
6
6
|
* `warlock check` — the single verify command: typecheck, lint (the app's
|
|
7
|
-
*
|
|
7
|
+
* Oxlint config), the tests affected by the change, and framework
|
|
8
8
|
* convention checks, reported as one problem list. Exit 0 clean, 1 problems
|
|
9
9
|
* in the code, 2 problems with this machine.
|
|
10
10
|
*
|
|
@@ -17,6 +17,11 @@ const checkCommand = command({
|
|
|
17
17
|
description: "Typecheck, lint, run the affected tests and check conventions; one problem list, exit 1 for your code and 2 for this machine",
|
|
18
18
|
action: checkCommandAction,
|
|
19
19
|
options: [
|
|
20
|
+
{
|
|
21
|
+
text: "--lint",
|
|
22
|
+
description: "Lint tool: oxlint (default)",
|
|
23
|
+
type: "string"
|
|
24
|
+
},
|
|
20
25
|
{
|
|
21
26
|
text: "--changed, -c",
|
|
22
27
|
description: "Only the files changed against HEAD (plus untracked files) and what they affect",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.command.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/cli/commands/check.command.ts"],"sourcesContent":["import { command } from \"../../commands/cli-command\";\nimport { checkCommandAction } from \"./check/check-command.action\";\n\n/**\n * `warlock check` — the single verify command: typecheck, lint (the app's\n *
|
|
1
|
+
{"version":3,"file":"check.command.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/cli/commands/check.command.ts"],"sourcesContent":["import { command } from \"../../commands/cli-command\";\nimport { checkCommandAction } from \"./check/check-command.action\";\n\n/**\n * `warlock check` — the single verify command: typecheck, lint (the app's\n * Oxlint config), the tests affected by the change, and framework\n * convention checks, reported as one problem list. Exit 0 clean, 1 problems\n * in the code, 2 problems with this machine.\n *\n * No preload: the steps read files and run tools; nothing boots, connects or\n * binds a port, so `check` behaves the same with or without a database or\n * Redis running.\n */\nexport const checkCommand = command({\n name: \"check\",\n description:\n \"Typecheck, lint, run the affected tests and check conventions; one problem list, exit 1 for your code and 2 for this machine\",\n action: checkCommandAction,\n options: [\n {\n text: \"--lint\",\n description: \"Lint tool: oxlint (default)\",\n type: \"string\",\n },\n {\n text: \"--changed, -c\",\n description:\n \"Only the files changed against HEAD (plus untracked files) and what they affect\",\n type: \"boolean\",\n },\n {\n text: \"--base, -b\",\n description: \"Compare with the merge base of this ref instead of HEAD (implies --changed)\",\n type: \"string\",\n },\n {\n text: \"--skip, -s\",\n description:\n \"Comma-separated steps to skip: environment, typecheck, lint, tests, conventions\",\n type: \"string\",\n },\n {\n text: \"--project, -p\",\n description: \"tsconfig path (default tsconfig.json)\",\n type: \"string\",\n },\n {\n text: \"--json, -j\",\n description: \"Print the report as JSON\",\n type: \"boolean\",\n },\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAa,eAAe,QAAQ;CAClC,MAAM;CACN,aACE;CACF,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC"}
|