@revoengine/cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +211 -0
  2. package/dist/bin/revo.d.ts +2 -0
  3. package/dist/bin/revo.js +19 -0
  4. package/dist/src/cli.d.ts +3 -0
  5. package/dist/src/cli.js +213 -0
  6. package/dist/src/client.d.ts +72 -0
  7. package/dist/src/client.js +315 -0
  8. package/dist/src/commands/auth.d.ts +2 -0
  9. package/dist/src/commands/auth.js +131 -0
  10. package/dist/src/commands/component.d.ts +2 -0
  11. package/dist/src/commands/component.js +905 -0
  12. package/dist/src/commands/endpoints.d.ts +2 -0
  13. package/dist/src/commands/endpoints.js +4 -0
  14. package/dist/src/commands/index.d.ts +7 -0
  15. package/dist/src/commands/index.js +7 -0
  16. package/dist/src/commands/info.d.ts +2 -0
  17. package/dist/src/commands/info.js +6 -0
  18. package/dist/src/commands/project.d.ts +2 -0
  19. package/dist/src/commands/project.js +80 -0
  20. package/dist/src/commands/request.d.ts +2 -0
  21. package/dist/src/commands/request.js +59 -0
  22. package/dist/src/commands/search.d.ts +2 -0
  23. package/dist/src/commands/search.js +22 -0
  24. package/dist/src/config.d.ts +54 -0
  25. package/dist/src/config.js +356 -0
  26. package/dist/src/index.d.ts +4 -0
  27. package/dist/src/index.js +4 -0
  28. package/dist/src/legacy.d.ts +8 -0
  29. package/dist/src/legacy.js +88 -0
  30. package/dist/src/project.d.ts +102 -0
  31. package/dist/src/project.js +475 -0
  32. package/dist/src/prompt.d.ts +4 -0
  33. package/dist/src/prompt.js +64 -0
  34. package/dist/src/runtime-view.d.ts +17 -0
  35. package/dist/src/runtime-view.js +80 -0
  36. package/dist/src/spinner.d.ts +14 -0
  37. package/dist/src/spinner.js +46 -0
  38. package/dist/src/types.d.ts +36 -0
  39. package/dist/src/types.js +1 -0
  40. package/dist/src/ui.d.ts +26 -0
  41. package/dist/src/ui.js +182 -0
  42. package/dist/src/utils.d.ts +10 -0
  43. package/dist/src/utils.js +86 -0
  44. package/package.json +32 -0
  45. package/tsconfig.build.json +15 -0
  46. package/tsconfig.json +19 -0
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # @revoengine/cli
2
+
3
+ Official CLI for RevoEngine Platform.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @revoengine/cli
9
+ ```
10
+
11
+ The package installs two binaries:
12
+
13
+ - `revo` is the primary documented command.
14
+ - `revoengine` is kept as a compatible alias.
15
+
16
+ ## Quick start
17
+
18
+ Inspect the local runtime and config path:
19
+
20
+ ```bash
21
+ revo -i
22
+ ```
23
+
24
+ Log in and save your default credentials:
25
+
26
+ ```bash
27
+ revo auth login
28
+ ```
29
+
30
+ `revo auth login` opens an interactive terminal prompt for your API key and instance ID. If you are already logged in, the CLI warns and asks you to `revo auth logout` first.
31
+
32
+ Check the active session:
33
+
34
+ ```bash
35
+ revo auth status
36
+ ```
37
+
38
+ Remove stored credentials:
39
+
40
+ ```bash
41
+ revo auth logout
42
+ ```
43
+
44
+ Show the command reference:
45
+
46
+ ```bash
47
+ revo --help
48
+ ```
49
+
50
+ Initialize a project for ambient low-code editor globals:
51
+
52
+ ```bash
53
+ revo project
54
+ revo project init ./app
55
+ revo project switch a931e927-738d-42a4-b486-5fc19bfb3dfb
56
+ ```
57
+
58
+ This writes `.revoengine/types/revo.editor.d.ts`, `.revoengine/revo.json`, patches `tsconfig.json` or `jsconfig.json`, and updates `.gitignore` so the generated type bundle stays local by default. `revo project switch <instanceId>` rebinds the current project to another stored instance and refreshes the ambient editor types for that instance.
59
+
60
+ ## Common commands
61
+
62
+ ```bash
63
+ revo endpoints
64
+ revo project ./app
65
+ revo search CODE button
66
+ revo search SIMPLE customer
67
+ revo request GET /api/component/list
68
+ revo request POST /api/component "{\"name\":\"Customer Card\"}"
69
+ ```
70
+
71
+ `revo search` accepts only two search types:
72
+
73
+ - `CODE` searches code references.
74
+ - `SIMPLE` searches everything.
75
+
76
+ `revo request` accepts an optional positional `<BODY>` argument as a JSON string. The existing `--body` / `-d` flag still works as well.
77
+
78
+ ## Component sync
79
+
80
+ Pull one or more components by id:
81
+
82
+ ```bash
83
+ revo component pull 6dfb536a-1111-4222-8333-123456789abc
84
+ revo component pull 6dfb536a-1111-4222-8333-123456789abc 7afc7b98-4444-4555-8666-abcdefabcdef
85
+ ```
86
+
87
+ Pull every available component:
88
+
89
+ ```bash
90
+ revo component pull --all
91
+ revo component pull --all --force
92
+ ```
93
+
94
+ Pull one or more remote components:
95
+
96
+ ```bash
97
+ revo component pull 6dfb536a-1111-4222-8333-123456789abc
98
+ revo component pull 6dfb536a-1111-4222-8333-123456789abc --stale
99
+ revo component pull --all
100
+ revo component pull --all --stale
101
+ revo component pull --all --force
102
+ ```
103
+
104
+ Push one or more local components:
105
+
106
+ ```bash
107
+ revo component push 6dfb536a-1111-4222-8333-123456789abc
108
+ revo component push --all
109
+ revo component push --all --force
110
+ ```
111
+
112
+ Debug one local component in sandbox:
113
+
114
+ ```bash
115
+ revo component debug 6dfb536a-1111-4222-8333-123456789abc
116
+ revo component debug 6dfb536a-1111-4222-8333-123456789abc -d '{"filters":{}}'
117
+ revo component debug 6dfb536a-1111-4222-8333-123456789abc --timeout 30 --memory 256
118
+ ```
119
+
120
+ Pulled components are stored as a tree:
121
+
122
+ ```text
123
+ Components/
124
+ Forms/
125
+ Customer_Card-6dfb536a-1111-4222-8333-123456789abc/
126
+ component.json
127
+ elements/
128
+ 1_template.ts
129
+ 2_logic.ts
130
+ ```
131
+
132
+ Each component folder keeps its `component.json` manifest alongside an `elements/` directory with the source payload for every element.
133
+ Components with `category: null` are stored under `Components/__no_category__/...` while the manifest keeps `"category": null`.
134
+
135
+ Bulk sync behavior:
136
+
137
+ - `revo component pull --all` and `revo component push --all` require terminal confirmation unless `--force` is passed.
138
+ - Pull compares the full local workspace contract before overwriting anything.
139
+ - Pull skips with `no changes` when the local workspace already matches the remote component.
140
+ - Pull skips with `changed` when local files differ from the remote contract.
141
+ - Pull skips with `stale version` when the local version is older than the remote version, unless `--stale` or `--force` is passed.
142
+ - Push treats backend `Not modified` responses as skipped instead of failing the whole run.
143
+ - Debug posts the local `component.json` plus `elements/{order}_{key}.{js|ts}` source files to the instance sandbox `debug` endpoint.
144
+ - Sync logs show direction explicitly: `RevoEngine -> path` for pull and `RevoEngine <- path` for push.
145
+ - Bulk runs print a summary such as `Deployed 54/67, Skipped 13/67 in 13s`.
146
+
147
+ ## Configuration
148
+
149
+ The CLI reads configuration in this order:
150
+
151
+ 1. CLI flags such as `--url`, `--instance`, and `--token`
152
+ 2. Environment variables such as `REVO_URL`, `REVO_INSTANCE`, and `REVO_TOKEN`
153
+ 3. Stored config in the OS config directory
154
+
155
+ This means environment variables override the locally stored config, which is the intended setup for CI and non-interactive automation.
156
+
157
+ Use `--url` for one-off local or dedicated API targets:
158
+
159
+ ```bash
160
+ revo --url https://api.test.revong.com -i
161
+ revo --url https://api.test.revong.com auth login --instance a931e927-738d-42a4-b486-5fc19bfb3dfb
162
+ revo --url https://api.revoengine.com endpoints
163
+ ```
164
+
165
+ Use environment variables for CI/CD or shell profiles:
166
+
167
+ ```bash
168
+ # TEST
169
+ export REVO_URL="https://api.test.revong.com"
170
+ export REVO_INSTANCE="a931e927-738d-42a4-b486-5fc19bfb3dfb"
171
+ export REVO_TOKEN="your-test-token"
172
+ revo endpoints
173
+
174
+ # PROD
175
+ export REVO_URL="https://api.revoengine.com"
176
+ export REVO_INSTANCE="a931e927-738d-42a4-b486-5fc19bfb3dfb"
177
+ export REVO_TOKEN="your-prod-token"
178
+ revo endpoints
179
+ ```
180
+
181
+ Supported environment variables:
182
+
183
+ - `REVO_URL` or `REVO_BASE_URL`
184
+ - `REVO_INSTANCE`
185
+ - `REVO_TOKEN` or `REVO_API_KEY`
186
+ - `REVOENGINE_URL` or `REVOENGINE_BASE_URL`
187
+ - `REVOENGINE_INSTANCE`
188
+ - `REVOENGINE_TOKEN` or `REVOENGINE_API_KEY`
189
+
190
+ Example for CI:
191
+
192
+ ```bash
193
+ export REVO_URL="https://api.revoengine.com"
194
+ export REVO_INSTANCE="a931e927-738d-42a4-b486-5fc19bfb3dfb"
195
+ export REVO_TOKEN="your-ci-token"
196
+ revo endpoints
197
+ ```
198
+
199
+ Stored config lives in:
200
+
201
+ - macOS and Linux: `~/.config/revoengine`
202
+ - Windows: `%APPDATA%/revoengine`
203
+
204
+ ## Local development
205
+
206
+ ```bash
207
+ npm install
208
+ npm run check
209
+ npm test
210
+ node ./bin/revo.ts --help
211
+ ```
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { realpathSync } from 'node:fs';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { main } from "../src/cli.js";
5
+ function isDirectExecution() {
6
+ try {
7
+ return Boolean(process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href);
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ if (isDirectExecution()) {
14
+ main().catch((error) => {
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ process.stderr.write(`${message}\n`);
17
+ process.exitCode = 1;
18
+ });
19
+ }
@@ -0,0 +1,3 @@
1
+ import type { ParsedArgs } from './types.ts';
2
+ export declare function parseArgs(argv: string[]): ParsedArgs;
3
+ export declare function main(argv?: string[]): Promise<void>;
@@ -0,0 +1,213 @@
1
+ import { realpathSync } from 'node:fs';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { RevoClient } from "./client.js";
4
+ import { resolveRuntimeConfig } from "./config.js";
5
+ import { handleAuthCommand, handleComponentCommand, handleEndpointsCommand, handleInfoCommand, handleProjectCommand, handleRequestCommand, handleSearchCommand, } from "./commands/index.js";
6
+ import { buildRuntimeViewModel } from "./runtime-view.js";
7
+ import { renderHelp, renderOverview } from "./ui.js";
8
+ import { readPackageVersion } from "./utils.js";
9
+ const packageVersion = readPackageVersion();
10
+ const ANSI = {
11
+ reset: '\u001b[0m',
12
+ red: '\u001b[31m',
13
+ };
14
+ function color(text, code) {
15
+ return process.stdout.isTTY || process.env.FORCE_COLOR ? `${code}${text}${ANSI.reset}` : text;
16
+ }
17
+ function danger(text) {
18
+ return color(text, ANSI.red);
19
+ }
20
+ function isDirectExecution() {
21
+ try {
22
+ return Boolean(process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href);
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ }
28
+ export function parseArgs(argv) {
29
+ const result = { _: [] };
30
+ let stopOptions = false;
31
+ const setValue = (key, value) => {
32
+ const current = result[key];
33
+ if (current === undefined) {
34
+ result[key] = value;
35
+ return;
36
+ }
37
+ if (typeof current === 'boolean') {
38
+ result[key] = typeof value === 'boolean' ? current || value : value;
39
+ return;
40
+ }
41
+ if (typeof value === 'boolean') {
42
+ result[key] = current;
43
+ return;
44
+ }
45
+ result[key] = Array.isArray(current) ? [...current, value] : [current, value];
46
+ };
47
+ const isValueToken = (value) => {
48
+ if (!value || value === '--') {
49
+ return false;
50
+ }
51
+ return value === '-' || !value.startsWith('-') || /^-\d+(\.\d+)?$/.test(value);
52
+ };
53
+ for (let index = 0; index < argv.length; index += 1) {
54
+ const arg = argv[index];
55
+ if (stopOptions || arg === '-' || !arg.startsWith('-')) {
56
+ result._.push(arg);
57
+ continue;
58
+ }
59
+ if (arg === '--') {
60
+ stopOptions = true;
61
+ continue;
62
+ }
63
+ if (arg.startsWith('--')) {
64
+ const body = arg.slice(2);
65
+ if (body.startsWith('no-') && body.length > 3) {
66
+ setValue(body.slice(3), false);
67
+ continue;
68
+ }
69
+ const equalsIndex = body.indexOf('=');
70
+ if (equalsIndex !== -1) {
71
+ setValue(body.slice(0, equalsIndex), body.slice(equalsIndex + 1));
72
+ continue;
73
+ }
74
+ const next = argv[index + 1];
75
+ if (isValueToken(next)) {
76
+ setValue(body, next);
77
+ index += 1;
78
+ continue;
79
+ }
80
+ setValue(body, true);
81
+ continue;
82
+ }
83
+ const body = arg.slice(1);
84
+ const equalsIndex = body.indexOf('=');
85
+ if (equalsIndex !== -1) {
86
+ const keys = body.slice(0, equalsIndex).split('').filter(Boolean);
87
+ const value = body.slice(equalsIndex + 1);
88
+ if (keys.length === 0) {
89
+ continue;
90
+ }
91
+ for (const key of keys.slice(0, -1)) {
92
+ setValue(key, true);
93
+ }
94
+ setValue(keys[keys.length - 1], value);
95
+ continue;
96
+ }
97
+ if (body.length > 1) {
98
+ const next = argv[index + 1];
99
+ if (isValueToken(next)) {
100
+ for (const key of body.slice(0, -1)) {
101
+ setValue(key, true);
102
+ }
103
+ setValue(body[body.length - 1], next);
104
+ index += 1;
105
+ continue;
106
+ }
107
+ for (const key of body) {
108
+ setValue(key, true);
109
+ }
110
+ continue;
111
+ }
112
+ const key = body;
113
+ const next = argv[index + 1];
114
+ if (!isValueToken(next)) {
115
+ setValue(key, true);
116
+ continue;
117
+ }
118
+ setValue(key, next);
119
+ index += 1;
120
+ }
121
+ return result;
122
+ }
123
+ function print(value) {
124
+ if (typeof value === 'string') {
125
+ process.stdout.write(`${value}\n`);
126
+ return;
127
+ }
128
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
129
+ }
130
+ function println(message) {
131
+ process.stdout.write(`${message}\n`);
132
+ }
133
+ function printError(error) {
134
+ const message = error instanceof Error ? error.message : String(error);
135
+ process.stderr.write(`${danger('Error:')} ${message}\n`);
136
+ }
137
+ function resolveClient(args) {
138
+ const runtime = resolveRuntimeConfig({
139
+ baseUrl: typeof args.url === 'string' ? args.url : typeof args.baseUrl === 'string' ? args.baseUrl : undefined,
140
+ instance: typeof args.instance === 'string' ? args.instance : typeof args.i === 'string' ? args.i : undefined,
141
+ token: typeof args.token === 'string' ? args.token : typeof args.t === 'string' ? args.t : undefined,
142
+ });
143
+ return new RevoClient(runtime);
144
+ }
145
+ function createContext(args) {
146
+ const client = resolveClient(args);
147
+ return {
148
+ args,
149
+ client,
150
+ cwd: process.cwd(),
151
+ packageVersion,
152
+ print,
153
+ println,
154
+ error: (message) => printError(new Error(message)),
155
+ };
156
+ }
157
+ function printHelp() {
158
+ print(renderHelp(packageVersion));
159
+ }
160
+ export async function main(argv = process.argv.slice(2)) {
161
+ const args = parseArgs(argv);
162
+ const command = args._[0];
163
+ const noPositionals = args._.length === 0;
164
+ if (command === 'version' || ((args.version !== undefined || args.V !== undefined) && noPositionals)) {
165
+ print(packageVersion);
166
+ return;
167
+ }
168
+ if (command === 'info' || ((args.i === true || args.info === true) && noPositionals)) {
169
+ await handleInfoCommand(createContext(args));
170
+ return;
171
+ }
172
+ if (command === 'help' || ((args.help !== undefined || args.h !== undefined) && noPositionals)) {
173
+ printHelp();
174
+ return;
175
+ }
176
+ const context = createContext(args);
177
+ if (!command) {
178
+ const view = await buildRuntimeViewModel(context);
179
+ print(renderOverview(view));
180
+ return;
181
+ }
182
+ if (command === 'auth') {
183
+ await handleAuthCommand(context);
184
+ return;
185
+ }
186
+ if (command === 'component') {
187
+ await handleComponentCommand(context);
188
+ return;
189
+ }
190
+ if (command === 'endpoints') {
191
+ await handleEndpointsCommand(context);
192
+ return;
193
+ }
194
+ if (command === 'project') {
195
+ await handleProjectCommand(context);
196
+ return;
197
+ }
198
+ if (command === 'search') {
199
+ await handleSearchCommand(context);
200
+ return;
201
+ }
202
+ if (command === 'request') {
203
+ await handleRequestCommand(context);
204
+ return;
205
+ }
206
+ throw new Error(`Unknown command: ${command}`);
207
+ }
208
+ if (isDirectExecution()) {
209
+ main().catch((error) => {
210
+ printError(error);
211
+ process.exitCode = 1;
212
+ });
213
+ }
@@ -0,0 +1,72 @@
1
+ export type RequestOptions = {
2
+ query?: Record<string, unknown>;
3
+ body?: unknown;
4
+ headers?: HeadersInit;
5
+ authGuard?: boolean;
6
+ spinnerLabel?: string;
7
+ };
8
+ export type ComponentListRequest = {
9
+ path?: string;
10
+ query?: Record<string, unknown>;
11
+ };
12
+ export type ClientOptions = {
13
+ baseUrl?: string;
14
+ instance?: string;
15
+ token?: string;
16
+ fetch?: typeof fetch;
17
+ };
18
+ export type ApiResponse<T = unknown> = {
19
+ status: number;
20
+ ok: boolean;
21
+ data: T;
22
+ headers: Headers;
23
+ };
24
+ export declare class ApiError extends Error {
25
+ status: number;
26
+ data: unknown;
27
+ constructor(status: number, message: string, data: unknown);
28
+ }
29
+ export declare class AuthenticationError extends ApiError {
30
+ constructor(status: number, message: string, data: unknown);
31
+ }
32
+ export declare class PermissionDeniedError extends ApiError {
33
+ path: string;
34
+ constructor(path: string, message: string, data: unknown);
35
+ }
36
+ declare function buildUrl(baseUrl: string, requestPath: string, query?: Record<string, unknown>): URL;
37
+ declare function normalizeToken(token?: string): string;
38
+ export declare class RevoClient {
39
+ baseUrl: string;
40
+ instance: string;
41
+ token: string;
42
+ fetchImpl: typeof fetch | undefined;
43
+ constructor(options?: ClientOptions);
44
+ get authHeader(): string;
45
+ get authValidationKey(): string;
46
+ assertReady(): void;
47
+ validateSession(options?: {
48
+ force?: boolean;
49
+ }): Promise<{
50
+ authenticated: boolean;
51
+ fromCache: true;
52
+ profile: {} | null;
53
+ } | {
54
+ authenticated: boolean;
55
+ fromCache: false;
56
+ profile: unknown;
57
+ }>;
58
+ request(method: string, path: string, options?: RequestOptions): Promise<ApiResponse>;
59
+ requestData<T = unknown>(method: string, path: string, options?: RequestOptions): Promise<T>;
60
+ me(options?: {
61
+ force?: boolean;
62
+ }): Promise<unknown>;
63
+ listEndpoints(): Promise<unknown>;
64
+ getEditorTypes(requestPath?: string): Promise<unknown>;
65
+ debugComponent(requestPath: string, body: unknown): Promise<unknown>;
66
+ search(params: Record<string, unknown>): Promise<unknown>;
67
+ listComponents(options?: ComponentListRequest): Promise<unknown>;
68
+ getComponent(componentId: string): Promise<unknown>;
69
+ createComponent(body: Record<string, unknown>): Promise<ApiResponse<unknown>>;
70
+ saveComponentElements(componentId: string, body: unknown): Promise<ApiResponse<unknown>>;
71
+ }
72
+ export { buildUrl, normalizeToken };