flowstride 1.0.15 → 1.0.16
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/dist/cli/index.js +94 -58
- package/dist/parser/parser.js +0 -7
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -54,7 +54,7 @@ const API_BASE = process.env.FLOWSTRIDE_API_URL || "https://cloud.flowstride.io"
|
|
|
54
54
|
program
|
|
55
55
|
.name("flowstride")
|
|
56
56
|
.description("Flowstride: The Enterprise Flow-First Automation CLI")
|
|
57
|
-
.version("1.0.
|
|
57
|
+
.version("1.0.16");
|
|
58
58
|
program
|
|
59
59
|
.command("init")
|
|
60
60
|
.description("Initialise a new Flowstride workspace in the current directory")
|
|
@@ -64,33 +64,40 @@ program
|
|
|
64
64
|
program
|
|
65
65
|
.command("login")
|
|
66
66
|
.description("Authenticate this machine with Flowstride Enterprise Cloud")
|
|
67
|
-
.
|
|
67
|
+
.option("-t, --token <token>", "Enterprise Developer Token")
|
|
68
|
+
.option("-g, --global", "Save the token globally for all projects on this machine", false)
|
|
69
|
+
.action(async (options) => {
|
|
68
70
|
const localPackageJsonPath = path.join(process.cwd(), "package.json");
|
|
69
|
-
if
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
// Enforce valid project directory ONLY if doing a local login
|
|
72
|
+
if (!options.global) {
|
|
73
|
+
if (!fs.existsSync(localPackageJsonPath)) {
|
|
74
|
+
console.error(chalk_1.default.red("✖ Error: Please navigate to a valid Flowstride project directory before logging in locally, or use --global."));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const pkg = JSON.parse(fs.readFileSync(localPackageJsonPath, "utf8"));
|
|
78
|
+
const deps = {
|
|
79
|
+
...(pkg.dependencies || {}),
|
|
80
|
+
...(pkg.devDependencies || {}),
|
|
81
|
+
};
|
|
82
|
+
if (!deps["flowstride"] && !deps["flowstride-os"]) {
|
|
83
|
+
console.error(chalk_1.default.red("✖ Error: Please navigate to a valid Flowstride project directory before logging in locally, or use --global."));
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
81
86
|
}
|
|
82
87
|
console.log(chalk_1.default.bold.blue("\n🌐 Flowstride Enterprise Cloud Sync\n"));
|
|
83
|
-
let
|
|
84
|
-
if (process.env.
|
|
85
|
-
console.log(chalk_1.default.yellow("🤖 CI/CD Environment Detected. Authenticating via
|
|
86
|
-
authHeader = `Bearer ${process.env.FLOWSTRIDE_API_KEY}`;
|
|
88
|
+
let token = process.env.FLOWSTRIDE_TOKEN || options.token;
|
|
89
|
+
if (process.env.FLOWSTRIDE_TOKEN) {
|
|
90
|
+
console.log(chalk_1.default.yellow("🤖 CI/CD Environment Detected. Authenticating via FLOWSTRIDE_TOKEN..."));
|
|
87
91
|
}
|
|
88
|
-
else {
|
|
89
|
-
|
|
90
|
-
message: "
|
|
92
|
+
else if (!token) {
|
|
93
|
+
token = await (0, prompts_1.password)({
|
|
94
|
+
message: "Paste your Enterprise Developer Token:",
|
|
95
|
+
mask: "*",
|
|
91
96
|
});
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
}
|
|
98
|
+
if (!token || token.trim().length === 0) {
|
|
99
|
+
console.error(chalk_1.default.red("✖ Error: A valid Developer Token is required."));
|
|
100
|
+
process.exit(1);
|
|
94
101
|
}
|
|
95
102
|
const deviceName = os.hostname();
|
|
96
103
|
const spinner = (0, ora_1.default)("Authenticating with Cloud...").start();
|
|
@@ -99,34 +106,37 @@ program
|
|
|
99
106
|
method: "POST",
|
|
100
107
|
headers: {
|
|
101
108
|
"Content-Type": "application/json",
|
|
102
|
-
Authorization:
|
|
109
|
+
Authorization: `Bearer ${token.trim()}`,
|
|
103
110
|
},
|
|
104
111
|
body: JSON.stringify({ deviceName }),
|
|
105
112
|
});
|
|
106
113
|
const data = await response.json();
|
|
107
114
|
if (!response.ok) {
|
|
108
|
-
spinner.fail(chalk_1.default.red(`Authentication Failed: ${data.error || "
|
|
115
|
+
spinner.fail(chalk_1.default.red(`Authentication Failed: ${data.error || "Invalid token or network error"}`));
|
|
109
116
|
process.exit(1);
|
|
110
117
|
}
|
|
111
118
|
spinner.succeed(chalk_1.default.green("Authentication Successful!"));
|
|
112
|
-
const configDir =
|
|
119
|
+
const configDir = options.global
|
|
120
|
+
? path.join(os.homedir(), ".flowstride")
|
|
121
|
+
: path.join(process.cwd(), ".flowstride");
|
|
113
122
|
if (!fs.existsSync(configDir)) {
|
|
114
123
|
fs.mkdirSync(configDir, { recursive: true });
|
|
115
124
|
}
|
|
116
|
-
const credentialsPath = path.join(configDir, "
|
|
125
|
+
const credentialsPath = path.join(configDir, "credentials.json");
|
|
117
126
|
fs.writeFileSync(credentialsPath, JSON.stringify({
|
|
118
|
-
machineToken: data.machineToken,
|
|
127
|
+
machineToken: data.machineToken || token.trim(),
|
|
119
128
|
workspaceId: data.workspaceId,
|
|
120
129
|
deviceName: deviceName,
|
|
121
130
|
syncedAt: new Date().toISOString(),
|
|
122
131
|
}, null, 2));
|
|
123
|
-
|
|
132
|
+
const scopeText = options.global ? "GLOBALLY" : "LOCALLY to this project";
|
|
133
|
+
console.log(chalk_1.default.gray(`\nCredentials securely scoped ${scopeText} at: ${credentialsPath}`));
|
|
124
134
|
if (data.isProEligible) {
|
|
125
135
|
console.log(chalk_1.default.yellow(`\n⚙️ System Check: You have access to Enterprise features (AI Healing, Parallel Workers).`));
|
|
126
136
|
const nodeModulesPath = path.join(process.cwd(), "node_modules", "@flowstride", "pro");
|
|
127
137
|
if (!fs.existsSync(nodeModulesPath)) {
|
|
128
138
|
let installPro = true;
|
|
129
|
-
if (!process.env.
|
|
139
|
+
if (!process.env.FLOWSTRIDE_TOKEN && !process.env.CI) {
|
|
130
140
|
installPro = await (0, prompts_1.confirm)({
|
|
131
141
|
message: "The Pro Engine is not installed in this project. Would you like Flowstride to automatically install it now?",
|
|
132
142
|
default: true,
|
|
@@ -138,7 +148,7 @@ program
|
|
|
138
148
|
try {
|
|
139
149
|
const tarballResponse = await fetch(`${API_BASE}/api/workspace/download-pro`, {
|
|
140
150
|
method: "GET",
|
|
141
|
-
headers: { Authorization: `Bearer ${
|
|
151
|
+
headers: { Authorization: `Bearer ${token.trim()}` },
|
|
142
152
|
});
|
|
143
153
|
if (!tarballResponse.ok) {
|
|
144
154
|
throw new Error(`Secure proxy rejected request: ${tarballResponse.statusText}`);
|
|
@@ -200,21 +210,34 @@ program
|
|
|
200
210
|
.command("status")
|
|
201
211
|
.description("Display the current Flowstride CLI authentication and engine tier")
|
|
202
212
|
.action(async () => {
|
|
203
|
-
let token = process.env.
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
213
|
+
let token = process.env.FLOWSTRIDE_TOKEN || null;
|
|
214
|
+
const localCredentialsPath = path.join(process.cwd(), ".flowstride", "credentials.json");
|
|
215
|
+
const globalCredentialsPath = path.join(os.homedir(), ".flowstride", "credentials.json");
|
|
216
|
+
let activeCredentialsPath = localCredentialsPath; // Default for cleanup
|
|
217
|
+
if (!token) {
|
|
218
|
+
if (fs.existsSync(localCredentialsPath)) {
|
|
219
|
+
activeCredentialsPath = localCredentialsPath;
|
|
220
|
+
try {
|
|
221
|
+
const credentials = JSON.parse(fs.readFileSync(localCredentialsPath, "utf8"));
|
|
222
|
+
token = credentials.machineToken;
|
|
223
|
+
}
|
|
224
|
+
catch (err) { }
|
|
225
|
+
}
|
|
226
|
+
else if (fs.existsSync(globalCredentialsPath)) {
|
|
227
|
+
activeCredentialsPath = globalCredentialsPath;
|
|
228
|
+
try {
|
|
229
|
+
const credentials = JSON.parse(fs.readFileSync(globalCredentialsPath, "utf8"));
|
|
230
|
+
token = credentials.machineToken;
|
|
231
|
+
}
|
|
232
|
+
catch (err) { }
|
|
210
233
|
}
|
|
211
|
-
catch (err) { }
|
|
212
234
|
}
|
|
213
|
-
|
|
235
|
+
const nodeModulesPath = path.join(process.cwd(), "node_modules", "@flowstride", "pro");
|
|
236
|
+
if (!process.env.FLOWSTRIDE_TOKEN &&
|
|
214
237
|
token &&
|
|
215
238
|
!fs.existsSync(nodeModulesPath)) {
|
|
216
|
-
if (fs.existsSync(
|
|
217
|
-
fs.unlinkSync(
|
|
239
|
+
if (fs.existsSync(activeCredentialsPath))
|
|
240
|
+
fs.unlinkSync(activeCredentialsPath);
|
|
218
241
|
token = null;
|
|
219
242
|
}
|
|
220
243
|
if (!token) {
|
|
@@ -228,8 +251,8 @@ program
|
|
|
228
251
|
});
|
|
229
252
|
if (response.status === 401) {
|
|
230
253
|
console.log(chalk_1.default.red("🔴 AUTH EXPIRED: Your device session was revoked or expired."));
|
|
231
|
-
if (fs.existsSync(
|
|
232
|
-
fs.unlinkSync(
|
|
254
|
+
if (fs.existsSync(activeCredentialsPath))
|
|
255
|
+
fs.unlinkSync(activeCredentialsPath);
|
|
233
256
|
}
|
|
234
257
|
else if (response.ok) {
|
|
235
258
|
const data = await response.json();
|
|
@@ -261,22 +284,35 @@ program
|
|
|
261
284
|
options = targetPath;
|
|
262
285
|
targetPath = undefined;
|
|
263
286
|
}
|
|
264
|
-
let token = process.env.
|
|
287
|
+
let token = process.env.FLOWSTRIDE_TOKEN || null;
|
|
265
288
|
let forceOS = false;
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
289
|
+
const localCredentialsPath = path.join(process.cwd(), ".flowstride", "credentials.json");
|
|
290
|
+
const globalCredentialsPath = path.join(os.homedir(), ".flowstride", "credentials.json");
|
|
291
|
+
let activeCredentialsPath = localCredentialsPath;
|
|
292
|
+
if (!token) {
|
|
293
|
+
if (fs.existsSync(localCredentialsPath)) {
|
|
294
|
+
activeCredentialsPath = localCredentialsPath;
|
|
295
|
+
try {
|
|
296
|
+
const credentials = JSON.parse(fs.readFileSync(localCredentialsPath, "utf8"));
|
|
297
|
+
token = credentials.machineToken;
|
|
298
|
+
}
|
|
299
|
+
catch (err) { }
|
|
300
|
+
}
|
|
301
|
+
else if (fs.existsSync(globalCredentialsPath)) {
|
|
302
|
+
activeCredentialsPath = globalCredentialsPath;
|
|
303
|
+
try {
|
|
304
|
+
const credentials = JSON.parse(fs.readFileSync(globalCredentialsPath, "utf8"));
|
|
305
|
+
token = credentials.machineToken;
|
|
306
|
+
}
|
|
307
|
+
catch (err) { }
|
|
272
308
|
}
|
|
273
|
-
catch (err) { }
|
|
274
309
|
}
|
|
275
|
-
|
|
310
|
+
const nodeModulesPath = path.join(process.cwd(), "node_modules", "@flowstride", "pro");
|
|
311
|
+
if (!process.env.FLOWSTRIDE_TOKEN &&
|
|
276
312
|
token &&
|
|
277
313
|
!fs.existsSync(nodeModulesPath)) {
|
|
278
|
-
if (fs.existsSync(
|
|
279
|
-
fs.unlinkSync(
|
|
314
|
+
if (fs.existsSync(activeCredentialsPath))
|
|
315
|
+
fs.unlinkSync(activeCredentialsPath);
|
|
280
316
|
token = null;
|
|
281
317
|
}
|
|
282
318
|
if (!token) {
|
|
@@ -291,8 +327,8 @@ program
|
|
|
291
327
|
});
|
|
292
328
|
if (response.status === 401) {
|
|
293
329
|
console.log(chalk_1.default.red("🔴 AUTH EXPIRED: Your device session was revoked or expired."));
|
|
294
|
-
if (fs.existsSync(
|
|
295
|
-
fs.unlinkSync(
|
|
330
|
+
if (fs.existsSync(activeCredentialsPath))
|
|
331
|
+
fs.unlinkSync(activeCredentialsPath);
|
|
296
332
|
forceOS = true;
|
|
297
333
|
}
|
|
298
334
|
else if (response.ok) {
|
|
@@ -328,7 +364,7 @@ program
|
|
|
328
364
|
});
|
|
329
365
|
async function handleOfflinePrompt() {
|
|
330
366
|
console.log(chalk_1.default.yellow("🛜 OFFLINE: Unable to reach Flowstride Cloud. Enterprise Sync paused."));
|
|
331
|
-
if (process.env.CI || process.env.
|
|
367
|
+
if (process.env.CI || process.env.FLOWSTRIDE_TOKEN) {
|
|
332
368
|
console.log(chalk_1.default.yellow("⚠️ CI/CD Environment: Automatically falling back to OS Engine."));
|
|
333
369
|
return true;
|
|
334
370
|
}
|
package/dist/parser/parser.js
CHANGED
|
@@ -357,7 +357,6 @@ class Parser {
|
|
|
357
357
|
throw new Error(`Line ${viaToken.line}: Expected 'via'`);
|
|
358
358
|
payload.verb = this.consume(lexer_1.TokenType.COMMAND, "Expected API verb (e.g. POST)").value.toLowerCase();
|
|
359
359
|
payload.url = this.consume(lexer_1.TokenType.STRING, "Endpoint URL expected").value;
|
|
360
|
-
// --- ENSURE ARRAY FORMAT FOR API CLIENT ---
|
|
361
360
|
payload.headers = [];
|
|
362
361
|
this.parseApiModifiers(payload);
|
|
363
362
|
break;
|
|
@@ -368,7 +367,6 @@ class Parser {
|
|
|
368
367
|
case "delete":
|
|
369
368
|
case "graphql":
|
|
370
369
|
payload.url = this.consume(lexer_1.TokenType.STRING, "Endpoint URL expected").value;
|
|
371
|
-
// --- ENSURE ARRAY FORMAT FOR API CLIENT ---
|
|
372
370
|
payload.headers = [];
|
|
373
371
|
this.parseApiModifiers(payload);
|
|
374
372
|
break;
|
|
@@ -617,16 +615,13 @@ class Parser {
|
|
|
617
615
|
throw new Error(`Syntax Error on line ${targetToken.line}: Invalid keyword '${target}'. Flowstride uses strict camelCase. Did you mean 'reqHeader'?`);
|
|
618
616
|
}
|
|
619
617
|
if (target === "reqHeader") {
|
|
620
|
-
// --- THE NEW MULTI-FORMAT HEADER PARSER ---
|
|
621
618
|
const firstToken = this.consume(lexer_1.TokenType.STRING, "Header key or JSON block expected");
|
|
622
|
-
// Lookahead: If the next token is ALSO a string, use the legacy Key-Value parsing
|
|
623
619
|
if (!this.isAtEnd() && this.peek().type === lexer_1.TokenType.STRING) {
|
|
624
620
|
const hKey = firstToken.value;
|
|
625
621
|
const hVal = this.advance().value;
|
|
626
622
|
payload.headers.push({ key: hKey, value: hVal });
|
|
627
623
|
}
|
|
628
624
|
else {
|
|
629
|
-
// Otherwise, it must be the new multi-line JSON Block format
|
|
630
625
|
try {
|
|
631
626
|
const headerObj = JSON.parse(firstToken.value);
|
|
632
627
|
if (typeof headerObj !== "object" ||
|
|
@@ -634,7 +629,6 @@ class Parser {
|
|
|
634
629
|
Array.isArray(headerObj)) {
|
|
635
630
|
throw new Error("Parsed JSON is not a Key-Value object.");
|
|
636
631
|
}
|
|
637
|
-
// Loop through the parsed block and push them into the array for the API client
|
|
638
632
|
for (const [k, v] of Object.entries(headerObj)) {
|
|
639
633
|
payload.headers.push({ key: k, value: String(v) });
|
|
640
634
|
}
|
|
@@ -643,7 +637,6 @@ class Parser {
|
|
|
643
637
|
throw new Error(`Line ${firstToken.line}: Invalid JSON format for reqHeader block. Ensure it is a valid JSON object, or use the legacy format with two strings (Key and Value).`);
|
|
644
638
|
}
|
|
645
639
|
}
|
|
646
|
-
// ------------------------------------------
|
|
647
640
|
}
|
|
648
641
|
else if (target === "reqBody") {
|
|
649
642
|
payload.body = this.consume(lexer_1.TokenType.STRING, "Body content expected").value;
|