@revos/cli 0.2.1 → 0.2.3

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.
Files changed (162) hide show
  1. package/README.md +289 -77
  2. package/dist/adapters/oclif/commands/action-runs/get.mjs +1 -1
  3. package/dist/adapters/oclif/commands/action-runs/list.mjs +8 -2
  4. package/dist/adapters/oclif/commands/actions/get-input-schema.mjs +2 -2
  5. package/dist/adapters/oclif/commands/actions/get-params-schema.mjs +2 -2
  6. package/dist/adapters/oclif/commands/actions/get.mjs +1 -1
  7. package/dist/adapters/oclif/commands/actions/list.mjs +8 -4
  8. package/dist/adapters/oclif/commands/ai-instructions/create.mjs +1 -1
  9. package/dist/adapters/oclif/commands/ai-instructions/delete.mjs +1 -1
  10. package/dist/adapters/oclif/commands/ai-instructions/get.mjs +1 -1
  11. package/dist/adapters/oclif/commands/ai-instructions/list.mjs +8 -2
  12. package/dist/adapters/oclif/commands/ai-instructions/update.mjs +1 -1
  13. package/dist/adapters/oclif/commands/api.d.mts +11 -0
  14. package/dist/adapters/oclif/commands/api.mjs +112 -0
  15. package/dist/adapters/oclif/commands/apply.d.mts +29 -0
  16. package/dist/adapters/oclif/commands/apply.mjs +77 -0
  17. package/dist/adapters/oclif/commands/auth/login.d.mts +6 -4
  18. package/dist/adapters/oclif/commands/auth/login.mjs +23 -11
  19. package/dist/adapters/oclif/commands/auth/logout.d.mts +2 -1
  20. package/dist/adapters/oclif/commands/auth/logout.mjs +3 -2
  21. package/dist/adapters/oclif/commands/auth/status.d.mts +4 -2
  22. package/dist/adapters/oclif/commands/auth/status.mjs +23 -3
  23. package/dist/adapters/oclif/commands/connections/create.d.mts +6 -0
  24. package/dist/adapters/oclif/commands/connections/create.mjs +8 -0
  25. package/dist/adapters/oclif/commands/connections/delete.d.mts +6 -0
  26. package/dist/adapters/oclif/commands/connections/delete.mjs +8 -0
  27. package/dist/adapters/oclif/commands/connections/get.d.mts +6 -0
  28. package/dist/adapters/oclif/commands/connections/get.mjs +8 -0
  29. package/dist/adapters/oclif/commands/connections/list.d.mts +6 -0
  30. package/dist/adapters/oclif/commands/connections/list.mjs +14 -0
  31. package/dist/adapters/oclif/commands/connections/update.d.mts +6 -0
  32. package/dist/adapters/oclif/commands/connections/update.mjs +8 -0
  33. package/dist/adapters/oclif/commands/cubes/create.d.mts +6 -0
  34. package/dist/adapters/oclif/commands/cubes/create.mjs +8 -0
  35. package/dist/adapters/oclif/commands/cubes/delete.d.mts +6 -0
  36. package/dist/adapters/oclif/commands/cubes/delete.mjs +8 -0
  37. package/dist/adapters/oclif/commands/cubes/get.d.mts +6 -0
  38. package/dist/adapters/oclif/commands/cubes/get.mjs +8 -0
  39. package/dist/adapters/oclif/commands/cubes/list.d.mts +6 -0
  40. package/dist/adapters/oclif/commands/cubes/list.mjs +13 -0
  41. package/dist/adapters/oclif/commands/cubes/update.d.mts +6 -0
  42. package/dist/adapters/oclif/commands/cubes/update.mjs +8 -0
  43. package/dist/adapters/oclif/commands/diff.d.mts +28 -0
  44. package/dist/adapters/oclif/commands/diff.mjs +66 -0
  45. package/dist/adapters/oclif/commands/gservice-account-keys/get.mjs +1 -1
  46. package/dist/adapters/oclif/commands/gservice-account-keys/reveal.mjs +2 -2
  47. package/dist/adapters/oclif/commands/gservice-accounts/create.mjs +1 -1
  48. package/dist/adapters/oclif/commands/gservice-accounts/delete.mjs +1 -1
  49. package/dist/adapters/oclif/commands/gservice-accounts/get.mjs +1 -1
  50. package/dist/adapters/oclif/commands/gservice-accounts/list.mjs +7 -2
  51. package/dist/adapters/oclif/commands/init.d.mts +3 -1
  52. package/dist/adapters/oclif/commands/init.mjs +27 -23
  53. package/dist/adapters/oclif/commands/org/create.mjs +3 -2
  54. package/dist/adapters/oclif/commands/org/current.d.mts +12 -3
  55. package/dist/adapters/oclif/commands/org/current.mjs +27 -2
  56. package/dist/adapters/oclif/commands/org/get.mjs +3 -2
  57. package/dist/adapters/oclif/commands/org/list.d.mts +3 -11
  58. package/dist/adapters/oclif/commands/org/list.mjs +35 -26
  59. package/dist/adapters/oclif/commands/org/switch.d.mts +4 -2
  60. package/dist/adapters/oclif/commands/org/switch.mjs +16 -3
  61. package/dist/adapters/oclif/commands/pull.d.mts +29 -0
  62. package/dist/adapters/oclif/commands/pull.mjs +88 -0
  63. package/dist/adapters/oclif/commands/score-groups/create.mjs +3 -2
  64. package/dist/adapters/oclif/commands/score-groups/delete.mjs +1 -1
  65. package/dist/adapters/oclif/commands/score-groups/get.mjs +1 -1
  66. package/dist/adapters/oclif/commands/score-groups/list.mjs +3 -2
  67. package/dist/adapters/oclif/commands/score-groups/update.mjs +1 -1
  68. package/dist/adapters/oclif/commands/scores/create.mjs +3 -2
  69. package/dist/adapters/oclif/commands/scores/delete.mjs +1 -1
  70. package/dist/adapters/oclif/commands/scores/list.mjs +3 -2
  71. package/dist/adapters/oclif/commands/scores/update.mjs +1 -1
  72. package/dist/adapters/oclif/commands/segments/create.mjs +1 -1
  73. package/dist/adapters/oclif/commands/segments/delete.mjs +1 -1
  74. package/dist/adapters/oclif/commands/segments/evaluate.mjs +2 -2
  75. package/dist/adapters/oclif/commands/segments/get-evaluation-history.mjs +2 -2
  76. package/dist/adapters/oclif/commands/segments/get-version.mjs +2 -2
  77. package/dist/adapters/oclif/commands/segments/get.mjs +1 -1
  78. package/dist/adapters/oclif/commands/segments/list-versions.mjs +16 -5
  79. package/dist/adapters/oclif/commands/segments/list.mjs +9 -2
  80. package/dist/adapters/oclif/commands/segments/restore-version.mjs +2 -2
  81. package/dist/adapters/oclif/commands/segments/update.mjs +1 -1
  82. package/dist/adapters/oclif/commands/sources/create.d.mts +11 -0
  83. package/dist/adapters/oclif/commands/sources/create.mjs +16 -0
  84. package/dist/adapters/oclif/commands/sources/delete.d.mts +6 -0
  85. package/dist/adapters/oclif/commands/sources/delete.mjs +8 -0
  86. package/dist/adapters/oclif/commands/sources/get.d.mts +6 -0
  87. package/dist/adapters/oclif/commands/sources/get.mjs +8 -0
  88. package/dist/adapters/oclif/commands/sources/list-streams.d.mts +6 -0
  89. package/dist/adapters/oclif/commands/sources/list-streams.mjs +31 -0
  90. package/dist/adapters/oclif/commands/sources/list.d.mts +6 -0
  91. package/dist/adapters/oclif/commands/sources/list.mjs +13 -0
  92. package/dist/adapters/oclif/commands/{integrations/get.d.mts → sources/update.d.mts} +4 -4
  93. package/dist/adapters/oclif/commands/sources/update.mjs +21 -0
  94. package/dist/adapters/oclif/commands/status.d.mts +27 -0
  95. package/dist/adapters/oclif/commands/status.mjs +77 -0
  96. package/dist/adapters/oclif/commands/table-views/create.mjs +3 -2
  97. package/dist/adapters/oclif/commands/table-views/delete.mjs +1 -1
  98. package/dist/adapters/oclif/commands/table-views/list.mjs +3 -2
  99. package/dist/adapters/oclif/commands/table-views/update.mjs +1 -1
  100. package/dist/adapters/oclif/commands/tables/create.mjs +1 -1
  101. package/dist/adapters/oclif/commands/tables/delete.mjs +1 -1
  102. package/dist/adapters/oclif/commands/tables/get.mjs +1 -1
  103. package/dist/adapters/oclif/commands/tables/list.mjs +3 -2
  104. package/dist/adapters/oclif/commands/tables/update.mjs +1 -1
  105. package/dist/{base.command-d7VW6WTp.d.mts → base.command-BmddDbHa.d.mts} +4 -1
  106. package/dist/base.command-D8taHOFF.mjs +83 -0
  107. package/dist/chunk-CfYAbeIz.mjs +13 -0
  108. package/dist/context-D5uelKLe.d.mts +62 -0
  109. package/dist/core-B-IdeRNl.mjs +2448 -0
  110. package/dist/{factory-BrFKT8t-.mjs → factory-CCcimDhl.mjs} +45 -10
  111. package/dist/iac-render-BSZZEP0n.mjs +17 -0
  112. package/dist/index-D0ax2I61.d.mts +581 -0
  113. package/dist/index.d.mts +4 -4
  114. package/dist/index.mjs +2 -2
  115. package/dist/{presets-D9b6IWKy.mjs → presets-Bb9gwgeh.mjs} +40 -8
  116. package/dist/templates/.claude/settings.json +39 -0
  117. package/dist/templates/.devcontainer/Dockerfile +9 -0
  118. package/dist/templates/.devcontainer/devcontainer.json +4 -1
  119. package/dist/templates/.devcontainer/setup.sh +3 -0
  120. package/dist/templates/AGENTS.md +33 -20
  121. package/dist/templates/dbt/dbt_project.yml +2 -2
  122. package/dist/templates/gitignore +3 -1
  123. package/dist/templates/skills/create-connections/SKILL.md +210 -0
  124. package/dist/templates/skills/create-connections/references/mappers.md +152 -0
  125. package/dist/templates/skills/{create-semantic-model → create-cubes}/SKILL.md +20 -18
  126. package/dist/templates/skills/create-cubes/references/bq-pk-fk-conventions.md +183 -0
  127. package/dist/templates/skills/{create-semantic-model → create-cubes}/references/cube-examples.md +2 -2
  128. package/dist/templates/skills/create-cubes/references/hubspot-entities.md +289 -0
  129. package/dist/templates/skills/create-cubes/references/jira-entities.md +201 -0
  130. package/dist/templates/skills/create-cubes/references/netsuite-entities.md +121 -0
  131. package/dist/templates/skills/create-cubes/references/stripe-entities.md +114 -0
  132. package/dist/templates/skills/create-dbt-transformations/SKILL.md +43 -22
  133. package/dist/templates/skills/create-dbt-transformations/references/edge-cases.md +20 -2
  134. package/dist/templates/skills/create-dbt-transformations/references/schema-conventions.md +21 -7
  135. package/dist/templates/skills/create-dbt-transformations/references/sql-templates.md +34 -20
  136. package/dist/templates/skills/explore-lakehouse/SKILL.md +3 -3
  137. package/dist/templates/skills/load-sample-data/SKILL.md +1 -1
  138. package/dist/templates/skills/visualize-semantic-model/SKILL.md +159 -0
  139. package/dist/templates/skills/visualize-semantic-model/scripts/render_graph.py +186 -0
  140. package/dist/{types-Y_ht_ja5.d.mts → types-Bk2Cb5yt.d.mts} +9 -0
  141. package/package.json +44 -7
  142. package/dist/adapters/oclif/commands/integrations/create.d.mts +0 -11
  143. package/dist/adapters/oclif/commands/integrations/create.mjs +0 -16
  144. package/dist/adapters/oclif/commands/integrations/get.mjs +0 -21
  145. package/dist/adapters/oclif/commands/integrations/list.d.mts +0 -11
  146. package/dist/adapters/oclif/commands/integrations/list.mjs +0 -16
  147. package/dist/adapters/oclif/commands/integrations/update.d.mts +0 -15
  148. package/dist/adapters/oclif/commands/integrations/update.mjs +0 -21
  149. package/dist/adapters/oclif/commands/overlays/diff.d.mts +0 -19
  150. package/dist/adapters/oclif/commands/overlays/diff.mjs +0 -80
  151. package/dist/adapters/oclif/commands/overlays/pull.d.mts +0 -15
  152. package/dist/adapters/oclif/commands/overlays/pull.mjs +0 -45
  153. package/dist/adapters/oclif/commands/overlays/push.d.mts +0 -18
  154. package/dist/adapters/oclif/commands/overlays/push.mjs +0 -59
  155. package/dist/adapters/oclif/commands/overlays/status.d.mts +0 -18
  156. package/dist/adapters/oclif/commands/overlays/status.mjs +0 -53
  157. package/dist/base.command-YiwlGlKs.mjs +0 -62
  158. package/dist/core-jpFPylBb.mjs +0 -997
  159. package/dist/index-DD2Vr-pu.d.mts +0 -193
  160. package/dist/types-C_p_6rkj.d.mts +0 -69
  161. /package/dist/templates/skills/{create-semantic-model → create-cubes}/references/key-patterns.md +0 -0
  162. /package/dist/templates/skills/{create-semantic-model → create-cubes}/references/validation-queries.md +0 -0
