@revoengine/cli 1.0.9 → 1.0.11

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 (81) hide show
  1. package/README.md +464 -8
  2. package/dist/src/cli.js +65 -6
  3. package/dist/src/client.d.ts +366 -5
  4. package/dist/src/client.js +954 -13
  5. package/dist/src/commands/auth.js +4 -2
  6. package/dist/src/commands/component.js +839 -412
  7. package/dist/src/commands/database-schemas.d.ts +2 -0
  8. package/dist/src/commands/database-schemas.js +188 -0
  9. package/dist/src/commands/database-views.d.ts +2 -0
  10. package/dist/src/commands/database-views.js +123 -0
  11. package/dist/src/commands/endpoints.js +114 -0
  12. package/dist/src/commands/env.d.ts +2 -0
  13. package/dist/src/commands/env.js +380 -0
  14. package/dist/src/commands/events.d.ts +2 -0
  15. package/dist/src/commands/events.js +146 -0
  16. package/dist/src/commands/groups.d.ts +2 -0
  17. package/dist/src/commands/groups.js +169 -0
  18. package/dist/src/commands/index.d.ts +10 -0
  19. package/dist/src/commands/index.js +10 -0
  20. package/dist/src/commands/job-templates.d.ts +2 -0
  21. package/dist/src/commands/job-templates.js +101 -0
  22. package/dist/src/commands/metadata.d.ts +2 -0
  23. package/dist/src/commands/metadata.js +159 -0
  24. package/dist/src/commands/project.js +82 -1
  25. package/dist/src/commands/role-groups.d.ts +2 -0
  26. package/dist/src/commands/role-groups.js +152 -0
  27. package/dist/src/commands/schedules.d.ts +2 -0
  28. package/dist/src/commands/schedules.js +141 -0
  29. package/dist/src/commands/terminal-service.d.ts +38 -0
  30. package/dist/src/commands/terminal-service.js +210 -0
  31. package/dist/src/commands/terminal.d.ts +22 -0
  32. package/dist/src/commands/terminal.js +511 -0
  33. package/dist/src/component-lock.d.ts +126 -2
  34. package/dist/src/component-lock.js +378 -15
  35. package/dist/src/config.d.ts +20 -0
  36. package/dist/src/config.js +121 -6
  37. package/dist/src/database-schema-artifacts.d.ts +7 -0
  38. package/dist/src/database-schema-artifacts.js +8 -0
  39. package/dist/src/env-sync.d.ts +83 -0
  40. package/dist/src/env-sync.js +315 -0
  41. package/dist/src/metadata-backfill.d.ts +56 -0
  42. package/dist/src/metadata-backfill.js +1176 -0
  43. package/dist/src/project.d.ts +17 -9
  44. package/dist/src/project.js +87 -11
  45. package/dist/src/prompt.js +10 -18
  46. package/dist/src/resource-metadata.d.ts +25 -0
  47. package/dist/src/resource-metadata.js +132 -0
  48. package/dist/src/resource-syncs/database-schema-sync.d.ts +117 -0
  49. package/dist/src/resource-syncs/database-schema-sync.js +2289 -0
  50. package/dist/src/resource-syncs/database-view-sync.d.ts +124 -0
  51. package/dist/src/resource-syncs/database-view-sync.js +1317 -0
  52. package/dist/src/resource-syncs/endpoint-sync.d.ts +96 -0
  53. package/dist/src/resource-syncs/endpoint-sync.js +1283 -0
  54. package/dist/src/resource-syncs/event-sync.d.ts +99 -0
  55. package/dist/src/resource-syncs/event-sync.js +949 -0
  56. package/dist/src/resource-syncs/group-sync.d.ts +86 -0
  57. package/dist/src/resource-syncs/group-sync.js +882 -0
  58. package/dist/src/resource-syncs/job-template-sync.d.ts +85 -0
  59. package/dist/src/resource-syncs/job-template-sync.js +782 -0
  60. package/dist/src/resource-syncs/role-group-sync.d.ts +83 -0
  61. package/dist/src/resource-syncs/role-group-sync.js +597 -0
  62. package/dist/src/resource-syncs/schedule-sync.d.ts +111 -0
  63. package/dist/src/resource-syncs/schedule-sync.js +1302 -0
  64. package/dist/src/resource-syncs/util.d.ts +19 -0
  65. package/dist/src/resource-syncs/util.js +116 -0
  66. package/dist/src/runtime-view.d.ts +1 -0
  67. package/dist/src/runtime-view.js +6 -1
  68. package/dist/src/sync-output.d.ts +38 -0
  69. package/dist/src/sync-output.js +131 -0
  70. package/dist/src/tracked-resources.d.ts +7 -0
  71. package/dist/src/tracked-resources.js +61 -0
  72. package/dist/src/types.d.ts +227 -0
  73. package/dist/src/ui.d.ts +3 -0
  74. package/dist/src/ui.js +68 -10
  75. package/dist/src/utils.d.ts +2 -0
  76. package/dist/src/utils.js +64 -0
  77. package/dist/src/workspace-component.d.ts +2 -0
  78. package/dist/src/workspace-component.js +34 -0
  79. package/dist/src/workspace-resource.d.ts +2 -0
  80. package/dist/src/workspace-resource.js +52 -0
  81. package/package.json +8 -3
package/README.md CHANGED
@@ -21,13 +21,21 @@ Inspect the local runtime and config path:
21
21
  revo -i
22
22
  ```
23
23
 
24
- Log in and save your default credentials:
24
+ Add a project connection. The new project becomes active immediately:
25
+
26
+ ```bash
27
+ revo projects add local --url http://localhost:3000
28
+ revo projects list
29
+ revo auth status
30
+ ```
31
+
32
+ To use the original `default` connection instead, save its credentials:
25
33
 
26
34
  ```bash
27
35
  revo auth login
28
36
  ```
29
37
 
30
- `revo auth login` opens an interactive terminal prompt for your API key. If you are already logged in, the CLI warns and asks you to `revo auth logout` first.
38
+ `revo auth login` opens a masked interactive terminal prompt for your API key and saves the `default` login. If you are already logged in, the CLI warns and asks you to `revo auth logout` first. For named connections, use `revo projects add <name>`.
31
39
 
32
40
  The CLI validates the API key against `/api/v1/me` and infers the RevoEngine instance from the authenticated profile for tenant-scoped operations such as component push.
33
41
 
@@ -37,6 +45,25 @@ Check the active session:
37
45
  revo auth status
38
46
  ```
