@synergenius/flow-weaver 0.26.7 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/account.d.ts +4 -0
- package/dist/cli/commands/account.js +50 -0
- package/dist/cli/commands/ai-credentials.d.ts +12 -0
- package/dist/cli/commands/ai-credentials.js +95 -0
- package/dist/cli/commands/apikey.d.ts +4 -0
- package/dist/cli/commands/apikey.js +64 -0
- package/dist/cli/commands/auth.d.ts +1 -0
- package/dist/cli/commands/auth.js +15 -15
- package/dist/cli/commands/deploy.js +14 -37
- package/dist/cli/commands/org.d.ts +8 -0
- package/dist/cli/commands/org.js +136 -0
- package/dist/cli/config/platform-client.d.ts +87 -0
- package/dist/cli/config/platform-client.js +127 -1
- package/dist/cli/flow-weaver.mjs +7447 -14908
- package/dist/cli/index.js +111 -0
- package/dist/cli/utils/cli-helpers.d.ts +44 -0
- package/dist/cli/utils/cli-helpers.js +99 -0
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/package.json +6 -6
|
@@ -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,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,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(`
|
|
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('
|
|
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(`
|
|
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('
|
|
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(
|
|
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
|
-
|
|
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(`
|
|
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('
|
|
215
|
-
console.log('
|
|
216
|
-
console.log('
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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(`
|
|
13
|
+
console.log(` ${fmt.dim(`Pushing ${name}...`)}`);
|
|
23
14
|
try {
|
|
24
15
|
const workflow = await client.pushWorkflow(name, source);
|
|
25
|
-
console.log(`
|
|
26
|
-
console.log(`
|
|
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(`
|
|
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
|
-
|
|
41
|
-
process.exit(1);
|
|
31
|
+
exitWithError(err, 'Deploy failed');
|
|
42
32
|
}
|
|
43
33
|
}
|
|
44
34
|
export async function undeployCommand(slug) {
|
|
45
|
-
const
|
|
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(`
|
|
38
|
+
console.log(fmt.ok(`Undeployed: ${slug}`));
|
|
55
39
|
}
|
|
56
40
|
catch (err) {
|
|
57
|
-
|
|
41
|
+
exitWithError(err, 'Undeploy failed');
|
|
58
42
|
}
|
|
59
43
|
}
|
|
60
44
|
export async function cloudStatusCommand() {
|
|
61
|
-
const creds =
|
|
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(`
|
|
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('
|
|
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
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { requireLogin, isUuid, fmt, exitWithError } from '../utils/cli-helpers.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve an org identifier (UUID, slug, name, or prefix) to an org ID.
|
|
4
|
+
* Exact slug/name matches take priority over prefix matches.
|
|
5
|
+
*/
|
|
6
|
+
async function resolveOrgId(client, identifier) {
|
|
7
|
+
if (isUuid(identifier))
|
|
8
|
+
return identifier;
|
|
9
|
+
const orgs = await client.listOrgs();
|
|
10
|
+
// Exact match first (slug or name)
|
|
11
|
+
const exact = orgs.find((o) => o.slug === identifier || o.name === identifier);
|
|
12
|
+
if (exact)
|
|
13
|
+
return exact.id;
|
|
14
|
+
// Prefix match as fallback
|
|
15
|
+
const prefixMatches = orgs.filter((o) => o.slug.startsWith(identifier) || o.id.startsWith(identifier));
|
|
16
|
+
if (prefixMatches.length === 0) {
|
|
17
|
+
throw new Error(`No organization matching "${identifier}". Run: fw org list`);
|
|
18
|
+
}
|
|
19
|
+
if (prefixMatches.length > 1) {
|
|
20
|
+
throw new Error(`Ambiguous: "${identifier}" matches ${prefixMatches.length} organizations. Use the full slug or ID.`);
|
|
21
|
+
}
|
|
22
|
+
return prefixMatches[0].id;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a user identifier (email, email prefix, or UUID) to a userId within an org.
|
|
26
|
+
* Exact email matches take priority over prefix matches.
|
|
27
|
+
*/
|
|
28
|
+
async function resolveUserId(client, orgId, identifier, orgLabel) {
|
|
29
|
+
if (isUuid(identifier))
|
|
30
|
+
return identifier;
|
|
31
|
+
const org = await client.getOrg(orgId);
|
|
32
|
+
// Exact match first
|
|
33
|
+
const exact = org.members.find((m) => m.email === identifier);
|
|
34
|
+
if (exact)
|
|
35
|
+
return exact.userId;
|
|
36
|
+
// Prefix match as fallback
|
|
37
|
+
const prefixMatches = org.members.filter((m) => m.email.startsWith(identifier) || m.userId.startsWith(identifier));
|
|
38
|
+
if (prefixMatches.length === 0) {
|
|
39
|
+
throw new Error(`No member matching "${identifier}" in this organization. Run: fw org members ${orgLabel ?? orgId}`);
|
|
40
|
+
}
|
|
41
|
+
if (prefixMatches.length > 1) {
|
|
42
|
+
throw new Error(`Ambiguous: "${identifier}" matches ${prefixMatches.length} members. Use the full email or ID.`);
|
|
43
|
+
}
|
|
44
|
+
return prefixMatches[0].userId;
|
|
45
|
+
}
|
|
46
|
+
export async function orgListCommand() {
|
|
47
|
+
const { client } = requireLogin();
|
|
48
|
+
try {
|
|
49
|
+
const orgs = await client.listOrgs();
|
|
50
|
+
console.log('');
|
|
51
|
+
if (orgs.length === 0) {
|
|
52
|
+
console.log(' No organizations. Create one with: fw org create <name>');
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.log(` ${orgs.length} organization${orgs.length === 1 ? '' : 's'}:`);
|
|
56
|
+
console.log('');
|
|
57
|
+
for (const org of orgs) {
|
|
58
|
+
const roleColor = org.role === 'owner' ? '\x1b[33m' : '\x1b[2m';
|
|
59
|
+
console.log(` ${fmt.bold(org.name)} ${fmt.dim(org.slug)} ${roleColor}${org.role}\x1b[0m`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
console.log('');
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
exitWithError(err, 'Failed to list organizations');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export async function orgCreateCommand(name) {
|
|
69
|
+
const { client } = requireLogin();
|
|
70
|
+
try {
|
|
71
|
+
const org = await client.createOrg(name);
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(fmt.ok('Organization created'));
|
|
74
|
+
console.log('');
|
|
75
|
+
console.log(` Name: ${org.name}`);
|
|
76
|
+
console.log(` Slug: ${org.slug}`);
|
|
77
|
+
console.log(` ID: ${fmt.dim(org.id)}`);
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log(` Invite members: fw org invite ${org.slug} <email>`);
|
|
80
|
+
console.log('');
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
exitWithError(err, 'Failed to create organization');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function orgMembersCommand(identifier) {
|
|
87
|
+
const { client } = requireLogin();
|
|
88
|
+
try {
|
|
89
|
+
const orgId = await resolveOrgId(client, identifier);
|
|
90
|
+
const org = await client.getOrg(orgId);
|
|
91
|
+
console.log('');
|
|
92
|
+
console.log(` ${fmt.bold(org.name)}`);
|
|
93
|
+
console.log('');
|
|
94
|
+
if (org.members.length === 0) {
|
|
95
|
+
console.log(' No members.');
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
for (const m of org.members) {
|
|
99
|
+
const roleColor = m.role === 'owner' ? '\x1b[33m' : '\x1b[2m';
|
|
100
|
+
console.log(` ${m.email.padEnd(30)} ${m.name.padEnd(20)} ${roleColor}${m.role}\x1b[0m`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
console.log('');
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
exitWithError(err, 'Failed to get organization');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export async function orgInviteCommand(identifier, email, options) {
|
|
110
|
+
const { client } = requireLogin();
|
|
111
|
+
const role = options.role ?? 'editor';
|
|
112
|
+
try {
|
|
113
|
+
if (!['editor', 'viewer'].includes(role)) {
|
|
114
|
+
throw new Error(`Invalid role "${role}". Use: editor, viewer`);
|
|
115
|
+
}
|
|
116
|
+
const orgId = await resolveOrgId(client, identifier);
|
|
117
|
+
await client.inviteOrgMember(orgId, email, role);
|
|
118
|
+
console.log(fmt.ok(`Invited ${email} as ${role}`));
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
exitWithError(err, 'Failed to invite member');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export async function orgRemoveCommand(identifier, userIdentifier) {
|
|
125
|
+
const { client } = requireLogin();
|
|
126
|
+
try {
|
|
127
|
+
const orgId = await resolveOrgId(client, identifier);
|
|
128
|
+
const userId = await resolveUserId(client, orgId, userIdentifier, identifier);
|
|
129
|
+
await client.removeOrgMember(orgId, userId);
|
|
130
|
+
console.log(fmt.ok('Member removed'));
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
exitWithError(err, 'Failed to remove member');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=org.js.map
|
|
@@ -30,6 +30,93 @@ export declare class PlatformClient {
|
|
|
30
30
|
plan: string;
|
|
31
31
|
}>;
|
|
32
32
|
streamChat(message: string, conversationId?: string): AsyncGenerator<Record<string, unknown>>;
|
|
33
|
+
createApiKey(name: string): Promise<{
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
keyPrefix: string;
|
|
37
|
+
key: string;
|
|
38
|
+
createdAt: string;
|
|
39
|
+
}>;
|
|
40
|
+
listApiKeys(): Promise<Array<{
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
keyPrefix: string;
|
|
44
|
+
createdAt: string;
|
|
45
|
+
}>>;
|
|
46
|
+
revokeApiKey(id: string): Promise<void>;
|
|
47
|
+
createAiCredential(opts: {
|
|
48
|
+
provider: string;
|
|
49
|
+
label: string;
|
|
50
|
+
apiKey: string;
|
|
51
|
+
baseUrl?: string;
|
|
52
|
+
defaultModel?: string;
|
|
53
|
+
isDefault?: boolean;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
id: string;
|
|
56
|
+
provider: string;
|
|
57
|
+
label: string;
|
|
58
|
+
createdAt: string;
|
|
59
|
+
}>;
|
|
60
|
+
listAiCredentials(): Promise<Array<{
|
|
61
|
+
id: string;
|
|
62
|
+
provider: string;
|
|
63
|
+
label: string;
|
|
64
|
+
defaultModel?: string;
|
|
65
|
+
isDefault: boolean;
|
|
66
|
+
createdAt: string;
|
|
67
|
+
}>>;
|
|
68
|
+
revokeAiCredential(id: string): Promise<void>;
|
|
69
|
+
testAiCredential(id: string): Promise<{
|
|
70
|
+
success: boolean;
|
|
71
|
+
message?: string;
|
|
72
|
+
}>;
|
|
73
|
+
getDetailedUsage(): Promise<{
|
|
74
|
+
plan: string;
|
|
75
|
+
usage: {
|
|
76
|
+
workflows: {
|
|
77
|
+
used: number;
|
|
78
|
+
limit: number;
|
|
79
|
+
};
|
|
80
|
+
deployments: {
|
|
81
|
+
used: number;
|
|
82
|
+
limit: number;
|
|
83
|
+
};
|
|
84
|
+
executions: {
|
|
85
|
+
used: number;
|
|
86
|
+
limit: number;
|
|
87
|
+
period: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
limits: {
|
|
91
|
+
timeoutMs: number;
|
|
92
|
+
};
|
|
93
|
+
}>;
|
|
94
|
+
listOrgs(): Promise<Array<{
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
slug: string;
|
|
98
|
+
role: string;
|
|
99
|
+
createdAt: string;
|
|
100
|
+
}>>;
|
|
101
|
+
createOrg(name: string): Promise<{
|
|
102
|
+
id: string;
|
|
103
|
+
name: string;
|
|
104
|
+
slug: string;
|
|
105
|
+
}>;
|
|
106
|
+
getOrg(orgId: string): Promise<{
|
|
107
|
+
id: string;
|
|
108
|
+
name: string;
|
|
109
|
+
slug: string;
|
|
110
|
+
members: Array<{
|
|
111
|
+
userId: string;
|
|
112
|
+
name: string;
|
|
113
|
+
email: string;
|
|
114
|
+
role: string;
|
|
115
|
+
joinedAt: string;
|
|
116
|
+
}>;
|
|
117
|
+
}>;
|
|
118
|
+
inviteOrgMember(orgId: string, email: string, role?: string): Promise<void>;
|
|
119
|
+
removeOrgMember(orgId: string, userId: string): Promise<void>;
|
|
33
120
|
validate(): Promise<boolean>;
|
|
34
121
|
}
|
|
35
122
|
export declare function createPlatformClient(creds: StoredCredentials): PlatformClient;
|