package/README.md CHANGED
@@ -24,16 +24,28 @@ revos [command] [options]
24
24
  ## Global Options
25
25
 
26
26
  ```
27
- -o, --org <id> Override organization ID for this command
27
+ -o, --org <id> Override organization ID. Only accepted OUTSIDE a project —
28
+ inside a project, the org is anchored in revos.yaml and
29
+ --org is rejected.
28
30
  --json Format output as JSON
29
31
  ```
30
32
 
33
+ ## Project context and the org
34
+
35
+ The CLI walks up from the current directory looking for `revos.yaml` on every command. When it finds one:
36
+
37
+ - `revos apply / pull / diff / status` and every resource command (`connections`, `cubes`, `tables`, …) target `metadata.orgId` from `revos.yaml`. This is authoritative.
38
+ - `--org` and `REVOS_ORG_ID` are validated against `revos.yaml`. A mismatch is a hard error — this prevents a Dev Container generated for org A from silently writing to org B when you switch contexts.
39
+ - Your stored global default (`revos org switch`) is informational only inside a project. The CLI warns if it differs from the project's org but uses the project's org for actual API calls.
40
+
41
+ Outside a project, the CLI uses `REVOS_ORG_ID` (env), then `--org`, then your stored global default (`revos org switch`). `revos init`, `revos auth *`, and `revos org *` skip the strict in-project enforcement so you can still log in, list, or switch orgs from inside a project tree.
42
+
31
43
  ## Commands
