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
@@ -1 +1 @@
1
- export declare const VERSION = "0.4.0";
1
+ export declare const VERSION = "0.5.0";
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/embed-types.ts -- do not edit
2
- export const VERSION = "0.4.0";
2
+ export const VERSION = "0.5.0";
package/dist/index.js CHANGED
@@ -10,6 +10,19 @@ import { logoutCommand } from './commands/logout.js';
10
10
  import { integrationsCommand } from './commands/integrations.js';
11
11
  import { openCommand } from './commands/open.js';
12
12
  import { infoCommand } from './commands/info.js';
13
+ import { skillsCommand } from './commands/skills.js';
14
+ import { entitiesCommand } from './commands/entities.js';
15
+ import { workflowsCommand } from './commands/workflows.js';
16
+ import { schedulesCommand } from './commands/schedules.js';
17
+ import { endpointsCommand } from './commands/endpoints.js';
18
+ import { filesCommand } from './commands/files.js';
19
+ import { componentsCommand } from './commands/components.js';
20
+ import { apiKeysCommand } from './commands/api-keys.js';
21
+ import { agentsCommand } from './commands/agents.js';
22
+ import { mcpCommand } from './commands/mcp.js';
23
+ import { setupCommand } from './commands/setup.js';
24
+ import { syncCommand } from './commands/sync.js';
25
+ import { appsCommand } from './commands/apps.js';
13
26
  import { handleGitCredentialRequest } from './git/credentials.js';
14
27
  import { VERSION } from './generated/version.js';
15
28
  const program = new Command();
@@ -30,6 +43,19 @@ program.addCommand(upgradeCommand);
30
43
  program.addCommand(logoutCommand);
31
44
  program.addCommand(integrationsCommand);
32
45
  program.addCommand(openCommand);
46
+ program.addCommand(skillsCommand);
47
+ program.addCommand(entitiesCommand);
48
+ program.addCommand(workflowsCommand);
49
+ program.addCommand(schedulesCommand);
50
+ program.addCommand(endpointsCommand);
51
+ program.addCommand(filesCommand);
52
+ program.addCommand(componentsCommand);
53
+ program.addCommand(apiKeysCommand);
54
+ program.addCommand(agentsCommand);
55
+ program.addCommand(mcpCommand);
56
+ program.addCommand(setupCommand);
57
+ program.addCommand(syncCommand);
58
+ program.addCommand(appsCommand);
33
59
  program.addHelpText('after', '\nFor AI agents: run "runwork info --json" to discover app context and available commands.');
34
60
  const credentialHelper = program
35
61
  .command('git-credential-helper', { hidden: true })
