@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,24 @@
1
+ interface RoutingConfig {
2
+ fast?: string;
3
+ balanced?: string;
4
+ powerful?: string;
5
+ }
6
+ interface RouteElement {
7
+ id: string;
8
+ type: string;
9
+ properties?: Record<string, unknown>;
10
+ outputs: Record<string, {
11
+ elementId: string;
12
+ } | Record<string, never>>;
13
+ }
14
+ export declare function buildDynamicRouteConfig(workspaceId: string, routing: RoutingConfig | undefined, plan: string): {
15
+ name: string;
16
+ elements: RouteElement[];
17
+ };
18
+ export declare function deployDynamicRoute(cfApiToken: string, workspaceId: string, routing: RoutingConfig | undefined, plan: string): Promise<{
19
+ success: boolean;
20
+ routeName: string;
21
+ error?: string;
22
+ }>;
23
+ export declare function deleteDynamicRoute(cfApiToken: string, workspaceId: string): Promise<boolean>;
24
+ export {};
@@ -0,0 +1,192 @@
1
+ const CF_API = "https://api.cloudflare.com/client/v4";
2
+ const ACCOUNT_ID = "dc4e6e6b6ceac3b63aa836729d31e5b4";
3
+ const GATEWAY_ID = "mug-production";
4
+ const PLATFORM_DEFAULTS = {
5
+ fast: "openai/gpt-5.4-nano",
6
+ balanced: "@cf/moonshotai/kimi-k2.6",
7
+ powerful: "anthropic/claude-sonnet-4-6",
8
+ };
9
+ const PLAN_CREDIT_LIMITS = {
10
+ free: 2500,
11
+ starter: 15000,
12
+ pro: 50000,
13
+ business: 100000,
14
+ };
15
+ const PLAN_RATE_LIMITS = {
16
+ free: 100,
17
+ starter: 1000,
18
+ pro: 5000,
19
+ business: 20000,
20
+ };
21
+ function parseProviderModel(spec) {
22
+ if (spec.startsWith("@cf/"))
23
+ return { provider: "workers-ai", model: spec };
24
+ const slash = spec.indexOf("/");
25
+ if (slash > 0)
26
+ return { provider: spec.slice(0, slash), model: spec.slice(slash + 1) };
27
+ return { provider: "anthropic", model: spec };
28
+ }
29
+ export function buildDynamicRouteConfig(workspaceId, routing, plan) {
30
+ const config = { ...PLATFORM_DEFAULTS, ...routing };
31
+ const fast = parseProviderModel(config.fast);
32
+ const balanced = parseProviderModel(config.balanced);
33
+ const powerful = parseProviderModel(config.powerful);
34
+ const creditLimit = PLAN_CREDIT_LIMITS[plan] ?? PLAN_CREDIT_LIMITS.starter;
35
+ const rateLimit = PLAN_RATE_LIMITS[plan] ?? PLAN_RATE_LIMITS.starter;
36
+ const elements = [
37
+ {
38
+ id: "start",
39
+ type: "start",
40
+ outputs: { next: { elementId: "rate_limit" } },
41
+ },
42
+ {
43
+ id: "rate_limit",
44
+ type: "rate",
45
+ properties: {
46
+ limitType: "count",
47
+ key: "metadata.workspace",
48
+ limit: rateLimit,
49
+ window: 3600,
50
+ },
51
+ outputs: {
52
+ success: { elementId: "budget_limit" },
53
+ fallback: { elementId: "end" },
54
+ },
55
+ },
56
+ {
57
+ id: "budget_limit",
58
+ type: "rate",
59
+ properties: {
60
+ limitType: "cost",
61
+ key: "metadata.workspace",
62
+ limit: creditLimit,
63
+ window: 2592000,
64
+ },
65
+ outputs: {
66
+ success: { elementId: "tier_check" },
67
+ fallback: { elementId: "end" },
68
+ },
69
+ },
70
+ {
71
+ id: "tier_check",
72
+ type: "conditional",
73
+ properties: {
74
+ conditions: { "metadata.tier": { $eq: "fast" } },
75
+ },
76
+ outputs: {
77
+ true: { elementId: "model_fast" },
78
+ false: { elementId: "tier_check_powerful" },
79
+ },
80
+ },
81
+ {
82
+ id: "tier_check_powerful",
83
+ type: "conditional",
84
+ properties: {
85
+ conditions: { "metadata.tier": { $eq: "powerful" } },
86
+ },
87
+ outputs: {
88
+ true: { elementId: "model_powerful" },
89
+ false: { elementId: "model_balanced" },
90
+ },
91
+ },
92
+ {
93
+ id: "model_fast",
94
+ type: "model",
95
+ properties: {
96
+ provider: fast.provider,
97
+ model: fast.model,
98
+ timeout: 30000,
99
+ retries: 2,
100
+ },
101
+ outputs: {
102
+ success: { elementId: "end" },
103
+ fallback: { elementId: "model_balanced" },
104
+ },
105
+ },
106
+ {
107
+ id: "model_balanced",
108
+ type: "model",
109
+ properties: {
110
+ provider: balanced.provider,
111
+ model: balanced.model,
112
+ timeout: 30000,
113
+ retries: 2,
114
+ },
115
+ outputs: {
116
+ success: { elementId: "end" },
117
+ fallback: { elementId: "model_powerful" },
118
+ },
119
+ },
120
+ {
121
+ id: "model_powerful",
122
+ type: "model",
123
+ properties: {
124
+ provider: powerful.provider,
125
+ model: powerful.model,
126
+ timeout: 60000,
127
+ retries: 2,
128
+ },
129
+ outputs: {
130
+ success: { elementId: "end" },
131
+ fallback: { elementId: "end" },
132
+ },
133
+ },
134
+ {
135
+ id: "end",
136
+ type: "end",
137
+ outputs: {},
138
+ },
139
+ ];
140
+ return { name: `ws-${workspaceId}`, elements };
141
+ }
142
+ export async function deployDynamicRoute(cfApiToken, workspaceId, routing, plan) {
143
+ const routeConfig = buildDynamicRouteConfig(workspaceId, routing, plan);
144
+ const baseUrl = `${CF_API}/accounts/${ACCOUNT_ID}/ai-gateway/gateways/${GATEWAY_ID}/routes`;
145
+ const existing = await fetch(baseUrl, {
146
+ headers: {
147
+ "Authorization": `Bearer ${cfApiToken}`,
148
+ "Content-Type": "application/json",
149
+ },
150
+ });
151
+ let routeId = null;
152
+ if (existing.ok) {
153
+ const data = (await existing.json());
154
+ routeId = data.result?.find((r) => r.name === routeConfig.name)?.id ?? null;
155
+ }
156
+ const method = routeId ? "PUT" : "POST";
157
+ const url = routeId ? `${baseUrl}/${routeId}` : baseUrl;
158
+ const res = await fetch(url, {
159
+ method,
160
+ headers: {
161
+ "Authorization": `Bearer ${cfApiToken}`,
162
+ "Content-Type": "application/json",
163
+ },
164
+ body: JSON.stringify(routeConfig),
165
+ });
166
+ if (!res.ok) {
167
+ const err = await res.text();
168
+ return { success: false, routeName: routeConfig.name, error: err };
169
+ }
170
+ return { success: true, routeName: routeConfig.name };
171
+ }
172
+ export async function deleteDynamicRoute(cfApiToken, workspaceId) {
173
+ const routeName = `ws-${workspaceId}`;
174
+ const baseUrl = `${CF_API}/accounts/${ACCOUNT_ID}/ai-gateway/gateways/${GATEWAY_ID}/routes`;
175
+ const existing = await fetch(baseUrl, {
176
+ headers: {
177
+ "Authorization": `Bearer ${cfApiToken}`,
178
+ "Content-Type": "application/json",
179
+ },
180
+ });
181
+ if (!existing.ok)
182
+ return false;
183
+ const data = (await existing.json());
184
+ const routeId = data.result?.find((r) => r.name === routeName)?.id;
185
+ if (!routeId)
186
+ return true;
187
+ const res = await fetch(`${baseUrl}/${routeId}`, {
188
+ method: "DELETE",
189
+ headers: { "Authorization": `Bearer ${cfApiToken}` },
190
+ });
191
+ return res.ok;
192
+ }
@@ -0,0 +1 @@
1
+ export declare function auth(provider: string): Promise<void>;
@@ -0,0 +1,42 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { exec } from "node:child_process";
4
+ const API_URL = "https://api.mug.work";
5
+ function openBrowser(url) {
6
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
7
+ exec(`${cmd} "${url}"`);
8
+ }
9
+ export async function auth(provider) {
10
+ const cwd = process.cwd();
11
+ const mugJsonPath = join(cwd, "mug.json");
12
+ if (!existsSync(mugJsonPath)) {
13
+ console.error("No mug.json found. Run `mug init` first.");
14
+ process.exit(1);
15
+ }
16
+ const config = JSON.parse(readFileSync(mugJsonPath, "utf-8"));
17
+ const workspace = config.name;
18
+ const state = crypto.randomUUID();
19
+ const startUrl = `${API_URL}/oauth/${provider}/start?workspace=${encodeURIComponent(workspace)}&state=${state}`;
20
+ console.log(`Opening browser to authorize ${provider}...`);
21
+ openBrowser(startUrl);
22
+ console.log("Waiting for authorization...");
23
+ const pollUrl = `${API_URL}/oauth/poll/${state}`;
24
+ const start = Date.now();
25
+ const timeout = 5 * 60 * 1000;
26
+ while (Date.now() - start < timeout) {
27
+ await new Promise((r) => setTimeout(r, 2000));
28
+ try {
29
+ const res = await fetch(pollUrl);
30
+ const data = (await res.json());
31
+ if (data.complete) {
32
+ console.log(`\n${provider} connected to workspace "${workspace}".`);
33
+ return;
34
+ }
35
+ }
36
+ catch {
37
+ // retry
38
+ }
39
+ }
40
+ console.error("\nTimed out waiting for authorization. Try again with `mug auth " + provider + "`");
41
+ process.exit(1);
42
+ }
@@ -0,0 +1,6 @@
1
+ export declare function buyPack(): Promise<void>;
2
+ export declare function billingSettings(opts: {
3
+ autoPacks?: string;
4
+ maxPacks?: string;
5
+ email?: string;
6
+ }): Promise<void>;
@@ -0,0 +1,76 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { getAccountToken } from "./login.js";
4
+ const DISPATCH_URL = "https://api.mug.work";
5
+ function getWorkspaceName() {
6
+ const mugJsonPath = join(process.cwd(), "mug.json");
7
+ if (!existsSync(mugJsonPath)) {
8
+ console.error("No mug.json found. Run `mug init` first.");
9
+ process.exit(1);
10
+ }
11
+ return JSON.parse(readFileSync(mugJsonPath, "utf-8")).name;
12
+ }
13
+ export async function buyPack() {
14
+ const workspace = getWorkspaceName();
15
+ const token = getAccountToken();
16
+ console.log(`Purchasing overage pack for ${workspace}...`);
17
+ const res = await fetch(`${DISPATCH_URL}/workspace/${workspace}/usage/pack`, {
18
+ method: "POST",
19
+ headers: { Authorization: `Bearer ${token}` },
20
+ });
21
+ const data = await res.json();
22
+ if (!res.ok || data.error) {
23
+ console.error(`Failed: ${data.error ?? res.statusText}`);
24
+ process.exit(1);
25
+ }
26
+ if (data.url) {
27
+ console.log(`\nOpen this URL to complete payment:\n\n ${data.url}\n`);
28
+ }
29
+ else {
30
+ console.log(`Overage pack added. Total packs this period: ${data.overage_packs}`);
31
+ }
32
+ }
33
+ export async function billingSettings(opts) {
34
+ const workspace = getWorkspaceName();
35
+ const token = getAccountToken();
36
+ const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
37
+ // If no flags, show current settings
38
+ if (opts.autoPacks === undefined && opts.maxPacks === undefined && opts.email === undefined) {
39
+ const res = await fetch(`${DISPATCH_URL}/workspace/${workspace}/billing`, {
40
+ headers: { Authorization: `Bearer ${token}` },
41
+ });
42
+ const data = await res.json();
43
+ if (!res.ok) {
44
+ console.error(`Failed: ${data.error ?? res.statusText}`);
45
+ process.exit(1);
46
+ }
47
+ console.log(`\n Billing settings: ${workspace}\n`);
48
+ console.log(` Plan tier: ${data.plan_tier}`);
49
+ console.log(` Auto-purchase packs: ${data.auto_purchase_packs ? "on" : "off"}`);
50
+ console.log(` Max packs/month: ${data.max_packs_per_month || "unlimited"}`);
51
+ console.log(` Billing email: ${data.billing_email ?? "(not set — uses account email)"}`);
52
+ console.log("");
53
+ return;
54
+ }
55
+ const body = {};
56
+ if (opts.autoPacks !== undefined) {
57
+ body.auto_purchase_packs = opts.autoPacks === "on" || opts.autoPacks === "true";
58
+ }
59
+ if (opts.maxPacks !== undefined) {
60
+ body.max_packs_per_month = parseInt(opts.maxPacks) || 0;
61
+ }
62
+ if (opts.email !== undefined) {
63
+ body.billing_email = opts.email || null;
64
+ }
65
+ const res = await fetch(`${DISPATCH_URL}/workspace/${workspace}/billing`, {
66
+ method: "PUT",
67
+ headers,
68
+ body: JSON.stringify(body),
69
+ });
70
+ const data = await res.json();
71
+ if (!res.ok || data.error) {
72
+ console.error(`Failed: ${data.error ?? res.statusText}`);
73
+ process.exit(1);
74
+ }
75
+ console.log(`Updated: ${data.updated?.join(", ")}`);
76
+ }
@@ -0,0 +1 @@
1
+ export declare function brain(agentName: string, subcommand?: string, ...args: string[]): void;
@@ -0,0 +1,194 @@
1
+ import { existsSync, readdirSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import Database from "better-sqlite3";
4
+ function findBrainDb(agentName) {
5
+ const cwd = process.cwd();
6
+ const brainPath = join(cwd, "src", "agents", agentName, "brain.db");
7
+ if (!existsSync(brainPath)) {
8
+ const agentsDir = join(cwd, "src", "agents");
9
+ if (!existsSync(agentsDir)) {
10
+ console.error("No src/agents/ directory found. Run `mug init` first.");
11
+ process.exit(1);
12
+ }
13
+ const folders = readdirSync(agentsDir).filter((f) => {
14
+ if (f === "shared-skills")
15
+ return false;
16
+ return statSync(join(agentsDir, f)).isDirectory() && existsSync(join(agentsDir, f, "agent.json"));
17
+ });
18
+ if (folders.length === 0) {
19
+ console.error("No agents found. Create an agent folder with agent.json + soul.md.");
20
+ process.exit(1);
21
+ }
22
+ console.error(`Agent "${agentName}" has no brain.db yet.`);
23
+ console.error(`Run \`mug pull\` to download runtime brain data, or the agent hasn't been invoked yet.`);
24
+ console.error(`\nAvailable agents: ${folders.join(", ")}`);
25
+ process.exit(1);
26
+ }
27
+ return brainPath;
28
+ }
29
+ function formatTable(rows, cols) {
30
+ if (rows.length === 0)
31
+ return " (none)\n";
32
+ const widths = cols.map((col) => {
33
+ const values = rows.map((r) => String(r[col] ?? "").slice(0, 60));
34
+ return Math.min(60, Math.max(col.length, ...values.map((v) => v.length)));
35
+ });
36
+ const header = cols.map((c, i) => c.padEnd(widths[i])).join(" ");
37
+ const sep = widths.map((w) => "-".repeat(w)).join(" ");
38
+ const body = rows.map((r) => cols.map((c, i) => String(r[c] ?? "").slice(0, 60).padEnd(widths[i])).join(" ")).join("\n");
39
+ return `${header}\n${sep}\n${body}\n`;
40
+ }
41
+ function overview(agentName) {
42
+ const db = new Database(findBrainDb(agentName), { readonly: true });
43
+ try {
44
+ const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map((r) => r.name);
45
+ if (!tables.includes("brain_entities")) {
46
+ console.log(`${agentName}: brain.db exists but has no schema (agent may not have run yet).`);
47
+ return;
48
+ }
49
+ const entities = db.prepare("SELECT COUNT(*) as c FROM brain_entities").get().c;
50
+ const facts = db.prepare("SELECT COUNT(*) as c FROM brain_facts").get().c;
51
+ const outcomes = db.prepare("SELECT COUNT(*) as c FROM brain_outcomes").get().c;
52
+ const struggles = db.prepare("SELECT COUNT(*) as c FROM brain_struggles").get().c;
53
+ const unresolvedStruggles = db.prepare("SELECT COUNT(*) as c FROM brain_struggles WHERE resolved = 0").get().c;
54
+ const sessions = db.prepare("SELECT COUNT(*) as c FROM brain_sessions").get().c;
55
+ console.log(`\n ${agentName} brain\n`);
56
+ console.log(` Entities: ${entities}`);
57
+ console.log(` Facts: ${facts}`);
58
+ console.log(` Outcomes: ${outcomes}`);
59
+ console.log(` Struggles: ${struggles} (${unresolvedStruggles} unresolved)`);
60
+ console.log(` Sessions: ${sessions}`);
61
+ if (unresolvedStruggles > 0) {
62
+ console.log(`\n Recent unresolved struggles:`);
63
+ const rows = db.prepare("SELECT category, description, recorded_at FROM brain_struggles WHERE resolved = 0 ORDER BY recorded_at DESC LIMIT 5").all();
64
+ for (const r of rows) {
65
+ console.log(` [${r.category}] ${r.description} (${r.recorded_at?.slice(0, 10)})`);
66
+ }
67
+ }
68
+ console.log();
69
+ }
70
+ finally {
71
+ db.close();
72
+ }
73
+ }
74
+ function struggles(agentName) {
75
+ const db = new Database(findBrainDb(agentName), { readonly: true });
76
+ try {
77
+ const rows = db.prepare("SELECT category, description, context, resolved, recorded_at FROM brain_struggles ORDER BY resolved ASC, recorded_at DESC").all();
78
+ if (rows.length === 0) {
79
+ console.log(`\n ${agentName}: no struggles recorded.\n`);
80
+ return;
81
+ }
82
+ const unresolved = rows.filter((r) => !r.resolved);
83
+ const resolved = rows.filter((r) => r.resolved);
84
+ console.log(`\n ${agentName} struggles (${unresolved.length} unresolved, ${resolved.length} resolved)\n`);
85
+ const grouped = {};
86
+ for (const r of unresolved) {
87
+ (grouped[r.category] ??= []).push(r);
88
+ }
89
+ for (const [cat, items] of Object.entries(grouped)) {
90
+ console.log(` [${cat}] (${items.length})`);
91
+ for (const item of items) {
92
+ console.log(` - ${item.description}`);
93
+ if (item.context)
94
+ console.log(` context: ${item.context.slice(0, 100)}`);
95
+ }
96
+ console.log();
97
+ }
98
+ }
99
+ finally {
100
+ db.close();
101
+ }
102
+ }
103
+ function entities(agentName) {
104
+ const db = new Database(findBrainDb(agentName), { readonly: true });
105
+ try {
106
+ const rows = db.prepare("SELECT name, type, summary, mention_count, last_seen FROM brain_entities ORDER BY mention_count DESC").all();
107
+ console.log(`\n ${agentName} entities (${rows.length})\n`);
108
+ console.log(formatTable(rows, ["name", "type", "summary", "mention_count"]));
109
+ }
110
+ finally {
111
+ db.close();
112
+ }
113
+ }
114
+ function outcomes(agentName) {
115
+ const db = new Database(findBrainDb(agentName), { readonly: true });
116
+ try {
117
+ const rows = db.prepare("SELECT action, result, CASE WHEN effective = 1 THEN 'yes' WHEN effective = 0 THEN 'no' ELSE '?' END as effective, recorded_at FROM brain_outcomes ORDER BY recorded_at DESC LIMIT 30").all();
118
+ console.log(`\n ${agentName} outcomes (last 30)\n`);
119
+ console.log(formatTable(rows, ["action", "result", "effective", "recorded_at"]));
120
+ const stats = db.prepare("SELECT COUNT(*) as total, SUM(CASE WHEN effective = 1 THEN 1 ELSE 0 END) as successes FROM brain_outcomes").get();
121
+ if (stats.total > 0) {
122
+ const rate = Math.round((stats.successes / stats.total) * 100);
123
+ console.log(` Success rate: ${rate}% (${stats.successes}/${stats.total})\n`);
124
+ }
125
+ }
126
+ finally {
127
+ db.close();
128
+ }
129
+ }
130
+ function sessionsList(agentName) {
131
+ const db = new Database(findBrainDb(agentName), { readonly: true });
132
+ try {
133
+ const rows = db.prepare("SELECT workflow, started_at, credits_used, turns_used, CASE WHEN capped = 1 THEN 'yes' ELSE '' END as capped, outcome_count, struggle_count FROM brain_sessions ORDER BY started_at DESC LIMIT 30").all();
134
+ console.log(`\n ${agentName} sessions (last 30)\n`);
135
+ console.log(formatTable(rows, ["workflow", "started_at", "credits_used", "turns_used", "capped"]));
136
+ }
137
+ finally {
138
+ db.close();
139
+ }
140
+ }
141
+ function search(agentName, query) {
142
+ const db = new Database(findBrainDb(agentName), { readonly: true });
143
+ try {
144
+ const q = `%${query}%`;
145
+ const entityRows = db.prepare("SELECT 'entity' as type, name as title, summary as detail FROM brain_entities WHERE LOWER(name) LIKE LOWER(?) OR LOWER(summary) LIKE LOWER(?)").all(q, q);
146
+ const factRows = db.prepare("SELECT 'fact' as type, content as title, observed_at as detail FROM brain_facts WHERE LOWER(content) LIKE LOWER(?)").all(q);
147
+ const struggleRows = db.prepare("SELECT 'struggle' as type, description as title, category as detail FROM brain_struggles WHERE LOWER(description) LIKE LOWER(?)").all(q);
148
+ const all = [...entityRows, ...factRows, ...struggleRows];
149
+ if (all.length === 0) {
150
+ console.log(`\n No results for "${query}"\n`);
151
+ return;
152
+ }
153
+ console.log(`\n ${agentName} brain search: "${query}" (${all.length} results)\n`);
154
+ console.log(formatTable(all, ["type", "title", "detail"]));
155
+ }
156
+ finally {
157
+ db.close();
158
+ }
159
+ }
160
+ export function brain(agentName, subcommand, ...args) {
161
+ if (!agentName) {
162
+ const cwd = process.cwd();
163
+ const agentsDir = join(cwd, "src", "agents");
164
+ if (existsSync(agentsDir)) {
165
+ const folders = readdirSync(agentsDir).filter((f) => {
166
+ if (f === "shared-skills")
167
+ return false;
168
+ return statSync(join(agentsDir, f)).isDirectory() && existsSync(join(agentsDir, f, "agent.json"));
169
+ });
170
+ if (folders.length > 0) {
171
+ console.log(`\nAvailable agents: ${folders.join(", ")}`);
172
+ console.log(`Usage: mug brain <agent-name> [struggles|entities|outcomes|sessions|search <query>]\n`);
173
+ return;
174
+ }
175
+ }
176
+ console.error("Usage: mug brain <agent-name> [subcommand]");
177
+ process.exit(1);
178
+ }
179
+ switch (subcommand) {
180
+ case "struggles": return struggles(agentName);
181
+ case "entities": return entities(agentName);
182
+ case "outcomes": return outcomes(agentName);
183
+ case "sessions": return sessionsList(agentName);
184
+ case "search": {
185
+ const query = args.join(" ");
186
+ if (!query) {
187
+ console.error("Usage: mug brain <agent> search <query>");
188
+ process.exit(1);
189
+ }
190
+ return search(agentName, query);
191
+ }
192
+ default: return overview(agentName);
193
+ }
194
+ }
@@ -0,0 +1,12 @@
1
+ export interface DemoEnableOpts {
2
+ as: string;
3
+ expires?: string;
4
+ notify?: string;
5
+ emailTo?: string;
6
+ smsTo?: string;
7
+ slackTo?: string;
8
+ noWorkflows?: boolean;
9
+ }
10
+ export declare function demoEnable(surfaceId: string, opts: DemoEnableOpts): Promise<void>;
11
+ export declare function demoDisable(surfaceId: string): Promise<void>;
12
+ export declare function demoStatus(): Promise<void>;