@nordsym/apiclaw 1.3.7 → 1.3.8

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 (189) hide show
  1. package/README.md +420 -200
  2. package/convex/_generated/api.d.ts +4 -0
  3. package/convex/agents.ts +403 -0
  4. package/convex/directCall.ts +80 -0
  5. package/convex/earnProgress.ts +753 -0
  6. package/convex/logs.ts +17 -0
  7. package/convex/providerKeys.ts +82 -2
  8. package/convex/schema.ts +71 -2
  9. package/convex/workspaces.ts +84 -2
  10. package/dist/adapters/base.d.ts +112 -0
  11. package/dist/adapters/base.d.ts.map +1 -0
  12. package/dist/adapters/base.js +247 -0
  13. package/dist/adapters/base.js.map +1 -0
  14. package/dist/adapters/claude-desktop.d.ts +12 -0
  15. package/dist/adapters/claude-desktop.d.ts.map +1 -0
  16. package/dist/adapters/claude-desktop.js +36 -0
  17. package/dist/adapters/claude-desktop.js.map +1 -0
  18. package/dist/adapters/cline.d.ts +20 -0
  19. package/dist/adapters/cline.d.ts.map +1 -0
  20. package/dist/adapters/cline.js +77 -0
  21. package/dist/adapters/cline.js.map +1 -0
  22. package/dist/adapters/continue.d.ts +26 -0
  23. package/dist/adapters/continue.d.ts.map +1 -0
  24. package/dist/adapters/continue.js +68 -0
  25. package/dist/adapters/continue.js.map +1 -0
  26. package/dist/adapters/cursor.d.ts +12 -0
  27. package/dist/adapters/cursor.d.ts.map +1 -0
  28. package/dist/adapters/cursor.js +38 -0
  29. package/dist/adapters/cursor.js.map +1 -0
  30. package/dist/adapters/custom.d.ts +47 -0
  31. package/dist/adapters/custom.d.ts.map +1 -0
  32. package/dist/adapters/custom.js +146 -0
  33. package/dist/adapters/custom.js.map +1 -0
  34. package/dist/adapters/detect.d.ts +69 -0
  35. package/dist/adapters/detect.d.ts.map +1 -0
  36. package/dist/adapters/detect.js +158 -0
  37. package/dist/adapters/detect.js.map +1 -0
  38. package/dist/adapters/index.d.ts +21 -0
  39. package/dist/adapters/index.d.ts.map +1 -0
  40. package/dist/adapters/index.js +23 -0
  41. package/dist/adapters/index.js.map +1 -0
  42. package/dist/adapters/windsurf.d.ts +12 -0
  43. package/dist/adapters/windsurf.d.ts.map +1 -0
  44. package/dist/adapters/windsurf.js +39 -0
  45. package/dist/adapters/windsurf.js.map +1 -0
  46. package/dist/bin.d.ts +9 -0
  47. package/dist/bin.d.ts.map +1 -0
  48. package/dist/bin.js +19 -0
  49. package/dist/bin.js.map +1 -0
  50. package/dist/cli/commands/doctor.d.ts +34 -0
  51. package/dist/cli/commands/doctor.d.ts.map +1 -0
  52. package/dist/cli/commands/doctor.js +312 -0
  53. package/dist/cli/commands/doctor.js.map +1 -0
  54. package/dist/cli/commands/index.d.ts +9 -0
  55. package/dist/cli/commands/index.d.ts.map +1 -0
  56. package/dist/cli/commands/index.js +9 -0
  57. package/dist/cli/commands/index.js.map +1 -0
  58. package/dist/cli/commands/restore.d.ts +50 -0
  59. package/dist/cli/commands/restore.d.ts.map +1 -0
  60. package/dist/cli/commands/restore.js +260 -0
  61. package/dist/cli/commands/restore.js.map +1 -0
  62. package/dist/cli/commands/setup.d.ts +19 -0
  63. package/dist/cli/commands/setup.d.ts.map +1 -0
  64. package/dist/cli/commands/setup.js +206 -0
  65. package/dist/cli/commands/setup.js.map +1 -0
  66. package/dist/cli/commands/uninstall.d.ts +37 -0
  67. package/dist/cli/commands/uninstall.d.ts.map +1 -0
  68. package/dist/cli/commands/uninstall.js +189 -0
  69. package/dist/cli/commands/uninstall.js.map +1 -0
  70. package/dist/cli/index.d.ts +7 -0
  71. package/dist/cli/index.d.ts.map +1 -0
  72. package/dist/cli/index.js +97 -0
  73. package/dist/cli/index.js.map +1 -0
  74. package/dist/discovery.d.ts +6 -2
  75. package/dist/discovery.d.ts.map +1 -1
  76. package/dist/discovery.js +296 -2
  77. package/dist/discovery.js.map +1 -1
  78. package/dist/enterprise/env.d.ts +56 -0
  79. package/dist/enterprise/env.d.ts.map +1 -0
  80. package/dist/enterprise/env.js +124 -0
  81. package/dist/enterprise/env.js.map +1 -0
  82. package/dist/enterprise/index.d.ts +7 -0
  83. package/dist/enterprise/index.d.ts.map +1 -0
  84. package/dist/enterprise/index.js +7 -0
  85. package/dist/enterprise/index.js.map +1 -0
  86. package/dist/enterprise/script-generator.d.ts +32 -0
  87. package/dist/enterprise/script-generator.d.ts.map +1 -0
  88. package/dist/enterprise/script-generator.js +461 -0
  89. package/dist/enterprise/script-generator.js.map +1 -0
  90. package/dist/execute.d.ts +21 -0
  91. package/dist/execute.d.ts.map +1 -1
  92. package/dist/execute.js +231 -0
  93. package/dist/execute.js.map +1 -1
  94. package/dist/index.js +79 -7
  95. package/dist/index.js.map +1 -1
  96. package/dist/stripe.d.ts +1 -1
  97. package/dist/stripe.js +1 -1
  98. package/dist/stripe.js.map +1 -1
  99. package/dist/types.d.ts +29 -0
  100. package/dist/types.d.ts.map +1 -1
  101. package/dist/ui/colors.d.ts +111 -0
  102. package/dist/ui/colors.d.ts.map +1 -0
  103. package/dist/ui/colors.js +185 -0
  104. package/dist/ui/colors.js.map +1 -0
  105. package/dist/ui/errors.d.ts +69 -0
  106. package/dist/ui/errors.d.ts.map +1 -0
  107. package/dist/ui/errors.js +334 -0
  108. package/dist/ui/errors.js.map +1 -0
  109. package/dist/ui/index.d.ts +10 -0
  110. package/dist/ui/index.d.ts.map +1 -0
  111. package/dist/ui/index.js +14 -0
  112. package/dist/ui/index.js.map +1 -0
  113. package/dist/ui/prompts.d.ts +88 -0
  114. package/dist/ui/prompts.d.ts.map +1 -0
  115. package/dist/ui/prompts.js +295 -0
  116. package/dist/ui/prompts.js.map +1 -0
  117. package/dist/ui/spinner.d.ts +112 -0
  118. package/dist/ui/spinner.d.ts.map +1 -0
  119. package/dist/ui/spinner.js +229 -0
  120. package/dist/ui/spinner.js.map +1 -0
  121. package/dist/utils/backup.d.ts +48 -0
  122. package/dist/utils/backup.d.ts.map +1 -0
  123. package/dist/utils/backup.js +182 -0
  124. package/dist/utils/backup.js.map +1 -0
  125. package/dist/utils/config.d.ts +80 -0
  126. package/dist/utils/config.d.ts.map +1 -0
  127. package/dist/utils/config.js +221 -0
  128. package/dist/utils/config.js.map +1 -0
  129. package/dist/utils/os.d.ts +45 -0
  130. package/dist/utils/os.d.ts.map +1 -0
  131. package/dist/utils/os.js +106 -0
  132. package/dist/utils/os.js.map +1 -0
  133. package/dist/utils/paths.d.ts +38 -0
  134. package/dist/utils/paths.d.ts.map +1 -0
  135. package/dist/utils/paths.js +160 -0
  136. package/dist/utils/paths.js.map +1 -0
  137. package/docs/PRD-BILLING.md +226 -0
  138. package/docs/PRD-EARN-SYSTEM.md +261 -0
  139. package/docs/PRD-MCP-AUTO-SETUP.md +623 -0
  140. package/docs/enterprise-deployment.md +728 -0
  141. package/landing/next.config.mjs +14 -0
  142. package/landing/public/stats.json +4 -2
  143. package/landing/scripts/generate-stats.js +12 -0
  144. package/landing/src/app/api/workspace-auth/magic-link/route.ts +6 -3
  145. package/landing/src/app/auth/verify/page.tsx +11 -4
  146. package/landing/src/app/docs/page.tsx +1 -1
  147. package/landing/src/app/join/page.tsx +49 -0
  148. package/landing/src/app/login/page.tsx +7 -1
  149. package/landing/src/app/page.tsx +13 -28
  150. package/landing/src/app/providers/register/page.tsx +1 -1
  151. package/landing/src/app/workspace/page.tsx +483 -710
  152. package/landing/src/components/CheckoutButton.tsx +1 -1
  153. package/landing/src/components/EarnCreditsTab.tsx +842 -0
  154. package/landing/src/lib/stats.json +3 -1
  155. package/package.json +9 -2
  156. package/src/adapters/base.ts +363 -0
  157. package/src/adapters/claude-desktop.ts +41 -0
  158. package/src/adapters/cline.ts +88 -0
  159. package/src/adapters/continue.ts +91 -0
  160. package/src/adapters/cursor.ts +43 -0
  161. package/src/adapters/custom.ts +188 -0
  162. package/src/adapters/detect.ts +202 -0
  163. package/src/adapters/index.ts +47 -0
  164. package/src/adapters/windsurf.ts +44 -0
  165. package/src/bin.ts +19 -0
  166. package/src/cli/commands/doctor.ts +367 -0
  167. package/src/cli/commands/index.ts +9 -0
  168. package/src/cli/commands/restore.ts +333 -0
  169. package/src/cli/commands/setup.ts +276 -0
  170. package/src/cli/commands/uninstall.ts +240 -0
  171. package/src/cli/index.ts +107 -0
  172. package/src/discovery.ts +328 -3
  173. package/src/enterprise/env.ts +156 -0
  174. package/src/enterprise/index.ts +7 -0
  175. package/src/enterprise/script-generator.ts +481 -0
  176. package/src/execute.ts +256 -0
  177. package/src/index.ts +85 -7
  178. package/src/stripe.ts +1 -1
  179. package/src/types.ts +32 -0
  180. package/src/ui/colors.ts +219 -0
  181. package/src/ui/errors.ts +394 -0
  182. package/src/ui/index.ts +17 -0
  183. package/src/ui/prompts.ts +390 -0
  184. package/src/ui/spinner.ts +325 -0
  185. package/src/utils/backup.ts +224 -0
  186. package/src/utils/config.ts +315 -0
  187. package/src/utils/os.ts +124 -0
  188. package/src/utils/paths.ts +203 -0
  189. package/landing/tsconfig.tsbuildinfo +0 -1