@@ -54,6 +54,9 @@ export function startLogTailer(options) {
54
54
  let seenEventIds = new Set();
55
55
  let stopped = false;
56
56
  let timer;
57
+ let consecutiveEmptyPolls = 0;
58
+ let paused = false;
59
+ const PAUSE_THRESHOLD = 60; // ~5 minutes at 5s interval
57
60
  function writeLineFiltered(line, tag, jsonData) {
58
61
  // Always write to file (stripped of ANSI codes)
59
62
  if (toFile) {
@@ -82,6 +85,8 @@ export function startLogTailer(options) {
82
85
  async function poll() {
83
86
  if (stopped)
84
87
  return;
88
+ const prevStdoutLength = lastStdoutLength;
89
+ const prevStderrLength = lastStderrLength;
85
90
  try {
86
91
  // Poll preview logs (stdout/stderr)
87
92
  const logs = await client.getPreviewLogs(appId);
@@ -116,6 +121,7 @@ export function startLogTailer(options) {
116
121
  catch {
117
122
  // Silently ignore - sandbox may not be ready yet
118
123
  }
124
+ let hadNewEvents = false;
119
125
  try {
120
126
  // Poll app events
121
127
  const eventOptions = { limit: 20 };
@@ -147,6 +153,7 @@ export function startLogTailer(options) {
147
153
  writeLineFiltered(` ${relTime} ${yellow('EVENT ')} ${event.type}: ${detail}`, 'EVENT', {
148
154
  event: 'log', type: 'event', eventType: event.type, detail, metadata: event.metadata || {}, timestamp: event.timestamp,
149
155
  });
156
+ hadNewEvents = true;
150
157
  seenEventIds.add(event.id);
151
158
  }
152
159
  // Cap the set size to prevent unbounded growth (keep last 200 IDs)
@@ -159,12 +166,39 @@ export function startLogTailer(options) {
159
166
  catch {
160
167
  // Silently ignore - events endpoint may not be available
161
168
  }
162
- if (!stopped) {
169
+ const hadNewLogs = lastStdoutLength > prevStdoutLength ||
170
+ lastStderrLength > prevStderrLength;
171
+ if (hadNewLogs || hadNewEvents) {
172
+ consecutiveEmptyPolls = 0;
173
+ }
174
+ else {
175
+ consecutiveEmptyPolls++;
176
+ }
177
+ if (consecutiveEmptyPolls >= PAUSE_THRESHOLD && !paused) {
178
+ paused = true;
179
+ if (toTerminal) {
180
+ console.log(formatLogLine('INFO', dim('No new output for 5 minutes. Polling paused. Press Enter to resume.')));
181
+ }
182
+ }
183
+ if (!stopped && !paused) {
163
184
  timer = setTimeout(poll, intervalMs);
164
185
  }
165
186
  }
166
187
  // Initial poll after a short delay to let sandbox start
167
188
  timer = setTimeout(poll, 2000);
189
+ let stdinHandler;
190
+ if (toTerminal && process.stdin.isTTY) {
191
+ process.stdin.resume();
192
+ stdinHandler = () => {
193
+ if (paused && !stopped) {
194
+ paused = false;
195
+ consecutiveEmptyPolls = 0;
196
+ console.log(formatLogLine('INFO', 'Polling resumed.'));
197
+ timer = setTimeout(poll, 0);
198
+ }
199
+ };
200
+ process.stdin.on('data', stdinHandler);
201
+ }
168
202
  return {
169
203
  stop() {
170
204
  stopped = true;
@@ -172,6 +206,12 @@ export function startLogTailer(options) {
172
206
  clearTimeout(timer);
173
207
  timer = undefined;
174
208
  }
209
+ if (toTerminal && process.stdin.isTTY) {
210
+ if (stdinHandler) {
211
+ process.stdin.off('data', stdinHandler);
212
+ }
213
+ process.stdin.pause();
214
+ }
175
215
  },
176
216
  };
177
217
  }
package/dist/types.d.ts CHANGED
@@ -8,6 +8,8 @@ export interface Credentials {
8
8
  apiKey: string;
9
9
  email: string;
10
10
  baseUrl: string;
11
+ defaultWorkspaceId?: string;
12
+ defaultWorkspaceName?: string;
11
13
  }
12
14
  export interface DevSession {
13
15
  sessionId: string;
@@ -84,3 +86,205 @@ export interface WorkspaceAllData {
84
86
  components: RegistryComponent[];
85
87
  fileStorages: RegistryFileStorage[];
86
88
  }
89
+ export interface WorkspaceSkill {
90
+ id: string;
91
+ name: string;
92
+ description?: string;
93
+ type: 'app' | 'external' | 'mcp';
94
+ appId?: string;
95
+ mcpServerId?: string;
96
+ }
97
+ export interface ExternalSkillRegistration {
98
+ id: string;
99
+ name: string;
100
+ description: string;
101
+ content: string;
102
+ apiEndpoints?: Array<{
103
+ url: string;
104
+ method: string;
105
+ description: string;
106
+ }>;
107
+ importedFrom: string;
108
+ importedAt: number;
109
+ importedBy: string;
110
+ }
111
+ export interface McpServerConfig {
112
+ id: string;
113
+ name: string;
114
+ url: string;
115
+ transport: 'sse' | 'streamable-http';
116
+ enabled: boolean;
117
+ addedAt: number;
118
+ addedBy: string;
119
+ lastConnectedAt?: number;
120
+ toolCount?: number;
121
+ }
122
+ export interface SetupState {
123
+ workspaceId: string;
124
+ workspaceName: string;
125
+ configuredAgents: string[];
126
+ scope: 'project' | 'user' | 'both';
127
+ lastSyncAt: string;
128
+ mcpServers: string[];
129
+ skills: string[];
130
+ }
131
+ export interface WorkflowInfo {
132
+ name: string;
133
+ appId: string;
134
+ appName: string;
135
+ description?: string;
136
+ enabled?: boolean;
137
+ deploymentMode?: 'preview' | 'production';
138
+ }
139
+ export interface TriggerWorkflowResponse {
140
+ instanceId: string;
141
+ status: string;
142
+ }
143
+ export interface WorkflowInstance {
144
+ id: string;
145
+ workflowName: string;
146
+ status: string;
147
+ appId: string;
148
+ createdAt?: string;
149
+ completedAt?: string;
150
+ }
151
+ export interface WorkflowInstancesData {
152
+ instances: WorkflowInstance[];
153
+ }
154
+ export interface WorkflowInstanceDetailData {
155
+ instance: WorkflowInstance & {
156
+ steps?: Array<{
157
+ name: string;
158
+ status: string;
159
+ output?: unknown;
160
+ }>;
161
+ params?: Record<string, unknown>;
162
+ };
163
+ }
164
+ export interface ScheduleInfo {
165
+ name: string;
166
+ appId: string;
167
+ appName: string;
168
+ schedule?: string;
169
+ description?: string;
170
+ deploymentMode?: 'preview' | 'production';
171
+ status?: string;
172
+ nextRun?: string;
173
+ lastRun?: string;
174
+ }
175
+ export interface TriggerScheduleResponse {
176
+ success: boolean;
177
+ message?: string;
178
+ }
179
+ export interface ScheduleHistoryEntry {
180
+ scheduleName: string;
181
+ executedAt: string;
182
+ status: string;
183
+ durationMs?: number;
184
+ error?: string;
185
+ }
186
+ export interface ScheduleHistoryData {
187
+ history: ScheduleHistoryEntry[];
188
+ }
189
+ export interface FileStorageInfo {
190
+ id: string;
191
+ appId: string;
192
+ appName: string;
193
+ workspaceId: string;
194
+ bucketName: string;
195
+ bucketStatus: string;
196
+ objectCount?: number;
197
+ totalSizeBytes?: number;
198
+ connectedApps?: string[];
199
+ deploymentMode?: 'preview' | 'production';
200
+ }
201
+ export interface BucketFile {
202
+ key: string;
203
+ size?: number;
204
+ lastModified?: string;
205
+ etag?: string;
206
+ }
207
+ export interface BucketFolder {
208
+ prefix: string;
209
+ }
210
+ export interface ListBucketFilesData {
211
+ files: BucketFile[];
212
+ folders: BucketFolder[];
213
+ truncated?: boolean;
214
+ cursor?: string;
215
+ }
216
+ export interface WorkspaceEndpoint {
217
+ appId: string;
218
+ appName?: string;
219
+ endpointPath: string;
220
+ method: string;
221
+ auth?: string;
222
+ description?: string;
223
+ tags?: string[];
224
+ schema?: Record<string, unknown>;
225
+ deploymentMode?: 'preview' | 'production';
226
+ }
227
+ export interface ComponentInfo {
228
+ componentName: string;
229
+ appId: string;
230
+ appName: string;
231
+ deploymentMode?: 'preview' | 'production';
232
+ metadata?: {
233
+ tag?: string;
234
+ description?: string;
235
+ attributes?: Record<string, unknown>;
236
+ };
237
+ }
238
+ export interface ApiKeyInfo {
239
+ id: string;
240
+ name: string;
241
+ keyPrefix: string;
242
+ appId?: string;
243
+ allowedEndpoints?: string[];
244
+ scopes?: string[];
245
+ isActive: boolean;
246
+ requestCount?: number;
247
+ lastUsedAt?: string;
248
+ expiresAt?: string;
249
+ createdAt: string;
250
+ }
251
+ export interface ApiKeyCreateResponse {
252
+ id: string;
253
+ name: string;
254
+ key: string;
255
+ keyPrefix: string;
256
+ }
257
+ export interface AgentInfo {
258
+ agentName: string;
259
+ description: string;
260
+ type: 'conversational' | 'task';
261
+ integrations: string[];
262
+ entities: string[];
263
+ appId: string;
264
+ appName: string;
265
+ deploymentMode?: 'preview' | 'production';
266
+ }
267
+ export interface AgentStatusResponse {
268
+ agentName: string;
269
+ status: string;
270
+ [key: string]: unknown;
271
+ }
272
+ export interface AgentExecutionRecord {
273
+ executionId: string;
274
+ sessionId: string;
275
+ appId: string;
276
+ agentName: string;
277
+ prompt: string;
278
+ status: 'running' | 'completed' | 'partial' | 'error' | 'timeout';
279
+ responseSummary?: string;
280
+ outputFileCount?: number;
281
+ usage?: {
282
+ inputTokens: number;
283
+ outputTokens: number;
284
+ cost: number;
285
+ };
286
+ exitCode?: number;
287
+ startedAt: string;
288
+ completedAt?: string;
289
+ durationMs?: number;
290
+ }
package/dist/ui/banner.js CHANGED
@@ -64,19 +64,20 @@ export async function runAgentWizard(directory) {
64
64
  const agent = SUPPORTED_AGENTS.find(a => a.id === result);
65
65
  if (!agent)
66
66
  return;
67
- const cmd = agent.command.replace(/{dir}/g, directory);
67
+ const slug = directory.split('/').pop() || directory;
68
+ const agentCmd = agent.command.replace(/{dir}/g, slug);
68
69
  console.log('');
69
- console.log(bold(`Open your app in ${agent.name}:`));
70
- console.log('');
71
- console.log(` ${cyan(cmd)}`);
70
+ console.log(bold('Get started:'));
72
71
  console.log('');
72
+ console.log(` ${dim('1.')} cd ${slug} && runwork dev`);
73
73
  if (agent.id === 'claude-desktop') {
74
+ console.log(` ${dim('2.')} ${agentCmd}`);
75
+ console.log('');
74
76
  console.log(dim(' Claude Desktop works with project folders. Add your app directory'));
75
77
  console.log(dim(' as a project and Claude will have full context of your Runwork app.'));
76
78
  }
77
79
  else {
78
- console.log(dim(' Run this in a separate terminal while `runwork dev` is running.'));
79
- console.log(dim(' Your AI agent will have full access to your app code and types.'));
80
+ console.log(` ${dim('2.')} ${cyan(agentCmd)} ${dim('(in a second terminal)')}`);
80
81
  }
81
82
  console.log('');
82
83
  }
@@ -0,0 +1,66 @@
1
+ import { ApiClient } from '../api/client.js';
2
+ import type { WorkspaceAllData } from '../types.js';
3
+ export interface AppInfoData {
4
+ app: {
5
+ id: string;
6
+ name: string;
7
+ slug: string;
8
+ };
9
+ workspace: {
10
+ id: string;
11
+ name: string;
12
+ };
13
+ preview: {
14
+ url: string | null;
15
+ active: boolean;
16
+ };
17
+ dashboardUrl: string;
18
+ gitRemoteUrl: string;
19
+ registries: AppRegistries | null;
20
+ }
21
+ export interface AppRegistries {
22
+ entities: Array<{
23
+ name: string;
24
+ deploymentMode?: string;
25
+ }>;
26
+ workflows: Array<{
27
+ name: string;
28
+ description?: string;
29
+ deploymentMode?: string;
30
+ }>;
31
+ scheduledJobs: Array<{
32
+ name: string;
33
+ schedule?: string;
34
+ deploymentMode?: string;
35
+ }>;
36
+ agents: Array<{
37
+ name: string;
38
+ type?: string;
39
+ description?: string;
40
+ deploymentMode?: string;
41
+ }>;
42
+ publicEndpoints: Array<{
43
+ path: string;
44
+ method?: string;
45
+ description?: string;
46
+ deploymentMode?: string;
47
+ }>;
48
+ components: Array<{
49
+ name: string;
50
+ deploymentMode?: string;
51
+ }>;
52
+ fileStorage: boolean;
53
+ }
54
+ /**
55
+ * Fetch app-specific info from the API.
56
+ * Optionally accepts pre-fetched serverData to avoid duplicate getWorkspaceAll calls.
57
+ */
58
+ export declare function fetchAppInfo(client: ApiClient, opts: {
59
+ appId: string;
60
+ appName: string;
61
+ appSlug: string;
62
+ workspaceId: string;
63
+ workspaceName: string;
64
+ baseUrl: string;
65
+ }, serverData?: WorkspaceAllData | null): Promise<AppInfoData>;
66
+ export declare function printAppInfo(data: AppInfoData): void;
@@ -0,0 +1,150 @@
1
+ import { bold, cyan, dim, green, gray } from '../ui/colors.js';
2
+ /**
3
+ * Fetch app-specific info from the API.
4
+ * Optionally accepts pre-fetched serverData to avoid duplicate getWorkspaceAll calls.
5
+ */
6
+ export async function fetchAppInfo(client, opts, serverData) {
7
+ let preview = { url: null, active: false };
8
+ try {
9
+ const status = await client.getDevStatus(opts.appId);
10
+ if (status.previewUrl) {
11
+ preview = { url: status.previewUrl, active: true };
12
+ }
13
+ }
14
+ catch {
15
+ // No active dev session
16
+ }
17
+ let registries = null;
18
+ const data = serverData !== undefined ? serverData : await fetchServerData(client, opts.workspaceId);
19
+ if (data) {
20
+ registries = filterRegistriesToApp(data, opts.appId);
21
+ }
22
+ return {
23
+ app: { id: opts.appId, name: opts.appName, slug: opts.appSlug },
24
+ workspace: { id: opts.workspaceId, name: opts.workspaceName },
25
+ preview,
26
+ dashboardUrl: `${opts.baseUrl}/chat/${opts.appId}`,
27
+ gitRemoteUrl: client.getGitRemoteUrl(opts.workspaceId, opts.appId),
28
+ registries,
29
+ };
30
+ }
31
+ async function fetchServerData(client, workspaceId) {
32
+ try {
33
+ return await client.getWorkspaceAll(workspaceId);
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ function filterRegistriesToApp(data, appId) {
40
+ return {
41
+ entities: data.entities.filter(e => e.appId === appId).map(e => ({
42
+ name: e.entityName, deploymentMode: e.deploymentMode,
43
+ })),
44
+ workflows: data.workflows.filter(w => w.appId === appId).map(w => ({
45
+ name: w.name, description: w.description, deploymentMode: w.deploymentMode,
46
+ })),
47
+ scheduledJobs: data.schedules.filter(s => s.appId === appId).map(s => ({
48
+ name: s.name, schedule: s.schedule, deploymentMode: s.deploymentMode,
49
+ })),
50
+ agents: data.agents.filter(a => a.appId === appId).map(a => ({
51
+ name: a.name, type: a.type, description: a.description, deploymentMode: a.deploymentMode,
52
+ })),
53
+ publicEndpoints: data.endpoints.filter(e => e.appId === appId).map(e => ({
54
+ path: e.path, method: e.method, description: e.description, deploymentMode: e.deploymentMode,
55
+ })),
56
+ components: data.components.filter(c => c.appId === appId).map(c => ({
57
+ name: c.componentName, deploymentMode: c.deploymentMode,
58
+ })),
59
+ fileStorage: data.fileStorages.some(f => f.appId === appId),
60
+ };
61
+ }
62
+ function formatMode(mode) {
63
+ if (mode === 'production')
64
+ return green('prod');
65
+ if (mode === 'preview')
66
+ return dim('preview');
67
+ return '';
68
+ }
69
+ export function printAppInfo(data) {
70
+ const pad = (label) => label.padEnd(14);
71
+ console.log('');
72
+ console.log(bold('App'));
73
+ console.log('');
74
+ console.log(` ${dim(pad('Name:'))}${bold(data.app.name)}`);
75
+ console.log(` ${dim(pad('ID:'))}${data.app.id}`);
76
+ console.log(` ${dim(pad('Workspace:'))}${data.workspace.name || data.workspace.id}`);
77
+ console.log(` ${dim(pad('Dashboard:'))}${cyan(data.dashboardUrl)}`);
78
+ console.log(` ${dim(pad('Git Remote:'))}${data.gitRemoteUrl}`);
79
+ if (data.preview.url) {
80
+ console.log(` ${dim(pad('Preview:'))}${green(data.preview.url)} ${dim('(running)')}`);
81
+ }
82
+ else {
83
+ console.log(` ${dim(pad('Preview:'))}${dim('(not running)')}`);
84
+ }
85
+ const reg = data.registries;
86
+ if (!reg) {
87
+ console.log('');
88
+ return;
89
+ }
90
+ const hasAny = reg.entities.length > 0 || reg.workflows.length > 0 ||
91
+ reg.scheduledJobs.length > 0 || reg.agents.length > 0 ||
92
+ reg.publicEndpoints.length > 0 || reg.components.length > 0 || reg.fileStorage;
93
+ if (!hasAny) {
94
+ console.log('');
95
+ return;
96
+ }
97
+ console.log('');
98
+ console.log(bold('Registries'));
99
+ console.log('');
100
+ if (reg.entities.length > 0) {
101
+ console.log(` ${bold('Entities:')}`);
102
+ for (const e of reg.entities) {
103
+ console.log(` ${e.name.padEnd(24)} ${formatMode(e.deploymentMode)}`);
104
+ }
105
+ console.log('');
106
+ }
107
+ if (reg.workflows.length > 0) {
108
+ console.log(` ${bold('Workflows:')}`);
109
+ for (const w of reg.workflows) {
110
+ const desc = w.description ? ` ${dim(w.description)}` : '';
111
+ console.log(` ${w.name.padEnd(24)} ${formatMode(w.deploymentMode)}${desc}`);
112
+ }
113
+ console.log('');
114
+ }
115
+ if (reg.scheduledJobs.length > 0) {
116
+ console.log(` ${bold('Scheduled Jobs:')}`);
117
+ for (const s of reg.scheduledJobs) {
118
+ const schedule = s.schedule ? ` ${gray(s.schedule)}` : '';
119
+ console.log(` ${s.name.padEnd(24)} ${formatMode(s.deploymentMode)}${schedule}`);
120
+ }
121
+ console.log('');
122
+ }
123
+ if (reg.agents.length > 0) {
124
+ console.log(` ${bold('Agents:')}`);
125
+ for (const a of reg.agents) {
126
+ const type = a.type ? ` ${dim(a.type)}` : '';
127
+ console.log(` ${a.name.padEnd(24)} ${formatMode(a.deploymentMode)}${type}`);
128
+ }
129
+ console.log('');
130
+ }
131
+ if (reg.publicEndpoints.length > 0) {
132
+ console.log(` ${bold('Public Endpoints:')}`);
133
+ for (const e of reg.publicEndpoints) {
134
+ const label = e.method ? `${e.method} ${e.path}` : e.path;
135
+ console.log(` ${label.padEnd(24)} ${formatMode(e.deploymentMode)}`);
136
+ }
137
+ console.log('');
138
+ }
139
+ if (reg.components.length > 0) {
140
+ console.log(` ${bold('Components:')}`);
141
+ for (const c of reg.components) {
142
+ console.log(` ${c.name.padEnd(24)} ${formatMode(c.deploymentMode)}`);
143
+ }
144
+ console.log('');
145
+ }
146
+ if (reg.fileStorage) {
147
+ console.log(` ${bold('File Storage:')} ${green('enabled')}`);
148
+ console.log('');
149
+ }
150
+ }
@@ -0,0 +1,12 @@
1
+ export interface ParsedRequest {
2
+ method: string;
3
+ path: string;
4
+ query?: string;
5
+ headers: Record<string, string>;
6
+ body?: unknown;
7
+ }
8
+ /**
9
+ * Parse a curl command string into a structured request.
10
+ * Uses curlconverter for robust parsing of all curl flag combinations.
11
+ */
12
+ export declare function parseCurlToRequest(curlStr: string): Promise<ParsedRequest>;
@@ -0,0 +1,44 @@
1
+ import { toJsonString } from 'curlconverter';
2
+ /** Headers that should be stripped — the integration proxy handles auth */
3
+ const STRIPPED_HEADERS = new Set(['host', 'authorization', 'cookie', 'connection', 'user-agent']);
4
+ /**
5
+ * Parse a curl command string into a structured request.
6
+ * Uses curlconverter for robust parsing of all curl flag combinations.
7
+ */
8
+ export async function parseCurlToRequest(curlStr) {
9
+ const raw = toJsonString(curlStr);
10
+ const parsed = JSON.parse(raw);
11
+ if (!parsed.url) {
12
+ throw new Error('Could not parse curl command -- no URL found.');
13
+ }
14
+ const method = (parsed.method || 'GET').toUpperCase();
15
+ // Extract path and query from full URL
16
+ const parsedUrl = new URL(parsed.raw_url || parsed.url);
17
+ const path = parsedUrl.pathname;
18
+ const query = parsedUrl.search ? parsedUrl.search.slice(1) : undefined;
19
+ // Copy headers, stripping proxy-managed ones
20
+ const headers = {};
21
+ if (parsed.headers) {
22
+ for (const [key, val] of Object.entries(parsed.headers)) {
23
+ if (!STRIPPED_HEADERS.has(key.toLowerCase())) {
24
+ headers[key] = val;
25
+ }
26
+ }
27
+ }
28
+ // Parse body
29
+ let body;
30
+ if (parsed.data) {
31
+ if (typeof parsed.data === 'string') {
32
+ try {
33
+ body = JSON.parse(parsed.data);
34
+ }
35
+ catch {
36
+ body = parsed.data;
37
+ }
38
+ }
39
+ else {
40
+ body = parsed.data;
41
+ }
42
+ }
43
+ return { method, path, query, headers, body };
44
+ }
@@ -0,0 +1 @@
1
+ export declare function parseDataInput(dataFlag?: string): Promise<Record<string, unknown>>;
@@ -0,0 +1,38 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ export async function parseDataInput(dataFlag) {
3
+ if (dataFlag) {
4
+ if (dataFlag.startsWith('@')) {
5
+ const filePath = dataFlag.slice(1);
6
+ if (!existsSync(filePath)) {
7
+ console.error(`File not found: ${filePath}`);
8
+ process.exit(1);
9
+ }
10
+ const content = readFileSync(filePath, 'utf-8');
11
+ return parseJson(content, filePath);
12
+ }
13
+ return parseJson(dataFlag, '--data');
14
+ }
15
+ if (!process.stdin.isTTY) {
16
+ const chunks = [];
17
+ for await (const chunk of process.stdin) {
18
+ chunks.push(chunk);
19
+ }
20
+ const content = Buffer.concat(chunks).toString('utf-8').trim();
21
+ if (!content) {
22
+ console.error('No data provided via stdin.');
23
+ process.exit(1);
24
+ }
25
+ return parseJson(content, 'stdin');
26
+ }
27
+ console.error('No data provided. Use --data \'{"key": "value"}\', --data @file.json, or pipe JSON via stdin.');
28
+ process.exit(1);
29
+ }
30
+ function parseJson(content, source) {
31
+ try {
32
+ return JSON.parse(content);
33
+ }
34
+ catch {
35
+ console.error(`Invalid JSON from ${source}: ${content.slice(0, 100)}`);
36
+ process.exit(1);
37
+ }
38
+ }