@slashfi/agents-sdk 0.8.0 → 0.9.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 (59) hide show
  1. package/dist/agent-definitions/auth.d.ts +17 -0
  2. package/dist/agent-definitions/auth.d.ts.map +1 -1
  3. package/dist/agent-definitions/auth.js +135 -1
  4. package/dist/agent-definitions/auth.js.map +1 -1
  5. package/dist/agent-definitions/integrations.d.ts +19 -0
  6. package/dist/agent-definitions/integrations.d.ts.map +1 -1
  7. package/dist/agent-definitions/integrations.js +218 -5
  8. package/dist/agent-definitions/integrations.js.map +1 -1
  9. package/dist/index.d.ts +9 -4
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +6 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/integration-interface.d.ts +37 -0
  14. package/dist/integration-interface.d.ts.map +1 -0
  15. package/dist/integration-interface.js +94 -0
  16. package/dist/integration-interface.js.map +1 -0
  17. package/dist/integrations-store.d.ts +33 -0
  18. package/dist/integrations-store.d.ts.map +1 -0
  19. package/dist/integrations-store.js +50 -0
  20. package/dist/integrations-store.js.map +1 -0
  21. package/dist/jwt.d.ts +86 -17
  22. package/dist/jwt.d.ts.map +1 -1
  23. package/dist/jwt.js +140 -17
  24. package/dist/jwt.js.map +1 -1
  25. package/dist/registry.d.ts +7 -0
  26. package/dist/registry.d.ts.map +1 -1
  27. package/dist/registry.js +8 -21
  28. package/dist/registry.js.map +1 -1
  29. package/dist/secret-collection.d.ts +37 -0
  30. package/dist/secret-collection.d.ts.map +1 -0
  31. package/dist/secret-collection.js +37 -0
  32. package/dist/secret-collection.js.map +1 -0
  33. package/dist/server.d.ts +41 -44
  34. package/dist/server.d.ts.map +1 -1
  35. package/dist/server.js +232 -592
  36. package/dist/server.js.map +1 -1
  37. package/dist/types.d.ts +7 -1
  38. package/dist/types.d.ts.map +1 -1
  39. package/package.json +5 -2
  40. package/src/agent-definitions/auth.ts +187 -1
  41. package/src/agent-definitions/integrations.ts +260 -5
  42. package/src/index.ts +18 -4
  43. package/src/integration-interface.ts +118 -0
  44. package/src/integrations-store.ts +84 -0
  45. package/src/jwt.ts +233 -65
  46. package/src/registry.ts +17 -2
  47. package/src/secret-collection.ts +66 -0
  48. package/src/server.ts +268 -681
  49. package/src/types.ts +8 -1
  50. package/dist/slack-oauth.d.ts +0 -27
  51. package/dist/slack-oauth.d.ts.map +0 -1
  52. package/dist/slack-oauth.js +0 -48
  53. package/dist/slack-oauth.js.map +0 -1
  54. package/dist/web-pages.d.ts +0 -8
  55. package/dist/web-pages.d.ts.map +0 -1
  56. package/dist/web-pages.js +0 -169
  57. package/dist/web-pages.js.map +0 -1
  58. package/src/slack-oauth.ts +0 -66
  59. package/src/web-pages.ts +0 -178
package/src/types.ts CHANGED
@@ -89,7 +89,11 @@ export interface IntegrationConfig {
89
89
  /** Brief description of what connecting this integration enables */
90
90
  description?: string;
91
91
 
92
+ /** JSON Schema for the setup/create params. Used by discover_integrations. */
93
+ setupSchema?: Record<string, unknown>;
92
94
 
95
+ /** JSON Schema for the connect params (e.g., OAuth scopes). */
96
+ connectSchema?: Record<string, unknown>;
93
97
  }
94
98
 
95
99
  // ============================================
@@ -191,6 +195,9 @@ export interface ToolContext extends CoreContext {
191
195
 
192
196
  /** Type of caller (required for tools) */
193
197
  callerType: CallerType;
198
+
199
+ /** Application-specific extensions (e.g., OS services, stores) */
200
+ [key: string]: unknown;
194
201
  }
195
202
 
196
203
  // ============================================
