@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.
- package/LICENSE +21 -0
- package/README.md +251 -0
- package/dist/explorer.js +3 -0
- package/dist/packages/email-template/src/email-template.d.ts +18 -0
- package/dist/packages/email-template/src/email-template.js +74 -0
- package/dist/packages/email-template/src/index.d.ts +1 -0
- package/dist/packages/email-template/src/index.js +1 -0
- package/dist/packages/surface-renderer/src/form-renderer.d.ts +117 -0
- package/dist/packages/surface-renderer/src/form-renderer.js +719 -0
- package/dist/packages/surface-renderer/src/index.d.ts +4 -0
- package/dist/packages/surface-renderer/src/index.js +2 -0
- package/dist/packages/surface-renderer/src/portal-renderer.d.ts +177 -0
- package/dist/packages/surface-renderer/src/portal-renderer.js +1089 -0
- package/dist/packages/surface-renderer/src/workspace-home.d.ts +46 -0
- package/dist/packages/surface-renderer/src/workspace-home.js +345 -0
- package/dist/runtime/agent-types.d.ts +48 -0
- package/dist/runtime/agent-types.js +3 -0
- package/dist/runtime/ai-router.d.ts +32 -0
- package/dist/runtime/ai-router.js +112 -0
- package/dist/runtime/app.d.ts +6 -0
- package/dist/runtime/app.js +399 -0
- package/dist/runtime/chunker.d.ts +6 -0
- package/dist/runtime/chunker.js +30 -0
- package/dist/runtime/context.d.ts +115 -0
- package/dist/runtime/context.js +440 -0
- package/dist/runtime/do/workspace-database.d.ts +10 -0
- package/dist/runtime/do/workspace-database.js +199 -0
- package/dist/runtime/form-types.d.ts +143 -0
- package/dist/runtime/form-types.js +1 -0
- package/dist/runtime/runtime.d.ts +9 -0
- package/dist/runtime/runtime.js +7 -0
- package/dist/runtime/source-types.d.ts +15 -0
- package/dist/runtime/source-types.js +1 -0
- package/dist/runtime/source.d.ts +70 -0
- package/dist/runtime/source.js +21 -0
- package/dist/runtime/sync-runtime.d.ts +10 -0
- package/dist/runtime/sync-runtime.js +185 -0
- package/dist/runtime/types.d.ts +21 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/workflow-entrypoint.d.ts +31 -0
- package/dist/runtime/workflow-entrypoint.js +1297 -0
- package/dist/runtime/workflow.d.ts +285 -0
- package/dist/runtime/workflow.js +1008 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +44116 -0
- package/dist/src/commands/ai-gateway-route.d.ts +24 -0
- package/dist/src/commands/ai-gateway-route.js +192 -0
- package/dist/src/commands/auth.d.ts +1 -0
- package/dist/src/commands/auth.js +42 -0
- package/dist/src/commands/billing.d.ts +6 -0
- package/dist/src/commands/billing.js +76 -0
- package/dist/src/commands/brain.d.ts +1 -0
- package/dist/src/commands/brain.js +194 -0
- package/dist/src/commands/demo.d.ts +12 -0
- package/dist/src/commands/demo.js +147 -0
- package/dist/src/commands/deploy.d.ts +1 -0
- package/dist/src/commands/deploy.js +1052 -0
- package/dist/src/commands/dev.d.ts +14 -0
- package/dist/src/commands/dev.js +2818 -0
- package/dist/src/commands/form.d.ts +8 -0
- package/dist/src/commands/form.js +396 -0
- package/dist/src/commands/init.d.ts +1 -0
- package/dist/src/commands/init.js +139 -0
- package/dist/src/commands/issue.d.ts +7 -0
- package/dist/src/commands/issue.js +191 -0
- package/dist/src/commands/login.d.ts +9 -0
- package/dist/src/commands/login.js +163 -0
- package/dist/src/commands/logs.d.ts +8 -0
- package/dist/src/commands/logs.js +113 -0
- package/dist/src/commands/portal.d.ts +2 -0
- package/dist/src/commands/portal.js +111 -0
- package/dist/src/commands/pull.d.ts +3 -0
- package/dist/src/commands/pull.js +184 -0
- package/dist/src/commands/push.d.ts +4 -0
- package/dist/src/commands/push.js +183 -0
- package/dist/src/commands/run.d.ts +6 -0
- package/dist/src/commands/run.js +91 -0
- package/dist/src/commands/secret.d.ts +7 -0
- package/dist/src/commands/secret.js +105 -0
- package/dist/src/commands/shutdown.d.ts +1 -0
- package/dist/src/commands/shutdown.js +46 -0
- package/dist/src/commands/sql.d.ts +8 -0
- package/dist/src/commands/sql.js +142 -0
- package/dist/src/commands/status.d.ts +5 -0
- package/dist/src/commands/status.js +39 -0
- package/dist/src/commands/sync.d.ts +7 -0
- package/dist/src/commands/sync.js +991 -0
- package/dist/src/commands/usage.d.ts +6 -0
- package/dist/src/commands/usage.js +78 -0
- package/dist/src/commands/webhooks.d.ts +1 -0
- package/dist/src/commands/webhooks.js +102 -0
- package/dist/src/commands/workspace.d.ts +23 -0
- package/dist/src/commands/workspace.js +590 -0
- package/dist/src/connector-migration.d.ts +20 -0
- package/dist/src/connector-migration.js +43 -0
- package/dist/src/connector-parser.d.ts +14 -0
- package/dist/src/connector-parser.js +94 -0
- package/dist/src/connector-service/discover.d.ts +37 -0
- package/dist/src/connector-service/discover.js +79 -0
- package/dist/src/connector-service/gather.d.ts +22 -0
- package/dist/src/connector-service/gather.js +89 -0
- package/dist/src/connector-service/init.d.ts +14 -0
- package/dist/src/connector-service/init.js +109 -0
- package/dist/src/connector-service/scaffold.d.ts +17 -0
- package/dist/src/connector-service/scaffold.js +194 -0
- package/dist/src/connector-service/spec-storage.d.ts +8 -0
- package/dist/src/connector-service/spec-storage.js +48 -0
- package/dist/src/connector-service/types.d.ts +57 -0
- package/dist/src/connector-service/types.js +2 -0
- package/dist/src/connector-service/verify.d.ts +24 -0
- package/dist/src/connector-service/verify.js +575 -0
- package/dist/src/email-template.d.ts +2 -0
- package/dist/src/email-template.js +1 -0
- package/dist/src/manifest.d.ts +31 -0
- package/dist/src/manifest.js +25 -0
- package/dist/src/mug-icon.d.ts +1 -0
- package/dist/src/mug-icon.js +12 -0
- package/dist/src/slack-manifest.d.ts +119 -0
- package/dist/src/slack-manifest.js +163 -0
- package/dist/src/source-migration.d.ts +20 -0
- package/dist/src/source-migration.js +43 -0
- package/dist/src/surface-renderer.d.ts +5 -0
- package/dist/src/surface-renderer.js +3 -0
- package/dist/src/templates.d.ts +3 -0
- package/dist/src/templates.js +48 -0
- package/dist/src/version-check.d.ts +1 -0
- package/dist/src/version-check.js +28 -0
- package/dist/src/workflow-parser.d.ts +95 -0
- package/dist/src/workflow-parser.js +526 -0
- package/dist/worker/src/agent-types.d.ts +27 -0
- package/dist/worker/src/agent-types.js +3 -0
- package/dist/worker/src/source-types.d.ts +14 -0
- package/dist/worker/src/source-types.js +1 -0
- package/package.json +90 -0
- package/src/data/model-capabilities.json +171 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
const GITHUB_REPO = "mugwork/mug";
|
|
6
|
+
const GITHUB_API = `https://api.github.com/repos/${GITHUB_REPO}/issues`;
|
|
7
|
+
const CATEGORIES = ["bug", "feature-request", "docs", "connector-pipeline"];
|
|
8
|
+
const CATEGORY_LABELS = {
|
|
9
|
+
bug: "bug",
|
|
10
|
+
"feature-request": "enhancement",
|
|
11
|
+
docs: "documentation",
|
|
12
|
+
"connector-pipeline": "connector",
|
|
13
|
+
};
|
|
14
|
+
function collectContext(cwd) {
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const pkg = require("../../package.json");
|
|
17
|
+
let workspaceName = null;
|
|
18
|
+
let templateVersion = null;
|
|
19
|
+
let sourceCount = 0;
|
|
20
|
+
let workflowCount = 0;
|
|
21
|
+
let agentCount = 0;
|
|
22
|
+
let formCount = 0;
|
|
23
|
+
let portalCount = 0;
|
|
24
|
+
const mugJsonPath = join(cwd, "mug.json");
|
|
25
|
+
if (existsSync(mugJsonPath)) {
|
|
26
|
+
try {
|
|
27
|
+
const config = JSON.parse(readFileSync(mugJsonPath, "utf-8"));
|
|
28
|
+
workspaceName = config.name ?? null;
|
|
29
|
+
templateVersion = config.templateVersion ?? null;
|
|
30
|
+
if (config.sources)
|
|
31
|
+
sourceCount = Object.keys(config.sources).length;
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
}
|
|
35
|
+
const srcDir = join(cwd, "src");
|
|
36
|
+
if (existsSync(srcDir)) {
|
|
37
|
+
const countFiles = (subdir) => {
|
|
38
|
+
const dir = join(srcDir, subdir);
|
|
39
|
+
if (!existsSync(dir))
|
|
40
|
+
return 0;
|
|
41
|
+
try {
|
|
42
|
+
return readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".js")).length;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
workflowCount = countFiles("workflows");
|
|
49
|
+
agentCount = countFiles("agents");
|
|
50
|
+
}
|
|
51
|
+
const surfacesDir = join(srcDir, "surfaces");
|
|
52
|
+
if (existsSync(surfacesDir)) {
|
|
53
|
+
try {
|
|
54
|
+
const files = readdirSync(surfacesDir).filter((f) => f.endsWith(".json"));
|
|
55
|
+
for (const f of files) {
|
|
56
|
+
try {
|
|
57
|
+
const content = JSON.parse(readFileSync(join(surfacesDir, f), "utf-8"));
|
|
58
|
+
if (content.type === "form")
|
|
59
|
+
formCount++;
|
|
60
|
+
else if (content.type === "portal")
|
|
61
|
+
portalCount++;
|
|
62
|
+
}
|
|
63
|
+
catch { }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch { }
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
cliVersion: pkg.version,
|
|
70
|
+
workspaceName,
|
|
71
|
+
templateVersion,
|
|
72
|
+
sourceCount,
|
|
73
|
+
workflowCount,
|
|
74
|
+
agentCount,
|
|
75
|
+
formCount,
|
|
76
|
+
portalCount,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function prompt(rl, question) {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
rl.question(question, (answer) => resolve(answer.trim()));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async function promptCategory(rl) {
|
|
85
|
+
console.log("\nCategory:");
|
|
86
|
+
CATEGORIES.forEach((c, i) => console.log(` ${i + 1}. ${c}`));
|
|
87
|
+
const answer = await prompt(rl, "> ");
|
|
88
|
+
const idx = parseInt(answer, 10);
|
|
89
|
+
if (idx >= 1 && idx <= CATEGORIES.length)
|
|
90
|
+
return CATEGORIES[idx - 1];
|
|
91
|
+
const match = CATEGORIES.find((c) => c.startsWith(answer.toLowerCase()));
|
|
92
|
+
if (match)
|
|
93
|
+
return match;
|
|
94
|
+
console.log(" (defaulting to bug)");
|
|
95
|
+
return "bug";
|
|
96
|
+
}
|
|
97
|
+
function formatIssueBody(category, answers, ctx) {
|
|
98
|
+
let body = "";
|
|
99
|
+
body += `## What were you trying to do?\n\n${answers.doing}\n\n`;
|
|
100
|
+
body += `## What happened?\n\n${answers.happened}\n\n`;
|
|
101
|
+
body += `## What did you expect?\n\n${answers.expected}\n\n`;
|
|
102
|
+
if (answers.reproduce && answers.reproduce.toLowerCase() !== "none") {
|
|
103
|
+
body += `## Steps to reproduce\n\n${answers.reproduce}\n\n`;
|
|
104
|
+
}
|
|
105
|
+
if (answers.files && answers.files.toLowerCase() !== "none") {
|
|
106
|
+
body += `## Relevant files\n\n${answers.files}\n\n`;
|
|
107
|
+
}
|
|
108
|
+
body += `<details>\n<summary>Workspace context</summary>\n\n`;
|
|
109
|
+
body += `| | |\n|---|---|\n`;
|
|
110
|
+
body += `| CLI version | ${ctx.cliVersion} |\n`;
|
|
111
|
+
if (ctx.workspaceName)
|
|
112
|
+
body += `| Workspace | ${ctx.workspaceName} |\n`;
|
|
113
|
+
if (ctx.templateVersion)
|
|
114
|
+
body += `| Template version | ${ctx.templateVersion} |\n`;
|
|
115
|
+
body += `| Sources | ${ctx.sourceCount} |\n`;
|
|
116
|
+
body += `| Workflows | ${ctx.workflowCount} |\n`;
|
|
117
|
+
body += `| Agents | ${ctx.agentCount} |\n`;
|
|
118
|
+
body += `| Forms | ${ctx.formCount} |\n`;
|
|
119
|
+
body += `| Portals | ${ctx.portalCount} |\n`;
|
|
120
|
+
body += `\n</details>\n`;
|
|
121
|
+
return body;
|
|
122
|
+
}
|
|
123
|
+
export async function issue(opts) {
|
|
124
|
+
const cwd = process.cwd();
|
|
125
|
+
const ctx = collectContext(cwd);
|
|
126
|
+
if (opts.attachDiagnostics) {
|
|
127
|
+
console.log("--attach-diagnostics is not yet available. Filing issue without diagnostics.\n");
|
|
128
|
+
}
|
|
129
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
130
|
+
try {
|
|
131
|
+
console.log("Filing an issue on github.com/mugwork/mug\n");
|
|
132
|
+
const category = await promptCategory(rl);
|
|
133
|
+
console.log("\nWhat were you trying to do?");
|
|
134
|
+
const doing = await prompt(rl, "> ");
|
|
135
|
+
console.log("\nWhat happened?");
|
|
136
|
+
const happened = await prompt(rl, "> ");
|
|
137
|
+
console.log("\nWhat did you expect to happen?");
|
|
138
|
+
const expected = await prompt(rl, "> ");
|
|
139
|
+
console.log("\nSteps to reproduce (or \"none\"):");
|
|
140
|
+
const reproduce = await prompt(rl, "> ");
|
|
141
|
+
console.log("\nRelevant files (comma-separated paths, or \"none\"):");
|
|
142
|
+
const files = await prompt(rl, "> ");
|
|
143
|
+
rl.close();
|
|
144
|
+
const title = doing.length > 70 ? doing.slice(0, 67) + "..." : doing;
|
|
145
|
+
const body = formatIssueBody(category, { doing, happened, expected, reproduce, files }, ctx);
|
|
146
|
+
const label = CATEGORY_LABELS[category];
|
|
147
|
+
if (opts.dryRun) {
|
|
148
|
+
console.log("\n--- DRY RUN ---");
|
|
149
|
+
console.log(`Title: ${title}`);
|
|
150
|
+
console.log(`Labels: [${label}]`);
|
|
151
|
+
console.log(`\n${body}`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const token = opts.token || process.env.MUG_GITHUB_TOKEN || process.env.GITHUB_TOKEN;
|
|
155
|
+
if (!token) {
|
|
156
|
+
console.log("\n--- No GitHub token found ---");
|
|
157
|
+
console.log("Set MUG_GITHUB_TOKEN or GITHUB_TOKEN env var, or pass --token <token>.");
|
|
158
|
+
console.log("\nIssue body (copy to github.com/mugwork/mug/issues/new):\n");
|
|
159
|
+
console.log(`Title: ${title}`);
|
|
160
|
+
console.log(`Labels: [${label}]`);
|
|
161
|
+
console.log(`\n${body}`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const res = await fetch(GITHUB_API, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: {
|
|
167
|
+
Authorization: `Bearer ${token}`,
|
|
168
|
+
Accept: "application/vnd.github+json",
|
|
169
|
+
"Content-Type": "application/json",
|
|
170
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
171
|
+
},
|
|
172
|
+
body: JSON.stringify({
|
|
173
|
+
title,
|
|
174
|
+
body,
|
|
175
|
+
labels: [label],
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
if (!res.ok) {
|
|
179
|
+
const err = await res.text();
|
|
180
|
+
console.error(`GitHub API error (${res.status}): ${err}`);
|
|
181
|
+
console.log("\nIssue body (copy to github.com/mugwork/mug/issues/new):\n");
|
|
182
|
+
console.log(`Title: ${title}\n\n${body}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
const data = (await res.json());
|
|
186
|
+
console.log(`\nIssue created: ${data.html_url}`);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
rl.close();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface Credentials {
|
|
2
|
+
email: string;
|
|
3
|
+
token: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function readCredentials(): Credentials | null;
|
|
6
|
+
export declare function getAccountToken(): string;
|
|
7
|
+
export declare function ensureDevEmail(mugJsonPath: string): void;
|
|
8
|
+
export declare function login(): Promise<void>;
|
|
9
|
+
export declare function whoami(): Promise<void>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { createInterface } from "node:readline";
|
|
5
|
+
const API_URL = "https://api.mug.work";
|
|
6
|
+
function prompt(question) {
|
|
7
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
rl.question(question, (answer) => {
|
|
10
|
+
rl.close();
|
|
11
|
+
resolve(answer.trim());
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function credentialsPath() {
|
|
16
|
+
return join(homedir(), ".mug", "credentials");
|
|
17
|
+
}
|
|
18
|
+
export function readCredentials() {
|
|
19
|
+
const path = credentialsPath();
|
|
20
|
+
if (!existsSync(path))
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function getAccountToken() {
|
|
30
|
+
const creds = readCredentials();
|
|
31
|
+
if (!creds) {
|
|
32
|
+
console.error("Not logged in. Run `mug login` first.");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
return creds.token;
|
|
36
|
+
}
|
|
37
|
+
export function ensureDevEmail(mugJsonPath) {
|
|
38
|
+
const creds = readCredentials();
|
|
39
|
+
if (!creds?.email)
|
|
40
|
+
return;
|
|
41
|
+
if (!existsSync(mugJsonPath))
|
|
42
|
+
return;
|
|
43
|
+
try {
|
|
44
|
+
const config = JSON.parse(readFileSync(mugJsonPath, "utf-8"));
|
|
45
|
+
if (config.dev?.email)
|
|
46
|
+
return;
|
|
47
|
+
config.dev = { ...config.dev, email: creds.email };
|
|
48
|
+
writeFileSync(mugJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
}
|
|
52
|
+
function saveCredentials(creds) {
|
|
53
|
+
const path = credentialsPath();
|
|
54
|
+
const dir = dirname(path);
|
|
55
|
+
if (!existsSync(dir))
|
|
56
|
+
mkdirSync(dir, { recursive: true });
|
|
57
|
+
writeFileSync(path, JSON.stringify(creds, null, 2) + "\n", { mode: 0o600 });
|
|
58
|
+
}
|
|
59
|
+
export async function login() {
|
|
60
|
+
const email = await prompt("Email: ");
|
|
61
|
+
if (!email || !email.includes("@")) {
|
|
62
|
+
console.error("Valid email required.");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
console.log(`Sending verification code to ${email}...`);
|
|
66
|
+
const sendRes = await fetch(`${API_URL}/auth/send-code`, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: { "Content-Type": "application/json" },
|
|
69
|
+
body: JSON.stringify({ email }),
|
|
70
|
+
});
|
|
71
|
+
if (!sendRes.ok) {
|
|
72
|
+
const err = (await sendRes.json());
|
|
73
|
+
console.error(`Failed: ${err.error ?? sendRes.statusText}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const code = await prompt("Verification code: ");
|
|
77
|
+
if (!code) {
|
|
78
|
+
console.error("No code provided.");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const verifyRes = await fetch(`${API_URL}/auth/verify`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: { "Content-Type": "application/json" },
|
|
84
|
+
body: JSON.stringify({ email, code }),
|
|
85
|
+
});
|
|
86
|
+
if (!verifyRes.ok) {
|
|
87
|
+
const err = (await verifyRes.json());
|
|
88
|
+
console.error(`Verification failed: ${err.error ?? verifyRes.statusText}`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
const data = (await verifyRes.json());
|
|
92
|
+
saveCredentials({ email: data.email, token: data.token });
|
|
93
|
+
console.log(`\nLogged in as ${data.email}`);
|
|
94
|
+
console.log(`Credentials saved to ~/.mug/credentials`);
|
|
95
|
+
}
|
|
96
|
+
export async function whoami() {
|
|
97
|
+
const creds = readCredentials();
|
|
98
|
+
if (!creds) {
|
|
99
|
+
console.log("Not logged in. Run `mug login` first.");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const res = await fetch(`${API_URL}/auth/me`, {
|
|
103
|
+
headers: { Authorization: `Bearer ${creds.token}` },
|
|
104
|
+
});
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
if (res.status === 401) {
|
|
107
|
+
console.log("Session expired. Run `mug login` to re-authenticate.");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
console.error(`Failed: ${res.statusText}`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
const data = (await res.json());
|
|
114
|
+
console.log(`Account: ${data.email}`);
|
|
115
|
+
const cwd = process.cwd();
|
|
116
|
+
const mugJsonPath = join(cwd, "mug.json");
|
|
117
|
+
if (existsSync(mugJsonPath)) {
|
|
118
|
+
const config = JSON.parse(readFileSync(mugJsonPath, "utf-8"));
|
|
119
|
+
const ws = data.workspaces.find((w) => w.id === config.id || w.name === config.name);
|
|
120
|
+
if (ws) {
|
|
121
|
+
console.log(`Workspace: ${ws.name} (${ws.id})`);
|
|
122
|
+
console.log(`Plan: ${ws.plan_tier}`);
|
|
123
|
+
console.log(`Role: ${ws.role}`);
|
|
124
|
+
if (ws.status === "archived")
|
|
125
|
+
console.log(`Status: archived`);
|
|
126
|
+
}
|
|
127
|
+
else if (config.name) {
|
|
128
|
+
console.log(`Workspace: ${config.name} (local only — not registered on platform)`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const active = data.workspaces.filter((w) => w.status !== "archived");
|
|
132
|
+
const archived = data.workspaces.filter((w) => w.status === "archived");
|
|
133
|
+
if (active.length > 0) {
|
|
134
|
+
console.log(`\nWorkspaces:`);
|
|
135
|
+
for (const ws of active) {
|
|
136
|
+
const domain = ws.subdomain ? `${ws.subdomain}.mug.work` : `${ws.name}.mug.work`;
|
|
137
|
+
console.log(` ${ws.name} (${ws.plan_tier}) — ${ws.role} ${domain}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (archived.length > 0) {
|
|
141
|
+
console.log(`\nArchived:`);
|
|
142
|
+
for (const ws of archived) {
|
|
143
|
+
const days = ws.archived_at ? Math.floor((Date.now() - new Date(ws.archived_at).getTime()) / 86400000) : 0;
|
|
144
|
+
const remaining = 365 - days;
|
|
145
|
+
console.log(` ${ws.name} — ${remaining} days until deletion`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const incoming = data.pendingInvites ?? [];
|
|
149
|
+
if (incoming.length > 0) {
|
|
150
|
+
console.log(`\nPending invites:`);
|
|
151
|
+
for (const inv of incoming) {
|
|
152
|
+
console.log(` #${inv.id} — ${inv.workspace_name} (${inv.role}), from ${inv.invited_by_email}`);
|
|
153
|
+
}
|
|
154
|
+
console.log(` Use "mug account accept <id>" or "mug account decline <id>"`);
|
|
155
|
+
}
|
|
156
|
+
const sent = (data.sentInvites ?? []).filter((i) => i.status === "pending");
|
|
157
|
+
if (sent.length > 0) {
|
|
158
|
+
console.log(`\nSent invites:`);
|
|
159
|
+
for (const inv of sent) {
|
|
160
|
+
console.log(` #${inv.id} — ${inv.email} → ${inv.workspace_name} (${inv.role})`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getAccountToken } from "./login.js";
|
|
4
|
+
const DEV_PORTS = [8787, 8788, 8789, 8790, 8791];
|
|
5
|
+
async function tryLogs(port, workflow, limit = 10) {
|
|
6
|
+
try {
|
|
7
|
+
const path = workflow ? `/logs/${workflow}?limit=${limit}` : `/logs?limit=${limit}`;
|
|
8
|
+
const res = await fetch(`http://localhost:${port}${path}`, {
|
|
9
|
+
signal: AbortSignal.timeout(5000),
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok)
|
|
12
|
+
return null;
|
|
13
|
+
return await res.json();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function formatLogs(data) {
|
|
20
|
+
if (data.length === 0)
|
|
21
|
+
return "(no runs)";
|
|
22
|
+
const lines = [];
|
|
23
|
+
for (const run of data) {
|
|
24
|
+
const icon = run.status === "complete" ? "+" : "x";
|
|
25
|
+
const dur = run.duration_ms != null ? `${run.duration_ms}ms` : "?";
|
|
26
|
+
const time = run.started_at.replace("T", " ").replace(/\.\d+Z$/, "");
|
|
27
|
+
lines.push(`[${icon}] ${run.workflow} — ${run.status} (${dur}) @ ${time}`);
|
|
28
|
+
lines.push(` id: ${run.id}`);
|
|
29
|
+
if (run.error) {
|
|
30
|
+
lines.push(` error: ${run.error}`);
|
|
31
|
+
}
|
|
32
|
+
if (run.steps && run.steps.length > 0) {
|
|
33
|
+
for (const step of run.steps) {
|
|
34
|
+
const dur = step.duration_ms != null ? ` (${step.duration_ms}ms)` : "";
|
|
35
|
+
const tokens = step.tokens_used ? ` [${step.tokens_used} tokens]` : "";
|
|
36
|
+
const err = step.error ? ` — ERROR: ${step.error}` : "";
|
|
37
|
+
const out = step.output && !step.error ? ` → ${step.output}` : "";
|
|
38
|
+
lines.push(` ${step.step_name}${dur}${tokens}${out}${err}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
lines.push("");
|
|
42
|
+
}
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
45
|
+
async function fetchProductionLogs(workflow, limit) {
|
|
46
|
+
const mugJsonPath = join(process.cwd(), "mug.json");
|
|
47
|
+
if (!existsSync(mugJsonPath)) {
|
|
48
|
+
console.error("No mug.json found. Run `mug init` first.");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const config = JSON.parse(readFileSync(mugJsonPath, "utf-8"));
|
|
52
|
+
const name = config.name;
|
|
53
|
+
const token = getAccountToken();
|
|
54
|
+
const params = new URLSearchParams({ limit: String(limit) });
|
|
55
|
+
if (workflow)
|
|
56
|
+
params.set("workflow", workflow);
|
|
57
|
+
const res = await fetch(`https://api.mug.work/workspace/${name}/logs?${params}`, {
|
|
58
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
59
|
+
signal: AbortSignal.timeout(10000),
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
console.error(`Failed to fetch production logs: ${res.status} ${await res.text()}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
return res.json();
|
|
66
|
+
}
|
|
67
|
+
export async function logs(workflow, opts) {
|
|
68
|
+
const limit = parseInt(opts.limit ?? "10");
|
|
69
|
+
const port = opts.port ? parseInt(opts.port) : undefined;
|
|
70
|
+
if (opts.production) {
|
|
71
|
+
const result = await fetchProductionLogs(workflow, limit);
|
|
72
|
+
if (opts.json) {
|
|
73
|
+
console.log(JSON.stringify(result, null, 2));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log(formatLogs(result));
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (port) {
|
|
81
|
+
const result = await tryLogs(port, workflow, limit);
|
|
82
|
+
if (!result) {
|
|
83
|
+
console.error(`Could not reach dev server on port ${port}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
if (opts.json) {
|
|
87
|
+
console.log(JSON.stringify(result, null, 2));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log(formatLogs(result));
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
for (const p of DEV_PORTS) {
|
|
95
|
+
const result = await tryLogs(p, workflow, limit);
|
|
96
|
+
if (result) {
|
|
97
|
+
if (opts.json) {
|
|
98
|
+
console.log(JSON.stringify(result, null, 2));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log(formatLogs(result));
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const result = await fetchProductionLogs(workflow, limit);
|
|
107
|
+
if (opts.json) {
|
|
108
|
+
console.log(JSON.stringify(result, null, 2));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log(formatLogs(result));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
function loadMugJson(dir) {
|
|
4
|
+
const path = join(dir, "mug.json");
|
|
5
|
+
if (!existsSync(path)) {
|
|
6
|
+
console.error("No mug.json found. Run `mug init` first.");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
10
|
+
}
|
|
11
|
+
function titleCase(kebab) {
|
|
12
|
+
return kebab
|
|
13
|
+
.split("-")
|
|
14
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
15
|
+
.join(" ");
|
|
16
|
+
}
|
|
17
|
+
export function portalInit(name) {
|
|
18
|
+
const dir = process.cwd();
|
|
19
|
+
const config = loadMugJson(dir);
|
|
20
|
+
const workspace = config.name;
|
|
21
|
+
const surfacesDir = join(dir, "src", "surfaces");
|
|
22
|
+
if (!existsSync(surfacesDir)) {
|
|
23
|
+
mkdirSync(surfacesDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
const configPath = join(surfacesDir, `${name}.json`);
|
|
26
|
+
if (existsSync(configPath)) {
|
|
27
|
+
console.error(`Surface config already exists: src/surfaces/${name}.json`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const portalConfig = {
|
|
31
|
+
type: "portal",
|
|
32
|
+
title: titleCase(name),
|
|
33
|
+
description: "",
|
|
34
|
+
access: { mode: "identify", method: "email" },
|
|
35
|
+
database: workspace,
|
|
36
|
+
tabs: [
|
|
37
|
+
{
|
|
38
|
+
id: "main",
|
|
39
|
+
label: titleCase(name),
|
|
40
|
+
sections: [
|
|
41
|
+
{
|
|
42
|
+
type: "table",
|
|
43
|
+
query: `SELECT id, name, status, created_at FROM your_table WHERE email = :user ORDER BY created_at DESC`,
|
|
44
|
+
primaryKey: "id",
|
|
45
|
+
pageSize: 25,
|
|
46
|
+
columns: [
|
|
47
|
+
{ key: "name", label: "Name" },
|
|
48
|
+
{ key: "status", label: "Status", badge: true },
|
|
49
|
+
{ key: "created_at", label: "Created", format: "date" },
|
|
50
|
+
],
|
|
51
|
+
detail: {
|
|
52
|
+
title: "{{name}}",
|
|
53
|
+
fields: [
|
|
54
|
+
{ key: "name", label: "Name" },
|
|
55
|
+
{ key: "status", label: "Status", badge: true },
|
|
56
|
+
{ key: "created_at", label: "Created", format: "datetime" },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
emptyMessage: "No records found.",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
writeFileSync(configPath, JSON.stringify(portalConfig, null, 2) + "\n");
|
|
66
|
+
console.log(` [+] src/surfaces/${name}.json`);
|
|
67
|
+
console.log(`\nPortal "${name}" ready. Edit the config to set your query and columns.`);
|
|
68
|
+
console.log(` mug dev`);
|
|
69
|
+
console.log(` open http://localhost:8787/${name}`);
|
|
70
|
+
}
|
|
71
|
+
export function portalList() {
|
|
72
|
+
const dir = process.cwd();
|
|
73
|
+
const config = loadMugJson(dir);
|
|
74
|
+
const workspace = config.name;
|
|
75
|
+
const surfacesDir = join(dir, "src", "surfaces");
|
|
76
|
+
if (!existsSync(surfacesDir)) {
|
|
77
|
+
console.log("No src/surfaces/ directory found.");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const files = readdirSync(surfacesDir).filter((f) => f.endsWith(".json"));
|
|
81
|
+
const portals = [];
|
|
82
|
+
for (const file of files) {
|
|
83
|
+
try {
|
|
84
|
+
const raw = readFileSync(join(surfacesDir, file), "utf-8");
|
|
85
|
+
const cfg = JSON.parse(raw);
|
|
86
|
+
if (cfg.type !== "portal")
|
|
87
|
+
continue;
|
|
88
|
+
const tabs = Array.isArray(cfg.tabs) ? cfg.tabs.length : 0;
|
|
89
|
+
portals.push({
|
|
90
|
+
file: `src/surfaces/${file}`,
|
|
91
|
+
id: file.replace(/\.json$/, ""),
|
|
92
|
+
title: cfg.title ?? "(untitled)",
|
|
93
|
+
tabs,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch { }
|
|
97
|
+
}
|
|
98
|
+
if (portals.length === 0) {
|
|
99
|
+
console.log("No portal surfaces found. Create one with: mug portal init <name>");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
console.log(`Portals in workspace "${workspace}":\n`);
|
|
103
|
+
for (const p of portals) {
|
|
104
|
+
console.log(` ${p.title} (${p.tabs} tab${p.tabs !== 1 ? "s" : ""})`);
|
|
105
|
+
console.log(` ID: ${p.id}`);
|
|
106
|
+
console.log(` File: ${p.file}`);
|
|
107
|
+
console.log(` Local: http://localhost:8787/${p.id}`);
|
|
108
|
+
console.log(` Prod: https://${workspace}.mug.work/${p.id}`);
|
|
109
|
+
console.log();
|
|
110
|
+
}
|
|
111
|
+
}
|