39
47
 
48
+ Connect this computer as an Agent Gateway terminal with the same CLI installation and login:
49
+
50
+ ```bash
51
+ revo terminal connect --workspace "$HOME/projects/my-project" --name "Work Mac"
52
+ revo terminal status
53
+ revo --version
54
+ ```
55
+
56
+ Pairing appears in AI Agents → Terminals for approval and access configuration; personal Vault settings also shows the grant. `revo terminal connect` runs in the foreground; stop it with Ctrl+C after approval if you want to move it into the background. Install a per-user background service for the approved terminal: LaunchAgent on macOS or `systemd --user` on Linux.
57
+
58
+ Each registration has a stable `agentTerminalId` and a model reference token `[AGENT_TERMINAL/<agentTerminalId>](Name)`. Existing local `runner.json` entries with `deviceId` are read with the same ID, so they do not need to be paired again. Access defaults to `restricted: true`; the owner can explicitly grant access to users, groups, or the entire instance.
59
+
60
+ ```bash
61
+ revo terminal service install --workspace "$HOME/projects/my-project"
62
+ revo terminal service uninstall --workspace "$HOME/projects/my-project"
63
+ ```
64
+
65
+ The service starts at login, reconnects automatically, and pins the environment active at installation (or an explicit `--project <name>`). Install one service per workspace/profile; pass the same `--project` to uninstall after switching environments. Uninstall stops the service but keeps the terminal grant and local registration. `revo terminal disconnect --workspace <path>` revokes the grant and removes its local credential; uninstall the service first. On macOS, service logs are in `~/Library/Logs/RevoEngine/`. On Linux, inspect them with `journalctl --user -u <unit-name>`; `install` prints the unit name. Linux user services normally stop after logout unless an administrator enables user lingering. The runner uses your OS permissions; the workspace controls the starting directory, not OS sandbox access. A personal API key is required.
66
+
40
67
  Remove stored credentials:
41
68
 
42
69
  ```bash
@@ -52,18 +79,269 @@ revo --help
52
79
  Initialize a project for ambient low-code editor globals:
53
80
 
54
81
  ```bash
55
- revo project
82
+ revo project init
56
83
  revo project init ./app
84
+ revo project init ./app --identity-mode stableKey --stable-key-name resourceKey --resources component
85
+ revo project init ./app --identity-mode componentId
57
86
  revo project update ./app