@@ -384,7 +391,7 @@ export interface AgentRuntime {
384
391
  /**
385
392
  * Visibility level for tools and agents.
386
393
  */
387
- export type Visibility = "public" | "internal" | "private";
394
+ export type Visibility = "public" | "authenticated" | "internal" | "private";
388
395
 
389
396
  /**
390
397
  * A tool definition with execute function.
@@ -1,27 +0,0 @@
1
- /**
2
- * Slack OAuth2 (Sign in with Slack / OpenID Connect)
3
- *
4
- * Flow:
5
- * GET /auth/slack → redirect to Slack authorize
6
- * GET /auth/slack/callback → exchange code, get user identity
7
- */
8
- export interface SlackOAuthConfig {
9
- clientId: string;
10
- clientSecret: string;
11
- redirectUri: string;
12
- }
13
- export declare function slackAuthUrl(config: SlackOAuthConfig, state?: string): string;
14
- export declare function exchangeSlackCode(code: string, config: SlackOAuthConfig): Promise<{
15
- access_token: string;
16
- id_token?: string;
17
- }>;
18
- export interface SlackProfile {
19
- sub: string;
20
- email: string;
21
- name: string;
22
- picture?: string;
23
- "https://slack.com/team_id"?: string;
24
- "https://slack.com/team_name"?: string;
25
- }
26
- export declare function getSlackProfile(accessToken: string): Promise<SlackProfile>;
27
- //# sourceMappingURL=slack-oauth.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"slack-oauth.d.ts","sourceRoot":"","sources":["../src/slack-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAS7E;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAetD;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,6BAA6B,CAAC,EAAE,MAAM,CAAC;CACxC;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAOhF"}
@@ -1,48 +0,0 @@
1
- /**
2
- * Slack OAuth2 (Sign in with Slack / OpenID Connect)
3
- *
4
- * Flow:
5
- * GET /auth/slack → redirect to Slack authorize
6
- * GET /auth/slack/callback → exchange code, get user identity
7
- */
8
- const SLACK_AUTH_URL = "https://slack.com/openid/connect/authorize";
9
- const SLACK_TOKEN_URL = "https://slack.com/api/openid.connect.token";
10
- const SLACK_USERINFO_URL = "https://slack.com/api/openid.connect.userInfo";
11
- export function slackAuthUrl(config, state) {
12
- const params = new URLSearchParams({
13
- client_id: config.clientId,
14
- redirect_uri: config.redirectUri,
15
- response_type: "code",
16
- scope: "openid email profile",
17
- });
18
- if (state)
19
- params.set("state", state);
20
- return `${SLACK_AUTH_URL}?${params.toString()}`;
21
- }
22
- export async function exchangeSlackCode(code, config) {
23
- const res = await fetch(SLACK_TOKEN_URL, {
24
- method: "POST",
25
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
26
- body: new URLSearchParams({
27
- code,
28
- client_id: config.clientId,
29
- client_secret: config.clientSecret,
30
- redirect_uri: config.redirectUri,
31
- grant_type: "authorization_code",
32
- }),
33
- });
34
- const data = await res.json();
35
- if (!data.ok)
36
- throw new Error(`Slack token exchange failed: ${data.error}`);
37
- return data;
38
- }
39
- export async function getSlackProfile(accessToken) {
40
- const res = await fetch(SLACK_USERINFO_URL, {
41
- headers: { Authorization: `Bearer ${accessToken}` },
42
- });
43
- const data = await res.json();
44
- if (!data.ok)
45
- throw new Error(`Slack userinfo failed: ${data.error}`);
46
- return data;
47
- }
48
- //# sourceMappingURL=slack-oauth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"slack-oauth.js","sourceRoot":"","sources":["../src/slack-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,MAAM,cAAc,GAAG,4CAA4C,CAAC;AACpE,MAAM,eAAe,GAAG,4CAA4C,CAAC;AACrE,MAAM,kBAAkB,GAAG,+CAA+C,CAAC;AAE3E,MAAM,UAAU,YAAY,CAAC,MAAwB,EAAE,KAAc;IACnE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,SAAS,EAAE,MAAM,CAAC,QAAQ;QAC1B,YAAY,EAAE,MAAM,CAAC,WAAW;QAChC,aAAa,EAAE,MAAM;QACrB,KAAK,EAAE,sBAAsB;KAC9B,CAAC,CAAC;IACH,IAAI,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,GAAG,cAAc,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,MAAwB;IAExB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE;QACvC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,IAAI;YACJ,SAAS,EAAE,MAAM,CAAC,QAAQ;YAC1B,aAAa,EAAE,MAAM,CAAC,YAAY;YAClC,YAAY,EAAE,MAAM,CAAC,WAAW;YAChC,UAAU,EAAE,oBAAoB;SACjC,CAAC;KACH,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAS,CAAC;IACrC,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC;AACd,CAAC;AAWD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAmB;IACvD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,kBAAkB,EAAE;QAC1C,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,EAAE,EAAE;KACpD,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAS,CAAC;IACrC,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,8 +0,0 @@
1
- export declare function renderLoginPage(_baseUrl: string, slackEnabled: boolean): string;
2
- export declare function renderTenantPage(_baseUrl: string, email: string, name: string): string;
3
- export declare function renderDashboardPage(baseUrl: string, token: string, session?: {
4
- name?: string;
5
- email?: string;
6
- tenantName?: string;
7
- }): string;
8
- //# sourceMappingURL=web-pages.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"web-pages.d.ts","sourceRoot":"","sources":["../src/web-pages.ts"],"names":[],"mappings":"AAgBA,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAe/E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAsCtF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAwG5I"}
package/dist/web-pages.js DELETED
@@ -1,169 +0,0 @@
1
- const css = `*{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,'SF Pro Display','Segoe UI',sans-serif;background:#f9f8f7;color:#1a1917;-webkit-font-smoothing:antialiased}a{color:#c4982a;text-decoration:none}.page{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}.card{background:#fff;border:1px solid #e4ddd6;border-radius:10px;padding:32px;max-width:440px;width:100%;box-shadow:0 4px 6px -2px rgba(21,20,15,.1)}.logo{display:flex;align-items:center;gap:10px;margin-bottom:12px}.logo h1{font-size:20px;font-weight:600;letter-spacing:-.02em}.sub{color:#6b6560;font-size:14px;line-height:1.5;margin-bottom:28px}input[type=text]{width:100%;padding:10px 14px;background:#fff;border:1px solid #e4ddd6;border-radius:8px;color:#1a1917;font-size:14px;outline:none;margin-bottom:12px}input:focus{border-color:#c4982a;box-shadow:0 0 0 3px rgba(196,152,42,.08)}input::placeholder{color:#9c958e}label{display:block;font-size:13px;font-weight:500;color:#6b6560;margin-bottom:6px}.hint{font-size:12px;color:#9c958e;margin-bottom:16px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:10px 20px;border:none;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;width:100%;transition:all .15s}.btn-primary{background:#1a1917;color:#fff}.btn-primary:hover{background:#2d2c28}.btn-google{background:#fff;color:#1a1917;border:1px solid #e4ddd6}.btn-google:hover{background:#f4f3f1}.btn-ghost{background:transparent;color:#6b6560;border:1px solid #e4ddd6;padding:6px 16px;width:auto;font-size:13px}.btn-copy{background:#f4f3f1;color:#6b6560;border:1px solid #e4ddd6;padding:4px 10px;border-radius:6px;font-size:12px;cursor:pointer;white-space:nowrap}.btn-copy:hover{background:rgba(196,152,42,.08);color:#c4982a}.footer{text-align:center;font-size:12px;color:#9c958e;margin-top:20px}.dash{max-width:720px;width:100%}.dh{display:flex;align-items:center;justify-content:space-between;margin-bottom:32px}.sec{background:#fff;border:1px solid #e4ddd6;border-radius:10px;padding:24px;margin-bottom:16px;box-shadow:0 1px 1px rgba(21,20,15,.05)}.sec h2{font-size:15px;font-weight:600;margin-bottom:4px}.sec .d{font-size:13px;color:#6b6560;margin-bottom:16px}.cb{display:flex;align-items:center;gap:8px;background:#f4f3f1;border:1px solid #e4ddd6;border-radius:8px;padding:10px 14px}.cb code{flex:1;font-size:13px;font-family:'SF Mono',Menlo,monospace;word-break:break-all}.sg{display:grid;gap:12px}.sc{background:#f4f3f1;border:1px solid #e4ddd6;border-radius:8px;padding:16px}.sc h3{font-size:14px;font-weight:600;margin-bottom:8px}.cbl{display:flex;align-items:flex-start;gap:8px;background:#fff;border:1px solid #e4ddd6;border-radius:6px;padding:10px 12px}.cbl code{flex:1;font-size:12px;font-family:'SF Mono',Menlo,monospace;white-space:pre-wrap;word-break:break-all;color:#6b6560}.err{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c;padding:10px 12px;border-radius:8px;font-size:13px;margin-bottom:16px}.avatar{width:32px;height:32px;border-radius:50%;border:1px solid #e4ddd6}`;
2
- const slackSvg = `<svg width="18" height="18" viewBox="0 0 54 54" xmlns="http://www.w3.org/2000/svg"><path d="M19.712.133a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386h5.376V5.52A5.381 5.381 0 0 0 19.712.133m0 14.365H5.376A5.381 5.381 0 0 0 0 19.884a5.381 5.381 0 0 0 5.376 5.387h14.336a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386" fill="#36C5F0"/><path d="M53.76 19.884a5.381 5.381 0 0 0-5.376-5.386 5.381 5.381 0 0 0-5.376 5.386v5.387h5.376a5.381 5.381 0 0 0 5.376-5.387m-14.336 0V5.52A5.381 5.381 0 0 0 34.048.133a5.381 5.381 0 0 0-5.376 5.387v14.364a5.381 5.381 0 0 0 5.376 5.387 5.381 5.381 0 0 0 5.376-5.387" fill="#2EB67D"/><path d="M34.048 54a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386h-5.376v5.386A5.381 5.381 0 0 0 34.048 54m0-14.365h14.336a5.381 5.381 0 0 0 5.376-5.386 5.381 5.381 0 0 0-5.376-5.387H34.048a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386" fill="#ECB22E"/><path d="M0 34.249a5.381 5.381 0 0 0 5.376 5.386 5.381 5.381 0 0 0 5.376-5.386v-5.387H5.376A5.381 5.381 0 0 0 0 34.25m14.336 0v14.364A5.381 5.381 0 0 0 19.712 54a5.381 5.381 0 0 0 5.376-5.387V34.25a5.381 5.381 0 0 0-5.376-5.387 5.381 5.381 0 0 0-5.376 5.387" fill="#E01E5A"/></svg>`;
3
- function h(s) {
4
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5
- }
6
- function wrap(title, body) {
7
- return `<!DOCTYPE html>
8
- <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
9
- <title>${h(title)}</title>
10
- <style>${css}</style></head>
11
- <body>${body}</body></html>`;
12
- }
13
- export function renderLoginPage(_baseUrl, slackEnabled) {
14
- const googleBtn = slackEnabled
15
- ? `<a href="/auth/slack" class="btn btn-google">${slackSvg} Sign in with Slack</a>`
16
- : `<p style="color:#9c958e;font-size:13px;text-align:center;padding:12px 0">Slack OAuth not configured.<br>Set SLACK_CLIENT_ID and SLACK_CLIENT_SECRET.</p>`;
17
- return wrap("Agent Registry", `
18
- <div class="page">
19
- <div class="card">
20
- <div class="logo"><span style="font-size:24px">\u2B21</span><h1>Agent Registry</h1></div>
21
- <p style="font-size:17px;font-weight:500;color:#1a1917;margin-bottom:8px">Connect your AI agents to any API.</p>
22
- <p style="font-size:13px;color:#9c958e;line-height:1.5;margin-bottom:28px">Set up integrations, manage credentials securely, and get an MCP endpoint for Claude, Cursor, and more.</p>
23
- ${googleBtn}
24
- <p class="footer">By continuing, you agree to our terms of service.</p>
25
- </div>
26
- </div>`);
27
- }
28
- export function renderTenantPage(_baseUrl, email, name) {
29
- return wrap("Create Registry — Agent Registry", `
30
- <div class="page">
31
- <div class="card">
32
- <div class="logo"><span style="font-size:24px">\u2B21</span><h1>Agent Registry</h1></div>
33
- <p class="sub">Welcome, ${h(name || email)}! Choose a name for your registry.</p>
34
- <div id="err" class="err" style="display:none"></div>
35
- <form id="f">
36
- <input type="hidden" name="email" value="${h(email)}">
37
- <label>Registry name</label>
38
- <input type="text" name="tenant" placeholder="my-company" required pattern="[a-z0-9-]+" autocomplete="off" autofocus>
39
- <p class="hint">Lowercase letters, numbers, and hyphens only.</p>
40
- <button type="submit" class="btn btn-primary">Create Registry</button>
41
- </form>
42
- </div>
43
- </div>
44
- <script>
45
- document.getElementById('f').addEventListener('submit', async e => {
46
- e.preventDefault();
47
- const fd = new FormData(e.target);
48
- const btn = e.target.querySelector('[type=submit]');
49
- btn.disabled = true; btn.textContent = 'Creating...';
50
- try {
51
- const r = await fetch('/setup', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ email: fd.get('email'), tenant: fd.get('tenant') }) });
52
- const d = await r.json();
53
- if (d.success && (d.result?.token || d.result?.tenantId)) {
54
- window.location.href = '/dashboard?token=' + (d.result.token || d.result.tenantId);
55
- } else if (d.token || d.result?.token) {
56
- window.location.href = '/dashboard?token=' + (d.token || d.result.token);
57
- } else {
58
- const el = document.getElementById('err');
59
- el.textContent = d.error || d.result?.error || 'Something went wrong';
60
- el.style.display = 'block';
61
- }
62
- } catch(err) { document.getElementById('err').textContent = err.message; document.getElementById('err').style.display = 'block'; }
63
- finally { btn.disabled = false; btn.textContent = 'Create Registry'; }
64
- });
65
- </script>`);
66
- }
67
- export function renderDashboardPage(baseUrl, token, session) {
68
- const mcpUrl = `${baseUrl}/mcp?token=${h(token)}`;
69
- const redacted = `${baseUrl}/mcp?token=${'*'.repeat(12)}`;
70
- const claudeCmd = `claude mcp add agent-registry ${mcpUrl}`;
71
- const redactedClaudeCmd = `claude mcp add agent-registry ${redacted}`;
72
- const desktopJson = JSON.stringify({ mcpServers: { "agent-registry": { url: mcpUrl } } }, null, 2);
73
- const redactedDesktopJson = JSON.stringify({ mcpServers: { "agent-registry": { url: redacted } } }, null, 2);
74
- const js = `
75
- var _v={mcp:${JSON.stringify(mcpUrl)},cc:${JSON.stringify(claudeCmd)},cd:${JSON.stringify(desktopJson)},cu:${JSON.stringify(mcpUrl)}};
76
- var _r=false;
77
- function cp(id){navigator.clipboard.writeText(_v[id]);event.target.textContent='\u2713 Copied';setTimeout(()=>event.target.textContent='Copy',2000)}
78
- function rv(){var el=document.getElementById('md');_r=!_r;el.textContent=_r?_v.mcp:_v.mcp.split('token=')[0]+'token=${'*'.repeat(12)}';document.getElementById('rb').textContent=_r?'Hide':'Reveal'}
79
- function st(n){document.querySelectorAll('.tab').forEach(t=>t.classList.toggle('active',t.dataset.t===n));document.querySelectorAll('.tp').forEach(p=>p.classList.toggle('active',p.dataset.t===n))}
80
- `;
81
- const extraCss = `.tabs{display:flex;gap:0;border-bottom:1px solid #e4ddd6;margin-bottom:16px}.tab{padding:8px 16px;}.tabs .tab:first-child{padding-left:0}.tab{padding:8px 16px;font-size:13px;font-weight:500;color:#9c958e;cursor:pointer;border-bottom:2px solid transparent;transition:all .15s;white-space:nowrap}.tab:hover{color:#6b6560}.tab.active{color:#1a1917;border-bottom-color:#c4982a}.tp{display:none}.tp.active{display:block}.page{align-items:flex-start;padding-top:48px}.divider{border:none;border-top:1px solid #e4ddd6;margin:20px 0}`;
82
- return wrap("Dashboard \u2014 Agent Registry", `
83
- <style>${extraCss}</style>
84
- <div class="page">
85
- <div class="dash">
86
- <header class="dh">
87
- <div class="logo"><span style="font-size:24px">\u2B21</span><h1>Agent Registry</h1></div>
88
- <div style="display:flex;align-items:center;gap:12px">
89
- <span style="font-size:13px;color:#6b6560">${h(session?.tenantName || session?.name || '')}</span>
90
- <form method="POST" action="/logout" style="margin:0"><button type="submit" class="btn btn-ghost">Logout</button></form>
91
- </div>
92
- </header>
93
-
94
- <section class="sec">
95
- <h2>MCP Endpoint</h2>
96
- <p class="d">Connect any MCP-compatible agent with this URL.</p>
97
- <div class="cb">
98
- <code id="md">${h(redacted)}</code>
99
- <button class="btn-copy" id="rb" onclick="rv()">Reveal</button>
100
- <button class="btn-copy" onclick="cp('mcp')">Copy</button>
101
- </div>
102
-
103
-
104
-
105
- <div class="tabs">
106
- <div class="tab active" data-t="cc" onclick="st('cc')">Claude Code</div>
107
- <div class="tab" data-t="cd" onclick="st('cd')">Claude Desktop</div>
108
- <div class="tab" data-t="cu" onclick="st('cu')">Cursor</div>
109
- </div>
110
- <div class="tp active" data-t="cc">
111
- <p class="hint" style="margin-bottom:8px">Run in your terminal:</p>
112
- <div class="cbl"><code>${h(redactedClaudeCmd)}</code><button class="btn-copy" onclick="cp('cc')">Copy</button></div>
113
- </div>
114
- <div class="tp" data-t="cd">
115
- <p class="hint" style="margin-bottom:8px">Add to claude_desktop_config.json:</p>
116
- <div class="cbl"><code>${h(redactedDesktopJson)}</code><button class="btn-copy" onclick="cp('cd')">Copy</button></div>
117
- </div>
118
- <div class="tp" data-t="cu">
119
- <p class="hint" style="margin-bottom:8px">Settings \u2192 MCP \u2192 Add Server \u2192 paste URL:</p>
120
- <div class="cbl"><code>${h(redacted)}</code><button class="btn-copy" onclick="cp('cu')">Copy</button></div>
121
- </div>
122
- </section>
123
-
124
- <section class="sec">
125
- <h2>Integrations</h2>
126
- <p class="d">Connect services to make them available to your agents.</p>
127
- <div style="display:grid;gap:8px">
128
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#f4f3f1;border:1px solid #e4ddd6;border-radius:8px">
129
- <div style="display:flex;align-items:center;gap:10px">
130
- <svg width="20" height="20" viewBox="0 0 54 54" xmlns="http://www.w3.org/2000/svg"><path d="M19.712.133a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386h5.376V5.52A5.381 5.381 0 0 0 19.712.133m0 14.365H5.376A5.381 5.381 0 0 0 0 19.884a5.381 5.381 0 0 0 5.376 5.387h14.336a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386" fill="#36C5F0"/><path d="M53.76 19.884a5.381 5.381 0 0 0-5.376-5.386 5.381 5.381 0 0 0-5.376 5.386v5.387h5.376a5.381 5.381 0 0 0 5.376-5.387m-14.336 0V5.52A5.381 5.381 0 0 0 34.048.133a5.381 5.381 0 0 0-5.376 5.387v14.364a5.381 5.381 0 0 0 5.376 5.387 5.381 5.381 0 0 0 5.376-5.387" fill="#2EB67D"/><path d="M34.048 54a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386h-5.376v5.386A5.381 5.381 0 0 0 34.048 54m0-14.365h14.336a5.381 5.381 0 0 0 5.376-5.386 5.381 5.381 0 0 0-5.376-5.387H34.048a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386" fill="#ECB22E"/><path d="M0 34.249a5.381 5.381 0 0 0 5.376 5.386 5.381 5.381 0 0 0 5.376-5.386v-5.387H5.376A5.381 5.381 0 0 0 0 34.25m14.336 0v14.364A5.381 5.381 0 0 0 19.712 54a5.381 5.381 0 0 0 5.376-5.387V34.25a5.381 5.381 0 0 0-5.376-5.387 5.381 5.381 0 0 0-5.376 5.387" fill="#E01E5A"/></svg>
131
- <div><div style="font-size:14px;font-weight:500">Slack</div><div style="font-size:12px;color:#9c958e">Connected</div></div>
132
- </div>
133
- <span style="font-size:12px;color:#2d7a3a;background:#f0f9f1;padding:2px 8px;border-radius:4px;font-weight:500">Connected</span>
134
- </div>
135
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border:1px solid #e4ddd6;border-radius:8px">
136
- <div style="display:flex;align-items:center;gap:10px">
137
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.009-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.115 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z" fill="#1a1917"/></svg>
138
- <div><div style="font-size:14px;font-weight:500">GitHub</div><div style="font-size:12px;color:#9c958e">Code repositories</div></div>
139
- </div>
140
- <button class="btn-copy" style="font-weight:500">Connect</button>
141
- </div>
142
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border:1px solid #e4ddd6;border-radius:8px">
143
- <div style="display:flex;align-items:center;gap:10px">
144
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 6h16M4 12h16M4 18h16" stroke="#9c958e" stroke-width="2" stroke-linecap="round"/></svg>
145
- <div><div style="font-size:14px;font-weight:500">Databases</div><div style="font-size:12px;color:#9c958e">PostgreSQL, MySQL, CockroachDB</div></div>
146
- </div>
147
- <button class="btn-copy" style="font-weight:500">Connect</button>
148
- </div>
149
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border:1px solid #e4ddd6;border-radius:8px">
150
- <div style="display:flex;align-items:center;gap:10px">
151
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke="#9c958e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
152
- <div><div style="font-size:14px;font-weight:500">Linear</div><div style="font-size:12px;color:#9c958e">Issue tracking</div></div>
153
- </div>
154
- <button class="btn-copy" style="font-weight:500">Connect</button>
155
- </div>
156
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border:1px solid #e4ddd6;border-radius:8px">
157
- <div style="display:flex;align-items:center;gap:10px">
158
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 4h16v16H4V4z" stroke="#9c958e" stroke-width="2"/><path d="M9 9h6v6H9V9z" fill="#9c958e"/></svg>
159
- <div><div style="font-size:14px;font-weight:500">Notion</div><div style="font-size:12px;color:#9c958e">Docs and databases</div></div>
160
- </div>
161
- <button class="btn-copy" style="font-weight:500">Connect</button>
162
- </div>
163
- </div>
164
- </section>
165
- </div>
166
- </div>
167
- <script>${js}</script>`);
168
- }
169
- //# sourceMappingURL=web-pages.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"web-pages.js","sourceRoot":"","sources":["../src/web-pages.ts"],"names":[],"mappings":"AAAA,MAAM,GAAG,GAAG,ogGAAogG,CAAC;AAEjhG,MAAM,QAAQ,GAAG,urCAAurC,CAAC;AAEzsC,SAAS,CAAC,CAAC,CAAS;IAClB,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAC,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAC,QAAQ,CAAC,CAAC;AAClG,CAAC;AAED,SAAS,IAAI,CAAC,KAAa,EAAE,IAAY;IACvC,OAAO;;SAEA,CAAC,CAAC,KAAK,CAAC;SACR,GAAG;QACJ,IAAI,gBAAgB,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,YAAqB;IACrE,MAAM,SAAS,GAAG,YAAY;QAC5B,CAAC,CAAC,gDAAgD,QAAQ,yBAAyB;QACnF,CAAC,CAAC,0JAA0J,CAAC;IAE/J,OAAO,IAAI,CAAC,gBAAgB,EAAE;;;;;;MAM1B,SAAS;;;OAGR,CAAC,CAAC;AACT,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,KAAa,EAAE,IAAY;IAC5E,OAAO,IAAI,CAAC,kCAAkC,EAAE;;;;8BAIpB,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC;;;iDAGG,CAAC,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6B/C,CAAC,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,KAAa,EAAE,OAAgE;IAClI,MAAM,MAAM,GAAG,GAAG,OAAO,cAAc,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;IAClD,MAAM,QAAQ,GAAG,GAAG,OAAO,cAAc,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IAC1D,MAAM,SAAS,GAAG,iCAAiC,MAAM,EAAE,CAAC;IAC5D,MAAM,iBAAiB,GAAG,iCAAiC,QAAQ,EAAE,CAAC;IACtE,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,gBAAgB,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnG,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,gBAAgB,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAE7G,MAAM,EAAE,GAAG;cACC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;;;sHAGb,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;;CAEnI,CAAC;IAEA,MAAM,QAAQ,GAAG,+gBAA+gB,CAAC;IAEjiB,OAAO,IAAI,CAAC,iCAAiC,EAAE;SACxC,QAAQ;;;;;;qDAMoC,CAAC,CAAC,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC;;;;;;;;;wBAS1E,CAAC,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;iCAcF,CAAC,CAAC,iBAAiB,CAAC;;;;iCAIpB,CAAC,CAAC,mBAAmB,CAAC;;;;iCAItB,CAAC,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA+ClC,EAAE,WAAW,CAAC,CAAC;AACzB,CAAC"}
@@ -1,66 +0,0 @@
1
- /**
2
- * Slack OAuth2 (Sign in with Slack / OpenID Connect)
3
- *
4
- * Flow:
5
- * GET /auth/slack → redirect to Slack authorize
6
- * GET /auth/slack/callback → exchange code, get user identity
7
- */
8
-
9
- export interface SlackOAuthConfig {
10
- clientId: string;
11
- clientSecret: string;
12
- redirectUri: string;
13
- }
14
-
15
- const SLACK_AUTH_URL = "https://slack.com/openid/connect/authorize";
16
- const SLACK_TOKEN_URL = "https://slack.com/api/openid.connect.token";
17
- const SLACK_USERINFO_URL = "https://slack.com/api/openid.connect.userInfo";
18
-
19
- export function slackAuthUrl(config: SlackOAuthConfig, state?: string): string {
20
- const params = new URLSearchParams({
21
- client_id: config.clientId,
22
- redirect_uri: config.redirectUri,
23
- response_type: "code",
24
- scope: "openid email profile",
25
- });
26
- if (state) params.set("state", state);
27
- return `${SLACK_AUTH_URL}?${params.toString()}`;
28
- }
29
-
30
- export async function exchangeSlackCode(
31
- code: string,
32
- config: SlackOAuthConfig,
33
- ): Promise<{ access_token: string; id_token?: string }> {
34
- const res = await fetch(SLACK_TOKEN_URL, {
35
- method: "POST",
36
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
37
- body: new URLSearchParams({
38
- code,
39
- client_id: config.clientId,
40
- client_secret: config.clientSecret,
41
- redirect_uri: config.redirectUri,
42
- grant_type: "authorization_code",
43
- }),
44
- });
45
- const data = await res.json() as any;
46
- if (!data.ok) throw new Error(`Slack token exchange failed: ${data.error}`);
47
- return data;
48
- }
49
-
50
- export interface SlackProfile {
51
- sub: string; // Slack user ID
52
- email: string;
53
- name: string;
54
- picture?: string;
55
- "https://slack.com/team_id"?: string;
56
- "https://slack.com/team_name"?: string;
57
- }
58
-
59
- export async function getSlackProfile(accessToken: string): Promise<SlackProfile> {
60
- const res = await fetch(SLACK_USERINFO_URL, {
61
- headers: { Authorization: `Bearer ${accessToken}` },
62
- });
63
- const data = await res.json() as any;
64
- if (!data.ok) throw new Error(`Slack userinfo failed: ${data.error}`);
65
- return data;
66
- }
package/src/web-pages.ts DELETED
@@ -1,178 +0,0 @@
1
- const css = `*{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,'SF Pro Display','Segoe UI',sans-serif;background:#f9f8f7;color:#1a1917;-webkit-font-smoothing:antialiased}a{color:#c4982a;text-decoration:none}.page{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}.card{background:#fff;border:1px solid #e4ddd6;border-radius:10px;padding:32px;max-width:440px;width:100%;box-shadow:0 4px 6px -2px rgba(21,20,15,.1)}.logo{display:flex;align-items:center;gap:10px;margin-bottom:12px}.logo h1{font-size:20px;font-weight:600;letter-spacing:-.02em}.sub{color:#6b6560;font-size:14px;line-height:1.5;margin-bottom:28px}input[type=text]{width:100%;padding:10px 14px;background:#fff;border:1px solid #e4ddd6;border-radius:8px;color:#1a1917;font-size:14px;outline:none;margin-bottom:12px}input:focus{border-color:#c4982a;box-shadow:0 0 0 3px rgba(196,152,42,.08)}input::placeholder{color:#9c958e}label{display:block;font-size:13px;font-weight:500;color:#6b6560;margin-bottom:6px}.hint{font-size:12px;color:#9c958e;margin-bottom:16px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:10px 20px;border:none;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;width:100%;transition:all .15s}.btn-primary{background:#1a1917;color:#fff}.btn-primary:hover{background:#2d2c28}.btn-google{background:#fff;color:#1a1917;border:1px solid #e4ddd6}.btn-google:hover{background:#f4f3f1}.btn-ghost{background:transparent;color:#6b6560;border:1px solid #e4ddd6;padding:6px 16px;width:auto;font-size:13px}.btn-copy{background:#f4f3f1;color:#6b6560;border:1px solid #e4ddd6;padding:4px 10px;border-radius:6px;font-size:12px;cursor:pointer;white-space:nowrap}.btn-copy:hover{background:rgba(196,152,42,.08);color:#c4982a}.footer{text-align:center;font-size:12px;color:#9c958e;margin-top:20px}.dash{max-width:720px;width:100%}.dh{display:flex;align-items:center;justify-content:space-between;margin-bottom:32px}.sec{background:#fff;border:1px solid #e4ddd6;border-radius:10px;padding:24px;margin-bottom:16px;box-shadow:0 1px 1px rgba(21,20,15,.05)}.sec h2{font-size:15px;font-weight:600;margin-bottom:4px}.sec .d{font-size:13px;color:#6b6560;margin-bottom:16px}.cb{display:flex;align-items:center;gap:8px;background:#f4f3f1;border:1px solid #e4ddd6;border-radius:8px;padding:10px 14px}.cb code{flex:1;font-size:13px;font-family:'SF Mono',Menlo,monospace;word-break:break-all}.sg{display:grid;gap:12px}.sc{background:#f4f3f1;border:1px solid #e4ddd6;border-radius:8px;padding:16px}.sc h3{font-size:14px;font-weight:600;margin-bottom:8px}.cbl{display:flex;align-items:flex-start;gap:8px;background:#fff;border:1px solid #e4ddd6;border-radius:6px;padding:10px 12px}.cbl code{flex:1;font-size:12px;font-family:'SF Mono',Menlo,monospace;white-space:pre-wrap;word-break:break-all;color:#6b6560}.err{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c;padding:10px 12px;border-radius:8px;font-size:13px;margin-bottom:16px}.avatar{width:32px;height:32px;border-radius:50%;border:1px solid #e4ddd6}`;
2
-
3
- const slackSvg = `<svg width="18" height="18" viewBox="0 0 54 54" xmlns="http://www.w3.org/2000/svg"><path d="M19.712.133a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386h5.376V5.52A5.381 5.381 0 0 0 19.712.133m0 14.365H5.376A5.381 5.381 0 0 0 0 19.884a5.381 5.381 0 0 0 5.376 5.387h14.336a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386" fill="#36C5F0"/><path d="M53.76 19.884a5.381 5.381 0 0 0-5.376-5.386 5.381 5.381 0 0 0-5.376 5.386v5.387h5.376a5.381 5.381 0 0 0 5.376-5.387m-14.336 0V5.52A5.381 5.381 0 0 0 34.048.133a5.381 5.381 0 0 0-5.376 5.387v14.364a5.381 5.381 0 0 0 5.376 5.387 5.381 5.381 0 0 0 5.376-5.387" fill="#2EB67D"/><path d="M34.048 54a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386h-5.376v5.386A5.381 5.381 0 0 0 34.048 54m0-14.365h14.336a5.381 5.381 0 0 0 5.376-5.386 5.381 5.381 0 0 0-5.376-5.387H34.048a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386" fill="#ECB22E"/><path d="M0 34.249a5.381 5.381 0 0 0 5.376 5.386 5.381 5.381 0 0 0 5.376-5.386v-5.387H5.376A5.381 5.381 0 0 0 0 34.25m14.336 0v14.364A5.381 5.381 0 0 0 19.712 54a5.381 5.381 0 0 0 5.376-5.387V34.25a5.381 5.381 0 0 0-5.376-5.387 5.381 5.381 0 0 0-5.376 5.387" fill="#E01E5A"/></svg>`;
4
-
5
- function h(s: string): string {
6
- return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");
7
- }
8
-
9
- function wrap(title: string, body: string): string {
10
- return `<!DOCTYPE html>
11
- <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
12
- <title>${h(title)}</title>
13
- <style>${css}</style></head>
14
- <body>${body}</body></html>`;
15
- }
16
-
17
- export function renderLoginPage(_baseUrl: string, slackEnabled: boolean): string {
18
- const googleBtn = slackEnabled
19
- ? `<a href="/auth/slack" class="btn btn-google">${slackSvg} Sign in with Slack</a>`
20
- : `<p style="color:#9c958e;font-size:13px;text-align:center;padding:12px 0">Slack OAuth not configured.<br>Set SLACK_CLIENT_ID and SLACK_CLIENT_SECRET.</p>`;
21
-
22
- return wrap("Agent Registry", `
23
- <div class="page">
24
- <div class="card">
25
- <div class="logo"><span style="font-size:24px">\u2B21</span><h1>Agent Registry</h1></div>
26
- <p style="font-size:17px;font-weight:500;color:#1a1917;margin-bottom:8px">Connect your AI agents to any API.</p>
27
- <p style="font-size:13px;color:#9c958e;line-height:1.5;margin-bottom:28px">Set up integrations, manage credentials securely, and get an MCP endpoint for Claude, Cursor, and more.</p>
28
- ${googleBtn}
29
- <p class="footer">By continuing, you agree to our terms of service.</p>
30
- </div>
31
- </div>`);
32
- }
33
-
34
- export function renderTenantPage(_baseUrl: string, email: string, name: string): string {
35
- return wrap("Create Registry — Agent Registry", `
36
- <div class="page">
37
- <div class="card">
38
- <div class="logo"><span style="font-size:24px">\u2B21</span><h1>Agent Registry</h1></div>
39
- <p class="sub">Welcome, ${h(name || email)}! Choose a name for your registry.</p>
40
- <div id="err" class="err" style="display:none"></div>
41
- <form id="f">
42
- <input type="hidden" name="email" value="${h(email)}">
43
- <label>Registry name</label>
44
- <input type="text" name="tenant" placeholder="my-company" required pattern="[a-z0-9-]+" autocomplete="off" autofocus>
45
- <p class="hint">Lowercase letters, numbers, and hyphens only.</p>
46
- <button type="submit" class="btn btn-primary">Create Registry</button>
47
- </form>
48
- </div>
49
- </div>
50
- <script>
51
- document.getElementById('f').addEventListener('submit', async e => {
52
- e.preventDefault();
53
- const fd = new FormData(e.target);
54
- const btn = e.target.querySelector('[type=submit]');
55
- btn.disabled = true; btn.textContent = 'Creating...';
56
- try {
57
- const r = await fetch('/setup', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ email: fd.get('email'), tenant: fd.get('tenant') }) });
58
- const d = await r.json();
59
- if (d.success && (d.result?.token || d.result?.tenantId)) {
60
- window.location.href = '/dashboard?token=' + (d.result.token || d.result.tenantId);
61
- } else if (d.token || d.result?.token) {
62
- window.location.href = '/dashboard?token=' + (d.token || d.result.token);
63
- } else {
64
- const el = document.getElementById('err');
65
- el.textContent = d.error || d.result?.error || 'Something went wrong';
66
- el.style.display = 'block';
67
- }
68
- } catch(err) { document.getElementById('err').textContent = err.message; document.getElementById('err').style.display = 'block'; }
69
- finally { btn.disabled = false; btn.textContent = 'Create Registry'; }
70
- });
71
- </script>`);
72
- }
73
-
74
- export function renderDashboardPage(baseUrl: string, token: string, session?: { name?: string; email?: string; tenantName?: string }): string {
75
- const mcpUrl = `${baseUrl}/mcp?token=${h(token)}`;
76
- const redacted = `${baseUrl}/mcp?token=${'*'.repeat(12)}`;
77
- const claudeCmd = `claude mcp add agent-registry ${mcpUrl}`;
78
- const redactedClaudeCmd = `claude mcp add agent-registry ${redacted}`;
79
- const desktopJson = JSON.stringify({ mcpServers: { "agent-registry": { url: mcpUrl } } }, null, 2);
80
- const redactedDesktopJson = JSON.stringify({ mcpServers: { "agent-registry": { url: redacted } } }, null, 2);
81
-
82
- const js = `
83
- var _v={mcp:${JSON.stringify(mcpUrl)},cc:${JSON.stringify(claudeCmd)},cd:${JSON.stringify(desktopJson)},cu:${JSON.stringify(mcpUrl)}};
84
- var _r=false;
85
- function cp(id){navigator.clipboard.writeText(_v[id]);event.target.textContent='\u2713 Copied';setTimeout(()=>event.target.textContent='Copy',2000)}
86
- function rv(){var el=document.getElementById('md');_r=!_r;el.textContent=_r?_v.mcp:_v.mcp.split('token=')[0]+'token=${'*'.repeat(12)}';document.getElementById('rb').textContent=_r?'Hide':'Reveal'}
87
- function st(n){document.querySelectorAll('.tab').forEach(t=>t.classList.toggle('active',t.dataset.t===n));document.querySelectorAll('.tp').forEach(p=>p.classList.toggle('active',p.dataset.t===n))}
88
- `;
89
-
90
- const extraCss = `.tabs{display:flex;gap:0;border-bottom:1px solid #e4ddd6;margin-bottom:16px}.tab{padding:8px 16px;}.tabs .tab:first-child{padding-left:0}.tab{padding:8px 16px;font-size:13px;font-weight:500;color:#9c958e;cursor:pointer;border-bottom:2px solid transparent;transition:all .15s;white-space:nowrap}.tab:hover{color:#6b6560}.tab.active{color:#1a1917;border-bottom-color:#c4982a}.tp{display:none}.tp.active{display:block}.page{align-items:flex-start;padding-top:48px}.divider{border:none;border-top:1px solid #e4ddd6;margin:20px 0}`;
91
-
92
- return wrap("Dashboard \u2014 Agent Registry", `
93
- <style>${extraCss}</style>
94
- <div class="page">
95
- <div class="dash">
96
- <header class="dh">
97
- <div class="logo"><span style="font-size:24px">\u2B21</span><h1>Agent Registry</h1></div>
98
- <div style="display:flex;align-items:center;gap:12px">
99
- <span style="font-size:13px;color:#6b6560">${h(session?.tenantName || session?.name || '')}</span>
100
- <form method="POST" action="/logout" style="margin:0"><button type="submit" class="btn btn-ghost">Logout</button></form>
101
- </div>
102
- </header>
103
-
104
- <section class="sec">
105
- <h2>MCP Endpoint</h2>
106
- <p class="d">Connect any MCP-compatible agent with this URL.</p>
107
- <div class="cb">
108
- <code id="md">${h(redacted)}</code>
109
- <button class="btn-copy" id="rb" onclick="rv()">Reveal</button>
110
- <button class="btn-copy" onclick="cp('mcp')">Copy</button>
111
- </div>
112
-
113
-
114
-
115
- <div class="tabs">
116
- <div class="tab active" data-t="cc" onclick="st('cc')">Claude Code</div>
117
- <div class="tab" data-t="cd" onclick="st('cd')">Claude Desktop</div>
118
- <div class="tab" data-t="cu" onclick="st('cu')">Cursor</div>
119
- </div>
120
- <div class="tp active" data-t="cc">
121
- <p class="hint" style="margin-bottom:8px">Run in your terminal:</p>
122
- <div class="cbl"><code>${h(redactedClaudeCmd)}</code><button class="btn-copy" onclick="cp('cc')">Copy</button></div>
123
- </div>
124
- <div class="tp" data-t="cd">
125
- <p class="hint" style="margin-bottom:8px">Add to claude_desktop_config.json:</p>
126
- <div class="cbl"><code>${h(redactedDesktopJson)}</code><button class="btn-copy" onclick="cp('cd')">Copy</button></div>
127
- </div>
128
- <div class="tp" data-t="cu">
129
- <p class="hint" style="margin-bottom:8px">Settings \u2192 MCP \u2192 Add Server \u2192 paste URL:</p>
130
- <div class="cbl"><code>${h(redacted)}</code><button class="btn-copy" onclick="cp('cu')">Copy</button></div>
131
- </div>
132
- </section>
133
-
134
- <section class="sec">
135
- <h2>Integrations</h2>
136
- <p class="d">Connect services to make them available to your agents.</p>
137
- <div style="display:grid;gap:8px">
138
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#f4f3f1;border:1px solid #e4ddd6;border-radius:8px">
139
- <div style="display:flex;align-items:center;gap:10px">
140
- <svg width="20" height="20" viewBox="0 0 54 54" xmlns="http://www.w3.org/2000/svg"><path d="M19.712.133a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386h5.376V5.52A5.381 5.381 0 0 0 19.712.133m0 14.365H5.376A5.381 5.381 0 0 0 0 19.884a5.381 5.381 0 0 0 5.376 5.387h14.336a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386" fill="#36C5F0"/><path d="M53.76 19.884a5.381 5.381 0 0 0-5.376-5.386 5.381 5.381 0 0 0-5.376 5.386v5.387h5.376a5.381 5.381 0 0 0 5.376-5.387m-14.336 0V5.52A5.381 5.381 0 0 0 34.048.133a5.381 5.381 0 0 0-5.376 5.387v14.364a5.381 5.381 0 0 0 5.376 5.387 5.381 5.381 0 0 0 5.376-5.387" fill="#2EB67D"/><path d="M34.048 54a5.381 5.381 0 0 0 5.376-5.387 5.381 5.381 0 0 0-5.376-5.386h-5.376v5.386A5.381 5.381 0 0 0 34.048 54m0-14.365h14.336a5.381 5.381 0 0 0 5.376-5.386 5.381 5.381 0 0 0-5.376-5.387H34.048a5.381 5.381 0 0 0-5.376 5.387 5.381 5.381 0 0 0 5.376 5.386" fill="#ECB22E"/><path d="M0 34.249a5.381 5.381 0 0 0 5.376 5.386 5.381 5.381 0 0 0 5.376-5.386v-5.387H5.376A5.381 5.381 0 0 0 0 34.25m14.336 0v14.364A5.381 5.381 0 0 0 19.712 54a5.381 5.381 0 0 0 5.376-5.387V34.25a5.381 5.381 0 0 0-5.376-5.387 5.381 5.381 0 0 0-5.376 5.387" fill="#E01E5A"/></svg>
141
- <div><div style="font-size:14px;font-weight:500">Slack</div><div style="font-size:12px;color:#9c958e">Connected</div></div>
142
- </div>
143
- <span style="font-size:12px;color:#2d7a3a;background:#f0f9f1;padding:2px 8px;border-radius:4px;font-weight:500">Connected</span>
144
- </div>
145
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border:1px solid #e4ddd6;border-radius:8px">
146
- <div style="display:flex;align-items:center;gap:10px">
147
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.009-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.115 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z" fill="#1a1917"/></svg>
148
- <div><div style="font-size:14px;font-weight:500">GitHub</div><div style="font-size:12px;color:#9c958e">Code repositories</div></div>
149
- </div>
150
- <button class="btn-copy" style="font-weight:500">Connect</button>
151
- </div>
152
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border:1px solid #e4ddd6;border-radius:8px">
153
- <div style="display:flex;align-items:center;gap:10px">
154
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 6h16M4 12h16M4 18h16" stroke="#9c958e" stroke-width="2" stroke-linecap="round"/></svg>
155
- <div><div style="font-size:14px;font-weight:500">Databases</div><div style="font-size:12px;color:#9c958e">PostgreSQL, MySQL, CockroachDB</div></div>
156
- </div>
157
- <button class="btn-copy" style="font-weight:500">Connect</button>
158
- </div>
159
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border:1px solid #e4ddd6;border-radius:8px">
160
- <div style="display:flex;align-items:center;gap:10px">
161
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke="#9c958e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
162
- <div><div style="font-size:14px;font-weight:500">Linear</div><div style="font-size:12px;color:#9c958e">Issue tracking</div></div>
163
- </div>
164
- <button class="btn-copy" style="font-weight:500">Connect</button>
165
- </div>
166
- <div style="display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border:1px solid #e4ddd6;border-radius:8px">
167
- <div style="display:flex;align-items:center;gap:10px">
168
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 4h16v16H4V4z" stroke="#9c958e" stroke-width="2"/><path d="M9 9h6v6H9V9z" fill="#9c958e"/></svg>
169
- <div><div style="font-size:14px;font-weight:500">Notion</div><div style="font-size:12px;color:#9c958e">Docs and databases</div></div>
170
- </div>
171
- <button class="btn-copy" style="font-weight:500">Connect</button>
172
- </div>
173
- </div>
174
- </section>
175
- </div>
176
- </div>
177
- <script>${js}</script>`);
178
- }