runwork 0.4.0 → 0.5.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.
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
@@ -0,0 +1,146 @@
1
+ import { Command } from 'commander';
2
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { homedir } from 'os';
5
+ import { requireAuth } from '../auth/store.js';
6
+ import { ApiClient } from '../api/client.js';
7
+ import { getAdapterBySlug } from '../agents/detect.js';
8
+ import { RUNWORK_MCP_PREFIX } from '../agents/types.js';
9
+ function loadSetupState(filePath) {
10
+ if (!existsSync(filePath))
11
+ return null;
12
+ try {
13
+ return JSON.parse(readFileSync(filePath, 'utf-8'));
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ async function syncFromState(state, statePath, credentials, dryRun) {
20
+ const client = new ApiClient(credentials);
21
+ console.log(`Syncing workspace: ${state.workspaceName || state.workspaceId}`);
22
+ // Fetch latest data
23
+ const [allSkills, mcpServers, externalSkills] = await Promise.all([
24
+ client.listWorkspaceSkills(state.workspaceId),
25
+ client.listMcpServers(state.workspaceId),
26
+ client.listExternalSkills(state.workspaceId),
27
+ ]);
28
+ // Build entries
29
+ const wsPrefix = state.workspaceId.substring(0, 8);
30
+ const baseUrl = credentials.baseUrl || 'https://runwork.ai';
31
+ const mcpEntries = mcpServers
32
+ .filter(s => s.enabled)
33
+ .map(s => ({
34
+ name: `${RUNWORK_MCP_PREFIX}${wsPrefix}-${s.name}`,
35
+ url: s.url,
36
+ transport: s.transport,
37
+ headers: { Authorization: `Bearer ${credentials.apiKey}` },
38
+ }));
39
+ mcpEntries.push({
40
+ name: `${RUNWORK_MCP_PREFIX}${wsPrefix}-workspace`,
41
+ url: `${baseUrl}/api/workspaces/${state.workspaceId}/mcp`,
42
+ transport: 'streamable-http',
43
+ headers: { Authorization: `Bearer ${credentials.apiKey}` },
44
+ });
45
+ const baseSkillFiles = externalSkills.map(s => ({
46
+ name: s.name,
47
+ filename: s.name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
48
+ content: s.content,
49
+ description: s.description,
50
+ }));
51
+ // Fetch all app skills in parallel
52
+ const allAppSkillFiles = [];
53
+ const appSkillEntries = allSkills.filter(s => s.type === 'app' && s.appId);
54
+ const appSkillResults = await Promise.all(appSkillEntries.map(async (s) => {
55
+ try {
56
+ const content = await client.getAppSkillContent(state.workspaceId, s.appId);
57
+ if (content && content.trim()) {
58
+ return {
59
+ name: s.name,
60
+ filename: s.name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
61
+ content,
62
+ description: s.description || `App skill for ${s.name}`,
63
+ };
64
+ }
65
+ }
66
+ catch { /* skip */ }
67
+ return null;
68
+ }));
69
+ for (const r of appSkillResults) {
70
+ if (r)
71
+ allAppSkillFiles.push(r);
72
+ }
73
+ // Project scope: only current app's skill; User scope: all app skills
74
+ let projectAppSkillFile = null;
75
+ if (existsSync('.runwork.json')) {
76
+ try {
77
+ const config = JSON.parse(readFileSync('.runwork.json', 'utf-8'));
78
+ if (config.appName) {
79
+ const fname = config.appName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
80
+ projectAppSkillFile = allAppSkillFiles.find(s => s.filename === fname) || null;
81
+ }
82
+ }
83
+ catch { /* ignore */ }
84
+ }
85
+ const userSkillFiles = [...baseSkillFiles, ...allAppSkillFiles];
86
+ const projectSkillFiles = projectAppSkillFile
87
+ ? [...baseSkillFiles, projectAppSkillFile]
88
+ : [...baseSkillFiles];
89
+ if (dryRun) {
90
+ console.log(` Would update: ${mcpEntries.length} MCP servers, ${userSkillFiles.length} skills (user), ${projectSkillFiles.length} skills (project)`);
91
+ console.log(` Agents: ${state.configuredAgents.join(', ')}`);
92
+ return;
93
+ }
94
+ // Write to configured agents
95
+ const scopes = state.scope === 'both' ? ['project', 'user'] : [state.scope];
96
+ for (const slug of state.configuredAgents) {
97
+ const adapter = getAdapterBySlug(slug);
98
+ if (!adapter)
99
+ continue;
100
+ for (const scope of scopes) {
101
+ const skillFiles = scope === 'project' ? projectSkillFiles : userSkillFiles;
102
+ try {
103
+ if (adapter.supportsMcpScope(scope) && mcpEntries.length > 0) {
104
+ await adapter.writeMcpServers(mcpEntries, scope);
105
+ }
106
+ if (adapter.supportsSkills() && skillFiles.length > 0) {
107
+ await adapter.writeSkills(skillFiles, scope);
108
+ }
109
+ }
110
+ catch (err) {
111
+ console.warn(` [${adapter.name}] Failed (${scope}): ${err instanceof Error ? err.message : err}`);
112
+ }
113
+ }
114
+ console.log(` [${adapter.name}] Updated`);
115
+ }
116
+ // Update state
117
+ state.lastSyncAt = new Date().toISOString();
118
+ state.mcpServers = mcpEntries.map(e => e.name);
119
+ state.skills = userSkillFiles.map(s => s.name);
120
+ writeFileSync(statePath, JSON.stringify(state, null, 2));
121
+ }
122
+ export const syncCommand = new Command('sync')
123
+ .description('Refresh local agent configs with latest workspace skills and MCP servers')
124
+ .option('--dry-run', 'Show what would change without writing files')
125
+ .action(async (opts) => {
126
+ const credentials = requireAuth();
127
+ const dryRun = !!opts.dryRun;
128
+ const projectStatePath = join(process.cwd(), '.runwork', 'setup.json');
129
+ const userStatePath = join(homedir(), '.runwork', 'setup.json');
130
+ const projectState = loadSetupState(projectStatePath);
131
+ const userState = loadSetupState(userStatePath);
132
+ if (!projectState && !userState) {
133
+ console.error('No setup state found. Run `runwork setup` first.');
134
+ process.exit(1);
135
+ }
136
+ // Sync both states independently — even if same workspace, they may target different scopes/agents
137
+ if (projectState) {
138
+ await syncFromState(projectState, projectStatePath, credentials, dryRun);
139
+ }
140
+ if (userState) {
141
+ await syncFromState(userState, userStatePath, credentials, dryRun);
142
+ }
143
+ if (!dryRun) {
144
+ console.log('\nSync complete.');
145
+ }
146
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const workflowsCommand: Command;
@@ -0,0 +1,187 @@
1
+ import { Command } from 'commander';
2
+ import { requireAuth } from '../auth/store.js';
3
+ import { ApiClient } from '../api/client.js';
4
+ import { resolveWorkspace, resolveApp } from '../workspace/resolve.js';
5
+ import { shouldOutputJson, jsonOut } from '../utils/output.js';
6
+ function truncate(text, max) {
7
+ if (!text)
8
+ return '';
9
+ const first = text.split('\n')[0];
10
+ if (first.length <= max)
11
+ return first;
12
+ return first.slice(0, max - 3) + '...';
13
+ }
14
+ const listCommand = new Command('list')
15
+ .description('List workflows in a workspace')
16
+ .option('--workspace <id>', 'Workspace ID')
17
+ .option('--app <name>', 'Filter by app name or ID')
18
+ .action(async (opts, command) => {
19
+ const useJson = shouldOutputJson(command.optsWithGlobals().json);
20
+ const credentials = requireAuth();
21
+ const client = new ApiClient(credentials);
22
+ const { workspaceId, workspaceName } = await resolveWorkspace(client, opts);
23
+ try {
24
+ let workflows = await client.listWorkflows(workspaceId);
25
+ if (opts.app) {
26
+ workflows = workflows.filter(w => w.appId === opts.app || w.appName === opts.app);
27
+ }
28
+ if (useJson) {
29
+ jsonOut({ workflows });
30
+ return;
31
+ }
32
+ if (workflows.length === 0) {
33
+ console.log('No workflows found.');
34
+ return;
35
+ }
36
+ console.log(`\nWorkspace: ${workspaceName || workspaceId}\n`);
37
+ console.log(' ' +
38
+ 'Name'.padEnd(24) +
39
+ 'App'.padEnd(20) +
40
+ 'Mode'.padEnd(12) +
41
+ 'Enabled'.padEnd(10) +
42
+ 'Description');
43
+ console.log(' ' + '-'.repeat(80));
44
+ for (const w of workflows) {
45
+ const mode = w.deploymentMode ?? '';
46
+ const enabled = w.enabled === false ? 'no' : 'yes';
47
+ console.log(' ' +
48
+ w.name.padEnd(24) +
49
+ (w.appName || w.appId).slice(0, 18).padEnd(20) +
50
+ mode.padEnd(12) +
51
+ enabled.padEnd(10) +
52
+ truncate(w.description, 40));
53
+ }
54
+ console.log('');
55
+ }
56
+ catch (err) {
57
+ console.error('Failed to list workflows:', err instanceof Error ? err.message : err);
58
+ process.exit(1);
59
+ }
60
+ });
61
+ const triggerCommand = new Command('trigger')
62
+ .description('Trigger a workflow by name')
63
+ .argument('<name>', 'Workflow name')
64
+ .option('--workspace <id>', 'Workspace ID')
65
+ .option('--app <name>', 'App name or ID (required if not in a project directory)')
66
+ .option('--params <json>', 'JSON string of parameters to pass to the workflow')
67
+ .option('--instance-id <id>', 'Custom instance ID for the workflow run')
68
+ .action(async (name, opts, command) => {
69
+ const useJson = shouldOutputJson(command.optsWithGlobals().json);
70
+ const credentials = requireAuth();
71
+ const client = new ApiClient(credentials);
72
+ const { workspaceId } = await resolveWorkspace(client, opts);
73
+ const { appId, appName } = await resolveApp(client, workspaceId, opts);
74
+ let params;
75
+ if (opts.params) {
76
+ try {
77
+ params = JSON.parse(opts.params);
78
+ }
79
+ catch {
80
+ console.error('Failed to parse --params: must be valid JSON');
81
+ process.exit(1);
82
+ }
83
+ }
84
+ try {
85
+ const result = await client.triggerWorkflow(workspaceId, {
86
+ workflowName: name,
87
+ appId,
88
+ params,
89
+ instanceId: opts.instanceId,
90
+ });
91
+ if (useJson) {
92
+ jsonOut({ result });
93
+ return;
94
+ }
95
+ console.log(`Triggered workflow "${name}" on app "${appName || appId}".`);
96
+ console.log(` Instance ID: ${result.instanceId}`);
97
+ console.log(` Status: ${result.status}`);
98
+ }
99
+ catch (err) {
100
+ console.error('Failed to trigger workflow:', err instanceof Error ? err.message : err);
101
+ process.exit(1);
102
+ }
103
+ });
104
+ const instancesCommand = new Command('instances')
105
+ .description('List workflow instances for an app')
106
+ .option('--workspace <id>', 'Workspace ID')
107
+ .option('--app <name>', 'App name or ID (required if not in a project directory)')
108
+ .option('--workflow <name>', 'Filter by workflow name')
109
+ .option('--status <status>', 'Filter by instance status')
110
+ .option('--limit <n>', 'Maximum number of instances to return')
111
+ .action(async (opts, command) => {
112
+ const useJson = shouldOutputJson(command.optsWithGlobals().json);
113
+ const credentials = requireAuth();
114
+ const client = new ApiClient(credentials);
115
+ const { workspaceId } = await resolveWorkspace(client, opts);
116
+ const { appId } = await resolveApp(client, workspaceId, opts);
117
+ try {
118
+ const data = await client.listWorkflowInstances(workspaceId, appId, {
119
+ workflowName: opts.workflow,
120
+ status: opts.status,
121
+ limit: opts.limit !== undefined ? Number(opts.limit) : undefined,
122
+ });
123
+ if (useJson) {
124
+ jsonOut({ instances: data.instances });
125
+ return;
126
+ }
127
+ if (data.instances.length === 0) {
128
+ console.log('No workflow instances found.');
129
+ return;
130
+ }
131
+ console.log('\n ' +
132
+ 'ID'.padEnd(28) +
133
+ 'Workflow'.padEnd(24) +
134
+ 'Status'.padEnd(14) +
135
+ 'Created At');
136
+ console.log(' ' + '-'.repeat(80));
137
+ for (const inst of data.instances) {
138
+ const created = inst.createdAt
139
+ ? new Date(inst.createdAt).toLocaleString()
140
+ : '';
141
+ console.log(' ' +
142
+ inst.id.slice(0, 26).padEnd(28) +
143
+ inst.workflowName.padEnd(24) +
144
+ inst.status.padEnd(14) +
145
+ created);
146
+ }
147
+ console.log('');
148
+ }
149
+ catch (err) {
150
+ console.error('Failed to list workflow instances:', err instanceof Error ? err.message : err);
151
+ process.exit(1);
152
+ }
153
+ });
154
+ const instanceCommand = new Command('instance')
155
+ .description('Get details for a specific workflow instance')
156
+ .argument('<id>', 'Workflow instance ID')
157
+ .option('--workspace <id>', 'Workspace ID')
158
+ .option('--app <name>', 'App name or ID (optional)')
159
+ .action(async (id, opts, command) => {
160
+ const useJson = shouldOutputJson(command.optsWithGlobals().json);
161
+ const credentials = requireAuth();
162
+ const client = new ApiClient(credentials);
163
+ const { workspaceId } = await resolveWorkspace(client, opts);
164
+ let appId;
165
+ if (opts.app) {
166
+ const resolved = await resolveApp(client, workspaceId, opts);
167
+ appId = resolved.appId;
168
+ }
169
+ try {
170
+ const data = await client.getWorkflowInstance(workspaceId, id, appId);
171
+ if (useJson) {
172
+ jsonOut({ instance: data.instance });
173
+ return;
174
+ }
175
+ console.log(JSON.stringify(data.instance, null, 2));
176
+ }
177
+ catch (err) {
178
+ console.error('Failed to get workflow instance:', err instanceof Error ? err.message : err);
179
+ process.exit(1);
180
+ }
181
+ });
182
+ export const workflowsCommand = new Command('workflows')
183
+ .description('Manage workspace workflows')
184
+ .addCommand(listCommand)
185
+ .addCommand(triggerCommand)
186
+ .addCommand(instancesCommand)
187
+ .addCommand(instanceCommand);