@mugwork/mug 0.1.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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/dist/explorer.js +3 -0
  4. package/dist/packages/email-template/src/email-template.d.ts +18 -0
  5. package/dist/packages/email-template/src/email-template.js +74 -0
  6. package/dist/packages/email-template/src/index.d.ts +1 -0
  7. package/dist/packages/email-template/src/index.js +1 -0
  8. package/dist/packages/surface-renderer/src/form-renderer.d.ts +117 -0
  9. package/dist/packages/surface-renderer/src/form-renderer.js +719 -0
  10. package/dist/packages/surface-renderer/src/index.d.ts +4 -0
  11. package/dist/packages/surface-renderer/src/index.js +2 -0
  12. package/dist/packages/surface-renderer/src/portal-renderer.d.ts +177 -0
  13. package/dist/packages/surface-renderer/src/portal-renderer.js +1089 -0
  14. package/dist/packages/surface-renderer/src/workspace-home.d.ts +46 -0
  15. package/dist/packages/surface-renderer/src/workspace-home.js +345 -0
  16. package/dist/runtime/agent-types.d.ts +48 -0
  17. package/dist/runtime/agent-types.js +3 -0
  18. package/dist/runtime/ai-router.d.ts +32 -0
  19. package/dist/runtime/ai-router.js +112 -0
  20. package/dist/runtime/app.d.ts +6 -0
  21. package/dist/runtime/app.js +399 -0
  22. package/dist/runtime/chunker.d.ts +6 -0
  23. package/dist/runtime/chunker.js +30 -0
  24. package/dist/runtime/context.d.ts +115 -0
  25. package/dist/runtime/context.js +440 -0
  26. package/dist/runtime/do/workspace-database.d.ts +10 -0
  27. package/dist/runtime/do/workspace-database.js +199 -0
  28. package/dist/runtime/form-types.d.ts +143 -0
  29. package/dist/runtime/form-types.js +1 -0
  30. package/dist/runtime/runtime.d.ts +9 -0
  31. package/dist/runtime/runtime.js +7 -0
  32. package/dist/runtime/source-types.d.ts +15 -0
  33. package/dist/runtime/source-types.js +1 -0
  34. package/dist/runtime/source.d.ts +70 -0
  35. package/dist/runtime/source.js +21 -0
  36. package/dist/runtime/sync-runtime.d.ts +10 -0
  37. package/dist/runtime/sync-runtime.js +185 -0
  38. package/dist/runtime/types.d.ts +21 -0
  39. package/dist/runtime/types.js +1 -0
  40. package/dist/runtime/workflow-entrypoint.d.ts +31 -0
  41. package/dist/runtime/workflow-entrypoint.js +1297 -0
  42. package/dist/runtime/workflow.d.ts +285 -0
  43. package/dist/runtime/workflow.js +1008 -0
  44. package/dist/src/cli.d.ts +2 -0
  45. package/dist/src/cli.js +44116 -0
  46. package/dist/src/commands/ai-gateway-route.d.ts +24 -0
  47. package/dist/src/commands/ai-gateway-route.js +192 -0
  48. package/dist/src/commands/auth.d.ts +1 -0
  49. package/dist/src/commands/auth.js +42 -0
  50. package/dist/src/commands/billing.d.ts +6 -0
  51. package/dist/src/commands/billing.js +76 -0
  52. package/dist/src/commands/brain.d.ts +1 -0
  53. package/dist/src/commands/brain.js +194 -0
  54. package/dist/src/commands/demo.d.ts +12 -0
  55. package/dist/src/commands/demo.js +147 -0
  56. package/dist/src/commands/deploy.d.ts +1 -0
  57. package/dist/src/commands/deploy.js +1052 -0
  58. package/dist/src/commands/dev.d.ts +14 -0
  59. package/dist/src/commands/dev.js +2818 -0
  60. package/dist/src/commands/form.d.ts +8 -0
  61. package/dist/src/commands/form.js +396 -0
  62. package/dist/src/commands/init.d.ts +1 -0
  63. package/dist/src/commands/init.js +139 -0
  64. package/dist/src/commands/issue.d.ts +7 -0
  65. package/dist/src/commands/issue.js +191 -0
  66. package/dist/src/commands/login.d.ts +9 -0
  67. package/dist/src/commands/login.js +163 -0
  68. package/dist/src/commands/logs.d.ts +8 -0
  69. package/dist/src/commands/logs.js +113 -0
  70. package/dist/src/commands/portal.d.ts +2 -0
  71. package/dist/src/commands/portal.js +111 -0
  72. package/dist/src/commands/pull.d.ts +3 -0
  73. package/dist/src/commands/pull.js +184 -0
  74. package/dist/src/commands/push.d.ts +4 -0
  75. package/dist/src/commands/push.js +183 -0
  76. package/dist/src/commands/run.d.ts +6 -0
  77. package/dist/src/commands/run.js +91 -0
  78. package/dist/src/commands/secret.d.ts +7 -0
  79. package/dist/src/commands/secret.js +105 -0
  80. package/dist/src/commands/shutdown.d.ts +1 -0
  81. package/dist/src/commands/shutdown.js +46 -0
  82. package/dist/src/commands/sql.d.ts +8 -0
  83. package/dist/src/commands/sql.js +142 -0
  84. package/dist/src/commands/status.d.ts +5 -0
  85. package/dist/src/commands/status.js +39 -0
  86. package/dist/src/commands/sync.d.ts +7 -0
  87. package/dist/src/commands/sync.js +991 -0
  88. package/dist/src/commands/usage.d.ts +6 -0
  89. package/dist/src/commands/usage.js +78 -0
  90. package/dist/src/commands/webhooks.d.ts +1 -0
  91. package/dist/src/commands/webhooks.js +102 -0
  92. package/dist/src/commands/workspace.d.ts +23 -0
  93. package/dist/src/commands/workspace.js +590 -0
  94. package/dist/src/connector-migration.d.ts +20 -0
  95. package/dist/src/connector-migration.js +43 -0
  96. package/dist/src/connector-parser.d.ts +14 -0
  97. package/dist/src/connector-parser.js +94 -0
  98. package/dist/src/connector-service/discover.d.ts +37 -0
  99. package/dist/src/connector-service/discover.js +79 -0
  100. package/dist/src/connector-service/gather.d.ts +22 -0
  101. package/dist/src/connector-service/gather.js +89 -0
  102. package/dist/src/connector-service/init.d.ts +14 -0
  103. package/dist/src/connector-service/init.js +109 -0
  104. package/dist/src/connector-service/scaffold.d.ts +17 -0
  105. package/dist/src/connector-service/scaffold.js +194 -0
  106. package/dist/src/connector-service/spec-storage.d.ts +8 -0
  107. package/dist/src/connector-service/spec-storage.js +48 -0
  108. package/dist/src/connector-service/types.d.ts +57 -0
  109. package/dist/src/connector-service/types.js +2 -0
  110. package/dist/src/connector-service/verify.d.ts +24 -0
  111. package/dist/src/connector-service/verify.js +575 -0
  112. package/dist/src/email-template.d.ts +2 -0
  113. package/dist/src/email-template.js +1 -0
  114. package/dist/src/manifest.d.ts +31 -0
  115. package/dist/src/manifest.js +25 -0
  116. package/dist/src/mug-icon.d.ts +1 -0
  117. package/dist/src/mug-icon.js +12 -0
  118. package/dist/src/slack-manifest.d.ts +119 -0
  119. package/dist/src/slack-manifest.js +163 -0
  120. package/dist/src/source-migration.d.ts +20 -0
  121. package/dist/src/source-migration.js +43 -0
  122. package/dist/src/surface-renderer.d.ts +5 -0
  123. package/dist/src/surface-renderer.js +3 -0
  124. package/dist/src/templates.d.ts +3 -0
  125. package/dist/src/templates.js +48 -0
  126. package/dist/src/version-check.d.ts +1 -0
  127. package/dist/src/version-check.js +28 -0
  128. package/dist/src/workflow-parser.d.ts +95 -0
  129. package/dist/src/workflow-parser.js +526 -0
  130. package/dist/worker/src/agent-types.d.ts +27 -0
  131. package/dist/worker/src/agent-types.js +3 -0
  132. package/dist/worker/src/source-types.d.ts +14 -0
  133. package/dist/worker/src/source-types.js +1 -0
  134. package/package.json +90 -0
  135. package/src/data/model-capabilities.json +171 -0