32
44
 
33
45
  ### Project Initialization
34
46
 
35
47
  ```bash
36
- revos init [destination] [--yes] [--dry-run]
48
+ revos init [destination] [--yes] [--dry-run] [--no-pull]
37
49
  ```
38
50
 
39
51
  Scaffolds a new RevOS data engineering project. If `destination` is omitted, initializes in the current directory.
@@ -54,6 +66,7 @@ destination Path or project name (default: current directory)
54
66
  ```
55
67
  -y, --yes Skip confirmation prompts, use defaults
56
68
  --dry-run Show what would be created without creating files or GCP resources
69
+ --no-pull Skip discovering existing remote Connections and Cubes
57
70
  ```
58
71
 
59
72
  **Non-empty directory behavior:**
@@ -64,16 +77,211 @@ If the destination already exists and is not empty, you will be prompted to conf
64
77
 
65
78
  1. Fetches your organizations and prompts you to select one (auto-selects if only one).
66
79
  2. Provisions a GCP service account for your org (idempotent) and writes the key to `~/.revos/{project-name}-gsa-creds.json`.
67
- 3. Creates the project directory structure (medallion layout: bronze/silver/gold).
68
- 4. Generates `.devcontainer/devcontainer.json` with Python 3.11, Node.js, Google Cloud SDK (`bq`), and dbt pre-installed. The Dev Container mounts `~/.revos/{project-name}-gsa-creds.json` automatically.
69
- 5. Generates `dbt/profiles.yml` pre-configured for BigQuery.
70
- 6. Generates `.gitignore` and `README.md`.
71
- 7. Scaffolds AI companion files: `CLAUDE.md`, `AGENTS.md`, and `.claude/skills/` with three pre-installed skills: `explore-lakehouse`, `create-semantic-model`, and `create-dbt-transformations`.
80
+ 3. Writes `revos.yaml` — the project marker that pins this directory to your organization. CLI commands like `revos status` walk up from the current directory to find this file.
81
+ 4. Creates the project directory structure (medallion layout: bronze/silver/gold).
82
+ 5. Generates `.devcontainer/devcontainer.json` with Python 3.11, Node.js, Google Cloud SDK (`bq`), GitHub CLI (`gh`), and dbt pre-installed. The Dev Container mounts `~/.revos/{project-name}-gsa-creds.json` automatically.
83
+ 6. Generates `dbt/profiles.yml` pre-configured for BigQuery.
84
+ 7. Generates `.gitignore` and `README.md`.
85
+ 8. Scaffolds AI companion files: `CLAUDE.md`, `AGENTS.md`, and `.claude/skills/` with six pre-installed skills: `explore-lakehouse`, `create-connections`, `create-cubes`, `create-dbt-transformations`, `load-sample-data`, and `visualize-semantic-model`. Also writes `.claude/settings.json` with deny rules that block AI assistants from reading `~/.revos/` (CLI credentials and service account keys), `~/.config/gcloud/` (Google Cloud Application Default Credentials), from running `gcloud auth print-*-token` commands, and from editing the settings file itself (so the AI can't lift its own restrictions).
86
+ 9. Discovers every Connection and Cube currently in the org and writes them under `connections/` and `cubes/` so a developer joining an org with existing pipelines and cubes starts from the live state. Pass `--no-pull` to skip. Sources are managed only through the API surface (see [Sources](#sources)) and are referenced from Connection YAML by their server id.
72
87
 
73
88
  **Requires:** `revos auth login` first.
74
89
 
75
90
  ---
76
91
 
92
+ ### Apply local resources
93
+
94
+ ```bash
95
+ revos apply [path] [--project <path>] [--dry-run] [--parallelism <n>] [--json]
96
+ ```
97
+
98
+ Reconciles local revos resources with the API:
99
+
100
+ - Resources without `metadata.id` are created (POST) and the YAML file is rewritten in place to record the returned id.
101
+ - Resources with `metadata.id` are read from the API and compared field by field; if the local spec drifts, the resource is updated (PATCH).
102
+ - Resources that already match the API are reported as `unchanged`.
103
+
104
+ Resources are applied in dependency order when one local resource references another. Within a level, up to `--parallelism` resources are processed concurrently (default 4). Unresolved references and cycles fail before any API call.
105
+
106
+ Two `kind`s are supported today: `Connection` (a sync pipeline from a Source into your org's data warehouse) and `Cube` (a Cube.dev semantic model definition). Connections reference a Source by its server id under `spec.source.id`. Get the id from `revos sources list --json`:
107
+
108
+ ```yaml
109
+ apiVersion: revos/v1
110
+ kind: Connection
111
+ metadata:
112
+ name: analytics-pipeline
113
+ spec:
114
+ name: "Analytics pipeline"
115
+ source: { id: src_abc123 } # from `revos sources list`
116
+ schedule: { units: 24, timeUnit: hours }
117
+ status: active
118
+ prefix: analytics_ # optional; auto-generated from the source if omitted
119
+ streams:
120
+ - name: users
121
+ namespace: public
122
+ syncMode: incremental_deduped_history
123
+ cursorField: [updated_at]
124
+ primaryKey: [[id]]
125
+ - name: orders
126
+ namespace: public
127
+ syncMode: full_refresh_overwrite
128
+ ```
129
+
130
+ Sources themselves are not IaC — create them via `revos sources create` (opens the UI) and pull the id with `revos sources list`.
131
+
132
+ `spec.prefix` is prepended to every destination table name. It must match `^[a-z0-9_]*$` (lowercase letters, digits, and underscores) and be at most 63 characters. If omitted on first apply, the API auto-generates a prefix from the source (e.g. `postgres_`) and `revos apply` writes it back into your YAML so subsequent diffs are clean. Edit `spec.prefix` and run `revos apply` to rename it on an existing connection.
133
+
134
+ #### Data masking on a Connection
135
+
136
+ Each stream may carry a `mappers:` array — server-side transformations applied before rows land in BigQuery. Use them to hash PII, drop columns, rename fields, or filter rows out of the sync. Five `type`s are supported: `hashing`, `field-renaming`, `field-filtering`, `row-filtering`, and `encryption`. The kind-specific configuration always lives under `mapperConfiguration`:
137
+
138
+ ```yaml
139
+ streams:
140
+ - name: customers
141
+ namespace: public
142
+ syncMode: incremental_deduped_history
143
+ cursorField: [updated_at]
144
+ primaryKey: [[id]]
145
+ mappers:
146
+ # Hash PII so analysts get joinable but irreversible identifiers.
147
+ - type: hashing
148
+ mapperConfiguration:
149
+ targetField: email
150
+ method: SHA-256
151
+ fieldNameSuffix: _hashed
152
+ # Rename a column on the way in.
153
+ - type: field-renaming
154
+ mapperConfiguration:
155
+ originalFieldName: ssn
156
+ newFieldName: ssn_redacted
157
+ # Drop a column entirely.
158
+ - type: field-filtering
159
+ mapperConfiguration:
160
+ targetField: internal_notes
161
+ # Keep rows where the predicate is true; the rest are dropped.
162
+ # NOT(is_test == "true") is true for every non-test row, so test
163
+ # rows get dropped. (Compare against a real sentinel value — empty
164
+ # strings behave unreliably.)
165
+ - type: row-filtering
166
+ mapperConfiguration:
167
+ conditions:
168
+ type: NOT
169
+ conditions:
170
+ - type: EQUAL
171
+ fieldName: is_test
172
+ comparisonValue: "true"
173
+ # Reversible encryption (RSA or AES). Keys are required.
174
+ - type: encryption
175
+ mapperConfiguration:
176
+ algorithm: AES
177
+ targetField: card_number
178
+ fieldNameSuffix: _enc
179
+ key: ${env.AES_KEY}
180
+ mode: GCM
181
+ padding: NoPadding
182
+ ```
183
+
184
+ Hashing methods: `MD2`, `MD5`, `SHA-1`, `SHA-224`, `SHA-256`, `SHA-384`, `SHA-512`. AES modes: `CBC`, `CFB`, `OFB`, `CTR`, `GCM`, `ECB`. AES paddings: `NoPadding`, `PKCS5Padding`. Masking is enforced at ingest — even a direct BigQuery query sees only the masked values.
185
+
186
+ A `Cube` carries the full Cube.dev semantic model under `spec.definition`. The cube file's `metadata.name` is the IaC slug; `spec.name` is the Cube.dev cube name (also used inside `${CUBE}` and join references). The CLI mirrors `spec.name` into `spec.definition.name` automatically:
187
+
188
+ ```yaml
189
+ apiVersion: revos/v1
190
+ kind: Cube
191
+ metadata:
192
+ name: revenue-metrics
193
+ spec:
194
+ name: revenue_metrics
195
+ definition:
196
+ sql_table: "`my_project.my_dataset.gold_revenue`"
197
+ dimensions:
198
+ id:
199
+ sql: "${CUBE}.id"
200
+ type: string
201
+ primary_key: true
202
+ measures:
203
+ count:
204
+ type: count
205
+ ```
206
+
207
+ #### Migrating from legacy auto-generated cubes
208
+
209
+ If your org previously relied on auto-generated cube definitions, you can migrate using Claude Code (or any AI assistant with `revos api` access):
210
+
211
+ > "Fetch `GET /cubes/preview` via `revos api`, convert each cube into an IaC YAML file under `cubes/`, then run `revos apply`."
212
+
213
+ The AI will call the endpoint, transform the JSON response into the correct `apiVersion: revos/v1 / kind: Cube` YAML format, write the files, and push them.
214
+
215
+ Once `revos apply` succeeds, the org switches to reading cubes directly from the database — auto-generation is bypassed.
216
+
217
+ ---
218
+
219
+ `--dry-run` reports what would change without writing to the API or YAML files. It still reads remote state to detect drift, so authentication is required.
220
+
221
+ The CLI walks up from the current working directory to find `revos.yaml`. Resources are addressed by `kind/metadata.name`; filenames are storage only.
222
+
223
+ > Updates are last-writer-wins. Run `revos diff` first if you want to inspect drift before reconciling.
224
+
225
+ ---
226
+
227
+ ### Diff local vs API
228
+
229
+ ```bash
230
+ revos diff [path] [--project <path>] [--parallelism <n>] [--json]
231
+ ```
232
+
233
+ Shows the drift between local YAML and the API without making any changes. For each resource that would be created or updated, prints the changed fields:
234
+
235
+ ```
236
+ update Connection/analytics-pipeline
237
+ ~ schedule.units: 24 → 6
238
+ + prefix: analytics_
239
+ ```
240
+
241
+ Sensitive fields (passwords, tokens, API keys, client secrets) are redacted as `(sensitive value)` so secrets never reach stdout. The same diff data is available via `--json`.
242
+
243
+ ---
244
+
245
+ ### Pull from API
246
+
247
+ ```bash
248
+ revos pull [path] [--project <path>] [--dry-run] [--force] [--json]
249
+ ```
250
+
251
+ The reverse of `apply`. For each registered kind, lists every remote resource:
252
+
253
+ - **Known resources** (matched against local YAML by `metadata.id`) get their `spec:` block rewritten in place when drifted, or marked `unchanged` when the live state already matches local. Sibling documents in multi-doc files, comments, and `metadata` are preserved byte-for-byte; only `spec:` is rewritten.
254
+ - **Unknown resources** are _discovered_ — a fresh YAML file is written under `<kind-lowercase>s/<slug>.yaml`. The slug comes from the server-side `name` field, kebab-cased; collisions against existing local addresses dedupe with `-2`, `-3`, etc.
255
+ - **Local resources whose `metadata.id` doesn't exist on the server** are reported as `skipped (not found)`.
256
+ - **Local resources without `metadata.id`** are reported as `skipped (no id)` — they don't yet exist remotely; run `revos apply` to create them.
257
+
258
+ Discovered Connections carry the server source id in `spec.source.id`, so the YAML you get out is immediately apply-able.
259
+
260
+ Sensitive fields (passwords, secrets, tokens) are dropped from discovered resources, since the server returns redacted placeholders that would corrupt the upstream service on a subsequent apply. For _existing_ resources, the local file's value wins on pull — `password: ${env.PG_PASSWORD}` survives a round-trip without being baked in as the resolved value.
261
+
262
+ Refuses to overwrite local files with uncommitted git changes; pass `--force` to override. Newly-discovered files are always written (they can't conflict with anything on disk). `--dry-run` reports what would be pulled or discovered without writing.
263
+
264
+ ---
265
+
266
+ ### Project Status
267
+
268
+ ```bash
269
+ revos status [path] [--project <path>] [--columns address,state,id,source] [--json]
270
+ ```
271
+
272
+ Lists local revos resources (Connections, Cubes) discovered under the project root, with their state.
273
+
274
+ States:
275
+
276
+ - `pending` — defined locally, not yet applied to the API
277
+ - `ok` — applied and synced to the API
278
+ - `tampered` — `metadata.id` looks malformed; recover by running `revos pull <kind>/<name>` (lands in a follow-up phase)
279
+ - `drifted` — local and remote diverge; run `revos diff` to inspect or `revos apply` to reconcile
280
+
281
+ The CLI walks up from the current working directory to find `revos.yaml` (or set `--project <path>` / `REVOS_PROJECT` to override). Resources are addressed by `kind/metadata.name`; filenames are storage only.
282
+
283
+ ---
284
+
77
285
  ### Authentication
78
286
 
79
287
  #### Login
@@ -101,7 +309,7 @@ revos auth status [--json]
101
309
  #### List organizations
102
310
 
103
311
  ```bash
104
- revos org list [--columns name,id] [--json]
312
+ revos org list [--columns id,name] [--json]
105
313
  ```
106
314
 
107
315
  #### Show current organization
@@ -117,99 +325,61 @@ revos org current [--json]
117
325
  revos org switch
118
326
 
119
327
  # Direct by ID
120
- revos org switch <org-id>
328
+ revos org switch <org-id> [--json]
121
329
  ```
122
330
 
331
+ `--json` requires an explicit `<org-id>` (interactive selection needs a TTY).
332
+
123
333
  ---
124
334
 
125
- ### Integrations
335
+ ### Sources
126
336
 
127
- Browser-opener commands that deep-link to the RevOS UI for managing data source integrations.
337
+ Manage data sources. `list`, `get`, and `delete` hit the API directly. `create` and `update` open the RevOS UI, since source configuration (connector picker, dynamic config schemas, OAuth flows) is not practical to drive from a CLI.
128
338
 
129
- #### List integrations
339
+ #### List sources
130
340
 
131
341
  ```bash
132
- revos integrations list
342
+ revos sources list [--json]
133
343
  ```
134
344
 
135
- Opens the integrations page in the RevOS UI.
136
-
137
- #### Get integration
345
+ #### Get source
138
346
 
139
347
  ```bash
140
- revos integrations get <id>
348
+ revos sources get <id> [--json]
141
349
  ```
142
350
 
143
- Opens a specific integration in the RevOS UI.
144
-
145
- #### Create integration
351
+ #### List streams the source exposes
146
352
 
147
353
  ```bash
148
- revos integrations create
354
+ revos sources list-streams <id> [--ignore-cache] [--columns a,b,c] [--json]
149
355
  ```
150
356
 
151
- Opens the RevOS UI to add a new data source.
152
-
153
- #### Update integration
357
+ Discovers the streams (tables, endpoints, etc.) the source advertises and the per-stream metadata you need to fill in a `Connection`: supported sync modes, default cursor field, source-defined primary key, and the list of fields available for selection. Pass `--ignore-cache` to bypass the cached catalog and re-discover.
154
358
 
155
359
  ```bash
156
- revos integrations update <id>
157
- ```
158
-
159
- Opens the RevOS UI to edit an existing integration.
160
-
161
- ---
162
-
163
- ### Overlays Management
164
-
165
- Overlay files are Cube.dev semantic model definitions stored as YAML in `semantic/`. The overlay name is taken from the `name` field inside the file (or derived from the filename as a fallback). A `description` field is synced with the overlay's description on push.
166
-
167
- Example `semantic/my_table.yml`:
168
-
169
- ```yaml
170
- name: my_table
171
- description: My custom overlay
172
- sql_table: my_table
173
- dimensions:
174
- id:
175
- sql: "${CUBE}.id"
176
- type: number
177
- primary_key: true
178
- measures:
179
- count:
180
- type: count
360
+ revos sources list-streams src_abc123 --json | jq '.[] | {streamName, syncModes, defaultCursorField}'
181
361
  ```
182
362
 
183
- #### Push overlays to API
363
+ #### Create source
184
364
 
185
365
  ```bash
186
- revos overlays push [files...] [-d <dir>] [-f] [--json]
187
-
188
- Options:
189
- -d, --dir <path> Directory containing overlay files (default: "./semantic")
190
- -f, --force Force push even if remote is newer
366
+ revos sources create
191
367
  ```
192
368
 
193
- #### Pull overlays from API
194
-
195
- ```bash
196
- revos overlays pull [-d <dir>] [-n <name>...] [--json]
197
-
198
- Options:
199
- -d, --dir <path> Directory to save overlay files (default: "./semantic")
200
- -n, --name <name> Specific overlay names to pull (repeatable)
201
- ```
369
+ Opens the RevOS UI to add a new data source.
202
370
 
203
- #### Show diff between local and remote
371
+ #### Update source
204
372
 
205
373
  ```bash
206
- revos overlays diff [files...] [-d <dir>] [--json]
374
+ revos sources update <id>
207
375
  ```
208
376
 
209
- #### Check overlay status
377
+ Opens the RevOS UI to edit an existing source.
378
+
379
+ #### Delete source
210
380
 
211
381
  ```bash
212
- revos overlays status [files...] [-d <dir>] [--columns name,status] [--json]
382
+ revos sources delete <id>
213
383
  ```
214
384
 
215
385
  ---
@@ -224,6 +394,8 @@ The CLI exposes every method of `@revos/api-client` as a topic-prefixed command.
224
394
  | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
225
395
  | `tables` | `list`, `get`, `create`, `update`, `delete` |
226
396
  | `table-views` | `list`, `create`, `update`, `delete` |
397
+ | `cubes` | `list`, `get`, `create`, `update`, `delete` |
398
+ | `connections` | `list`, `get`, `create`, `update`, `delete` |
227
399
  | `scores` | `list`, `create`, `update`, `delete` |
228
400
  | `score-groups` | `list`, `get`, `create`, `update`, `delete` |
229
401
  | `gservice-accounts` | `list`, `get`, `create`, `delete` |
@@ -236,12 +408,20 @@ The CLI exposes every method of `@revos/api-client` as a topic-prefixed command.
236
408
 
237
409
  #### List flags
238
410
 
239
- `list` commands accept `--page-size`, `--page-token`, `--order-by`, `--filter`, and `--fields`. `actions list` adds `--only-used`.
411
+ `list` commands accept `--page-size`, `--page-token`, `--order-by`, `--filter`, `--fields`, and `--columns`. `actions list` adds `--only-used`.
240
412
 
241
413
  ```bash
242
414
  revos tables list --page-size 50 --order-by 'createdAt desc'
243
415
  ```
244
416
 
417
+ Each `list` command ships a curated default column set; use `--columns` to override (comma-separated). Pass `--json` for the full payload.
418
+
419
+ ```bash
420
+ revos tables list # default columns
421
+ revos tables list --columns id,name # only id and name
422
+ revos tables list --json # full SDK response
423
+ ```
424
+
245
425
  #### Request bodies (`--body`)
246
426
 
247
427
  `create` and `update` take a single `--body` flag. Three forms:
@@ -273,6 +453,37 @@ revos gservice-account-keys reveal key_abc
273
453
 
274
454
  ---
275
455
 
456
+ ### Raw API access (`revos api`)
457
+
458
+ Call any RevOS REST endpoint directly, reusing your stored auth, API base URL, and organization context. Useful for scripts, debugging, and endpoints not yet covered by typed resource commands.
459
+
460
+ ```bash
461
+ revos api <path> [-X <method>] [-H 'Name: value']... [-q 'key=value']... [--body JSON|@file|-] [--json]
462
+ ```
463
+
464
+ | Flag | Meaning |
465
+ | -------------- | ------------------------------------------------------------------------- |
466
+ | `-X, --method` | HTTP method (default `GET`; case-insensitive). |
467
+ | `-H, --header` | Extra request header (`'Name: value'`). Repeatable. |
468
+ | `-q, --query` | Query parameter (`'key=value'`). Repeatable; repeated keys become arrays. |
469
+ | `--body` | Request body — inline JSON, `@file`, or `-` (stdin). |
470
+ | `--json` | No-op for content (output is already JSON); changes error formatting. |
471
+
472
+ Output is the pretty-printed response body, including the `{ data, metadata }` envelope that list endpoints use — so `metadata.nextPageToken` stays visible for scripted pagination.
473
+
474
+ ```bash
475
+ revos api /organizations
476
+ revos api /tables -q pageSize=50 -q 'orderBy=createdAt desc'
477
+ revos api /connections -X POST --body '{"name":"my-conn"}'
478
+ revos api /connections -X POST --body @body.json
479
+ revos api /segments/seg_123 -X DELETE
480
+ revos api /organizations -H 'X-Foo: bar'
481
+ ```
482
+
483
+ Errors follow the same conventions as other commands: 401 prompts you to run `revos auth login`, 404 reports the missing URL, 5xx surfaces the server error.
484
+
485
+ ---
486
+
276
487
  ## Output Formats
277
488
 
278
489
  Every command supports `--json` for machine-readable output:
@@ -280,22 +491,23 @@ Every command supports `--json` for machine-readable output:
280
491
  ```bash
281
492
  revos org list --json
282
493
  revos auth status --json
283
- revos overlays status --json
494
+ revos status --json
284
495
  ```
285
496
 
286
- List commands support `--columns` to limit displayed columns:
497
+ List commands ship a curated default column set, and accept `--columns` to override:
287
498
 
288
499
  ```bash
500
+ revos tables list --columns id,name
289
501
  revos org list --columns name
290
- revos overlays status --columns name,status
502
+ revos status --columns address,state,id
291
503
  ```
292
504
 
293
505
  ---
294
506
 
295
507
  ## Configuration
296
508
 
297
- | Variable | Description |
298
- | --------------- | -------------------------------------------------------- |
299
- | `REVOS_TOKEN` | Authentication token (alternative to `revos auth login`) |
300
- | `REVOS_API_URL` | API endpoint (default: `https://api.revos.ai`) |
301
- | `REVOS_ORG_ID` | Organization ID override |
509
+ | Variable | Description |
510
+ | --------------- | ------------------------------------------------------------------------------------------------------------------ |
511
+ | `REVOS_TOKEN` | Authentication token (alternative to `revos auth login`) |
512
+ | `REVOS_API_URL` | API endpoint (default: `https://api.revos.ai`) |
513
+ | `REVOS_ORG_ID` | Organization ID override. Inside a project, must match `metadata.orgId` in `revos.yaml`; mismatch is a hard error. |
@@ -1,4 +1,4 @@
1
- import { r as getCommand } from "../../../../presets-D9b6IWKy.mjs";
1
+ import { r as getCommand } from "../../../../presets-Bb9gwgeh.mjs";
2
2
  //#region src/adapters/oclif/commands/action-runs/get.ts
3
3
  var get_default = getCommand({
4
4
  resource: "actionRuns",
@@ -1,8 +1,14 @@
1
- import { i as listCommand } from "../../../../presets-D9b6IWKy.mjs";
1
+ import { i as listCommand } from "../../../../presets-Bb9gwgeh.mjs";
2
2
  //#region src/adapters/oclif/commands/action-runs/list.ts
3
3
  var list_default = listCommand({
4
4
  resource: "actionRuns",
5
- description: "List action runs"
5
+ description: "List action runs",
6
+ defaultColumns: [
7
+ "id",
8
+ "actionId",
9
+ "status",
10
+ "createdAt"
11
+ ]
6
12
  });
7
13
  //#endregion
8
14
  export { list_default as default };
@@ -1,5 +1,5 @@
1
- import { m as unwrap } from "../../../../core-jpFPylBb.mjs";
2
- import { n as defineApiCommand } from "../../../../factory-BrFKT8t-.mjs";
1
+ import { x as unwrap } from "../../../../core-B-IdeRNl.mjs";
2
+ import { r as defineApiCommand } from "../../../../factory-CCcimDhl.mjs";
3
3
  import { Args, Flags } from "@oclif/core";
4
4
  //#region src/adapters/oclif/commands/actions/get-input-schema.ts
5
5
  var get_input_schema_default = defineApiCommand({
@@ -1,5 +1,5 @@
1
- import { m as unwrap } from "../../../../core-jpFPylBb.mjs";
2
- import { n as defineApiCommand } from "../../../../factory-BrFKT8t-.mjs";
1
+ import { x as unwrap } from "../../../../core-B-IdeRNl.mjs";
2
+ import { r as defineApiCommand } from "../../../../factory-CCcimDhl.mjs";
3
3
  import { Args, Flags } from "@oclif/core";
4
4
  //#region src/adapters/oclif/commands/actions/get-params-schema.ts
5
5
  var get_params_schema_default = defineApiCommand({
@@ -1,4 +1,4 @@
1
- import { r as getCommand } from "../../../../presets-D9b6IWKy.mjs";
1
+ import { r as getCommand } from "../../../../presets-Bb9gwgeh.mjs";
2
2
  //#region src/adapters/oclif/commands/actions/get.ts
3
3
  var get_default = getCommand({
4
4
  resource: "actions",
@@ -1,7 +1,6 @@
1
- import { m as unwrap } from "../../../../core-jpFPylBb.mjs";
2
- import { n as defineApiCommand } from "../../../../factory-BrFKT8t-.mjs";
1
+ import { x as unwrap } from "../../../../core-B-IdeRNl.mjs";
2
+ import { n as createListRender, r as defineApiCommand } from "../../../../factory-CCcimDhl.mjs";
3
3
  import { Flags } from "@oclif/core";
4
- //#region src/adapters/oclif/commands/actions/list.ts
5
4
  var list_default = defineApiCommand({
6
5
  description: "List available actions",
7
6
  flags: {
@@ -10,6 +9,10 @@ var list_default = defineApiCommand({
10
9
  "order-by": Flags.string({ description: "Field to order results by" }),
11
10
  filter: Flags.string({ description: "Filter expression" }),
12
11
  fields: Flags.string({ description: "Comma-separated fields to include" }),
12
+ columns: Flags.string({
13
+ description: "Columns to display in table output (comma-separated). Overrides the resource default. Ignored with --json.",
14
+ helpValue: "a,b,c"
15
+ }),
13
16
  "only-used": Flags.string({ description: "If set, only returns actions used by the organization" })
14
17
  },
15
18
  call: async ({ api, flags }) => {
@@ -21,7 +24,8 @@ var list_default = defineApiCommand({
21
24
  if (flags.fields !== void 0) params.fields = flags.fields;
22
25
  if (flags["only-used"] !== void 0) params.onlyUsed = flags["only-used"];
23
26
  return unwrap(await api.actions.list(params));
24
- }
27
+ },
28
+ render: createListRender(["id", "name"])
25
29
  });
26
30
  //#endregion
27
31
  export { list_default as default };
@@ -1,4 +1,4 @@
1
- import { t as createCommand } from "../../../../presets-D9b6IWKy.mjs";
1
+ import { t as createCommand } from "../../../../presets-Bb9gwgeh.mjs";
2
2
  //#region src/adapters/oclif/commands/ai-instructions/create.ts
3
3
  var create_default = createCommand({
4
4
  resource: "aiInstructions",
@@ -1,4 +1,4 @@
1
- import { n as deleteCommand } from "../../../../presets-D9b6IWKy.mjs";
1
+ import { n as deleteCommand } from "../../../../presets-Bb9gwgeh.mjs";
2
2
  //#region src/adapters/oclif/commands/ai-instructions/delete.ts
3
3
  var delete_default = deleteCommand({
4
4
  resource: "aiInstructions",
@@ -1,4 +1,4 @@
1
- import { r as getCommand } from "../../../../presets-D9b6IWKy.mjs";
1
+ import { r as getCommand } from "../../../../presets-Bb9gwgeh.mjs";
2
2
  //#region src/adapters/oclif/commands/ai-instructions/get.ts
3
3
  var get_default = getCommand({
4
4
  resource: "aiInstructions",
@@ -1,8 +1,14 @@
1
- import { i as listCommand } from "../../../../presets-D9b6IWKy.mjs";
1
+ import { i as listCommand } from "../../../../presets-Bb9gwgeh.mjs";
2
2
  //#region src/adapters/oclif/commands/ai-instructions/list.ts
3
3
  var list_default = listCommand({
4
4
  resource: "aiInstructions",
5
- description: "List AI instructions"
5
+ description: "List AI instructions",
6
+ defaultColumns: [
7
+ "id",
8
+ "integrationName",
9
+ "actionId",
10
+ "updatedAt"
11
+ ]
6
12
  });
7
13
  //#endregion
8
14
  export { list_default as default };
@@ -1,4 +1,4 @@
1
- import { a as updateCommand } from "../../../../presets-D9b6IWKy.mjs";
1
+ import { a as updateCommand } from "../../../../presets-Bb9gwgeh.mjs";
2
2
  //#region src/adapters/oclif/commands/ai-instructions/update.ts
3
3
  var update_default = updateCommand({
4
4
  resource: "aiInstructions",
@@ -0,0 +1,11 @@
1
+ import * as _$_oclif_core0 from "@oclif/core";
2
+
3
+ //#region src/adapters/oclif/commands/api.d.ts
4
+ declare const ALLOWED_METHODS: readonly ["GET", "POST", "PATCH", "PUT", "DELETE"];
5
+ type Method = (typeof ALLOWED_METHODS)[number];
6
+ declare function normalizeMethod(input: string | undefined): Method;
7
+ declare function parseHeaders(values: string[] | undefined): Record<string, string>;
8
+ declare function parseQuery(values: string[] | undefined): Record<string, unknown>;
9
+ declare const _default: typeof _$_oclif_core0.Command;
10
+ //#endregion
11
+ export { ALLOWED_METHODS, Method, _default as default, normalizeMethod, parseHeaders, parseQuery };