agentmomo 0.2.1 → 0.4.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/dist/assets/index-C9jbdpsT.js +5940 -0
- package/dist/assets/index-DNMQ_afu.css +1 -0
- package/dist/claude-code-hook.cjs +5 -1
- package/dist/index.html +7 -3
- 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 +476 -7
- package/package.json +5 -2
- package/dist/assets/index-BQyAbSge.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,8 +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);
|
|
55345
55353
|
var LOCAL_UI_ENABLED = process.env.AGENTMOMO_LOCAL_UI === "1";
|
|
55346
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);
|
|
55347
55356
|
var FEEDBACK_WINDOW_MS = 6e4;
|
|
55348
55357
|
var FEEDBACK_MAX_PER_WINDOW = 5;
|
|
55349
55358
|
var feedbackIpHits = /* @__PURE__ */ new Map();
|
|
@@ -55395,6 +55404,281 @@ function allowFeedbackFromIp(ip) {
|
|
|
55395
55404
|
return true;
|
|
55396
55405
|
}
|
|
55397
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 --approval-mode=yolo -p "${safe}"` : `qwen -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 ? ["--approval-mode=yolo", "-p", prompt] : ["-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
|
+
}
|
|
55398
55682
|
app.use(
|
|
55399
55683
|
import_express2.default.json({
|
|
55400
55684
|
verify: (req, _res, buf) => {
|
|
@@ -55461,6 +55745,40 @@ app.post("/api/event", requireAuth, async (req, res) => {
|
|
|
55461
55745
|
}
|
|
55462
55746
|
processEvent(event);
|
|
55463
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
|
+
}
|
|
55464
55782
|
if (creditResult.cost > 0 && creditResult.balance >= 0) {
|
|
55465
55783
|
sendToUser(req.userId, {
|
|
55466
55784
|
agentId: "__system__",
|
|
@@ -55514,6 +55832,53 @@ app.post("/api/event", requireAuth, async (req, res) => {
|
|
|
55514
55832
|
app.get("/api/health", (_req, res) => {
|
|
55515
55833
|
res.json({ status: "ok", agents: getAllAgents().length });
|
|
55516
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
|
+
});
|
|
55517
55882
|
app.get("/.well-known/agent.json", (_req, res) => {
|
|
55518
55883
|
res.json({
|
|
55519
55884
|
name: "agentmomo",
|
|
@@ -56026,10 +56391,10 @@ app.post("/api/keys", requireAuth, async (req, res) => {
|
|
|
56026
56391
|
return;
|
|
56027
56392
|
}
|
|
56028
56393
|
const supabase2 = getSupabase();
|
|
56029
|
-
const name = req.body?.name || "
|
|
56394
|
+
const name = req.body?.name || "momo";
|
|
56030
56395
|
const isBootstrapRequest = req.get("x-agentmomo-key-bootstrap") === "1";
|
|
56031
|
-
if (isBootstrapRequest && name === "
|
|
56032
|
-
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);
|
|
56033
56398
|
if (existingDefaultKeys && existingDefaultKeys.length > 0) {
|
|
56034
56399
|
const idsToDeactivate = existingDefaultKeys.map((k) => k.id);
|
|
56035
56400
|
await supabase2.from("api_keys").update({ is_active: false }).in("id", idsToDeactivate).eq("user_id", req.userId);
|
|
@@ -56191,6 +56556,8 @@ app.post("/api/claude-code-config", (req, res) => {
|
|
|
56191
56556
|
cfg.hooks.PostToolUse = [matcher];
|
|
56192
56557
|
cfg.hooks.TeammateIdle = [matcher];
|
|
56193
56558
|
cfg.hooks.TaskCompleted = [matcher];
|
|
56559
|
+
if (!cfg.env) cfg.env = {};
|
|
56560
|
+
cfg.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
|
|
56194
56561
|
} else {
|
|
56195
56562
|
const strip = (arr) => (arr ?? []).filter(
|
|
56196
56563
|
(e) => !e.hooks?.some(
|
|
@@ -56206,6 +56573,10 @@ app.post("/api/claude-code-config", (req, res) => {
|
|
|
56206
56573
|
if (!cfg.hooks.TeammateIdle.length) delete cfg.hooks.TeammateIdle;
|
|
56207
56574
|
if (!cfg.hooks.TaskCompleted.length) delete cfg.hooks.TaskCompleted;
|
|
56208
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
|
+
}
|
|
56209
56580
|
}
|
|
56210
56581
|
fs2.writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
56211
56582
|
res.json({ ok: true, configPath });
|
|
@@ -56230,6 +56601,9 @@ app.post("/api/build-hook", (_req, res) => {
|
|
|
56230
56601
|
function geminiSettingsPath(projectPath) {
|
|
56231
56602
|
return path3.join(projectPath, ".gemini", "settings.json");
|
|
56232
56603
|
}
|
|
56604
|
+
function qwenSettingsPath(projectPath) {
|
|
56605
|
+
return path3.join(projectPath, ".qwen", "settings.json");
|
|
56606
|
+
}
|
|
56233
56607
|
app.get("/api/gemini-cli-config", (req, res) => {
|
|
56234
56608
|
const projectPath = req.query.project || process.cwd();
|
|
56235
56609
|
const configPath = geminiSettingsPath(projectPath);
|
|
@@ -56328,6 +56702,101 @@ app.post("/api/build-gemini-hook", (_req, res) => {
|
|
|
56328
56702
|
}
|
|
56329
56703
|
);
|
|
56330
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
|
+
});
|
|
56331
56800
|
app.get("/api/codex-cli-config", (req, res) => {
|
|
56332
56801
|
const projectPath = req.query.project || process.cwd();
|
|
56333
56802
|
const helperPath = codexHelperPath(projectPath);
|
|
@@ -56441,7 +56910,7 @@ if (config.httpProxy.targets.length > 0) {
|
|
|
56441
56910
|
const proxyRouter = createHttpProxyRouter(config.httpProxy.targets);
|
|
56442
56911
|
app.use(proxyRouter);
|
|
56443
56912
|
}
|
|
56444
|
-
if (
|
|
56913
|
+
if (SHOULD_SERVE_STATIC_UI) {
|
|
56445
56914
|
app.use(import_express2.default.static(DIST_DIR));
|
|
56446
56915
|
app.get("*", (req, res, next) => {
|
|
56447
56916
|
if (req.path.startsWith("/api") || req.path.startsWith("/proxy") || req.path.startsWith("/ws") || req.path.startsWith("/.well-known") || req.path.startsWith("/a2a")) {
|
|
@@ -56451,7 +56920,7 @@ if (LOCAL_UI_ENABLED && fs2.existsSync(INDEX_HTML)) {
|
|
|
56451
56920
|
res.sendFile(INDEX_HTML);
|
|
56452
56921
|
});
|
|
56453
56922
|
}
|
|
56454
|
-
if (!LOCAL_UI_ENABLED) {
|
|
56923
|
+
if (IS_PACKAGED_COMPANION && !LOCAL_UI_ENABLED) {
|
|
56455
56924
|
app.get("/", (_req, res) => {
|
|
56456
56925
|
const lines = [
|
|
56457
56926
|
"AgentMomo local bridge is running.",
|
|
@@ -56499,7 +56968,7 @@ server.listen(PORT, () => {
|
|
|
56499
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
|
|
56500
56969
|
`);
|
|
56501
56970
|
console.log(
|
|
56502
|
-
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}` : ""}.`
|
|
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}`
|
|
56503
56972
|
);
|
|
56504
56973
|
});
|
|
56505
56974
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentmomo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|