statelyai 0.3.7 → 0.4.1
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 +25 -0
- package/dist/bin.mjs +1 -1
- package/dist/{cli-B9CRx5db.mjs → cli-DkWxDq0T.mjs} +166 -58
- package/dist/index.d.mts +10 -3
- package/dist/index.mjs +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ CLI implementation for Stately.
|
|
|
4
4
|
|
|
5
5
|
This package contains the `statelyai` command implementation.
|
|
6
6
|
|
|
7
|
+
<!-- happy-path CLI usage and statelyai.json shape derived from packages/cli/src/cli.ts#COMMANDS and packages/cli/src/projectConfig.ts -->
|
|
7
8
|
## Happy Path
|
|
8
9
|
|
|
9
10
|
1. Get an API key from [Stately API Key settings](https://stately.ai/registry/user/my-settings?tab=API+Key).
|
|
@@ -25,18 +26,42 @@ statelyai init
|
|
|
25
26
|
statelyai init --scan
|
|
26
27
|
```
|
|
27
28
|
|
|
29
|
+
That writes a `statelyai.json` like:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"$schema": "https://stately.ai/schemas/statelyai.json",
|
|
34
|
+
"version": "1.0.0",
|
|
35
|
+
"projectId": "project_123",
|
|
36
|
+
"studioUrl": "https://stately.ai",
|
|
37
|
+
"defaultXStateVersion": 5,
|
|
38
|
+
"include": ["src/**/*.ts"],
|
|
39
|
+
"exclude": ["**/*.test.*", "**/*.spec.*"]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
28
43
|
5. Push local machines:
|
|
29
44
|
|
|
30
45
|
```bash
|
|
31
46
|
statelyai push
|
|
32
47
|
```
|
|
33
48
|
|
|
49
|
+
Preview what `push` would do without updating Studio or local files:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
statelyai push --dry-run
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
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.
|
|
56
|
+
|
|
34
57
|
6. Pull remote changes back into linked local files:
|
|
35
58
|
|
|
36
59
|
```bash
|
|
37
60
|
statelyai pull
|
|
38
61
|
```
|
|
39
62
|
|
|
63
|
+
`pull` skips locally modified files unless you pass `--force`.
|
|
64
|
+
|
|
40
65
|
Run it without installing it globally:
|
|
41
66
|
|
|
42
67
|
```bash
|
package/dist/bin.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import * as crypto from "node:crypto";
|
|
|
12
12
|
import * as http from "node:http";
|
|
13
13
|
import * as https from "node:https";
|
|
14
14
|
import os from "node:os";
|
|
15
|
-
import { createStatelyClient, getStatelyPragma } from "@statelyai/sdk";
|
|
15
|
+
import { StudioApiError, createStatelyClient, getStatelyPragma } from "@statelyai/sdk";
|
|
16
16
|
import { planSync, pullSync, pushLocalMachineLinks } from "@statelyai/sdk/sync";
|
|
17
17
|
|
|
18
18
|
//#region src/cliHost.ts
|
|
@@ -945,6 +945,15 @@ const CODE_SOURCE_EXTENSIONS = new Set([
|
|
|
945
945
|
".cjs"
|
|
946
946
|
]);
|
|
947
947
|
const execFileAsync$1 = promisify(execFile);
|
|
948
|
+
const ALWAYS_IGNORED_DIRECTORIES = new Set([
|
|
949
|
+
".git",
|
|
950
|
+
"node_modules",
|
|
951
|
+
"dist",
|
|
952
|
+
"build",
|
|
953
|
+
"out",
|
|
954
|
+
"coverage",
|
|
955
|
+
".next"
|
|
956
|
+
]);
|
|
948
957
|
function createStatelyProjectConfig(options) {
|
|
949
958
|
const defaultXStateVersion = options.defaultXStateVersion ?? 5;
|
|
950
959
|
return {
|
|
@@ -953,21 +962,20 @@ function createStatelyProjectConfig(options) {
|
|
|
953
962
|
projectId: options.projectId,
|
|
954
963
|
studioUrl: options.studioUrl,
|
|
955
964
|
defaultXStateVersion,
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
}
|
|
959
|
-
function normalizeSourceConfig(source) {
|
|
960
|
-
return {
|
|
961
|
-
include: [...source.include ?? []],
|
|
962
|
-
exclude: source.exclude == null ? [...DEFAULT_SOURCE_EXCLUDES] : [...source.exclude],
|
|
963
|
-
format: source.format ?? "xstate",
|
|
964
|
-
...source.xstateVersion == null ? {} : { xstateVersion: source.xstateVersion }
|
|
965
|
+
include: [],
|
|
966
|
+
exclude: [...DEFAULT_SOURCE_EXCLUDES]
|
|
965
967
|
};
|
|
966
968
|
}
|
|
967
969
|
function normalizeProjectConfig(config) {
|
|
970
|
+
const firstSource = config.sources?.[0];
|
|
968
971
|
return {
|
|
969
|
-
|
|
970
|
-
|
|
972
|
+
$schema: config.$schema ?? STATELY_CONFIG_SCHEMA_URL,
|
|
973
|
+
version: config.version ?? STATELY_CONFIG_VERSION,
|
|
974
|
+
projectId: config.projectId ?? "",
|
|
975
|
+
studioUrl: config.studioUrl ?? "https://stately.ai",
|
|
976
|
+
defaultXStateVersion: Math.max(5, config.defaultXStateVersion ?? 5),
|
|
977
|
+
include: [...config.include ?? firstSource?.include ?? []],
|
|
978
|
+
exclude: config.exclude == null ? [...firstSource?.exclude ?? DEFAULT_SOURCE_EXCLUDES] : [...config.exclude]
|
|
971
979
|
};
|
|
972
980
|
}
|
|
973
981
|
async function readStatelyProjectConfig(options = {}) {
|
|
@@ -984,7 +992,7 @@ async function walkFiles(rootDir, currentDir = rootDir) {
|
|
|
984
992
|
const entries = await fsPromises.readdir(currentDir, { withFileTypes: true });
|
|
985
993
|
const files = [];
|
|
986
994
|
for (const entry of entries) {
|
|
987
|
-
if (entry.name
|
|
995
|
+
if (ALWAYS_IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
988
996
|
const absolutePath = path.join(currentDir, entry.name);
|
|
989
997
|
if (entry.isDirectory()) {
|
|
990
998
|
files.push(...await walkFiles(rootDir, absolutePath));
|
|
@@ -996,21 +1004,14 @@ async function walkFiles(rootDir, currentDir = rootDir) {
|
|
|
996
1004
|
return files;
|
|
997
1005
|
}
|
|
998
1006
|
async function filterGitIgnoredPaths(rootDir, relativeFiles) {
|
|
1007
|
+
if (relativeFiles.length === 0) return relativeFiles;
|
|
999
1008
|
try {
|
|
1000
|
-
const { stdout } = await execFileAsync$1("git", [
|
|
1001
|
-
"ls-files",
|
|
1002
|
-
"--others",
|
|
1003
|
-
"--ignored",
|
|
1004
|
-
"--exclude-standard",
|
|
1005
|
-
"--directory"
|
|
1006
|
-
], { cwd: rootDir });
|
|
1009
|
+
const { stdout } = await execFileAsync$1("git", ["check-ignore", ...relativeFiles], { cwd: rootDir });
|
|
1007
1010
|
const ignoredEntries = stdout.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
|
|
1008
1011
|
if (ignoredEntries.length === 0) return relativeFiles;
|
|
1012
|
+
const ignoredPaths = new Set(ignoredEntries);
|
|
1009
1013
|
return relativeFiles.filter((relativePath) => {
|
|
1010
|
-
return !
|
|
1011
|
-
if (ignoredEntry.endsWith("/")) return relativePath.startsWith(ignoredEntry);
|
|
1012
|
-
return relativePath === ignoredEntry;
|
|
1013
|
-
});
|
|
1014
|
+
return !ignoredPaths.has(relativePath);
|
|
1014
1015
|
});
|
|
1015
1016
|
} catch {
|
|
1016
1017
|
return relativeFiles;
|
|
@@ -1063,30 +1064,51 @@ async function discoverCodeSourceFiles(options = {}) {
|
|
|
1063
1064
|
const rootDir = path.resolve(options.cwd ?? process.cwd());
|
|
1064
1065
|
return (await filterGitIgnoredPaths(rootDir, await walkFiles(rootDir))).filter((relativePath) => isCodeSourceFile(relativePath)).filter((relativePath) => !matchesAny(DEFAULT_SOURCE_EXCLUDES, relativePath)).sort((left, right) => left.localeCompare(right));
|
|
1065
1066
|
}
|
|
1066
|
-
function
|
|
1067
|
+
function createSuggestedConfig(include) {
|
|
1067
1068
|
return {
|
|
1068
1069
|
include: [include],
|
|
1069
|
-
exclude: [...DEFAULT_SOURCE_EXCLUDES]
|
|
1070
|
-
format: "xstate",
|
|
1071
|
-
xstateVersion: 5
|
|
1070
|
+
exclude: [...DEFAULT_SOURCE_EXCLUDES]
|
|
1072
1071
|
};
|
|
1073
1072
|
}
|
|
1074
|
-
function
|
|
1073
|
+
function toExtensionToken(extname) {
|
|
1074
|
+
return extname.startsWith(".") ? extname.slice(1) : extname;
|
|
1075
|
+
}
|
|
1076
|
+
function formatExtensionPattern(extnames) {
|
|
1077
|
+
const unique = [...new Set(extnames.map(toExtensionToken))].sort();
|
|
1078
|
+
if (unique.length === 1) return `.${unique[0]}`;
|
|
1079
|
+
return `.{${unique.join(",")}}`;
|
|
1080
|
+
}
|
|
1081
|
+
function getSuggestionRoot(relativePath) {
|
|
1082
|
+
if (relativePath.startsWith("src/")) return "src/";
|
|
1083
|
+
const packageMatch = relativePath.match(/^packages\/([^/]+)\/src\//);
|
|
1084
|
+
if (packageMatch) return `packages/${packageMatch[1]}/src/`;
|
|
1085
|
+
const appMatch = relativePath.match(/^apps\/([^/]+)\/src\//);
|
|
1086
|
+
if (appMatch) return `apps/${appMatch[1]}/src/`;
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
function suggestGroupedInclude(relativePaths, root) {
|
|
1090
|
+
const extPattern = formatExtensionPattern(relativePaths.map((relativePath) => path.posix.extname(relativePath)));
|
|
1091
|
+
if (relativePaths.map((relativePath) => path.posix.basename(relativePath, path.posix.extname(relativePath))).every((basename) => basename.toLowerCase().endsWith("machine"))) return `${root}**/*machine${extPattern}`;
|
|
1092
|
+
return `${root}**/*${extPattern}`;
|
|
1093
|
+
}
|
|
1094
|
+
function suggestStatelySourceConfigs(relativePaths) {
|
|
1075
1095
|
const pending = new Set(relativePaths.map((relativePath) => relativePath.replace(/\\/g, "/")).filter(Boolean));
|
|
1076
1096
|
const suggestions = [];
|
|
1077
1097
|
const addSuggestion = (include, predicate) => {
|
|
1078
1098
|
const matched = [...pending].filter(predicate);
|
|
1079
1099
|
if (matched.length === 0) return;
|
|
1080
|
-
suggestions.push(
|
|
1081
|
-
...createSuggestedSource(include),
|
|
1082
|
-
xstateVersion: defaultXStateVersion
|
|
1083
|
-
});
|
|
1100
|
+
suggestions.push(createSuggestedConfig(include));
|
|
1084
1101
|
for (const matchedPath of matched) pending.delete(matchedPath);
|
|
1085
1102
|
};
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1103
|
+
const groupedRoots = /* @__PURE__ */ new Map();
|
|
1104
|
+
for (const relativePath of pending) {
|
|
1105
|
+
const root = getSuggestionRoot(relativePath);
|
|
1106
|
+
if (!root) continue;
|
|
1107
|
+
const bucket = groupedRoots.get(root) ?? [];
|
|
1108
|
+
bucket.push(relativePath);
|
|
1109
|
+
groupedRoots.set(root, bucket);
|
|
1110
|
+
}
|
|
1111
|
+
for (const [root, groupedPaths] of [...groupedRoots.entries()].sort((left, right) => left[0].localeCompare(right[0]))) addSuggestion(suggestGroupedInclude(groupedPaths, root), (relativePath) => groupedPaths.includes(relativePath));
|
|
1090
1112
|
const byDirectory = /* @__PURE__ */ new Map();
|
|
1091
1113
|
for (const relativePath of pending) {
|
|
1092
1114
|
const directory = path.posix.dirname(relativePath);
|
|
@@ -1112,7 +1134,13 @@ async function discoverStatelySourceFiles(options = {}) {
|
|
|
1112
1134
|
} : await readStatelyProjectConfig(options);
|
|
1113
1135
|
const relativeFiles = await filterGitIgnoredPaths(rootDir, await walkFiles(rootDir));
|
|
1114
1136
|
const discovered = /* @__PURE__ */ new Map();
|
|
1115
|
-
|
|
1137
|
+
const source = {
|
|
1138
|
+
include: config.include,
|
|
1139
|
+
exclude: config.exclude ?? DEFAULT_SOURCE_EXCLUDES,
|
|
1140
|
+
format: "xstate",
|
|
1141
|
+
xstateVersion: config.defaultXStateVersion
|
|
1142
|
+
};
|
|
1143
|
+
for (const relativePath of relativeFiles) {
|
|
1116
1144
|
if (!matchesAny(source.include, relativePath)) continue;
|
|
1117
1145
|
if (matchesAny(source.exclude, relativePath)) continue;
|
|
1118
1146
|
const filePath = path.join(rootDir, relativePath);
|
|
@@ -1293,6 +1321,13 @@ function buildMachineEditorUrl(studioUrl, projectId, machineId) {
|
|
|
1293
1321
|
url.searchParams.set("machineId", machineId);
|
|
1294
1322
|
return url.toString();
|
|
1295
1323
|
}
|
|
1324
|
+
function isRelinkableLinkedMachineError(error) {
|
|
1325
|
+
return error instanceof StudioApiError && (error.status === 403 || error.status === 404);
|
|
1326
|
+
}
|
|
1327
|
+
async function fileHasLinkedMachineId(filePath) {
|
|
1328
|
+
const contents = await fsPromises.readFile(filePath, "utf8");
|
|
1329
|
+
return Boolean(getStatelyPragma(contents, filePath)?.id);
|
|
1330
|
+
}
|
|
1296
1331
|
async function fileExists(filePath) {
|
|
1297
1332
|
try {
|
|
1298
1333
|
await fsPromises.access(filePath);
|
|
@@ -1414,13 +1449,28 @@ async function scanProjectSources(options) {
|
|
|
1414
1449
|
const filePath = path.join(cwd, relativePath);
|
|
1415
1450
|
if (looksLikeXStateMachineSource(await fsPromises.readFile(filePath, "utf8"))) machineRelativePaths.push(relativePath);
|
|
1416
1451
|
}
|
|
1417
|
-
return suggestStatelySourceConfigs(machineRelativePaths
|
|
1452
|
+
return suggestStatelySourceConfigs(machineRelativePaths);
|
|
1418
1453
|
}
|
|
1419
1454
|
function looksLikeXStateMachineSource(source) {
|
|
1420
1455
|
const hasXStateImport = /from\s+['"]xstate(?:\/[^'"]+)?['"]/.test(source) || /require\(\s*['"]xstate(?:\/[^'"]+)?['"]\s*\)/.test(source);
|
|
1421
1456
|
const hasMachineFactory = /\bcreateMachine\s*\(/.test(source) || /\.createMachine\s*\(/.test(source);
|
|
1422
1457
|
return hasXStateImport && hasMachineFactory;
|
|
1423
1458
|
}
|
|
1459
|
+
async function classifyPushCandidates(files) {
|
|
1460
|
+
const machineFiles = [];
|
|
1461
|
+
const nonMachineFiles = [];
|
|
1462
|
+
for (const file of files) {
|
|
1463
|
+
if (looksLikeXStateMachineSource(await fsPromises.readFile(file.filePath, "utf8"))) {
|
|
1464
|
+
machineFiles.push(file);
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1467
|
+
nonMachineFiles.push(file);
|
|
1468
|
+
}
|
|
1469
|
+
return {
|
|
1470
|
+
machineFiles,
|
|
1471
|
+
nonMachineFiles
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1424
1474
|
function supportsMachineDiscovery(file) {
|
|
1425
1475
|
return file.source.format === "xstate" || file.source.format === "auto";
|
|
1426
1476
|
}
|
|
@@ -1629,14 +1679,28 @@ var PushCommand = class PushCommand extends Command {
|
|
|
1629
1679
|
help: Flags.help({ char: "h" }),
|
|
1630
1680
|
"api-key": Flags.string({ description: "Stately API key used to create or update remote machines" }),
|
|
1631
1681
|
"base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" }),
|
|
1632
|
-
config: Flags.string({ description: "Path to statelyai.json" })
|
|
1682
|
+
config: Flags.string({ description: "Path to statelyai.json" }),
|
|
1683
|
+
"dry-run": Flags.boolean({
|
|
1684
|
+
description: "Show which matched files contain machines and what push would do without changing remote or local state",
|
|
1685
|
+
default: false
|
|
1686
|
+
})
|
|
1633
1687
|
};
|
|
1634
1688
|
async run() {
|
|
1635
1689
|
const { args, flags } = await this.parse(PushCommand);
|
|
1636
|
-
const resolvedApiKey = await resolveApiKey(flags["api-key"]);
|
|
1637
|
-
if (!resolvedApiKey.apiKey) this.error(getMissingApiKeyMessage());
|
|
1690
|
+
const resolvedApiKey = flags["dry-run"] ? { source: "missing" } : await resolveApiKey(flags["api-key"]);
|
|
1691
|
+
if (!flags["dry-run"] && !resolvedApiKey.apiKey) this.error(getMissingApiKeyMessage());
|
|
1638
1692
|
this.log("Resolving configured project and source files...");
|
|
1639
|
-
const { client, config, files } = await
|
|
1693
|
+
const { client, config, files } = flags["dry-run"] ? await (async () => {
|
|
1694
|
+
const { config, rootDir } = await readStatelyProjectConfig({ configPath: flags.config });
|
|
1695
|
+
return {
|
|
1696
|
+
client: void 0,
|
|
1697
|
+
config,
|
|
1698
|
+
files: await discoverStatelySourceFiles({
|
|
1699
|
+
cwd: rootDir,
|
|
1700
|
+
config
|
|
1701
|
+
})
|
|
1702
|
+
};
|
|
1703
|
+
})() : await resolveConfiguredProject({
|
|
1640
1704
|
apiKey: resolvedApiKey.apiKey,
|
|
1641
1705
|
baseUrl: flags["base-url"],
|
|
1642
1706
|
configPath: flags.config
|
|
@@ -1646,6 +1710,7 @@ var PushCommand = class PushCommand extends Command {
|
|
|
1646
1710
|
relativePath: path.relative(process.cwd(), path.resolve(args.file)),
|
|
1647
1711
|
source: {
|
|
1648
1712
|
include: [args.file],
|
|
1713
|
+
exclude: [],
|
|
1649
1714
|
format: "xstate",
|
|
1650
1715
|
xstateVersion: config.defaultXStateVersion
|
|
1651
1716
|
}
|
|
@@ -1654,24 +1719,66 @@ var PushCommand = class PushCommand extends Command {
|
|
|
1654
1719
|
this.log("No matching local machine source files were discovered.");
|
|
1655
1720
|
return;
|
|
1656
1721
|
}
|
|
1657
|
-
|
|
1722
|
+
const { machineFiles } = await classifyPushCandidates(candidateFiles);
|
|
1723
|
+
if (machineFiles.length === 0) {
|
|
1724
|
+
this.log(`Matched ${candidateFiles.length} ${pluralize(candidateFiles.length, "file")}, but no local machine sources were discovered.`);
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
this.log(`Discovered ${machineFiles.length} ${pluralize(machineFiles.length, "file with a machine")} from ${candidateFiles.length} matched ${pluralize(candidateFiles.length, "file")}.`);
|
|
1728
|
+
if (flags["dry-run"]) {
|
|
1729
|
+
const wouldLink = [];
|
|
1730
|
+
const wouldUpdate = [];
|
|
1731
|
+
for (const file of machineFiles) {
|
|
1732
|
+
const pragma = getStatelyPragma(await fsPromises.readFile(file.filePath, "utf8"), file.filePath);
|
|
1733
|
+
if (pragma?.id) wouldUpdate.push(`${file.relativePath}: ${pragma.id}`);
|
|
1734
|
+
else wouldLink.push(file.relativePath);
|
|
1735
|
+
}
|
|
1736
|
+
if (wouldLink.length > 0) this.log(`Would link:\n${wouldLink.join("\n")}`);
|
|
1737
|
+
if (wouldUpdate.length > 0) this.log(`Would update:\n${wouldUpdate.join("\n")}`);
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
this.log(`Processing ${machineFiles.length} ${pluralize(machineFiles.length, "source file")}...`);
|
|
1658
1741
|
const linked = [];
|
|
1659
1742
|
const refreshed = [];
|
|
1660
1743
|
const skipped = [];
|
|
1661
|
-
for (const file of
|
|
1744
|
+
for (const file of machineFiles) {
|
|
1662
1745
|
this.log(`Pushing ${file.relativePath}...`);
|
|
1663
1746
|
if (!supportsMachineDiscovery(file)) {
|
|
1664
1747
|
skipped.push(`${file.relativePath}: unsupported format ${file.source.format}`);
|
|
1665
1748
|
continue;
|
|
1666
1749
|
}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1750
|
+
let result;
|
|
1751
|
+
try {
|
|
1752
|
+
result = await pushLocalMachineLinks({
|
|
1753
|
+
source: file.filePath,
|
|
1754
|
+
apiKey: resolvedApiKey.apiKey,
|
|
1755
|
+
baseUrl: flags["base-url"] ?? config.studioUrl,
|
|
1756
|
+
client,
|
|
1757
|
+
project: { projectId: config.projectId },
|
|
1758
|
+
xstateVersion: Math.max(5, file.source.xstateVersion ?? config.defaultXStateVersion)
|
|
1759
|
+
});
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
if (isRelinkableLinkedMachineError(error) && await fileHasLinkedMachineId(file.filePath)) {
|
|
1762
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1763
|
+
skipped.push(`${file.relativePath}: linked remote machine is missing or inaccessible. Re-run interactively to relink it as a new machine.`);
|
|
1764
|
+
continue;
|
|
1765
|
+
}
|
|
1766
|
+
if (!await promptYesNo(`Linked remote machine for ${file.relativePath} is missing or inaccessible. Push it as a new machine and replace the local @statelyai id?`, true)) {
|
|
1767
|
+
skipped.push(`${file.relativePath}: linked remote machine is missing or inaccessible.`);
|
|
1768
|
+
continue;
|
|
1769
|
+
}
|
|
1770
|
+
this.log(`Relinking ${file.relativePath} as a new remote machine...`);
|
|
1771
|
+
result = await pushLocalMachineLinks({
|
|
1772
|
+
source: file.filePath,
|
|
1773
|
+
apiKey: resolvedApiKey.apiKey,
|
|
1774
|
+
baseUrl: flags["base-url"] ?? config.studioUrl,
|
|
1775
|
+
client,
|
|
1776
|
+
project: { projectId: config.projectId },
|
|
1777
|
+
xstateVersion: Math.max(5, file.source.xstateVersion ?? config.defaultXStateVersion),
|
|
1778
|
+
relink: true
|
|
1779
|
+
});
|
|
1780
|
+
} else throw error;
|
|
1781
|
+
}
|
|
1675
1782
|
if (result.created.length > 0) linked.push(`${file.relativePath}: ${result.created.map(({ machineIndex, machine }) => `${machine.id} [${machineIndex}] ${buildMachineEditorUrl(config.studioUrl, config.projectId, machine.id)}`).join(", ")}`);
|
|
1676
1783
|
if (result.updated.length > 0) refreshed.push(`${file.relativePath}: ${result.updated.map(({ machineIndex, machine }) => `${machine.id} [${machineIndex}] ${buildMachineEditorUrl(config.studioUrl, config.projectId, machine.id)}`).join(", ")}`);
|
|
1677
1784
|
for (const entry of result.skipped) skipped.push(`${file.relativePath} [${entry.machineIndex}]: ${entry.reason}`);
|
|
@@ -1776,7 +1883,7 @@ var InitCommand = class InitCommand extends Command {
|
|
|
1776
1883
|
defaultXStateVersion: result.config.defaultXStateVersion
|
|
1777
1884
|
});
|
|
1778
1885
|
if (suggestions.length === 0) {
|
|
1779
|
-
this.log(`${initMessage}\nNo
|
|
1886
|
+
this.log(`${initMessage}\nNo include globs were suggested from files with a machine. Edit statelyai.json before running statelyai push.`);
|
|
1780
1887
|
return;
|
|
1781
1888
|
}
|
|
1782
1889
|
this.log(initMessage);
|
|
@@ -1784,14 +1891,15 @@ var InitCommand = class InitCommand extends Command {
|
|
|
1784
1891
|
if (await promptYesNo("Save these source globs to statelyai.json?", true)) {
|
|
1785
1892
|
const nextConfig = {
|
|
1786
1893
|
...result.config,
|
|
1787
|
-
|
|
1894
|
+
include: suggestions.flatMap((suggestion) => suggestion.include),
|
|
1895
|
+
exclude: [...new Set(suggestions.flatMap((suggestion) => suggestion.exclude ?? []))]
|
|
1788
1896
|
};
|
|
1789
1897
|
await fsPromises.writeFile(result.configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
|
|
1790
1898
|
this.log("Saved scanned source globs to statelyai.json.");
|
|
1791
|
-
} else this.log("Left statelyai.json
|
|
1899
|
+
} else this.log("Left statelyai.json unchanged. Edit it before running statelyai push.");
|
|
1792
1900
|
return;
|
|
1793
1901
|
}
|
|
1794
|
-
this.log(`${initMessage}\nNo
|
|
1902
|
+
this.log(`${initMessage}\nNo include globs configured yet. Edit statelyai.json before running statelyai push, or rerun with --scan.`);
|
|
1795
1903
|
}
|
|
1796
1904
|
};
|
|
1797
1905
|
var LoginCommand = class LoginCommand extends Command {
|
|
@@ -1883,4 +1991,4 @@ function isDirectExecution() {
|
|
|
1883
1991
|
if (isDirectExecution()) run$1();
|
|
1884
1992
|
|
|
1885
1993
|
//#endregion
|
|
1886
|
-
export {
|
|
1994
|
+
export { getEnvApiKey as a, isFileGitDirty as c, scanProjectSources as d, createStatelyProjectConfig as f, formatPlanSummary as i, resolveApiKey as l, classifyPushCandidates as n, inferInitProjectName as o, discoverLinkedPullTargets as r, initProject as s, COMMANDS as t, run$1 as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -16,7 +16,8 @@ interface StatelyProjectConfig {
|
|
|
16
16
|
projectId: string;
|
|
17
17
|
studioUrl: string;
|
|
18
18
|
defaultXStateVersion: number;
|
|
19
|
-
|
|
19
|
+
include: string[];
|
|
20
|
+
exclude?: string[];
|
|
20
21
|
}
|
|
21
22
|
interface DiscoveredSourceFile {
|
|
22
23
|
filePath: string;
|
|
@@ -54,6 +55,10 @@ interface LinkedPullTarget {
|
|
|
54
55
|
file: DiscoveredSourceFile;
|
|
55
56
|
machineId: string;
|
|
56
57
|
}
|
|
58
|
+
interface PushCandidateClassification {
|
|
59
|
+
machineFiles: DiscoveredSourceFile[];
|
|
60
|
+
nonMachineFiles: DiscoveredSourceFile[];
|
|
61
|
+
}
|
|
57
62
|
type ApiKeyResolution = {
|
|
58
63
|
apiKey: string;
|
|
59
64
|
detail: string;
|
|
@@ -72,7 +77,8 @@ declare function inferInitProjectName(cwd: string, repo?: ConnectedRepo): string
|
|
|
72
77
|
declare function isFileGitDirty(filePath: string): Promise<boolean>;
|
|
73
78
|
declare function discoverLinkedPullTargets(files: DiscoveredSourceFile[]): Promise<LinkedPullTarget[]>;
|
|
74
79
|
declare function initProject(options: InitProjectOptions): Promise<InitProjectResult>;
|
|
75
|
-
declare function scanProjectSources(options: ScanProjectSourcesOptions): Promise<
|
|
80
|
+
declare function scanProjectSources(options: ScanProjectSourcesOptions): Promise<Array<Pick<StatelyProjectConfig, 'include' | 'exclude'>>>;
|
|
81
|
+
declare function classifyPushCandidates(files: DiscoveredSourceFile[]): Promise<PushCandidateClassification>;
|
|
76
82
|
declare function formatPlanSummary(plan: SyncPlan): string;
|
|
77
83
|
declare abstract class BaseSyncCommand extends Command {
|
|
78
84
|
static enableJsonFlag: boolean;
|
|
@@ -142,6 +148,7 @@ declare class PushCommand extends Command {
|
|
|
142
148
|
'api-key': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
143
149
|
'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
144
150
|
config: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
151
|
+
'dry-run': _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
145
152
|
};
|
|
146
153
|
run(): Promise<void>;
|
|
147
154
|
}
|
|
@@ -220,4 +227,4 @@ declare const COMMANDS: {
|
|
|
220
227
|
};
|
|
221
228
|
declare function run(argv?: any, entryUrl?: string): Promise<void>;
|
|
222
229
|
//#endregion
|
|
223
|
-
export { ApiKeyResolution, COMMANDS, InitProjectOptions, InitProjectResult, LinkedPullTarget, ScanProjectSourcesOptions, createStatelyProjectConfig, discoverLinkedPullTargets, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, run, scanProjectSources };
|
|
230
|
+
export { ApiKeyResolution, COMMANDS, InitProjectOptions, InitProjectResult, LinkedPullTarget, PushCandidateClassification, ScanProjectSourcesOptions, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, run, scanProjectSources };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as getEnvApiKey, c as isFileGitDirty, d as scanProjectSources, f as createStatelyProjectConfig, i as formatPlanSummary, l as resolveApiKey, n as classifyPushCandidates, o as inferInitProjectName, r as discoverLinkedPullTargets, s as initProject, t as COMMANDS, u as run } from "./cli-DkWxDq0T.mjs";
|
|
2
2
|
|
|
3
|
-
export { COMMANDS, createStatelyProjectConfig, discoverLinkedPullTargets, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, run, scanProjectSources };
|
|
3
|
+
export { COMMANDS, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, run, scanProjectSources };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "statelyai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
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.9.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsdown": "0.21.0-beta.2",
|