@synergenius/flow-weaver 0.26.7 → 0.27.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 CHANGED
@@ -6,14 +6,61 @@
6
6
  [![Tests](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/moraispgsi/305430ef59a51d0a58eb61fefdfbe634/raw/flow-weaver-test-count.json&style=flat)](https://github.com/synergenius-fw/flow-weaver/actions/workflows/ci.yml)
7
7
  [![Coverage](https://img.shields.io/codecov/c/github/synergenius-fw/flow-weaver?style=flat)](https://codecov.io/gh/synergenius-fw/flow-weaver)
8
8
  [![License: Flow Weaver Library License](https://img.shields.io/badge/License-Flow%20Weaver%20Library-blue?style=flat)](./LICENSE)
9
- [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-green?style=flat)](https://nodejs.org)
9
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22-green?style=flat)](https://nodejs.org)
10
10
 
11
11
  **Design agent workflows in conversation. The compiled output is yours.**
12
12
 
13
- [**flowweaver.ai**](https://flowweaver.ai) · [**Studio**](https://flowweaver.ai/studio) · [**Docs**](https://flowweaver.ai/docs) · [**Discord**](https://discord.gg/6Byh3ur2bk) · [**npm**](https://www.npmjs.com/package/@synergenius/flow-weaver)
13
+ [**flowweaver.ai**](https://flowweaver.ai) · [**Studio**](https://flowweaver.ai/studio) · [**Features**](https://flowweaver.ai/features) · [**Discord**](https://discord.gg/6Byh3ur2bk) · [**npm**](https://www.npmjs.com/package/@synergenius/flow-weaver)
14
14
 
15
15
  ---
16
16
 
17
+ Flow Weaver is a TypeScript workflow compiler. Define workflows with JSDoc annotations in your existing codebase, compile to standalone TypeScript functions with zero runtime dependencies, and deploy to any serverless target. The compiled output has no dependency on Flow Weaver. You own it.
18
+
19
+ Build AI agent workflows, data pipelines, or automation scripts through natural language using MCP tools in Claude Code, Cursor, Windsurf, VS Code, JetBrains, Codex, or any MCP-compatible editor. Or write them by hand with annotations the same way you write JSDoc today.
20
+
21
+ ## Project Status
22
+
23
+ Flow Weaver is in **active alpha**. The compiler, validator, CLI, and MCP tools are stable and thoroughly tested. The test suite covers thousands of cases across parsing, compilation, validation, diffing, and deployment. CI runs on every commit.
24
+
25
+ This started as a curiosity by [Ricardo Morais](https://linkedin.com/in/moraispgsi) into why visual programming never took off and whether code-first and visual editing could actually coexist. It has grown into something real. It is actively maintained, with releases shipping regularly. If you run into issues or have questions, the [Discord](https://discord.gg/6Byh3ur2bk) is the best place to reach out. Every issue and question gets a response, usually within a day.
26
+
27
+ **Flow Weaver Studio**, the browser-based visual IDE, is the next major milestone and is being actively built. Core editing (canvas, terminal, diagnostics) is live today. The visual debugger, AI chat assistant, version history, and deployment dashboard are in progress. You can follow development on Discord or in the [GitHub releases](https://github.com/synergenius-fw/flow-weaver/releases).
28
+
29
+ Breaking changes may occur between minor versions during alpha. Pin your version in `package.json` if stability matters for your project.
30
+
31
+ ## Capabilities
32
+
33
+ **Annotation-driven workflows:** Define node graphs directly in TypeScript using JSDoc annotations. No YAML, no JSON configs, no separate workflow files. Your workflows live in `.ts` files, version-controlled alongside your code.
34
+
35
+ **Zero-dependency compiled output:** The compiler generates a self-contained TypeScript function. No runtime to install in production, no SDK to import, no vendor lock-in. Delete the package after compiling and the output still runs.
36
+
37
+ **AI-native editing:** MCP tools expose the full compiler, validator, debugger, and diagram surface to any MCP-compatible editor. Scaffold a project, add nodes, wire connections, validate, compile, run, and diff through conversation.
38
+
39
+ **Structural validation:** Type checking across port connections, graph topology analysis, agent safety rules, and breaking change detection. Runs in CI, on every save, or on demand. Errors include source locations, fix suggestions, and documentation links.
40
+
41
+ **Step-through debugging:** Set breakpoints on nodes, step through execution node by node, inspect variables mid-run, and resume from checkpoints. Variable injection available through MCP tools.
42
+
43
+ **Semantic diffing:** Compare workflow versions at the graph level instead of text diffs. Impact analysis categorizes every change as critical, breaking, minor, or cosmetic. Know exactly what changed and whether it matters.
44
+
45
+ **Deploy anywhere:** Export to Inngest, AWS Lambda, Vercel, Cloudflare Workers, GitHub Actions, or GitLab CI. Or serve workflows over HTTP with built-in framework adapters for Next.js, Express, Hono, Fastify, and Remix.
46
+
47
+ **Extensible pack system:** Node types, deploy targets, CLI commands, and MCP tools are contributed by npm packages. Install community packs or build and publish your own.
48
+
49
+ ## Quick Start
50
+
51
+ ```bash
52
+ npm install @synergenius/flow-weaver
53
+ npx flow-weaver init my-agent
54
+ npx flow-weaver compile my-agent.ts
55
+ npx flow-weaver run my-agent.ts
56
+ ```
57
+
58
+ The CLI scaffolds a project, compiles annotations into a runnable workflow, and executes it. Run `fw --help` for the full command reference. The example below shows a more complete workflow built through conversation.
59
+
60
+ ## See It in Action
61
+
62
+ The conversation below shows [Weaver](https://github.com/synergenius-fw/flow-weaver-pack-weaver), an optional AI bot that drives Flow Weaver through natural language. It handles scaffolding, modification, validation, compilation, execution, and deployment. Install it with `fw market install @synergenius/flow-weaver-pack-weaver`, or use the MCP tools directly from your editor without Weaver.
63
+
17
64
  > **You:** Build a support agent that classifies messages and either auto-replies or escalates.
18
65
 
19
66
  > **Weaver:** Created `support-agent.ts`. Four nodes, one workflow.
@@ -101,19 +148,156 @@
101
148
  > fw export support-agent.ts --target inngest
102
149
  > ```
103
150
 
104
- ---
151
+ ## How Workflows Are Defined
152
+
153
+ Workflows are TypeScript functions annotated with JSDoc. You write plain functions for each node, and the annotations describe how they connect.
154
+
155
+ The function signature is the source of truth. Input parameters become input ports, return object fields become output ports. Every function has an `execute` boolean that controls whether it runs or passes through, and returns `onSuccess`/`onFailure` for branching. Expression nodes (marked with `@expression`) skip the execute/branching pattern and map return fields directly to outputs.
156
+
157
+ The `@flowWeaver workflow` block wires node instances together. `@node` creates an instance of a node type, `@connect` wires ports between nodes, and `@path` is shorthand for a linear route. `Start` and `Exit` are virtual nodes representing the workflow's inputs and outputs.
105
158
 
106
- ## Install
159
+ The compiler reads these annotations, builds the execution graph, and generates the body in place between markers. Everything outside the markers stays untouched. Node type functions are never modified.
160
+
161
+ ```typescript
162
+ /**
163
+ * @flowWeaver nodeType
164
+ * @expression
165
+ * @input message - The support message
166
+ * @output context - Classified context
167
+ */
168
+ function classifyIntent(message: string): { context: SupportContext } {
169
+ const intent = /refund|cancel/i.test(message) ? 'billing' : 'general';
170
+ return { context: { message, intent, priority: 'normal' } };
171
+ }
172
+
173
+ // routeAction, llmReply, and escalate are defined the same way:
174
+ // plain functions with @flowWeaver nodeType annotations.
175
+
176
+ /**
177
+ * @flowWeaver workflow
178
+ * @node classify classifyIntent
179
+ * @node route routeAction
180
+ * @node reply llmReply
181
+ * @node esc escalate
182
+ * @path Start -> classify -> route -> reply -> Exit
183
+ * @path route:fail -> esc -> Exit
184
+ * @connect Start.message -> classify.message
185
+ * @connect classify.context -> route.context
186
+ * @param message - The support message
187
+ * @returns reply - Generated reply
188
+ * @returns ticket - Escalation ticket
189
+ */
190
+ export async function supportAgent(
191
+ execute: boolean,
192
+ params: { message: string },
193
+ ): Promise<{
194
+ onSuccess: boolean;
195
+ onFailure: boolean;
196
+ reply: string | null;
197
+ ticket: string | null;
198
+ }> {
199
+ // @flow-weaver-body-start
200
+ // Generated by the compiler. Do not edit.
201
+ // @flow-weaver-body-end
202
+ }
203
+ ```
204
+
205
+ ## CLI Reference
206
+
207
+ | Command | Description |
208
+ |---------|-------------|
209
+ | `fw init` | Scaffold a new project with presets and templates |
210
+ | `fw compile` | Compile workflow files to standalone TypeScript |
211
+ | `fw validate` | Validate workflows without compiling |
212
+ | `fw run` | Execute a workflow with optional debug mode |
213
+ | `fw watch` | Recompile on file changes |
214
+ | `fw dev` | Watch + compile + run loop |
215
+ | `fw describe` | Output workflow structure as JSON, text, Mermaid, ASCII, or paths |
216
+ | `fw diagram` | Generate SVG, HTML, or ASCII diagrams |
217
+ | `fw diff` | Semantic diff between two workflow versions |
218
+ | `fw export` | Export to a deploy target (Inngest, Lambda, Vercel, Cloudflare, etc.) |
219
+ | `fw serve` | Start an HTTP server exposing workflows as endpoints |
220
+ | `fw modify` | Programmatic graph mutations (addNode, removeNode, connect, etc.) |
221
+ | `fw mcp-server` | Start the MCP server for AI editor integration |
222
+ | `fw mcp-setup` | Auto-configure MCP for Claude, Cursor, VS Code, Windsurf, or Codex |
223
+ | `fw docs` | Browse bundled reference documentation |
224
+
225
+ Run `fw --help` or `fw <command> --help` for full options.
226
+
227
+ ## Flow Weaver Studio (Early Beta)
228
+
229
+ A browser-based visual IDE for building workflows. Canvas editor, integrated terminal, real-time diagnostics, and the full CLI available in the cloud.
230
+
231
+ Studio is in early beta. Core editing is live. The visual debugger, AI chat assistant, version history, and deployment dashboard are being actively developed and will ship incrementally. The cloud platform is not yet production-ready, and paid plans are not available yet. Use it to explore and experiment, but expect rough edges.
232
+
233
+ [Open Studio](https://flowweaver.ai/studio) · [Learn more](https://flowweaver.ai/features)
234
+
235
+ ## Weaver (Experimental)
236
+
237
+ Weaver is an optional AI bot that automates the full workflow lifecycle. Give it a task in natural language and it plans, scaffolds, compiles, validates, and fixes issues in a loop until the workflow is correct. It connects to Anthropic, Claude CLI, or GitHub Copilot CLI.
238
+
239
+ - `weaver bot "create a greeting workflow"` handles a task end-to-end with plan approval
240
+ - `weaver run workflow.ts` executes a workflow with an AI agent channel attached
241
+ - `weaver session` polls a task queue continuously for automated pipelines
242
+ - `weaver swarm start` runs multiple bot instances in parallel, with an orchestrator dispatching tasks across them
243
+
244
+ Weaver is itself built as a set of Flow Weaver workflows. Run `weaver eject` to get a local copy you can customize.
107
245
 
108
246
  ```bash
109
- npm install @synergenius/flow-weaver
110
- npx flow-weaver init
247
+ fw market install @synergenius/flow-weaver-pack-weaver
111
248
  ```
112
249
 
113
- The CLI handles the rest.
250
+ > **Weaver is experimental.** The bot, session, and swarm modes work and are being used internally, but the APIs and behavior may change between releases. Weaver is not required to use Flow Weaver. The compiler, CLI, and MCP tools are the stable foundation and work independently.
114
251
 
115
- ---
252
+ ## Documentation
253
+
254
+ Reference documentation is bundled with the CLI. Run `fw docs` to browse topics, or `fw docs <topic>` to read a specific one:
255
+
256
+ ```bash
257
+ fw docs list # List all topics
258
+ fw docs tutorial # Step-by-step first workflow guide
259
+ fw docs concepts # Core concepts
260
+ fw docs jsdoc-grammar # Annotation syntax reference
261
+ fw docs cli-reference # Full CLI command reference
262
+ fw docs search <query> # Search across all docs
263
+ ```
264
+
265
+ **Online resources:**
266
+
267
+ - [Features](https://flowweaver.ai/features)
268
+ - [Compare with Alternatives](https://flowweaver.ai/compare)
269
+ - [License](https://flowweaver.ai/license)
270
+
271
+ ## Community
272
+
273
+ Flow Weaver is built and maintained by a solo developer. If you try it, find a bug, or just want to talk about workflows, the [Discord](https://discord.gg/6Byh3ur2bk) is open. Every message gets read and responded to, usually within a day.
274
+
275
+ If something breaks, [open an issue](https://github.com/synergenius-fw/flow-weaver/issues). Bug reports with reproduction steps get prioritized. Feature requests are welcome too.
276
+
277
+ ## Contributing
278
+
279
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
116
280
 
117
281
  ## License
118
282
 
119
- Licensed under the [Flow Weaver License](https://flowweaver.ai/license). See [LICENSE.md](./LICENSE.md).
283
+ Licensed under the [Flow Weaver Library License](https://flowweaver.ai/license). See [LICENSE](./LICENSE).
284
+
285
+ The license is designed to be generous for the vast majority of users while protecting the project's IP. Here is what it means in practice:
286
+
287
+ **Use freely:**
288
+ - Install, compile, validate, run, and deploy workflows in any project
289
+ - All compiled output is fully yours with no restrictions or attribution required
290
+ - Use in CI/CD pipelines and build systems
291
+ - Host internally for organizations with 15 or fewer people
292
+ - Include as a dependency in larger products where Flow Weaver is not the primary value
293
+
294
+ **Requires a commercial license:**
295
+ - Internal hosting for organizations with more than 15 people
296
+ - Building a hosted or standalone workflow authoring product
297
+ - Offering Flow Weaver capabilities as a managed service to third parties
298
+
299
+ **Never permitted:**
300
+ - Using source code, tests, documentation, or outputs as AI/ML training data to replicate the functionality
301
+ - Extracting specifications to build competing software
302
+
303
+ If you are unsure whether your use case needs a commercial license, reach out at support@synergenius.pt. The answer is probably "you're fine."
@@ -0,0 +1,4 @@
1
+ export declare function formatLimit(used: number, limit: number): string;
2
+ export declare function usageBar(used: number, limit: number, width?: number): string;
3
+ export declare function accountCommand(): Promise<void>;
4
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1,50 @@
1
+ import { requireLogin, fmt, exitWithError } from '../utils/cli-helpers.js';
2
+ export function formatLimit(used, limit) {
3
+ if (limit === -1)
4
+ return `${used} (unlimited)`;
5
+ return `${used} / ${limit}`;
6
+ }
7
+ export function usageBar(used, limit, width = 20) {
8
+ if (limit === -1)
9
+ return '\x1b[36m∞\x1b[0m';
10
+ if (limit <= 0)
11
+ return '\x1b[2m-\x1b[0m';
12
+ const ratio = Math.min(used / limit, 1);
13
+ const filled = Math.round(ratio * width);
14
+ const empty = width - filled;
15
+ const color = ratio >= 0.9 ? '\x1b[31m' : ratio >= 0.7 ? '\x1b[33m' : '\x1b[32m';
16
+ return `${color}${'█'.repeat(filled)}\x1b[2m${'░'.repeat(empty)}\x1b[0m`;
17
+ }
18
+ export async function accountCommand() {
19
+ const { creds, client } = requireLogin();
20
+ try {
21
+ const [user, usage] = await Promise.all([
22
+ client.getUser(),
23
+ client.getDetailedUsage().catch(() => null),
24
+ ]);
25
+ console.log('');
26
+ console.log(` ${fmt.bold(user.name)}`);
27
+ console.log(` ${user.email}`);
28
+ console.log(` Plan: ${user.plan}`);
29
+ console.log(` Platform: ${fmt.dim(creds.platformUrl)}`);
30
+ if (usage) {
31
+ console.log('');
32
+ const rows = [
33
+ { label: 'Workflows', ...usage.usage.workflows },
34
+ { label: 'Deployments', ...usage.usage.deployments },
35
+ { label: 'Executions', ...usage.usage.executions },
36
+ ];
37
+ for (const row of rows) {
38
+ const bar = usageBar(row.used, row.limit);
39
+ const nums = formatLimit(row.used, row.limit);
40
+ console.log(` ${row.label.padEnd(14)} ${bar} ${nums}`);
41
+ }
42
+ console.log(` ${'Timeout'.padEnd(14)} ${usage.limits.timeoutMs / 1000}s per run`);
43
+ }
44
+ console.log('');
45
+ }
46
+ catch (err) {
47
+ exitWithError(err, 'Failed to fetch account');
48
+ }
49
+ }
50
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1,12 @@
1
+ export declare function aiAddCommand(provider: string, options: {
2
+ key?: string;
3
+ label?: string;
4
+ model?: string;
5
+ default?: boolean;
6
+ }): Promise<void>;
7
+ export declare function aiListCommand(): Promise<void>;
8
+ export declare function aiRevokeCommand(id: string, options: {
9
+ force?: boolean;
10
+ }): Promise<void>;
11
+ export declare function aiTestCommand(id: string): Promise<void>;
12
+ //# sourceMappingURL=ai-credentials.d.ts.map
@@ -0,0 +1,95 @@
1
+ import { requireLogin, readLine, confirm, fmt, exitWithError } from '../utils/cli-helpers.js';
2
+ const VALID_PROVIDERS = ['anthropic', 'openai'];
3
+ export async function aiAddCommand(provider, options) {
4
+ const { client } = requireLogin();
5
+ try {
6
+ if (!VALID_PROVIDERS.includes(provider)) {
7
+ throw new Error(`Invalid provider "${provider}". Use: ${VALID_PROVIDERS.join(', ')}`);
8
+ }
9
+ // Read key from --key flag or prompt via stdin (non-TTY requires --key)
10
+ let apiKey = options.key;
11
+ if (!apiKey) {
12
+ apiKey = await readLine(' Enter API key: ') ?? undefined;
13
+ if (!apiKey) {
14
+ throw new Error('No API key provided. Use --key <key> or run in a terminal.');
15
+ }
16
+ }
17
+ const label = options.label ?? `${provider} key`;
18
+ const cred = await client.createAiCredential({
19
+ provider,
20
+ label,
21
+ apiKey,
22
+ defaultModel: options.model,
23
+ isDefault: options.default,
24
+ });
25
+ console.log('');
26
+ console.log(fmt.ok('Credential added'));
27
+ console.log('');
28
+ console.log(` Provider: ${cred.provider}`);
29
+ console.log(` Label: ${cred.label}`);
30
+ console.log(` ID: ${fmt.dim(cred.id)}`);
31
+ console.log('');
32
+ console.log(` Test it: fw ai test ${cred.id}`);
33
+ console.log('');
34
+ }
35
+ catch (err) {
36
+ exitWithError(err, 'Failed to add credential');
37
+ }
38
+ }
39
+ export async function aiListCommand() {
40
+ const { client } = requireLogin();
41
+ try {
42
+ const creds = await client.listAiCredentials();
43
+ console.log('');
44
+ if (creds.length === 0) {
45
+ console.log(' No AI credentials. Add one with: fw ai add <provider>');
46
+ }
47
+ else {
48
+ console.log(` ${creds.length} credential${creds.length === 1 ? '' : 's'}:`);
49
+ console.log('');
50
+ for (const c of creds) {
51
+ const def = c.isDefault ? ` ${fmt.yellow('★ default')}` : '';
52
+ const model = c.defaultModel ? ` ${fmt.dim(`(${c.defaultModel})`)}` : '';
53
+ console.log(` ${fmt.cyan(c.provider.padEnd(10))} ${c.label}${model}${def} ${fmt.dim(c.id)}`);
54
+ }
55
+ }
56
+ console.log('');
57
+ }
58
+ catch (err) {
59
+ exitWithError(err, 'Failed to list credentials');
60
+ }
61
+ }
62
+ export async function aiRevokeCommand(id, options) {
63
+ const { client } = requireLogin();
64
+ if (!options.force) {
65
+ const confirmed = await confirm(' Are you sure? This cannot be undone. [y/N] ');
66
+ if (!confirmed) {
67
+ console.log(' Cancelled.');
68
+ return;
69
+ }
70
+ }
71
+ try {
72
+ await client.revokeAiCredential(id);
73
+ console.log(fmt.ok('Credential revoked'));
74
+ }
75
+ catch (err) {
76
+ exitWithError(err, 'Failed to revoke credential');
77
+ }
78
+ }
79
+ export async function aiTestCommand(id) {
80
+ const { client } = requireLogin();
81
+ try {
82
+ console.log(` ${fmt.dim('Testing credential...')}`);
83
+ const result = await client.testAiCredential(id);
84
+ if (result.success) {
85
+ console.log(fmt.ok('Credential is valid'));
86
+ }
87
+ else {
88
+ console.log(fmt.err(`Credential test failed${result.message ? `: ${result.message}` : ''}`));
89
+ }
90
+ }
91
+ catch (err) {
92
+ exitWithError(err, 'Test failed');
93
+ }
94
+ }
95
+ //# sourceMappingURL=ai-credentials.js.map
@@ -0,0 +1,4 @@
1
+ export declare function apiKeyCreateCommand(name: string): Promise<void>;
2
+ export declare function apiKeyListCommand(): Promise<void>;
3
+ export declare function apiKeyRevokeCommand(idOrPrefix: string): Promise<void>;
4
+ //# sourceMappingURL=apikey.d.ts.map
@@ -0,0 +1,64 @@
1
+ import { requireLogin, isUuid, fmt, exitWithError } from '../utils/cli-helpers.js';
2
+ export async function apiKeyCreateCommand(name) {
3
+ const { client } = requireLogin();
4
+ try {
5
+ const apiKey = await client.createApiKey(name);
6
+ console.log('');
7
+ console.log(fmt.ok('API key created'));
8
+ console.log('');
9
+ console.log(` Name: ${apiKey.name}`);
10
+ console.log(` Key: ${fmt.bold(apiKey.key)}`);
11
+ console.log(` Prefix: ${apiKey.keyPrefix}`);
12
+ console.log('');
13
+ console.log(` ${fmt.yellow('⚠')} Copy this key now — it cannot be shown again.`);
14
+ console.log('');
15
+ }
16
+ catch (err) {
17
+ exitWithError(err, 'Failed to create API key');
18
+ }
19
+ }
20
+ export async function apiKeyListCommand() {
21
+ const { client } = requireLogin();
22
+ try {
23
+ const keys = await client.listApiKeys();
24
+ console.log('');
25
+ if (keys.length === 0) {
26
+ console.log(' No API keys. Create one with: fw apikey create <name>');
27
+ }
28
+ else {
29
+ console.log(` ${keys.length} API key${keys.length === 1 ? '' : 's'}:`);
30
+ console.log('');
31
+ for (const key of keys) {
32
+ const date = new Date(key.createdAt).toLocaleDateString();
33
+ console.log(` ${fmt.cyan(key.keyPrefix + '...')} ${key.name.padEnd(20)} ${date} ${fmt.dim(key.id)}`);
34
+ }
35
+ }
36
+ console.log('');
37
+ }
38
+ catch (err) {
39
+ exitWithError(err, 'Failed to list API keys');
40
+ }
41
+ }
42
+ export async function apiKeyRevokeCommand(idOrPrefix) {
43
+ const { client } = requireLogin();
44
+ try {
45
+ let id = idOrPrefix;
46
+ if (!isUuid(idOrPrefix)) {
47
+ const keys = await client.listApiKeys();
48
+ const match = keys.filter((k) => k.keyPrefix.startsWith(idOrPrefix) || k.id.startsWith(idOrPrefix));
49
+ if (match.length === 0) {
50
+ throw new Error(`No API key matching "${idOrPrefix}"`);
51
+ }
52
+ if (match.length > 1) {
53
+ throw new Error(`Ambiguous: "${idOrPrefix}" matches ${match.length} keys. Use the full ID.`);
54
+ }
55
+ id = match[0].id;
56
+ }
57
+ await client.revokeApiKey(id);
58
+ console.log(fmt.ok('API key revoked'));
59
+ }
60
+ catch (err) {
61
+ exitWithError(err, 'Failed to revoke API key');
62
+ }
63
+ }
64
+ //# sourceMappingURL=apikey.js.map
@@ -1,5 +1,6 @@
1
1
  export declare function loginCommand(options: {
2
2
  email?: string;
3
+ password?: string;
3
4
  apiKey?: string;
4
5
  platformUrl?: string;
5
6
  }): Promise<void>;
@@ -1,11 +1,12 @@
1
1
  import * as readline from 'node:readline';
2
2
  import { loadCredentials, saveCredentials, clearCredentials, getPlatformUrl } from '../config/credentials.js';
3
3
  import { PlatformClient } from '../config/platform-client.js';
4
+ import { fmt } from '../utils/cli-helpers.js';
4
5
  export async function loginCommand(options) {
5
6
  const platformUrl = options.platformUrl ?? getPlatformUrl();
6
7
  const displayUrl = platformUrl.replace(/^https?:\/\//, '');
7
8
  console.log('');
8
- console.log(` \x1b[1mFlow Weaver\x1b[0m \x1b[2m(${displayUrl})\x1b[0m`);
9
+ console.log(` ${fmt.bold('Flow Weaver')} ${fmt.dim(`(${displayUrl})`)}`);
9
10
  console.log('');
10
11
  // API key mode (for CI/headless)
11
12
  if (options.apiKey) {
@@ -14,7 +15,7 @@ export async function loginCommand(options) {
14
15
  }
15
16
  // Email mode (explicit --email flag)
16
17
  if (options.email) {
17
- await loginWithEmail(options.email, platformUrl);
18
+ await loginWithEmail(options.email, platformUrl, options.password);
18
19
  return;
19
20
  }
20
21
  // Default: browser-first device auth
@@ -148,17 +149,17 @@ async function loginWithApiKey(apiKey, platformUrl) {
148
149
  userId = user.id;
149
150
  }
150
151
  catch {
151
- console.error(' \x1b[31m✗\x1b[0m Invalid API key');
152
+ console.error(fmt.err('Invalid API key'));
152
153
  process.exit(1);
153
154
  return;
154
155
  }
155
156
  const expiresAt = Date.now() + 365 * 24 * 60 * 60 * 1000; // 1 year for API keys
156
157
  saveCredentials({ token: apiKey, email, plan: plan, platformUrl, expiresAt, userId });
157
- console.log(` \x1b[32m✓\x1b[0m Logged in as \x1b[1m${email}\x1b[0m (${plan} plan)`);
158
+ console.log(fmt.ok(`Logged in as ${fmt.bold(email)} (${plan} plan)`));
158
159
  console.log('');
159
160
  }
160
- async function loginWithEmail(email, platformUrl) {
161
- const password = await prompt(' Password: ', true);
161
+ async function loginWithEmail(email, platformUrl, passwordFlag) {
162
+ const password = passwordFlag ?? await prompt(' Password: ', true);
162
163
  try {
163
164
  const resp = await fetch(`${platformUrl}/auth/login`, {
164
165
  method: 'POST',
@@ -192,28 +193,27 @@ async function loginWithEmail(email, platformUrl) {
192
193
  }
193
194
  export async function logoutCommand() {
194
195
  clearCredentials();
195
- console.log(' \x1b[32m✓\x1b[0m Logged out');
196
+ console.log(fmt.ok('Logged out'));
196
197
  }
197
198
  export async function authStatusCommand() {
198
199
  const creds = loadCredentials();
199
200
  if (!creds) {
200
201
  console.log('');
201
- console.log(' Not logged in.');
202
- console.log(' Run: \x1b[36mfw login\x1b[0m');
202
+ console.log(fmt.err(`Not logged in. Run: ${fmt.cyan('fw login')}`));
203
203
  console.log('');
204
- return;
204
+ process.exit(1);
205
205
  }
206
206
  const expiresIn = Math.floor((creds.expiresAt - Date.now()) / 1000 / 60 / 60);
207
207
  console.log('');
208
- console.log(` \x1b[32m✓\x1b[0m Logged in as \x1b[1m${creds.email}\x1b[0m`);
208
+ console.log(fmt.ok(`Logged in as ${fmt.bold(creds.email)}`));
209
209
  console.log(` Plan: ${creds.plan}`);
210
- console.log(` Platform: ${creds.platformUrl}`);
210
+ console.log(` Platform: ${fmt.dim(creds.platformUrl)}`);
211
211
  console.log(` Token expires in: ${expiresIn}h`);
212
212
  console.log('');
213
213
  console.log(' Commands unlocked:');
214
- console.log(' \x1b[36mfw deploy <file>\x1b[0m deploy to cloud');
215
- console.log(' \x1b[36mfw cloud-status\x1b[0m see deployments + usage');
216
- console.log(' \x1b[36mweaver assistant\x1b[0m AI with platform credits');
214
+ console.log(` ${fmt.cyan('fw deploy <file>')} deploy to cloud`);
215
+ console.log(` ${fmt.cyan('fw cloud-status')} see deployments + usage`);
216
+ console.log(` ${fmt.cyan('weaver assistant')} AI with platform credits`);
217
217
  console.log('');
218
218
  }
219
219
  function prompt(message, hidden = false) {
@@ -1,31 +1,22 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
- import { loadCredentials } from '../config/credentials.js';
4
- import { PlatformClient } from '../config/platform-client.js';
3
+ import { requireLogin, fmt, exitWithError } from '../utils/cli-helpers.js';
5
4
  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
- }
5
+ const { creds, client } = requireLogin();
12
6
  const absPath = path.resolve(filePath);
13
7
  if (!fs.existsSync(absPath)) {
14
- console.error(` \x1b[31m✗\x1b[0m File not found: ${filePath}`);
15
- process.exit(1);
16
- return;
8
+ exitWithError(new Error(`File not found: ${filePath}`), 'File not found');
17
9
  }
18
10
  const source = fs.readFileSync(absPath, 'utf-8');
19
11
  const name = options.name ?? path.basename(filePath, path.extname(filePath));
20
- const client = new PlatformClient(creds);
21
12
  console.log('');
22
- console.log(` \x1b[2mPushing ${name}...\x1b[0m`);
13
+ console.log(` ${fmt.dim(`Pushing ${name}...`)}`);
23
14
  try {
24
15
  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`);
16
+ console.log(fmt.ok(`Pushed (v${workflow.version})`));
17
+ console.log(` ${fmt.dim('Deploying...')}`);
27
18
  const deployment = await client.deploy(workflow.slug);
28
- console.log(` \x1b[32m✓\x1b[0m Deployed: ${deployment.slug}`);
19
+ console.log(fmt.ok(`Deployed: ${deployment.slug}`));
29
20
  console.log('');
30
21
  console.log(` Endpoint: ${creds.platformUrl}/run/${deployment.slug}`);
31
22
  console.log('');
@@ -37,37 +28,23 @@ export async function deployCommand(filePath, options = {}) {
37
28
  console.log('');
38
29
  }
39
30
  catch (err) {
40
- console.error(` \x1b[31m✗\x1b[0m ${err instanceof Error ? err.message : 'Deploy failed'}`);
41
- process.exit(1);
31
+ exitWithError(err, 'Deploy failed');
42
32
  }
43
33
  }
44
34
  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);
35
+ const { client } = requireLogin();
52
36
  try {
53
37
  await client.undeploy(slug);
54
- console.log(` \x1b[32m✓\x1b[0m Undeployed: ${slug}`);
38
+ console.log(fmt.ok(`Undeployed: ${slug}`));
55
39
  }
56
40
  catch (err) {
57
- console.error(` \x1b[31m✗\x1b[0m ${err instanceof Error ? err.message : 'Undeploy failed'}`);
41
+ exitWithError(err, 'Undeploy failed');
58
42
  }
59
43
  }
60
44
  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);
45
+ const { creds, client } = requireLogin();
69
46
  console.log('');
70
- console.log(` \x1b[1m${creds.email}\x1b[0m \x1b[2m(${creds.plan} plan)\x1b[0m`);
47
+ console.log(` ${fmt.bold(creds.email)} ${fmt.dim(`(${creds.plan} plan)`)}`);
71
48
  console.log('');
72
49
  try {
73
50
  const deployments = await client.listDeployments();
@@ -83,7 +60,7 @@ export async function cloudStatusCommand() {
83
60
  }
84
61
  }
85
62
  catch {
86
- console.log(' \x1b[33m⚠\x1b[0m Could not fetch deployments');
63
+ console.log(` ${fmt.yellow('⚠')} Could not fetch deployments`);
87
64
  }
88
65
  try {
89
66
  const usage = await client.getUsage();
@@ -0,0 +1,8 @@
1
+ export declare function orgListCommand(): Promise<void>;
2
+ export declare function orgCreateCommand(name: string): Promise<void>;
3
+ export declare function orgMembersCommand(identifier: string): Promise<void>;
4
+ export declare function orgInviteCommand(identifier: string, email: string, options: {
5
+ role?: string;
6
+ }): Promise<void>;
7
+ export declare function orgRemoveCommand(identifier: string, userIdentifier: string): Promise<void>;
8
+ //# sourceMappingURL=org.d.ts.map