runwork 0.4.1 → 0.5.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.
Files changed (95) hide show
  1. package/bundled-types/core-agent.d.ts +8 -0
  2. package/bundled-types/core-base-agent.d.ts +4 -0
  3. package/bundled-types/core-types.d.ts +101 -0
  4. package/bundled-types/core-workspace.d.ts +10 -0
  5. package/bundled-types/index.d.ts +2 -0
  6. package/bundled-types/shared/core-types.d.ts +101 -0
  7. package/bundled-types/shared/shared/core-types.d.ts +101 -0
  8. package/bundled-types/task-delegation/index.d.ts +50 -0
  9. package/bundled-types/task-delegation/sandbox-bridge.d.ts +88 -0
  10. package/bundled-types/task-delegation/session-manager.d.ts +86 -0
  11. package/bundled-types/task-delegation/stream-parser.d.ts +25 -0
  12. package/bundled-types/task-delegation/task-delegation/index.d.ts +30 -0
  13. package/bundled-types/task-delegation/task-delegation/sandbox-bridge.d.ts +88 -0
  14. package/bundled-types/task-delegation/task-delegation/session-manager.d.ts +86 -0
  15. package/bundled-types/task-delegation/task-delegation/stream-parser.d.ts +25 -0
  16. package/bundled-types/task-delegation/task-delegation/types.d.ts +165 -0
  17. package/bundled-types/task-delegation/types.d.ts +336 -0
  18. package/bundled-types/task-delegation.d.ts +2 -0
  19. package/dist/__tests__/curl-parser.test.d.ts +1 -0
  20. package/dist/__tests__/curl-parser.test.js +250 -0
  21. package/dist/__tests__/types-manager.test.js +22 -18
  22. package/dist/agents/claude-code.d.ts +10 -0
  23. package/dist/agents/claude-code.js +50 -0
  24. package/dist/agents/claude-desktop.d.ts +10 -0
  25. package/dist/agents/claude-desktop.js +141 -0
  26. package/dist/agents/codex.d.ts +10 -0
  27. package/dist/agents/codex.js +45 -0
  28. package/dist/agents/copilot-cli.d.ts +10 -0
  29. package/dist/agents/copilot-cli.js +46 -0
  30. package/dist/agents/copilot-vscode.d.ts +10 -0
  31. package/dist/agents/copilot-vscode.js +37 -0
  32. package/dist/agents/cursor.d.ts +10 -0
  33. package/dist/agents/cursor.js +55 -0
  34. package/dist/agents/detect.d.ts +4 -0
  35. package/dist/agents/detect.js +43 -0
  36. package/dist/agents/gemini.d.ts +10 -0
  37. package/dist/agents/gemini.js +47 -0
  38. package/dist/agents/types.d.ts +35 -0
  39. package/dist/agents/types.js +13 -0
  40. package/dist/agents/utils/json-config.d.ts +14 -0
  41. package/dist/agents/utils/json-config.js +42 -0
  42. package/dist/agents/utils/toml-config.d.ts +8 -0
  43. package/dist/agents/utils/toml-config.js +27 -0
  44. package/dist/agents/windsurf.d.ts +10 -0
  45. package/dist/agents/windsurf.js +50 -0
  46. package/dist/api/client.d.ts +97 -1
  47. package/dist/api/client.js +199 -0
  48. package/dist/auth/store.js +18 -0
  49. package/dist/commands/agents.d.ts +2 -0
  50. package/dist/commands/agents.js +160 -0
  51. package/dist/commands/api-keys.d.ts +2 -0
  52. package/dist/commands/api-keys.js +95 -0
  53. package/dist/commands/apps.d.ts +2 -0
  54. package/dist/commands/apps.js +82 -0
  55. package/dist/commands/components.d.ts +2 -0
  56. package/dist/commands/components.js +43 -0
  57. package/dist/commands/endpoints.d.ts +2 -0
  58. package/dist/commands/endpoints.js +188 -0
  59. package/dist/commands/entities.d.ts +2 -0
  60. package/dist/commands/entities.js +198 -0
  61. package/dist/commands/files.d.ts +2 -0
  62. package/dist/commands/files.js +192 -0
  63. package/dist/commands/info.d.ts +1 -0
  64. package/dist/commands/info.js +71 -127
  65. package/dist/commands/init.d.ts +2 -0
  66. package/dist/commands/init.js +10 -8
  67. package/dist/commands/integrations.js +105 -12
  68. package/dist/commands/mcp.d.ts +2 -0
  69. package/dist/commands/mcp.js +117 -0
  70. package/dist/commands/schedules.d.ts +2 -0
  71. package/dist/commands/schedules.js +221 -0
  72. package/dist/commands/setup.d.ts +2 -0
  73. package/dist/commands/setup.js +199 -0
  74. package/dist/commands/skills.d.ts +2 -0
  75. package/dist/commands/skills.js +134 -0
  76. package/dist/commands/sync.d.ts +2 -0
  77. package/dist/commands/sync.js +146 -0
  78. package/dist/commands/workflows.d.ts +2 -0
  79. package/dist/commands/workflows.js +187 -0
  80. package/dist/generated/bundled-types.js +35 -33
  81. package/dist/generated/version.d.ts +1 -1
  82. package/dist/generated/version.js +1 -1
  83. package/dist/index.js +26 -0
  84. package/dist/logs/tailer.js +41 -1
  85. package/dist/types.d.ts +204 -0
  86. package/dist/ui/banner.js +7 -6
  87. package/dist/utils/app-info.d.ts +66 -0
  88. package/dist/utils/app-info.js +150 -0
  89. package/dist/utils/curl-parser.d.ts +12 -0
  90. package/dist/utils/curl-parser.js +44 -0
  91. package/dist/utils/data-input.d.ts +1 -0
  92. package/dist/utils/data-input.js +38 -0
  93. package/dist/workspace/resolve.d.ts +19 -0
  94. package/dist/workspace/resolve.js +100 -0
  95. package/package.json +3 -2