package/src/discovery.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  // Discovery engine for APIvault
2
2
  // MVP: Keyword matching. Future: Embeddings + semantic search
3
3
 
4
- import { APIProvider, SearchResult } from './types.js';
4
+ import { APIProvider, SearchResult, APIDetailsResponse } from './types.js';
5
5
  import { readFileSync } from 'fs';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { dirname, join } from 'path';
8
+ import { getConnectedProviders } from './execute.js';
9
+ import { openAPIs, isOpenAPI } from './open-apis.js';
8
10
 
9
11
  const __filename = fileURLToPath(import.meta.url);
10
12
  const __dirname = dirname(__filename);
@@ -14,6 +16,218 @@ const apisData = JSON.parse(
14
16
  );
15
17
  const apis: APIProvider[] = apisData.apis;
16
18
 
19
+ // Direct Call provider specs (hardcoded handlers with params)
20
+ const DIRECT_CALL_SPECS: Record<string, {
21
+ description: string;
22
+ auth: string;
23
+ docs: string;
24
+ actions: Record<string, { params: { name: string; required: boolean; desc: string }[]; desc: string }>;
25
+ }> = {
26
+ '46elks': {
27
+ description: 'Swedish SMS and voice API',
28
+ auth: 'basic',
29
+ docs: 'https://46elks.com/docs',
30
+ actions: {
31
+ send_sms: {
32
+ desc: 'Send SMS message',
33
+ params: [
34
+ { name: 'to', required: true, desc: 'Phone number (+46...)' },
35
+ { name: 'message', required: true, desc: 'SMS text (max 160 chars for 1 segment)' },
36
+ { name: 'from', required: false, desc: 'Sender ID (default: APIClaw)' },
37
+ ],
38
+ },
39
+ },
40
+ },
41
+ twilio: {
42
+ description: 'Global SMS and voice API',
43
+ auth: 'basic',
44
+ docs: 'https://www.twilio.com/docs',
45
+ actions: {
46
+ send_sms: {
47
+ desc: 'Send SMS message',
48
+ params: [
49
+ { name: 'to', required: true, desc: 'Phone number (E.164 format)' },
50
+ { name: 'message', required: true, desc: 'SMS text' },
51
+ { name: 'from', required: false, desc: 'Sender phone number' },
52
+ ],
53
+ },
54
+ },
55
+ },
56
+ brave_search: {
57
+ description: 'Web search API',
58
+ auth: 'api_key',
59
+ docs: 'https://api.search.brave.com/docs',
60
+ actions: {
61
+ search: {
62
+ desc: 'Search the web',
63
+ params: [
64
+ { name: 'query', required: true, desc: 'Search query' },
65
+ { name: 'count', required: false, desc: 'Number of results (default: 5)' },
66
+ ],
67
+ },
68
+ },
69
+ },
70
+ resend: {
71
+ description: 'Email API',
72
+ auth: 'bearer',
73
+ docs: 'https://resend.com/docs',
74
+ actions: {
75
+ send_email: {
76
+ desc: 'Send email',
77
+ params: [
78
+ { name: 'to', required: true, desc: 'Recipient email' },
79
+ { name: 'subject', required: true, desc: 'Email subject' },
80
+ { name: 'html', required: false, desc: 'HTML body' },
81
+ { name: 'text', required: false, desc: 'Plain text body' },
82
+ { name: 'from', required: false, desc: 'Sender (default: noreply@apiclaw.nordsym.com)' },
83
+ ],
84
+ },
85
+ },
86
+ },
87
+ openrouter: {
88
+ description: 'LLM routing (100+ models)',
89
+ auth: 'bearer',
90
+ docs: 'https://openrouter.ai/docs',
91
+ actions: {
92
+ chat: {
93
+ desc: 'Chat completion',
94
+ params: [
95
+ { name: 'messages', required: true, desc: 'Array of {role, content}' },
96
+ { name: 'model', required: false, desc: 'Model ID (default: claude-3-haiku)' },
97
+ { name: 'max_tokens', required: false, desc: 'Max response tokens (default: 1000)' },
98
+ ],
99
+ },
100
+ },
101
+ },
102
+ elevenlabs: {
103
+ description: 'Text-to-speech',
104
+ auth: 'api_key',
105
+ docs: 'https://elevenlabs.io/docs',
106
+ actions: {
107
+ text_to_speech: {
108
+ desc: 'Generate audio from text',
109
+ params: [
110
+ { name: 'text', required: true, desc: 'Text to speak' },
111
+ { name: 'voice_id', required: false, desc: 'Voice ID (default: Rachel)' },
112
+ { name: 'model_id', required: false, desc: 'Model ID' },
113
+ ],
114
+ },
115
+ },
116
+ },
117
+ replicate: {
118
+ description: 'Run any AI model (images, video, audio)',
119
+ auth: 'bearer',
120
+ docs: 'https://replicate.com/docs',
121
+ actions: {
122
+ run: {
123
+ desc: 'Run a model',
124
+ params: [
125
+ { name: 'model', required: true, desc: 'Model ID (e.g., stability-ai/sdxl:...)' },
126
+ { name: 'input', required: true, desc: 'Model input parameters' },
127
+ ],
128
+ },
129
+ list_models: {
130
+ desc: 'List available models',
131
+ params: [],
132
+ },
133
+ },
134
+ },
135
+ firecrawl: {
136
+ description: 'Web scraping and crawling',
137
+ auth: 'bearer',
138
+ docs: 'https://firecrawl.dev/docs',
139
+ actions: {
140
+ scrape: {
141
+ desc: 'Scrape a URL',
142
+ params: [
143
+ { name: 'url', required: true, desc: 'URL to scrape' },
144
+ { name: 'formats', required: false, desc: 'Output formats (default: ["markdown"])' },
145
+ ],
146
+ },
147
+ crawl: {
148
+ desc: 'Start a crawl job',
149
+ params: [
150
+ { name: 'url', required: true, desc: 'Starting URL' },
151
+ { name: 'limit', required: false, desc: 'Max pages (default: 10)' },
152
+ ],
153
+ },
154
+ map: {
155
+ desc: 'Map site structure',
156
+ params: [
157
+ { name: 'url', required: true, desc: 'URL to map' },
158
+ ],
159
+ },
160
+ },
161
+ },
162
+ github: {
163
+ description: 'Code repos and developer data',
164
+ auth: 'bearer',
165
+ docs: 'https://docs.github.com/rest',
166
+ actions: {
167
+ search_repos: {
168
+ desc: 'Search repositories',
169
+ params: [
170
+ { name: 'query', required: true, desc: 'Search query' },
171
+ { name: 'sort', required: false, desc: 'Sort by (default: stars)' },
172
+ { name: 'limit', required: false, desc: 'Max results (default: 10)' },
173
+ ],
174
+ },
175
+ get_repo: {
176
+ desc: 'Get repo details',
177
+ params: [
178
+ { name: 'owner', required: true, desc: 'Repo owner' },
179
+ { name: 'repo', required: true, desc: 'Repo name' },
180
+ ],
181
+ },
182
+ list_issues: {
183
+ desc: 'List issues',
184
+ params: [
185
+ { name: 'owner', required: true, desc: 'Repo owner' },
186
+ { name: 'repo', required: true, desc: 'Repo name' },
187
+ { name: 'state', required: false, desc: 'State filter (default: open)' },
188
+ ],
189
+ },
190
+ create_issue: {
191
+ desc: 'Create issue',
192
+ params: [
193
+ { name: 'owner', required: true, desc: 'Repo owner' },
194
+ { name: 'repo', required: true, desc: 'Repo name' },
195
+ { name: 'title', required: true, desc: 'Issue title' },
196
+ { name: 'body', required: false, desc: 'Issue body' },
197
+ ],
198
+ },
199
+ get_file: {
200
+ desc: 'Get file contents',
201
+ params: [
202
+ { name: 'owner', required: true, desc: 'Repo owner' },
203
+ { name: 'repo', required: true, desc: 'Repo name' },
204
+ { name: 'path', required: true, desc: 'File path' },
205
+ ],
206
+ },
207
+ },
208
+ },
209
+ e2b: {
210
+ description: 'Code sandbox for AI agents',
211
+ auth: 'api_key',
212
+ docs: 'https://e2b.dev/docs',
213
+ actions: {
214
+ run_code: {
215
+ desc: 'Execute code in sandbox',
216
+ params: [
217
+ { name: 'code', required: true, desc: 'Code to run' },
218
+ { name: 'language', required: false, desc: 'Language (default: python)' },
219
+ ],
220
+ },
221
+ run_shell: {
222
+ desc: 'Execute shell command',
223
+ params: [
224
+ { name: 'command', required: true, desc: 'Shell command' },
225
+ ],
226
+ },
227
+ },
228
+ },
229
+ };
230
+
17
231
  /**
18
232
  * Discover APIs based on a natural language query
19
233
  * MVP uses keyword matching; production would use embeddings
@@ -107,9 +321,120 @@ export function discoverAPIs(
107
321
 
108
322
  /**
109
323
  * Get detailed information about a specific API
324
+ * @param apiId - The API provider ID
325
+ * @param options.compact - If true, returns minified spec (saves ~60% tokens)
110
326
  */
