@nordsym/apiclaw 1.5.9 → 1.5.11
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.
- package/APILAYER_STATUS_2026-03-24.md +38 -0
- package/CHANGELOG-WHITELIST-V2.md +269 -0
- package/HIVR-INTEGRATION.md +281 -0
- package/HIVR-WHITELIST-STATUS.md +205 -0
- package/HIVR-WHITELIST.md +148 -0
- package/TERMINOLOGY-AUDIT.md +99 -0
- package/TERMINOLOGY-FIXED.md +74 -0
- package/VIDEO-DEMO-GUIDE.md +82 -0
- package/WHITELIST-ARCHITECTURE.md +379 -0
- package/api/discover.ts +71 -0
- package/api/health.ts +20 -0
- package/convex/http.d.ts.map +1 -1
- package/convex/http.js +8 -0
- package/convex/http.js.map +1 -1
- package/convex/http.ts +8 -0
- package/direct-test.mjs +51 -0
- package/dist/access-control.d.ts +45 -0
- package/dist/access-control.d.ts.map +1 -0
- package/dist/access-control.js +142 -0
- package/dist/access-control.js.map +1 -0
- package/dist/analytics.d.ts +4 -0
- package/dist/analytics.d.ts.map +1 -1
- package/dist/analytics.js +1 -0
- package/dist/analytics.js.map +1 -1
- package/dist/cli/commands/mcp-install.d.ts.map +1 -1
- package/dist/cli/commands/mcp-install.js +55 -40
- package/dist/cli/commands/mcp-install.js.map +1 -1
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +148 -0
- package/dist/credentials.js.map +1 -1
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +191 -82
- package/dist/discovery.js.map +1 -1
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +274 -0
- package/dist/execute.js.map +1 -1
- package/dist/hivr-whitelist.d.ts +18 -0
- package/dist/hivr-whitelist.d.ts.map +1 -0
- package/dist/hivr-whitelist.js +95 -0
- package/dist/hivr-whitelist.js.map +1 -0
- package/dist/http-api.d.ts.map +1 -1
- package/dist/http-api.js +17 -33
- package/dist/http-api.js.map +1 -1
- package/dist/http-server-minimal.d.ts +7 -0
- package/dist/http-server-minimal.d.ts.map +1 -0
- package/dist/http-server-minimal.js +126 -0
- package/dist/http-server-minimal.js.map +1 -0
- package/dist/product-whitelist.d.ts +37 -0
- package/dist/product-whitelist.d.ts.map +1 -0
- package/dist/product-whitelist.js +203 -0
- package/dist/product-whitelist.js.map +1 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +1 -1
- package/dist/proxy.js.map +1 -1
- package/email-templates/README.md +104 -0
- package/email-templates/partnership-template.html +116 -0
- package/landing/next-env.d.ts +1 -0
- package/landing/pages/api/discover.ts +43 -0
- package/landing/pages/api/health.ts +20 -0
- package/landing/src/app/api/auth/magic-link/route.ts +1 -1
- package/landing/src/app/auth/verify/page.tsx +6 -0
- package/landing/src/app/dashboard/verify/page.tsx +6 -0
- package/landing/src/app/join/page.tsx +6 -0
- package/landing/src/app/layout.tsx +2 -2
- package/landing/src/app/login/page.tsx +1 -1
- package/landing/src/app/mou/[partnerId]/page.tsx +6 -0
- package/landing/src/app/page.tsx +39 -18
- package/landing/src/app/providers/dashboard/[apiId]/actions/[actionId]/edit/page.tsx +6 -0
- package/landing/src/app/providers/dashboard/[apiId]/actions/new/page.tsx +5 -0
- package/landing/src/app/providers/dashboard/[apiId]/actions/page.tsx +5 -0
- package/landing/src/app/providers/dashboard/[apiId]/direct-call/page.tsx +6 -1
- package/landing/src/app/providers/dashboard/[apiId]/page.tsx +5 -0
- package/landing/src/app/providers/dashboard/[apiId]/test/page.tsx +5 -0
- package/landing/src/app/providers/dashboard/layout.tsx +6 -6
- package/landing/src/app/providers/dashboard/login/page.tsx +1 -1
- package/landing/src/app/providers/dashboard/page.tsx +1 -1
- package/landing/src/app/providers/dashboard/verify/page.tsx +6 -0
- package/landing/src/app/providers/layout.tsx +1 -1
- package/landing/src/app/upgrade/page.tsx +6 -0
- package/landing/src/app/workspace/page.tsx +6 -0
- package/landing/src/components/HeroTabs.tsx +2 -2
- package/landing/src/components/VideoDemo.tsx +94 -0
- package/landing/src/components/{ProviderDashboard.tsx → Workspace.tsx} +2 -2
- package/landing/src/lib/mock-data.ts +1 -1
- package/landing/src/lib/stats.json +1 -1
- package/package.json +4 -2
- package/scripts/test-whitelist-v2.sh +128 -0
- package/src/access-control.ts +174 -0
- package/src/analytics.ts +5 -0
- package/src/cli/commands/mcp-install.ts +14 -4
- package/src/credentials.ts +156 -0
- package/src/discovery.ts +191 -82
- package/src/execute.ts +274 -0
- package/src/hivr-whitelist.ts +110 -0
- package/src/http-api.ts +18 -34
- package/src/http-server-minimal.ts +154 -0
- package/src/product-whitelist.ts +246 -0
- package/src/proxy.ts +1 -1
- package/test-actual-handlers.ts +92 -0
- package/test-apilayer-all-14.ts +249 -0
- package/test-apilayer-fixed.ts +248 -0
- package/test-direct-endpoints.ts +174 -0
- package/test-exact-endpoints.ts +144 -0
- package/test-final.ts +83 -0
- package/test-full-routing.ts +100 -0
- package/test-handlers-correct.ts +217 -0
- package/test-numverify-key.ts +41 -0
- package/test-via-handlers.ts +92 -0
- package/test-worldnews.mjs +26 -0
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,UAAU,GAAG,0DAA0D,CAAC;AAE9E,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,MAAW;IAC3D,MAAM,GAAG,GAAG,GAAG,UAAU,IAAI,QAAQ,EAAE,CAAC;IAExC,2CAA2C;IAC3C,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAG,qBAAqB,EAAE,CAAC;IAC5C,MAAM,UAAU,GAAG,OAAO,EAAE,WAAW,IAAI,QAAQ,WAAW,EAAE,CAAC;IAEjE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,sBAAsB,EAAE,UAAU;YAClC,oBAAoB,EAAE,QAAQ;YAC9B,kBAAkB,EAAE,MAAM,CAAC,MAAM,IAAI,MAAM;SAC5C;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;KAC7B,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAuB,CAAC;QAC/G,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,IAAI,gBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,UAAU,GAAG,0DAA0D,CAAC;AAE9E,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,MAAW;IAC3D,MAAM,GAAG,GAAG,GAAG,UAAU,IAAI,QAAQ,EAAE,CAAC;IAExC,2CAA2C;IAC3C,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAG,qBAAqB,EAAE,CAAC;IAC5C,MAAM,UAAU,GAAG,OAAO,EAAE,WAAW,IAAI,QAAQ,WAAW,EAAE,CAAC;IAEjE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,sBAAsB,EAAE,UAAU;YAClC,oBAAoB,EAAE,QAAQ;YAC9B,kBAAkB,EAAE,MAAM,CAAC,MAAM,IAAI,MAAM;SAC5C;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;KAC7B,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAuB,CAAC;QAC/G,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,IAAI,gBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# APIClaw Email Templates
|
|
2
|
+
|
|
3
|
+
Official HTML email templates for APIClaw communications.
|
|
4
|
+
|
|
5
|
+
## Design System
|
|
6
|
+
|
|
7
|
+
**Brand Colors:**
|
|
8
|
+
- Primary Red: `#ef4444`
|
|
9
|
+
- Background: `#fafafa`
|
|
10
|
+
- Card Background: `#ffffff`
|
|
11
|
+
- Text Primary: `#1a1a1a`
|
|
12
|
+
- Text Secondary: `#4b5563`
|
|
13
|
+
- Text Muted: `#6b7280`
|
|
14
|
+
- Border: `#e5e7eb`
|
|
15
|
+
- Highlight Background: `#fef2f2`
|
|
16
|
+
|
|
17
|
+
**Typography:**
|
|
18
|
+
- System font stack: `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif`
|
|
19
|
+
- Headers: Bold, red (`#ef4444`)
|
|
20
|
+
- Body: Regular, dark gray (`#4b5563`)
|
|
21
|
+
- Code blocks: Monospace, gray background (`#f9fafb`)
|
|
22
|
+
|
|
23
|
+
**Layout:**
|
|
24
|
+
- Max width: 600px
|
|
25
|
+
- Card style with rounded corners (16px)
|
|
26
|
+
- Subtle shadow and border
|
|
27
|
+
- Responsive padding
|
|
28
|
+
- Center-aligned container
|
|
29
|
+
|
|
30
|
+
## Templates
|
|
31
|
+
|
|
32
|
+
### `partnership-template.html`
|
|
33
|
+
**Use for:** Partnership outreach, integration proposals, technical follow-ups
|
|
34
|
+
|
|
35
|
+
**Variables to replace:**
|
|
36
|
+
- `{{EMAIL_TITLE}}` - Main heading (e.g., "APIClaw × Apideck")
|
|
37
|
+
- `{{EMAIL_SUBTITLE}}` - Subtitle (e.g., "Partnership Follow-up")
|
|
38
|
+
- `{{GREETING}}` - Opening (e.g., "Hi Pratham,")
|
|
39
|
+
- `{{INTRO_TEXT}}` - First paragraph
|
|
40
|
+
- `{{SECTION_X_TITLE}}` - Section headings
|
|
41
|
+
- `{{SECTION_X_CONTENT}}` - Section content (can include HTML lists, paragraphs)
|
|
42
|
+
- `{{HIGHLIGHT_TITLE}}` - Callout box title
|
|
43
|
+
- `{{HIGHLIGHT_TEXT}}` - Callout box content
|
|
44
|
+
- `{{CODE_BLOCK}}` - Technical/code content (optional)
|
|
45
|
+
- `{{CTA_LINK}}` - Button URL
|
|
46
|
+
- `{{CTA_TEXT}}` - Button text
|
|
47
|
+
- `{{CLOSING_TEXT}}` - Closing paragraph
|
|
48
|
+
- `{{FOOTER_TEXT}}` - Footer context (e.g., "APIClaw × Apideck • March 2026")
|
|
49
|
+
|
|
50
|
+
**Structure:**
|
|
51
|
+
1. Header with logo + title
|
|
52
|
+
2. Greeting + intro
|
|
53
|
+
3. Multiple sections with red headings
|
|
54
|
+
4. Optional highlight box
|
|
55
|
+
5. Optional code block
|
|
56
|
+
6. CTA button
|
|
57
|
+
7. Closing + signature
|
|
58
|
+
8. Footer
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
Send via n8n workflow:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
curl -s -X POST "https://nordsym.app.n8n.cloud/webhook/symbot-gmail" \
|
|
66
|
+
-H "Content-Type: application/json" \
|
|
67
|
+
-d '{
|
|
68
|
+
"action": "send",
|
|
69
|
+
"to": "recipient@example.com",
|
|
70
|
+
"subject": "Subject line",
|
|
71
|
+
"message": "'"$(cat partnership-template.html | sed 's/{{VAR}}/value/g')"'"
|
|
72
|
+
}'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or use `action: "smtp"` for sending from `Symbot@nordsym.com`.
|
|
76
|
+
|
|
77
|
+
## Design Principles
|
|
78
|
+
|
|
79
|
+
✅ **DO:**
|
|
80
|
+
- Use light mode (professional, readable)
|
|
81
|
+
- Keep sections scannable
|
|
82
|
+
- Use red for emphasis sparingly
|
|
83
|
+
- Include clear CTA
|
|
84
|
+
- Test in both light and dark mode (email clients auto-convert)
|
|
85
|
+
- Maintain 600px max width for readability
|
|
86
|
+
|
|
87
|
+
❌ **DON'T:**
|
|
88
|
+
- Use dark mode as default
|
|
89
|
+
- Overuse red color
|
|
90
|
+
- Create walls of text
|
|
91
|
+
- Skip the highlight box for key points
|
|
92
|
+
- Forget mobile responsiveness
|
|
93
|
+
|
|
94
|
+
## Created
|
|
95
|
+
|
|
96
|
+
**Date:** 2026-03-24
|
|
97
|
+
**Design:** Light APIClaw theme matching MoU pages
|
|
98
|
+
**First use:** Pratham/Apideck partnership follow-up (draft)
|
|
99
|
+
|
|
100
|
+
## Notes
|
|
101
|
+
|
|
102
|
+
Design is production-ready. Copy/content templates can be improved iteratively.
|
|
103
|
+
|
|
104
|
+
Each email should feel like it came from the same visual system as apiclaw.nordsym.com.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>APIClaw Email Template</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body style="margin: 0; padding: 0; background-color: #fafafa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;">
|
|
9
|
+
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #fafafa; padding: 40px 20px;">
|
|
10
|
+
<tr>
|
|
11
|
+
<td align="center">
|
|
12
|
+
<!-- Main container -->
|
|
13
|
+
<table width="600" cellpadding="0" cellspacing="0" style="background-color: #ffffff; border-radius: 16px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); overflow: hidden; border: 1px solid #e5e7eb;">
|
|
14
|
+
|
|
15
|
+
<!-- Header -->
|
|
16
|
+
<tr>
|
|
17
|
+
<td style="background-color: #ffffff; border-bottom: 1px solid #e5e7eb; padding: 32px; text-align: center;">
|
|
18
|
+
<div style="font-size: 28px; font-weight: bold; color: #ef4444; margin-bottom: 16px;">🦞 APIClaw</div>
|
|
19
|
+
<h1 style="margin: 0; font-size: 24px; font-weight: bold; color: #1a1a1a;">{{EMAIL_TITLE}}</h1>
|
|
20
|
+
<p style="margin: 4px 0 0 0; color: #6b7280; font-size: 14px;">{{EMAIL_SUBTITLE}}</p>
|
|
21
|
+
<div style="height: 4px; width: 80px; background-color: #ef4444; margin: 16px auto 0; border-radius: 2px;"></div>
|
|
22
|
+
</td>
|
|
23
|
+
</tr>
|
|
24
|
+
|
|
25
|
+
<!-- Content -->
|
|
26
|
+
<tr>
|
|
27
|
+
<td style="padding: 32px;">
|
|
28
|
+
<!-- Greeting -->
|
|
29
|
+
<p style="margin: 0 0 20px 0; color: #1a1a1a; font-size: 16px; line-height: 1.6;">
|
|
30
|
+
{{GREETING}}
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
<!-- Intro paragraph -->
|
|
34
|
+
<p style="margin: 0 0 20px 0; color: #4b5563; font-size: 16px; line-height: 1.6;">
|
|
35
|
+
{{INTRO_TEXT}}
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<!-- Section 1 -->
|
|
39
|
+
<div style="margin: 24px 0;">
|
|
40
|
+
<h2 style="margin: 0 0 12px 0; font-size: 18px; font-weight: 600; color: #ef4444; border-bottom: 2px solid #f3f4f6; padding-bottom: 8px;">
|
|
41
|
+
{{SECTION_1_TITLE}}
|
|
42
|
+
</h2>
|
|
43
|
+
{{SECTION_1_CONTENT}}
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- Highlight Box (optional) -->
|
|
47
|
+
<div style="margin: 24px 0; background-color: #fef2f2; border-left: 4px solid #ef4444; padding: 16px; border-radius: 8px;">
|
|
48
|
+
<h3 style="margin: 0 0 8px 0; font-size: 16px; font-weight: 600; color: #ef4444;">
|
|
49
|
+
{{HIGHLIGHT_TITLE}}
|
|
50
|
+
</h3>
|
|
51
|
+
<p style="margin: 0; color: #4b5563; font-size: 14px; line-height: 1.6;">
|
|
52
|
+
{{HIGHLIGHT_TEXT}}
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<!-- Section 2 -->
|
|
57
|
+
<div style="margin: 24px 0;">
|
|
58
|
+
<h2 style="margin: 0 0 12px 0; font-size: 18px; font-weight: 600; color: #ef4444; border-bottom: 2px solid #f3f4f6; padding-bottom: 8px;">
|
|
59
|
+
{{SECTION_2_TITLE}}
|
|
60
|
+
</h2>
|
|
61
|
+
{{SECTION_2_CONTENT}}
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<!-- Code/Technical Block (optional) -->
|
|
65
|
+
<div style="background-color: #f9fafb; padding: 16px; border-radius: 8px; font-family: 'Courier New', monospace; font-size: 13px; color: #374151; line-height: 1.6;">
|
|
66
|
+
{{CODE_BLOCK}}
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- CTA Button -->
|
|
70
|
+
<div style="text-align: center; margin: 32px 0;">
|
|
71
|
+
<a href="{{CTA_LINK}}" style="display: inline-block; background-color: #ef4444; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-weight: 600; font-size: 16px; box-shadow: 0 2px 4px rgba(239, 68, 68, 0.2);">
|
|
72
|
+
{{CTA_TEXT}}
|
|
73
|
+
</a>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<!-- Closing -->
|
|
77
|
+
<p style="margin: 24px 0 0 0; color: #4b5563; font-size: 16px; line-height: 1.6;">
|
|
78
|
+
{{CLOSING_TEXT}}
|
|
79
|
+
</p>
|
|
80
|
+
|
|
81
|
+
<!-- Signature -->
|
|
82
|
+
<p style="margin: 20px 0 0 0; color: #4b5563; font-size: 16px; line-height: 1.6;">
|
|
83
|
+
Best,<br>
|
|
84
|
+
<strong style="color: #1a1a1a;">Gustav Hemmingsson</strong><br>
|
|
85
|
+
<span style="color: #6b7280; font-size: 14px;">CEO, NordSym AB</span>
|
|
86
|
+
</p>
|
|
87
|
+
</td>
|
|
88
|
+
</tr>
|
|
89
|
+
|
|
90
|
+
<!-- Footer -->
|
|
91
|
+
<tr>
|
|
92
|
+
<td style="background-color: #f9fafb; border-top: 1px solid #e5e7eb; padding: 24px; text-align: center;">
|
|
93
|
+
<p style="margin: 0 0 8px 0; color: #6b7280; font-size: 13px;">
|
|
94
|
+
{{FOOTER_TEXT}}
|
|
95
|
+
</p>
|
|
96
|
+
<p style="margin: 0; color: #9ca3af; font-size: 12px;">
|
|
97
|
+
Questions? Contact <a href="mailto:gustav@nordsym.com" style="color: #ef4444; text-decoration: none;">gustav@nordsym.com</a>
|
|
98
|
+
</p>
|
|
99
|
+
</td>
|
|
100
|
+
</tr>
|
|
101
|
+
|
|
102
|
+
</table>
|
|
103
|
+
|
|
104
|
+
<!-- Signature -->
|
|
105
|
+
<div style="margin-top: 32px; text-align: center; color: #9ca3af; font-size: 12px;">
|
|
106
|
+
<p style="margin: 0;">NordSym AB (559535-5768) • Sweden</p>
|
|
107
|
+
<p style="margin: 4px 0 0 0;">
|
|
108
|
+
<a href="https://apiclaw.nordsym.com" style="color: #ef4444; text-decoration: none;">apiclaw.nordsym.com</a> •
|
|
109
|
+
<a href="https://github.com/nordsym/apiclaw" style="color: #ef4444; text-decoration: none;">GitHub</a>
|
|
110
|
+
</p>
|
|
111
|
+
</div>
|
|
112
|
+
</td>
|
|
113
|
+
</tr>
|
|
114
|
+
</table>
|
|
115
|
+
</body>
|
|
116
|
+
</html>
|
package/landing/next-env.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
+
/// <reference types="next/navigation-types/compat/navigation" />
|
|
3
4
|
|
|
4
5
|
// NOTE: This file should not be edited
|
|
5
6
|
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* APIClaw Discovery API - Next.js API Route
|
|
3
|
+
* GET /api/discover?query=...&agentId=...
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
7
|
+
|
|
8
|
+
// Import from built dist (since we can't easily import TS from parent)
|
|
9
|
+
// For now, return a simple response - we'll implement full logic after verifying deployment
|
|
10
|
+
|
|
11
|
+
export default async function handler(
|
|
12
|
+
req: NextApiRequest,
|
|
13
|
+
res: NextApiResponse
|
|
14
|
+
) {
|
|
15
|
+
// CORS
|
|
16
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
17
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
18
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Agent-Id');
|
|
19
|
+
|
|
20
|
+
if (req.method === 'OPTIONS') {
|
|
21
|
+
return res.status(200).end();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (req.method !== 'GET') {
|
|
25
|
+
return res.status(405).json({ error: 'Method not allowed' });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { query, agentId, category, maxResults } = req.query;
|
|
29
|
+
|
|
30
|
+
if (!query || typeof query !== 'string') {
|
|
31
|
+
return res.status(400).json({ error: 'Missing query parameter' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// TODO: Import actual whitelist + discovery logic
|
|
35
|
+
// For now, simple passthrough
|
|
36
|
+
return res.status(200).json({
|
|
37
|
+
success: true,
|
|
38
|
+
query,
|
|
39
|
+
agentId: typeof agentId === 'string' ? agentId : undefined,
|
|
40
|
+
message: 'APIClaw Discovery API - implementation pending',
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* APIClaw Health Check - Next.js API Route
|
|
3
|
+
* GET /api/health
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
7
|
+
|
|
8
|
+
export default async function handler(
|
|
9
|
+
req: NextApiRequest,
|
|
10
|
+
res: NextApiResponse
|
|
11
|
+
) {
|
|
12
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
13
|
+
|
|
14
|
+
return res.status(200).json({
|
|
15
|
+
status: 'ok',
|
|
16
|
+
service: 'apiclaw-http-api',
|
|
17
|
+
version: '2.0.0',
|
|
18
|
+
timestamp: new Date().toISOString(),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -51,7 +51,7 @@ export async function POST(req: NextRequest) {
|
|
|
51
51
|
</div>
|
|
52
52
|
|
|
53
53
|
<p style="color: #525252; font-size: 16px; line-height: 1.6; text-align: center;">
|
|
54
|
-
Click the button below to sign in to your
|
|
54
|
+
Click the button below to sign in to your workspace.
|
|
55
55
|
</p>
|
|
56
56
|
|
|
57
57
|
<div style="text-align: center; margin: 32px 0;">
|
|
@@ -22,6 +22,12 @@ interface VerifyResult {
|
|
|
22
22
|
|
|
23
23
|
function VerifyContent() {
|
|
24
24
|
const searchParams = useSearchParams();
|
|
25
|
+
|
|
26
|
+
// Handle null searchParams (shouldn't happen in client component, but TypeScript requires it)
|
|
27
|
+
if (!searchParams) {
|
|
28
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
const token = searchParams.get("token");
|
|
26
32
|
const referralCode = searchParams.get("ref"); // Referral code from signup URL
|
|
27
33
|
|
|
@@ -8,6 +8,12 @@ import Link from "next/link";
|
|
|
8
8
|
function VerifyContent() {
|
|
9
9
|
const router = useRouter();
|
|
10
10
|
const searchParams = useSearchParams();
|
|
11
|
+
|
|
12
|
+
// Handle null searchParams
|
|
13
|
+
if (!searchParams) {
|
|
14
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
const token = searchParams.get("token");
|
|
12
18
|
|
|
13
19
|
const [status, setStatus] = useState<"verifying" | "success" | "error">("verifying");
|
|
@@ -7,6 +7,12 @@ import { Suspense } from "react";
|
|
|
7
7
|
function JoinContent() {
|
|
8
8
|
const searchParams = useSearchParams();
|
|
9
9
|
const router = useRouter();
|
|
10
|
+
|
|
11
|
+
// Handle null searchParams
|
|
12
|
+
if (!searchParams) {
|
|
13
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
const refCode = searchParams.get("ref");
|
|
11
17
|
|
|
12
18
|
useEffect(() => {
|
|
@@ -51,7 +51,7 @@ const schemaOrg = {
|
|
|
51
51
|
"@type": "WebSite",
|
|
52
52
|
"name": "APIClaw",
|
|
53
53
|
"url": "https://apiclaw.nordsym.com",
|
|
54
|
-
"description": "Your agent's API encyclopedia. Search by capability, call instantly. 22,392 APIs indexed • 1,636 Open APIs •
|
|
54
|
+
"description": "Your agent's API encyclopedia. Search by capability, call instantly. 22,392 APIs indexed • 1,636 Open APIs • 19 Direct Call • MCP native"
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"@type": "Organization",
|
|
@@ -63,7 +63,7 @@ const schemaOrg = {
|
|
|
63
63
|
"name": "APIClaw",
|
|
64
64
|
"applicationCategory": "DeveloperApplication",
|
|
65
65
|
"operatingSystem": "Web",
|
|
66
|
-
"description": "The API layer for AI agents. 22,392 APIs indexed, 1,636 Open APIs,
|
|
66
|
+
"description": "The API layer for AI agents. 22,392 APIs indexed, 1,636 Open APIs, 19 Direct Call providers. MCP native.",
|
|
67
67
|
"offers": {
|
|
68
68
|
"@type": "Offer",
|
|
69
69
|
"price": "0",
|
|
@@ -199,7 +199,7 @@ export default function LoginPage() {
|
|
|
199
199
|
<p className="text-sm text-[var(--text-muted)]">
|
|
200
200
|
Are you an API provider?{" "}
|
|
201
201
|
<Link href="/providers/dashboard/login" className="text-accent hover:underline">
|
|
202
|
-
|
|
202
|
+
Workspace
|
|
203
203
|
</Link>
|
|
204
204
|
</p>
|
|
205
205
|
</div>
|
|
@@ -126,6 +126,12 @@ const mouContent: Record<string, MOUPartner> = {
|
|
|
126
126
|
|
|
127
127
|
export default function MOUPage() {
|
|
128
128
|
const params = useParams();
|
|
129
|
+
|
|
130
|
+
// Handle null params
|
|
131
|
+
if (!params || !params.partnerId) {
|
|
132
|
+
return <div className="min-h-screen flex items-center justify-center">Invalid partner ID</div>;
|
|
133
|
+
}
|
|
134
|
+
|
|
129
135
|
const partnerId = params.partnerId as string;
|
|
130
136
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
131
137
|
const [isDrawing, setIsDrawing] = useState(false);
|
package/landing/src/app/page.tsx
CHANGED
|
@@ -12,11 +12,12 @@ import { useState, useEffect, useRef } from "react";
|
|
|
12
12
|
import { HeroTabs } from "@/components/HeroTabs";
|
|
13
13
|
import { PhoneDemo } from "@/components/demo";
|
|
14
14
|
import { AITestimonials } from "@/components/AITestimonials";
|
|
15
|
+
import { VideoDemo } from "@/components/VideoDemo";
|
|
15
16
|
|
|
16
17
|
const stats = [
|
|
17
18
|
{ number: statsData.apiCount.toLocaleString(), label: "APIs Indexed", live: true },
|
|
18
19
|
{ number: statsData.openApiCount.toLocaleString(), label: "Open APIs", live: true },
|
|
19
|
-
{ number:
|
|
20
|
+
{ number: "19", label: "Direct Call", live: false },
|
|
20
21
|
{ number: (statsData.npmDownloads || 4232).toLocaleString(), label: "Installs", live: false },
|
|
21
22
|
{ number: statsData.categoryCount.toString(), label: "Categories", live: false },
|
|
22
23
|
];
|
|
@@ -146,6 +147,7 @@ const terminalLines = [
|
|
|
146
147
|
const directCallProviders = [
|
|
147
148
|
{ name: "Replicate", desc: "1000+ ML models" },
|
|
148
149
|
{ name: "OpenRouter", desc: "100+ LLMs" },
|
|
150
|
+
{ name: "APILayer", desc: "14 APIs" },
|
|
149
151
|
{ name: "Firecrawl", desc: "Web scraping" },
|
|
150
152
|
{ name: "E2B", desc: "Code sandbox" },
|
|
151
153
|
{ name: "GitHub", desc: "Repos & Issues" },
|
|
@@ -170,22 +172,23 @@ export default function Home() {
|
|
|
170
172
|
const [waitlistEmail, setWaitlistEmail] = useState("");
|
|
171
173
|
const [waitlistStatus, setWaitlistStatus] = useState<"idle" | "loading" | "success" | "error">("idle");
|
|
172
174
|
|
|
173
|
-
const directCallProviders = [
|
|
174
|
-
//
|
|
175
|
-
{ name: "
|
|
176
|
-
{ name: "
|
|
175
|
+
const directCallProviders: Array<{ name: string; desc: string; category: string; featured?: boolean; apis?: number }> = [
|
|
176
|
+
// Multi-API providers (top)
|
|
177
|
+
{ name: "APILayer", desc: "Exchange rates, stocks, aviation, PDF, screenshots, email/phone verification, VAT, news, scraping", category: "Multi-API", apis: 14 },
|
|
178
|
+
{ name: "Replicate", desc: "Whisper, Stable Diffusion, Flux, Luma, 1000+ ML models", category: "Multi-API", apis: 1000 },
|
|
179
|
+
{ name: "OpenRouter", desc: "GPT-4, Claude, Llama, Gemini, 100+ LLMs", category: "Multi-API", apis: 100 },
|
|
180
|
+
// Single-purpose
|
|
177
181
|
{ name: "ElevenLabs", desc: "Text-to-speech in 29 languages", category: "Voice & TTS" },
|
|
178
|
-
{ name: "46elks", desc: "SMS in Sweden and globally", category: "SMS & Messaging" },
|
|
179
|
-
{ name: "Twilio", desc: "Enterprise SMS and voice", category: "SMS & Messaging" },
|
|
180
|
-
{ name: "Resend", desc: "Modern email API for developers", category: "Email" },
|
|
181
|
-
{ name: "Brave Search", desc: "Privacy-focused web search", category: "Search" },
|
|
182
|
-
{ name: "Firecrawl", desc: "Web scraping to LLM-ready markdown", category: "Search" },
|
|
183
|
-
{ name: "E2B", desc: "Secure cloud sandboxes for code execution", category: "Code Execution" },
|
|
184
|
-
{ name: "GitHub", desc: "Repos, issues, PRs, and more", category: "Developer Tools" },
|
|
185
|
-
// New 8
|
|
186
182
|
{ name: "Groq", desc: "Ultra-fast LLM inference", category: "AI & LLM" },
|
|
187
183
|
{ name: "Deepgram", desc: "Speech-to-text transcription", category: "Voice & TTS" },
|
|
184
|
+
{ name: "Firecrawl", desc: "Web scraping to LLM-ready markdown", category: "Search" },
|
|
185
|
+
{ name: "Brave Search", desc: "Privacy-focused web search", category: "Search" },
|
|
188
186
|
{ name: "Serper", desc: "Google search API for AI", category: "Search" },
|
|
187
|
+
{ name: "E2B", desc: "Secure cloud sandboxes for code execution", category: "Code Execution" },
|
|
188
|
+
{ name: "GitHub", desc: "Repos, issues, PRs, and more", category: "Developer Tools" },
|
|
189
|
+
{ name: "Resend", desc: "Modern email API for developers", category: "Email" },
|
|
190
|
+
{ name: "46elks", desc: "SMS in Sweden and globally", category: "SMS & Messaging" },
|
|
191
|
+
{ name: "Twilio", desc: "Enterprise SMS and voice", category: "SMS & Messaging" },
|
|
189
192
|
{ name: "Mistral", desc: "Open-weight LLMs from Mistral AI", category: "AI & LLM" },
|
|
190
193
|
{ name: "Cohere", desc: "Enterprise NLP and embeddings", category: "AI & LLM" },
|
|
191
194
|
{ name: "Together AI", desc: "Open-source model inference", category: "AI & LLM" },
|
|
@@ -217,6 +220,7 @@ Voice: ElevenLabs, Deepgram, AssemblyAI
|
|
|
217
220
|
Search: Brave Search, Serper, Firecrawl
|
|
218
221
|
Code: E2B, GitHub
|
|
219
222
|
Utility: Resend (email), 46elks & Twilio (SMS)
|
|
223
|
+
Multi-API: APILayer (exchange rates, stocks, aviation, PDF, screenshots, verification, VAT, news, scraping — 14 APIs)
|
|
220
224
|
|
|
221
225
|
Your Tools:
|
|
222
226
|
• call_api(provider, action, params) – Execute immediately (leave auth fields empty – APIClaw handles it)
|
|
@@ -587,12 +591,26 @@ Docs: https://apiclaw.nordsym.com/docs`;
|
|
|
587
591
|
<p className="text-text-muted mb-4">These APIs work through APIClaw's proxy. Your agent calls them without needing API keys.</p>
|
|
588
592
|
<div className="space-y-3">
|
|
589
593
|
{directCallProviders.map((provider, i) => (
|
|
590
|
-
<div key={i} className=
|
|
594
|
+
<div key={i} className={`flex items-center justify-between p-3 rounded-xl border transition-all ${
|
|
595
|
+
provider.featured
|
|
596
|
+
? 'bg-gradient-to-r from-accent/5 to-purple-500/5 border-accent/30 hover:border-accent/50 hover:shadow-[0_0_20px_rgba(0,212,255,0.08)]'
|
|
597
|
+
: provider.apis
|
|
598
|
+
? 'bg-surface border-border/80'
|
|
599
|
+
: 'bg-surface border-border'
|
|
600
|
+
}`}>
|
|
591
601
|
<div>
|
|
592
|
-
<div className="
|
|
602
|
+
<div className="flex items-center gap-2">
|
|
603
|
+
<span className="font-medium">{provider.name}</span>
|
|
604
|
+
{provider.featured && (
|
|
605
|
+
<span className="text-[10px] font-bold uppercase tracking-wider px-1.5 py-0.5 rounded bg-accent/20 text-accent">Partner</span>
|
|
606
|
+
)}
|
|
607
|
+
{provider.apis && (
|
|
608
|
+
<span className="text-[10px] font-medium px-1.5 py-0.5 rounded bg-purple-500/20 text-purple-400">{provider.apis >= 1000 ? `${Math.floor(provider.apis/1000)}k+` : `${provider.apis}`} APIs</span>
|
|
609
|
+
)}
|
|
610
|
+
</div>
|
|
593
611
|
<div className="text-sm text-text-muted">{provider.desc}</div>
|
|
594
612
|
</div>
|
|
595
|
-
<span className="text-xs px-2 py-1 rounded-full bg-accent/20 text-accent">{provider.category}</span>
|
|
613
|
+
<span className="text-xs px-2 py-1 rounded-full bg-accent/20 text-accent flex-shrink-0 ml-3">{provider.category}</span>
|
|
596
614
|
</div>
|
|
597
615
|
))}
|
|
598
616
|
</div>
|
|
@@ -1029,7 +1047,7 @@ Docs: https://apiclaw.nordsym.com/docs`;
|
|
|
1029
1047
|
</li>
|
|
1030
1048
|
<li className="flex items-start gap-3 text-text-secondary text-sm">
|
|
1031
1049
|
<Check className="w-5 h-5 text-accent flex-shrink-0 mt-0.5" />
|
|
1032
|
-
|
|
1050
|
+
19 Direct Call providers
|
|
1033
1051
|
</li>
|
|
1034
1052
|
<li className="flex items-start gap-3 text-text-secondary text-sm">
|
|
1035
1053
|
<Check className="w-5 h-5 text-accent flex-shrink-0 mt-0.5" />
|
|
@@ -1144,7 +1162,7 @@ Docs: https://apiclaw.nordsym.com/docs`;
|
|
|
1144
1162
|
},
|
|
1145
1163
|
{
|
|
1146
1164
|
q: "How do I add my API?",
|
|
1147
|
-
a: "Go to
|
|
1165
|
+
a: "Go to your Workspace, sign up with your email, and follow the self-service onboarding. Your API will be discoverable by AI agents immediately. Want to become a Direct Call partner? Set that up in your Workspace too."
|
|
1148
1166
|
},
|
|
1149
1167
|
{
|
|
1150
1168
|
q: "What's MCP?",
|
|
@@ -1327,6 +1345,9 @@ Docs: https://apiclaw.nordsym.com/docs`;
|
|
|
1327
1345
|
</div>
|
|
1328
1346
|
</div>
|
|
1329
1347
|
)}
|
|
1348
|
+
|
|
1349
|
+
{/* Video Demo Bubble - Always visible */}
|
|
1350
|
+
<VideoDemo />
|
|
1330
1351
|
</main>
|
|
1331
1352
|
);
|
|
1332
1353
|
}
|
|
@@ -47,6 +47,12 @@ const PARAM_LOCATIONS = ["body", "query", "path"] as const;
|
|
|
47
47
|
export default function EditActionPage() {
|
|
48
48
|
const params = useParams();
|
|
49
49
|
const router = useRouter();
|
|
50
|
+
|
|
51
|
+
// Handle null params
|
|
52
|
+
if (!params || !params.apiId || !params.actionId) {
|
|
53
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
const apiId = params.apiId as string;
|
|
51
57
|
const actionId = params.actionId as string;
|
|
52
58
|
|
|
@@ -45,6 +45,11 @@ const PARAM_LOCATIONS = ["body", "query", "path"] as const;
|
|
|
45
45
|
|
|
46
46
|
export default function NewActionPage() {
|
|
47
47
|
const params = useParams();
|
|
48
|
+
|
|
49
|
+
// Handle null params
|
|
50
|
+
if (!params || !params.apiId) {
|
|
51
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
52
|
+
}
|
|
48
53
|
const router = useRouter();
|
|
49
54
|
const apiId = params.apiId as string;
|
|
50
55
|
|
|
@@ -41,6 +41,11 @@ const CONVEX_URL = process.env.NEXT_PUBLIC_CONVEX_URL || 'https://brilliant-puff
|
|
|
41
41
|
|
|
42
42
|
export default function ActionsPage() {
|
|
43
43
|
const params = useParams();
|
|
44
|
+
|
|
45
|
+
// Handle null params
|
|
46
|
+
if (!params || !params.apiId) {
|
|
47
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
48
|
+
}
|
|
44
49
|
const router = useRouter();
|
|
45
50
|
const apiId = params.apiId as string;
|
|
46
51
|
|
|
@@ -50,6 +50,11 @@ const authTypes = [
|
|
|
50
50
|
|
|
51
51
|
export default function DirectCallSetupPage() {
|
|
52
52
|
const params = useParams();
|
|
53
|
+
|
|
54
|
+
// Handle null params
|
|
55
|
+
if (!params || !params.apiId) {
|
|
56
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
57
|
+
}
|
|
53
58
|
const router = useRouter();
|
|
54
59
|
const apiId = params.apiId as string;
|
|
55
60
|
|
|
@@ -440,7 +445,7 @@ export default function DirectCallSetupPage() {
|
|
|
440
445
|
<label className="block text-sm font-medium mb-2">Service Account Key</label>
|
|
441
446
|
<p className="text-xs text-text-muted mb-3">
|
|
442
447
|
The API key APIClaw uses to make requests on behalf of your users.
|
|
443
|
-
This is typically a privileged key from your
|
|
448
|
+
This is typically a privileged key from your workspace.
|
|
444
449
|
</p>
|
|
445
450
|
<div className="relative">
|
|
446
451
|
<Key className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-text-muted" />
|
|
@@ -32,6 +32,11 @@ interface DirectCallConfig {
|
|
|
32
32
|
|
|
33
33
|
export default function ApiOverviewPage() {
|
|
34
34
|
const params = useParams();
|
|
35
|
+
|
|
36
|
+
// Handle null params
|
|
37
|
+
if (!params || !params.apiId) {
|
|
38
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
39
|
+
}
|
|
35
40
|
const router = useRouter();
|
|
36
41
|
const apiId = params.apiId as string;
|
|
37
42
|
|
|
@@ -61,6 +61,11 @@ const methodColors: Record<string, string> = {
|
|
|
61
61
|
|
|
62
62
|
export default function TestConsolePage() {
|
|
63
63
|
const params = useParams();
|
|
64
|
+
|
|
65
|
+
// Handle null params
|
|
66
|
+
if (!params || !params.apiId) {
|
|
67
|
+
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
|
68
|
+
}
|
|
64
69
|
const router = useRouter();
|
|
65
70
|
const apiId = params.apiId as string;
|
|
66
71
|
|