@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.
@@ -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';
@@ -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