@synergenius/flow-weaver 0.23.1 → 0.23.4
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 +93 -442
- package/dist/agent/device-connection.d.ts +7 -0
- package/dist/agent/device-connection.js +7 -0
- package/dist/cli/commands/auth.js +33 -23
- package/dist/cli/commands/connect.d.ts +2 -0
- package/dist/cli/commands/connect.js +95 -1
- package/dist/cli/flow-weaver.mjs +182 -33
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/generator/unified.js +47 -7
- package/dist/marketplace/index.d.ts +1 -1
- package/dist/marketplace/index.js +1 -1
- package/dist/marketplace/registry.d.ts +16 -0
- package/dist/marketplace/registry.js +21 -0
- package/dist/marketplace/types.d.ts +2 -0
- package/package.json +3 -2
|
@@ -3,8 +3,9 @@ import { loadCredentials, saveCredentials, clearCredentials, getPlatformUrl } fr
|
|
|
3
3
|
import { PlatformClient } from '../config/platform-client.js';
|
|
4
4
|
export async function loginCommand(options) {
|
|
5
5
|
const platformUrl = options.platformUrl ?? getPlatformUrl();
|
|
6
|
+
const displayUrl = platformUrl.replace(/^https?:\/\//, '');
|
|
6
7
|
console.log('');
|
|
7
|
-
console.log(
|
|
8
|
+
console.log(` \x1b[1mFlow Weaver\x1b[0m \x1b[2m(${displayUrl})\x1b[0m`);
|
|
8
9
|
console.log('');
|
|
9
10
|
// API key mode (for CI/headless)
|
|
10
11
|
if (options.apiKey) {
|
|
@@ -42,8 +43,8 @@ async function loginWithBrowser(platformUrl) {
|
|
|
42
43
|
interval = data.interval ?? 5;
|
|
43
44
|
}
|
|
44
45
|
catch {
|
|
45
|
-
console.error(
|
|
46
|
-
console.error(' Check
|
|
46
|
+
console.error(` \x1b[31m✗\x1b[0m Cannot connect to ${platformUrl}`);
|
|
47
|
+
console.error(' Check the URL or set FW_PLATFORM_URL');
|
|
47
48
|
process.exit(1);
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
@@ -65,20 +66,24 @@ async function loginWithBrowser(platformUrl) {
|
|
|
65
66
|
}
|
|
66
67
|
console.log('');
|
|
67
68
|
// Step 3: Poll for completion
|
|
68
|
-
process.
|
|
69
|
+
process.stderr.write(' Waiting for approval');
|
|
69
70
|
let cancelled = false;
|
|
70
71
|
const sigHandler = () => { cancelled = true; };
|
|
71
72
|
process.on('SIGINT', sigHandler);
|
|
72
73
|
const maxAttempts = 120; // 10 minutes at 5s intervals
|
|
74
|
+
let networkErrors = 0;
|
|
73
75
|
for (let i = 0; i < maxAttempts && !cancelled; i++) {
|
|
74
76
|
await new Promise(r => setTimeout(r, interval * 1000));
|
|
75
77
|
try {
|
|
76
78
|
const resp = await fetch(`${platformUrl}/auth/device/poll?deviceCode=${deviceCode}`);
|
|
77
|
-
|
|
79
|
+
networkErrors = 0; // reset on successful connection
|
|
80
|
+
if (!resp.ok) {
|
|
81
|
+
process.stderr.write('.');
|
|
78
82
|
continue;
|
|
83
|
+
}
|
|
79
84
|
const data = await resp.json();
|
|
80
85
|
if (data.status === 'approved' && data.token && data.user) {
|
|
81
|
-
process.
|
|
86
|
+
process.stderr.write(' \x1b[32m✓\x1b[0m\n\n');
|
|
82
87
|
saveCredentials({
|
|
83
88
|
token: data.token,
|
|
84
89
|
email: data.user.email,
|
|
@@ -95,7 +100,7 @@ async function loginWithBrowser(platformUrl) {
|
|
|
95
100
|
return;
|
|
96
101
|
}
|
|
97
102
|
if (data.status === 'expired') {
|
|
98
|
-
process.
|
|
103
|
+
process.stderr.write(' \x1b[31mtimed out\x1b[0m\n\n');
|
|
99
104
|
console.log(' Code expired. Run \x1b[36mfw login\x1b[0m again.');
|
|
100
105
|
console.log('');
|
|
101
106
|
process.removeListener('SIGINT', sigHandler);
|
|
@@ -103,27 +108,30 @@ async function loginWithBrowser(platformUrl) {
|
|
|
103
108
|
return;
|
|
104
109
|
}
|
|
105
110
|
if (data.status === 'denied') {
|
|
106
|
-
process.
|
|
111
|
+
process.stderr.write(' \x1b[31mdenied\x1b[0m\n\n');
|
|
107
112
|
console.log(' Access denied.');
|
|
108
113
|
console.log('');
|
|
109
114
|
process.removeListener('SIGINT', sigHandler);
|
|
110
115
|
process.exit(1);
|
|
111
116
|
return;
|
|
112
117
|
}
|
|
113
|
-
// Still pending — show a dot
|
|
114
|
-
|
|
115
|
-
process.stdout.write('.');
|
|
118
|
+
// Still pending — show a dot every poll
|
|
119
|
+
process.stderr.write('.');
|
|
116
120
|
}
|
|
117
121
|
catch {
|
|
118
|
-
|
|
122
|
+
networkErrors++;
|
|
123
|
+
if (networkErrors >= 3) {
|
|
124
|
+
process.stderr.write(' \x1b[33m!\x1b[0m');
|
|
125
|
+
networkErrors = 0;
|
|
126
|
+
}
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
process.removeListener('SIGINT', sigHandler);
|
|
122
130
|
if (cancelled) {
|
|
123
|
-
process.
|
|
131
|
+
process.stderr.write(' \x1b[33mcancelled\x1b[0m\n\n');
|
|
124
132
|
}
|
|
125
133
|
else {
|
|
126
|
-
process.
|
|
134
|
+
process.stderr.write(' \x1b[31mtimed out\x1b[0m\n\n');
|
|
127
135
|
console.log(' Authentication timed out. Run \x1b[36mfw login\x1b[0m again.');
|
|
128
136
|
console.log('');
|
|
129
137
|
}
|
|
@@ -209,23 +217,24 @@ export async function authStatusCommand() {
|
|
|
209
217
|
console.log('');
|
|
210
218
|
}
|
|
211
219
|
function prompt(message, hidden = false) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// Hide password input
|
|
220
|
+
if (hidden && process.stdin.isTTY) {
|
|
221
|
+
// Raw-mode password input — no readline (it competes for stdin)
|
|
222
|
+
return new Promise((resolve) => {
|
|
216
223
|
process.stderr.write(message);
|
|
217
224
|
process.stdin.setRawMode(true);
|
|
225
|
+
process.stdin.resume();
|
|
218
226
|
let input = '';
|
|
219
227
|
const handler = (key) => {
|
|
220
228
|
const ch = key.toString();
|
|
221
229
|
if (ch === '\r' || ch === '\n') {
|
|
222
230
|
process.stdin.setRawMode(false);
|
|
231
|
+
process.stdin.pause();
|
|
223
232
|
process.stdin.removeListener('data', handler);
|
|
224
233
|
process.stderr.write('\n');
|
|
225
|
-
rl.close();
|
|
226
234
|
resolve(input);
|
|
227
235
|
}
|
|
228
236
|
else if (ch === '\x03') { // Ctrl+C
|
|
237
|
+
process.stdin.setRawMode(false);
|
|
229
238
|
process.exit(1);
|
|
230
239
|
}
|
|
231
240
|
else if (ch === '\x7f') { // Backspace
|
|
@@ -236,10 +245,11 @@ function prompt(message, hidden = false) {
|
|
|
236
245
|
}
|
|
237
246
|
};
|
|
238
247
|
process.stdin.on('data', handler);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
return new Promise((resolve) => {
|
|
251
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
252
|
+
rl.question(message, (answer) => { rl.close(); resolve(answer); });
|
|
243
253
|
});
|
|
244
254
|
}
|
|
245
255
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
import { DeviceConnection } from '../../agent/device-connection.js';
|
|
2
|
+
export declare function loadPackDeviceHandlers(conn: DeviceConnection, projectDir: string): Promise<string[]>;
|
|
1
3
|
export declare function handleConnect(projectDir: string): Promise<void>;
|
|
2
4
|
//# sourceMappingURL=connect.d.ts.map
|
|
@@ -1,7 +1,42 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
|
+
import * as readline from 'node:readline';
|
|
4
5
|
import { DeviceConnection } from '../../agent/device-connection.js';
|
|
6
|
+
import { discoverDeviceHandlers } from '../../marketplace/registry.js';
|
|
7
|
+
function promptYesNo(message) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
10
|
+
rl.question(message, (answer) => {
|
|
11
|
+
rl.close();
|
|
12
|
+
const normalized = answer.trim().toLowerCase();
|
|
13
|
+
resolve(normalized === '' || normalized === 'y' || normalized === 'yes');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export async function loadPackDeviceHandlers(conn, projectDir) {
|
|
18
|
+
const loadedPacks = [];
|
|
19
|
+
try {
|
|
20
|
+
const handlers = await discoverDeviceHandlers(projectDir);
|
|
21
|
+
for (const handler of handlers) {
|
|
22
|
+
try {
|
|
23
|
+
const mod = await import(handler.entrypoint);
|
|
24
|
+
if (typeof mod.register === 'function') {
|
|
25
|
+
await mod.register(conn, { projectDir });
|
|
26
|
+
loadedPacks.push(handler.packageName);
|
|
27
|
+
process.stderr.write(` \x1b[2m+ ${handler.packageName} handlers\x1b[0m\n`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
process.stderr.write(` \x1b[33m⚠\x1b[0m Failed to load handlers from ${handler.packageName}: ${err instanceof Error ? err.message : err}\n`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Discovery failed — non-fatal
|
|
37
|
+
}
|
|
38
|
+
return loadedPacks;
|
|
39
|
+
}
|
|
5
40
|
export async function handleConnect(projectDir) {
|
|
6
41
|
// Load credentials
|
|
7
42
|
const credPath = path.join(os.homedir(), '.fw', 'credentials.json');
|
|
@@ -46,13 +81,72 @@ export async function handleConnect(projectDir) {
|
|
|
46
81
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
47
82
|
return entries
|
|
48
83
|
.filter(e => !e.name.startsWith('.') && e.name !== 'node_modules' && e.name !== 'dist')
|
|
49
|
-
.map(e => ({ name: e.name, type: e.isDirectory() ? 'directory' : 'file', path: path.relative(projectDir, path.join(dirPath, e.name)) }));
|
|
84
|
+
.map(e => ({ name: e.name, type: e.isDirectory() ? 'directory' : 'file', path: path.relative(projectDir, path.join(dirPath, e.name)), hasUnfetchedChildren: e.isDirectory() }));
|
|
50
85
|
});
|
|
86
|
+
// Load pack device handlers (if any installed packs provide them)
|
|
87
|
+
const loadedPacks = await loadPackDeviceHandlers(conn, projectDir);
|
|
88
|
+
// Tell the platform which packs contributed handlers (for auto-install)
|
|
89
|
+
if (loadedPacks.length > 0) {
|
|
90
|
+
conn.setPacks(loadedPacks);
|
|
91
|
+
}
|
|
51
92
|
console.log('');
|
|
52
93
|
console.log(' \x1b[1mflow-weaver connect\x1b[0m');
|
|
53
94
|
console.log(` \x1b[2mProject: ${path.basename(projectDir)}\x1b[0m`);
|
|
54
95
|
console.log(` \x1b[2mPlatform: ${creds.platformUrl}\x1b[0m`);
|
|
55
96
|
console.log('');
|
|
97
|
+
// Check if packs need to be installed in the Studio workspace
|
|
98
|
+
if (loadedPacks.length > 0) {
|
|
99
|
+
try {
|
|
100
|
+
const checkRes = await fetch(`${creds.platformUrl}/devices/check-packs`, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${creds.token}` },
|
|
103
|
+
body: JSON.stringify({ packs: loadedPacks }),
|
|
104
|
+
});
|
|
105
|
+
if (checkRes.ok) {
|
|
106
|
+
const { missing } = await checkRes.json();
|
|
107
|
+
if (missing.length > 0) {
|
|
108
|
+
console.log(' The following packs need to be installed in Studio:');
|
|
109
|
+
for (const p of missing) {
|
|
110
|
+
console.log(` \x1b[36m${p}\x1b[0m`);
|
|
111
|
+
}
|
|
112
|
+
console.log('');
|
|
113
|
+
const answer = await promptYesNo(' Install now? (Y/n) ');
|
|
114
|
+
if (answer) {
|
|
115
|
+
process.stderr.write(' Installing...');
|
|
116
|
+
const installRes = await fetch(`${creds.platformUrl}/devices/install-packs`, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${creds.token}` },
|
|
119
|
+
body: JSON.stringify({ packs: missing }),
|
|
120
|
+
});
|
|
121
|
+
if (installRes.ok) {
|
|
122
|
+
const { results } = await installRes.json();
|
|
123
|
+
const allOk = results.every(r => r.ok);
|
|
124
|
+
if (allOk) {
|
|
125
|
+
process.stderr.write(' \x1b[32m✓\x1b[0m\n\n');
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
process.stderr.write(' \x1b[33mpartial\x1b[0m\n');
|
|
129
|
+
for (const r of results) {
|
|
130
|
+
if (!r.ok)
|
|
131
|
+
console.log(` \x1b[31m✗\x1b[0m ${r.pack}: ${r.error}`);
|
|
132
|
+
}
|
|
133
|
+
console.log('');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
process.stderr.write(' \x1b[31mfailed\x1b[0m\n\n');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
console.log(' \x1b[2mSkipped. Install manually via Studio marketplace.\x1b[0m\n');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Check failed — non-fatal, continue connecting
|
|
148
|
+
}
|
|
149
|
+
}
|
|
56
150
|
try {
|
|
57
151
|
await conn.connect();
|
|
58
152
|
console.log(' \x1b[2mPress Ctrl+C to disconnect.\x1b[0m\n');
|