runwork 0.6.0 → 0.6.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.
@@ -62,8 +62,21 @@ describe('generateIntroSkill', () => {
62
62
  mcpServerCount: 3,
63
63
  skillCount: 5,
64
64
  }));
65
- expect(skill.content).toContain('3 MCP servers');
66
- expect(skill.content).toContain('5 skills');
65
+ expect(skill.content).toContain('MCP Servers');
66
+ expect(skill.content).toContain('3 servers configured');
67
+ expect(skill.content).toContain('5 skills configured');
68
+ });
69
+ it('includes MCP and skill names when provided', () => {
70
+ const skill = generateIntroSkill(makeContext({
71
+ mcpServerCount: 2,
72
+ skillCount: 2,
73
+ mcpServerNames: ['Workspace Tools', 'aTars MCP'],
74
+ skillNames: ['Developer Tools', 'Writing Plans'],
75
+ }));
76
+ expect(skill.content).toContain('MCP Servers (2)');
77
+ expect(skill.content).toContain('Workspace Tools');
78
+ expect(skill.content).toContain('Skills (2)');
79
+ expect(skill.content).toContain('Developer Tools');
67
80
  });
68
81
  it('includes CLI commands section', () => {
69
82
  const skill = generateIntroSkill(makeContext());
@@ -1 +1 @@
1
- export declare const VERSION = "0.6.0";
1
+ export declare const VERSION = "0.6.1";
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/embed-types.ts -- do not edit
2
- export const VERSION = "0.6.0";
2
+ export const VERSION = "0.6.1";
@@ -7,6 +7,6 @@ export interface ParsedRequest {
7
7
  }
8
8
  /**
9
9
  * Parse a curl command string into a structured request.
10
- * Uses curlconverter for robust parsing of all curl flag combinations.
10
+ * Pure JS implementation, no native dependencies.
11
11
  */
12
12
  export declare function parseCurlToRequest(curlStr: string): Promise<ParsedRequest>;
@@ -1,44 +1,162 @@
1
- import { toJsonString } from 'curlconverter';
2
- /** Headers that should be stripped — the integration proxy handles auth */
1
+ /** Headers that should be stripped -- the integration proxy handles auth */
3
2
  const STRIPPED_HEADERS = new Set(['host', 'authorization', 'cookie', 'connection', 'user-agent']);
3
+ /**
4
+ * Tokenize a curl command string, respecting single/double quotes and backslash continuations.
5
+ */
6
+ function tokenize(input) {
7
+ // Normalize backslash-newline continuations into a single line
8
+ const normalized = input.replace(/\\\s*\n\s*/g, ' ');
9
+ const tokens = [];
10
+ let i = 0;
11
+ while (i < normalized.length) {
12
+ // Skip whitespace
13
+ if (/\s/.test(normalized[i])) {
14
+ i++;
15
+ continue;
16
+ }
17
+ // Quoted string
18
+ if (normalized[i] === "'" || normalized[i] === '"') {
19
+ const quote = normalized[i];
20
+ let token = '';
21
+ i++; // skip opening quote
22
+ while (i < normalized.length && normalized[i] !== quote) {
23
+ if (normalized[i] === '\\' && quote === '"' && i + 1 < normalized.length) {
24
+ token += normalized[i + 1];
25
+ i += 2;
26
+ }
27
+ else {
28
+ token += normalized[i];
29
+ i++;
30
+ }
31
+ }
32
+ i++; // skip closing quote
33
+ tokens.push(token);
34
+ continue;
35
+ }
36
+ // Unquoted token
37
+ let token = '';
38
+ while (i < normalized.length && !/\s/.test(normalized[i])) {
39
+ token += normalized[i];
40
+ i++;
41
+ }
42
+ tokens.push(token);
43
+ }
44
+ return tokens;
45
+ }
46
+ /** Flags that take no argument (should be skipped, not consumed as URL) */
47
+ const NO_ARG_FLAGS = new Set([
48
+ '--compressed', '-s', '--silent', '-S', '--show-error',
49
+ '-L', '--location', '-v', '--verbose', '-k', '--insecure',
50
+ '-I', '--head', '-i', '--include', '-N', '--no-buffer',
51
+ '--fail', '-f', '--globoff', '-g',
52
+ ]);
53
+ /** Flags that take one argument (flag + value should both be consumed) */
54
+ const ONE_ARG_FLAGS = new Set([
55
+ '-o', '--output', '-u', '--user', '-A', '--user-agent',
56
+ '-e', '--referer', '-b', '--cookie', '-c', '--cookie-jar',
57
+ '--connect-timeout', '-m', '--max-time', '--retry',
58
+ '-w', '--write-out', '--resolve', '--proxy', '-x',
59
+ '--cert', '--key', '--cacert', '-T', '--upload-file',
60
+ ]);
4
61
  /**
5
62
  * Parse a curl command string into a structured request.
6
- * Uses curlconverter for robust parsing of all curl flag combinations.
63
+ * Pure JS implementation, no native dependencies.
7
64
  */
8
65
  export async function parseCurlToRequest(curlStr) {
9
- const raw = toJsonString(curlStr);
10
- const parsed = JSON.parse(raw);
11
- if (!parsed.url) {
66
+ const trimmed = curlStr.trim();
67
+ if (!trimmed) {
68
+ throw new Error('Could not parse curl command -- empty input.');
69
+ }
70
+ const tokens = tokenize(trimmed);
71
+ // Skip leading "curl" token
72
+ let start = 0;
73
+ if (tokens[start] === 'curl')
74
+ start++;
75
+ let url;
76
+ let method;
77
+ const headers = {};
78
+ let dataStr;
79
+ for (let i = start; i < tokens.length; i++) {
80
+ const token = tokens[i];
81
+ if (token === '-X' || token === '--request') {
82
+ method = tokens[++i];
83
+ continue;
84
+ }
85
+ if (token === '-H' || token === '--header') {
86
+ const headerVal = tokens[++i];
87
+ if (headerVal) {
88
+ const colonIdx = headerVal.indexOf(':');
89
+ if (colonIdx > 0) {
90
+ const key = headerVal.slice(0, colonIdx);
91
+ const value = headerVal.slice(colonIdx + 1).trimStart();
92
+ headers[key] = value;
93
+ }
94
+ }
95
+ continue;
96
+ }
97
+ if (token === '-d' || token === '--data' || token === '--data-raw' || token === '--data-binary') {
98
+ dataStr = tokens[++i];
99
+ continue;
100
+ }
101
+ if (NO_ARG_FLAGS.has(token)) {
102
+ continue;
103
+ }
104
+ if (ONE_ARG_FLAGS.has(token)) {
105
+ i++; // skip the argument
106
+ continue;
107
+ }
108
+ // If it looks like a URL (starts with http or has ://) and we don't have one yet
109
+ if (!url && (token.startsWith('http://') || token.startsWith('https://') || token.includes('://'))) {
110
+ url = token;
111
+ continue;
112
+ }
113
+ // If it doesn't start with '-' and we don't have a URL, treat as URL
114
+ if (!url && !token.startsWith('-')) {
115
+ url = token;
116
+ continue;
117
+ }
118
+ }
119
+ if (!url) {
12
120
  throw new Error('Could not parse curl command -- no URL found.');
13
121
  }
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);
122
+ // Infer method: POST if data is present, otherwise GET
123
+ if (!method) {
124
+ method = dataStr !== undefined ? 'POST' : 'GET';
125
+ }
126
+ // Parse URL
127
+ const parsedUrl = new URL(url);
17
128
  const path = parsedUrl.pathname;
18
129
  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
- }
130
+ // Strip proxy-managed headers
131
+ const filteredHeaders = {};
132
+ for (const [key, val] of Object.entries(headers)) {
133
+ if (!STRIPPED_HEADERS.has(key.toLowerCase())) {
134
+ filteredHeaders[key] = val;
26
135
  }
27
136
  }
28
137
  // Parse body
29
138
  let body;
30
- if (parsed.data) {
31
- if (typeof parsed.data === 'string') {
32
- try {
33
- body = JSON.parse(parsed.data);
139
+ if (dataStr !== undefined && dataStr !== '') {
140
+ // Try JSON first
141
+ try {
142
+ body = JSON.parse(dataStr);
143
+ }
144
+ catch {
145
+ // Try form-encoded: key=value&key=value
146
+ if (dataStr.includes('=') && !dataStr.includes('{')) {
147
+ const formData = {};
148
+ for (const pair of dataStr.split('&')) {
149
+ const eqIdx = pair.indexOf('=');
150
+ if (eqIdx > 0) {
151
+ formData[decodeURIComponent(pair.slice(0, eqIdx))] = decodeURIComponent(pair.slice(eqIdx + 1));
152
+ }
153
+ }
154
+ body = formData;
34
155
  }
35
- catch {
36
- body = parsed.data;
156
+ else {
157
+ body = dataStr;
37
158
  }
38
159
  }
39
- else {
40
- body = parsed.data;
41
- }
42
160
  }
43
- return { method, path, query, headers, body };
161
+ return { method: method.toUpperCase(), path, query, headers: filteredHeaders, body };
44
162
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runwork",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "CLI for Runwork: develop, preview, and deploy Runwork apps from your local machine.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Runwork <info@runwork.ai> (https://www.runwork.ai)",
@@ -46,7 +46,6 @@
46
46
  "better-sqlite3": "^12.8.0",
47
47
  "chokidar": "^4.0.0",
48
48
  "commander": "^13.0.0",
49
- "curlconverter": "^4.12.0",
50
49
  "fflate": "^0.8.2",
51
50
  "open": "^10.0.0",
52
51
  "picocolors": "^1.1.1",