statelyai 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -6
- package/dist/bin.mjs +1 -1
- package/dist/{cli-DB0A-O4Y.mjs → cli-Buz_BC3I.mjs} +399 -39
- package/dist/index.d.mts +53 -3
- package/dist/index.mjs +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,20 +7,32 @@ This package contains the `statelyai` command implementation.
|
|
|
7
7
|
<!-- happy-path CLI usage and statelyai.json shape derived from packages/cli/src/cli.ts#COMMANDS and packages/cli/src/projectConfig.ts -->
|
|
8
8
|
## Happy Path
|
|
9
9
|
|
|
10
|
-
1.
|
|
11
|
-
2. Log in once:
|
|
10
|
+
1. Log in once:
|
|
12
11
|
|
|
13
12
|
```bash
|
|
14
13
|
statelyai login
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
Automation can still pass `--api-key`, set `STATELY_API_KEY`, or set
|
|
17
|
+
`STATELY_ACCESS_TOKEN`. For no-auth self-hosted deployments, run
|
|
18
|
+
`statelyai login --auth none`.
|
|
19
|
+
|
|
20
|
+
For a self-hosted or local MCP/API resource, discover OAuth from that resource:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
statelyai login --base-url http://localhost:3000/registry/api/mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`statelyai login --auth bearer` uses the same browser OAuth flow explicitly.
|
|
27
|
+
`statelyai login --api-key` keeps the legacy manual API-key path.
|
|
28
|
+
|
|
29
|
+
2. Initialize the current repo:
|
|
18
30
|
|
|
19
31
|
```bash
|
|
20
32
|
statelyai init
|
|
21
33
|
```
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
3. Optionally scan the repo and save suggested source globs:
|
|
24
36
|
|
|
25
37
|
```bash
|
|
26
38
|
statelyai init --scan
|
|
@@ -43,7 +55,7 @@ That writes a `statelyai.json` like:
|
|
|
43
55
|
}
|
|
44
56
|
```
|
|
45
57
|
|
|
46
|
-
|
|
58
|
+
4. Push local machines:
|
|
47
59
|
|
|
48
60
|
```bash
|
|
49
61
|
statelyai push
|
|
@@ -57,7 +69,7 @@ statelyai push --dry-run
|
|
|
57
69
|
|
|
58
70
|
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.
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
5. Pull remote changes back into linked local files and create new local files for remote-only project machines when `newMachinesDir` is configured:
|
|
61
73
|
|
|
62
74
|
```bash
|
|
63
75
|
statelyai pull
|
package/dist/bin.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
1
2
|
import fs, { promises, watch } from "node:fs";
|
|
2
3
|
import fsPromises from "node:fs/promises";
|
|
4
|
+
import * as http from "node:http";
|
|
3
5
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
4
6
|
import * as path$1 from "node:path";
|
|
5
7
|
import path from "node:path";
|
|
@@ -8,10 +10,8 @@ import { Writable } from "node:stream";
|
|
|
8
10
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
11
|
import { promisify } from "node:util";
|
|
10
12
|
import { Args, Command, Flags, flush, handle, run } from "@oclif/core";
|
|
11
|
-
import * as crypto from "node:crypto";
|
|
12
|
-
import * as http from "node:http";
|
|
13
13
|
import os from "node:os";
|
|
14
|
-
import { StudioApiError, createStatelyClient, getStatelyPragma } from "@statelyai/sdk";
|
|
14
|
+
import { StudioApiError, buildAuthorizeUrl, createPkcePair, createStatelyClient, discoverAuthorizationServer, discoverProtectedResource, exchangeCodeForToken, getStatelyPragma } from "@statelyai/sdk";
|
|
15
15
|
import { planSync, pullSync, pushLocalMachineLinks } from "@statelyai/sdk/sync";
|
|
16
16
|
|
|
17
17
|
//#region src/cliHost.ts
|
|
@@ -701,36 +701,109 @@ function getCredentialsFilePath() {
|
|
|
701
701
|
return path.join(getConfigDir(), CREDENTIALS_FILE_NAME);
|
|
702
702
|
}
|
|
703
703
|
function shouldUseFileBackendOnly() {
|
|
704
|
-
return process.env.STATELYAI_CREDENTIALS_BACKEND === "file";
|
|
704
|
+
return process.env.STATELYAI_CREDENTIALS_BACKEND === "file" || process.env.STATELYAI_CONFIG_DIR !== void 0;
|
|
705
705
|
}
|
|
706
706
|
async function readFileCredentials() {
|
|
707
|
+
const storedCredential = await readFileStoredCredential();
|
|
708
|
+
if (storedCredential?.credential?.type !== "api_key") return;
|
|
709
|
+
return {
|
|
710
|
+
apiKey: storedCredential.credential.token,
|
|
711
|
+
backend: storedCredential.backend,
|
|
712
|
+
location: storedCredential.location
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function parseStoredCredential(raw, backend, location) {
|
|
707
716
|
try {
|
|
708
|
-
const raw = await promises.readFile(getCredentialsFilePath(), "utf8");
|
|
709
717
|
const parsed = JSON.parse(raw);
|
|
710
|
-
if (typeof parsed.apiKey
|
|
718
|
+
if (typeof parsed.apiKey === "string" && parsed.apiKey.length > 0) return {
|
|
719
|
+
credential: {
|
|
720
|
+
type: "api_key",
|
|
721
|
+
token: parsed.apiKey
|
|
722
|
+
},
|
|
723
|
+
backend,
|
|
724
|
+
location
|
|
725
|
+
};
|
|
726
|
+
if (parsed.authMode === "none") return {
|
|
727
|
+
authMode: "none",
|
|
728
|
+
backend,
|
|
729
|
+
location
|
|
730
|
+
};
|
|
731
|
+
if (typeof parsed.credential === "object" && parsed.credential !== null && "type" in parsed.credential) {
|
|
732
|
+
const credential = parsed.credential;
|
|
733
|
+
if (credential.type === "api_key" && typeof credential.token === "string" && credential.token.length > 0) return {
|
|
734
|
+
credential: {
|
|
735
|
+
type: "api_key",
|
|
736
|
+
token: credential.token
|
|
737
|
+
},
|
|
738
|
+
backend,
|
|
739
|
+
location
|
|
740
|
+
};
|
|
741
|
+
if (credential.type === "oauth" && typeof credential.accessToken === "string" && credential.accessToken.length > 0) return {
|
|
742
|
+
credential: {
|
|
743
|
+
type: "oauth",
|
|
744
|
+
accessToken: credential.accessToken,
|
|
745
|
+
...typeof credential.refreshToken === "string" ? { refreshToken: credential.refreshToken } : {},
|
|
746
|
+
...typeof credential.expiresAt === "number" ? { expiresAt: credential.expiresAt } : {}
|
|
747
|
+
},
|
|
748
|
+
backend,
|
|
749
|
+
location
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
return;
|
|
753
|
+
} catch {
|
|
754
|
+
const token = raw.trim();
|
|
755
|
+
if (!token) return;
|
|
711
756
|
return {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
757
|
+
credential: {
|
|
758
|
+
type: "api_key",
|
|
759
|
+
token
|
|
760
|
+
},
|
|
761
|
+
backend,
|
|
762
|
+
location
|
|
715
763
|
};
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
async function readFileStoredCredential() {
|
|
767
|
+
try {
|
|
768
|
+
const credentialsPath = getCredentialsFilePath();
|
|
769
|
+
return parseStoredCredential(await promises.readFile(credentialsPath, "utf8"), "file", credentialsPath);
|
|
716
770
|
} catch {
|
|
717
771
|
return;
|
|
718
772
|
}
|
|
719
773
|
}
|
|
720
774
|
async function writeFileCredentials(apiKey) {
|
|
775
|
+
const stored = await writeFileStoredCredential({
|
|
776
|
+
type: "api_key",
|
|
777
|
+
token: apiKey
|
|
778
|
+
});
|
|
779
|
+
return {
|
|
780
|
+
apiKey,
|
|
781
|
+
backend: stored.backend,
|
|
782
|
+
location: stored.location
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
async function writeFileStoredCredential(credential, authMode) {
|
|
721
786
|
const configDir = getConfigDir();
|
|
722
787
|
const credentialsPath = getCredentialsFilePath();
|
|
788
|
+
const payload = authMode === "none" ? {
|
|
789
|
+
version: 1,
|
|
790
|
+
authMode: "none"
|
|
791
|
+
} : {
|
|
792
|
+
version: 1,
|
|
793
|
+
credential
|
|
794
|
+
};
|
|
723
795
|
await promises.mkdir(configDir, {
|
|
724
796
|
recursive: true,
|
|
725
797
|
mode: 448
|
|
726
798
|
});
|
|
727
|
-
await promises.writeFile(credentialsPath, `${JSON.stringify(
|
|
799
|
+
await promises.writeFile(credentialsPath, `${JSON.stringify(payload, null, 2)}\n`, {
|
|
728
800
|
encoding: "utf8",
|
|
729
801
|
mode: 384
|
|
730
802
|
});
|
|
731
803
|
await promises.chmod(credentialsPath, 384);
|
|
732
804
|
return {
|
|
733
|
-
|
|
805
|
+
...credential ? { credential } : {},
|
|
806
|
+
...authMode ? { authMode } : {},
|
|
734
807
|
backend: "file",
|
|
735
808
|
location: credentialsPath
|
|
736
809
|
};
|
|
@@ -744,6 +817,15 @@ async function deleteFileCredentials() {
|
|
|
744
817
|
}
|
|
745
818
|
}
|
|
746
819
|
async function readMacOSKeychain() {
|
|
820
|
+
const storedCredential = await readMacOSStoredCredential();
|
|
821
|
+
if (storedCredential?.credential?.type !== "api_key") return;
|
|
822
|
+
return {
|
|
823
|
+
apiKey: storedCredential.credential.token,
|
|
824
|
+
backend: storedCredential.backend,
|
|
825
|
+
location: storedCredential.location
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
async function readMacOSStoredCredential() {
|
|
747
829
|
if (process.platform !== "darwin") return;
|
|
748
830
|
try {
|
|
749
831
|
const { stdout } = await execFile$1("security", [
|
|
@@ -754,19 +836,31 @@ async function readMacOSKeychain() {
|
|
|
754
836
|
ACCOUNT_NAME,
|
|
755
837
|
"-w"
|
|
756
838
|
]);
|
|
757
|
-
|
|
758
|
-
if (!apiKey) return;
|
|
759
|
-
return {
|
|
760
|
-
apiKey,
|
|
761
|
-
backend: "macos-keychain",
|
|
762
|
-
location: "macOS Keychain"
|
|
763
|
-
};
|
|
839
|
+
return parseStoredCredential(stdout, "macos-keychain", "macOS Keychain");
|
|
764
840
|
} catch {
|
|
765
841
|
return;
|
|
766
842
|
}
|
|
767
843
|
}
|
|
768
844
|
async function writeMacOSKeychain(apiKey) {
|
|
845
|
+
const stored = await writeMacOSStoredCredential({
|
|
846
|
+
type: "api_key",
|
|
847
|
+
token: apiKey
|
|
848
|
+
});
|
|
849
|
+
return stored ? {
|
|
850
|
+
apiKey,
|
|
851
|
+
backend: stored.backend,
|
|
852
|
+
location: stored.location
|
|
853
|
+
} : void 0;
|
|
854
|
+
}
|
|
855
|
+
async function writeMacOSStoredCredential(credential, authMode) {
|
|
769
856
|
if (process.platform !== "darwin") return;
|
|
857
|
+
const payload = authMode === "none" ? {
|
|
858
|
+
version: 1,
|
|
859
|
+
authMode: "none"
|
|
860
|
+
} : {
|
|
861
|
+
version: 1,
|
|
862
|
+
credential
|
|
863
|
+
};
|
|
770
864
|
try {
|
|
771
865
|
await execFile$1("security", [
|
|
772
866
|
"add-generic-password",
|
|
@@ -776,10 +870,11 @@ async function writeMacOSKeychain(apiKey) {
|
|
|
776
870
|
"-a",
|
|
777
871
|
ACCOUNT_NAME,
|
|
778
872
|
"-w",
|
|
779
|
-
|
|
873
|
+
JSON.stringify(payload)
|
|
780
874
|
]);
|
|
781
875
|
return {
|
|
782
|
-
|
|
876
|
+
...credential ? { credential } : {},
|
|
877
|
+
...authMode ? { authMode } : {},
|
|
783
878
|
backend: "macos-keychain",
|
|
784
879
|
location: "macOS Keychain"
|
|
785
880
|
};
|
|
@@ -803,6 +898,15 @@ async function deleteMacOSKeychain() {
|
|
|
803
898
|
}
|
|
804
899
|
}
|
|
805
900
|
async function readLinuxSecretTool() {
|
|
901
|
+
const storedCredential = await readLinuxStoredCredential();
|
|
902
|
+
if (storedCredential?.credential?.type !== "api_key") return;
|
|
903
|
+
return {
|
|
904
|
+
apiKey: storedCredential.credential.token,
|
|
905
|
+
backend: storedCredential.backend,
|
|
906
|
+
location: storedCredential.location
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
async function readLinuxStoredCredential() {
|
|
806
910
|
if (process.platform !== "linux") return;
|
|
807
911
|
try {
|
|
808
912
|
const { stdout } = await execFile$1("secret-tool", [
|
|
@@ -812,19 +916,31 @@ async function readLinuxSecretTool() {
|
|
|
812
916
|
"account",
|
|
813
917
|
ACCOUNT_NAME
|
|
814
918
|
]);
|
|
815
|
-
|
|
816
|
-
if (!apiKey) return;
|
|
817
|
-
return {
|
|
818
|
-
apiKey,
|
|
819
|
-
backend: "linux-secret-tool",
|
|
820
|
-
location: "Secret Service (secret-tool)"
|
|
821
|
-
};
|
|
919
|
+
return parseStoredCredential(stdout, "linux-secret-tool", "Secret Service (secret-tool)");
|
|
822
920
|
} catch {
|
|
823
921
|
return;
|
|
824
922
|
}
|
|
825
923
|
}
|
|
826
924
|
async function writeLinuxSecretTool(apiKey) {
|
|
925
|
+
const stored = await writeLinuxStoredCredential({
|
|
926
|
+
type: "api_key",
|
|
927
|
+
token: apiKey
|
|
928
|
+
});
|
|
929
|
+
return stored ? {
|
|
930
|
+
apiKey,
|
|
931
|
+
backend: stored.backend,
|
|
932
|
+
location: stored.location
|
|
933
|
+
} : void 0;
|
|
934
|
+
}
|
|
935
|
+
async function writeLinuxStoredCredential(credential, authMode) {
|
|
827
936
|
if (process.platform !== "linux") return;
|
|
937
|
+
const payload = authMode === "none" ? {
|
|
938
|
+
version: 1,
|
|
939
|
+
authMode: "none"
|
|
940
|
+
} : {
|
|
941
|
+
version: 1,
|
|
942
|
+
credential
|
|
943
|
+
};
|
|
828
944
|
try {
|
|
829
945
|
execFileSync("secret-tool", [
|
|
830
946
|
"store",
|
|
@@ -834,9 +950,10 @@ async function writeLinuxSecretTool(apiKey) {
|
|
|
834
950
|
SERVICE_NAME,
|
|
835
951
|
"account",
|
|
836
952
|
ACCOUNT_NAME
|
|
837
|
-
], { input:
|
|
953
|
+
], { input: JSON.stringify(payload) });
|
|
838
954
|
return {
|
|
839
|
-
|
|
955
|
+
...credential ? { credential } : {},
|
|
956
|
+
...authMode ? { authMode } : {},
|
|
840
957
|
backend: "linux-secret-tool",
|
|
841
958
|
location: "Secret Service (secret-tool)"
|
|
842
959
|
};
|
|
@@ -863,10 +980,22 @@ async function getStoredApiKey() {
|
|
|
863
980
|
if (shouldUseFileBackendOnly()) return readFileCredentials();
|
|
864
981
|
return await readMacOSKeychain() ?? await readLinuxSecretTool() ?? await readFileCredentials();
|
|
865
982
|
}
|
|
983
|
+
async function getStoredCredential() {
|
|
984
|
+
if (shouldUseFileBackendOnly()) return readFileStoredCredential();
|
|
985
|
+
return await readMacOSStoredCredential() ?? await readLinuxStoredCredential() ?? await readFileStoredCredential();
|
|
986
|
+
}
|
|
866
987
|
async function setStoredApiKey(apiKey) {
|
|
867
988
|
if (shouldUseFileBackendOnly()) return writeFileCredentials(apiKey);
|
|
868
989
|
return await writeMacOSKeychain(apiKey) ?? await writeLinuxSecretTool(apiKey) ?? await writeFileCredentials(apiKey);
|
|
869
990
|
}
|
|
991
|
+
async function setStoredCredential(credential) {
|
|
992
|
+
if (shouldUseFileBackendOnly()) return writeFileStoredCredential(credential);
|
|
993
|
+
return await writeMacOSStoredCredential(credential) ?? await writeLinuxStoredCredential(credential) ?? await writeFileStoredCredential(credential);
|
|
994
|
+
}
|
|
995
|
+
async function setStoredNoAuth() {
|
|
996
|
+
if (shouldUseFileBackendOnly()) return writeFileStoredCredential(void 0, "none");
|
|
997
|
+
return await writeMacOSStoredCredential(void 0, "none") ?? await writeLinuxStoredCredential(void 0, "none") ?? await writeFileStoredCredential(void 0, "none");
|
|
998
|
+
}
|
|
870
999
|
async function deleteStoredApiKey() {
|
|
871
1000
|
if (shouldUseFileBackendOnly()) {
|
|
872
1001
|
const deleted = await deleteFileCredentials();
|
|
@@ -1168,6 +1297,11 @@ function loadLocalEnv() {
|
|
|
1168
1297
|
}
|
|
1169
1298
|
loadLocalEnv();
|
|
1170
1299
|
function getEnvApiKey() {
|
|
1300
|
+
const accessToken = process.env.STATELY_ACCESS_TOKEN;
|
|
1301
|
+
if (accessToken) return {
|
|
1302
|
+
apiKey: accessToken,
|
|
1303
|
+
variable: "STATELY_ACCESS_TOKEN"
|
|
1304
|
+
};
|
|
1171
1305
|
const statelyApiKey = process.env.STATELY_API_KEY;
|
|
1172
1306
|
if (statelyApiKey) return {
|
|
1173
1307
|
apiKey: statelyApiKey,
|
|
@@ -1179,6 +1313,32 @@ function getEnvApiKey() {
|
|
|
1179
1313
|
variable: "NEXT_PUBLIC_STATELY_API_KEY"
|
|
1180
1314
|
};
|
|
1181
1315
|
}
|
|
1316
|
+
function getEnvCredential() {
|
|
1317
|
+
const accessToken = process.env.STATELY_ACCESS_TOKEN;
|
|
1318
|
+
if (accessToken) return {
|
|
1319
|
+
credential: {
|
|
1320
|
+
type: "oauth",
|
|
1321
|
+
accessToken
|
|
1322
|
+
},
|
|
1323
|
+
variable: "STATELY_ACCESS_TOKEN"
|
|
1324
|
+
};
|
|
1325
|
+
const statelyApiKey = process.env.STATELY_API_KEY;
|
|
1326
|
+
if (statelyApiKey) return {
|
|
1327
|
+
credential: {
|
|
1328
|
+
type: "api_key",
|
|
1329
|
+
token: statelyApiKey
|
|
1330
|
+
},
|
|
1331
|
+
variable: "STATELY_API_KEY"
|
|
1332
|
+
};
|
|
1333
|
+
const publicApiKey = process.env.NEXT_PUBLIC_STATELY_API_KEY;
|
|
1334
|
+
if (publicApiKey) return {
|
|
1335
|
+
credential: {
|
|
1336
|
+
type: "api_key",
|
|
1337
|
+
token: publicApiKey
|
|
1338
|
+
},
|
|
1339
|
+
variable: "NEXT_PUBLIC_STATELY_API_KEY"
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1182
1342
|
async function resolveApiKey(explicitApiKey) {
|
|
1183
1343
|
if (explicitApiKey) return {
|
|
1184
1344
|
apiKey: explicitApiKey,
|
|
@@ -1208,6 +1368,37 @@ function getDefaultEditorUrl() {
|
|
|
1208
1368
|
function getResolvedStudioUrl(baseUrl) {
|
|
1209
1369
|
return baseUrl ?? getDefaultBaseUrl() ?? "https://stately.ai";
|
|
1210
1370
|
}
|
|
1371
|
+
function getDefaultMcpResourceUrl() {
|
|
1372
|
+
return process.env.STATELY_MCP_RESOURCE_URL ?? process.env.NEXT_PUBLIC_MCP_RESOURCE_URL ?? process.env.STATELY_MCP_API_BASE_URL ?? "https://editor.stately.ai/api/mcp";
|
|
1373
|
+
}
|
|
1374
|
+
function normalizeLoginResourceUrl(baseUrl) {
|
|
1375
|
+
const raw = baseUrl ?? getDefaultMcpResourceUrl();
|
|
1376
|
+
const url = new URL(raw);
|
|
1377
|
+
if (url.pathname === "/" || url.pathname === "") url.pathname = "/api/mcp";
|
|
1378
|
+
return url.toString();
|
|
1379
|
+
}
|
|
1380
|
+
function getOauthClientId() {
|
|
1381
|
+
return process.env.STATELY_OAUTH_CLIENT_ID ?? process.env.NEXT_PUBLIC_STATELY_OAUTH_CLIENT_ID ?? "statelyai-cli";
|
|
1382
|
+
}
|
|
1383
|
+
async function discoverOAuthLogin(options = {}) {
|
|
1384
|
+
const resourceUrl = normalizeLoginResourceUrl(options.baseUrl);
|
|
1385
|
+
const protectedResource = await discoverProtectedResource(resourceUrl, { fetch: options.fetch });
|
|
1386
|
+
const [issuer] = protectedResource.authorization_servers ?? [];
|
|
1387
|
+
if (!issuer) throw new Error(`No OAuth authorization server advertised by ${resourceUrl}. Use \`statelyai login --auth none\` for no-auth self-hosted deployments.`);
|
|
1388
|
+
const authorizationServer = await discoverAuthorizationServer(issuer, { fetch: options.fetch });
|
|
1389
|
+
const authorizationEndpoint = authorizationServer.authorization_endpoint;
|
|
1390
|
+
const tokenEndpoint = authorizationServer.token_endpoint;
|
|
1391
|
+
if (!authorizationEndpoint || !tokenEndpoint) throw new Error(`Authorization server ${issuer} did not advertise authorization and token endpoints.`);
|
|
1392
|
+
return {
|
|
1393
|
+
authorizationServer,
|
|
1394
|
+
authorizationEndpoint,
|
|
1395
|
+
clientId: options.clientId ?? getOauthClientId(),
|
|
1396
|
+
protectedResource,
|
|
1397
|
+
resourceUrl,
|
|
1398
|
+
scopes: protectedResource.scopes_supported ?? [],
|
|
1399
|
+
tokenEndpoint
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1211
1402
|
function parseGitHubRemote(remoteUrl) {
|
|
1212
1403
|
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
1213
1404
|
if (httpsMatch) {
|
|
@@ -1289,6 +1480,147 @@ async function promptForApiKey() {
|
|
|
1289
1480
|
rl.close();
|
|
1290
1481
|
}
|
|
1291
1482
|
}
|
|
1483
|
+
async function promptForOAuthCallback() {
|
|
1484
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("No interactive terminal available to paste the OAuth callback URL.");
|
|
1485
|
+
const rl = createInterface({
|
|
1486
|
+
input: process.stdin,
|
|
1487
|
+
output: process.stdout,
|
|
1488
|
+
terminal: true
|
|
1489
|
+
});
|
|
1490
|
+
try {
|
|
1491
|
+
return (await rl.question("Paste the callback URL or code: ")).trim();
|
|
1492
|
+
} finally {
|
|
1493
|
+
rl.close();
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
function parseOAuthCallback(value) {
|
|
1497
|
+
try {
|
|
1498
|
+
const url = new URL(value);
|
|
1499
|
+
const code = url.searchParams.get("code");
|
|
1500
|
+
const state = url.searchParams.get("state") ?? void 0;
|
|
1501
|
+
if (!code) throw new Error("Callback URL did not include a code parameter.");
|
|
1502
|
+
return {
|
|
1503
|
+
code,
|
|
1504
|
+
state
|
|
1505
|
+
};
|
|
1506
|
+
} catch (error) {
|
|
1507
|
+
if (error instanceof Error && error.message.includes("code parameter")) throw error;
|
|
1508
|
+
if (!value) throw new Error("OAuth code cannot be empty.");
|
|
1509
|
+
return { code: value };
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
function createOAuthCredential(tokenResponse, now = Date.now()) {
|
|
1513
|
+
if (!tokenResponse.access_token) throw new Error("Token response did not include an access_token.");
|
|
1514
|
+
const expiresAt = typeof tokenResponse.expires_at === "number" ? tokenResponse.expires_at * 1e3 : typeof tokenResponse.expires_in === "number" ? now + tokenResponse.expires_in * 1e3 : void 0;
|
|
1515
|
+
return {
|
|
1516
|
+
type: "oauth",
|
|
1517
|
+
accessToken: tokenResponse.access_token,
|
|
1518
|
+
...tokenResponse.refresh_token ? { refreshToken: tokenResponse.refresh_token } : {},
|
|
1519
|
+
...expiresAt ? { expiresAt } : {}
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
async function createLocalOAuthCallbackServer(options) {
|
|
1523
|
+
let resolveCode;
|
|
1524
|
+
let rejectCode;
|
|
1525
|
+
const codePromise = new Promise((resolve, reject) => {
|
|
1526
|
+
resolveCode = resolve;
|
|
1527
|
+
rejectCode = reject;
|
|
1528
|
+
});
|
|
1529
|
+
const server = http.createServer((req, res) => {
|
|
1530
|
+
const requestUrl = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
1531
|
+
const code = requestUrl.searchParams.get("code");
|
|
1532
|
+
const state = requestUrl.searchParams.get("state");
|
|
1533
|
+
const error = requestUrl.searchParams.get("error");
|
|
1534
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
1535
|
+
if (error) {
|
|
1536
|
+
res.statusCode = 400;
|
|
1537
|
+
res.end("<!doctype html><title>Sign in failed</title><p>Sign in failed. Return to the terminal.</p>");
|
|
1538
|
+
rejectCode(new Error(error));
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
if (!code || state !== options.state) {
|
|
1542
|
+
res.statusCode = 400;
|
|
1543
|
+
res.end("<!doctype html><title>Sign in failed</title><p>Invalid sign-in callback. Return to the terminal.</p>");
|
|
1544
|
+
rejectCode(/* @__PURE__ */ new Error("Invalid OAuth callback."));
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
res.end("<!doctype html><title>Signed in</title><p>Signed in. You can close this window.</p>");
|
|
1548
|
+
resolveCode(code);
|
|
1549
|
+
});
|
|
1550
|
+
await new Promise((resolve) => {
|
|
1551
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
1552
|
+
});
|
|
1553
|
+
const address = server.address();
|
|
1554
|
+
if (!address || typeof address === "string") throw new Error("Could not start OAuth callback server.");
|
|
1555
|
+
return {
|
|
1556
|
+
redirectUri: `http://127.0.0.1:${address.port}/callback`,
|
|
1557
|
+
waitForCode: () => codePromise,
|
|
1558
|
+
close: () => new Promise((resolve) => {
|
|
1559
|
+
server.close(() => resolve());
|
|
1560
|
+
})
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
async function buildOAuthLoginStart(options) {
|
|
1564
|
+
const [discovery, pkce] = await Promise.all([discoverOAuthLogin({
|
|
1565
|
+
baseUrl: options.baseUrl,
|
|
1566
|
+
clientId: options.clientId,
|
|
1567
|
+
fetch: options.fetch
|
|
1568
|
+
}), createPkcePair()]);
|
|
1569
|
+
return {
|
|
1570
|
+
authorizeUrl: buildAuthorizeUrl({
|
|
1571
|
+
authorizationEndpoint: discovery.authorizationEndpoint,
|
|
1572
|
+
clientId: discovery.clientId,
|
|
1573
|
+
redirectUri: options.redirectUri,
|
|
1574
|
+
codeChallenge: pkce.codeChallenge,
|
|
1575
|
+
state: options.state,
|
|
1576
|
+
scopes: discovery.scopes
|
|
1577
|
+
}),
|
|
1578
|
+
codeVerifier: pkce.codeVerifier,
|
|
1579
|
+
discovery
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
function formatStoredCredentialSource(storedCredential) {
|
|
1583
|
+
if (storedCredential.credential?.type === "api_key") return `Credential source: stored api_key (${describeCredentialBackend(storedCredential.backend, storedCredential.location)}).`;
|
|
1584
|
+
if (storedCredential.credential?.type === "oauth") return `Credential source: stored oauth (${describeCredentialBackend(storedCredential.backend, storedCredential.location)}).`;
|
|
1585
|
+
if (storedCredential.authMode === "none") return `Credential source: stored no-auth mode (${describeCredentialBackend(storedCredential.backend, storedCredential.location)}).`;
|
|
1586
|
+
return `Credential source: stored unknown (${describeCredentialBackend(storedCredential.backend, storedCredential.location)}).`;
|
|
1587
|
+
}
|
|
1588
|
+
async function runOAuthBrowserLogin(options) {
|
|
1589
|
+
const state = crypto.randomBytes(16).toString("base64url");
|
|
1590
|
+
const callbackServer = await createLocalOAuthCallbackServer({ state });
|
|
1591
|
+
try {
|
|
1592
|
+
const { authorizeUrl, codeVerifier, discovery } = await buildOAuthLoginStart({
|
|
1593
|
+
baseUrl: options.baseUrl,
|
|
1594
|
+
redirectUri: callbackServer.redirectUri,
|
|
1595
|
+
state
|
|
1596
|
+
});
|
|
1597
|
+
options.log(`Opening browser for ${discovery.resourceUrl}`);
|
|
1598
|
+
options.log(authorizeUrl);
|
|
1599
|
+
let openedBrowser = false;
|
|
1600
|
+
try {
|
|
1601
|
+
openBrowser(authorizeUrl);
|
|
1602
|
+
openedBrowser = true;
|
|
1603
|
+
} catch {
|
|
1604
|
+
options.log("Could not open a browser automatically.");
|
|
1605
|
+
}
|
|
1606
|
+
const codePromise = callbackServer.waitForCode();
|
|
1607
|
+
const pastedCodePromise = !openedBrowser && process.stdin.isTTY && process.stdout.isTTY ? promptForOAuthCallback().then((value) => {
|
|
1608
|
+
const callback = parseOAuthCallback(value);
|
|
1609
|
+
if (callback.state && callback.state !== state) throw new Error("OAuth state did not match.");
|
|
1610
|
+
return callback.code;
|
|
1611
|
+
}) : void 0;
|
|
1612
|
+
const code = await (pastedCodePromise ? Promise.race([codePromise, pastedCodePromise]) : codePromise);
|
|
1613
|
+
return setStoredCredential(createOAuthCredential(await exchangeCodeForToken({
|
|
1614
|
+
tokenEndpoint: discovery.tokenEndpoint,
|
|
1615
|
+
code,
|
|
1616
|
+
codeVerifier,
|
|
1617
|
+
redirectUri: callbackServer.redirectUri,
|
|
1618
|
+
clientId: discovery.clientId
|
|
1619
|
+
})));
|
|
1620
|
+
} finally {
|
|
1621
|
+
await callbackServer.close();
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1292
1624
|
async function promptYesNo(question, defaultValue = true) {
|
|
1293
1625
|
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("No interactive terminal available.");
|
|
1294
1626
|
const rl = createInterface({
|
|
@@ -2062,23 +2394,46 @@ var InitCommand = class InitCommand extends Command {
|
|
|
2062
2394
|
};
|
|
2063
2395
|
var LoginCommand = class LoginCommand extends Command {
|
|
2064
2396
|
static enableJsonFlag = false;
|
|
2065
|
-
static summary = "Store
|
|
2066
|
-
static description = "Stores
|
|
2397
|
+
static summary = "Store Stately credentials for future CLI use.";
|
|
2398
|
+
static description = "Stores Stately credentials in the OS credential store when available, with a private file fallback.";
|
|
2067
2399
|
static flags = {
|
|
2068
2400
|
help: Flags.help({ char: "h" }),
|
|
2069
2401
|
"api-key": Flags.string({ description: "API key to store without an interactive prompt" }),
|
|
2402
|
+
"base-url": Flags.string({ description: "MCP/API resource URL used for OAuth discovery" }),
|
|
2070
2403
|
stdin: Flags.boolean({
|
|
2071
2404
|
description: "Read the API key from standard input",
|
|
2072
2405
|
default: false
|
|
2406
|
+
}),
|
|
2407
|
+
auth: Flags.string({
|
|
2408
|
+
description: "Authentication mode for the target server",
|
|
2409
|
+
options: ["none", "bearer"]
|
|
2073
2410
|
})
|
|
2074
2411
|
};
|
|
2075
2412
|
async run() {
|
|
2076
2413
|
const { flags } = await this.parse(LoginCommand);
|
|
2414
|
+
if (flags["api-key"] && flags.auth === "bearer") this.error("Pass either --api-key or --auth bearer, not both.");
|
|
2415
|
+
if (flags.auth === "none") {
|
|
2416
|
+
if (flags.stdin || flags["api-key"]) this.error("Pass either --auth none or an API key, not both.");
|
|
2417
|
+
const stored = await setStoredNoAuth();
|
|
2418
|
+
this.log(`Stored no-auth mode in ${describeCredentialBackend(stored.backend, stored.location)}.`);
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
if (!flags["api-key"] && !flags.stdin) {
|
|
2422
|
+
const stored = await runOAuthBrowserLogin({
|
|
2423
|
+
baseUrl: flags["base-url"],
|
|
2424
|
+
log: (message) => this.log(message)
|
|
2425
|
+
});
|
|
2426
|
+
this.log(`Stored OAuth credential in ${describeCredentialBackend(stored.backend, stored.location)}.`);
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2077
2429
|
if (flags.stdin && flags["api-key"]) this.error("Pass either --api-key or --stdin, not both.");
|
|
2078
2430
|
if (!flags["api-key"] && !flags.stdin && process.stdin.isTTY && process.stdout.isTTY) this.log(`Get or create an API key at ${STATELY_API_KEY_SETTINGS_URL}`);
|
|
2079
2431
|
const apiKey = normalizeApiKey(flags["api-key"] ?? (!process.stdin.isTTY || flags.stdin ? await readApiKeyFromStdin() : await promptForApiKey()));
|
|
2080
2432
|
if (!apiKey) this.error(`API key cannot be empty.\nGet or create an API key at ${STATELY_API_KEY_SETTINGS_URL}`);
|
|
2081
|
-
const stored = await setStoredApiKey(apiKey)
|
|
2433
|
+
const stored = flags["api-key"] || flags.stdin ? await setStoredApiKey(apiKey) : await setStoredCredential({
|
|
2434
|
+
type: "api_key",
|
|
2435
|
+
token: apiKey
|
|
2436
|
+
});
|
|
2082
2437
|
this.log(`Stored API key in ${describeCredentialBackend(stored.backend, stored.location)}.`);
|
|
2083
2438
|
}
|
|
2084
2439
|
};
|
|
@@ -2102,15 +2457,20 @@ var WhoamiCommand = class extends Command {
|
|
|
2102
2457
|
static description = "Reports whether the CLI would use a flag, environment variable, or stored credential.";
|
|
2103
2458
|
static flags = { help: Flags.help({ char: "h" }) };
|
|
2104
2459
|
async run() {
|
|
2105
|
-
const
|
|
2106
|
-
const
|
|
2107
|
-
|
|
2108
|
-
|
|
2460
|
+
const envCredential = getEnvCredential();
|
|
2461
|
+
const storedCredential = await getStoredCredential();
|
|
2462
|
+
const storedApiKey = storedCredential?.credential?.type === "api_key" ? {
|
|
2463
|
+
apiKey: storedCredential.credential.token,
|
|
2464
|
+
backend: storedCredential.backend,
|
|
2465
|
+
location: storedCredential.location
|
|
2466
|
+
} : void 0;
|
|
2467
|
+
if (envCredential) {
|
|
2468
|
+
this.log(`Credential source: environment ${envCredential.credential.type} (${envCredential.variable}).`);
|
|
2109
2469
|
if (storedApiKey) this.log(`Stored credential also available in ${describeCredentialBackend(storedApiKey.backend, storedApiKey.location)}.`);
|
|
2110
2470
|
return;
|
|
2111
2471
|
}
|
|
2112
|
-
if (
|
|
2113
|
-
this.log(
|
|
2472
|
+
if (storedCredential) {
|
|
2473
|
+
this.log(formatStoredCredentialSource(storedCredential));
|
|
2114
2474
|
return;
|
|
2115
2475
|
}
|
|
2116
2476
|
this.log(getMissingApiKeyMessage());
|
|
@@ -2149,4 +2509,4 @@ function isDirectExecution() {
|
|
|
2149
2509
|
if (isDirectExecution()) run$1();
|
|
2150
2510
|
|
|
2151
2511
|
//#endregion
|
|
2152
|
-
export {
|
|
2512
|
+
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 };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import { Command } from "@oclif/core";
|
|
2
|
-
import { ConnectedRepo, CreateProjectInput, ProjectData, StudioClient } from "@statelyai/sdk";
|
|
2
|
+
import { ConnectedRepo, CreateProjectInput, OAuthAuthorizationServerMetadata, OAuthProtectedResourceMetadata, ProjectData, StudioClient } from "@statelyai/sdk";
|
|
3
3
|
import { SyncPlan } from "@statelyai/sdk/sync";
|
|
4
4
|
import * as _oclif_core_interfaces0 from "@oclif/core/interfaces";
|
|
5
5
|
|
|
6
|
+
//#region src/credentials.d.ts
|
|
7
|
+
type StatelyCredential = {
|
|
8
|
+
type: 'api_key';
|
|
9
|
+
token: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'oauth';
|
|
12
|
+
accessToken: string;
|
|
13
|
+
refreshToken?: string;
|
|
14
|
+
expiresAt?: number;
|
|
15
|
+
};
|
|
16
|
+
type CredentialBackend = 'file' | 'linux-secret-tool' | 'macos-keychain';
|
|
17
|
+
type StoredCredential = {
|
|
18
|
+
credential?: StatelyCredential;
|
|
19
|
+
authMode?: 'none';
|
|
20
|
+
backend: CredentialBackend;
|
|
21
|
+
location: string;
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
6
24
|
//#region src/projectConfig.d.ts
|
|
7
25
|
interface StatelySourceConfig {
|
|
8
26
|
include: string[];
|
|
@@ -90,10 +108,40 @@ type ApiKeyResolution = {
|
|
|
90
108
|
};
|
|
91
109
|
declare function getEnvApiKey(): {
|
|
92
110
|
apiKey: string;
|
|
93
|
-
variable: 'NEXT_PUBLIC_STATELY_API_KEY' | 'STATELY_API_KEY';
|
|
111
|
+
variable: 'NEXT_PUBLIC_STATELY_API_KEY' | 'STATELY_API_KEY' | 'STATELY_ACCESS_TOKEN';
|
|
112
|
+
} | undefined;
|
|
113
|
+
declare function getEnvCredential(): {
|
|
114
|
+
credential: StatelyCredential;
|
|
115
|
+
variable: 'NEXT_PUBLIC_STATELY_API_KEY' | 'STATELY_API_KEY' | 'STATELY_ACCESS_TOKEN';
|
|
94
116
|
} | undefined;
|
|
95
117
|
declare function resolveApiKey(explicitApiKey?: string): Promise<ApiKeyResolution>;
|
|
118
|
+
interface OAuthLoginDiscovery {
|
|
119
|
+
authorizationServer: OAuthAuthorizationServerMetadata;
|
|
120
|
+
authorizationEndpoint: string;
|
|
121
|
+
clientId: string;
|
|
122
|
+
protectedResource: OAuthProtectedResourceMetadata;
|
|
123
|
+
resourceUrl: string;
|
|
124
|
+
scopes: string[];
|
|
125
|
+
tokenEndpoint: string;
|
|
126
|
+
}
|
|
127
|
+
declare function discoverOAuthLogin(options?: {
|
|
128
|
+
baseUrl?: string;
|
|
129
|
+
clientId?: string;
|
|
130
|
+
fetch?: typeof fetch;
|
|
131
|
+
}): Promise<OAuthLoginDiscovery>;
|
|
96
132
|
declare function inferInitProjectName(cwd: string, repo?: ConnectedRepo): string;
|
|
133
|
+
declare function buildOAuthLoginStart(options: {
|
|
134
|
+
baseUrl?: string;
|
|
135
|
+
clientId?: string;
|
|
136
|
+
fetch?: typeof fetch;
|
|
137
|
+
redirectUri: string;
|
|
138
|
+
state: string;
|
|
139
|
+
}): Promise<{
|
|
140
|
+
authorizeUrl: string;
|
|
141
|
+
codeVerifier: string;
|
|
142
|
+
discovery: OAuthLoginDiscovery;
|
|
143
|
+
}>;
|
|
144
|
+
declare function formatStoredCredentialSource(storedCredential: StoredCredential): string;
|
|
97
145
|
declare function formatMissingNewMachineDirMessage(options: {
|
|
98
146
|
config: Pick<StatelyProjectConfig, 'include' | 'newMachinesDir'>;
|
|
99
147
|
remoteMachineCount: number;
|
|
@@ -230,7 +278,9 @@ declare class LoginCommand extends Command {
|
|
|
230
278
|
static flags: {
|
|
231
279
|
help: _oclif_core_interfaces0.BooleanFlag<void>;
|
|
232
280
|
'api-key': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
281
|
+
'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
233
282
|
stdin: _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
283
|
+
auth: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
234
284
|
};
|
|
235
285
|
run(): Promise<void>;
|
|
236
286
|
}
|
|
@@ -265,4 +315,4 @@ declare const COMMANDS: {
|
|
|
265
315
|
};
|
|
266
316
|
declare function run(argv?: any, entryUrl?: string): Promise<void>;
|
|
267
317
|
//#endregion
|
|
268
|
-
export { ApiKeyResolution, COMMANDS, InitProjectOptions, InitProjectResult, LinkedPullTarget, PushCandidateClassification, RemoteProjectMachineTarget, ResolvedConfiguredPullTargets, ScanProjectSourcesOptions, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, run, scanProjectSources };
|
|
318
|
+
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 };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
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-Buz_BC3I.mjs";
|
|
2
2
|
|
|
3
|
-
export { COMMANDS, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, run, scanProjectSources };
|
|
3
|
+
export { COMMANDS, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, run, scanProjectSources };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "statelyai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
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.
|
|
24
|
+
"@statelyai/sdk": "0.12.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsdown": "0.21.0-beta.2",
|