@oxy-hq/cli 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -7
- package/dist/main.mjs +4914 -3760
- package/dist/main.mjs.map +1 -1
- package/dist/{mcp-xxWL3_dz.mjs → mcp-Bdp0rX0l.mjs} +2 -2
- package/dist/{mcp-xxWL3_dz.mjs.map → mcp-Bdp0rX0l.mjs.map} +1 -1
- package/package.json +5 -5
- package/skills/oxy-agents/SKILL.md +318 -0
- package/skills/oxy-agents/references/quick-reference.md +222 -0
- package/skills/oxy-agents/templates/agentic-template.yml +141 -0
- package/skills/oxy-airway-pipelines/SKILL.md +223 -316
- package/skills/oxy-automations/SKILL.md +61 -15
- package/skills/oxy-cli/SKILL.md +32 -5
- package/skills/oxy-custom-apps/SKILL.md +193 -291
- package/skills/oxy-custom-apps/references/minimal-starter.md +63 -0
- package/skills/oxy-custom-apps/references/performance-and-usage.md +105 -0
- package/skills/oxy-custom-apps/references/reading-data.md +106 -0
- package/skills/oxy-custom-apps/references/sandbox-agent-loop.md +267 -0
- package/skills/oxy-custom-apps/references/verifying-in-the-browser.md +65 -0
- package/skills/oxy-data-apps/SKILL.md +362 -0
- package/skills/oxy-data-apps/references/examples.md +263 -0
- package/skills/oxy-data-apps/references/quick-reference.md +171 -0
- package/skills/oxy-data-apps/templates/dashboard-with-controls.app.yml +128 -0
- package/skills/oxy-data-apps/templates/simple-dashboard.app.yml +102 -0
- package/skills/oxy-data-architecture/SKILL.md +137 -152
- package/skills/oxy-evals/SKILL.md +362 -0
- package/skills/oxy-evals/references/diagnosis.md +243 -0
- package/skills/oxy-evals/references/results-json.md +145 -0
- package/skills/oxy-evals/references/writing-expected.md +112 -0
- package/skills/oxy-semantic-modeling/SKILL.md +239 -59
- package/skills/oxy-semantic-modeling/references/warehouse-casts.md +57 -0
- package/skills/oxy-semantic-modeling/templates/topic-template.yml +38 -0
- package/skills/oxy-semantic-modeling/templates/view-template.yml +119 -0
- package/template/.github/workflows/publish.yaml +15 -13
- package/template/.nvmrc +1 -1
- package/template/.oxyc-managed +5 -5
- package/template/README.md +21 -10
- package/template/package.json +3 -2
- package/template/pnpm-workspace.yaml +28 -17
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Casting a date column per warehouse
|
|
2
|
+
|
|
3
|
+
Use when a dimension whose meaning is a date or timestamp sits on a column that
|
|
4
|
+
is **not** stored as `DATE` / `DATETIME` / `TIMESTAMP`. Declare the dimension
|
|
5
|
+
`type: date` (or `type: datetime`) and wrap its `expr` with a cast, so filters
|
|
6
|
+
compile against a real date instead of failing with `TYPE_MISMATCH` on the
|
|
7
|
+
first query that filters on it.
|
|
8
|
+
|
|
9
|
+
**Sample first.** Stored formats differ between warehouses and between tables in
|
|
10
|
+
one warehouse — ISO strings (`"2024-01-31"`), compact strings (`"20240131"`),
|
|
11
|
+
integers (`20240131`), epoch seconds or milliseconds. One
|
|
12
|
+
`SELECT <col> FROM <table> LIMIT 1` tells you which you have; guessing is cheap
|
|
13
|
+
to get wrong and the failure only shows at query time.
|
|
14
|
+
|
|
15
|
+
| Warehouse | Cast functions |
|
|
16
|
+
| ---------- | -------------- |
|
|
17
|
+
| ClickHouse | `toDate(<col>)`, `toDateTime(<col>)`, `parseDateTimeBestEffort(<col>)`, `toDate(toString(<col>))` for integer-encoded dates |
|
|
18
|
+
| BigQuery | `CAST(<col> AS DATE)`, `PARSE_DATE('<format>', <col>)`, `PARSE_TIMESTAMP('<format>', <col>)`, `TIMESTAMP_SECONDS(<col>)` |
|
|
19
|
+
| Snowflake | `TO_DATE(<col>[, '<format>'])`, `TO_TIMESTAMP(<col>)`, `TRY_TO_DATE(<col>)` |
|
|
20
|
+
| Postgres | `(<col>)::date`, `(<col>)::timestamp`, `to_date(<col>, '<format>')`, `to_timestamp(<col>)` |
|
|
21
|
+
| DuckDB / Airhouse | `CAST(<col> AS DATE)`, `strptime(<col>, '<format>')`, `to_timestamp(<col>)`, `epoch_ms(<col>)` |
|
|
22
|
+
|
|
23
|
+
A column already typed as a date or timestamp needs no cast — `expr: <col>` is
|
|
24
|
+
enough.
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
# ClickHouse: business_date stored as Int32 like 20240131
|
|
30
|
+
- name: business_date
|
|
31
|
+
type: date
|
|
32
|
+
expr: "toDate(toString(business_date))"
|
|
33
|
+
|
|
34
|
+
# BigQuery: order_date stored as STRING like "2024-01-31"
|
|
35
|
+
- name: order_date
|
|
36
|
+
type: date
|
|
37
|
+
expr: "PARSE_DATE('%Y-%m-%d', order_date)"
|
|
38
|
+
|
|
39
|
+
# Snowflake: created_at already a TIMESTAMP — no cast
|
|
40
|
+
- name: created_at
|
|
41
|
+
type: datetime
|
|
42
|
+
expr: created_at
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Derived parts re-derive from the raw column
|
|
46
|
+
|
|
47
|
+
A derived dimension (`business_month`, `day_of_week`, …) repeats the cast
|
|
48
|
+
inside its own `expr` rather than referencing the base date dimension:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
- name: business_quarter
|
|
52
|
+
type: number
|
|
53
|
+
expr: "toQuarter(toDate(toString(business_date)))"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If the same cast chain appears in many dimensions, the column type is the real
|
|
57
|
+
problem — fix it once in the pipeline that loads the table, then drop the casts.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Oxy topic template — copy, rename to <name>.topic.yml, and fill in.
|
|
2
|
+
# A topic is a question area: a base view plus the views it may join to.
|
|
3
|
+
# Name it after the questions people ask, not after a table; most topics
|
|
4
|
+
# span several views that share entity names.
|
|
5
|
+
|
|
6
|
+
name: my_topic_name
|
|
7
|
+
description: "Which questions this topic answers, and what it always excludes"
|
|
8
|
+
base_view: primary_view_name # the view queries start from
|
|
9
|
+
views:
|
|
10
|
+
- primary_view_name
|
|
11
|
+
# - related_view_name # joins resolve through matching entity names
|
|
12
|
+
|
|
13
|
+
# Optional: filters applied to every query in this topic, ANDed with the
|
|
14
|
+
# user's own. Write `field:` as view.dimension — a bare name is looked up in a
|
|
15
|
+
# view named like the TOPIC, not the base view, so it only works when the two
|
|
16
|
+
# share a name.
|
|
17
|
+
# default_filters:
|
|
18
|
+
# - field: primary_view_name.is_voided
|
|
19
|
+
# eq:
|
|
20
|
+
# value: false
|
|
21
|
+
#
|
|
22
|
+
# - field: primary_view_name.status
|
|
23
|
+
# not_in:
|
|
24
|
+
# values: ["cancelled", "test"]
|
|
25
|
+
#
|
|
26
|
+
# - field: primary_view_name.amount
|
|
27
|
+
# gte:
|
|
28
|
+
# value: 0
|
|
29
|
+
#
|
|
30
|
+
# - field: primary_view_name.created_date
|
|
31
|
+
# in_date_range:
|
|
32
|
+
# from: "2024-01-01" # a date, or a relative expression
|
|
33
|
+
# to: "now"
|
|
34
|
+
|
|
35
|
+
# Operators (the operator name is the key; its value sits under it):
|
|
36
|
+
# - eq / neq / gt / gte / lt / lte: value: <scalar>
|
|
37
|
+
# - in / not_in: values: [<scalar>, ...]
|
|
38
|
+
# - in_date_range / not_in_date_range: from: <date>, to: <date>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Oxy view template — copy, rename to <name>.view.yml, and fill in.
|
|
2
|
+
# One view = one table (or `sql:`) plus the vocabulary an agent may use on it.
|
|
3
|
+
# No `# yaml-language-server: $schema=` header: no schema is published for views.
|
|
4
|
+
|
|
5
|
+
name: my_view_name
|
|
6
|
+
description: "What a row is, when the data is complete, and what it excludes"
|
|
7
|
+
datasource: "database_name" # a database name from config.yml
|
|
8
|
+
table: "schema_name.table_name"
|
|
9
|
+
|
|
10
|
+
entities:
|
|
11
|
+
# Primary entity: the grain of one row (one per view).
|
|
12
|
+
- name: primary_entity_name
|
|
13
|
+
type: primary
|
|
14
|
+
description: "What one row represents"
|
|
15
|
+
key: primary_key_dimension # a DIMENSION name below, never a raw column
|
|
16
|
+
|
|
17
|
+
# Foreign entity: one per foreign-key column. Use the SAME name as the other
|
|
18
|
+
# view's primary entity — matching names are what create the join.
|
|
19
|
+
# - name: related_entity_name
|
|
20
|
+
# type: foreign
|
|
21
|
+
# description: "The related row in another view"
|
|
22
|
+
# key: foreign_key_dimension
|
|
23
|
+
|
|
24
|
+
dimensions:
|
|
25
|
+
# Key dimensions (entity keys reference these by name)
|
|
26
|
+
- name: primary_key_dimension
|
|
27
|
+
type: string
|
|
28
|
+
description: "Primary key"
|
|
29
|
+
expr: primary_key_column
|
|
30
|
+
|
|
31
|
+
# Regular dimensions (filterable / groupable)
|
|
32
|
+
- name: dimension_name
|
|
33
|
+
type: string # string | number | date | datetime | boolean
|
|
34
|
+
description: "Business-friendly description"
|
|
35
|
+
expr: column_name_or_sql_expression
|
|
36
|
+
samples: ["VALUE_A", "VALUE_B"] # the FULL set of valid values for enum-like fields
|
|
37
|
+
synonyms: ["alternative name", "abbreviation"]
|
|
38
|
+
|
|
39
|
+
# Date dimensions: type date/datetime, and expr must yield a real date.
|
|
40
|
+
# If the column is a string, integer or epoch, wrap it in a cast (sample one
|
|
41
|
+
# row first) — otherwise date filters fail with TYPE_MISMATCH.
|
|
42
|
+
- name: created_date
|
|
43
|
+
type: date
|
|
44
|
+
description: "Date the record was created"
|
|
45
|
+
expr: created_at
|
|
46
|
+
|
|
47
|
+
# Derived date parts, so the agent never writes date arithmetic
|
|
48
|
+
- name: created_year
|
|
49
|
+
type: number
|
|
50
|
+
description: "Year the record was created"
|
|
51
|
+
expr: "EXTRACT(YEAR FROM created_at)"
|
|
52
|
+
synonyms: ["year"]
|
|
53
|
+
|
|
54
|
+
# Boolean dimensions
|
|
55
|
+
- name: is_active
|
|
56
|
+
type: boolean
|
|
57
|
+
description: "Whether the record is active"
|
|
58
|
+
expr: "status = 'active'"
|
|
59
|
+
samples: ["true", "false"] # samples are ALWAYS strings, even for boolean/number dimensions
|
|
60
|
+
|
|
61
|
+
measures:
|
|
62
|
+
- name: total_records
|
|
63
|
+
type: count
|
|
64
|
+
description: "Number of records"
|
|
65
|
+
synonyms: ["record count"]
|
|
66
|
+
|
|
67
|
+
- name: total_amount
|
|
68
|
+
type: sum
|
|
69
|
+
description: "Sum of amounts"
|
|
70
|
+
expr: amount_column
|
|
71
|
+
synonyms: ["total"]
|
|
72
|
+
|
|
73
|
+
- name: average_amount
|
|
74
|
+
type: average
|
|
75
|
+
description: "Average amount per record"
|
|
76
|
+
expr: amount_column
|
|
77
|
+
|
|
78
|
+
# Filtered measure: "aggregate X where Y". Each filter expr is a SQL
|
|
79
|
+
# condition over this view's columns, or a bare {{dimension}} naming a
|
|
80
|
+
# dimension of THIS view. Prefer this over a custom CASE WHEN: it can be
|
|
81
|
+
# answered from a pre-aggregation, a custom measure never can.
|
|
82
|
+
- name: completed_amount
|
|
83
|
+
type: sum
|
|
84
|
+
description: "Sum of amounts for completed records only"
|
|
85
|
+
expr: amount_column
|
|
86
|
+
filters:
|
|
87
|
+
- expr: "status = 'completed'"
|
|
88
|
+
|
|
89
|
+
# Custom measure: only for what a filtered measure cannot express (ratios,
|
|
90
|
+
# correlations, cross-period math). The expr is already an aggregate.
|
|
91
|
+
# Never served from a pre-aggregation (nor are median or number measures).
|
|
92
|
+
- name: completion_rate
|
|
93
|
+
type: custom
|
|
94
|
+
description: "Share of records that are completed"
|
|
95
|
+
expr: "SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) / NULLIF(COUNT(*), 0)"
|
|
96
|
+
|
|
97
|
+
# Freshness — OPTIONAL but cheap. Every value is a one-element LIST of strings.
|
|
98
|
+
# Without freshness_watermark_column the agent uses the first date dimension.
|
|
99
|
+
# meta:
|
|
100
|
+
# freshness_watermark_column: ["created_date"]
|
|
101
|
+
# freshness_expected_cadence: ["hourly"]
|
|
102
|
+
# freshness_complete_through: ["previous full hour (UTC)"]
|
|
103
|
+
# freshness_caveat: ["The current hour is partial."]
|
|
104
|
+
|
|
105
|
+
# Pre-aggregations (rollups) — OPTIONAL and opt-in: with no block, the view has
|
|
106
|
+
# no rollups at all. Members are names declared above, never raw columns.
|
|
107
|
+
#
|
|
108
|
+
# refresh_key: # view-level default; exactly ONE of every / sql
|
|
109
|
+
# every: "1h"
|
|
110
|
+
#
|
|
111
|
+
# pre_aggregations:
|
|
112
|
+
# - name: by_month
|
|
113
|
+
# dimensions: [dimension_name]
|
|
114
|
+
# measures: [total_records, total_amount] # count/count_distinct/sum/average/min/max
|
|
115
|
+
# time_dimension: created_date # a date/datetime dimension above
|
|
116
|
+
# granularity: day # second|minute|hour|day|week|month|quarter|year; serves this
|
|
117
|
+
# # grain or coarser — but a `week` rollup never serves month+
|
|
118
|
+
# refresh_key: # optional per-rollup override
|
|
119
|
+
# sql: "SELECT MAX(updated_at) FROM schema_name.table_name"
|
|
@@ -160,7 +160,7 @@ env:
|
|
|
160
160
|
# service account are honoured from 0.7.0, which tries them first and falls
|
|
161
161
|
# back to the publisher. A pin older than that still publishes, through the
|
|
162
162
|
# publisher alone.
|
|
163
|
-
OXYC_VERSION: "0.
|
|
163
|
+
OXYC_VERSION: "0.9.0"
|
|
164
164
|
|
|
165
165
|
jobs:
|
|
166
166
|
build:
|
|
@@ -281,25 +281,27 @@ jobs:
|
|
|
281
281
|
|
|
282
282
|
# pnpm BEFORE setup-node: setup-node's `cache: pnpm` shells out to
|
|
283
283
|
# `pnpm store path`, so it fails if pnpm is not already installed.
|
|
284
|
+
#
|
|
285
|
+
# v6, the first major that supports pnpm 11; v4 does not. And NO
|
|
286
|
+
# `version:` input, on purpose: the action installs the pnpm that
|
|
287
|
+
# package.json's `packageManager` pins, and it fails this step
|
|
288
|
+
# ("Multiple versions of pnpm specified") when a `version:` differs from
|
|
289
|
+
# that pin — `11` against `11.22.0` included. A package.json with no pin
|
|
290
|
+
# fails here too ("No pnpm version is specified"); the fix is the
|
|
291
|
+
# template's `packageManager` line, which `oxyc update` shows in its
|
|
292
|
+
# package.json diff, not a `version:` in this managed file.
|
|
284
293
|
- name: Install pnpm
|
|
285
294
|
if: steps.discover.outputs.count != '0'
|
|
286
|
-
uses: pnpm/action-setup@
|
|
287
|
-
with:
|
|
288
|
-
# package.json floors pnpm at >=10 and there is no `packageManager`
|
|
289
|
-
# field for the action to read, so the major goes here. 10 is not
|
|
290
|
-
# cosmetic: pnpm 9 reads no settings at all from
|
|
291
|
-
# pnpm-workspace.yaml, which silently makes its
|
|
292
|
-
# `onlyBuiltDependencies` block inert and blocks an app's
|
|
293
|
-
# esbuild/sharp postinstall with no diagnostic.
|
|
294
|
-
version: 10
|
|
295
|
+
uses: pnpm/action-setup@v6
|
|
295
296
|
|
|
296
297
|
- name: Install Node
|
|
297
298
|
if: steps.discover.outputs.count != '0'
|
|
298
299
|
uses: actions/setup-node@v4
|
|
299
300
|
with:
|
|
300
|
-
# .nvmrc is the single source of truth (
|
|
301
|
-
#
|
|
302
|
-
#
|
|
301
|
+
# .nvmrc is the single source of truth (26, what the customer-apps
|
|
302
|
+
# monorepo pins), so read the file rather than restating a number
|
|
303
|
+
# here. Whatever it says must be >= 22.13: that is the Node pnpm 11
|
|
304
|
+
# requires, and package.json's `engines.node` says so too.
|
|
303
305
|
node-version-file: .nvmrc
|
|
304
306
|
cache: pnpm
|
|
305
307
|
cache-dependency-path: pnpm-lock.yaml
|
package/template/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
26
|
package/template/.oxyc-managed
CHANGED
|
@@ -239,11 +239,11 @@ mixed package.json
|
|
|
239
239
|
# which is `mixed`, not a judgement call between the two.
|
|
240
240
|
mixed .gitignore
|
|
241
241
|
|
|
242
|
-
# Same shape, and the file says so itself: the `
|
|
243
|
-
#
|
|
244
|
-
#
|
|
245
|
-
# tool's; a fourth dependency
|
|
246
|
-
# customer's.
|
|
242
|
+
# Same shape, and the file says so itself: the `allowBuilds` map carries the
|
|
243
|
+
# three entries the shipped app bundles need, with an instruction to ADD to
|
|
244
|
+
# the map rather than replace it. The `packages:` globs and
|
|
245
|
+
# `confirmModulesPurge` are the tool's; a fourth dependency whose install
|
|
246
|
+
# script somebody decided on is the customer's.
|
|
247
247
|
mixed pnpm-workspace.yaml
|
|
248
248
|
|
|
249
249
|
# --- scope: which tree each file describes ----------------------------------
|
package/template/README.md
CHANGED
|
@@ -468,13 +468,24 @@ should.
|
|
|
468
468
|
|
|
469
469
|
## Node and pnpm
|
|
470
470
|
|
|
471
|
-
Node **
|
|
472
|
-
derived from.
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
471
|
+
Node **26** (`.nvmrc`), matching the customer-apps monorepo this build config
|
|
472
|
+
is derived from. Anything from **22.13** up works (`engines.node`): that is the
|
|
473
|
+
floor pnpm 11 itself requires, so a Node below it cannot run pnpm here at all.
|
|
474
|
+
Use **pnpm**, never npm or yarn — there is no `package-lock.json` here and
|
|
475
|
+
there should not be one.
|
|
476
|
+
|
|
477
|
+
pnpm is **pinned**, not just floored: `packageManager` in `package.json` names
|
|
478
|
+
pnpm 11.22.0, and that one line decides the pnpm everywhere. `corepack enable
|
|
479
|
+
pnpm` installs exactly it; a pnpm 10 already on your PATH switches itself to
|
|
480
|
+
it; CI's `pnpm/action-setup` installs it — which is why the workflow carries
|
|
481
|
+
no `version:` of its own: the action refuses to run with two. To move to
|
|
482
|
+
another pnpm, change that line (`corepack use pnpm@<version>`).
|
|
483
|
+
|
|
484
|
+
Dependencies' install scripts are approved in `pnpm-workspace.yaml`, in the
|
|
485
|
+
`allowBuilds` map. pnpm 11 **fails the install** (`ERR_PNPM_IGNORED_BUILDS`)
|
|
486
|
+
on a dependency with a build script the map does not decide, so the first
|
|
487
|
+
install after adding such a dependency stops until somebody sets it to `true`
|
|
488
|
+
(run the script) or `false` (skip it) there and commits that. A repo that
|
|
489
|
+
still carries the older `onlyBuiltDependencies` list gets that failure even
|
|
490
|
+
for the dependencies it lists: pnpm 11 removed the key and ignores it without
|
|
491
|
+
a word.
|
package/template/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "__SLUG__",
|
|
3
3
|
"private": true,
|
|
4
|
+
"packageManager": "pnpm@11.22.0+sha512.1ff870c4c6133dfd88fb2afc46dd13d47f09c9794b438c6fdb47ca98caf3bc16381ee0be93a091b8e3824cf01f889f46d7d9e20910fb0be1ab0fb5baa80dd621",
|
|
4
5
|
"engines": {
|
|
5
|
-
"node": ">=
|
|
6
|
-
"pnpm": ">=
|
|
6
|
+
"node": ">=22.13",
|
|
7
|
+
"pnpm": ">=11"
|
|
7
8
|
},
|
|
8
9
|
"scripts": {
|
|
9
10
|
"dev": "./scripts/dev.sh",
|
|
@@ -12,21 +12,32 @@ packages:
|
|
|
12
12
|
- "apps/*"
|
|
13
13
|
- "apps/*/*"
|
|
14
14
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# (
|
|
18
|
-
#
|
|
19
|
-
#
|
|
15
|
+
# Which dependencies may run their install/postinstall scripts. pnpm 11 runs
|
|
16
|
+
# none it has not been told about, and FAILS the install
|
|
17
|
+
# (`ERR_PNPM_IGNORED_BUILDS`) on any dependency with a build script this map
|
|
18
|
+
# does not decide. These three are what the real app bundles need: esbuild and
|
|
19
|
+
# unrs-resolver check for, or fetch, a platform binary, sharp builds one when
|
|
20
|
+
# no prebuilt fits. esbuild is in every app that has Oxy Functions — `oxyc
|
|
21
|
+
# publish` bundles them with `pnpm exec esbuild`. Add to the map rather than
|
|
22
|
+
# replacing it: a new dependency with a build script stops the install until
|
|
23
|
+
# somebody writes `true` (run it) or `false` (skip it) for it here.
|
|
20
24
|
#
|
|
21
|
-
# The key is `
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
# The key is `allowBuilds`. pnpm 11 removed `onlyBuiltDependencies`, the list
|
|
26
|
+
# this file used to carry, and ignores it without a word: with esbuild listed
|
|
27
|
+
# there, pnpm 11.22.0 still fails an app's install on `Ignored build scripts:
|
|
28
|
+
# esbuild@0.28.2`. pnpm 10.33 reads `allowBuilds` too, though it rarely gets
|
|
29
|
+
# the chance: package.json pins pnpm 11.22.0 (`packageManager`), and a pnpm 10
|
|
30
|
+
# switches itself to that version before installing anything.
|
|
31
|
+
allowBuilds:
|
|
32
|
+
esbuild: true
|
|
33
|
+
sharp: true
|
|
34
|
+
unrs-resolver: true
|
|
35
|
+
|
|
36
|
+
# pnpm 11 lays `node_modules` out for a new store version, so its first install
|
|
37
|
+
# over a tree pnpm 10 wrote has to delete the tree and recreate it — and it
|
|
38
|
+
# asks first. With no terminal to ask on — an agent or a script running
|
|
39
|
+
# `pnpm install`, or `oxyc publish`, which runs one — it aborts instead, with
|
|
40
|
+
# `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`. Nothing is lost by not asking:
|
|
41
|
+
# the tree is rebuilt from the lockfile. CI never hits it — a runner starts
|
|
42
|
+
# with no `node_modules`.
|
|
43
|
+
confirmModulesPurge: false
|