statelyai 0.7.3 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,48 +1,126 @@
1
1
  # statelyai
2
2
 
3
- CLI implementation for Stately.
3
+ `statelyai` connects local machine source files to Stately Studio. Use it to
4
+ open a file in the visual editor, compare local and remote machines, or sync a
5
+ project in both directions.
4
6
 
5
- This package contains the `statelyai` command implementation.
7
+ ## Run the CLI
6
8
 
7
- <!-- happy-path CLI usage and statelyai.json shape derived from packages/cli/src/cli.ts#COMMANDS and packages/cli/src/projectConfig.ts -->
8
- ## Happy Path
9
+ <!-- package name and binary from package.json -->
9
10
 
10
- 1. Log in once:
11
+ Run without installing:
12
+
13
+ ```bash
14
+ npx statelyai --help
15
+ ```
16
+
17
+ Or install it globally:
18
+
19
+ ```bash
20
+ npm install --global statelyai
21
+ ```
22
+
23
+ ## Choose a workflow
24
+
25
+ <!-- command capabilities and side effects from src/cli.ts#COMMANDS -->
26
+
27
+ | Goal | Command | Writes |
28
+ | ---------------------------- | ---------------------------------------------------- | ------------------------------------------------------- |
29
+ | Edit one local file visually | `statelyai open <file>` | Local file, when you save in the editor |
30
+ | Set up project sync | `statelyai init [--scan]` | Remote project and local `statelyai.json` |
31
+ | Inspect project machines | `statelyai status` | Nothing |
32
+ | Preview project uploads | `statelyai push --dry-run` | Nothing |
33
+ | Upload local machines | `statelyai push [file]` | Remote machines and local `@statelyai` IDs |
34
+ | Download project machines | `statelyai pull` | Linked local files and new files under `newMachinesDir` |
35
+ | Compare two machines | `statelyai diff <source> <target>` | Nothing |
36
+ | Fail on differences in CI | `statelyai diff <source> <target> --fail-on-changes` | Nothing |
37
+
38
+ ## Quick start
39
+
40
+ <!-- project initialization and sync flow from InitCommand, PushCommand, PullCommand, and projectConfig.ts -->
41
+
42
+ For hosted Stately, authenticate once:
11
43
 
12
44
  ```bash
13
45
  statelyai login
14
46
  ```
15
47
 
16
- Automation can set `STATELY_ACCESS_TOKEN` for OAuth or `STATELY_API_KEY` for
17
- API-key auth.
48
+ Initialize the current repository and scan for XState files:
18
49
 
19
- For a custom OAuth protected resource, discover OAuth from that resource:
50
+ ```bash
51
+ statelyai init --scan
52
+ ```
53
+
54
+ `--scan` suggests `include` globs and asks before saving them. Without
55
+ `--scan`, `init` creates `statelyai.json` with an empty `include` list.
56
+
57
+ Preview the files and remote operations:
58
+
59
+ ```bash
60
+ statelyai push --dry-run
61
+ ```
62
+
63
+ Then push and pull:
20
64
 
21
65
  ```bash
22
- statelyai login --base-url http://localhost:3000/registry/api/mcp
66
+ statelyai push
67
+ statelyai pull
23
68
  ```
24
69
 
25
- `statelyai login --auth bearer` uses the same browser OAuth flow explicitly.
26
- `statelyai login --api-key` prompts securely for an API key, and
27
- `statelyai login --stdin` reads an API key from standard input. All commands use
28
- stored OAuth credentials or stored API keys. Free accounts open read-only
29
- root-level views.
70
+ `push` creates remote machines for unlinked source machines, updates already
71
+ linked machines, and writes returned IDs into source comments. `pull` updates
72
+ linked files. If `newMachinesDir` is configured, it also creates local files
73
+ for machines that exist only in the Studio project.
74
+
75
+ ## Authentication
30
76
 
31
- 2. Initialize the current repo:
77
+ <!-- authentication flags, environment resolution, and credential storage from src/cli.ts and src/credentials.ts -->
78
+
79
+ Browser OAuth is the default:
32
80
 
33
81
  ```bash
34
- statelyai init
82
+ statelyai login
35
83
  ```
36
84
 
37
- 3. Optionally scan the repo and save suggested source globs:
85
+ Use an API key instead:
38
86
 