@@ -2,16 +2,21 @@ import { Command } from 'commander';
2
2
  import { readFileSync, existsSync } from 'fs';
3
3
  import { join } from 'path';
4
4
  import { shouldOutputJson, jsonOut } from '../utils/output.js';
5
+ import { fetchAppInfo } from '../utils/app-info.js';
5
6
  import { bold, cyan, dim, green, yellow, gray } from '../ui/colors.js';
6
7
  import { VERSION } from '../generated/version.js';
7
8
  import { requireAuth } from '../auth/store.js';
8
9
  import { ApiClient } from '../api/client.js';
9
- function readConfig() {
10
- if (!existsSync('.runwork.json')) {
11
- console.error('No .runwork.json found. Run `runwork init` first.');
12
- process.exit(1);
10
+ import { resolveWorkspace } from '../workspace/resolve.js';
11
+ function tryReadConfig() {
12
+ if (!existsSync('.runwork.json'))
13
+ return null;
14
+ try {
15
+ return JSON.parse(readFileSync('.runwork.json', 'utf-8'));
16
+ }
17
+ catch {
18
+ return null;
13
19
  }
14
- return JSON.parse(readFileSync('.runwork.json', 'utf-8'));
15
20
  }
16
21
  function readBlueprint(cwd) {
17
22
  const blueprintPath = join(cwd, 'blueprint.json');
@@ -153,97 +158,19 @@ function buildRegistries(blueprint, serverData, appId) {
153
158
  };
154
159
  return { entities, workflows, scheduledJobs, agents, publicEndpoints, components, fileStorage };
155
160
  }
156
- const CLI_COMMANDS = [
157
- {
158
- name: 'info',
159
- description: 'Show app context, registries, integrations, and CLI reference',
160
- usage: 'runwork info [--json]',
161
- examples: [
162
- 'runwork info --json',
163
- "runwork info --json | jq '.registries.entities'",
164
- "runwork info --json | jq '.preview.url'",
165
- ],
166
- },
167
- {
168
- name: 'dev',
169
- description: 'Start local development with live sync, preview sandbox, and file watching',
170
- usage: 'runwork dev [--json] [--no-logs]',
171
- examples: ['runwork dev', 'runwork dev --json'],
172
- },
173
- {
174
- name: 'deploy',
175
- description: 'Deploy the current app to production',
176
- usage: 'runwork deploy [--json]',
177
- examples: ['runwork deploy', 'runwork deploy --json'],
178
- },
179
- {
180
- name: 'logs',
181
- description: 'View app logs and events from preview or production',
182
- usage: 'runwork logs [--json] [--events] [--production] [--follow] [--type <type>] [--search <text>]',
183
- examples: [
184
- 'runwork logs --json',
185
- 'runwork logs --events --json',
186
- 'runwork logs --events --type failed --json',
187
- 'runwork logs --production --json',
188
- 'runwork logs --follow --json',
189
- ],
190
- },
191
- {
192
- name: 'open',
193
- description: 'Open preview URL or dashboard in browser. In JSON mode, outputs the URL without opening the browser.',
194
- usage: 'runwork open [preview|dashboard] [--json]',
195
- examples: ['runwork open', 'runwork open dashboard', 'runwork open --json'],
196
- },
197
- {
198
- name: 'integrations search',
199
- description: 'Search available integrations from the platform catalog (3200+ integrations)',
200
- usage: 'runwork integrations search <query> [--json] [--limit <n>]',
201
- examples: [
202
- 'runwork integrations search stripe --json',
203
- "runwork integrations search 'google drive' --json --limit 5",
204
- ],
205
- },
206
- {
207
- name: 'integrations list',
208
- description: 'Show integrations connected to the current workspace',
209
- usage: 'runwork integrations list [--json]',
210
- examples: ['runwork integrations list --json'],
211
- },
212
- {
213
- name: 'login',
214
- description: 'Authenticate with Runwork platform (interactive -- requires browser)',
215
- usage: 'runwork login [--base-url <url>]',
216
- examples: ['runwork login'],
217
- note: 'Not available in JSON mode -- requires interactive browser OAuth flow',
218
- },
219
- {
220
- name: 'init',
221
- description: 'Initialize a new Runwork app (interactive)',
222
- usage: 'runwork init [name]',
223
- examples: ['runwork init my-app'],
224
- note: 'Requires interactive prompts for workspace selection. Provide app name as argument to reduce prompts.',
225
- },
226
- {
227
- name: 'clone',
228
- description: 'Clone an existing Runwork app to a local directory for development',
229
- usage: 'runwork clone [appId] [directory]',
230
- examples: ['runwork clone abc-123 ./my-app'],
231
- note: 'Provide appId argument to skip interactive selection',
232
- },
233
- {
234
- name: 'upgrade',
235
- description: 'Upgrade Runwork CLI to the latest version. Use --check to see if an update is available without installing.',
236
- usage: 'runwork upgrade [--check]',
237
- examples: ['runwork upgrade', 'runwork upgrade --check'],
238
- },
239
- {
240
- name: 'logout',
241
- description: 'Remove stored credentials and git credential configuration',
242
- usage: 'runwork logout',
243
- examples: ['runwork logout'],
244
- },
245
- ];
246
- function formatSources(sources) {
161
+ function getCliCommands(command) {
162
+ const program = command.parent;
163
+ if (!program)
164
+ return [];
165
+ return program.commands
166
+ .filter(cmd => !cmd._hidden)
167
+ .map(cmd => {
168
+ const aliases = cmd.aliases();
169
+ const name = aliases.length > 0 ? `${cmd.name()}|${aliases.join('|')}` : cmd.name();
170
+ return { name, description: cmd.description() };
171
+ });
172
+ }
173
+ export function formatSources(sources) {
247
174
  return sources
248
175
  .map(s => {
249
176
  if (s === 'blueprint')
@@ -257,19 +184,22 @@ function formatSources(sources) {
257
184
  .join(dim(', '));
258
185
  }
259
186
  function printHumanOutput(data) {
260
- console.log('');
261
- console.log(bold('Runwork App Info'));
262
187
  console.log('');
263
188
  const pad = (label) => label.padEnd(12);
264
- console.log(` ${dim(pad('App:'))}${bold(data.app.name)}`);
265
189
  console.log(` ${dim(pad('Workspace:'))}${data.workspace.name || data.workspace.id}`);
266
- if (data.preview.url) {
267
- console.log(` ${dim(pad('Preview:'))}${green(data.preview.url)} ${dim('(running)')}`);
268
- }
269
- else {
270
- console.log(` ${dim(pad('Preview:'))}${dim('(not running)')}`);
190
+ if (data.app) {
191
+ console.log('');
192
+ console.log(bold('App Info'));
193
+ console.log('');
194
+ console.log(` ${dim(pad('App:'))}${bold(data.app.name)}`);
195
+ if (data.preview.url) {
196
+ console.log(` ${dim(pad('Preview:'))}${green(data.preview.url)} ${dim('(running)')}`);
197
+ }
198
+ else {
199
+ console.log(` ${dim(pad('Preview:'))}${dim('(not running)')}`);
200
+ }
201
+ console.log(` ${dim(pad('Production:'))}${dim('(not available)')}`);
271
202
  }
272
- console.log(` ${dim(pad('Production:'))}${dim('(not available)')}`);
273
203
  console.log('');
274
204
  // Integrations
275
205
  if (data.integrations === null) {
@@ -368,25 +298,39 @@ export const infoCommand = new Command('info')
368
298
  .description('Show app context, registries, and CLI reference (use --json for agent discovery)')
369
299
  .action(async (_opts, command) => {
370
300
  const asJson = shouldOutputJson(command.optsWithGlobals().json);
371
- const config = readConfig();
372
- const blueprint = readBlueprint(process.cwd());
373
301
  const creds = requireAuth();
374
302
  const client = new ApiClient(creds);
375
- // Preview status -- read-only, never starts a session
376
- let preview = { url: null, active: false };
377
- try {
378
- const status = await client.getDevStatus(config.appId);
379
- if (status.previewUrl) {
380
- preview = { url: status.previewUrl, active: true };
381
- }
303
+ // Try to read project config, but don't require it
304
+ const config = tryReadConfig();
305
+ const blueprint = config ? readBlueprint(process.cwd()) : null;
306
+ // Resolve workspace: project config > --workspace > default > prompt
307
+ let workspaceId;
308
+ let workspaceName;
309
+ if (config) {
310
+ workspaceId = config.workspaceId;
311
+ workspaceName = config.workspaceName ?? config.workspaceId;
382
312
  }
383
- catch {
384
- // No active session -- that's fine
313
+ else {
314
+ const resolved = await resolveWorkspace(client, {});
315
+ workspaceId = resolved.workspaceId;
316
+ workspaceName = resolved.workspaceName || resolved.workspaceId;
385
317
  }
318
+ const appId = config?.appId;
319
+ const appName = config?.appName;
320
+ // App-specific info (preview, dashboard, git remote, server registries)
321
+ const appInfo = appId ? await fetchAppInfo(client, {
322
+ appId,
323
+ appName: appName ?? '',
324
+ appSlug: appName ?? '',
325
+ workspaceId,
326
+ workspaceName,
327
+ baseUrl: creds.baseUrl || 'https://runwork.ai',
328
+ }) : null;
329
+ const preview = appInfo?.preview ?? { url: null, active: false };
386
330
  // Integrations
387
331
  let integrations = null;
388
332
  try {
389
- const raw = await client.listConnectedIntegrations(config.workspaceId);
333
+ const raw = await client.listConnectedIntegrations(workspaceId);
390
334
  integrations = raw.map(i => ({
391
335
  id: i.canonicalId ?? i.integrationId,
392
336
  status: i.status ?? 'connected',
@@ -398,21 +342,21 @@ export const infoCommand = new Command('info')
398
342
  // Workspace registries
399
343
  let serverData = null;
400
344
  try {
401
- serverData = await client.getWorkspaceAll(config.workspaceId);
345
+ serverData = await client.getWorkspaceAll(workspaceId);
402
346
  }
403
347
  catch {
404
348
  process.stderr.write('Warning: failed to fetch workspace registries\n');
405
349
  }
406
- const registries = buildRegistries(blueprint, serverData, config.appId);
350
+ const registries = buildRegistries(blueprint, serverData, appId ?? '');
407
351
  const output = {
408
- app: {
409
- id: config.appId,
410
- name: config.appName,
411
- slug: config.appName,
412
- },
352
+ app: appId ? {
353
+ id: appId,
354
+ name: appName ?? '',
355
+ slug: appName ?? '',
356
+ } : null,
413
357
  workspace: {
414
- id: config.workspaceId,
415
- name: config.workspaceName ?? config.workspaceId,
358
+ id: workspaceId,
359
+ name: workspaceName,
416
360
  },
417
361
  preview,
418
362
  production: { url: null, deployed: false },
@@ -420,7 +364,7 @@ export const infoCommand = new Command('info')
420
364
  registries,
421
365
  cli: {
422
366
  version: VERSION,
423
- commands: CLI_COMMANDS,
367
+ commands: getCliCommands(command),
424
368
  },
425
369
  };
426
370
  if (asJson) {
@@ -2,4 +2,6 @@ import { Command } from 'commander';
2
2
  import { ApiClient } from '../api/client.js';
3
3
  import type { WorkspaceInfo } from '../types.js';
4
4
  export declare function execInit(client: ApiClient, appName: string, workspace: WorkspaceInfo): Promise<string>;
5
+ /** Full create flow: prompt for name/workspace, init, and run agent wizard */
6
+ export declare function runCreateFlow(name?: string): Promise<void>;
5
7
  export declare const initCommand: Command;
@@ -66,13 +66,10 @@ export async function execInit(client, appName, workspace) {
66
66
  }
67
67
  execFileSync('git', ['push', '-u', 'runwork', 'main'], { cwd: dir, stdio: 'inherit' });
68
68
  console.log(`\nApp "${app.name}" initialized in ${dir}/`);
69
- console.log(`Remote: ${remoteUrl}`);
70
69
  return resolve(dir);
71
70
  }
72
- export const initCommand = new Command('init')
73
- .description('Initialize a new Runwork app')
74
- .argument('[name]', 'App name')
75
- .action(async (name) => {
71
+ /** Full create flow: prompt for name/workspace, init, and run agent wizard */
72
+ export async function runCreateFlow(name) {
76
73
  const creds = requireAuth();
77
74
  const client = new ApiClient(creds);
78
75
  const appName = name || await promptInput('App name');
@@ -87,9 +84,14 @@ export const initCommand = new Command('init')
87
84
  }
88
85
  const workspaceChoice = await promptSelect('Select workspace:', workspaces.map(w => ({ label: w.name, value: w })));
89
86
  const workspace = workspaceChoice.value;
90
- console.log(`Initializing "${appName}" in workspace "${workspace.name}"...`);
87
+ console.log(`Creating "${appName}" in workspace "${workspace.name}"...`);
91
88
  const dir = await execInit(client, appName, workspace);
92
- const slug = dir.split('/').pop() || dir;
93
89
  await runAgentWizard(dir);
94
- console.log(`Next: cd ${slug} && runwork dev`);
90
+ }
91
+ export const initCommand = new Command('init')
92
+ .alias('create')
93
+ .description('Create a new Runwork app')
94
+ .argument('[name]', 'App name')
95
+ .action(async (name) => {
96
+ await runCreateFlow(name);
95
97
  });
@@ -1,15 +1,10 @@
1
1
  import { Command } from 'commander';
2
+ import { readFileSync, existsSync } from 'fs';
2
3
  import { requireAuth } from '../auth/store.js';
3
4
  import { ApiClient } from '../api/client.js';
4
- import { readFileSync, existsSync } from 'fs';
5
5
  import { shouldOutputJson, jsonOut } from '../utils/output.js';
6
- function readConfig() {
7
- if (!existsSync('.runwork.json')) {
8
- console.error('No .runwork.json found. Run `runwork init` first.');
9
- process.exit(1);
10
- }
11
- return JSON.parse(readFileSync('.runwork.json', 'utf-8'));
12
- }
6
+ import { resolveWorkspace } from '../workspace/resolve.js';
7
+ import { parseCurlToRequest } from '../utils/curl-parser.js';
13
8
  const searchCommand = new Command('search')
14
9
  .description('Search available integrations from the platform catalog')
15
10
  .argument('<query>', 'Search query (e.g., "google drive", "hubspot", "slack")')
@@ -63,13 +58,14 @@ const searchCommand = new Command('search')
63
58
  });
64
59
  const listCommand = new Command('list')
65
60
  .description('List connected workspace integrations')
66
- .action(async (_opts, command) => {
61
+ .option('--workspace <id>', 'Workspace ID')
62
+ .action(async (opts, command) => {
67
63
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
68
64
  const credentials = requireAuth();
69
65
  const client = new ApiClient(credentials);
70
- const config = readConfig();
66
+ const { workspaceId } = await resolveWorkspace(client, opts);
71
67
  try {
72
- const integrations = await client.listConnectedIntegrations(config.workspaceId);
68
+ const integrations = await client.listConnectedIntegrations(workspaceId);
73
69
  if (useJson) {
74
70
  const items = integrations.map(i => ({
75
71
  id: i.canonicalId || i.integrationId,
@@ -97,7 +93,104 @@ const listCommand = new Command('list')
97
93
  process.exit(1);
98
94
  }
99
95
  });
96
+ const callCommand = new Command('call')
97
+ .description('Make a proxy call to a connected integration')
98
+ .argument('<integration>', 'Integration name (e.g., hubspot, slack)')
99
+ .argument('[method]', 'HTTP method (GET, POST, PUT, DELETE)')
100
+ .argument('[path]', 'API path (e.g., /crm/v3/contacts)')
101
+ .option('--workspace <id>', 'Workspace ID')
102
+ .option('--body <json>', 'Request body JSON')
103
+ .option('--header <header>', 'Request header (repeatable)', (val, prev) => [...prev, val], [])
104
+ .option('--query <query>', 'Query string (e.g., limit=10&offset=0)')
105
+ .option('--curl <command>', 'Parse a curl command (paste from Chrome DevTools "Copy as cURL")')
106
+ .option('--curl-file <file>', 'Read curl command from a file')
107
+ .action(async (integration, method, path, opts, command) => {
108
+ const useJson = shouldOutputJson(command.optsWithGlobals().json);
109
+ const credentials = requireAuth();
110
+ const client = new ApiClient(credentials);
111
+ const { workspaceId } = await resolveWorkspace(client, opts);
112
+ let finalMethod;
113
+ let finalPath;
114
+ const headers = {};
115
+ let body;
116
+ let query = opts.query;
117
+ // Mode 1: --curl or --curl-file
118
+ if (opts.curl || opts.curlFile) {
119
+ let curlStr = opts.curl;
120
+ if (opts.curlFile) {
121
+ const filePath = opts.curlFile;
122
+ if (!existsSync(filePath)) {
123
+ console.error(`File not found: ${filePath}`);
124
+ process.exit(1);
125
+ }
126
+ curlStr = readFileSync(filePath, 'utf-8');
127
+ }
128
+ try {
129
+ const result = await parseCurlToRequest(curlStr);
130
+ finalMethod = result.method;
131
+ finalPath = result.path;
132
+ if (result.query)
133
+ query = query || result.query;
134
+ Object.assign(headers, result.headers);
135
+ body = result.body;
136
+ }
137
+ catch (err) {
138
+ console.error('Failed to parse curl command:', err instanceof Error ? err.message : err);
139
+ process.exit(1);
140
+ }
141
+ }
142
+ // Mode 2: explicit method + path args
143
+ else if (method && path) {
144
+ finalMethod = method.toUpperCase();
145
+ finalPath = path;
146
+ // Parse headers from repeatable --header flags
147
+ for (const h of (opts.header || [])) {
148
+ const [key, ...rest] = h.split(':');
149
+ headers[key.trim()] = rest.join(':').trim();
150
+ }
151
+ // Parse body
152
+ if (opts.body) {
153
+ try {
154
+ body = JSON.parse(opts.body);
155
+ }
156
+ catch {
157
+ console.error('Invalid JSON in --body');
158
+ process.exit(1);
159
+ }
160
+ }
161
+ }
162
+ else {
163
+ console.error('Usage: runwork integrations call <integration> <method> <path>');
164
+ console.error(' or: runwork integrations call <integration> --curl "curl ..."');
165
+ process.exit(1);
166
+ }
167
+ try {
168
+ // Find the integration's database row ID from connected integrations
169
+ const integrations = await client.listConnectedIntegrations(workspaceId);
170
+ const match = integrations.find(i => (i.canonicalId || i.integrationId) === integration || i.provider === integration);
171
+ if (!match) {
172
+ console.error(`Integration "${integration}" not found. Run "runwork integrations list" to see connected integrations.`);
173
+ process.exit(1);
174
+ }
175
+ const result = await client.callIntegrationProxy(match.integrationId, finalMethod, finalPath, { body, headers: Object.keys(headers).length > 0 ? headers : undefined, query });
176
+ if (useJson) {
177
+ jsonOut(result);
178
+ return;
179
+ }
180
+ if (typeof result === 'string') {
181
+ console.log(result);
182
+ }
183
+ else {
184
+ console.log(JSON.stringify(result, null, 2));
185
+ }
186
+ }
187
+ catch (err) {
188
+ console.error('Failed to call integration:', err instanceof Error ? err.message : err);
189
+ process.exit(1);
190
+ }
191
+ });
100
192
  export const integrationsCommand = new Command('integrations')
101
193
  .description('Search and manage workspace integrations')
102
194
  .addCommand(searchCommand)
103
- .addCommand(listCommand);
195
+ .addCommand(listCommand)
196
+ .addCommand(callCommand);
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const mcpCommand: Command;
@@ -0,0 +1,117 @@
1
+ import { Command } from 'commander';
2
+ import { requireAuth } from '../auth/store.js';
3
+ import { ApiClient } from '../api/client.js';
4
+ import { resolveWorkspace } from '../workspace/resolve.js';
5
+ import { promptConfirm } from '../utils/prompt.js';
6
+ import { shouldOutputJson, jsonOut } from '../utils/output.js';
7
+ const listCommand = new Command('list')
8
+ .description('List workspace MCP servers')
9
+ .option('--workspace <id>', 'Workspace ID')
10
+ .action(async (opts, command) => {
11
+ const useJson = shouldOutputJson(command.optsWithGlobals().json);
12
+ const credentials = requireAuth();
13
+ const client = new ApiClient(credentials);
14
+ const { workspaceId, workspaceName } = await resolveWorkspace(client, opts);
15
+ try {
16
+ const servers = await client.listMcpServers(workspaceId);
17
+ if (useJson) {
18
+ jsonOut({ servers });
19
+ return;
20
+ }
21
+ if (servers.length === 0) {
22
+ console.log('No MCP servers configured in this workspace.');
23
+ console.log('Add one with: runwork mcp add <name> <url>');
24
+ return;
25
+ }
26
+ console.log(`\nWorkspace: ${workspaceName || workspaceId}\n`);
27
+ console.log(` ${'Name'.padEnd(20)} ${'URL'.padEnd(45)} Status`);
28
+ console.log(' ' + '-'.repeat(75));
29
+ for (const s of servers) {
30
+ const status = s.enabled
31
+ ? (s.toolCount ? `connected (${s.toolCount} tools)` : 'enabled')
32
+ : 'disabled';
33
+ console.log(` ${s.name.padEnd(20)} ${s.url.padEnd(45)} ${status}`);
34
+ }
35
+ console.log('');
36
+ }
37
+ catch (err) {
38
+ console.error('Failed to list MCP servers:', err instanceof Error ? err.message : err);
39
+ process.exit(1);
40
+ }
41
+ });
42
+ const addCommand = new Command('add')
43
+ .description('Add an MCP server to workspace')
44
+ .argument('<name>', 'Server name')
45
+ .argument('<url>', 'Server URL (Streamable HTTP or SSE endpoint)')
46
+ .option('--transport <type>', 'Transport type: streamable-http or sse', 'streamable-http')
47
+ .option('--workspace <id>', 'Workspace ID')
48
+ .action(async (name, url, opts, command) => {
49
+ const useJson = shouldOutputJson(command.optsWithGlobals().json);
50
+ const credentials = requireAuth();
51
+ const client = new ApiClient(credentials);
52
+ const { workspaceId } = await resolveWorkspace(client, opts);
53
+ try {
54
+ new URL(url);
55
+ }
56
+ catch {
57
+ console.error(`Invalid URL: ${url}`);
58
+ process.exit(1);
59
+ }
60
+ try {
61
+ const server = await client.addMcpServer(workspaceId, {
62
+ name,
63
+ url,
64
+ transport: opts.transport,
65
+ });
66
+ if (useJson) {
67
+ jsonOut({ server });
68
+ return;
69
+ }
70
+ console.log(`Added MCP server "${name}" (${url})`);
71
+ console.log('Run `runwork sync` to update your local agent configs.');
72
+ }
73
+ catch (err) {
74
+ console.error('Failed to add MCP server:', err instanceof Error ? err.message : err);
75
+ process.exit(1);
76
+ }
77
+ });
78
+ const removeCommand = new Command('remove')
79
+ .description('Remove an MCP server from workspace')
80
+ .argument('<name>', 'Server name')
81
+ .option('--yes', 'Skip confirmation')
82
+ .option('--workspace <id>', 'Workspace ID')
83
+ .action(async (name, opts, command) => {
84
+ const useJson = shouldOutputJson(command.optsWithGlobals().json);
85
+ const credentials = requireAuth();
86
+ const client = new ApiClient(credentials);
87
+ const { workspaceId } = await resolveWorkspace(client, opts);
88
+ try {
89
+ const servers = await client.listMcpServers(workspaceId);
90
+ const match = servers.find(s => s.name === name);
91
+ if (!match) {
92
+ console.error(`MCP server "${name}" not found in workspace.`);
93
+ process.exit(1);
94
+ }
95
+ if (!opts.yes && !useJson) {
96
+ const confirmed = await promptConfirm(`Remove MCP server "${name}" (${match.url})?`);
97
+ if (!confirmed)
98
+ return;
99
+ }
100
+ await client.removeMcpServer(workspaceId, match.id);
101
+ if (useJson) {
102
+ jsonOut({ removed: true, name });
103
+ return;
104
+ }
105
+ console.log(`Removed MCP server "${name}".`);
106
+ console.log('Run `runwork sync` to update your local agent configs.');
107
+ }
108
+ catch (err) {
109
+ console.error('Failed to remove MCP server:', err instanceof Error ? err.message : err);
110
+ process.exit(1);
111
+ }
112
+ });
113
+ export const mcpCommand = new Command('mcp')
114
+ .description('Manage workspace MCP servers')
115
+ .addCommand(listCommand)
116
+ .addCommand(addCommand)
117
+ .addCommand(removeCommand);
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const schedulesCommand: Command;