@synergenius/flow-weaver 0.22.9 → 0.22.10
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/agent/index.d.ts +2 -0
- package/dist/agent/index.js +1 -0
- package/dist/agent/providers/platform.d.ts +23 -0
- package/dist/agent/providers/platform.js +130 -0
- package/dist/cli/commands/auth.d.ts +8 -0
- package/dist/cli/commands/auth.js +137 -0
- package/dist/cli/commands/deploy.d.ts +6 -0
- package/dist/cli/commands/deploy.js +97 -0
- package/dist/cli/commands/init.d.ts +2 -1
- package/dist/cli/commands/init.js +28 -2
- package/dist/cli/config/credentials.d.ts +15 -0
- package/dist/cli/config/credentials.js +43 -0
- package/dist/cli/config/platform-client.d.ts +36 -0
- package/dist/cli/config/platform-client.js +122 -0
- package/dist/cli/flow-weaver.mjs +806 -343
- package/dist/cli/index.js +66 -0
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/package.json +1 -1
package/dist/agent/index.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export type { AnthropicProviderOptions } from './providers/anthropic.js';
|
|
|
11
11
|
export { ClaudeCliProvider, createClaudeCliProvider } from './providers/claude-cli.js';
|
|
12
12
|
export { OpenAICompatProvider, createOpenAICompatProvider } from './providers/openai-compat.js';
|
|
13
13
|
export type { OpenAICompatProviderOptions } from './providers/openai-compat.js';
|
|
14
|
+
export { PlatformProvider, createPlatformProvider } from './providers/platform.js';
|
|
15
|
+
export type { PlatformProviderOptions } from './providers/platform.js';
|
|
14
16
|
export { createMcpBridge } from './mcp-bridge.js';
|
|
15
17
|
export { CliSession, getOrCreateCliSession, killCliSession, killAllCliSessions, } from './cli-session.js';
|
|
16
18
|
export { buildSafeEnv, buildSafeSpawnOpts, MINIMAL_PATH, ENV_ALLOWLIST } from './env-allowlist.js';
|
package/dist/agent/index.js
CHANGED
|
@@ -10,6 +10,7 @@ export { runAgentLoop } from './agent-loop.js';
|
|
|
10
10
|
export { AnthropicProvider, createAnthropicProvider } from './providers/anthropic.js';
|
|
11
11
|
export { ClaudeCliProvider, createClaudeCliProvider } from './providers/claude-cli.js';
|
|
12
12
|
export { OpenAICompatProvider, createOpenAICompatProvider } from './providers/openai-compat.js';
|
|
13
|
+
export { PlatformProvider, createPlatformProvider } from './providers/platform.js';
|
|
13
14
|
// MCP bridge
|
|
14
15
|
export { createMcpBridge } from './mcp-bridge.js';
|
|
15
16
|
// CLI session (warm persistent sessions)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform AI provider — routes AI calls through the Flow Weaver platform.
|
|
3
|
+
* Uses the platform's AI credits, no local API key needed.
|
|
4
|
+
* Connects to POST /ai-chat/stream and parses SSE events.
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentProvider, AgentMessage, ToolDefinition, StreamEvent, StreamOptions } from '../types.js';
|
|
7
|
+
export interface PlatformProviderOptions {
|
|
8
|
+
/** JWT token or API key for platform auth */
|
|
9
|
+
token: string;
|
|
10
|
+
/** Platform base URL */
|
|
11
|
+
platformUrl: string;
|
|
12
|
+
/** Model override (optional — platform selects default) */
|
|
13
|
+
model?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class PlatformProvider implements AgentProvider {
|
|
16
|
+
private token;
|
|
17
|
+
private baseUrl;
|
|
18
|
+
private model;
|
|
19
|
+
constructor(options: PlatformProviderOptions);
|
|
20
|
+
stream(messages: AgentMessage[], _tools: ToolDefinition[], options?: StreamOptions): AsyncGenerator<StreamEvent>;
|
|
21
|
+
}
|
|
22
|
+
export declare function createPlatformProvider(options: PlatformProviderOptions): PlatformProvider;
|
|
23
|
+
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform AI provider — routes AI calls through the Flow Weaver platform.
|
|
3
|
+
* Uses the platform's AI credits, no local API key needed.
|
|
4
|
+
* Connects to POST /ai-chat/stream and parses SSE events.
|
|
5
|
+
*/
|
|
6
|
+
export class PlatformProvider {
|
|
7
|
+
token;
|
|
8
|
+
baseUrl;
|
|
9
|
+
model;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
if (!options.token)
|
|
12
|
+
throw new Error('PlatformProvider requires a token');
|
|
13
|
+
if (!options.platformUrl)
|
|
14
|
+
throw new Error('PlatformProvider requires a platformUrl');
|
|
15
|
+
this.token = options.token;
|
|
16
|
+
this.baseUrl = options.platformUrl.replace(/\/+$/, '');
|
|
17
|
+
this.model = options.model;
|
|
18
|
+
}
|
|
19
|
+
async *stream(messages, _tools, options) {
|
|
20
|
+
// Format the last user message as the prompt
|
|
21
|
+
// The platform's agent loop handles tools internally
|
|
22
|
+
const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
|
|
23
|
+
const message = lastUserMsg
|
|
24
|
+
? (typeof lastUserMsg.content === 'string' ? lastUserMsg.content : JSON.stringify(lastUserMsg.content))
|
|
25
|
+
: '';
|
|
26
|
+
if (!message) {
|
|
27
|
+
yield { type: 'message_stop', finishReason: 'stop' };
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const isApiKey = this.token.startsWith('fw_');
|
|
31
|
+
const headers = {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
...(isApiKey
|
|
34
|
+
? { 'X-API-Key': this.token }
|
|
35
|
+
: { Authorization: `Bearer ${this.token}` }),
|
|
36
|
+
};
|
|
37
|
+
const body = { message };
|
|
38
|
+
if (options?.systemPrompt) {
|
|
39
|
+
// Platform doesn't accept system prompt directly via API — embed in message
|
|
40
|
+
body.message = `[System context: ${options.systemPrompt.slice(0, 2000)}]\n\n${message}`;
|
|
41
|
+
}
|
|
42
|
+
const response = await fetch(`${this.baseUrl}/ai-chat/stream`, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers,
|
|
45
|
+
body: JSON.stringify(body),
|
|
46
|
+
signal: options?.signal,
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const errText = await response.text().catch(() => '');
|
|
50
|
+
yield { type: 'text_delta', text: `Platform error ${response.status}: ${errText.slice(0, 300)}` };
|
|
51
|
+
yield { type: 'message_stop', finishReason: 'error' };
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!response.body) {
|
|
55
|
+
yield { type: 'message_stop', finishReason: 'error' };
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Parse SSE stream from platform
|
|
59
|
+
const reader = response.body.getReader();
|
|
60
|
+
const decoder = new TextDecoder();
|
|
61
|
+
let buffer = '';
|
|
62
|
+
try {
|
|
63
|
+
while (true) {
|
|
64
|
+
const { done, value } = await reader.read();
|
|
65
|
+
if (done)
|
|
66
|
+
break;
|
|
67
|
+
buffer += decoder.decode(value, { stream: true });
|
|
68
|
+
const lines = buffer.split('\n');
|
|
69
|
+
buffer = lines.pop() || '';
|
|
70
|
+
for (const line of lines) {
|
|
71
|
+
if (!line.startsWith('data: '))
|
|
72
|
+
continue;
|
|
73
|
+
let event;
|
|
74
|
+
try {
|
|
75
|
+
event = JSON.parse(line.slice(6));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
// Map platform SSE events to canonical StreamEvent
|
|
81
|
+
switch (event.type) {
|
|
82
|
+
case 'text_delta':
|
|
83
|
+
yield { type: 'text_delta', text: String(event.content ?? '') };
|
|
84
|
+
break;
|
|
85
|
+
case 'thinking_delta':
|
|
86
|
+
yield { type: 'thinking_delta', text: String(event.content ?? '') };
|
|
87
|
+
break;
|
|
88
|
+
case 'tool_call_start':
|
|
89
|
+
yield {
|
|
90
|
+
type: 'tool_use_start',
|
|
91
|
+
id: String(event.toolCallId ?? ''),
|
|
92
|
+
name: String(event.name ?? ''),
|
|
93
|
+
};
|
|
94
|
+
break;
|
|
95
|
+
case 'tool_call_result':
|
|
96
|
+
yield {
|
|
97
|
+
type: 'tool_result',
|
|
98
|
+
id: String(event.toolCallId ?? ''),
|
|
99
|
+
result: String(event.result ?? ''),
|
|
100
|
+
isError: !!event.isError,
|
|
101
|
+
};
|
|
102
|
+
break;
|
|
103
|
+
case 'usage':
|
|
104
|
+
yield {
|
|
105
|
+
type: 'usage',
|
|
106
|
+
promptTokens: event.promptTokens ?? 0,
|
|
107
|
+
completionTokens: event.completionTokens ?? 0,
|
|
108
|
+
};
|
|
109
|
+
break;
|
|
110
|
+
case 'done':
|
|
111
|
+
yield { type: 'message_stop', finishReason: 'stop' };
|
|
112
|
+
return;
|
|
113
|
+
case 'error':
|
|
114
|
+
yield { type: 'text_delta', text: `Error: ${event.message ?? 'Unknown error'}` };
|
|
115
|
+
yield { type: 'message_stop', finishReason: 'error' };
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
reader.releaseLock();
|
|
123
|
+
}
|
|
124
|
+
yield { type: 'message_stop', finishReason: 'stop' };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function createPlatformProvider(options) {
|
|
128
|
+
return new PlatformProvider(options);
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function loginCommand(options: {
|
|
2
|
+
email?: string;
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
platformUrl?: string;
|
|
5
|
+
}): Promise<void>;
|
|
6
|
+
export declare function logoutCommand(): Promise<void>;
|
|
7
|
+
export declare function authStatusCommand(): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as readline from 'node:readline';
|
|
2
|
+
import { loadCredentials, saveCredentials, clearCredentials, getPlatformUrl } from '../config/credentials.js';
|
|
3
|
+
import { PlatformClient } from '../config/platform-client.js';
|
|
4
|
+
export async function loginCommand(options) {
|
|
5
|
+
const platformUrl = options.platformUrl ?? getPlatformUrl();
|
|
6
|
+
console.log('');
|
|
7
|
+
console.log(' \x1b[1mFlow Weaver Login\x1b[0m');
|
|
8
|
+
console.log(` \x1b[2mPlatform: ${platformUrl}\x1b[0m`);
|
|
9
|
+
console.log('');
|
|
10
|
+
// Validate platform reachable
|
|
11
|
+
try {
|
|
12
|
+
const resp = await fetch(`${platformUrl}/ready`);
|
|
13
|
+
if (!resp.ok)
|
|
14
|
+
throw new Error();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
console.error(' \x1b[31m✗\x1b[0m Cannot connect to platform');
|
|
18
|
+
console.error(` Check: ${platformUrl}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
let token;
|
|
22
|
+
let email;
|
|
23
|
+
let plan;
|
|
24
|
+
let userId;
|
|
25
|
+
if (options.apiKey) {
|
|
26
|
+
// API key auth
|
|
27
|
+
token = options.apiKey;
|
|
28
|
+
const client = new PlatformClient({ token, email: '', plan: 'free', platformUrl, expiresAt: Infinity });
|
|
29
|
+
try {
|
|
30
|
+
const user = await client.getUser();
|
|
31
|
+
email = user.email;
|
|
32
|
+
plan = user.plan;
|
|
33
|
+
userId = user.id;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
console.error(' \x1b[31m✗\x1b[0m Invalid API key');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Email/password auth
|
|
43
|
+
email = options.email ?? await prompt(' Email: ');
|
|
44
|
+
const password = await prompt(' Password: ', true);
|
|
45
|
+
try {
|
|
46
|
+
const resp = await fetch(`${platformUrl}/auth/login`, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: { 'Content-Type': 'application/json' },
|
|
49
|
+
body: JSON.stringify({ email, password }),
|
|
50
|
+
});
|
|
51
|
+
if (!resp.ok) {
|
|
52
|
+
const err = await resp.json().catch(() => ({ error: 'Login failed' }));
|
|
53
|
+
console.error(`\n \x1b[31m✗\x1b[0m ${err.error}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const data = await resp.json();
|
|
58
|
+
token = data.token;
|
|
59
|
+
email = data.user.email;
|
|
60
|
+
plan = data.user.plan;
|
|
61
|
+
userId = data.user.id;
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
console.error(`\n \x1b[31m✗\x1b[0m ${err instanceof Error ? err.message : 'Login failed'}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const expiresAt = options.apiKey
|
|
70
|
+
? Date.now() + 365 * 24 * 60 * 60 * 1000 // 1 year for API keys
|
|
71
|
+
: Date.now() + 7 * 24 * 60 * 60 * 1000; // 7 days for JWT
|
|
72
|
+
saveCredentials({ token, email, plan: plan, platformUrl, expiresAt, userId });
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log(` \x1b[32m✓\x1b[0m Logged in as \x1b[1m${email}\x1b[0m (${plan} plan)`);
|
|
75
|
+
console.log('');
|
|
76
|
+
console.log(' What\'s unlocked:');
|
|
77
|
+
console.log(' \x1b[36mfw deploy src/workflow.ts\x1b[0m \x1b[2m# deploy to cloud\x1b[0m');
|
|
78
|
+
console.log(' \x1b[36mfw status\x1b[0m \x1b[2m# see deployments + usage\x1b[0m');
|
|
79
|
+
console.log(' \x1b[36mweaver assistant\x1b[0m \x1b[2m# AI with platform credits\x1b[0m');
|
|
80
|
+
console.log('');
|
|
81
|
+
}
|
|
82
|
+
export async function logoutCommand() {
|
|
83
|
+
clearCredentials();
|
|
84
|
+
console.log(' \x1b[32m✓\x1b[0m Logged out');
|
|
85
|
+
}
|
|
86
|
+
export async function authStatusCommand() {
|
|
87
|
+
const creds = loadCredentials();
|
|
88
|
+
if (!creds) {
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log(' Not logged in.');
|
|
91
|
+
console.log(' Run: \x1b[36mfw login\x1b[0m');
|
|
92
|
+
console.log('');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const expiresIn = Math.floor((creds.expiresAt - Date.now()) / 1000 / 60 / 60);
|
|
96
|
+
console.log('');
|
|
97
|
+
console.log(` \x1b[32m✓\x1b[0m Logged in as \x1b[1m${creds.email}\x1b[0m`);
|
|
98
|
+
console.log(` Plan: ${creds.plan}`);
|
|
99
|
+
console.log(` Platform: ${creds.platformUrl}`);
|
|
100
|
+
console.log(` Token expires in: ${expiresIn}h`);
|
|
101
|
+
console.log('');
|
|
102
|
+
}
|
|
103
|
+
function prompt(message, hidden = false) {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
106
|
+
if (hidden && process.stdin.isTTY) {
|
|
107
|
+
// Hide password input
|
|
108
|
+
process.stderr.write(message);
|
|
109
|
+
process.stdin.setRawMode(true);
|
|
110
|
+
let input = '';
|
|
111
|
+
const handler = (key) => {
|
|
112
|
+
const ch = key.toString();
|
|
113
|
+
if (ch === '\r' || ch === '\n') {
|
|
114
|
+
process.stdin.setRawMode(false);
|
|
115
|
+
process.stdin.removeListener('data', handler);
|
|
116
|
+
process.stderr.write('\n');
|
|
117
|
+
rl.close();
|
|
118
|
+
resolve(input);
|
|
119
|
+
}
|
|
120
|
+
else if (ch === '\x03') { // Ctrl+C
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
else if (ch === '\x7f') { // Backspace
|
|
124
|
+
input = input.slice(0, -1);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
input += ch;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
process.stdin.on('data', handler);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
rl.question(message, (answer) => { rl.close(); resolve(answer); });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function deployCommand(filePath: string, options?: {
|
|
2
|
+
name?: string;
|
|
3
|
+
}): Promise<void>;
|
|
4
|
+
export declare function undeployCommand(slug: string): Promise<void>;
|
|
5
|
+
export declare function cloudStatusCommand(): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { loadCredentials } from '../config/credentials.js';
|
|
4
|
+
import { PlatformClient } from '../config/platform-client.js';
|
|
5
|
+
export async function deployCommand(filePath, options = {}) {
|
|
6
|
+
const creds = loadCredentials();
|
|
7
|
+
if (!creds) {
|
|
8
|
+
console.error(' \x1b[31m✗\x1b[0m Not logged in. Run: fw login');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const absPath = path.resolve(filePath);
|
|
13
|
+
if (!fs.existsSync(absPath)) {
|
|
14
|
+
console.error(` \x1b[31m✗\x1b[0m File not found: ${filePath}`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const source = fs.readFileSync(absPath, 'utf-8');
|
|
19
|
+
const name = options.name ?? path.basename(filePath, path.extname(filePath));
|
|
20
|
+
const client = new PlatformClient(creds);
|
|
21
|
+
console.log('');
|
|
22
|
+
console.log(` \x1b[2mPushing ${name}...\x1b[0m`);
|
|
23
|
+
try {
|
|
24
|
+
const workflow = await client.pushWorkflow(name, source);
|
|
25
|
+
console.log(` \x1b[32m✓\x1b[0m Pushed (v${workflow.version})`);
|
|
26
|
+
console.log(` \x1b[2mDeploying...\x1b[0m`);
|
|
27
|
+
const deployment = await client.deploy(workflow.slug);
|
|
28
|
+
console.log(` \x1b[32m✓\x1b[0m Deployed: ${deployment.slug}`);
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log(` Endpoint: ${creds.platformUrl}/run/${deployment.slug}`);
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(' Test it:');
|
|
33
|
+
console.log(` curl -X POST ${creds.platformUrl}/run/${deployment.slug} \\`);
|
|
34
|
+
console.log(` -H "X-API-Key: <your-api-key>" \\`);
|
|
35
|
+
console.log(` -H "Content-Type: application/json" \\`);
|
|
36
|
+
console.log(` -d '{"input": "hello"}'`);
|
|
37
|
+
console.log('');
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error(` \x1b[31m✗\x1b[0m ${err instanceof Error ? err.message : 'Deploy failed'}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export async function undeployCommand(slug) {
|
|
45
|
+
const creds = loadCredentials();
|
|
46
|
+
if (!creds) {
|
|
47
|
+
console.error(' \x1b[31m✗\x1b[0m Not logged in.');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const client = new PlatformClient(creds);
|
|
52
|
+
try {
|
|
53
|
+
await client.undeploy(slug);
|
|
54
|
+
console.log(` \x1b[32m✓\x1b[0m Undeployed: ${slug}`);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error(` \x1b[31m✗\x1b[0m ${err instanceof Error ? err.message : 'Undeploy failed'}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export async function cloudStatusCommand() {
|
|
61
|
+
const creds = loadCredentials();
|
|
62
|
+
if (!creds) {
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(' Not logged in. Run: \x1b[36mfw login\x1b[0m');
|
|
65
|
+
console.log('');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const client = new PlatformClient(creds);
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log(` \x1b[1m${creds.email}\x1b[0m \x1b[2m(${creds.plan} plan)\x1b[0m`);
|
|
71
|
+
console.log('');
|
|
72
|
+
try {
|
|
73
|
+
const deployments = await client.listDeployments();
|
|
74
|
+
if (deployments.length === 0) {
|
|
75
|
+
console.log(' No deployments.');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.log(' Deployments:');
|
|
79
|
+
for (const d of deployments) {
|
|
80
|
+
const icon = d.status === 'active' ? '\x1b[32m●\x1b[0m' : '\x1b[33m○\x1b[0m';
|
|
81
|
+
console.log(` ${icon} ${d.slug.padEnd(25)} ${d.status}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
console.log(' \x1b[33m⚠\x1b[0m Could not fetch deployments');
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const usage = await client.getUsage();
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log(` AI Credits: ${usage.aiCalls} calls this month`);
|
|
92
|
+
console.log(` Executions: ${usage.executions} this month`);
|
|
93
|
+
}
|
|
94
|
+
catch { /* usage not available */ }
|
|
95
|
+
console.log('');
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -32,6 +32,7 @@ export interface InitConfig {
|
|
|
32
32
|
/** Free-text description when user picked "Something else" */
|
|
33
33
|
useCaseDescription?: string;
|
|
34
34
|
mcp: boolean;
|
|
35
|
+
installWeaver: boolean;
|
|
35
36
|
}
|
|
36
37
|
export interface InitReport {
|
|
37
38
|
projectDir: string;
|
|
@@ -55,7 +56,7 @@ export declare function validateProjectName(name: string): string | true;
|
|
|
55
56
|
export declare function toWorkflowName(projectName: string): string;
|
|
56
57
|
export declare function isNonInteractive(): boolean;
|
|
57
58
|
export declare function resolveInitConfig(dirArg: string | undefined, options: InitOptions): Promise<InitConfig>;
|
|
58
|
-
export declare function generateProjectFiles(projectName: string, template: string, format?: TModuleFormat, persona?: PersonaId): Record<string, string>;
|
|
59
|
+
export declare function generateProjectFiles(projectName: string, template: string, format?: TModuleFormat, persona?: PersonaId, installWeaver?: boolean): Record<string, string>;
|
|
59
60
|
export declare function scaffoldProject(targetDir: string, files: Record<string, string>, options: {
|
|
60
61
|
force: boolean;
|
|
61
62
|
}): {
|
|
@@ -183,6 +183,17 @@ export async function resolveInitConfig(dirArg, options) {
|
|
|
183
183
|
if (!useCaseDescription)
|
|
184
184
|
useCaseDescription = undefined;
|
|
185
185
|
}
|
|
186
|
+
// 3c. Weaver AI assistant opt-in
|
|
187
|
+
let installWeaver;
|
|
188
|
+
if (skipPrompts) {
|
|
189
|
+
installWeaver = false;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
installWeaver = await confirm({
|
|
193
|
+
message: 'Install Weaver AI assistant? (Recommended)\n Weaver helps you create, modify, and manage workflows with AI.',
|
|
194
|
+
default: true,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
186
197
|
// 4. MCP setup (nocode, vibecoder, lowcode: prompt; expert: skip unless --mcp)
|
|
187
198
|
let mcp;
|
|
188
199
|
if (options.mcp !== undefined) {
|
|
@@ -255,10 +266,11 @@ export async function resolveInitConfig(dirArg, options) {
|
|
|
255
266
|
useCase,
|
|
256
267
|
useCaseDescription,
|
|
257
268
|
mcp,
|
|
269
|
+
installWeaver,
|
|
258
270
|
};
|
|
259
271
|
}
|
|
260
272
|
// ── Pure file generation ─────────────────────────────────────────────────────
|
|
261
|
-
export function generateProjectFiles(projectName, template, format = 'esm', persona = 'expert') {
|
|
273
|
+
export function generateProjectFiles(projectName, template, format = 'esm', persona = 'expert', installWeaver = false) {
|
|
262
274
|
const workflowName = toWorkflowName(projectName);
|
|
263
275
|
const workflowFile = `${projectName}-workflow.ts`;
|
|
264
276
|
const tmpl = getWorkflowTemplate(template);
|
|
@@ -284,6 +296,7 @@ export function generateProjectFiles(projectName, template, format = 'esm', pers
|
|
|
284
296
|
scripts,
|
|
285
297
|
dependencies: {
|
|
286
298
|
'@synergenius/flow-weaver': 'latest',
|
|
299
|
+
...(installWeaver ? { '@synergenius/flow-weaver-pack-weaver': 'latest' } : {}),
|
|
287
300
|
},
|
|
288
301
|
devDependencies: {
|
|
289
302
|
typescript: '^5.3.0',
|
|
@@ -382,6 +395,10 @@ export function generateProjectFiles(projectName, template, format = 'esm', pers
|
|
|
382
395
|
if (persona === 'lowcode') {
|
|
383
396
|
files['examples/example-workflow.ts'] = generateExampleWorkflow(projectName);
|
|
384
397
|
}
|
|
398
|
+
// Add Weaver config if opted in
|
|
399
|
+
if (installWeaver) {
|
|
400
|
+
files['.weaver.json'] = JSON.stringify({ provider: 'auto', approval: 'auto' }, null, 2) + '\n';
|
|
401
|
+
}
|
|
385
402
|
return files;
|
|
386
403
|
}
|
|
387
404
|
// ── Filesystem writer ────────────────────────────────────────────────────────
|
|
@@ -502,7 +519,7 @@ export async function initCommand(dirArg, options) {
|
|
|
502
519
|
throw new Error(`${config.targetDir} already contains a package.json. Use --force to overwrite.`);
|
|
503
520
|
}
|
|
504
521
|
// Generate and scaffold
|
|
505
|
-
const files = generateProjectFiles(config.projectName, config.template, config.format, config.persona);
|
|
522
|
+
const files = generateProjectFiles(config.projectName, config.template, config.format, config.persona, config.installWeaver);
|
|
506
523
|
const { filesCreated, filesSkipped } = scaffoldProject(config.targetDir, files, {
|
|
507
524
|
force: config.force,
|
|
508
525
|
});
|
|
@@ -622,6 +639,15 @@ export async function initCommand(dirArg, options) {
|
|
|
622
639
|
logger.warn(`Compile failed: ${compileResult.error}`);
|
|
623
640
|
}
|
|
624
641
|
}
|
|
642
|
+
if (config.installWeaver) {
|
|
643
|
+
logger.success('Weaver AI assistant installed');
|
|
644
|
+
logger.newline();
|
|
645
|
+
logger.log(' Weaver installed. Try:');
|
|
646
|
+
logger.log(' flow-weaver weaver assistant # AI assistant');
|
|
647
|
+
logger.log(' flow-weaver weaver bot "..." # create workflows with AI');
|
|
648
|
+
logger.log(' flow-weaver weaver examples # see what\'s possible');
|
|
649
|
+
logger.newline();
|
|
650
|
+
}
|
|
625
651
|
// Read the workflow code for preview
|
|
626
652
|
const workflowCode = files[`src/${workflowFile}`] ?? null;
|
|
627
653
|
// Persona-specific rich output
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface StoredCredentials {
|
|
2
|
+
token: string;
|
|
3
|
+
email: string;
|
|
4
|
+
plan: 'free' | 'pro' | 'business';
|
|
5
|
+
platformUrl: string;
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
userId?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function loadCredentials(): StoredCredentials | null;
|
|
10
|
+
export declare function saveCredentials(creds: StoredCredentials): void;
|
|
11
|
+
export declare function clearCredentials(): void;
|
|
12
|
+
export declare function isTokenExpired(creds: StoredCredentials): boolean;
|
|
13
|
+
export declare function getPlatformUrl(): string;
|
|
14
|
+
export declare function isLoggedIn(): boolean;
|
|
15
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
const FW_CONFIG_DIR = path.join(os.homedir(), '.fw');
|
|
5
|
+
const CREDENTIALS_FILE = path.join(FW_CONFIG_DIR, 'credentials.json');
|
|
6
|
+
export function loadCredentials() {
|
|
7
|
+
if (!fs.existsSync(CREDENTIALS_FILE))
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
const data = JSON.parse(fs.readFileSync(CREDENTIALS_FILE, 'utf-8'));
|
|
11
|
+
if (isTokenExpired(data))
|
|
12
|
+
return null;
|
|
13
|
+
return data;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function saveCredentials(creds) {
|
|
20
|
+
fs.mkdirSync(FW_CONFIG_DIR, { recursive: true });
|
|
21
|
+
fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), 'utf-8');
|
|
22
|
+
try {
|
|
23
|
+
fs.chmodSync(CREDENTIALS_FILE, 0o600);
|
|
24
|
+
}
|
|
25
|
+
catch { /* Windows */ }
|
|
26
|
+
}
|
|
27
|
+
export function clearCredentials() {
|
|
28
|
+
try {
|
|
29
|
+
fs.unlinkSync(CREDENTIALS_FILE);
|
|
30
|
+
}
|
|
31
|
+
catch { /* not found */ }
|
|
32
|
+
}
|
|
33
|
+
export function isTokenExpired(creds) {
|
|
34
|
+
return Date.now() > creds.expiresAt;
|
|
35
|
+
}
|
|
36
|
+
export function getPlatformUrl() {
|
|
37
|
+
const creds = loadCredentials();
|
|
38
|
+
return creds?.platformUrl ?? process.env.FW_PLATFORM_URL ?? 'https://app.synergenius.pt';
|
|
39
|
+
}
|
|
40
|
+
export function isLoggedIn() {
|
|
41
|
+
return loadCredentials() !== null;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { StoredCredentials } from './credentials.js';
|
|
2
|
+
export declare class PlatformClient {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private token;
|
|
5
|
+
constructor(creds: StoredCredentials);
|
|
6
|
+
private fetch;
|
|
7
|
+
getUser(): Promise<{
|
|
8
|
+
id: string;
|
|
9
|
+
email: string;
|
|
10
|
+
name: string;
|
|
11
|
+
plan: string;
|
|
12
|
+
}>;
|
|
13
|
+
pushWorkflow(name: string, source: string): Promise<{
|
|
14
|
+
slug: string;
|
|
15
|
+
version: number;
|
|
16
|
+
}>;
|
|
17
|
+
deploy(slug: string): Promise<{
|
|
18
|
+
slug: string;
|
|
19
|
+
status: string;
|
|
20
|
+
}>;
|
|
21
|
+
undeploy(slug: string): Promise<void>;
|
|
22
|
+
listDeployments(): Promise<Array<{
|
|
23
|
+
slug: string;
|
|
24
|
+
status: string;
|
|
25
|
+
workflowName?: string;
|
|
26
|
+
}>>;
|
|
27
|
+
getUsage(): Promise<{
|
|
28
|
+
executions: number;
|
|
29
|
+
aiCalls: number;
|
|
30
|
+
plan: string;
|
|
31
|
+
}>;
|
|
32
|
+
streamChat(message: string, conversationId?: string): AsyncGenerator<Record<string, unknown>>;
|
|
33
|
+
validate(): Promise<boolean>;
|
|
34
|
+
}
|
|
35
|
+
export declare function createPlatformClient(creds: StoredCredentials): PlatformClient;
|
|
36
|
+
//# sourceMappingURL=platform-client.d.ts.map
|