39
87
  ```bash
40
- statelyai init --scan
88
+ statelyai login --api-key
89
+ ```
90
+
91
+ Create or manage keys in [Stately API key settings](https://stately.ai/registry/user/my-settings?tab=API+Key).
92
+
93
+ For noninteractive input:
94
+
95
+ ```bash
96
+ printf '%s\n' "$STATELY_API_KEY" | statelyai login --stdin
41
97
  ```
42
98
 
43
- If you have an older `statelyai.json` that still uses the legacy `sources` shape, the CLI will rewrite simple single-source configs automatically when it reads them.
99
+ For automation, prefer an environment variable instead of storing a
100
+ credential. Resolution order is:
101
+
102
+ 1. `STATELY_ACCESS_TOKEN`
103
+ 2. `STATELY_API_KEY`
104
+ 3. `NEXT_PUBLIC_STATELY_API_KEY`
105
+ 4. the credential stored by `statelyai login`
106
+
107
+ The CLI loads `.env.local` from the current directory before resolving these
108
+ variables.
109
+
110
+ Use `statelyai status --auth` to report the selected source without printing
111
+ the credential. `statelyai logout` removes stored credentials but does not modify
112
+ environment variables.
44
113
 
45
- That writes a `statelyai.json` like:
114
+ Stored credentials use macOS Keychain or Linux Secret Service when available,
115
+ with a private config file as fallback. Set `STATELYAI_CREDENTIALS_BACKEND=file`
116
+ to force file storage, or `STATELYAI_CONFIG_DIR` to choose its directory.
117
+
118
+ ## `statelyai.json`
119
+
120
+ <!-- config shape, defaults, and migration behavior from src/projectConfig.ts -->
121
+
122
+ `push` and `pull` use `statelyai.json` from the current directory unless
123
+ `--config` supplies another path.
46
124
 
47
125
  ```json
48
126
  {
@@ -53,34 +131,250 @@ That writes a `statelyai.json` like:
53
131
  "defaultXStateVersion": 5,
54
132
  "include": ["src/**/*.ts"],
55
133
  "exclude": ["**/*.test.*", "**/*.spec.*"],
56
- "newMachinesDir": "src"
134
+ "newMachinesDir": "src/machines"
57
135
  }
58
136
  ```
59
137
 
60
- 4. Push local machines:
138
+ | Field | Purpose |
139
+ | ---------------------- | --------------------------------------------------------------- |
140
+ | `$schema` | Published JSON Schema URL. |
141
+ | `version` | Config format version. Currently `1.0.0`. |
142
+ | `projectId` | Remote Studio project ID. |
143
+ | `studioUrl` | Studio API origin. |
144
+ | `defaultXStateVersion` | XState version used when creating remote machines. Minimum `5`. |
145
+ | `include` | Source globs used by project-wide `push` and `pull`. |
146
+ | `exclude` | Globs removed from discovery. Defaults to tests and specs. |
147
+ | `newMachinesDir` | Destination for remote-only machines created by `pull`. |
148
+
149
+ Project-wide `push` currently discovers machine-bearing JavaScript and
150
+ TypeScript files configured as `xstate` or `auto`. The config schema accepts
151
+ other format names, but project discovery does not push them.
152
+
153
+ Mutating project commands rewrite legacy configs with one `sources` entry to
154
+ the current top-level shape. `status` and `push --dry-run` normalize legacy
155
+ config in memory without writing it. Multiple legacy entries cannot be merged
156
+ safely and require manual migration.
157
+
158
+ ## Commands
159
+
160
+ <!-- command names, arguments, flags, defaults, and behavior from src/cli.ts#COMMANDS -->
161
+
162
+ ### `login`
163
+
164
+ Store an OAuth credential or API key.
165
+
166
+ ```bash
167
+ statelyai login
168
+ statelyai login --api-key
169
+ ```
170
+
171
+ Flags: `--api-key`, `--stdin`, `--base-url`.
172
+
173
+ ### `logout`
174
+
175
+ ```bash
176
+ statelyai logout
177
+ ```
178
+
179
+ `logout` deletes stored credentials. Environment variables are unchanged.
180
+
181
+ ### `status`
182
+
183
+ Inspect the configured project and classify local and remote machines:
184
+
185
+ ```bash
186
+ statelyai status
187
+ ```
188
+
189
+ Statuses are `linked`, `local-only`, `remote-only`, and `missing-remote`.
190
+ Use `--json` for automation or `--auth` to show only credential resolution.
191
+ `status` is read-only and never migrates `statelyai.json`.
192
+
193
+ Flags: `--config <path>`, `--base-url <url>`, `--json`, `--auth`.
194
+
195
+ ### `init`
196
+
197
+ Create or reuse a Studio project and write `statelyai.json`.
198
+
199
+ ```bash
200
+ statelyai init --name "Checkout" --visibility Private --scan
201
+ ```
202
+
203
+ Flags:
204
+
205
+ - `--name <name>` sets the remote project name.
206
+ - `--visibility Private|Public|Unlisted` defaults to `Private`.
207
+ - `--scan` proposes source globs interactively.
208
+ - `--force` replaces an existing config.
209
+ - `--base-url <url>` overrides the Studio API origin.
210
+
211
+ ### `open`
212
+
213
+ Start a local bridge and open one source file in the browser editor:
214
+
215
+ ```bash
216
+ statelyai open src/checkout.machine.ts
217
+ ```
218
+
219
+ Saved file changes refresh the editor. Saving visual edits writes them back to
220
+ the source file.
221
+
222
+ Flags:
223
+
224
+ - `--editor-url <url>` selects the editor origin. Default:
225
+ `https://editor.stately.ai`.
226
+ - `--host <host>` sets the local bridge host. Default: `127.0.0.1`.
227
+ - `--port <port>` selects a port. Default: a random available port.
228
+ - `--no-open` starts the bridge without launching a browser.
229
+ - `--debug` logs editor protocol messages.
230
+
231
+ ### `diff`
232
+
233
+ Compare two locators after normalizing them to graph form:
234
+
235
+ ```bash
236
+ statelyai diff src/checkout.machine.ts machine_123 --fail-on-changes
237
+ ```
238
+
239
+ Locators may be:
240
+
241
+ - a local JavaScript or TypeScript machine file
242
+ - a local XState JSON, Stately graph JSON, or Studio digraph JSON file
243
+ - a Studio machine ID
244
+ - a Studio machine URL
245
+
246
+ `--fail-on-changes` exits with status `1` when the normalized graphs differ.
247
+ Use `--base-url` for remote IDs at another Studio origin. `plan` remains a
248
+ hidden compatibility alias for `diff`.
249
+
250
+ ### `push`
251
+
252
+ Push every discovered machine in `statelyai.json`:
61
253
 
62
254
  ```bash
63
255
  statelyai push
64
256
  ```
65
257
 
66
- Preview what `push` would do without updating Studio or local files:
258
+ Push one file while still using the project and defaults from the config:
259
+
260
+ ```bash
261
+ statelyai push src/checkout.machine.ts
262
+ ```
263
+
264
+ Preview discovery and link/update decisions without a credential:
67
265
 
68
266
  ```bash
69
267
  statelyai push --dry-run
70
268
  ```
71
269
 
72
- If a saved `// @statelyai id=...` points to a deleted or inaccessible remote machine, `push` will prompt to relink the file as a new remote machine and replace the local id.
270
+ `--dry-run` does not create or update machines, write `@statelyai` IDs, or
271
+ migrate legacy configuration.
272
+
273
+ Flags: `--config <path>`, `--base-url <url>`, `--dry-run`.
274
+
275
+ ### `pull`
73
276
 
74
- 5. Pull remote changes back into linked local files and create new local files for remote-only project machines when `newMachinesDir` is configured:
277
+ Pull all linked files from the configured project:
75
278
 
76
279
  ```bash
77
280
  statelyai pull
78
281
  ```
79
282
 
80
- `pull` skips locally modified linked files unless you pass `--force`. New remote-only machines are written as `<machine-name>.machine.ts` inside `newMachinesDir`.
283
+ Pull a linked file using its `@statelyai` ID:
81
284
 
82
- Run it without installing it globally:
285
+ ```bash
286
+ statelyai pull src/checkout.machine.ts
287
+ ```
288
+
289
+ Pull a machine ID or URL to an explicit target:
83
290
 
84
291
  ```bash
85
- npx statelyai --help
292
+ statelyai pull machine_123 src/checkout.machine.ts
293
+ ```
294
+
295
+ New targets support JavaScript/TypeScript, `.digraph.json`, and `.graph.json`.
296
+ For existing JSON targets, the current file shape determines the output format.
297
+
298
+ Project-wide pull skips linked files with uncommitted Git changes. Pass
299
+ `--force` to overwrite them. Remote-only machines are skipped until
300
+ `newMachinesDir` is set.
301
+
302
+ Flags: `--config <path>`, `--base-url <url>`, `--force`.
303
+
304
+ ## CI examples
305
+
306
+ <!-- noninteractive and exit-code behavior from credential resolution, DiffCommand, and PushCommand -->
307
+
308
+ Fail when a local machine differs from Studio:
309
+
310
+ ```bash
311
+ npx statelyai diff src/checkout.machine.ts machine_123 --fail-on-changes
312
+ ```
313
+
314
+ Provide `STATELY_API_KEY` or `STATELY_ACCESS_TOKEN` through the CI runner's
315
+ secret environment.
316
+
317
+ Check project discovery without network credentials or writes:
318
+
319
+ ```bash
320
+ npx statelyai push --dry-run
321
+ ```
322
+
323
+ Set `NO_COLOR=1` or `CI=true` for plain output.
324
+
325
+ ## Common problems
326
+
327
+ <!-- error and safeguard behavior from src/cli.ts and src/projectConfig.ts -->
328
+
329
+ - **The server returns `401`:** run `statelyai login`, or set
330
+ `STATELY_ACCESS_TOKEN` or `STATELY_API_KEY`.
331
+ - **`push` finds no files:** check `include` and `exclude`; `init` without
332
+ `--scan` intentionally leaves `include` empty.
333
+ - **`push` matches files but finds no machines:** project discovery currently
334
+ requires XState imports plus `createMachine(...)` or
335
+ `setup(...).createMachine(...)`.
336
+ - **Remote-only machines are skipped:** set `newMachinesDir`, then run
337
+ `statelyai pull` again.
338
+ - **`pull` refuses to overwrite a file:** commit or stash its changes, or pass
339
+ `--force` if overwriting is intentional.
340
+ - **A linked remote machine was deleted or is inaccessible:** interactive
341
+ `push` offers to relink it as a new remote machine and replaces the local ID.
342
+
343
+ Run `statelyai <command> --help` for generated command syntax and flags.
344
+
345
+ ## Self-hosting
346
+
347
+ <!-- self-host auth and URL behavior from LoginCommand, baseUrlFlags, OpenCommand, credential resolution, and projectConfig.ts -->
348
+
349
+ Skip login when the server has authentication disabled. The CLI sends no
350
+ authorization header when no credential exists; the server decides whether
351
+ authentication is required. This also applies to `open` editor-sync requests
352
+ and CI commands.
353
+
354
+ For OAuth, point login at the deployment's protected resource:
355
+
356
+ ```bash
357
+ statelyai login --base-url https://editor.example.com/api/mcp
358
+ ```
359
+
360
+ Passing only an origin uses its `/api/mcp` resource. The resource must
361
+ advertise an authorization server. If that server does not support dynamic
362
+ client registration, set `STATELY_OAUTH_CLIENT_ID`.
363
+
364
+ URL settings are intentionally scoped:
365
+
366
+ | Setting | Used by | Meaning |
367
+ | ------------------ | ---------------------------------------- | ---------------------------------------------------------- |
368
+ | `--base-url` | `init`, `status`, `diff`, `push`, `pull` | Studio API origin. |
369
+ | `studioUrl` | `statelyai.json` project sync | Default Studio API origin for that project. |
370
+ | `login --base-url` | `login` only | OAuth protected-resource URL used for discovery. |
371
+ | `--editor-url` | `open` only | Visual editor origin. Default `https://editor.stately.ai`. |
372
+
373
+ A complete setup may look like:
374
+
375
+ ```bash
376
+ statelyai login --base-url https://editor.example.com/api/mcp
377
+ statelyai init --base-url https://studio.example.com --scan
378
+ statelyai open src/checkout.machine.ts \
379
+ --editor-url https://editor.example.com
86
380
  ```
package/dist/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _ as run } from "./cli-DR7_Qkzm.mjs";
2
+ import { b as run } from "./cli-DRKY520A.mjs";
3
3
 
