agentmomo 0.2.0 → 0.3.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/README.md +10 -2
- package/dist/assets/index-BWPCZthq.js +5940 -0
- package/dist/assets/index-cg8wGnKr.css +1 -0
- package/dist/claude-code-hook.cjs +5 -1
- package/dist/cli.js +42 -11
- package/dist/index.html +2 -2
- package/dist/models/NPC/ocean/brute-shark/Brute_Shark.png +0 -0
- package/dist/models/NPC/ocean/brute-shark/Brute_Shark_anim.fbx +0 -0
- package/dist/models/NPC/ocean/brute-shark/Brute_Shark_illum.png +0 -0
- package/dist/models/NPC/ocean/brute-shark/Brute_Shark_normal.png +0 -0
- package/dist/qwen-cli-hook.cjs +242 -0
- package/dist/server.js +490 -6
- package/package.json +5 -2
- package/dist/assets/index-5pVyUNvu.js +0 -5574
- package/dist/assets/index-BjOLTppo.css +0 -1
package/dist/server.js
CHANGED
|
@@ -49915,7 +49915,7 @@ import crypto3 from "crypto";
|
|
|
49915
49915
|
import fs2 from "fs";
|
|
49916
49916
|
import path3 from "path";
|
|
49917
49917
|
import { fileURLToPath } from "url";
|
|
49918
|
-
import { exec } from "child_process";
|
|
49918
|
+
import { exec, spawn } from "child_process";
|
|
49919
49919
|
|
|
49920
49920
|
// node_modules/ws/wrapper.mjs
|
|
49921
49921
|
var import_stream = __toESM(require_stream2(), 1);
|
|
@@ -55181,6 +55181,12 @@ async function checkAndDeductCredits(userId, event) {
|
|
|
55181
55181
|
async function getCreditBalance(userId) {
|
|
55182
55182
|
const supabase2 = getSupabase();
|
|
55183
55183
|
if (!supabase2) return null;
|
|
55184
|
+
const { error: rechargeError } = await supabase2.rpc("recharge_credits", {
|
|
55185
|
+
p_user_id: userId
|
|
55186
|
+
});
|
|
55187
|
+
if (rechargeError && !rechargeError.message.toLowerCase().includes("recharge_credits")) {
|
|
55188
|
+
console.warn("[credits] recharge_credits failed:", rechargeError.message);
|
|
55189
|
+
}
|
|
55184
55190
|
const { data, error } = await supabase2.from("user_profiles").select("credits_remaining, credits_used_total, tier").eq("id", userId).single();
|
|
55185
55191
|
if (error || !data) return null;
|
|
55186
55192
|
return {
|
|
@@ -55210,6 +55216,7 @@ var WRAPPER_JS = AGENTSMOMO_DIST_DIR ? path3.join(AGENTSMOMO_DIST_DIR, "stdio-wr
|
|
|
55210
55216
|
var HOOK_JS = AGENTSMOMO_DIST_DIR ? path3.join(AGENTSMOMO_DIST_DIR, "claude-code-hook.cjs") : path3.join(process.cwd(), "proxy", "dist", "claude-code-hook.cjs");
|
|
55211
55217
|
var GEMINI_HOOK_JS = AGENTSMOMO_DIST_DIR ? path3.join(AGENTSMOMO_DIST_DIR, "gemini-cli-hook.cjs") : path3.join(process.cwd(), "proxy", "dist", "gemini-cli-hook.cjs");
|
|
55212
55218
|
var CODEX_HOOK_JS = AGENTSMOMO_DIST_DIR ? path3.join(AGENTSMOMO_DIST_DIR, "codex-cli-hook.cjs") : path3.join(process.cwd(), "proxy", "dist", "codex-cli-hook.cjs");
|
|
55219
|
+
var QWEN_HOOK_JS = AGENTSMOMO_DIST_DIR ? path3.join(AGENTSMOMO_DIST_DIR, "qwen-cli-hook.cjs") : path3.join(process.cwd(), "proxy", "dist", "qwen-cli-hook.cjs");
|
|
55213
55220
|
function projectSettingsPath(projectPath) {
|
|
55214
55221
|
return path3.join(projectPath, ".claude", "settings.json");
|
|
55215
55222
|
}
|
|
@@ -55342,6 +55349,10 @@ var config = {
|
|
|
55342
55349
|
var PORT = Number(process.env.PORT ?? config.httpProxy.port);
|
|
55343
55350
|
var DIST_DIR = AGENTSMOMO_DIST_DIR ?? path3.join(process.cwd(), "dist");
|
|
55344
55351
|
var INDEX_HTML = path3.join(DIST_DIR, "index.html");
|
|
55352
|
+
var IS_PACKAGED_COMPANION = Boolean(AGENTSMOMO_DIST_DIR);
|
|
55353
|
+
var LOCAL_UI_ENABLED = process.env.AGENTMOMO_LOCAL_UI === "1";
|
|
55354
|
+
var HOSTED_APP_URL = process.env.AGENTMOMO_APP_URL?.trim() ?? "https://agentmomo.net/app";
|
|
55355
|
+
var SHOULD_SERVE_STATIC_UI = fs2.existsSync(INDEX_HTML) && (!IS_PACKAGED_COMPANION || LOCAL_UI_ENABLED);
|
|
55345
55356
|
var FEEDBACK_WINDOW_MS = 6e4;
|
|
55346
55357
|
var FEEDBACK_MAX_PER_WINDOW = 5;
|
|
55347
55358
|
var feedbackIpHits = /* @__PURE__ */ new Map();
|
|
@@ -55393,6 +55404,281 @@ function allowFeedbackFromIp(ip) {
|
|
|
55393
55404
|
return true;
|
|
55394
55405
|
}
|
|
55395
55406
|
var app = (0, import_express2.default)();
|
|
55407
|
+
function deriveOpenworldAgentPrompt(agentDefinition) {
|
|
55408
|
+
if (agentDefinition.mode === "prompt") {
|
|
55409
|
+
const prompt2 = typeof agentDefinition.prompt === "string" ? agentDefinition.prompt.trim() : "";
|
|
55410
|
+
if (!prompt2) {
|
|
55411
|
+
throw new Error("prompt is empty.");
|
|
55412
|
+
}
|
|
55413
|
+
return prompt2;
|
|
55414
|
+
}
|
|
55415
|
+
let prompt = typeof agentDefinition.goal === "string" ? agentDefinition.goal.trim() : "";
|
|
55416
|
+
if (!prompt) {
|
|
55417
|
+
throw new Error("subagent goal is empty.");
|
|
55418
|
+
}
|
|
55419
|
+
if (agentDefinition.name) {
|
|
55420
|
+
prompt = `You are ${agentDefinition.name}${agentDefinition.role ? `, ${agentDefinition.role}` : ""}.
|
|
55421
|
+
|
|
55422
|
+
${prompt}`;
|
|
55423
|
+
}
|
|
55424
|
+
return prompt;
|
|
55425
|
+
}
|
|
55426
|
+
function buildSpawnTarget(tool, prompt, skipPermissions) {
|
|
55427
|
+
if (process.platform === "win32") {
|
|
55428
|
+
const safe = prompt.replace(/[\r\n]+/g, " ").replace(/"/g, '""');
|
|
55429
|
+
switch (tool) {
|
|
55430
|
+
case "claude":
|
|
55431
|
+
return {
|
|
55432
|
+
command: skipPermissions ? `claude --dangerously-skip-permissions -p "${safe}"` : `claude -p "${safe}"`,
|
|
55433
|
+
args: []
|
|
55434
|
+
};
|
|
55435
|
+
case "gemini":
|
|
55436
|
+
return {
|
|
55437
|
+
command: skipPermissions ? `gemini --sandbox=none -p "${safe}"` : `gemini -p "${safe}"`,
|
|
55438
|
+
args: []
|
|
55439
|
+
};
|
|
55440
|
+
case "codex":
|
|
55441
|
+
return {
|
|
55442
|
+
command: skipPermissions ? `codex exec --full-auto "${safe}"` : `codex exec "${safe}"`,
|
|
55443
|
+
args: []
|
|
55444
|
+
};
|
|
55445
|
+
case "qwen":
|
|
55446
|
+
return {
|
|
55447
|
+
command: skipPermissions ? `qwen --experimental-hooks --approval-mode=yolo -p "${safe}"` : `qwen --experimental-hooks -p "${safe}"`,
|
|
55448
|
+
args: []
|
|
55449
|
+
};
|
|
55450
|
+
default:
|
|
55451
|
+
throw new Error("Unknown tool.");
|
|
55452
|
+
}
|
|
55453
|
+
}
|
|
55454
|
+
switch (tool) {
|
|
55455
|
+
case "claude":
|
|
55456
|
+
return {
|
|
55457
|
+
command: "claude",
|
|
55458
|
+
args: skipPermissions ? ["--dangerously-skip-permissions", "-p", prompt] : ["-p", prompt]
|
|
55459
|
+
};
|
|
55460
|
+
case "gemini":
|
|
55461
|
+
return {
|
|
55462
|
+
command: "gemini",
|
|
55463
|
+
args: skipPermissions ? ["--sandbox=none", "-p", prompt] : ["-p", prompt]
|
|
55464
|
+
};
|
|
55465
|
+
case "codex":
|
|
55466
|
+
return {
|
|
55467
|
+
command: "codex",
|
|
55468
|
+
args: skipPermissions ? ["exec", "--full-auto", prompt] : ["exec", prompt]
|
|
55469
|
+
};
|
|
55470
|
+
case "qwen":
|
|
55471
|
+
return {
|
|
55472
|
+
command: "qwen",
|
|
55473
|
+
args: skipPermissions ? ["--experimental-hooks", "--approval-mode=yolo", "-p", prompt] : ["--experimental-hooks", "-p", prompt]
|
|
55474
|
+
};
|
|
55475
|
+
default:
|
|
55476
|
+
throw new Error("Unknown tool.");
|
|
55477
|
+
}
|
|
55478
|
+
}
|
|
55479
|
+
function spawnPromptDetached(tool, prompt, cwd, skipPermissions) {
|
|
55480
|
+
const { command, args } = buildSpawnTarget(tool, prompt, skipPermissions);
|
|
55481
|
+
const child = spawn(command, args, {
|
|
55482
|
+
cwd,
|
|
55483
|
+
detached: true,
|
|
55484
|
+
stdio: "ignore",
|
|
55485
|
+
shell: process.platform === "win32",
|
|
55486
|
+
windowsHide: true
|
|
55487
|
+
});
|
|
55488
|
+
child.on("error", (error) => {
|
|
55489
|
+
console.error(`[openworld/spawn-agent] ${tool} error:`, error.message);
|
|
55490
|
+
});
|
|
55491
|
+
child.unref();
|
|
55492
|
+
}
|
|
55493
|
+
function getOpenworldChatTimeoutMs(tool) {
|
|
55494
|
+
const sharedFromEnv = Number(process.env.AGENTMOMO_OPENWORLD_CHAT_TIMEOUT_MS);
|
|
55495
|
+
if (Number.isFinite(sharedFromEnv) && sharedFromEnv > 0) {
|
|
55496
|
+
return Math.floor(sharedFromEnv);
|
|
55497
|
+
}
|
|
55498
|
+
if (tool === "gemini") {
|
|
55499
|
+
const geminiFromEnv = Number(process.env.AGENTMOMO_GEMINI_CHAT_TIMEOUT_MS);
|
|
55500
|
+
if (Number.isFinite(geminiFromEnv) && geminiFromEnv > 0) {
|
|
55501
|
+
return Math.floor(geminiFromEnv);
|
|
55502
|
+
}
|
|
55503
|
+
return 3e5;
|
|
55504
|
+
}
|
|
55505
|
+
if (tool === "codex") {
|
|
55506
|
+
const codexFromEnv = Number(process.env.AGENTMOMO_CODEX_CHAT_TIMEOUT_MS);
|
|
55507
|
+
if (Number.isFinite(codexFromEnv) && codexFromEnv > 0) {
|
|
55508
|
+
return Math.floor(codexFromEnv);
|
|
55509
|
+
}
|
|
55510
|
+
return 42e4;
|
|
55511
|
+
}
|
|
55512
|
+
return 12e4;
|
|
55513
|
+
}
|
|
55514
|
+
function runPromptCaptured(tool, prompt, cwd, skipPermissions, openworldAgentId) {
|
|
55515
|
+
const { command, args } = buildSpawnTarget(tool, prompt, skipPermissions);
|
|
55516
|
+
const childEnv = openworldAgentId ? { ...process.env, AGENTMOMO_OPENWORLD_AGENT_ID: openworldAgentId } : void 0;
|
|
55517
|
+
return new Promise((resolve, reject) => {
|
|
55518
|
+
const child = spawn(command, args, {
|
|
55519
|
+
cwd,
|
|
55520
|
+
env: childEnv,
|
|
55521
|
+
shell: process.platform === "win32",
|
|
55522
|
+
windowsHide: true,
|
|
55523
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
55524
|
+
});
|
|
55525
|
+
let stdout = "";
|
|
55526
|
+
let stderr = "";
|
|
55527
|
+
let settled = false;
|
|
55528
|
+
const timeoutMs = getOpenworldChatTimeoutMs(
|
|
55529
|
+
tool
|
|
55530
|
+
);
|
|
55531
|
+
const timeoutId = setTimeout(() => {
|
|
55532
|
+
if (settled) {
|
|
55533
|
+
return;
|
|
55534
|
+
}
|
|
55535
|
+
settled = true;
|
|
55536
|
+
child.kill();
|
|
55537
|
+
reject(
|
|
55538
|
+
new Error(
|
|
55539
|
+
`${tool} did not finish before the timeout (${Math.round(timeoutMs / 1e3)}s).`
|
|
55540
|
+
)
|
|
55541
|
+
);
|
|
55542
|
+
}, timeoutMs);
|
|
55543
|
+
child.stdout.on("data", (chunk) => {
|
|
55544
|
+
stdout += chunk.toString();
|
|
55545
|
+
});
|
|
55546
|
+
child.stderr.on("data", (chunk) => {
|
|
55547
|
+
stderr += chunk.toString();
|
|
55548
|
+
});
|
|
55549
|
+
child.on("error", (error) => {
|
|
55550
|
+
if (settled) {
|
|
55551
|
+
return;
|
|
55552
|
+
}
|
|
55553
|
+
settled = true;
|
|
55554
|
+
clearTimeout(timeoutId);
|
|
55555
|
+
reject(error);
|
|
55556
|
+
});
|
|
55557
|
+
child.on("close", (code) => {
|
|
55558
|
+
if (settled) {
|
|
55559
|
+
return;
|
|
55560
|
+
}
|
|
55561
|
+
settled = true;
|
|
55562
|
+
clearTimeout(timeoutId);
|
|
55563
|
+
if (code === 0) {
|
|
55564
|
+
resolve({ stdout: stdout.trim(), stderr: stderr.trim() });
|
|
55565
|
+
return;
|
|
55566
|
+
}
|
|
55567
|
+
reject(
|
|
55568
|
+
new Error(
|
|
55569
|
+
stderr.trim() || stdout.trim() || `${tool} exited with code ${code}.`
|
|
55570
|
+
)
|
|
55571
|
+
);
|
|
55572
|
+
});
|
|
55573
|
+
});
|
|
55574
|
+
}
|
|
55575
|
+
function buildOpenworldConversationPrompt(agentDefinition, message, history) {
|
|
55576
|
+
const MAX_HISTORY_TURNS = 6;
|
|
55577
|
+
const MAX_TURN_CHARS = 1200;
|
|
55578
|
+
const toCompactText = (value) => {
|
|
55579
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
55580
|
+
if (compact.length <= MAX_TURN_CHARS) {
|
|
55581
|
+
return compact;
|
|
55582
|
+
}
|
|
55583
|
+
return `${compact.slice(0, MAX_TURN_CHARS)}...`;
|
|
55584
|
+
};
|
|
55585
|
+
const agentContext = (() => {
|
|
55586
|
+
if (agentDefinition.mode === "prompt") {
|
|
55587
|
+
return typeof agentDefinition.prompt === "string" ? agentDefinition.prompt.trim() : "";
|
|
55588
|
+
}
|
|
55589
|
+
const goal = typeof agentDefinition.goal === "string" ? agentDefinition.goal.trim() : "";
|
|
55590
|
+
if (!goal) {
|
|
55591
|
+
return "";
|
|
55592
|
+
}
|
|
55593
|
+
if (agentDefinition.name) {
|
|
55594
|
+
return `You are ${agentDefinition.name}${agentDefinition.role ? `, ${agentDefinition.role}` : ""}.
|
|
55595
|
+
|
|
55596
|
+
${goal}`;
|
|
55597
|
+
}
|
|
55598
|
+
return goal;
|
|
55599
|
+
})();
|
|
55600
|
+
const recentHistory = history.slice(-MAX_HISTORY_TURNS);
|
|
55601
|
+
const historyBlock = recentHistory.length ? recentHistory.map(
|
|
55602
|
+
(turn, index) => `${index + 1}. ${turn.role === "user" ? "User" : "Assistant"}: ${toCompactText(turn.content)}`
|
|
55603
|
+
).join("\n") : "";
|
|
55604
|
+
const parts = [
|
|
55605
|
+
"You are responding inside the AgentMomo Openworld terminal.",
|
|
55606
|
+
agentContext ? `Agent instructions:
|
|
55607
|
+
${agentContext}` : "",
|
|
55608
|
+
historyBlock ? `Recent conversation:
|
|
55609
|
+
${historyBlock}` : "",
|
|
55610
|
+
`Latest user message:
|
|
55611
|
+
${message}`,
|
|
55612
|
+
"Respond with the final answer to the latest user message. If tool work is needed, still provide a direct final response."
|
|
55613
|
+
].filter(Boolean);
|
|
55614
|
+
return parts.join("\n\n");
|
|
55615
|
+
}
|
|
55616
|
+
function getOpenworldChatProvider(source) {
|
|
55617
|
+
if (typeof source !== "string") {
|
|
55618
|
+
return null;
|
|
55619
|
+
}
|
|
55620
|
+
if (source.startsWith("qwen")) {
|
|
55621
|
+
return "qwen";
|
|
55622
|
+
}
|
|
55623
|
+
if (source.startsWith("gemini")) {
|
|
55624
|
+
return "gemini";
|
|
55625
|
+
}
|
|
55626
|
+
if (source.startsWith("codex")) {
|
|
55627
|
+
return "codex";
|
|
55628
|
+
}
|
|
55629
|
+
if (source.startsWith("claude")) {
|
|
55630
|
+
return "claude";
|
|
55631
|
+
}
|
|
55632
|
+
return null;
|
|
55633
|
+
}
|
|
55634
|
+
async function handleOpenworldConversation(tool, req, res) {
|
|
55635
|
+
const {
|
|
55636
|
+
agentId,
|
|
55637
|
+
agentDefinition,
|
|
55638
|
+
history,
|
|
55639
|
+
message,
|
|
55640
|
+
projectPath,
|
|
55641
|
+
skipPermissions
|
|
55642
|
+
} = req.body ?? {};
|
|
55643
|
+
if (!agentDefinition || !agentDefinition.mode) {
|
|
55644
|
+
res.status(400).json({ error: "Missing agentDefinition." });
|
|
55645
|
+
return;
|
|
55646
|
+
}
|
|
55647
|
+
const trimmedMessage = typeof message === "string" ? message.trim() : "";
|
|
55648
|
+
if (!trimmedMessage) {
|
|
55649
|
+
res.status(400).json({ error: "Message is empty." });
|
|
55650
|
+
return;
|
|
55651
|
+
}
|
|
55652
|
+
const cwd = typeof projectPath === "string" && projectPath.trim() ? projectPath.trim() : process.cwd();
|
|
55653
|
+
const shouldSkipPermissions = skipPermissions === true;
|
|
55654
|
+
const safeHistory = Array.isArray(history) ? history.filter(
|
|
55655
|
+
(turn) => turn && (turn.role === "user" || turn.role === "assistant") && typeof turn.content === "string" && turn.content.trim().length > 0
|
|
55656
|
+
) : [];
|
|
55657
|
+
const prompt = buildOpenworldConversationPrompt(
|
|
55658
|
+
agentDefinition,
|
|
55659
|
+
trimmedMessage,
|
|
55660
|
+
safeHistory
|
|
55661
|
+
);
|
|
55662
|
+
try {
|
|
55663
|
+
const result = await runPromptCaptured(
|
|
55664
|
+
tool,
|
|
55665
|
+
prompt,
|
|
55666
|
+
cwd,
|
|
55667
|
+
shouldSkipPermissions,
|
|
55668
|
+
typeof agentId === "string" && agentId ? agentId : void 0
|
|
55669
|
+
);
|
|
55670
|
+
res.json({
|
|
55671
|
+
ok: true,
|
|
55672
|
+
reply: result.stdout || result.stderr,
|
|
55673
|
+
cwd
|
|
55674
|
+
});
|
|
55675
|
+
} catch (error) {
|
|
55676
|
+
const label = tool.charAt(0).toUpperCase() + tool.slice(1);
|
|
55677
|
+
res.status(500).json({
|
|
55678
|
+
error: error instanceof Error ? error.message : `${label} chat request failed.`
|
|
55679
|
+
});
|
|
55680
|
+
}
|
|
55681
|
+
}
|
|
55396
55682
|
app.use(
|
|
55397
55683
|
import_express2.default.json({
|
|
55398
55684
|
verify: (req, _res, buf) => {
|
|
@@ -55459,6 +55745,40 @@ app.post("/api/event", requireAuth, async (req, res) => {
|
|
|
55459
55745
|
}
|
|
55460
55746
|
processEvent(event);
|
|
55461
55747
|
broadcast(event);
|
|
55748
|
+
const openworldAgentId = event.metadata?.openworldAgentId;
|
|
55749
|
+
const provider = getOpenworldChatProvider(event.source);
|
|
55750
|
+
if (typeof openworldAgentId === "string" && openworldAgentId && provider && (event.event === "call_start" || event.event === "call_end")) {
|
|
55751
|
+
const activityEvent = {
|
|
55752
|
+
agentId: openworldAgentId,
|
|
55753
|
+
agentName: openworldAgentId,
|
|
55754
|
+
source: "manual",
|
|
55755
|
+
event: "openworld_chat_activity",
|
|
55756
|
+
toolName: event.toolName,
|
|
55757
|
+
message: event.message,
|
|
55758
|
+
timestamp: event.timestamp,
|
|
55759
|
+
requestId: `${event.requestId}-openworld-activity`,
|
|
55760
|
+
metadata: {
|
|
55761
|
+
provider
|
|
55762
|
+
}
|
|
55763
|
+
};
|
|
55764
|
+
broadcast(activityEvent);
|
|
55765
|
+
}
|
|
55766
|
+
if (typeof openworldAgentId === "string" && openworldAgentId && event.source === "claude-code-teammate") {
|
|
55767
|
+
const teammateEvent = {
|
|
55768
|
+
agentId: event.agentId,
|
|
55769
|
+
agentName: event.agentName,
|
|
55770
|
+
source: "claude-code-teammate",
|
|
55771
|
+
event: "openworld_teammate_register",
|
|
55772
|
+
toolName: "",
|
|
55773
|
+
timestamp: event.timestamp,
|
|
55774
|
+
requestId: `${event.requestId}-openworld-teammate`,
|
|
55775
|
+
metadata: {
|
|
55776
|
+
...event.metadata,
|
|
55777
|
+
openworldAgentId
|
|
55778
|
+
}
|
|
55779
|
+
};
|
|
55780
|
+
broadcast(teammateEvent);
|
|
55781
|
+
}
|
|
55462
55782
|
if (creditResult.cost > 0 && creditResult.balance >= 0) {
|
|
55463
55783
|
sendToUser(req.userId, {
|
|
55464
55784
|
agentId: "__system__",
|
|
@@ -55512,6 +55832,53 @@ app.post("/api/event", requireAuth, async (req, res) => {
|
|
|
55512
55832
|
app.get("/api/health", (_req, res) => {
|
|
55513
55833
|
res.json({ status: "ok", agents: getAllAgents().length });
|
|
55514
55834
|
});
|
|
55835
|
+
app.post("/api/openworld/spawn-agent", (req, res) => {
|
|
55836
|
+
const { tool, agentDefinition, projectPath, skipPermissions } = req.body ?? {};
|
|
55837
|
+
const allowedTools = ["claude", "codex", "gemini", "qwen"];
|
|
55838
|
+
if (!tool || !allowedTools.includes(String(tool))) {
|
|
55839
|
+
res.status(400).json({ error: "Invalid tool. Must be claude, codex, gemini, or qwen." });
|
|
55840
|
+
return;
|
|
55841
|
+
}
|
|
55842
|
+
if (!agentDefinition || !agentDefinition.mode) {
|
|
55843
|
+
res.status(400).json({ error: "Missing agentDefinition." });
|
|
55844
|
+
return;
|
|
55845
|
+
}
|
|
55846
|
+
let prompt;
|
|
55847
|
+
try {
|
|
55848
|
+
prompt = deriveOpenworldAgentPrompt(
|
|
55849
|
+
agentDefinition
|
|
55850
|
+
);
|
|
55851
|
+
} catch (error) {
|
|
55852
|
+
res.status(400).json({
|
|
55853
|
+
error: error instanceof Error ? error.message : "Invalid agent prompt."
|
|
55854
|
+
});
|
|
55855
|
+
return;
|
|
55856
|
+
}
|
|
55857
|
+
const cwd = typeof projectPath === "string" && projectPath.trim() ? projectPath.trim() : process.cwd();
|
|
55858
|
+
const shouldSkipPermissions = skipPermissions === true;
|
|
55859
|
+
try {
|
|
55860
|
+
spawnPromptDetached(String(tool), prompt, cwd, shouldSkipPermissions);
|
|
55861
|
+
} catch (error) {
|
|
55862
|
+
res.status(400).json({
|
|
55863
|
+
error: error instanceof Error ? error.message : "Unknown tool."
|
|
55864
|
+
});
|
|
55865
|
+
return;
|
|
55866
|
+
}
|
|
55867
|
+
console.log(`[openworld/spawn-agent] Spawned ${tool} in ${cwd}`);
|
|
55868
|
+
res.json({ ok: true, tool, cwd });
|
|
55869
|
+
});
|
|
55870
|
+
app.post("/api/openworld/claude-chat", async (req, res) => {
|
|
55871
|
+
await handleOpenworldConversation("claude", req, res);
|
|
55872
|
+
});
|
|
55873
|
+
app.post("/api/openworld/codex-chat", async (req, res) => {
|
|
55874
|
+
await handleOpenworldConversation("codex", req, res);
|
|
55875
|
+
});
|
|
55876
|
+
app.post("/api/openworld/gemini-chat", async (req, res) => {
|
|
55877
|
+
await handleOpenworldConversation("gemini", req, res);
|
|
55878
|
+
});
|
|
55879
|
+
app.post("/api/openworld/qwen-chat", async (req, res) => {
|
|
55880
|
+
await handleOpenworldConversation("qwen", req, res);
|
|
55881
|
+
});
|
|
55515
55882
|
app.get("/.well-known/agent.json", (_req, res) => {
|
|
55516
55883
|
res.json({
|
|
55517
55884
|
name: "agentmomo",
|
|
@@ -56024,10 +56391,10 @@ app.post("/api/keys", requireAuth, async (req, res) => {
|
|
|
56024
56391
|
return;
|
|
56025
56392
|
}
|
|
56026
56393
|
const supabase2 = getSupabase();
|
|
56027
|
-
const name = req.body?.name || "
|
|
56394
|
+
const name = req.body?.name || "momo";
|
|
56028
56395
|
const isBootstrapRequest = req.get("x-agentmomo-key-bootstrap") === "1";
|
|
56029
|
-
if (isBootstrapRequest && name === "
|
|
56030
|
-
const { data: existingDefaultKeys } = await supabase2.from("api_keys").select("id, key_hash").eq("user_id", req.userId).eq("name", "
|
|
56396
|
+
if (isBootstrapRequest && name === "momo") {
|
|
56397
|
+
const { data: existingDefaultKeys } = await supabase2.from("api_keys").select("id, key_hash").eq("user_id", req.userId).eq("name", "momo").eq("is_active", true);
|
|
56031
56398
|
if (existingDefaultKeys && existingDefaultKeys.length > 0) {
|
|
56032
56399
|
const idsToDeactivate = existingDefaultKeys.map((k) => k.id);
|
|
56033
56400
|
await supabase2.from("api_keys").update({ is_active: false }).in("id", idsToDeactivate).eq("user_id", req.userId);
|
|
@@ -56189,6 +56556,8 @@ app.post("/api/claude-code-config", (req, res) => {
|
|
|
56189
56556
|
cfg.hooks.PostToolUse = [matcher];
|
|
56190
56557
|
cfg.hooks.TeammateIdle = [matcher];
|
|
56191
56558
|
cfg.hooks.TaskCompleted = [matcher];
|
|
56559
|
+
if (!cfg.env) cfg.env = {};
|
|
56560
|
+
cfg.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
|
|
56192
56561
|
} else {
|
|
56193
56562
|
const strip = (arr) => (arr ?? []).filter(
|
|
56194
56563
|
(e) => !e.hooks?.some(
|
|
@@ -56204,6 +56573,10 @@ app.post("/api/claude-code-config", (req, res) => {
|
|
|
56204
56573
|
if (!cfg.hooks.TeammateIdle.length) delete cfg.hooks.TeammateIdle;
|
|
56205
56574
|
if (!cfg.hooks.TaskCompleted.length) delete cfg.hooks.TaskCompleted;
|
|
56206
56575
|
if (!Object.keys(cfg.hooks).length) delete cfg.hooks;
|
|
56576
|
+
if (cfg.env) {
|
|
56577
|
+
delete cfg.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
56578
|
+
if (!Object.keys(cfg.env).length) delete cfg.env;
|
|
56579
|
+
}
|
|
56207
56580
|
}
|
|
56208
56581
|
fs2.writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
56209
56582
|
res.json({ ok: true, configPath });
|
|
@@ -56228,6 +56601,9 @@ app.post("/api/build-hook", (_req, res) => {
|
|
|
56228
56601
|
function geminiSettingsPath(projectPath) {
|
|
56229
56602
|
return path3.join(projectPath, ".gemini", "settings.json");
|
|
56230
56603
|
}
|
|
56604
|
+
function qwenSettingsPath(projectPath) {
|
|
56605
|
+
return path3.join(projectPath, ".qwen", "settings.json");
|
|
56606
|
+
}
|
|
56231
56607
|
app.get("/api/gemini-cli-config", (req, res) => {
|
|
56232
56608
|
const projectPath = req.query.project || process.cwd();
|
|
56233
56609
|
const configPath = geminiSettingsPath(projectPath);
|
|
@@ -56326,6 +56702,101 @@ app.post("/api/build-gemini-hook", (_req, res) => {
|
|
|
56326
56702
|
}
|
|
56327
56703
|
);
|
|
56328
56704
|
});
|
|
56705
|
+
app.get("/api/qwen-cli-config", (req, res) => {
|
|
56706
|
+
const projectPath = req.query.project || process.cwd();
|
|
56707
|
+
const configPath = qwenSettingsPath(projectPath);
|
|
56708
|
+
const hookBuilt = fs2.existsSync(QWEN_HOOK_JS);
|
|
56709
|
+
const configExists = fs2.existsSync(configPath);
|
|
56710
|
+
let config2 = null;
|
|
56711
|
+
let hooksInstalled = false;
|
|
56712
|
+
if (configExists) {
|
|
56713
|
+
try {
|
|
56714
|
+
config2 = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
|
|
56715
|
+
const preToolUse = config2?.hooks?.PreToolUse ?? [];
|
|
56716
|
+
hooksInstalled = preToolUse.some(
|
|
56717
|
+
(entry) => entry.hooks?.some(
|
|
56718
|
+
(hook) => String(hook.command ?? "").includes("qwen-cli-hook")
|
|
56719
|
+
)
|
|
56720
|
+
);
|
|
56721
|
+
} catch {
|
|
56722
|
+
config2 = null;
|
|
56723
|
+
}
|
|
56724
|
+
}
|
|
56725
|
+
res.json({
|
|
56726
|
+
configPath,
|
|
56727
|
+
projectPath,
|
|
56728
|
+
exists: configExists,
|
|
56729
|
+
config: config2,
|
|
56730
|
+
hooksInstalled,
|
|
56731
|
+
hookPath: QWEN_HOOK_JS,
|
|
56732
|
+
hookBuilt
|
|
56733
|
+
});
|
|
56734
|
+
});
|
|
56735
|
+
app.post("/api/qwen-cli-config", (req, res) => {
|
|
56736
|
+
const {
|
|
56737
|
+
action,
|
|
56738
|
+
projectPath: pp,
|
|
56739
|
+
apiKey
|
|
56740
|
+
} = req.body;
|
|
56741
|
+
const projectPath = pp || process.cwd();
|
|
56742
|
+
const configPath = qwenSettingsPath(projectPath);
|
|
56743
|
+
try {
|
|
56744
|
+
const configDir = path3.dirname(configPath);
|
|
56745
|
+
if (!fs2.existsSync(configDir)) fs2.mkdirSync(configDir, { recursive: true });
|
|
56746
|
+
let cfg = {};
|
|
56747
|
+
if (fs2.existsSync(configPath)) {
|
|
56748
|
+
cfg = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
|
|
56749
|
+
}
|
|
56750
|
+
if (!cfg.hooks) cfg.hooks = {};
|
|
56751
|
+
const hookCommandParts = [`node "${QWEN_HOOK_JS.replace(/\\/g, "/")}"`];
|
|
56752
|
+
if (apiKey && typeof apiKey === "string" && apiKey.startsWith("amk_")) {
|
|
56753
|
+
hookCommandParts.push(`--api-key ${apiKey}`);
|
|
56754
|
+
}
|
|
56755
|
+
const hookEntry = {
|
|
56756
|
+
name: "agentmomo",
|
|
56757
|
+
type: "command",
|
|
56758
|
+
command: hookCommandParts.join(" "),
|
|
56759
|
+
timeout: 2e3
|
|
56760
|
+
};
|
|
56761
|
+
const matcher = { matcher: ".*", hooks: [hookEntry] };
|
|
56762
|
+
if (action === "install") {
|
|
56763
|
+
cfg.hooks.PreToolUse = [matcher];
|
|
56764
|
+
cfg.hooks.PostToolUse = [matcher];
|
|
56765
|
+
cfg.hooks.SessionStart = [matcher];
|
|
56766
|
+
} else {
|
|
56767
|
+
const strip = (arr) => (arr ?? []).filter(
|
|
56768
|
+
(entry) => !entry.hooks?.some(
|
|
56769
|
+
(hook) => String(hook.command ?? "").includes("qwen-cli-hook")
|
|
56770
|
+
)
|
|
56771
|
+
);
|
|
56772
|
+
cfg.hooks.PreToolUse = strip(cfg.hooks.PreToolUse ?? []);
|
|
56773
|
+
cfg.hooks.PostToolUse = strip(cfg.hooks.PostToolUse ?? []);
|
|
56774
|
+
cfg.hooks.SessionStart = strip(cfg.hooks.SessionStart ?? []);
|
|
56775
|
+
if (!cfg.hooks.PreToolUse.length) delete cfg.hooks.PreToolUse;
|
|
56776
|
+
if (!cfg.hooks.PostToolUse.length) delete cfg.hooks.PostToolUse;
|
|
56777
|
+
if (!cfg.hooks.SessionStart.length) delete cfg.hooks.SessionStart;
|
|
56778
|
+
if (!Object.keys(cfg.hooks).length) delete cfg.hooks;
|
|
56779
|
+
}
|
|
56780
|
+
fs2.writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
56781
|
+
res.json({ ok: true, configPath });
|
|
56782
|
+
} catch (err) {
|
|
56783
|
+
res.status(500).json({ error: err.message });
|
|
56784
|
+
}
|
|
56785
|
+
});
|
|
56786
|
+
app.post("/api/build-qwen-hook", (_req, res) => {
|
|
56787
|
+
if (AGENTSMOMO_DIST_DIR) {
|
|
56788
|
+
res.json({ ok: true });
|
|
56789
|
+
return;
|
|
56790
|
+
}
|
|
56791
|
+
exec(
|
|
56792
|
+
`npx esbuild proxy/qwen-cli-hook.ts --bundle --platform=node --format=cjs --outfile=proxy/dist/qwen-cli-hook.cjs`,
|
|
56793
|
+
{ cwd: process.cwd() },
|
|
56794
|
+
(err, _stdout, stderr) => {
|
|
56795
|
+
if (err) res.status(500).json({ error: stderr || err.message });
|
|
56796
|
+
else res.json({ ok: true });
|
|
56797
|
+
}
|
|
56798
|
+
);
|
|
56799
|
+
});
|
|
56329
56800
|
app.get("/api/codex-cli-config", (req, res) => {
|
|
56330
56801
|
const projectPath = req.query.project || process.cwd();
|
|
56331
56802
|
const helperPath = codexHelperPath(projectPath);
|
|
@@ -56439,7 +56910,7 @@ if (config.httpProxy.targets.length > 0) {
|
|
|
56439
56910
|
const proxyRouter = createHttpProxyRouter(config.httpProxy.targets);
|
|
56440
56911
|
app.use(proxyRouter);
|
|
56441
56912
|
}
|
|
56442
|
-
if (
|
|
56913
|
+
if (SHOULD_SERVE_STATIC_UI) {
|
|
56443
56914
|
app.use(import_express2.default.static(DIST_DIR));
|
|
56444
56915
|
app.get("*", (req, res, next) => {
|
|
56445
56916
|
if (req.path.startsWith("/api") || req.path.startsWith("/proxy") || req.path.startsWith("/ws") || req.path.startsWith("/.well-known") || req.path.startsWith("/a2a")) {
|
|
@@ -56449,6 +56920,16 @@ if (fs2.existsSync(INDEX_HTML)) {
|
|
|
56449
56920
|
res.sendFile(INDEX_HTML);
|
|
56450
56921
|
});
|
|
56451
56922
|
}
|
|
56923
|
+
if (IS_PACKAGED_COMPANION && !LOCAL_UI_ENABLED) {
|
|
56924
|
+
app.get("/", (_req, res) => {
|
|
56925
|
+
const lines = [
|
|
56926
|
+
"AgentMomo local bridge is running.",
|
|
56927
|
+
HOSTED_APP_URL ? `Open the hosted AgentMomo app at ${HOSTED_APP_URL} and keep this process running.` : "Open your hosted AgentMomo app and keep this process running.",
|
|
56928
|
+
`Health check: http://localhost:${PORT}/api/health`
|
|
56929
|
+
];
|
|
56930
|
+
res.type("text/plain").send(lines.join("\n"));
|
|
56931
|
+
});
|
|
56932
|
+
}
|
|
56452
56933
|
var server = createServer(app);
|
|
56453
56934
|
var wss2 = startEventEmitter(PORT);
|
|
56454
56935
|
server.on("upgrade", async (request, socket, head2) => {
|
|
@@ -56478,7 +56959,7 @@ server.listen(PORT, () => {
|
|
|
56478
56959
|
const wsHost = `ws://localhost:${PORT}/ws`;
|
|
56479
56960
|
console.log(`
|
|
56480
56961
|
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
56481
|
-
\u2502
|
|
56962
|
+
\u2502 \u{1F3E2} AgentMomo Local Bridge \u2502
|
|
56482
56963
|
\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
|
|
56483
56964
|
\u2502 REST API: ${host}/api \u2502
|
|
56484
56965
|
\u2502 WebSocket: ${wsHost} \u2502
|
|
@@ -56486,6 +56967,9 @@ server.listen(PORT, () => {
|
|
|
56486
56967
|
\u2502 Targets: ${config.httpProxy.targets.length} configured \u2502
|
|
56487
56968
|
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
56488
56969
|
`);
|
|
56970
|
+
console.log(
|
|
56971
|
+
IS_PACKAGED_COMPANION ? LOCAL_UI_ENABLED ? `[agentmomo] Local fallback UI enabled at ${host}` : `[agentmomo] Bridge-only mode enabled. Use the hosted app${HOSTED_APP_URL ? ` at ${HOSTED_APP_URL}` : ""}.` : `[agentmomo] Hosted UI serving enabled at ${host}`
|
|
56972
|
+
);
|
|
56489
56973
|
});
|
|
56490
56974
|
export {
|
|
56491
56975
|
app,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentmomo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "3D visualization of MCP agent activity — watch your AI tools work in a virtual office",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,9 +42,10 @@
|
|
|
42
42
|
"build:hook": "npx esbuild proxy/claude-code-hook.ts --bundle --platform=node --format=cjs --outfile=proxy/dist/claude-code-hook.cjs",
|
|
43
43
|
"build:gemini-hook": "npx esbuild proxy/gemini-cli-hook.ts --bundle --platform=node --format=cjs --outfile=proxy/dist/gemini-cli-hook.cjs",
|
|
44
44
|
"build:codex-hook": "npx esbuild proxy/codex-cli-hook.ts --bundle --platform=node --format=cjs --outfile=proxy/dist/codex-cli-hook.cjs",
|
|
45
|
+
"build:qwen-hook": "npx esbuild proxy/qwen-cli-hook.ts --bundle --platform=node --format=cjs --outfile=proxy/dist/qwen-cli-hook.cjs",
|
|
45
46
|
"build:server": "npx esbuild proxy/server.ts --bundle --platform=node --format=esm --outfile=dist/server.js",
|
|
46
47
|
"build:cli": "npx esbuild bin/cli.ts --bundle --platform=node --format=esm --banner:js=\"#!/usr/bin/env node\" --outfile=dist/cli.js",
|
|
47
|
-
"build:npm": "npm run build && npm run build:server && npm run build:cli && npx esbuild proxy/stdio-wrapper.ts --bundle --platform=node --format=esm --outfile=dist/stdio-wrapper.js && npx esbuild proxy/claude-code-hook.ts --bundle --platform=node --format=cjs --outfile=dist/claude-code-hook.cjs && npx esbuild proxy/gemini-cli-hook.ts --bundle --platform=node --format=cjs --outfile=dist/gemini-cli-hook.cjs && npx esbuild proxy/codex-cli-hook.ts --bundle --platform=node --format=cjs --outfile=dist/codex-cli-hook.cjs",
|
|
48
|
+
"build:npm": "npm run build && npm run build:server && npm run build:cli && npx esbuild proxy/stdio-wrapper.ts --bundle --platform=node --format=esm --outfile=dist/stdio-wrapper.js && npx esbuild proxy/claude-code-hook.ts --bundle --platform=node --format=cjs --outfile=dist/claude-code-hook.cjs && npx esbuild proxy/gemini-cli-hook.ts --bundle --platform=node --format=cjs --outfile=dist/gemini-cli-hook.cjs && npx esbuild proxy/codex-cli-hook.ts --bundle --platform=node --format=cjs --outfile=dist/codex-cli-hook.cjs && npx esbuild proxy/qwen-cli-hook.ts --bundle --platform=node --format=cjs --outfile=dist/qwen-cli-hook.cjs",
|
|
48
49
|
"prepack": "npm run build:npm",
|
|
49
50
|
"preview": "vite preview"
|
|
50
51
|
},
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"@react-three/fiber": "^9.0.0",
|
|
70
71
|
"@react-three/postprocessing": "^3.0.4",
|
|
71
72
|
"@supabase/supabase-js": "^2.98.0",
|
|
73
|
+
"cannon-es": "^0.20.0",
|
|
72
74
|
"class-variance-authority": "^0.7.1",
|
|
73
75
|
"clsx": "^2.1.1",
|
|
74
76
|
"cors": "^2.8.6",
|
|
@@ -96,6 +98,7 @@
|
|
|
96
98
|
"@vitejs/plugin-react": "^4.3.0",
|
|
97
99
|
"autoprefixer": "^10.4.27",
|
|
98
100
|
"concurrently": "^9.1.0",
|
|
101
|
+
"playwright": "^1.58.2",
|
|
99
102
|
"postcss": "^8.5.8",
|
|
100
103
|
"tailwindcss": "^3.4.19",
|
|
101
104
|
"tsx": "^4.19.0",
|