@@ -0,0 +1,46 @@
1
+ import { type BrandingConfig } from "./form-renderer.js";
2
+ interface WorkspaceHomeConfig {
3
+ workspace: string;
4
+ title?: string;
5
+ description?: string;
6
+ accentColor?: string | null;
7
+ branding?: BrandingConfig;
8
+ }
9
+ interface HomeScreenSurface {
10
+ surfaceId: string;
11
+ title: string;
12
+ description?: string;
13
+ isPublic: boolean;
14
+ }
15
+ interface HomeScreenGroup {
16
+ label?: string;
17
+ description?: string;
18
+ color?: string;
19
+ icon?: string;
20
+ collapsible?: boolean;
21
+ collapsed?: boolean;
22
+ buttons?: {
23
+ surface: string;
24
+ label?: string;
25
+ color?: string;
26
+ }[];
27
+ cards?: {
28
+ surface: string;
29
+ label?: string;
30
+ description?: string;
31
+ color?: string;
32
+ icon?: string;
33
+ }[];
34
+ }
35
+ interface HomeScreenLayout {
36
+ title?: string;
37
+ description?: string;
38
+ access?: {
39
+ mode: string;
40
+ };
41
+ showLogin?: boolean;
42
+ groups: HomeScreenGroup[];
43
+ }
44
+ export declare function renderWorkspaceAuth(config: WorkspaceHomeConfig): string;
45
+ export declare function renderWorkspaceHome(config: WorkspaceHomeConfig, surfaces: HomeScreenSurface[], identity: string, layout?: HomeScreenLayout | null, isDemo?: boolean, showLogin?: boolean): string;
46
+ export {};
@@ -0,0 +1,345 @@
1
+ import { esc, renderMetaTags } from "./form-renderer.js";
2
+ function titleCase(slug) {
3
+ return slug.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
4
+ }
5
+ export function renderWorkspaceAuth(config) {
6
+ const { workspace, accentColor, branding } = config;
7
+ const displayTitle = config.title ?? titleCase(workspace);
8
+ const logoSrc = branding?.logo ?? branding?.logoSquare;
9
+ const accentVar = accentColor ? `<style>:root { --accent: ${esc(accentColor)}; }</style>` : "";
10
+ const ogImageUrl = branding?.ogImage ?? "/_og-image.png";
11
+ const metaTags = renderMetaTags({ title: displayTitle, description: config.description, ogImageUrl });
12
+ return `<!DOCTYPE html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="utf-8">
16
+ <meta name="viewport" content="width=device-width, initial-scale=1">
17
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
18
+ <title>${esc(displayTitle)}</title>
19
+ ${metaTags}
20
+ <style>${HOME_CSS}</style>
21
+ ${accentVar}
22
+ </head>
23
+ <body>
24
+ <div class="container">
25
+ <header>
26
+ ${logoSrc ? `<img src="${esc(logoSrc)}" alt="" class="brand-logo">` : ""}
27
+ <div class="workspace-name">${esc(displayTitle)}</div>
28
+ ${config.description ? `<p class="desc">${esc(config.description)}</p>` : ""}
29
+ </header>
30
+
31
+ <div id="auth-gate">
32
+ <div class="auth-box">
33
+ <div id="email-form">
34
+ <label for="auth-input">Enter your email</label>
35
+ <input type="email" id="auth-input" placeholder="you@example.com" required autocomplete="email">
36
+ <button type="button" id="auth-submit" class="btn btn-primary">Continue</button>
37
+ <button type="button" id="toggle-phone" class="toggle-method">Login with phone instead</button>
38
+ </div>
39
+ <div id="phone-form" style="display:none">
40
+ <label for="phone-input">Enter your phone number</label>
41
+ <input type="tel" id="phone-input" placeholder="+1 555 123 4567" required autocomplete="tel">
42
+ <button type="button" id="phone-submit" class="btn btn-primary">Continue</button>
43
+ <button type="button" id="toggle-email" class="toggle-method">Login with email instead</button>
44
+ </div>
45
+ <div id="auth-msg" class="msg"></div>
46
+ </div>
47
+ <div id="code-box" class="auth-box" style="display:none">
48
+ <label for="code-input">Enter verification code</label>
49
+ <input type="text" id="code-input" placeholder="123456" maxlength="6" inputmode="numeric" pattern="[0-9]*">
50
+ <button type="button" id="code-submit" class="btn btn-primary">Verify</button>
51
+ <div id="code-msg" class="msg"></div>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ <script>${HOME_AUTH_JS}</script>
56
+ </body>
57
+ </html>`;
58
+ }
59
+ export function renderWorkspaceHome(config, surfaces, identity, layout, isDemo, showLogin) {
60
+ const { workspace, accentColor, branding } = config;
61
+ const displayTitle = layout?.title ?? config.title ?? titleCase(workspace);
62
+ const displayDesc = layout?.description ?? config.description;
63
+ const logoSrc = branding?.logo ?? branding?.logoSquare;
64
+ const accentVar = accentColor ? `<style>:root { --accent: ${esc(accentColor)}; }</style>` : "";
65
+ const ogImageUrl = branding?.ogImage ?? "/_og-image.png";
66
+ const metaTags = renderMetaTags({ title: displayTitle, description: displayDesc, ogImageUrl });
67
+ const surfaceMap = new Map(surfaces.map(s => [s.surfaceId, s]));
68
+ let bodyHtml = "";
69
+ if (layout?.groups?.length) {
70
+ const placed = new Set();
71
+ for (const group of layout.groups) {
72
+ const groupStyle = group.color ? ` style="border-color: ${esc(group.color)}; background: color-mix(in srgb, ${esc(group.color)} 12%, #fff); --group-color: ${esc(group.color)}"` : "";
73
+ const useDetails = !!group.collapsible;
74
+ const openAttr = group.collapsed ? "" : " open";
75
+ const groupIcon = group.icon ? `<i data-lucide="${esc(group.icon)}" class="group-icon"></i>` : "";
76
+ if (useDetails) {
77
+ bodyHtml += `<details class="group"${groupStyle}${openAttr}>`;
78
+ bodyHtml += `<summary class="group-header"><h2>${groupIcon}${esc(group.label)}</h2>`;
79
+ if (group.description)
80
+ bodyHtml += `<p class="group-desc">${esc(group.description)}</p>`;
81
+ bodyHtml += `</summary>`;
82
+ }
83
+ else {
84
+ bodyHtml += `<div class="group"${groupStyle}>`;
85
+ if (group.label) {
86
+ bodyHtml += `<div class="group-header"><h2>${groupIcon}${esc(group.label)}</h2>`;
87
+ if (group.description)
88
+ bodyHtml += `<p class="group-desc">${esc(group.description)}</p>`;
89
+ bodyHtml += `</div>`;
90
+ }
91
+ }
92
+ const bodyContent = [];
93
+ if (group.buttons?.length) {
94
+ let btnHtml = `<div class="button-row">`;
95
+ for (const btn of group.buttons) {
96
+ const surface = surfaceMap.get(btn.surface);
97
+ if (!surface)
98
+ continue;
99
+ placed.add(btn.surface);
100
+ const label = btn.label ?? surface.title;
101
+ const btnStyle = btn.color ? ` style="background: ${esc(btn.color)}"` : "";
102
+ btnHtml += `<a href="/${esc(surface.surfaceId)}?from=/&amp;fromLabel=Home" class="surface-btn"${btnStyle}>${esc(label)}</a>`;
103
+ }
104
+ btnHtml += `</div>`;
105
+ bodyContent.push(btnHtml);
106
+ }
107
+ if (group.cards?.length) {
108
+ let cardHtml = `<div class="card-list">`;
109
+ for (const card of group.cards) {
110
+ const surface = surfaceMap.get(card.surface);
111
+ if (!surface)
112
+ continue;
113
+ placed.add(card.surface);
114
+ const label = card.label ?? surface.title;
115
+ const desc = card.description ?? surface.description;
116
+ const cardStyle = card.color ? ` style="border-color: ${esc(card.color)}; --card-color: ${esc(card.color)}"` : "";
117
+ const cardIcon = card.icon ? `<i data-lucide="${esc(card.icon)}" class="card-icon"></i>` : "";
118
+ cardHtml += `<a href="/${esc(surface.surfaceId)}?from=/&amp;fromLabel=Home" class="surface-card"${cardStyle}>`;
119
+ cardHtml += `<div class="card-title">${cardIcon}${esc(label)}</div>`;
120
+ if (desc)
121
+ cardHtml += `<div class="card-desc">${esc(desc)}</div>`;
122
+ cardHtml += `</a>`;
123
+ }
124
+ cardHtml += `</div>`;
125
+ bodyContent.push(cardHtml);
126
+ }
127
+ if (useDetails) {
128
+ bodyHtml += `<div class="group-body">${bodyContent.join("")}</div></details>`;
129
+ }
130
+ else {
131
+ bodyHtml += bodyContent.join("") + `</div>`;
132
+ }
133
+ }
134
+ const remaining = surfaces.filter(s => !placed.has(s.surfaceId));
135
+ if (remaining.length) {
136
+ bodyHtml += `<div class="group"><div class="card-list">`;
137
+ for (const s of remaining) {
138
+ bodyHtml += `<a href="/${esc(s.surfaceId)}?from=/&amp;fromLabel=Home" class="surface-card">`;
139
+ bodyHtml += `<div class="card-title">${esc(s.title)}</div>`;
140
+ if (s.description)
141
+ bodyHtml += `<div class="card-desc">${esc(s.description)}</div>`;
142
+ bodyHtml += `</a>`;
143
+ }
144
+ bodyHtml += `</div></div>`;
145
+ }
146
+ }
147
+ else {
148
+ bodyHtml += `<div class="card-list">`;
149
+ for (const s of surfaces) {
150
+ bodyHtml += `<a href="/${esc(s.surfaceId)}?from=/&amp;fromLabel=Home" class="surface-card">`;
151
+ bodyHtml += `<div class="card-title">${esc(s.title)}</div>`;
152
+ if (s.description)
153
+ bodyHtml += `<div class="card-desc">${esc(s.description)}</div>`;
154
+ bodyHtml += `</a>`;
155
+ }
156
+ bodyHtml += `</div>`;
157
+ }
158
+ return `<!DOCTYPE html>
159
+ <html lang="en">
160
+ <head>
161
+ <meta charset="utf-8">
162
+ <meta name="viewport" content="width=device-width, initial-scale=1">
163
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
164
+ <title>${esc(displayTitle)}</title>
165
+ ${metaTags}
166
+ <style>${HOME_CSS}</style>
167
+ ${accentVar}
168
+ </head>
169
+ <body>
170
+ <div class="container">
171
+ <header>
172
+ ${logoSrc ? `<img src="${esc(logoSrc)}" alt="" class="brand-logo">` : ""}
173
+ <h1>${esc(displayTitle)}</h1>
174
+ ${displayDesc ? `<p class="desc">${esc(displayDesc)}</p>` : ""}
175
+ ${identity ? `<div class="header-meta">
176
+ <div class="session">${esc(identity)}</div>
177
+ ${!isDemo ? `<form method="POST" action="/logout"><button type="submit" class="logout-btn"><i data-lucide="log-out"></i> Log out</button></form>` : ""}
178
+ </div>` : showLogin ? `<div class="header-meta">
179
+ <a href="/?login=true" class="login-btn"><i data-lucide="log-in"></i> Log in</a>
180
+ </div>` : ""}
181
+ </header>
182
+ ${bodyHtml}
183
+ </div>
184
+ <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
185
+ <script>lucide.createIcons();</script>
186
+ </body>
187
+ </html>`;
188
+ }
189
+ const HOME_CSS = `
190
+ :root { --accent: #1a1a1a; }
191
+ * { box-sizing: border-box; margin: 0; padding: 0; }
192
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; color: #1a1a1a; line-height: 1.5; }
193
+ .container { max-width: 560px; margin: 0 auto; padding: 24px 16px; }
194
+ header { margin-bottom: 24px; position: relative; }
195
+ .brand-logo { display: block; max-height: 72px; max-width: 240px; margin-bottom: 12px; }
196
+ h1 { font-size: 24px; font-weight: 600; margin-bottom: 4px; }
197
+ .desc { color: #666; font-size: 14px; margin-bottom: 4px; }
198
+ .header-meta { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
199
+ .session { font-size: 13px; color: #666; background: #f0f0f0; padding: 6px 12px; border-radius: 6px; display: inline-block; }
200
+ .logout-btn { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border: none; background: #f0f0f0; border-radius: 6px; cursor: pointer; color: #666; font-size: 13px; font-family: inherit; transition: background 0.15s, color 0.15s; }
201
+ .logout-btn:hover { background: #e0e0e0; color: #333; }
202
+ .logout-btn svg { width: 16px; height: 16px; }
203
+ .login-btn { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; background: #f0f0f0; border-radius: 6px; color: #666; font-size: 13px; text-decoration: none; transition: background 0.15s, color 0.15s; }
204
+ .login-btn:hover { background: #e0e0e0; color: #333; }
205
+ .login-btn svg { width: 16px; height: 16px; }
206
+
207
+ #auth-gate label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 8px; }
208
+ #auth-gate input[type="email"], #auth-gate input[type="tel"], #auth-gate input[type="text"] {
209
+ width: 100%; padding: 10px 12px; border: 1px solid #ddd; border-radius: 8px;
210
+ font-size: 16px; font-family: inherit; background: #fff; transition: border-color 0.15s; margin-bottom: 12px;
211
+ }
212
+ #auth-gate input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 15%, transparent); }
213
+ .btn { display: block; width: 100%; padding: 12px 24px; border: none; border-radius: 8px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background 0.15s; }
214
+ .btn-primary { background: var(--accent); color: #fff; }
215
+ .btn-primary:hover { background: color-mix(in srgb, var(--accent) 80%, #000); }
216
+ .btn-primary:disabled { background: #999; cursor: not-allowed; }
217
+ .toggle-method { display: block; width: 100%; margin-top: 12px; padding: 8px; border: none; background: none; color: #666; font-size: 13px; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
218
+ .toggle-method:hover { color: var(--accent); }
219
+
220
+ .auth-box { background: #fff; padding: 24px; border-radius: 12px; margin-bottom: 16px; }
221
+ .msg { font-size: 14px; color: #666; margin-top: 12px; }
222
+
223
+ .group { margin-bottom: 16px; padding: 16px; background: #fff; border-radius: 12px; border: 1px solid #e8e8e8; }
224
+ details.group { padding: 0; }
225
+ details.group > .group-header { padding: 16px; margin: 0; cursor: pointer; list-style: none; user-select: none; }
226
+ details.group > .group-header::-webkit-details-marker { display: none; }
227
+ details.group > .group-header h2::after { content: ''; display: inline-block; width: 8px; height: 8px; border-right: 2px solid var(--group-color, #999); border-bottom: 2px solid var(--group-color, #999); transform: rotate(45deg); margin-left: auto; flex-shrink: 0; transition: transform 0.2s; }
228
+ details[open].group > .group-header h2::after { transform: rotate(-135deg); }
229
+ .group-body { padding: 0 16px 16px; }
230
+ .group-header { margin-bottom: 12px; }
231
+ .group-header h2 { font-size: 16px; font-weight: 600; color: var(--group-color, #1a1a1a); display: flex; align-items: center; gap: 8px; }
232
+ .group-icon { width: 20px; height: 20px; flex-shrink: 0; color: var(--group-color, #666); }
233
+ .group-desc { font-size: 13px; color: #666; margin-top: 2px; }
234
+ .card-icon { width: 16px; height: 16px; flex-shrink: 0; color: var(--group-color, #666); }
235
+
236
+ .button-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
237
+ .surface-btn {
238
+ display: inline-flex; align-items: center; padding: 10px 16px;
239
+ background: var(--accent); color: #fff; border-radius: 8px;
240
+ font-size: 14px; font-weight: 500; text-decoration: none; transition: background 0.15s;
241
+ }
242
+ .surface-btn:hover { background: color-mix(in srgb, var(--accent) 80%, #000); }
243
+
244
+ .card-list { display: flex; flex-direction: column; gap: 8px; }
245
+ .surface-card {
246
+ display: block; padding: 16px; background: #f9f9f9; border-radius: 10px;
247
+ text-decoration: none; color: inherit; transition: box-shadow 0.15s, background 0.15s;
248
+ border: 1px solid #eee;
249
+ }
250
+ .group[style*="--group-color"] .surface-card { border-color: var(--group-color); background: color-mix(in srgb, var(--group-color) 6%, #fff); }
251
+ .surface-card[style*="border-color"] { background: color-mix(in srgb, var(--card-color) 6%, #fff); }
252
+ .surface-card:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.06); filter: brightness(0.97); }
253
+ .card-title { font-size: 15px; font-weight: 500; display: flex; align-items: center; gap: 6px; }
254
+ .card-desc { font-size: 13px; color: #666; margin-top: 4px; }
255
+
256
+ @media (max-width: 480px) {
257
+ .container { padding: 16px 12px; }
258
+ .btn { padding: 14px 20px; }
259
+ .surface-btn { padding: 12px 16px; font-size: 15px; }
260
+ .surface-card { padding: 14px; }
261
+ }
262
+ `;
263
+ const HOME_AUTH_JS = `
264
+ (function() {
265
+ var emailForm = document.getElementById('email-form');
266
+ var phoneForm = document.getElementById('phone-form');
267
+ var emailInput = document.getElementById('auth-input');
268
+ var phoneInput = document.getElementById('phone-input');
269
+ var emailBtn = document.getElementById('auth-submit');
270
+ var phoneBtn = document.getElementById('phone-submit');
271
+ var togglePhone = document.getElementById('toggle-phone');
272
+ var toggleEmail = document.getElementById('toggle-email');
273
+ var msg = document.getElementById('auth-msg');
274
+ var codeBox = document.getElementById('code-box');
275
+ var codeInput = document.getElementById('code-input');
276
+ var codeBtn = document.getElementById('code-submit');
277
+ var codeMsg = document.getElementById('code-msg');
278
+ var currentMethod = 'email';
279
+ var identifier = '';
280
+
281
+ togglePhone.addEventListener('click', function() {
282
+ emailForm.style.display = 'none';
283
+ phoneForm.style.display = '';
284
+ phoneInput.focus();
285
+ msg.textContent = '';
286
+ currentMethod = 'phone';
287
+ });
288
+
289
+ toggleEmail.addEventListener('click', function() {
290
+ phoneForm.style.display = 'none';
291
+ emailForm.style.display = '';
292
+ emailInput.focus();
293
+ msg.textContent = '';
294
+ currentMethod = 'email';
295
+ });
296
+
297
+ function submitAuth(input, btn, method) {
298
+ identifier = input.value.trim();
299
+ if (!identifier) return;
300
+ btn.disabled = true;
301
+ msg.textContent = 'Sending...';
302
+ fetch('/auth', {
303
+ method: 'POST',
304
+ headers: { 'Content-Type': 'application/json' },
305
+ body: JSON.stringify({ identifier: identifier, method: method }),
306
+ credentials: 'include'
307
+ }).then(function(r) { return r.json(); }).then(function(d) {
308
+ btn.disabled = false;
309
+ if (d.status === 'sent') {
310
+ if (method === 'email') {
311
+ msg.textContent = 'Check your inbox. If this email has access to this workspace, you\\u2019ll receive a login email.';
312
+ } else {
313
+ msg.textContent = 'If this number has access, you\\u2019ll receive a code.';
314
+ codeBox.style.display = '';
315
+ codeInput.focus();
316
+ }
317
+ }
318
+ }).catch(function() { btn.disabled = false; msg.textContent = 'Error. Try again.'; });
319
+ }
320
+
321
+ emailBtn.addEventListener('click', function() { submitAuth(emailInput, emailBtn, 'email'); });
322
+ phoneBtn.addEventListener('click', function() { submitAuth(phoneInput, phoneBtn, 'phone'); });
323
+
324
+ emailInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') emailBtn.click(); });
325
+ phoneInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') phoneBtn.click(); });
326
+
327
+ codeBtn.addEventListener('click', function() {
328
+ var code = codeInput.value.trim();
329
+ if (!code) return;
330
+ codeBtn.disabled = true;
331
+ codeMsg.textContent = 'Verifying...';
332
+ fetch('/verify-code', {
333
+ method: 'POST',
334
+ headers: { 'Content-Type': 'application/json' },
335
+ body: JSON.stringify({ identifier: identifier, code: code }),
336
+ credentials: 'include'
337
+ }).then(function(r) {
338
+ if (r.ok) { location.reload(); }
339
+ else { return r.json().then(function(d) { codeBtn.disabled = false; codeMsg.textContent = d.error || 'Invalid code.'; }); }
340
+ }).catch(function() { codeBtn.disabled = false; codeMsg.textContent = 'Error. Try again.'; });
341
+ });
342
+
343
+ codeInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') codeBtn.click(); });
344
+ })();
345
+ `;
@@ -0,0 +1,48 @@
1
+ export type AgentModel = "claude-sonnet" | "claude-haiku" | "claude-opus" | "gpt-4o" | "gpt-4o-mini" | "gpt-4.1-nano" | "gpt-4.1-mini" | "gpt-4.1" | (string & {});
2
+ export interface AgentTierRouting {
3
+ fast?: string;
4
+ balanced?: string;
5
+ powerful?: string;
6
+ }
7
+ export type AgentMemory = boolean | {
8
+ entities?: boolean;
9
+ outcomes?: boolean;
10
+ struggles?: boolean;
11
+ };
12
+ export type AgentToolGrant = "query" | "search" | "ask" | "notify" | "http" | "workspace" | "ai" | "trigger_workflow" | (string & {});
13
+ export interface AgentCaps {
14
+ maxTurns?: number;
15
+ maxCredits?: number;
16
+ maxDuration?: number;
17
+ }
18
+ export interface AgentEmailFilter {
19
+ allowDomains?: string[];
20
+ blockDomains?: string[];
21
+ requireSubject?: boolean;
22
+ }
23
+ export interface AgentEmailCategory {
24
+ name: string;
25
+ prompt: string;
26
+ reply?: boolean;
27
+ }
28
+ export interface AgentEmailConfig {
29
+ enabled: boolean;
30
+ address?: string;
31
+ filter?: AgentEmailFilter;
32
+ categories?: AgentEmailCategory[];
33
+ fallback?: "ignore" | string;
34
+ }
35
+ export interface AgentConfig {
36
+ name: string;
37
+ model: AgentModel | AgentTierRouting;
38
+ instructions?: string;
39
+ tools?: AgentToolGrant[];
40
+ workflows?: string[];
41
+ memory?: AgentMemory;
42
+ caps?: AgentCaps;
43
+ requireApproval?: string[];
44
+ chat?: boolean;
45
+ slackName?: string;
46
+ email?: AgentEmailConfig;
47
+ }
48
+ export declare function agent(config: AgentConfig): AgentConfig;
@@ -0,0 +1,3 @@
1
+ export function agent(config) {
2
+ return config;
3
+ }
@@ -0,0 +1,32 @@
1
+ export type Tier = "fast" | "balanced" | "powerful";
2
+ export interface RoutingConfig {
3
+ fast?: string;
4
+ balanced?: string;
5
+ powerful?: string;
6
+ }
7
+ export interface RoutingResult {
8
+ tier: Tier;
9
+ model: string;
10
+ provider: string;
11
+ reason: string;
12
+ }
13
+ export interface ParsedModel {
14
+ provider: string;
15
+ model: string;
16
+ }
17
+ export declare function parseModelSpec(spec: string): ParsedModel;
18
+ export declare function scoreComplexity(prompt: string, options?: {
19
+ system?: string;
20
+ maxTokens?: number;
21
+ }): {
22
+ tier: Tier;
23
+ reason: string;
24
+ };
25
+ export declare function resolveModel(tier: Tier, perCall?: RoutingConfig, workspace?: RoutingConfig): ParsedModel;
26
+ export interface BillingConfig {
27
+ default?: string;
28
+ fast?: string;
29
+ balanced?: string;
30
+ powerful?: string;
31
+ }
32
+ export declare function resolveBilling(tier: Tier | null, perCall?: string, perWorkflow?: string, workspace?: BillingConfig): string;
@@ -0,0 +1,112 @@
1
+ const LEGACY_ALIASES = {
2
+ haiku: { provider: "anthropic", model: "claude-haiku-4-5" },
3
+ sonnet: { provider: "anthropic", model: "claude-sonnet-4-6" },
4
+ opus: { provider: "anthropic", model: "claude-opus-4-6" },
5
+ };
6
+ const PLATFORM_DEFAULTS = {
7
+ fast: "openai/gpt-5.4-nano",
8
+ balanced: "@cf/moonshotai/kimi-k2.6",
9
+ powerful: "anthropic/claude-sonnet-4-6",
10
+ };
11
+ const FAST_KEYWORDS = /\b(classify|extract|list|define|format|translate)\b|yes or no|one word|true or false/i;
12
+ const POWERFUL_KEYWORDS = /\b(analyze|compare|step by step|evaluate|design|debug|refactor|explain why|critique|synthesize)\b/i;
13
+ const CODE_MARKERS = /```|(?:^|\n)\s*(?:function |class |def |import |const |let |var |export )/;
14
+ const TIER_ORDER = ["fast", "balanced", "powerful"];
15
+ function tierIndex(tier) {
16
+ return TIER_ORDER.indexOf(tier);
17
+ }
18
+ function scoreToTier(score) {
19
+ if (score < 0.30)
20
+ return "fast";
21
+ if (score < 0.60)
22
+ return "balanced";
23
+ return "powerful";
24
+ }
25
+ export function parseModelSpec(spec) {
26
+ if (LEGACY_ALIASES[spec])
27
+ return LEGACY_ALIASES[spec];
28
+ if (spec.startsWith("@cf/")) {
29
+ return { provider: "workers-ai", model: spec };
30
+ }
31
+ const slashIndex = spec.indexOf("/");
32
+ if (slashIndex > 0) {
33
+ return {
34
+ provider: spec.slice(0, slashIndex),
35
+ model: spec.slice(slashIndex + 1),
36
+ };
37
+ }
38
+ return { provider: "anthropic", model: spec };
39
+ }
40
+ export function scoreComplexity(prompt, options) {
41
+ const maxTokens = options?.maxTokens;
42
+ const parts = [];
43
+ const fullText = options?.system ? `${options.system}\n${prompt}` : prompt;
44
+ const estimatedTokens = Math.ceil(fullText.length / 4);
45
+ parts.push(`tokens:${estimatedTokens}`);
46
+ let score;
47
+ if (estimatedTokens < 200)
48
+ score = 0.10;
49
+ else if (estimatedTokens < 800)
50
+ score = 0.25;
51
+ else if (estimatedTokens < 2000)
52
+ score = 0.40;
53
+ else if (estimatedTokens < 8000)
54
+ score = 0.55;
55
+ else
56
+ score = 0.70;
57
+ const fastMatches = fullText.match(FAST_KEYWORDS);
58
+ const powerfulMatches = fullText.match(POWERFUL_KEYWORDS);
59
+ const hasCode = CODE_MARKERS.test(fullText);
60
+ let keywordMod = 0;
61
+ const keywordParts = [];
62
+ if (powerfulMatches) {
63
+ keywordMod += 0.20;
64
+ keywordParts.push(powerfulMatches[0].trim().toLowerCase());
65
+ }
66
+ if (hasCode) {
67
+ keywordMod += 0.15;
68
+ keywordParts.push("code");
69
+ }
70
+ if (fastMatches && !powerfulMatches && !hasCode) {
71
+ keywordMod -= 0.10;
72
+ keywordParts.push(fastMatches[0].trim().toLowerCase());
73
+ }
74
+ keywordMod = Math.max(-0.15, Math.min(0.25, keywordMod));
75
+ if (keywordParts.length > 0) {
76
+ parts.push(`keywords:${keywordParts.join("+")}`);
77
+ }
78
+ score += keywordMod;
79
+ if (maxTokens !== undefined) {
80
+ if (maxTokens <= 50) {
81
+ score -= 0.05;
82
+ parts.push("maxTokens:≤50");
83
+ }
84
+ else if (maxTokens > 2000) {
85
+ score += 0.10;
86
+ parts.push(`maxTokens:${maxTokens}`);
87
+ }
88
+ }
89
+ score = Math.max(0, Math.min(1, score));
90
+ let tier = scoreToTier(score);
91
+ if (hasCode && tierIndex(tier) < tierIndex("balanced")) {
92
+ tier = "balanced";
93
+ }
94
+ return { tier, reason: parts.join(", ") };
95
+ }
96
+ export function resolveModel(tier, perCall, workspace) {
97
+ const spec = perCall?.[tier] ??
98
+ workspace?.[tier] ??
99
+ PLATFORM_DEFAULTS[tier];
100
+ return parseModelSpec(spec);
101
+ }
102
+ export function resolveBilling(tier, perCall, perWorkflow, workspace) {
103
+ if (perCall)
104
+ return perCall;
105
+ if (perWorkflow)
106
+ return perWorkflow;
107
+ if (tier && workspace?.[tier])
108
+ return workspace[tier];
109
+ if (workspace?.default)
110
+ return workspace.default;
111
+ return "mug-metered";
112
+ }
@@ -0,0 +1,6 @@
1
+ import type { Env } from "./types.js";
2
+ declare const _default: {
3
+ fetch: (request: Request, Env?: {} | Env | undefined, executionCtx?: import("hono").ExecutionContext) => Response | Promise<Response>;
4
+ scheduled(_event: ScheduledEvent, env: Env): Promise<void>;
5
+ };
6
+ export default _default;