4
4
  //#region src/bin.ts
5
5
  run();
@@ -1,7 +1,6 @@
1
1
  import * as crypto from "node:crypto";
2
2
  import fs, { promises, watch } from "node:fs";
3
3
  import fsPromises from "node:fs/promises";
4
- import * as http from "node:http";
5
4
  import { execFile, execFileSync, spawn } from "node:child_process";
6
5
  import * as path$1 from "node:path";
7
6
  import path from "node:path";
@@ -10,6 +9,7 @@ import { Writable } from "node:stream";
10
9
  import { fileURLToPath, pathToFileURL } from "node:url";
11
10
  import { promisify } from "node:util";
12
11
  import { Args, Command, Flags, flush, handle, run } from "@oclif/core";
12
+ import * as http from "node:http";
13
13
  import os from "node:os";
14
14
  import { StudioApiError, buildAuthorizeUrl, createPkcePair, createStatelyClient, discoverAuthorizationServer, discoverProtectedResource, exchangeCodeForToken, getStatelyPragma, registerOAuthClient } from "@statelyai/sdk";
15
15
  import { planSync, pullSync, pushLocalMachineLinks } from "@statelyai/sdk/sync";
@@ -698,6 +698,51 @@ function escapeAttribute(value) {
698
698
  return value.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
699
699
  }
700
700
 
701
+ //#endregion
702
+ //#region src/oauthCallbackServer.ts
703
+ async function createLocalOAuthCallbackServer(options) {
704
+ let resolveCode;
705
+ let rejectCode;
706
+ const codePromise = new Promise((resolve, reject) => {
707
+ resolveCode = resolve;
708
+ rejectCode = reject;
709
+ });
710
+ const server = http.createServer((req, res) => {
711
+ const requestUrl = new URL(req.url ?? "/", "http://127.0.0.1");
712
+ const code = requestUrl.searchParams.get("code");
713
+ const state = requestUrl.searchParams.get("state");
714
+ const error = requestUrl.searchParams.get("error");
715
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
716
+ res.setHeader("Connection", "close");
717
+ if (error) {
718
+ res.statusCode = 400;
719
+ res.end("<!doctype html><title>Sign in failed</title><p>Sign in failed. Return to the terminal.</p>");
720
+ rejectCode(new Error(error));
721
+ return;
722
+ }
723
+ if (!code || state !== options.state) {
724
+ res.statusCode = 400;
725
+ res.end("<!doctype html><title>Sign in failed</title><p>Invalid sign-in callback. Return to the terminal.</p>");
726
+ rejectCode(/* @__PURE__ */ new Error("Invalid OAuth callback."));
727
+ return;
728
+ }
729
+ res.end("<!doctype html><title>Signed in</title><p>Signed in. You can close this window.</p>");
730
+ resolveCode(code);
731
+ });
732
+ await new Promise((resolve) => {
733
+ server.listen(0, "127.0.0.1", resolve);
734
+ });
735
+ const address = server.address();
736
+ if (!address || typeof address === "string") throw new Error("Could not start OAuth callback server.");
737
+ return {
738
+ redirectUri: `http://127.0.0.1:${address.port}/callback`,
739
+ waitForCode: () => codePromise,
740
+ close: () => new Promise((resolve) => {
741
+ server.close(() => resolve());
742
+ })
743
+ };
744
+ }
745
+
701
746
  //#endregion
702
747
  //#region src/credentials.ts
703
748
  const execFile$1 = promisify(execFile);
@@ -787,13 +832,10 @@ async function writeFileCredentials(apiKey) {
787
832
  location: stored.location
788
833
  };
789
834
  }