58
87
  ```
59
88
 
60
89
  This writes or refreshes root `.revoengine/types/revo.editor.d.ts`, `.revoengine/types/revo.editor.definitions.json`, and `.revoengine/revo.json`, patches the workspace `tsconfig.json` or `jsconfig.json`, and updates root `.gitignore` so the generated editor bundle stays local by default. Pass a path such as `./backend` when the RevoEngine workspace is nested; the path is saved as `.revoengine/revo.json.workspace` while root `.revoengine/` remains the single state directory.
61
90
 
91
+ When compiler options are missing, project initialization sets `target: ES2022`, `module: ESNext`, and `moduleDetection: force`. Revo TypeScript elements are runtime-wrapped snippets, so module detection is required for top-level `await`; existing explicit compiler options are preserved.
92
+
93
+ `--identity-mode stableKey|componentId` stores the component matching strategy in `.revoengine/revo.json`. New projects default to `stableKey`; existing projects without this setting retain legacy `componentId` matching. In stable-key mode, `--stable-key-name <name>` selects the metadata property and defaults to `stableKey`.
94
+
95
+ ```json
96
+ {
97
+ "componentIdentity": {
98
+ "mode": "stableKey",
99
+ "metadataProperty": "resourceKey"
100
+ }
101
+ }
102
+ ```
103
+
104
+ Stable-key mode manages only components that contain the configured metadata property. Component UUIDs remain in API requests and lock diagnostics, but they are not used as durable identity. Duplicate stable-key values block plan, pull, and push. Use `--identity-mode componentId` only for a workspace that intentionally remains bound to one RevoEngine instance.
105
+
106
+ During interactive init, the CLI asks for the stable key metadata name and which resource families it should track. Press Enter to use `stableKey` and track `all` resources. For non-interactive runs, `--resources <list>` accepts `all` or comma-separated resource names such as `component`, `endpoint`, `group`, `role-group`, `job-template`, `schedule`, `event`, `database-schema`, or `database-view`; when omitted it defaults to `all`. Project initialization backfills missing stable-key metadata for the established resource families while preserving existing keys. Database Schema and Database View backfill are deliberately excluded from automatic initialization because large catalogs and metadata-only database updates require an explicit operator review. Init prints the exact follow-up plan command. Pass `--no-metadata-backfill` when initialization must not write any remote metadata.
107
+
108
+ Backfill stable-key metadata for tracked resources with an explicit plan/apply flow:
109
+
110
+ ```bash
111
+ revo metadata plan --project test
112
+ revo metadata apply --project test --yes
113
+ ```
114
+
115
+ Backfill only writes resources missing the configured stable-key metadata unless `--force` is passed. Generated resource keys use `category/name#<random-hex>`. Database Schema and Database View keys are immutable once present, even with `--force`. Database View backfill reads the current view and uses the deployed full `PUT` API with the same definition, type, version, and ACL, changing only user metadata to add the Stable Key. Because this is a full view update, run it in a reviewed maintenance window. Database Schema backfill also plans column keys in `definition[].metadata.<stable-key-name>` for root tables only, using `category/table/column#<random-hex>`; existing, malformed, or duplicate column keys are never overwritten, including with `--force`. Partition definitions inherit their root table's columns and never receive independent column keys.
116
+
117
+ ### Database Schemas
118
+
119
+ Database Schemas use an explicit SOURCE bootstrap followed by a portable pull:
120
+
121
+ ```bash
122
+ revo metadata plan --project test --resources database-schema
123
+ revo metadata apply --project test --resources database-schema --yes
124
+ # Generate Stable Keys only for root schemas and leave partitions unmanaged:
125
+ revo metadata plan --project test --resources database-schema --skip-partitions
126
+ revo metadata apply --project test --resources database-schema --skip-partitions --yes
127
+ revo database-schemas pull --all --project test
128
+ # Fail when any active schema or partition remains unmanaged:
129
+ revo database-schemas pull --all --project test --strict
130
+ # Compare desired manifests, the selected lock baseline, and live E2E without writes:
131
+ revo database-schemas plan --all --project e2e
132
+ revo database-schemas plan --all --project e2e --json
133
+ # Review only root schemas; partitions are excluded from the API catalog, workspace comparison, and strict coverage:
134
+ revo database-schemas plan --all --project e2e --skip-partitions
135
+ # Add one cached bulk size request for table/index/audit risk context:
136
+ revo database-schemas plan --all --project e2e --with-stats
137
+ # CI convergence and Stable Key coverage gate:
138
+ revo database-schemas plan --all --project e2e --strict
139
+ # Create, safely update, and verify managed root schemas and their partitions:
140
+ revo database-schemas push --all --project e2e --yes
141
+ # Or create one reviewed graph node by Stable Key:
142
+ revo database-schemas push 'Sales/Orders#1234abcd' --project e2e --yes
143
+ # Only after reviewing safe remote content drift; --force never bypasses manual DDL migration:
144
+ revo database-schemas push --all --project e2e --yes --force
145
+ # Destructive prune of previously managed partitions only:
146
+ revo database-schemas push --all --project e2e --yes --force --prune-partitions
147
+ # Destructive removal of reviewed target-only root schemas (no active partitions or Database View dependencies):
148
+ revo database-schemas push --all --project e2e --yes --force --prune-orphans
149
+ # After a reviewed deletion on the source, remove stale local desired manifests and locks only:
150
+ revo database-schemas pull --all --project test --yes --force --prune-orphans
151
+ ```
152
+
153
+ The catalog is read in deterministic pages of 200 records. Pass `--skip-partitions` to `metadata plan` and `metadata apply` when only root Database Schemas should receive generated table Stable Keys; the CLI adds a server-side `parent isNull` filter and defensively excludes any partition returned by the API. Without this flag, root schemas and partitions remain in scope for table keys, while column-key backfill always remains root-only. Root manifests are written below `<workspace>/DatabaseSchemas/<category>/`; every managed partition has its own nested manifest under its parent and carries its own Stable Key plus `parentStableKey`. Partition manifests do not duplicate the inherited column definition.
154
+
155
+ Manifests retain the complete non-reserved user metadata and portable column/partition configuration. Instance UUIDs, column definition IDs, versions, timestamps, records, details, users, ACL, audit settings, and tags are excluded. ACL, audit, and tags are returned only as pull diagnostics and are outside the portable hash; the lock retains only a root's source `restricted` boolean so first creation can safely reject a restricted source without storing target principals. Foreign references remain visible in the column definition with `migration.foreignReferences: "manual"` because they require operator migration. Missing Stable Keys are reported as unmanaged and skipped; duplicates, invalid keys, parent cycles, missing parents, and path collisions stop the pull before files change. A locally edited manifest is preserved unless `--force` is supplied. A normal pull never deletes local manifests. After reviewing source deletions, `pull --prune-orphans --yes --force` removes only local manifests and lock entries that no longer exist on the selected source; it never deletes any target table.
156
+
157
+ `database-schemas plan` is fileless and read-only. It reports three-way states plus root/partition orphans, previously managed partition removals, unmanaged WIP resources, collisions, reconciled manual work, and `manual-schema-migration-required`. Pass `--skip-partitions` for a root-schema-only view: partitions are excluded server-side with `parent isNull`, local partition manifests are excluded too, and they do not contribute to `--strict` coverage. This view intentionally cannot report partition drift, lifecycle work, or unmanaged partitions. Table names, parent relationships, partition bounds, column removals, unsafe keyless column additions, and structural changes without exact column identity remain manual. Changes to an existing column are operational when desired and target definitions share one unique column Stable Key; this includes rename, type, default, nullability, array, primary-key, unique, and index fields. A required, defaulted, indexed, unique, or primary-key new column is also operational when its desired definition carries one unique Stable Key that is absent from the target. A rename is still blocked as manual when the table has an active Database View or foreign-reference declaration. Column presentation order, column metadata, and adding an explicitly nullable column without a default, primary key, unique constraint, index, or foreign reference remain safe definition changes. Every reported change includes an operational hint covering the relevant lock, scan, rewrite, index, validation, conversion, or data-loss risk. `--with-stats` performs exactly one cached bulk `/api/v1/databases/stats` request; the default plan avoids that database-size query. `--strict` accepts only converged resources and informational target-only orphans, and fails for drift, collisions, manual migrations, deletions, missing baselines, or incomplete Stable Key coverage.
158
+
159
+ `database-schemas push` creates missing schemas and applies reviewed safe or controlled operational updates. It re-plans live state, then resolves an exact Stable Key match and fetches its current version immediately before every write; it never sends source UUIDs, rows, ACLs, audit or tags. Existing target column IDs are reattached first by a unique shared column Stable Key and otherwise by exact column name. With a unique shared key, the CLI imports changes to type, default, nullability, array, primary-key, unique, index, name, description, and metadata while preserving the target column identity. It also creates a required, defaulted, indexed, unique, or primary-key column when the new desired definition has one unique Stable Key absent from the target; no instance-local definition ID is sent for that new column. These structural writes can still fail when live data is incompatible and should be run in a reviewed maintenance window; every write is followed by an exact GET and portable-hash verification before the selected environment baseline is recorded. The write is still refused if an active Database View appears after planning a rename. Column drops, unsafe additions without a unique key, structural changes without a unique shared key, malformed or duplicate column keys, and foreign-reference deployment remain manual; `--force` can overwrite reviewed portable content drift but never bypasses those blockers. If an environment lock baseline was lost, `--force` first adopts an exactly matching target through GET-and-hash verification only; it sends no PUT. A reviewed safe difference without that baseline can also be aligned with `--force`. When a manually aligned target already matches the manifest, push verifies the live hash and advances only the selected lock baseline. Audited targets show an audit-trigger-rebuild warning, serialize writes, and retry one audit-lock conflict only after fresh validation. Root tables are created `restricted=false` and `audit=false`; a root pulled as restricted is reported as `access-policy-required` and skipped. Partitions follow root → child → nested-child order, reference the resolved target parent by name, and inherit definition, audit and ACL from that target parent. A failed or unavailable parent skips only its dependent branch, while unrelated creates continue. Removing a previously managed partition manifest produces `delete-partition`; deletion requires all of `--prune-partitions --yes --force`, is processed leaf-to-root, rechecks the live parent and active Database View dependencies, then records an environment tombstone after readback. A target-only root marked `orphan-root` can be deleted only with `--prune-orphans --yes --force`; it is revalidated by Stable Key and hash, and deletion is blocked when it has an active partition or Database View dependency. Unmanaged target-only partitions are never deleted. A tombstoned Stable Key is never recreated automatically, and overlapping replacement bounds require two separate pushes: prune the old partition first, then create the new partition with a new Stable Key.
160
+
161
+ ### Database Views
162
+
163
+ Database Views are migrated only after their Database Schemas and column Stable Keys converge on the target:
164
+
165
+ ```bash
166
+ revo metadata plan --project test --resources database-view
167
+ revo metadata apply --project test --resources database-view --yes
168
+ revo database-views pull --all --project test
169
+ revo database-views plan --all --project e2e --strict
170
+ revo database-views push --all --project e2e --yes
171
+ # Or deploy one reviewed ordinary view by Stable Key:
172
+ revo database-views push 'Reporting/high_value_customers#f00dbabe' --project e2e --yes
173
+ # Materialized writes need their own explicit operational gate:
174
+ revo database-views push --all --project e2e --yes --allow-materialized
175
+ ```
176
+
177
+ The workspace contains `STRUCTURED` and `RAW_SQL` `VIEW` and `MATERIALIZED_VIEW` manifests below `<workspace>/DatabaseViews/`. A `RAW_SQL` manifest retains the SQL text and source schema Stable Keys, while a `STRUCTURED` manifest additionally retains referenced-column Stable Keys; neither retains instance IDs, generated lineage, versions, or source ACL principals. RAW_SQL preview and writes require the target's `databaseViewsRawSql` feature flag and `RESOURCE_ADMIN` permission. A materialized manifest retains the ordered `uniqueKey` and index columns but drops server-generated index names; independent index definitions are normalized deterministically. A plan is fileless: it resolves every source on the target, runs a bounded `take: 0` preview, reports materialization/quota risk and active refresh schedules, and blocks collisions, missing baselines, incompatible output, and any rename or type/mode transition. Materialized creates and updates require `--allow-materialized`; an active refresh schedule blocks an update and must be coordinated manually. Create re-plans live, previews again immediately before POST, and creates a private target view with empty users and groups. Updates fetch the exact live record, then send the full desired definition in a PUT with the current optimistic version while preserving its target-local ACL; preview and post-write hash verification are repeated before advancing the baseline. `--force` may overwrite reviewed portable content drift, but never identity, source-readiness, preview, ACL, rename, type, mode, schedule, or materialized-write-gate blockers. An identical target with a missing baseline is adopted by GET-and-hash verification without a PUT. A matching private view with an empty target-local ACL is `clean`; source ACLs are never copied, and access can be configured independently on the target. For the TEST → E2E operating procedure, including stable-key bootstrap, plan artifacts, target-local ACL work, materialized-view maintenance windows, and strict verification, see [the Database Views runbook](docs/runbooks/database-views-test-to-e2e.md).
178
+
179
+ Job templates use the same metadata bootstrap and workspace boundary. Generate keys on the source environment, then pull portable manifests into `<workspace>/JobTemplates`:
180
+
181
+ ```bash
182
+ revo metadata plan --project test --resources job-template
183
+ revo metadata apply --project test --resources job-template --yes
184
+ revo job-templates pull --all --project test
185
+ revo job-templates plan --all --project e2e
186
+ revo job-templates plan --all --project e2e --json
187
+ revo job-templates push --all --project e2e --yes
188
+ # Only after reviewing remote-changed, conflict, or missing-lock:
189
+ revo job-templates push --all --project e2e --yes --force
190
+ ```
191
+
192
+ ### Endpoints
193
+
194
+ ```bash
195
+ # Establish Stable Key identity on SOURCE, then pull portable endpoint configuration to
196
+ # <workspace>/Endpoints and extend .revoengine/revo.lock.json.
197
+ revo metadata plan --project test --resources endpoint
198
+ revo metadata apply --project test --resources endpoint --yes
199
+ revo endpoints pull --all --project test
200
+ revo endpoints plan --all --project e2e
201
+ revo endpoints plan --all --project e2e --json
202
+ revo endpoints push --all --project e2e --yes
203
+ revo endpoints plan --all --project e2e --strict
204
+ ```
205
+
206
+ Endpoint plan is fileless and read-only. It compares portable workspace manifests, the selected target-environment baseline in `.revoengine/revo.lock.json`, and the live target. Component dependencies are resolved only by exact stable key. Latest bindings require identical portable Component content in the workspace and target; pinned bindings require the pulled digest, workspace digest, and target digest to all match. Dependency mismatches block every Endpoint write and instruct the operator to align Components before Endpoints.
207
+
208
+ Push recomputes the live plan, resolves target Component UUIDs and versions only in memory, and rejects all hard blockers before the first write. Creates, updates, and exact Stable Key restores converge configuration first and then the desired active state. Every write is fetched again and compared by the full portable hash before the target lock baseline is updated. `--force` can accept reviewed three-way content drift, but cannot bypass identity, route, dependency, restore-safety, or concurrency checks. Target-only and unmanaged Endpoints remain untouched, and `--prune` is rejected.
209
+
210
+ For the complete **SOURCE** → **TARGET** operator flow, Component dependency gate,
211
+ GitLab CI plan artifact, manual PROD approval, strict verification, and rollback,
212
+ see [the Endpoint runbook](https://gitlab.com/revoengine/cli/-/blob/master/docs/runbooks/endpoint-source-to-target.md).
213
+
214
+ Job-template manifests keep portable `options` and legacy `inputs`, reference their component by component stable key, and exclude UUIDs, versions, audit data, pinned component versions, and `triggerUser`. When `triggerUser` is present, pull prints a warning because the execution principal must be configured independently per environment.
215
+
216
+ Schedules support source bootstrap, portable pull, planning, and deployment for user-managed `JOB_TEMPLATE` targets and Materialized View refresh targets:
217
+
218
+ ```bash
219
+ # Establish stable-key identity for the source schedules.
220
+ revo metadata plan --project test --resources schedule
221
+ revo metadata apply --project test --resources schedule --yes
222
+
223
+ # Event bootstrap currently covers only JOB_TEMPLATE targets.
224
+ revo metadata plan --project test --resources event
225
+ revo metadata apply --project test --resources event --yes
226
+
227
+ # Pull source Schedule definitions and their lock baselines.
228
+ revo schedules pull --all --project test
229
+
230
+ # Compare the local definitions, the E2E lock baseline, and live E2E schedules.
231
+ revo schedules plan --all --project e2e
232
+ revo schedules plan --all --project e2e --json
233
+ revo schedules plan --all --project e2e --strict
234
+
235
+ # Create disabled Schedules and safely update existing definitions.
236
+ revo schedules push --all --project e2e --yes
237
+ # Materialized View refresh definitions require an additional operational gate.
238
+ revo schedules push --all --project e2e --yes --allow-materialized
239
+ # Only after reviewing remote-changed, conflict, or missing-lock:
240
+ revo schedules push --all --project e2e --yes --force --allow-materialized
241
+
242
+ # Deliberately activate only candidates created or updated by this push.
243
+ revo schedules push --all --project e2e --yes --allow-materialized --activate
244
+ ```
245
+
246
+ Each manifest references either a Job Template by Stable Key or a Materialized View by Database View Stable Key plus `CONCURRENT` or `BLOCKING` refresh mode. It excludes Schedule UUIDs, dependency UUIDs, versions, source `active`, and `targetVersion`. Source `active` becomes an editable `activationCandidate`; Job Template `sourceTargetConfig` is retained for operator context only. Before a Schedule can be created or updated, its dependency must resolve to exactly one compatible live target by exact Stable Key. A target-kind transition is reported as a warning and conflict, and can be applied only after review with `--force`. Missing or ambiguous dependencies, ordinary Views, `AGENT`, unrelated `PLATFORM_OPERATION`, system-managed, deleted, and unmanaged Schedules remain blocking or explicit exclusions. `--strict` fails for drift, blockers, or exclusions; target-only orphans remain informational.
247
+
248
+ Schedule push repeats its live preflight immediately before each write. Creates always send `active: false`; ordinary updates do not change the target activation state. Every create or update re-resolves the target Schedule and dependency, uses the live Schedule version for updates, re-fetches the result, verifies its portable hash, and then advances the selected environment baseline. Materialized View writes require `--allow-materialized`; that gate cannot be bypassed with `--force`. Deploy Database Views before their refresh Schedules. `remote-changed`, `conflict` (including target-kind transitions), and `missing-lock` require explicit `--force`. An exactly matching `missing-lock` target is re-read and adopted into the selected environment baseline without a PUT. There is no Schedule prune: target-only orphans remain untouched.
249
+
250
+ `schedules push --activate` is the optional final activation phase. It activates only candidates created or updated and verified in that same push, and never disables an already active target. Interactive use asks for a second confirmation; non-interactive use requires both `--yes` and `--activate`. Activation failures are isolated per Schedule.
251
+
252
+ Events currently support the source pull step for `JOB_TEMPLATE` targets. After assigning Stable Keys to Events and their Job Templates, pull the portable source manifests and lock baselines:
253
+
254
+ ```bash
255
+ revo metadata plan --project test --resources event
256
+ revo metadata apply --project test --resources event --yes
257
+ revo events pull --all --project test
258
+ revo events plan --all --project e2e
259
+ revo events plan --all --project e2e --json
260
+ revo events plan --all --project e2e --strict
261
+
262
+ # Create disabled Events and safely update their configuration.
263
+ revo events push --all --project e2e --yes
264
+ # Only after reviewing remote-changed, conflict, or missing-lock states:
265
+ revo events push --all --project e2e --yes --force
266
+
267
+ # After the configuration phase completes, align active state with source.
268
+ revo events push --all --project e2e --yes --activate
269
+ ```
270
+
271
+ The resulting `<workspace>/Events/**/event.json` manifests retain the Event name, category, description, type, filter, metadata, activation declaration, and API configuration fields that are portable. They replace `targetId` with the Job Template Stable Key and exclude instance UUIDs, target version/configuration, Event version, audits, execution history, and deletion fields. Events targeting `AGENT`, deleted Events, and Events whose target Job Template has no Stable Key are reported as exclusions and are not written. Pull preserves a locally changed Event manifest unless it is repeated with `--force`.
272
+
273
+ `events plan` is read-only. It compares each local Event with the selected environment's lock baseline and live target, then reports `clean`, `create`, `safe-update`, `remote-changed`, `conflict`, `missing-lock`, `activation-drift`, `orphan`, `collision`, and `skipped`. A difference only in `active` is always `activation-drift`, never a skip. Each Job Template Stable Key must resolve exactly once on the target; missing or ambiguous dependencies block the plan. Orphans and exclusions are reported but are not changed. `--strict` accepts only `clean`, informational `orphan`, and `skipped` states.
274
+
275
+ `events push` repeats the live preflight for every write and resolves `target.jobTemplateStableKey` to one exact target UUID and fresh version. Creates run before updates in Stable Key order and always send `active: false`; ordinary updates never change target activation. Each write is read back and verified against the full portable Event configuration before its environment baseline advances. The current Event API has no write contract for unknown `extensions`; they are retained by pull for visibility but block plan/push rather than risking a partial migration. `remote-changed`, `conflict`, and `missing-lock` require `--force`; invalid identity, unresolved dependencies, unmanaged targets, and API errors never become forceable. Orphans and skipped Events remain untouched.
276
+
277
+ `events push --activate` is the optional second phase. It starts only after all configuration writes have verified successfully, then activates or disables verified Events to match source `active`. Interactive use asks for a second confirmation; non-interactive use requires `--yes --activate`. Activation results, including partial failures, are present in both terminal and JSON output.
278
+
279
+ For the complete **SOURCE** → **TARGET** operator and CI sequence, including the required Component gate, plan artifact, and rollback procedure, see [the Job Template runbook](https://gitlab.com/revoengine/cli/-/blob/master/docs/runbooks/job-template-test-to-e2e.md). The commands in that runbook use TEST → E2E as a concrete example.
280
+
281
+ ### Group migration
282
+
283
+ Groups use the same configured workspace and shared `.revoengine/revo.lock.json` as the other resource families. A Group manifest is stored below `<workspace>/Groups/` and contains only the portable name, category, description, user-owned metadata, and Stable Key. UUIDs, versions, members, user identifiers, member counts, leader, avatar, and access type are not persisted. The shared lock stores a `GROUP` owner only as another Group Stable Key. A source `USER` owner is recorded only as a manual-owner policy, never as a user UUID.
284
+
285
+ ```bash
286
+ # Establish Stable Keys on the source environment. Existing keys are preserved.
287
+ revo metadata plan --project test --resources group
288
+ revo metadata apply --project test --resources group --yes
289
+
290
+ # Pull portable source configuration and the TEST baseline.
291
+ revo groups pull --all --project test
292
+
293
+ # Compare the local desired state, E2E baseline, and live E2E Groups.
294
+ revo groups plan --all --project e2e
295
+ revo groups plan --all --project e2e --json
296
+ revo groups plan --all --project e2e --strict
297
+
298
+ # Apply after review. Push recomputes the live plan and never consumes plan JSON.
299
+ revo groups push --all --project e2e --yes
300
+ ```
301
+
302
+ `plan` reports missing `GROUP`-owned structures as `create` when the owner can be resolved on the target or created earlier in the same acyclic dependency graph. Missing source `USER`-owned structures are `manual-owner`: create them in the target UI with a target-local user owner and the exact source Stable Key, then run plan again. A same category/name target Group with a missing or different Stable Key is a blocking `collision`; the CLI never infers identity from its name.
303
+
304
+ `push` resolves every source `GROUP` owner by Stable Key to its target UUID immediately before POST or PUT. Create sends `members: []`. Updates synchronize a source `GROUP` owner relationship, but preserve a target-local owner when the source policy is `USER`. Member data is never hashed, compared, or sent. Target-only Groups remain `orphan` and are never deleted. Cyclic owner graphs cannot be created through the current single-Group API when the target has no bootstrap Group, so plan blocks them before any write. After an explicitly reviewed manual bootstrap creates both cycle members with temporary target-local owners and exact Stable Keys, plan reports owner drift and push safely closes the cycle with target Group UUIDs. An atomic/deferred-owner backend contract remains the preferred fully automatic solution.
305
+
306
+ For the complete **SOURCE** → **TARGET** operator flow, GitLab CI artifact
307
+ template, bootstrap procedure, and rollback, see [the Group runbook](https://gitlab.com/revoengine/cli/-/blob/master/docs/runbooks/group-source-to-target.md).
308
+
309
+ ### Role-group migration
310
+
311
+ Role groups use the same lock-based `pull -> plan -> push` workflow as components. Generate stable keys on TEST first. Before deploying to another environment, copy the exact TEST `metadata.stableKey` to any matching role group that already exists there. Members and user UUIDs are always excluded.
312
+
313
+ Role-group manifests are committed below `RoleGroups/` in the workspace configured by `revo project init`. For a project configured with `workspace: "backend"`, the files live below `backend/RoleGroups/`. Sync baselines live in the shared `.revoengine/revo.lock.json`; TEST, E2E and PROD hashes are isolated by environment name. Role-group lock entries contain no UUID or version.
314
+
315
+ ```bash
316
+ # Bootstrap missing role-group stable keys on the source environment.
317
+ revo metadata plan --project test --resources role-group
318
+ revo metadata apply --project test --resources role-group --yes
319
+
320
+ # Pull the portable desired state into the configured workspace.
321
+ revo role-groups pull --all --project test
322
+
323
+ # Preview E2E without changing files, lock, or the environment.
324
+ revo role-groups plan --all --project e2e
325
+ revo role-groups plan --all --project e2e --json
326
+
327
+ # Apply after review. Push recomputes the live plan and resolves target UUID/version.
328
+ revo role-groups push --all --project e2e --yes
329
+
330
+ # Verify convergence. Orphans and unrelated unmanaged groups remain untouched.
331
+ revo role-groups plan --all --project e2e --strict
332
+ ```
333
+
334
+ `plan` performs a three-way comparison between the local manifest, the selected environment baseline in `revo.lock.json`, and the live target. It reports `clean`, `create`, `safe-update`, `remote-changed`, `conflict`, `missing-lock`, `orphan`, `collision`, and `skipped`. JSON output may be retained as a CI review artifact, but it is never accepted as push input.
335
+
336
+ `push` resolves the current target UUID by stableKey and reads the current target version immediately before mutation. A matching resource is updated in place even when its UUID differs from TEST. Base fields use the optimistic-version `PUT`; role relationships are synchronized separately through the backend `roleAdd` and `roleDelete` operations. `--force` may adopt or overwrite reviewed content drift, but never bypasses missing/duplicate stable keys, unknown system roles, or ambiguous identity. Prune is disabled until the backend soft-delete contract is confirmed. To roll back an update, restore the previous manifest commit, run plan again, and push that desired state.
337
+
338
+ Artifacts from the earlier experimental `resource` workflow under `.revoengine/resources`, `.revoengine/plans`, and `.revoengine/resources.lock.json` are ignored with a warning and are not removed automatically. The old `revo resource ...` command is no longer part of the CLI surface.
339
+
62
340
  ## Common commands
63
341
 
64
342
  ```bash
65
343
  revo endpoints
66
- revo project ./app
344
+ revo project init ./app
67
345
  revo search CODE button
68
346
  revo search SIMPLE customer
69
347
  revo request GET /api/component/list
@@ -111,6 +389,22 @@ revo component push --all --yes
111
389
  revo component push --all --force
112
390
  ```
113
391
 
392
+ For a migration from a saved source profile to another instance, use the same workspace workflow as Job Templates and Role Groups. Without `--project`, component commands use the active project. With `--project <name>`, they use that saved profile for one invocation and record a separate baseline under `components.<stableKey>.environments.<name>` in `.revoengine/revo.lock.json`. This profile-based workflow requires `componentIdentity.mode: "stableKey"` in `.revoengine/revo.json`; it refuses UUID-only identity so different instance UUIDs cannot be mistaken for missing components.
393
+
394
+ ```bash
395
+ # Pull TEST into the workspace and record the TEST baseline.
396
+ revo component pull --all --yes --project test
397
+
398
+ # Compare local source with live E2E and the E2E baseline.
399
+ revo component plan --all --project e2e
400
+ revo component plan --all --project e2e --json
401
+
402
+ # Create missing target components or safely update managed target components.
403
+ revo component push --all --yes --project e2e
404
+ ```
405
+
406
+ The profile mode matches components by the configured stable key. It adopts an already identical target as the E2E baseline, creates a component missing from E2E, and blocks target drift without an E2E baseline. Commands targeting `default` retain their legacy lock behavior.
407
+
114
408
  Debug one local component in sandbox:
115
409
 
116
410
  ```bash
@@ -120,6 +414,8 @@ revo component debug 6dfb536a-1111-4222-8333-123456789abc --timeout 30 --memory
120
414
  revo component debug 6dfb536a-1111-4222-8333-123456789abc --stream
121
415
  ```
122
416
 
417
+ `--raw-debug` saves the full request and source map as a private file under the CLI config directory and prints only its path to stderr. Delete that file when the investigation is finished.
418
+
123
419
  Pulled components are stored as a tree:
124
420
 
125
421
  ```text
@@ -135,12 +431,12 @@ Components/
135
431
  Each component folder keeps its `component.json` manifest alongside an `elements/` directory with the source payload for every element.
136
432
  Components with `category: null` are stored under `Components/__no_category__/...` while the manifest keeps `"category": null`.
137
433
 
138
- Remote sync state is stored separately in `.revoengine/revo.lock.json`. The lock file records the last remote component hash/version that this workspace was pulled from or successfully pushed to. It is a baseline, not the deployed source of truth; the server remains authoritative for deployed state and local component files remain the desired source.
434
+ Remote sync state is stored separately in `.revoengine/revo.lock.json`. Lock schema v2 records the configured identity strategy and, in stable-key mode, keys each entry by the configured metadata value while retaining both the repository-local and current remote UUID. The lock records the last remote component hash/version that this workspace was pulled from or successfully pushed to. It is a baseline, not the deployed source of truth; the server remains authoritative for deployed state and local component files remain the desired source.
139
435
 
140
436
  Bulk sync behavior:
141
437
 
142
438
  - `revo component pull --all` and `revo component push --all` require terminal confirmation unless `--yes` or `--force` is passed.
143
- - `--yes` only confirms the bulk operation. `--force` is the destructive escape hatch that bypasses lock safety.
439
+ - `--yes` only confirms the bulk operation. `--force` bypasses lock-content safety, but never bypasses stable-key ownership: missing or duplicate configured identities are still blocked or ignored.
144
440
  - `revo component pull --all` requests only active remote components where `deletedAt` is empty.
145
441
  - Pull compares local files, `.revoengine/revo.lock.json`, and the current remote component before overwriting anything.
146
442
  - Pull fast-forwards clean local files when the remote moved and the local copy still matches the lock.
@@ -148,6 +444,7 @@ Bulk sync behavior:
148
444
  - Pull writes stable source manifests and keeps remote version churn out of `Components/**/component.json`.
149
445
  - `revo component plan --all --strict` exits with an error when conflicts or missing lock entries are present.
150
446
  - Push checks the current remote component against the lock before saving local changes.
447
+ - In stable-key mode, push resolves the target remote UUID from metadata and remaps element IDs by element key before saving. Source-instance UUIDs are never sent as target identity.
151
448
  - Push rejects stale remote state instead of trusting the version inside local component JSON.
152
449
  - Push treats backend `Not modified` responses as skipped instead of failing the whole run.
153
450
  - Push treats backend `404` responses as skipped with `doesn't exist remotely`; restore the component in RevoEngine before pushing local changes to it.
@@ -158,15 +455,171 @@ Bulk sync behavior:
158
455
  - Sync logs show direction explicitly: `RevoEngine -> path` for pull and `RevoEngine <- path` for push.
159
456
  - Bulk runs print a summary such as `Deployed 54/67, Skipped 13/67 in 13s`.
160
457
 
458
+ ## Projects
459
+
460
+ Save named project connections and switch the active one for ordinary CLI commands. `revo project init` configures the local workspace:
461
+
462
+ ```bash
463
+ revo projects add develop --url https://api.dev.revong.com
464
+ revo projects add e2e --url https://api.e2e.revong.com
465
+ revo projects add prod --url https://app.revo.com
466
+ revo projects list
467
+ revo projects set develop
468
+ revo auth status
469
+ revo endpoints
470
+ revo projects set default
471
+ revo projects remove e2e
472
+ ```
473
+
474
+ `revo projects add` prompts for the API key, validates it against `/api/v1/me`, asks whether it is production, saves it, and makes it active. Use `--offline` to skip validation. `revo projects list` marks the active connection with `*`; `revo -i` and `revo auth status` show its name and API URL. `revo projects set <name>` switches the persistent default for subsequent commands. `revo projects set default` selects the original `revo auth login` credentials. An explicit `--project <name>` selects one profile for a single invocation without changing the active project. `--env` and `revo env` remain supported as compatibility aliases. Named profiles keep their own URL, key, and instance even when `REVO_URL`, `REVO_TOKEN`, or `REVO_INSTANCE` is set. Removing the active profile returns to `default`.
475
+
476
+ `revo auth logout` clears only the original default login and keeps named project profiles and the active selection. Resource sync commands use the active named profile when `--project` is omitted; their existing requirement for a named profile remains. A production profile (stored as `"production": true` in `config.json`) requires an interactive confirmation before the first POST, PUT, PATCH, or DELETE in every CLI invocation. This protection cannot be bypassed with `--yes`, `--force`, or non-interactive CI.
477
+
478
+ Cross-environment component operations remain available as advanced commands:
479
+
480
+ ```bash
481
+ revo env diff --from develop --to e2e
482
+ revo env diff --from develop --to e2e --json
483
+ revo env diff --from develop --to e2e --strict # CI drift gate: non-zero exit on any difference
484
+ revo env diff --from develop --to e2e --prune # classify target-only components as deletes
485
+ revo env diff --from develop --to e2e --key Forms/Card --category Shared
486
+ ```
487
+
488
+ The special name `default` refers to your normal login (`revo auth login` / `REVO_TOKEN`), so `--from default --to e2e` works without saving a profile for the default.
489
+
490
+ Components are matched across environments by a **stable key** stored in resource metadata by the platform. The metadata property defaults to `stableKey`; initialize a workspace with `revo project init --stable-key-name <name>` or pass `--stable-key-name <name>` to env commands to use a different property. `revo metadata apply` writes this value to `metadata` for components. Env sync reads `metadata`, `metaData`, and `resourceMetadata` for compatibility, but it does not generate stable keys while creating resources. Project metadata also stores `trackedResources`, defaulting to `all`; env commands read it or accept `--resources <list>` directly. For older components that do not have metadata yet, the CLI falls back to normalized `category/name`, because component ids and element ids are instance-local. Content comparison uses a portable hash that ignores ids, versions, and timestamps. Two components in one environment that share the same stable key are reported as a `collision` and block `apply` until resolved or scoped out with `--key`/`--category`.
491
+
492
+ Mirror components from a source environment into a target:
493
+
494
+ ```bash
495
+ revo env apply --from develop --to e2e # prints the plan, then asks for confirmation
496
+ revo env apply --from develop --to e2e --dry-run # plan only, never writes
497
+ revo env apply --from develop --to e2e --yes # confirm creates/updates non-interactively
498
+ revo env apply --from develop --to e2e --yes --prune --force # full mirror including deletes
499
+ ```
500
+
501
+ Apply semantics:
502
+
503
+ - Creates run first, then updates, then deletes; failures are reported per component and the run continues.
504
+ - `--yes` only confirms the bulk apply. Deletes additionally require `--force` (non-interactive) or a second dedicated confirmation (interactive), matching the component sync semantics.
505
+ - Deletes only happen with `--prune`; without it, target-only components are reported as `orphan` and left alone.
506
+ - Element identity is preserved on update: source elements are matched to the target's elements by key, so the target keeps its own element ids.
507
+ - After every create/update the CLI re-fetches the target component and verifies its content hash against the source; mismatches are flagged as `verification mismatch`.
508
+
509
+ ## Environment sync runbook
510
+
511
+ Use this flow when preparing one environment to track and mirror resources with the CLI. The examples use `test` as the source and `dev` as the target.
512
+
513
+ 1. Initialize the local project metadata:
514
+
515
+ ```bash
516
+ revo project init
517
+ ```
518
+
519
+ For CI or non-interactive setup:
520
+
521
+ ```bash
522
+ revo project init --identity-mode stableKey --stable-key-name stableKey --resources all
523
+ ```
524
+
525
+ This writes `.revoengine/revo.json` with:
526
+
527
+ - `componentIdentity.mode`: `stableKey` for portable identity or `componentId` for legacy instance-bound identity.
528
+ - `componentIdentity.metadataProperty`: the component metadata property used in stable-key mode. Default: `stableKey`.
529
+ - `trackedResources`: resource families the CLI should track. Default: `all`; this CLI currently tracks components.
530
+
531
+ 2. Save environment profiles:
532
+
533
+ ```bash
534
+ revo projects add test --url https://api.test.revong.com
535
+ revo projects add dev --url https://api.dev.revong.com
536
+ revo projects list
537
+ ```
538
+
539
+ For one-off commands without a saved profile, use `default` plus environment variables:
540
+
541
+ ```bash
542
+ REVO_URL="https://api.dev.revong.com" REVO_TOKEN="$REVO_DEV_TOKEN" revo metadata plan --project default
543
+ ```
544
+
545
+ 3. Plan metadata backfill before writing anything:
546
+
547
+ ```bash
548
+ revo metadata plan --project test
549
+ revo metadata plan --project dev
550
+ ```
551
+
552
+ Use JSON when reviewing exact resources:
553
+
554
+ ```bash
555
+ revo metadata plan --project test --json
556
+ ```
557
+
558
+ 4. Apply metadata backfill:
559
+
560
+ ```bash
561
+ revo metadata apply --project test --yes
562
+ revo metadata apply --project dev --yes
563
+ ```
564
+
565
+ Important: if the same resources already exist in multiple environments, do not treat independently generated random suffixes as interchangeable. `category/name#aaaa1111` and `category/name#bbbb2222` are different stable keys, even when the readable prefix matches. Pick a source of truth and make the target environment use the exact same metadata value, including casing, before running destructive sync.
566
+
567
+ 5. Verify metadata backfill:
568
+
569
+ ```bash
570
+ revo metadata plan --project test
571
+ revo metadata plan --project dev
572
+ ```
573
+
574
+ Expected after a successful backfill:
575
+
576
+ ```text
577
+ Plan: 0 to update, N already populated, 0 blocked.
578
+ ```
579
+
580
+ 6. Preview environment drift:
581
+
582
+ ```bash
583
+ revo env diff --from test --to dev
584
+ revo env diff --from test --to dev --json
585
+ ```
586
+
587
+ Use `--strict` in CI:
588
+
589
+ ```bash
590
+ revo env diff --from test --to dev --strict
591
+ ```
592
+
593
+ 7. Apply non-destructive sync:
594
+
595
+ ```bash
596
+ revo env apply --from test --to dev --dry-run
597
+ revo env apply --from test --to dev --yes
598
+ ```
599
+
600
+ Without `--prune`, target-only resources are reported as `orphan` and left untouched.
601
+
602
+ 8. Apply full mirror with orphan removal:
603
+
604
+ ```bash
605
+ revo env diff --from test --to dev --prune
606
+ revo env apply --from test --to dev --prune --dry-run
607
+ revo env apply --from test --to dev --prune --yes --force
608
+ ```
609
+
610
+ Only use `--prune --force` after reviewing the plan. It deletes target resources that are missing from the source.
611
+
161
612
  ## Configuration
162
613
 
163
- The CLI reads configuration in this order:
614
+ The CLI selects a project from `--project <name>` or the active project. For a named project, it uses that project's URL, key, and instance; explicit `--url`, `--token`, and `--instance` flags can override individual values. Named projects do not inherit values from environment variables or the `default` login.
615
+
616
+ For `default`, the CLI reads configuration in this order:
164
617
 
165
618
  1. CLI flags such as `--url` and `--token`
166
619
  2. Environment variables such as `REVO_URL` and `REVO_TOKEN`
167
620
  3. Stored config in the OS config directory
168
621
 
169
- This means environment variables override the locally stored config, which is the intended setup for CI and non-interactive automation.
622
+ This means environment variables override the locally stored `default` config, which supports CI and non-interactive automation.
170
623
 
171
624
  Use `--url` for one-off local or dedicated API targets:
172
625
 
@@ -195,9 +648,12 @@ Supported environment variables:
195
648
  - `REVO_URL` or `REVO_BASE_URL`
196
649
  - `REVO_TOKEN` or `REVO_API_KEY`
197
650
  - `REVO_NO_UPDATE_NOTIFIER`
651
+ - `REVO_TRUSTED_SERVICE_ORIGINS` (comma-separated exact HTTPS origins for custom sandbox services)
198
652
  - `REVOENGINE_URL` or `REVOENGINE_BASE_URL`
199
653
  - `REVOENGINE_TOKEN` or `REVOENGINE_API_KEY`
200
654
 
655
+ Authenticated API requests reject cross-origin pagination links and redirects. The sandbox service advertised by the authenticated profile may use a different origin: matching first-party API/sandbox hosts and `localhost` services are trusted automatically. For a custom sandbox, set `REVO_TRUSTED_SERVICE_ORIGINS` to its exact origin before running editor-type update or component debug. Do not include a path or credentials in this setting.
656
+
201
657
  Example for CI:
202
658
 
203
659
  ```bash