111
- export function getAPIDetails(apiId: string): APIProvider | null {
112
- return apis.find(api => api.id === apiId) || null;
327
+ export function getAPIDetails(
328
+ apiId: string,
329
+ options: { compact?: boolean } = {}
330
+ ): APIDetailsResponse | null {
331
+ const { compact = false } = options;
332
+
333
+ // Check if it's a Direct Call provider (hardcoded handlers)
334
+ const directSpec = DIRECT_CALL_SPECS[apiId];
335
+ if (directSpec) {
336
+ if (compact) {
337
+ // Minified format: ~60% smaller
338
+ return {
339
+ id: apiId,
340
+ type: 'direct_call',
341
+ desc: directSpec.description,
342
+ auth: directSpec.auth,
343
+ actions: Object.fromEntries(
344
+ Object.entries(directSpec.actions).map(([action, info]) => [
345
+ action,
346
+ {
347
+ params: info.params.map(p =>
348
+ p.required ? p.name : `${p.name}?`
349
+ ),
350
+ },
351
+ ])
352
+ ),
353
+ } as APIDetailsResponse;
354
+ }
355
+
356
+ return {
357
+ id: apiId,
358
+ type: 'direct_call',
359
+ name: apiId,
360
+ description: directSpec.description,
361
+ auth_type: directSpec.auth,
362
+ docs_url: directSpec.docs,
363
+ direct_call: true,
364
+ actions: Object.fromEntries(
365
+ Object.entries(directSpec.actions).map(([action, info]) => [
366
+ action,
367
+ {
368
+ description: info.desc,
369
+ params: info.params,
370
+ },
371
+ ])
372
+ ),
373
+ } as APIDetailsResponse;
374
+ }
375
+
376
+ // Check if it's an Open API (free, no auth)
377
+ if (isOpenAPI(apiId)) {
378
+ const openApi = openAPIs[apiId];
379
+ const actions = Object.keys(openApi.actions);
380
+
381
+ if (compact) {
382
+ return {
383
+ id: apiId,
384
+ type: 'open',
385
+ desc: openApi.description,
386
+ auth: 'none',
387
+ actions: Object.fromEntries(
388
+ actions.map(a => [a, { params: [] }])
389
+ ),
390
+ } as APIDetailsResponse;
391
+ }
392
+
393
+ return {
394
+ id: apiId,
395
+ type: 'open',
396
+ name: openApi.name,
397
+ description: openApi.description,
398
+ auth_type: 'none',
399
+ free: true,
400
+ actions: Object.fromEntries(
401
+ actions.map(a => [a, { description: `Execute ${a}`, params: [] }])
402
+ ),
403
+ } as APIDetailsResponse;
404
+ }
405
+
406
+ // Fall back to registry (19,000+ APIs - basic info only)
407
+ const registryApi = apis.find(api =>
408
+ api.id === apiId ||
409
+ api.name?.toLowerCase() === apiId.toLowerCase()
410
+ );
411
+
412
+ if (!registryApi) {
413
+ return null;
414
+ }
415
+
416
+ if (compact) {
417
+ return {
418
+ id: registryApi.id || registryApi.name,
419
+ type: 'registry',
420
+ desc: registryApi.description?.slice(0, 80),
421
+ auth: registryApi.auth_type || (registryApi as any).auth || 'unknown',
422
+ url: registryApi.base_url || (registryApi as any).baseUrl,
423
+ } as APIDetailsResponse;
424
+ }
425
+
426
+ return {
427
+ id: registryApi.id || registryApi.name,
428
+ type: 'registry',
429
+ name: registryApi.name,
430
+ description: registryApi.description,
431
+ category: registryApi.category,
432
+ auth_type: registryApi.auth_type || (registryApi as any).auth,
433
+ base_url: registryApi.base_url || (registryApi as any).baseUrl,
434
+ docs_url: registryApi.docs_url || (registryApi as any).docsUrl,
435
+ pricing: registryApi.pricing || (registryApi as any).pricing,
436
+ note: 'Registry API - use call_api with customer_key or check docs for integration',
437
+ } as APIDetailsResponse;
113
438
  }
114
439
 
115
440
  /**
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Environment Variable Handler
3
+ * Manages APIClaw environment variables for configuration
4
+ */
5
+
6
+ export interface ApiclawEnvConfig {
7
+ workspace?: string;
8
+ apiUrl?: string;
9
+ disableTelemetry?: boolean;
10
+ }
11
+
12
+ export const ENV_VARS = {
13
+ WORKSPACE: 'APICLAW_WORKSPACE',
14
+ API_URL: 'APICLAW_API_URL',
15
+ DISABLE_TELEMETRY: 'APICLAW_DISABLE_TELEMETRY',
16
+ } as const;
17
+
18
+ export const DEFAULT_API_URL = 'https://api.apiclaw.com';
19
+
20
+ /**
21
+ * Read APIClaw config from environment variables
22
+ */
23
+ export function readEnvConfig(): ApiclawEnvConfig {
24
+ return {
25
+ workspace: process.env[ENV_VARS.WORKSPACE] || undefined,
26
+ apiUrl: process.env[ENV_VARS.API_URL] || undefined,
27
+ disableTelemetry: process.env[ENV_VARS.DISABLE_TELEMETRY] === 'true' ||
28
+ process.env[ENV_VARS.DISABLE_TELEMETRY] === '1',
29
+ };
30
+ }
31
+
32
+ /**
33
+ * Get API URL with fallback to default
34
+ */
35
+ export function getApiUrl(): string {
36
+ return process.env[ENV_VARS.API_URL] || DEFAULT_API_URL;
37
+ }
38
+
39
+ /**
40
+ * Check if telemetry is disabled
41
+ */
42
+ export function isTelemetryDisabled(): boolean {
43
+ const val = process.env[ENV_VARS.DISABLE_TELEMETRY];
44
+ return val === 'true' || val === '1';
45
+ }
46
+
47
+ /**
48
+ * Get pre-configured workspace ID
49
+ */
50
+ export function getWorkspaceFromEnv(): string | undefined {
51
+ return process.env[ENV_VARS.WORKSPACE];
52
+ }
53
+
54
+ /**
55
+ * Generate env block for MCP server config
56
+ */
57
+ export function generateEnvBlock(config: ApiclawEnvConfig): Record<string, string> {
58
+ const env: Record<string, string> = {};
59
+
60
+ if (config.workspace) {
61
+ env[ENV_VARS.WORKSPACE] = config.workspace;
62
+ }
63
+
64
+ if (config.apiUrl && config.apiUrl !== DEFAULT_API_URL) {
65
+ env[ENV_VARS.API_URL] = config.apiUrl;
66
+ }
67
+
68
+ if (config.disableTelemetry) {
69
+ env[ENV_VARS.DISABLE_TELEMETRY] = 'true';
70
+ }
71
+
72
+ return env;
73
+ }
74
+
75
+ /**
76
+ * Format env config for display
77
+ */
78
+ export function formatEnvConfig(config: ApiclawEnvConfig): string[] {
79
+ const lines: string[] = [];
80
+
81
+ if (config.workspace) {
82
+ lines.push(` Workspace: ${config.workspace}`);
83
+ }
84
+
85
+ if (config.apiUrl) {
86
+ lines.push(` API URL: ${config.apiUrl}`);
87
+ }
88
+
89
+ if (config.disableTelemetry) {
90
+ lines.push(` Telemetry: Disabled`);
91
+ }
92
+
93
+ return lines;
94
+ }
95
+
96
+ /**
97
+ * Validate workspace ID format
98
+ */
99
+ export function isValidWorkspaceId(id: string): boolean {
100
+ // Workspace IDs should be alphanumeric with dashes/underscores
101
+ return /^[a-zA-Z0-9_-]{3,64}$/.test(id);
102
+ }
103
+
104
+ /**
105
+ * Validate API URL format
106
+ */
107
+ export function isValidApiUrl(url: string): boolean {
108
+ try {
109
+ const parsed = new URL(url);
110
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
111
+ } catch {
112
+ return false;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Generate shell export statements
118
+ */
119
+ export function generateShellExports(config: ApiclawEnvConfig): string {
120
+ const lines: string[] = [];
121
+
122
+ if (config.workspace) {
123
+ lines.push(`export ${ENV_VARS.WORKSPACE}="${config.workspace}"`);
124
+ }
125
+
126
+ if (config.apiUrl) {
127
+ lines.push(`export ${ENV_VARS.API_URL}="${config.apiUrl}"`);
128
+ }
129
+
130
+ if (config.disableTelemetry) {
131
+ lines.push(`export ${ENV_VARS.DISABLE_TELEMETRY}="true"`);
132
+ }
133
+
134
+ return lines.join('\n');
135
+ }
136
+
137
+ /**
138
+ * Generate PowerShell $env statements
139
+ */
140
+ export function generatePowerShellEnv(config: ApiclawEnvConfig): string {
141
+ const lines: string[] = [];
142
+
143
+ if (config.workspace) {
144
+ lines.push(`$env:${ENV_VARS.WORKSPACE} = "${config.workspace}"`);
145
+ }
146
+
147
+ if (config.apiUrl) {
148
+ lines.push(`$env:${ENV_VARS.API_URL} = "${config.apiUrl}"`);
149
+ }
150
+
151
+ if (config.disableTelemetry) {
152
+ lines.push(`$env:${ENV_VARS.DISABLE_TELEMETRY} = "true"`);
153
+ }
154
+
155
+ return lines.join('\n');
156
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Enterprise Module
3
+ * Exports for enterprise features: script generation, env handling
4
+ */
5
+
6
+ export * from './env.js';
7
+ export * from './script-generator.js';