790
- async function writeFileStoredCredential(credential, authMode) {
835
+ async function writeFileStoredCredential(credential) {
791
836
  const configDir = getConfigDir();
792
837
  const credentialsPath = getCredentialsFilePath();
793
- const payload = authMode === "none" ? {
794
- version: 1,
795
- authMode: "none"
796
- } : {
838
+ const payload = {
797
839
  version: 1,
798
840
  credential
799
841
  };
@@ -808,7 +850,6 @@ async function writeFileStoredCredential(credential, authMode) {
808
850
  await promises.chmod(credentialsPath, 384);
809
851
  return {
810
852
  ...credential ? { credential } : {},
811
- ...authMode ? { authMode } : {},
812
853
  backend: "file",
813
854
  location: credentialsPath
814
855
  };
@@ -848,12 +889,9 @@ async function writeMacOSKeychain(apiKey) {
848
889
  location: stored.location
849
890
  } : void 0;
850
891
  }
851
- async function writeMacOSStoredCredential(credential, authMode) {
892
+ async function writeMacOSStoredCredential(credential) {
852
893
  if (process.platform !== "darwin") return;
853
- const payload = authMode === "none" ? {
854
- version: 1,
855
- authMode: "none"
856
- } : {
894
+ const payload = {
857
895
  version: 1,
858
896
  credential
859
897
  };
@@ -870,7 +908,6 @@ async function writeMacOSStoredCredential(credential, authMode) {
870
908
  ]);
871
909
  return {
872
910
  ...credential ? { credential } : {},
873
- ...authMode ? { authMode } : {},
874
911
  backend: "macos-keychain",
875
912
  location: "macOS Keychain"
876
913
  };
@@ -919,12 +956,9 @@ async function writeLinuxSecretTool(apiKey) {
919
956
  location: stored.location
920
957
  } : void 0;
921
958
  }
922
- async function writeLinuxStoredCredential(credential, authMode) {
959
+ async function writeLinuxStoredCredential(credential) {
923
960
  if (process.platform !== "linux") return;
924
- const payload = authMode === "none" ? {
925
- version: 1,
926
- authMode: "none"
927
- } : {
961
+ const payload = {
928
962
  version: 1,
929
963
  credential
930
964
  };
@@ -940,7 +974,6 @@ async function writeLinuxStoredCredential(credential, authMode) {
940
974
  ], { input: JSON.stringify(payload) });
941
975
  return {
942
976
  ...credential ? { credential } : {},
943
- ...authMode ? { authMode } : {},
944
977
  backend: "linux-secret-tool",
945
978
  location: "Secret Service (secret-tool)"
946
979
  };
@@ -975,10 +1008,6 @@ async function setStoredCredential(credential) {
975
1008
  if (shouldUseFileBackendOnly()) return writeFileStoredCredential(credential);
976
1009
  return await writeMacOSStoredCredential(credential) ?? await writeLinuxStoredCredential(credential) ?? await writeFileStoredCredential(credential);
977
1010
  }
978
- async function setStoredNoAuth() {
979
- if (shouldUseFileBackendOnly()) return writeFileStoredCredential(void 0, "none");
980
- return await writeMacOSStoredCredential(void 0, "none") ?? await writeLinuxStoredCredential(void 0, "none") ?? await writeFileStoredCredential(void 0, "none");
981
- }
982
1011
  async function deleteStoredApiKey() {
983
1012
  if (shouldUseFileBackendOnly()) {
984
1013
  const deleted = await deleteFileCredentials();
@@ -1511,47 +1540,6 @@ function createOAuthCredential(tokenResponse, now = Date.now()) {
1511
1540
  ...expiresAt ? { expiresAt } : {}
1512
1541
  };
1513
1542
  }
1514
- async function createLocalOAuthCallbackServer(options) {
1515
- let resolveCode;
1516
- let rejectCode;
1517
- const codePromise = new Promise((resolve, reject) => {
1518
- resolveCode = resolve;
1519
- rejectCode = reject;
1520
- });
1521
- const server = http.createServer((req, res) => {
1522
- const requestUrl = new URL(req.url ?? "/", "http://127.0.0.1");
1523
- const code = requestUrl.searchParams.get("code");
1524
- const state = requestUrl.searchParams.get("state");
1525
- const error = requestUrl.searchParams.get("error");
1526
- res.setHeader("Content-Type", "text/html; charset=utf-8");
1527
- if (error) {
1528
- res.statusCode = 400;
1529
- res.end("<!doctype html><title>Sign in failed</title><p>Sign in failed. Return to the terminal.</p>");
1530
- rejectCode(new Error(error));
1531
- return;
1532
- }
1533
- if (!code || state !== options.state) {
1534
- res.statusCode = 400;
1535
- res.end("<!doctype html><title>Sign in failed</title><p>Invalid sign-in callback. Return to the terminal.</p>");
1536
- rejectCode(/* @__PURE__ */ new Error("Invalid OAuth callback."));
1537
- return;
1538
- }
1539
- res.end("<!doctype html><title>Signed in</title><p>Signed in. You can close this window.</p>");
1540
- resolveCode(code);
1541
- });
1542
- await new Promise((resolve) => {
1543
- server.listen(0, "127.0.0.1", resolve);
1544
- });
1545
- const address = server.address();
1546
- if (!address || typeof address === "string") throw new Error("Could not start OAuth callback server.");
1547
- return {
1548
- redirectUri: `http://127.0.0.1:${address.port}/callback`,
1549
- waitForCode: () => codePromise,
1550
- close: () => new Promise((resolve) => {
1551
- server.close(() => resolve());
1552
- })
1553
- };
1554
- }
1555
1543
  async function buildOAuthLoginStart(options) {
1556
1544
  const [discovery, pkce] = await Promise.all([discoverOAuthLogin({
1557
1545
  baseUrl: options.baseUrl,
@@ -1674,9 +1662,6 @@ function formatMissingNewMachineDirMessage(options) {
1674
1662
  function toMachineFileStem(machineName) {
1675
1663
  return (typeof machineName === "string" ? machineName : "").trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "machine";
1676
1664
  }
1677
- function getMissingCredentialMessage() {
1678
- return `No Stately credential configured. Run \`statelyai login\`, set \`STATELY_ACCESS_TOKEN\`, or set \`STATELY_API_KEY\`.\nGet or create an API key at ${STATELY_API_KEY_SETTINGS_URL}`;
1679
- }
1680
1665
  function buildProjectEditorUrl(studioUrl, projectId) {
1681
1666
  return `${studioUrl.replace(/\/+$/, "")}/registry/editor/${encodeURIComponent(projectId)}`;
1682
1667
  }
@@ -1892,7 +1877,7 @@ async function resolveConfiguredProject(options) {
1892
1877
  const { config, configPath, rootDir, rewriteStatus } = await readStatelyProjectConfig({
1893
1878
  cwd: options.cwd,
1894
1879
  configPath: options.configPath,
1895
- rewriteIfNeeded: true
1880
+ rewriteIfNeeded: options.rewriteIfNeeded ?? true
1896
1881
  });
1897
1882
  const studioUrl = options.baseUrl ?? config.studioUrl;
1898
1883
  return {
@@ -1938,13 +1923,102 @@ async function resolveConfiguredPullTargets(options) {
1938
1923
  rewriteStatus
1939
1924
  };
1940
1925
  }
1941
- const sharedFlags = {
1926
+ async function resolveCredentialStatus() {
1927
+ const envCredential = getEnvCredential();
1928
+ if (envCredential) return {
1929
+ detail: envCredential.variable,
1930
+ source: "environment",
1931
+ type: envCredential.credential.type
1932
+ };
1933
+ const storedCredential = await getStoredCredential();
1934
+ if (storedCredential?.credential) return {
1935
+ detail: describeCredentialBackend(storedCredential.backend, storedCredential.location),
1936
+ source: "stored",
1937
+ type: storedCredential.credential.type
1938
+ };
1939
+ return {
1940
+ detail: "server decides whether authentication is required",
1941
+ source: "none",
1942
+ type: "none"
1943
+ };
1944
+ }
1945
+ function getProjectMachineId(machine) {
1946
+ const candidate = machine;
1947
+ return candidate.machineId || candidate.id;
1948
+ }
1949
+ function formatCredentialStatus(status) {
1950
+ return status.type === "none" ? `Auth: none (${status.detail})` : `Auth: ${status.type} from ${status.source} (${status.detail})`;
1951
+ }
1952
+ async function resolveProjectStatus(options = {}) {
1953
+ const { client, config, configPath, files } = await resolveConfiguredProject({
1954
+ ...options,
1955
+ rewriteIfNeeded: false
1956
+ });
1957
+ const project = await client.projects.get(config.projectId);
1958
+ const { machineFiles } = await classifyPushCandidates(files.filter(supportsMachineDiscovery));
1959
+ const linkedTargets = await discoverLinkedPullTargets(machineFiles);
1960
+ const linkedByPath = new Map(linkedTargets.map((target) => [target.file.filePath, target]));
1961
+ const remoteById = new Map(project.machines.flatMap((machine) => {
1962
+ const id = getProjectMachineId(machine);
1963
+ return id ? [[id, machine]] : [];
1964
+ }));
1965
+ const linkedIds = new Set(linkedTargets.map((target) => target.machineId));
1966
+ const studioUrl = options.baseUrl ?? config.studioUrl;
1967
+ const machines = machineFiles.map((file) => {
1968
+ const linked = linkedByPath.get(file.filePath);
1969
+ if (!linked) return {
1970
+ name: path.basename(file.relativePath),
1971
+ path: file.relativePath,
1972
+ status: "local-only"
1973
+ };
1974
+ const remote = remoteById.get(linked.machineId);
1975
+ return {
1976
+ id: linked.machineId,
1977
+ name: remote?.name || path.basename(file.relativePath),
1978
+ path: file.relativePath,
1979
+ status: remote ? "linked" : "missing-remote",
1980
+ url: buildMachineEditorUrl(studioUrl, config.projectId, linked.machineId)
1981
+ };
1982
+ });
1983
+ for (const remote of project.machines) {
1984
+ const id = getProjectMachineId(remote);
1985
+ if (!id || linkedIds.has(id)) continue;
1986
+ machines.push({
1987
+ id,
1988
+ name: remote.name || id,
1989
+ status: "remote-only",
1990
+ url: buildMachineEditorUrl(studioUrl, config.projectId, id)
1991
+ });
1992
+ }
1993
+ machines.sort((left, right) => left.status.localeCompare(right.status) || left.name.localeCompare(right.name));
1994
+ return {
1995
+ auth: await resolveCredentialStatus(),
1996
+ configPath,
1997
+ counts: {
1998
+ linked: machines.filter((machine) => machine.status === "linked").length,
1999
+ "local-only": machines.filter((machine) => machine.status === "local-only").length,
2000
+ "missing-remote": machines.filter((machine) => machine.status === "missing-remote").length,
2001
+ "remote-only": machines.filter((machine) => machine.status === "remote-only").length
2002
+ },
2003
+ machines,
2004
+ project: {
2005
+ id: config.projectId,
2006
+ ...project.name ? { name: project.name } : {},
2007
+ url: buildProjectEditorUrl(studioUrl, config.projectId)
2008
+ },
2009
+ studioUrl
2010
+ };
2011
+ }
2012
+ const baseUrlFlags = {
1942
2013
  help: Flags.help({ char: "h" }),
2014
+ "base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" })
2015
+ };
2016
+ const sharedFlags = {
2017
+ ...baseUrlFlags,
1943
2018
  "fail-on-changes": Flags.boolean({
1944
2019
  default: false,
1945
2020
  description: "Exit with a nonzero code when differences are found"
1946
- }),
1947
- "base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" })
2021
+ })
1948
2022
  };
1949
2023
  function formatChangeList(label, items) {
1950
2024
  if (items.length === 0) return null;
@@ -1992,19 +2066,22 @@ var ParsedSyncCommand = class extends BaseSyncCommand {
1992
2066
  return this.parse(command);
1993
2067
  }
1994
2068
  };
2069
+ async function createSemanticDiff(source, target, baseUrl) {
2070
+ return planSync({
2071
+ source,
2072
+ target,
2073
+ apiKey: (await resolveApiKey()).apiKey,
2074
+ baseUrl: baseUrl ?? getDefaultBaseUrl()
2075
+ });
2076
+ }
1995
2077
  var PlanCommand = class PlanCommand extends ParsedSyncCommand {
2078
+ static hidden = true;
1996
2079
  static summary = "Plan semantic sync changes between a source and target.";
1997
2080
  static description = "Resolves the source and target, normalizes both into graph form, and prints a semantic change summary.";
1998
2081
  static args = sharedArgs;
1999
2082
  async run() {
2000
2083
  const { args, flags } = await this.parseSync(PlanCommand);
2001
- const apiKey = (await resolveApiKey()).apiKey;
2002
- const plan = await planSync({
2003
- source: args.source,
2004
- target: args.target,
2005
- apiKey,
2006
- baseUrl: flags["base-url"] ?? getDefaultBaseUrl()
2007
- });
2084
+ const plan = await createSemanticDiff(args.source, args.target, flags["base-url"]);
2008
2085
  this.log(formatPlanSummary(plan));
2009
2086
  this.exit(flags["fail-on-changes"] && plan.summary.hasChanges ? 1 : 0);
2010
2087
  }
@@ -2015,13 +2092,7 @@ var DiffCommand = class DiffCommand extends ParsedSyncCommand {
2015
2092
  static args = sharedArgs;
2016
2093
  async run() {
2017
2094
  const { args, flags } = await this.parseSync(DiffCommand);
2018
- const apiKey = (await resolveApiKey()).apiKey;
2019
- const plan = await planSync({
2020
- source: args.source,
2021
- target: args.target,
2022
- apiKey,
2023
- baseUrl: flags["base-url"] ?? getDefaultBaseUrl()
2024
- });
2095
+ const plan = await createSemanticDiff(args.source, args.target, flags["base-url"]);
2025
2096
  this.log(formatPlanSummary(plan));
2026
2097
  this.exit(flags["fail-on-changes"] && plan.summary.hasChanges ? 1 : 0);
2027
2098
  }
@@ -2030,7 +2101,7 @@ var PullCommand = class PullCommand extends ParsedSyncCommand {
2030
2101
  static summary = "Pull a source locator into a local target file.";
2031
2102
  static description = "Resolves the source, materializes it in the target format inferred from the target file, and writes the result locally. With no arguments, pulls all linked files discovered from statelyai.json.";
2032
2103
  static flags = {
2033
- ...sharedFlags,
2104
+ ...baseUrlFlags,
2034
2105
  config: Flags.string({ description: "Path to statelyai.json" }),
2035
2106
  force: Flags.boolean({
2036
2107
  description: "Overwrite linked files even when they have local git changes",
@@ -2051,7 +2122,6 @@ var PullCommand = class PullCommand extends ParsedSyncCommand {
2051
2122
  const { args, flags } = await this.parseSync(PullCommand);
2052
2123
  const apiKey = (await resolveApiKey()).apiKey;
2053
2124
  if (!args.source) {
2054
- if (!apiKey) this.error(getMissingCredentialMessage());
2055
2125
  this.log(colorize("Resolving configured project and linked source files...", ANSI_CYAN));
2056
2126
  const { config, linkedTargets, project, remoteOnlyTargets, skipped, rewriteStatus } = await resolveConfiguredPullTargets({
2057
2127
  apiKey,
@@ -2130,7 +2200,6 @@ var PullCommand = class PullCommand extends ParsedSyncCommand {
2130
2200
  target = localCandidate;
2131
2201
  }
2132
2202
  if (!target) this.error("Missing target path. Pass `statelyai pull <machine-id|url> <file>` or `statelyai pull <linked-file>`.");
2133
- if (!apiKey) this.error(getMissingCredentialMessage());
2134
2203
  if (!flags.force && await fileExists(target) && await isFileGitDirty(target)) this.error(`Refusing to overwrite locally modified file ${target}. Pass --force to overwrite it.`);
2135
2204
  this.log(colorize(`Pulling ${source} into ${target}...`, ANSI_CYAN));
2136
2205
  const result = await pullSync({
@@ -2162,12 +2231,11 @@ var PushCommand = class PushCommand extends Command {
2162
2231
  async run() {
2163
2232
  const { args, flags } = await this.parse(PushCommand);
2164
2233
  const resolvedApiKey = flags["dry-run"] ? { source: "missing" } : await resolveApiKey();
2165
- if (!flags["dry-run"] && !resolvedApiKey.apiKey) this.error(getMissingCredentialMessage());
2166
2234
  this.log(colorize("Resolving configured project and source files...", ANSI_CYAN));
2167
2235
  const { client, config, files, rewriteStatus } = flags["dry-run"] ? await (async () => {
2168
2236
  const { config, rootDir, rewriteStatus } = await readStatelyProjectConfig({
2169
2237
  configPath: flags.config,
2170
- rewriteIfNeeded: true
2238
+ rewriteIfNeeded: false
2171
2239
  });
2172
2240
  return {
2173
2241
  client: void 0,
@@ -2300,7 +2368,6 @@ var OpenCommand = class OpenCommand extends Command {
2300
2368
  async run() {
2301
2369
  const { args, flags } = await this.parse(OpenCommand);
2302
2370
  const apiKey = (await resolveApiKey()).apiKey;
2303
- if (!apiKey) throw new Error("No Stately credential configured. Run `statelyai login` and retry.");
2304
2371
  await openEditor({
2305
2372
  fileName: path.resolve(args.file),
2306
2373
  editorUrl: flags["editor-url"] ?? getDefaultEditorUrl(),
@@ -2341,7 +2408,6 @@ var InitCommand = class InitCommand extends Command {
2341
2408
  async run() {
2342
2409
  const { flags } = await this.parse(InitCommand);
2343
2410
  const resolvedApiKey = await resolveApiKey();
2344
- if (!resolvedApiKey.apiKey) this.error(getMissingCredentialMessage());
2345
2411
  this.log("Creating or reusing remote project...");
2346
2412
  const result = await initProject({
2347
2413
  apiKey: resolvedApiKey.apiKey,
@@ -2393,21 +2459,10 @@ var LoginCommand = class LoginCommand extends Command {
2393
2459
  stdin: Flags.boolean({
2394
2460
  description: "Read the API key from standard input",
2395
2461
  default: false
2396
- }),
2397
- auth: Flags.string({
2398
- description: "Authentication mode for the target server",
2399
- options: ["none", "bearer"]
2400
2462
  })
2401
2463
  };
2402
2464
  async run() {
2403
2465
  const { flags } = await this.parse(LoginCommand);
2404
- if ((flags["api-key"] || flags.stdin) && flags.auth === "bearer") this.error("Pass either --auth bearer or API-key login flags, not both.");
2405
- if (flags.auth === "none") {
2406
- if (flags.stdin || flags["api-key"]) this.error("Pass either --auth none or an API key, not both.");
2407
- const stored = await setStoredNoAuth();
2408
- this.log(`Stored no-auth mode in ${describeCredentialBackend(stored.backend, stored.location)}.`);
2409
- return;
2410
- }
2411
2466
  if (!flags["api-key"] && !flags.stdin) {
2412
2467
  const stored = await runOAuthBrowserLogin({
2413
2468
  baseUrl: flags["base-url"],
@@ -2437,29 +2492,64 @@ var LogoutCommand = class extends Command {
2437
2492
  this.log(`Removed stored credential from ${result.locations.join(", ")}.`);
2438
2493
  }
2439
2494
  };
2495
+ function formatProjectStatus(result) {
2496
+ const lines = [
2497
+ formatCredentialStatus(result.auth),
2498
+ `Project: ${result.project.name ? `${result.project.name} ` : ""}(${result.project.id})`,
2499
+ `Studio: ${result.project.url}`,
2500
+ `Config: ${result.configPath}`,
2501
+ `Machines: ${result.machines.length} (${result.counts.linked} linked, ${result.counts["local-only"]} local-only, ${result.counts["remote-only"]} remote-only, ${result.counts["missing-remote"]} missing-remote)`
2502
+ ];
2503
+ for (const machine of result.machines) {
2504
+ const details = [machine.id, machine.path].filter(Boolean).join(" ");
2505
+ lines.push(` [${machine.status}] ${machine.name}${details ? ` ${details}` : ""}`);
2506
+ }
2507
+ const next = [];
2508
+ if (result.counts["local-only"] > 0 || result.counts["missing-remote"] > 0) next.push("statelyai push");
2509
+ if (result.counts["remote-only"] > 0) next.push("statelyai pull");
2510
+ if (next.length > 0) lines.push(`Next: ${next.join(", ")}`);
2511
+ return lines.join("\n");
2512
+ }
2513
+ var StatusCommand = class StatusCommand extends Command {
2514
+ static enableJsonFlag = false;
2515
+ static summary = "Show project, machine, and authentication status.";
2516
+ static description = "Reads statelyai.json and Studio project metadata, then classifies configured machine files as linked, local-only, remote-only, or missing remotely.";
2517
+ static flags = {
2518
+ ...baseUrlFlags,
2519
+ auth: Flags.boolean({
2520
+ description: "Show only credential resolution",
2521
+ default: false
2522
+ }),
2523
+ config: Flags.string({ description: "Path to statelyai.json" }),
2524
+ json: Flags.boolean({
2525
+ description: "Print machine-readable JSON",
2526
+ default: false
2527
+ })
2528
+ };
2529
+ async run() {
2530
+ const { flags } = await this.parse(StatusCommand);
2531
+ if (flags.auth) {
2532
+ const auth = await resolveCredentialStatus();
2533
+ this.log(flags.json ? JSON.stringify({ auth }, null, 2) : formatCredentialStatus(auth));
2534
+ return;
2535
+ }
2536
+ const apiKey = (await resolveApiKey()).apiKey;
2537
+ const result = await resolveProjectStatus({
2538
+ apiKey,
2539
+ baseUrl: flags["base-url"],
2540
+ configPath: flags.config
2541
+ });
2542
+ this.log(flags.json ? JSON.stringify(result, null, 2) : formatProjectStatus(result));
2543
+ }
2544
+ };
2440
2545
  var WhoamiCommand = class extends Command {
2546
+ static hidden = true;
2441
2547
  static enableJsonFlag = false;
2442
2548
  static summary = "Show how the CLI would resolve credentials.";
2443
2549
  static description = "Reports whether the CLI would use an environment variable or stored credential.";
2444
2550
  static flags = { help: Flags.help({ char: "h" }) };
2445
2551
  async run() {
2446
- const envCredential = getEnvCredential();
2447
- const storedCredential = await getStoredCredential();
2448
- const storedApiKey = storedCredential?.credential?.type === "api_key" ? {
2449
- apiKey: storedCredential.credential.token,
2450
- backend: storedCredential.backend,
2451
- location: storedCredential.location
2452
- } : void 0;
2453
- if (envCredential) {
2454
- this.log(`Credential source: environment ${envCredential.credential.type} (${envCredential.variable}).`);
2455
- if (storedApiKey) this.log(`Stored credential also available in ${describeCredentialBackend(storedApiKey.backend, storedApiKey.location)}.`);
2456
- return;
2457
- }
2458
- if (storedCredential) {
2459
- this.log(formatStoredCredentialSource(storedCredential));
2460
- return;
2461
- }
2462
- this.log(getMissingCredentialMessage());
2552
+ this.log(formatCredentialStatus(await resolveCredentialStatus()));
2463
2553
  }
2464
2554
  };
2465
2555
  const COMMANDS = {
@@ -2468,6 +2558,7 @@ const COMMANDS = {
2468
2558
  pull: PullCommand,
2469
2559
  push: PushCommand,
2470
2560
  open: OpenCommand,
2561
+ status: StatusCommand,
2471
2562
  init: InitCommand,
2472
2563
  login: LoginCommand,
2473
2564
  logout: LogoutCommand,
@@ -2495,4 +2586,4 @@ function isDirectExecution() {
2495
2586
  if (isDirectExecution()) run$1();
2496
2587
 
2497
2588
  //#endregion
2498
- export { run$1 as _, discoverOAuthLogin as a, formatPlanSummary as c, getEnvCredential as d, inferInitProjectName as f, resolveConfiguredPullTargets as g, resolveApiKey as h, discoverLinkedPullTargets as i, formatStoredCredentialSource as l, isFileGitDirty as m, buildOAuthLoginStart as n, discoverRemoteProjectMachineTargets as o, initProject as p, classifyPushCandidates as r, formatMissingNewMachineDirMessage as s, COMMANDS as t, getEnvApiKey as u, scanProjectSources as v, createStatelyProjectConfig as y };
2589
+ export { createStatelyProjectConfig as S, resolveConfiguredPullTargets as _, discoverOAuthLogin as a, run$1 as b, formatPlanSummary as c, getEnvApiKey as d, getEnvCredential as f, resolveApiKey as g, isFileGitDirty as h, discoverLinkedPullTargets as i, formatProjectStatus as l, initProject as m, buildOAuthLoginStart as n, discoverRemoteProjectMachineTargets as o, inferInitProjectName as p, classifyPushCandidates as r, formatMissingNewMachineDirMessage as s, COMMANDS as t, formatStoredCredentialSource as u, resolveCredentialStatus as v, scanProjectSources as x, resolveProjectStatus as y };
package/dist/index.d.mts CHANGED
@@ -51,7 +51,7 @@ declare function createStatelyProjectConfig(options: {
51
51
  //#endregion
52
52
  //#region src/cli.d.ts
53
53
  interface InitProjectOptions {
54
- apiKey: string;
54
+ apiKey?: string;
55
55
  baseUrl?: string;
56
56
  cwd?: string;
57
57
  client?: StudioClient;
@@ -97,6 +97,31 @@ interface ResolvedConfiguredPullTargets {
97
97
  reason?: string;
98
98
  };
99
99
  }
100
+ type ProjectMachineStatus = 'linked' | 'local-only' | 'missing-remote' | 'remote-only';
101
+ interface ProjectStatusMachine {
102
+ id?: string;
103
+ name: string;
104
+ path?: string;
105
+ status: ProjectMachineStatus;
106
+ url?: string;
107
+ }
108
+ interface CredentialStatus {
109
+ detail: string;
110
+ source: 'environment' | 'none' | 'stored';
111
+ type: 'api_key' | 'none' | 'oauth';
112
+ }
113
+ interface ProjectStatusResult {
114
+ auth: CredentialStatus;
115
+ configPath: string;
116
+ counts: Record<ProjectMachineStatus, number>;
117
+ machines: ProjectStatusMachine[];
118
+ project: {
119
+ id: string;
120
+ name?: string;
121
+ url: string;
122
+ };
123
+ studioUrl: string;
124
+ }
100
125
  type ApiKeyResolution = {
101
126
  apiKey: string;
102
127
  detail: string;
@@ -162,23 +187,31 @@ declare function classifyPushCandidates(files: DiscoveredSourceFile[]): Promise<
162
187
  declare function resolveConfiguredPullTargets(options: {
163
188
  cwd?: string;
164
189
  configPath?: string;
165
- apiKey: string;
190
+ apiKey?: string;
166
191
  baseUrl?: string;
167
192
  client?: StudioClient;
168
193
  }): Promise<ResolvedConfiguredPullTargets>;
194
+ declare function resolveCredentialStatus(): Promise<CredentialStatus>;
195
+ declare function resolveProjectStatus(options?: {
196
+ apiKey?: string;
197
+ baseUrl?: string;
198
+ client?: StudioClient;
199
+ configPath?: string;
200
+ cwd?: string;
201
+ }): Promise<ProjectStatusResult>;
169
202
  declare function formatPlanSummary(plan: SyncPlan): string;
170
203
  declare abstract class BaseSyncCommand extends Command {
171
204
  static enableJsonFlag: boolean;
172
205
  static flags: {
173
- help: _oclif_core_interfaces0.BooleanFlag<void>;
174
206
  'fail-on-changes': _oclif_core_interfaces0.BooleanFlag<boolean>;
207
+ help: _oclif_core_interfaces0.BooleanFlag<void>;
175
208
  'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
176
209
  };
177
210
  }
178
211
  declare abstract class ParsedSyncCommand extends BaseSyncCommand {
179
212
  protected parseSync<T extends typeof BaseSyncCommand>(command: T): Promise<_oclif_core_interfaces0.ParserOutput<{
180
- help: void;
181
213
  'fail-on-changes': boolean;
214
+ help: void;
182
215
  'base-url': string | undefined;
183
216
  }, {
184
217
  [flag: string]: any;
@@ -187,6 +220,7 @@ declare abstract class ParsedSyncCommand extends BaseSyncCommand {
187
220
  }>>;
188
221
  }
189
222
  declare class PlanCommand extends ParsedSyncCommand {
223
+ static hidden: boolean;
190
224
  static summary: string;
191
225
  static description: string;
192
226
  static args: {
@@ -211,7 +245,6 @@ declare class PullCommand extends ParsedSyncCommand {
211
245
  config: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
212
246
  force: _oclif_core_interfaces0.BooleanFlag<boolean>;
213
247
  help: _oclif_core_interfaces0.BooleanFlag<void>;
214
- 'fail-on-changes': _oclif_core_interfaces0.BooleanFlag<boolean>;
215
248
  'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
216
249
  };
217
250
  static args: {
@@ -275,7 +308,6 @@ declare class LoginCommand extends Command {
275
308
  'api-key': _oclif_core_interfaces0.BooleanFlag<boolean>;
276
309
  'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
277
310
  stdin: _oclif_core_interfaces0.BooleanFlag<boolean>;
278
- auth: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
279
311
  };
280
312
  run(): Promise<void>;
281
313
  }
@@ -288,7 +320,22 @@ declare class LogoutCommand extends Command {
288
320
  };
289
321
  run(): Promise<void>;
290
322
  }
323
+ declare function formatProjectStatus(result: ProjectStatusResult): string;
324
+ declare class StatusCommand extends Command {
325
+ static enableJsonFlag: boolean;
326
+ static summary: string;
327
+ static description: string;
328
+ static flags: {
329
+ auth: _oclif_core_interfaces0.BooleanFlag<boolean>;
330
+ config: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
331
+ json: _oclif_core_interfaces0.BooleanFlag<boolean>;
332
+ help: _oclif_core_interfaces0.BooleanFlag<void>;
333
+ 'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
334
+ };
335
+ run(): Promise<void>;
336
+ }
291
337
  declare class WhoamiCommand extends Command {
338
+ static hidden: boolean;
292
339
  static enableJsonFlag: boolean;
293
340
  static summary: string;
294
341
  static description: string;
@@ -303,6 +350,7 @@ declare const COMMANDS: {
303
350
  pull: typeof PullCommand;
304
351
  push: typeof PushCommand;
305
352
  open: typeof OpenCommand;
353
+ status: typeof StatusCommand;
306
354
  init: typeof InitCommand;
307
355
  login: typeof LoginCommand;
308
356
  logout: typeof LogoutCommand;
@@ -310,4 +358,4 @@ declare const COMMANDS: {
310
358
  };
311
359
  declare function run(argv?: any, entryUrl?: string): Promise<void>;
312
360
  //#endregion
313
- export { ApiKeyResolution, COMMANDS, InitProjectOptions, InitProjectResult, LinkedPullTarget, OAuthLoginDiscovery, PushCandidateClassification, RemoteProjectMachineTarget, ResolvedConfiguredPullTargets, ScanProjectSourcesOptions, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, run, scanProjectSources };
361
+ export { ApiKeyResolution, COMMANDS, CredentialStatus, InitProjectOptions, InitProjectResult, LinkedPullTarget, OAuthLoginDiscovery, ProjectMachineStatus, ProjectStatusMachine, ProjectStatusResult, PushCandidateClassification, RemoteProjectMachineTarget, ResolvedConfiguredPullTargets, ScanProjectSourcesOptions, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatProjectStatus, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, resolveCredentialStatus, resolveProjectStatus, run, scanProjectSources };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as run, a as discoverOAuthLogin, c as formatPlanSummary, d as getEnvCredential, f as inferInitProjectName, g as resolveConfiguredPullTargets, h as resolveApiKey, i as discoverLinkedPullTargets, l as formatStoredCredentialSource, m as isFileGitDirty, n as buildOAuthLoginStart, o as discoverRemoteProjectMachineTargets, p as initProject, r as classifyPushCandidates, s as formatMissingNewMachineDirMessage, t as COMMANDS, u as getEnvApiKey, v as scanProjectSources, y as createStatelyProjectConfig } from "./cli-DR7_Qkzm.mjs";
1
+ import { S as createStatelyProjectConfig, _ as resolveConfiguredPullTargets, a as discoverOAuthLogin, b as run, c as formatPlanSummary, d as getEnvApiKey, f as getEnvCredential, g as resolveApiKey, h as isFileGitDirty, i as discoverLinkedPullTargets, l as formatProjectStatus, m as initProject, n as buildOAuthLoginStart, o as discoverRemoteProjectMachineTargets, p as inferInitProjectName, r as classifyPushCandidates, s as formatMissingNewMachineDirMessage, t as COMMANDS, u as formatStoredCredentialSource, v as resolveCredentialStatus, x as scanProjectSources, y as resolveProjectStatus } from "./cli-DRKY520A.mjs";
2
2
 
3
- export { COMMANDS, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, run, scanProjectSources };
3
+ export { COMMANDS, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatProjectStatus, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, resolveCredentialStatus, resolveProjectStatus, run, scanProjectSources };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statelyai",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "Command-line tools for Stately",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@oclif/core": "^4.10.3",
24
- "@statelyai/sdk": "0.13.1"
24
+ "@statelyai/sdk": "0.13.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "tsdown": "0.21.0-beta.2",