@geminixiang/mama 0.2.0-beta.5 → 0.2.0-beta.7
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 +81 -18
- package/dist/adapter.d.ts +3 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +1 -1
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +84 -17
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +12 -0
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +219 -15
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +5 -0
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/tools/attach.d.ts +1 -1
- package/dist/adapters/slack/tools/attach.d.ts.map +1 -1
- package/dist/adapters/slack/tools/attach.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +32 -35
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +42 -26
- package/dist/agent.js.map +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +10 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/model.d.ts +14 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +112 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/new.d.ts +9 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +28 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/sandbox.d.ts +10 -0
- package/dist/commands/sandbox.d.ts.map +1 -0
- package/dist/commands/sandbox.js +65 -0
- package/dist/commands/sandbox.js.map +1 -0
- package/dist/commands/session-view.d.ts.map +1 -1
- package/dist/commands/session-view.js +29 -9
- package/dist/commands/session-view.js.map +1 -1
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/utils.d.ts +3 -0
- package/dist/commands/utils.d.ts.map +1 -1
- package/dist/commands/utils.js +5 -0
- package/dist/commands/utils.js.map +1 -1
- package/dist/config.d.ts +13 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +177 -31
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +50 -35
- package/dist/context.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +53 -4
- package/dist/main.js.map +1 -1
- package/dist/provisioner.d.ts +12 -0
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +41 -10
- package/dist/provisioner.js.map +1 -1
- package/dist/runtime/session-runtime.d.ts +1 -0
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/runtime/session-runtime.js +18 -0
- package/dist/runtime/session-runtime.js.map +1 -1
- package/dist/session-store.d.ts +1 -1
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +1 -1
- package/dist/session-store.js.map +1 -1
- package/dist/session-view/service.d.ts.map +1 -1
- package/dist/session-view/service.js +1 -1
- package/dist/session-view/service.js.map +1 -1
- package/dist/tools/bash.d.ts +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/event.d.ts +1 -1
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/write.d.ts +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { replyWithContext } from "./utils.js";
|
|
2
|
+
export function parseNewCommand(text) {
|
|
3
|
+
const tokens = text.trim().split(/\s+/).filter(Boolean);
|
|
4
|
+
if (tokens.length === 0)
|
|
5
|
+
return null;
|
|
6
|
+
const command = tokens[0].toLowerCase();
|
|
7
|
+
if (command !== "new" && command !== "/new" && command !== "/pi-new") {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
return { command: command };
|
|
11
|
+
}
|
|
12
|
+
export class NewCommandHandler {
|
|
13
|
+
async tryHandle(context) {
|
|
14
|
+
if (!parseNewCommand(context.commandText))
|
|
15
|
+
return false;
|
|
16
|
+
if (!context.privateConversation) {
|
|
17
|
+
await replyWithContext(context.responseCtx, "為了避免誤清除共享上下文,`/new` 目前只能在與機器人的私訊 / DM 中使用。");
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (!context.services.runtime) {
|
|
21
|
+
await replyWithContext(context.responseCtx, "New command is not configured correctly on the server. Please try again later.");
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
await context.services.runtime.handleNew(context.sessionKey, context.conversationId, context.bot);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAM9C,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACrE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAsC,EAAE,CAAC;AAC7D,CAAC;AAED,MAAM,OAAO,iBAAiB;IAC5B,KAAK,CAAC,SAAS,CAAC,OAAuB;QACrC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;QAExD,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC;YACjC,MAAM,gBAAgB,CACpB,OAAO,CAAC,WAAW,EACnB,4CAA4C,CAC7C,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YAC9B,MAAM,gBAAgB,CACpB,OAAO,CAAC,WAAW,EACnB,gFAAgF,CACjF,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CACtC,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,GAAG,CACZ,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyWithContext } from \"./utils.js\";\n\nexport interface ParsedNewCommand {\n command: \"new\" | \"/new\" | \"/pi-new\";\n}\n\nexport function parseNewCommand(text: string): ParsedNewCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"new\" && command !== \"/new\" && command !== \"/pi-new\") {\n return null;\n }\n\n return { command: command as ParsedNewCommand[\"command\"] };\n}\n\nexport class NewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseNewCommand(context.commandText)) return false;\n\n if (!context.privateConversation) {\n await replyWithContext(\n context.responseCtx,\n \"為了避免誤清除共享上下文,`/new` 目前只能在與機器人的私訊 / DM 中使用。\",\n );\n return true;\n }\n\n if (!context.services.runtime) {\n await replyWithContext(\n context.responseCtx,\n \"New command is not configured correctly on the server. Please try again later.\",\n );\n return true;\n }\n\n await context.services.runtime.handleNew(\n context.sessionKey,\n context.conversationId,\n context.bot,\n );\n return true;\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CommandContext, CommandHandler } from "./types.js";
|
|
2
|
+
export interface ParsedSandboxCommand {
|
|
3
|
+
command: "/pi-sandbox" | "/sandbox";
|
|
4
|
+
action?: "boost";
|
|
5
|
+
}
|
|
6
|
+
export declare function parseSandboxCommand(text: string): ParsedSandboxCommand | null;
|
|
7
|
+
export declare class SandboxCommandHandler implements CommandHandler {
|
|
8
|
+
tryHandle(context: CommandContext): Promise<boolean>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/commands/sandbox.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjE,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,GAAG,UAAU,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAW7E;AAMD,qBAAa,qBAAsB,YAAW,cAAc;IACpD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAkEzD;CACF","sourcesContent":["import { resolveActorVaultKey } from \"../vault-routing.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nexport interface ParsedSandboxCommand {\n command: \"/pi-sandbox\" | \"/sandbox\";\n action?: \"boost\";\n}\n\nexport function parseSandboxCommand(text: string): ParsedSandboxCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].replace(/@\\w+$/i, \"\").toLowerCase();\n if (command !== \"/pi-sandbox\" && command !== \"/sandbox\") return null;\n if (tokens.length === 1) return { command };\n if (tokens.length === 2 && tokens[1].toLowerCase() === \"boost\") {\n return { command, action: \"boost\" };\n }\n return { command };\n}\n\nfunction formatSandboxCommandSummary(title: string, lines: string[]): string {\n return [`_${title}_`, ...lines].join(\"\\n\");\n}\n\nexport class SandboxCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseSandboxCommand(context.commandText);\n if (!parsed) return false;\n\n if (context.services.sandbox.type !== \"image\" || !context.services.provisioner) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox\", [\n \"`/pi-sandbox` 目前只支援 `image:*` managed sandbox。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const containerKey = resolveActorVaultKey(\n context.services.sandbox,\n context.platformUserId,\n context.conversationId,\n );\n\n if (parsed.action === \"boost\") {\n const boostLimits = context.services.provisioner.getBoostLimits();\n if (!boostLimits?.cpus && !boostLimits?.memory) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Boost\", [\n \"此 mama instance 尚未設定 sandbox boost 規格。\",\n \"請先在全域 settings.json 設定 `sandbox.boost`。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const status = await context.services.provisioner.boost(containerKey);\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Boost\", [\n \"已暫時提升此 conversation 的 sandbox 規格。\",\n `Current: ${formatLimits(status.limits)}`,\n \"boost 會在此 sandbox container 關閉後結束。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const status = context.services.provisioner.getLimitStatus(containerKey);\n const defaultLimits = context.services.provisioner.getDefaultLimits();\n const boostLimits = context.services.provisioner.getBoostLimits();\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\n \"Sandbox\",\n [\n `Current: ${formatLimits(status.limits)}`,\n `Status: ${status.boosted ? \"boosted\" : \"default\"}`,\n \"\",\n `Default: ${formatLimits(defaultLimits)}`,\n boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,\n ].filter((line): line is string => line !== undefined),\n ),\n { style: \"muted\" },\n );\n return true;\n }\n}\n\nfunction formatLimits(limits: { cpus?: string; memory?: string } | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { resolveActorVaultKey } from "../vault-routing.js";
|
|
2
|
+
import { replyDiagnosticWithContext } from "./utils.js";
|
|
3
|
+
export function parseSandboxCommand(text) {
|
|
4
|
+
const tokens = text.trim().split(/\s+/).filter(Boolean);
|
|
5
|
+
if (tokens.length === 0)
|
|
6
|
+
return null;
|
|
7
|
+
const command = tokens[0].replace(/@\w+$/i, "").toLowerCase();
|
|
8
|
+
if (command !== "/pi-sandbox" && command !== "/sandbox")
|
|
9
|
+
return null;
|
|
10
|
+
if (tokens.length === 1)
|
|
11
|
+
return { command };
|
|
12
|
+
if (tokens.length === 2 && tokens[1].toLowerCase() === "boost") {
|
|
13
|
+
return { command, action: "boost" };
|
|
14
|
+
}
|
|
15
|
+
return { command };
|
|
16
|
+
}
|
|
17
|
+
function formatSandboxCommandSummary(title, lines) {
|
|
18
|
+
return [`_${title}_`, ...lines].join("\n");
|
|
19
|
+
}
|
|
20
|
+
export class SandboxCommandHandler {
|
|
21
|
+
async tryHandle(context) {
|
|
22
|
+
const parsed = parseSandboxCommand(context.commandText);
|
|
23
|
+
if (!parsed)
|
|
24
|
+
return false;
|
|
25
|
+
if (context.services.sandbox.type !== "image" || !context.services.provisioner) {
|
|
26
|
+
await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox", [
|
|
27
|
+
"`/pi-sandbox` 目前只支援 `image:*` managed sandbox。",
|
|
28
|
+
]), { style: "muted" });
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
const containerKey = resolveActorVaultKey(context.services.sandbox, context.platformUserId, context.conversationId);
|
|
32
|
+
if (parsed.action === "boost") {
|
|
33
|
+
const boostLimits = context.services.provisioner.getBoostLimits();
|
|
34
|
+
if (!boostLimits?.cpus && !boostLimits?.memory) {
|
|
35
|
+
await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox Boost", [
|
|
36
|
+
"此 mama instance 尚未設定 sandbox boost 規格。",
|
|
37
|
+
"請先在全域 settings.json 設定 `sandbox.boost`。",
|
|
38
|
+
]), { style: "muted" });
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
const status = await context.services.provisioner.boost(containerKey);
|
|
42
|
+
await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox Boost", [
|
|
43
|
+
"已暫時提升此 conversation 的 sandbox 規格。",
|
|
44
|
+
`Current: ${formatLimits(status.limits)}`,
|
|
45
|
+
"boost 會在此 sandbox container 關閉後結束。",
|
|
46
|
+
]), { style: "muted" });
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
const status = context.services.provisioner.getLimitStatus(containerKey);
|
|
50
|
+
const defaultLimits = context.services.provisioner.getDefaultLimits();
|
|
51
|
+
const boostLimits = context.services.provisioner.getBoostLimits();
|
|
52
|
+
await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox", [
|
|
53
|
+
`Current: ${formatLimits(status.limits)}`,
|
|
54
|
+
`Status: ${status.boosted ? "boosted" : "default"}`,
|
|
55
|
+
"",
|
|
56
|
+
`Default: ${formatLimits(defaultLimits)}`,
|
|
57
|
+
boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,
|
|
58
|
+
].filter((line) => line !== undefined)), { style: "muted" });
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function formatLimits(limits) {
|
|
63
|
+
return `CPU ${limits?.cpus ?? "unlimited"} / Memory ${limits?.memory ?? "unlimited"}`;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/commands/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveActorVaultKey } from \"../vault-routing.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nexport interface ParsedSandboxCommand {\n command: \"/pi-sandbox\" | \"/sandbox\";\n action?: \"boost\";\n}\n\nexport function parseSandboxCommand(text: string): ParsedSandboxCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].replace(/@\\w+$/i, \"\").toLowerCase();\n if (command !== \"/pi-sandbox\" && command !== \"/sandbox\") return null;\n if (tokens.length === 1) return { command };\n if (tokens.length === 2 && tokens[1].toLowerCase() === \"boost\") {\n return { command, action: \"boost\" };\n }\n return { command };\n}\n\nfunction formatSandboxCommandSummary(title: string, lines: string[]): string {\n return [`_${title}_`, ...lines].join(\"\\n\");\n}\n\nexport class SandboxCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseSandboxCommand(context.commandText);\n if (!parsed) return false;\n\n if (context.services.sandbox.type !== \"image\" || !context.services.provisioner) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox\", [\n \"`/pi-sandbox` 目前只支援 `image:*` managed sandbox。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const containerKey = resolveActorVaultKey(\n context.services.sandbox,\n context.platformUserId,\n context.conversationId,\n );\n\n if (parsed.action === \"boost\") {\n const boostLimits = context.services.provisioner.getBoostLimits();\n if (!boostLimits?.cpus && !boostLimits?.memory) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Boost\", [\n \"此 mama instance 尚未設定 sandbox boost 規格。\",\n \"請先在全域 settings.json 設定 `sandbox.boost`。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const status = await context.services.provisioner.boost(containerKey);\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Boost\", [\n \"已暫時提升此 conversation 的 sandbox 規格。\",\n `Current: ${formatLimits(status.limits)}`,\n \"boost 會在此 sandbox container 關閉後結束。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const status = context.services.provisioner.getLimitStatus(containerKey);\n const defaultLimits = context.services.provisioner.getDefaultLimits();\n const boostLimits = context.services.provisioner.getBoostLimits();\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\n \"Sandbox\",\n [\n `Current: ${formatLimits(status.limits)}`,\n `Status: ${status.boosted ? \"boosted\" : \"default\"}`,\n \"\",\n `Default: ${formatLimits(defaultLimits)}`,\n boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,\n ].filter((line): line is string => line !== undefined),\n ),\n { style: \"muted\" },\n );\n return true;\n }\n}\n\nfunction formatLimits(limits: { cpus?: string; memory?: string } | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-view.d.ts","sourceRoot":"","sources":["../../src/commands/session-view.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"session-view.d.ts","sourceRoot":"","sources":["../../src/commands/session-view.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAOjE,qBAAa,yBAA0B,YAAW,cAAc;IACxD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAuEzD;CACF","sourcesContent":["import { resolveExistingSessionFile } from \"../session-view/service.js\";\nimport { parseSessionViewCommand } from \"../session-view/command.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nfunction formatSessionCommandSummary(lines: string[]): string {\n return [\"_Session_\", ...lines].join(\"\\n\");\n}\n\nexport class SessionViewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseSessionViewCommand(context.commandText)) return false;\n\n const sendSessionViewReply = async (lines: string[]): Promise<void> => {\n const text = formatSessionCommandSummary(lines);\n if (context.privateConversation) {\n await replyDiagnosticWithContext(context.responseCtx, text, { style: \"muted\" });\n return;\n }\n\n if (context.bot.postPrivateDiagnostic) {\n await context.bot.postPrivateDiagnostic(\n context.conversationId,\n context.platformUserId,\n text,\n {\n style: \"muted\",\n },\n );\n return;\n }\n\n if (context.bot.postPrivate) {\n await context.bot.postPrivate(context.conversationId, context.platformUserId, text);\n return;\n }\n\n await replyDiagnosticWithContext(context.responseCtx, text, { style: \"muted\" });\n };\n\n if (!context.privateConversation && !context.bot.postPrivate) {\n await sendSessionViewReply([\n \"為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。\",\n ]);\n return true;\n }\n\n if (!context.services.portalBaseUrl) {\n await sendSessionViewReply([\n \"Session viewer is not configured.\",\n \"Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.\",\n ]);\n return true;\n }\n\n const sessionFile = resolveExistingSessionFile(\n context.services.workingDir,\n context.conversationId,\n context.sessionKey,\n );\n if (!sessionFile) {\n await sendSessionViewReply([\n \"目前還沒有可查看的 session。\",\n \"先和機器人對話一次,建立 session 後再試。\",\n ]);\n return true;\n }\n\n const token = context.services.sessionViewTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n context.sessionKey,\n sessionFile,\n );\n\n await sendSessionViewReply([\n \"Open this read-only session link (expires in 24 hours):\",\n `${context.services.portalBaseUrl}/session?token=${token.token}`,\n ]);\n return true;\n }\n}\n"]}
|
|
@@ -1,37 +1,57 @@
|
|
|
1
1
|
import { resolveExistingSessionFile } from "../session-view/service.js";
|
|
2
2
|
import { parseSessionViewCommand } from "../session-view/command.js";
|
|
3
|
-
import {
|
|
3
|
+
import { replyDiagnosticWithContext } from "./utils.js";
|
|
4
|
+
function formatSessionCommandSummary(lines) {
|
|
5
|
+
return ["_Session_", ...lines].join("\n");
|
|
6
|
+
}
|
|
4
7
|
export class SessionViewCommandHandler {
|
|
5
8
|
async tryHandle(context) {
|
|
6
9
|
if (!parseSessionViewCommand(context.commandText))
|
|
7
10
|
return false;
|
|
8
|
-
const sendSessionViewReply = async (
|
|
11
|
+
const sendSessionViewReply = async (lines) => {
|
|
12
|
+
const text = formatSessionCommandSummary(lines);
|
|
9
13
|
if (context.privateConversation) {
|
|
10
|
-
await
|
|
14
|
+
await replyDiagnosticWithContext(context.responseCtx, text, { style: "muted" });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (context.bot.postPrivateDiagnostic) {
|
|
18
|
+
await context.bot.postPrivateDiagnostic(context.conversationId, context.platformUserId, text, {
|
|
19
|
+
style: "muted",
|
|
20
|
+
});
|
|
11
21
|
return;
|
|
12
22
|
}
|
|
13
23
|
if (context.bot.postPrivate) {
|
|
14
24
|
await context.bot.postPrivate(context.conversationId, context.platformUserId, text);
|
|
15
25
|
return;
|
|
16
26
|
}
|
|
17
|
-
await
|
|
27
|
+
await replyDiagnosticWithContext(context.responseCtx, text, { style: "muted" });
|
|
18
28
|
};
|
|
19
29
|
if (!context.privateConversation && !context.bot.postPrivate) {
|
|
20
|
-
await sendSessionViewReply(
|
|
30
|
+
await sendSessionViewReply([
|
|
31
|
+
"為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。",
|
|
32
|
+
]);
|
|
21
33
|
return true;
|
|
22
34
|
}
|
|
23
35
|
if (!context.services.portalBaseUrl) {
|
|
24
|
-
await sendSessionViewReply(
|
|
36
|
+
await sendSessionViewReply([
|
|
37
|
+
"Session viewer is not configured.",
|
|
38
|
+
"Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.",
|
|
39
|
+
]);
|
|
25
40
|
return true;
|
|
26
41
|
}
|
|
27
42
|
const sessionFile = resolveExistingSessionFile(context.services.workingDir, context.conversationId, context.sessionKey);
|
|
28
43
|
if (!sessionFile) {
|
|
29
|
-
await sendSessionViewReply(
|
|
44
|
+
await sendSessionViewReply([
|
|
45
|
+
"目前還沒有可查看的 session。",
|
|
46
|
+
"先和機器人對話一次,建立 session 後再試。",
|
|
47
|
+
]);
|
|
30
48
|
return true;
|
|
31
49
|
}
|
|
32
50
|
const token = context.services.sessionViewTokenStore.create(context.platform, context.platformUserId, context.conversationId, context.sessionKey, sessionFile);
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
await sendSessionViewReply([
|
|
52
|
+
"Open this read-only session link (expires in 24 hours):",
|
|
53
|
+
`${context.services.portalBaseUrl}/session?token=${token.token}`,
|
|
54
|
+
]);
|
|
35
55
|
return true;
|
|
36
56
|
}
|
|
37
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-view.js","sourceRoot":"","sources":["../../src/commands/session-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"session-view.js","sourceRoot":"","sources":["../../src/commands/session-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAExD,SAAS,2BAA2B,CAAC,KAAe;IAClD,OAAO,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,OAAO,yBAAyB;IACpC,KAAK,CAAC,SAAS,CAAC,OAAuB;QACrC,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;QAEhE,MAAM,oBAAoB,GAAG,KAAK,EAAE,KAAe,EAAiB,EAAE;YACpE,MAAM,IAAI,GAAG,2BAA2B,CAAC,KAAK,CAAC,CAAC;YAChD,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;gBAChC,MAAM,0BAA0B,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;gBAChF,OAAO;YACT,CAAC;YAED,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC;gBACtC,MAAM,OAAO,CAAC,GAAG,CAAC,qBAAqB,CACrC,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,cAAc,EACtB,IAAI,EACJ;oBACE,KAAK,EAAE,OAAO;iBACf,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YAED,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBAC5B,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;gBACpF,OAAO;YACT,CAAC;YAED,MAAM,0BAA0B,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAClF,CAAC,CAAC;QAEF,IAAI,CAAC,OAAO,CAAC,mBAAmB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAC7D,MAAM,oBAAoB,CAAC;gBACzB,4CAA4C;aAC7C,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;YACpC,MAAM,oBAAoB,CAAC;gBACzB,mCAAmC;gBACnC,wDAAwD;aACzD,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,WAAW,GAAG,0BAA0B,CAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,EAC3B,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,UAAU,CACnB,CAAC;QACF,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,oBAAoB,CAAC;gBACzB,oBAAoB;gBACpB,2BAA2B;aAC5B,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,MAAM,CACzD,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,UAAU,EAClB,WAAW,CACZ,CAAC;QAEF,MAAM,oBAAoB,CAAC;YACzB,yDAAyD;YACzD,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,kBAAkB,KAAK,CAAC,KAAK,EAAE;SACjE,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import { resolveExistingSessionFile } from \"../session-view/service.js\";\nimport { parseSessionViewCommand } from \"../session-view/command.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nfunction formatSessionCommandSummary(lines: string[]): string {\n return [\"_Session_\", ...lines].join(\"\\n\");\n}\n\nexport class SessionViewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseSessionViewCommand(context.commandText)) return false;\n\n const sendSessionViewReply = async (lines: string[]): Promise<void> => {\n const text = formatSessionCommandSummary(lines);\n if (context.privateConversation) {\n await replyDiagnosticWithContext(context.responseCtx, text, { style: \"muted\" });\n return;\n }\n\n if (context.bot.postPrivateDiagnostic) {\n await context.bot.postPrivateDiagnostic(\n context.conversationId,\n context.platformUserId,\n text,\n {\n style: \"muted\",\n },\n );\n return;\n }\n\n if (context.bot.postPrivate) {\n await context.bot.postPrivate(context.conversationId, context.platformUserId, text);\n return;\n }\n\n await replyDiagnosticWithContext(context.responseCtx, text, { style: \"muted\" });\n };\n\n if (!context.privateConversation && !context.bot.postPrivate) {\n await sendSessionViewReply([\n \"為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。\",\n ]);\n return true;\n }\n\n if (!context.services.portalBaseUrl) {\n await sendSessionViewReply([\n \"Session viewer is not configured.\",\n \"Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.\",\n ]);\n return true;\n }\n\n const sessionFile = resolveExistingSessionFile(\n context.services.workingDir,\n context.conversationId,\n context.sessionKey,\n );\n if (!sessionFile) {\n await sendSessionViewReply([\n \"目前還沒有可查看的 session。\",\n \"先和機器人對話一次,建立 session 後再試。\",\n ]);\n return true;\n }\n\n const token = context.services.sessionViewTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n context.sessionKey,\n sessionFile,\n );\n\n await sendSessionViewReply([\n \"Open this read-only session link (expires in 24 hours):\",\n `${context.services.portalBaseUrl}/session?token=${token.token}`,\n ]);\n return true;\n }\n}\n"]}
|
package/dist/commands/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Bot, BotAdapters, PlatformName } from "../adapter.js";
|
|
2
2
|
import type { UserBindingStore } from "../bindings.js";
|
|
3
3
|
import type { DockerContainerManager } from "../provisioner.js";
|
|
4
|
+
import type { SessionRuntime } from "../runtime/session-runtime.js";
|
|
4
5
|
import type { SandboxConfig } from "../sandbox.js";
|
|
5
6
|
import type { VaultManager } from "../vault.js";
|
|
6
7
|
export interface LinkTokenStoreLike {
|
|
@@ -15,6 +16,7 @@ export interface SessionViewTokenStoreLike {
|
|
|
15
16
|
}
|
|
16
17
|
export interface CommandServices {
|
|
17
18
|
workingDir: string;
|
|
19
|
+
runtime?: SessionRuntime;
|
|
18
20
|
sandbox: SandboxConfig;
|
|
19
21
|
vaultManager: VaultManager;
|
|
20
22
|
bindingStore?: UserBindingStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IACjC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,cAAc,EAAE,kBAAkB,CAAC;IACnC,qBAAqB,EAAE,yBAAyB,CAAC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,GAAG,CAAC;IACT,WAAW,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACxC,QAAQ,EAAE,YAAY,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtD","sourcesContent":["import type { Bot, BotAdapters, PlatformName } from \"../adapter.js\";\nimport type { UserBindingStore } from \"../bindings.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { SandboxConfig } from \"../sandbox.js\";\nimport type { VaultManager } from \"../vault.js\";\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n sessionKey: string,\n sessionFile: string,\n ): { token: string };\n}\n\nexport interface CommandServices {\n workingDir: string;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n bindingStore?: UserBindingStore;\n provisioner?: DockerContainerManager;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: Bot;\n responseCtx: BotAdapters[\"responseCtx\"];\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IACjC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,cAAc,EAAE,kBAAkB,CAAC;IACnC,qBAAqB,EAAE,yBAAyB,CAAC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,GAAG,CAAC;IACT,WAAW,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACxC,QAAQ,EAAE,YAAY,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtD","sourcesContent":["import type { Bot, BotAdapters, PlatformName } from \"../adapter.js\";\nimport type { UserBindingStore } from \"../bindings.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { SessionRuntime } from \"../runtime/session-runtime.js\";\nimport type { SandboxConfig } from \"../sandbox.js\";\nimport type { VaultManager } from \"../vault.js\";\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n sessionKey: string,\n sessionFile: string,\n ): { token: string };\n}\n\nexport interface CommandServices {\n workingDir: string;\n runtime?: SessionRuntime;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n bindingStore?: UserBindingStore;\n provisioner?: DockerContainerManager;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: Bot;\n responseCtx: BotAdapters[\"responseCtx\"];\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Bot, BotAdapters, PlatformName } from \"../adapter.js\";\nimport type { UserBindingStore } from \"../bindings.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { SandboxConfig } from \"../sandbox.js\";\nimport type { VaultManager } from \"../vault.js\";\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n sessionKey: string,\n sessionFile: string,\n ): { token: string };\n}\n\nexport interface CommandServices {\n workingDir: string;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n bindingStore?: UserBindingStore;\n provisioner?: DockerContainerManager;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: Bot;\n responseCtx: BotAdapters[\"responseCtx\"];\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Bot, BotAdapters, PlatformName } from \"../adapter.js\";\nimport type { UserBindingStore } from \"../bindings.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { SessionRuntime } from \"../runtime/session-runtime.js\";\nimport type { SandboxConfig } from \"../sandbox.js\";\nimport type { VaultManager } from \"../vault.js\";\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n sessionKey: string,\n sessionFile: string,\n ): { token: string };\n}\n\nexport interface CommandServices {\n workingDir: string;\n runtime?: SessionRuntime;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n bindingStore?: UserBindingStore;\n provisioner?: DockerContainerManager;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: Bot;\n responseCtx: BotAdapters[\"responseCtx\"];\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n"]}
|
package/dist/commands/utils.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { BotEvent } from "../adapter.js";
|
|
2
2
|
import type { CommandContext } from "./types.js";
|
|
3
3
|
export declare function replyWithContext(responseCtx: CommandContext["responseCtx"], text: string): Promise<void>;
|
|
4
|
+
export declare function replyDiagnosticWithContext(responseCtx: CommandContext["responseCtx"], text: string, options?: {
|
|
5
|
+
style?: "muted" | "error";
|
|
6
|
+
}): Promise<void>;
|
|
4
7
|
export declare function isPrivateConversation(event: BotEvent): boolean;
|
|
5
8
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/commands/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,EAC1C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAE9D","sourcesContent":["import type { BotEvent } from \"../adapter.js\";\nimport type { CommandContext } from \"./types.js\";\n\nexport async function replyWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respond(text);\n}\n\nexport function isPrivateConversation(event: BotEvent): boolean {\n return event.conversationKind === \"direct\" || event.type === \"dm\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/commands/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,EAC1C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,0BAA0B,CAC9C,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,EAC1C,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;CAAE,GACtC,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAE9D","sourcesContent":["import type { BotEvent } from \"../adapter.js\";\nimport type { CommandContext } from \"./types.js\";\n\nexport async function replyWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respond(text);\n}\n\nexport async function replyDiagnosticWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n options?: { style?: \"muted\" | \"error\" },\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respondDiagnostic(text, options);\n}\n\nexport function isPrivateConversation(event: BotEvent): boolean {\n return event.conversationKind === \"direct\" || event.type === \"dm\";\n}\n"]}
|
package/dist/commands/utils.js
CHANGED
|
@@ -3,6 +3,11 @@ export async function replyWithContext(responseCtx, text) {
|
|
|
3
3
|
await responseCtx.setWorking(false);
|
|
4
4
|
await responseCtx.respond(text);
|
|
5
5
|
}
|
|
6
|
+
export async function replyDiagnosticWithContext(responseCtx, text, options) {
|
|
7
|
+
await responseCtx.setTyping(false);
|
|
8
|
+
await responseCtx.setWorking(false);
|
|
9
|
+
await responseCtx.respondDiagnostic(text, options);
|
|
10
|
+
}
|
|
6
11
|
export function isPrivateConversation(event) {
|
|
7
12
|
return event.conversationKind === "direct" || event.type === "dm";
|
|
8
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/commands/utils.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAA0C,EAC1C,IAAY;IAEZ,MAAM,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAe;IACnD,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AACpE,CAAC","sourcesContent":["import type { BotEvent } from \"../adapter.js\";\nimport type { CommandContext } from \"./types.js\";\n\nexport async function replyWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respond(text);\n}\n\nexport function isPrivateConversation(event: BotEvent): boolean {\n return event.conversationKind === \"direct\" || event.type === \"dm\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/commands/utils.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAA0C,EAC1C,IAAY;IAEZ,MAAM,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,WAA0C,EAC1C,IAAY,EACZ,OAAuC;IAEvC,MAAM,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,WAAW,CAAC,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAe;IACnD,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AACpE,CAAC","sourcesContent":["import type { BotEvent } from \"../adapter.js\";\nimport type { CommandContext } from \"./types.js\";\n\nexport async function replyWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respond(text);\n}\n\nexport async function replyDiagnosticWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n options?: { style?: \"muted\" | \"error\" },\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respondDiagnostic(text, options);\n}\n\nexport function isPrivateConversation(event: BotEvent): boolean {\n return event.conversationKind === \"direct\" || event.type === \"dm\";\n}\n"]}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
|
+
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
|
+
export declare class MissingGlobalSettingsError extends Error {
|
|
3
|
+
readonly settingsPath: string;
|
|
4
|
+
constructor(settingsPath: string);
|
|
5
|
+
}
|
|
1
6
|
export interface AgentConfig {
|
|
2
7
|
provider: string;
|
|
3
8
|
model: string;
|
|
4
|
-
thinkingLevel
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
logLevel?: "trace" | "debug" | "info" | "warn" | "error";
|
|
9
|
+
thinkingLevel: ThinkingLevel;
|
|
10
|
+
logFormat: "console" | "json";
|
|
11
|
+
logLevel: "trace" | "debug" | "info" | "warn" | "error";
|
|
8
12
|
sentryDsn?: string;
|
|
9
13
|
sandboxCpus?: string;
|
|
10
14
|
sandboxMemory?: string;
|
|
15
|
+
sandboxBoostCpus?: string;
|
|
16
|
+
sandboxBoostMemory?: string;
|
|
11
17
|
}
|
|
12
18
|
export declare function loadAgentConfig(): AgentConfig;
|
|
19
|
+
export declare function loadAgentConfigForConversation(conversationDir: string): AgentConfig;
|
|
20
|
+
export declare function saveConversationModelConfig(conversationDir: string, config: Pick<AgentConfig, "provider" | "model"> & Partial<Pick<AgentConfig, "thinkingLevel">>): void;
|
|
13
21
|
export declare function resolveWorkspaceDirFromArgv(args?: string[]): string | undefined;
|
|
14
22
|
export declare function resolveStateDirFromArgv(args?: string[]): string;
|
|
15
23
|
export declare function resolveSentryDsn(): string | undefined;
|
|
24
|
+
export declare function createGlobalSettingsFile(stateDir: string): string;
|
|
16
25
|
/**
|
|
17
26
|
* Externally-visible base URL of the link/OAuth server, e.g.
|
|
18
27
|
* `https://mama.example.com` (no trailing slash). Read from `MAMA_LINK_URL`,
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACpC,SAAS,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAyCD,wBAAgB,eAAe,IAAI,WAAW,CAwB7C;AAED,wBAAgB,2BAA2B,CAAC,IAAI,WAAwB,GAAG,MAAM,GAAG,SAAS,CA2B5F;AAED,wBAAgB,uBAAuB,CAAC,IAAI,WAAwB,GAAG,MAAM,CAY5E;AAED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAOrD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAIvD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CA+BlE","sourcesContent":["import { existsSync, mkdirSync, readFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\n\nexport interface AgentConfig {\n provider: string;\n model: string;\n thinkingLevel?: string;\n sessionScope?: \"thread\" | \"channel\";\n logFormat?: \"console\" | \"json\";\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\";\n sentryDsn?: string;\n sandboxCpus?: string;\n sandboxMemory?: string;\n}\n\nconst DEFAULTS: AgentConfig = {\n provider: \"anthropic\",\n model: \"claude-sonnet-4-5\",\n thinkingLevel: \"off\",\n sessionScope: \"thread\",\n logFormat: \"console\",\n logLevel: \"info\",\n};\n\nfunction loadConfigFile(settingsPath: string): Partial<AgentConfig> | undefined {\n if (!existsSync(settingsPath)) {\n return undefined;\n }\n\n const raw = readFileSync(settingsPath, \"utf-8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(`Malformed settings file at ${settingsPath}: ${detail}`);\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\n `Malformed settings file at ${settingsPath}: expected a JSON object at the top level`,\n );\n }\n return parsed as Partial<AgentConfig>;\n}\n\nfunction getStateDir(): string {\n const raw = process.env.MAMA_STATE_DIR?.trim();\n return raw ? resolve(raw) : join(homedir(), \".mama\");\n}\n\nfunction loadRawAgentConfig(): Partial<AgentConfig> {\n return loadConfigFile(join(getStateDir(), \"settings.json\")) ?? {};\n}\n\nexport function loadAgentConfig(): AgentConfig {\n const fromFile = loadRawAgentConfig();\n\n const provider = fromFile.provider || process.env.MAMA_AI_PROVIDER || DEFAULTS.provider;\n const model = fromFile.model || process.env.MAMA_AI_MODEL || DEFAULTS.model;\n const thinkingLevel = fromFile.thinkingLevel ?? DEFAULTS.thinkingLevel;\n const sessionScope = fromFile.sessionScope ?? DEFAULTS.sessionScope;\n const logFormat = fromFile.logFormat ?? DEFAULTS.logFormat;\n const logLevel = fromFile.logLevel ?? DEFAULTS.logLevel;\n const sentryDsn = fromFile.sentryDsn ?? process.env.SENTRY_DSN;\n const sandboxCpus = fromFile.sandboxCpus;\n const sandboxMemory = fromFile.sandboxMemory;\n\n return {\n provider,\n model,\n thinkingLevel,\n sessionScope,\n logFormat,\n logLevel,\n sentryDsn,\n sandboxCpus,\n sandboxMemory,\n };\n}\n\nexport function resolveWorkspaceDirFromArgv(args = process.argv.slice(2)): string | undefined {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === \"--sandbox\" || arg === \"--download\" || arg === \"--state-dir\") {\n i += 1;\n continue;\n }\n\n if (arg === \"--version\" || arg === \"-v\" || arg === \"-V\") {\n continue;\n }\n\n if (\n arg.startsWith(\"--sandbox=\") ||\n arg.startsWith(\"--download=\") ||\n arg.startsWith(\"--state-dir=\")\n ) {\n continue;\n }\n\n if (!arg.startsWith(\"-\")) {\n return arg;\n }\n }\n\n return undefined;\n}\n\nexport function resolveStateDirFromArgv(args = process.argv.slice(2)): string {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg.startsWith(\"--state-dir=\")) {\n return resolve(arg.slice(\"--state-dir=\".length));\n }\n if (arg === \"--state-dir\") {\n return resolve(args[++i] || \"\");\n }\n }\n\n return join(homedir(), \".mama\");\n}\n\nexport function resolveSentryDsn(): string | undefined {\n const fromFile = loadRawAgentConfig();\n if (fromFile.sentryDsn) {\n return fromFile.sentryDsn;\n }\n\n return process.env.SENTRY_DSN;\n}\n\n/**\n * Externally-visible base URL of the link/OAuth server, e.g.\n * `https://mama.example.com` (no trailing slash). Read from `MAMA_LINK_URL`,\n * the same env var the bot uses to build credential onboarding links.\n */\nexport function resolveLinkBaseUrl(): string | undefined {\n const raw = process.env.MAMA_LINK_URL?.trim();\n if (!raw) return undefined;\n return raw.replace(/\\/+$/, \"\");\n}\n\nexport function saveAgentConfig(config: Partial<AgentConfig>): void {\n const settingsPath = join(getStateDir(), \"settings.json\");\n\n let existing: Partial<AgentConfig> = {};\n if (existsSync(settingsPath)) {\n const raw = readFileSync(settingsPath, \"utf-8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Refusing to overwrite malformed settings file at ${settingsPath}: ${detail}`,\n );\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\n `Refusing to overwrite malformed settings file at ${settingsPath}: expected a JSON object at the top level`,\n );\n }\n existing = parsed as Partial<AgentConfig>;\n }\n\n const merged = { ...existing, ...config };\n\n const dir = dirname(settingsPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n atomicWritePrivateFile(settingsPath, JSON.stringify(merged, null, 2));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAMnE,qBAAa,0BAA2B,SAAQ,KAAK;aACvB,YAAY,EAAE,MAAM;IAAhD,YAA4B,YAAY,EAAE,MAAM,EAG/C;CACF;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,SAAS,GAAG,MAAM,CAAC;IAC9B,QAAQ,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAiJD,wBAAgB,eAAe,IAAI,WAAW,CAE7C;AAED,wBAAgB,8BAA8B,CAAC,eAAe,EAAE,MAAM,GAAG,WAAW,CAMnF;AAED,wBAAgB,2BAA2B,CACzC,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC,GAC5F,IAAI,CAWN;AAED,wBAAgB,2BAA2B,CAAC,IAAI,WAAwB,GAAG,MAAM,GAAG,SAAS,CA2B5F;AAED,wBAAgB,uBAAuB,CAAC,IAAI,WAAwB,GAAG,MAAM,CAY5E;AAED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAOrD;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAIvD;AAwDD,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAwBlE","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport { existsSync, mkdirSync, readFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\n\nexport class MissingGlobalSettingsError extends Error {\n constructor(public readonly settingsPath: string) {\n super(`Missing global settings file at ${settingsPath}`);\n this.name = \"MissingGlobalSettingsError\";\n }\n}\n\nexport interface AgentConfig {\n provider: string;\n model: string;\n thinkingLevel: ThinkingLevel;\n logFormat: \"console\" | \"json\";\n logLevel: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\";\n sentryDsn?: string;\n sandboxCpus?: string;\n sandboxMemory?: string;\n sandboxBoostCpus?: string;\n sandboxBoostMemory?: string;\n}\n\nconst ONBOARD_SETTINGS: SettingsFileConfig = {\n llm: {\n provider: \"anthropic\",\n model: \"claude-sonnet-4-5\",\n thinkingLevel: \"off\",\n },\n log: {\n format: \"console\",\n level: \"info\",\n },\n sandbox: {\n cpus: \"0.5\",\n memory: \"1g\",\n boost: {\n cpus: \"2\",\n memory: \"4g\",\n },\n },\n};\n\ninterface SettingsFileConfig {\n llm?: Partial<Pick<AgentConfig, \"provider\" | \"model\" | \"thinkingLevel\">>;\n log?: { format?: AgentConfig[\"logFormat\"]; level?: AgentConfig[\"logLevel\"] };\n sentry?: { dsn?: string };\n sandbox?: { cpus?: string; memory?: string; boost?: { cpus?: string; memory?: string } };\n}\n\nfunction loadSettingsFile(settingsPath: string): SettingsFileConfig | undefined {\n if (!existsSync(settingsPath)) {\n return undefined;\n }\n\n const raw = readFileSync(settingsPath, \"utf-8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(`Malformed settings file at ${settingsPath}: ${detail}`);\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\n `Malformed settings file at ${settingsPath}: expected a JSON object at the top level`,\n );\n }\n return parsed as SettingsFileConfig;\n}\n\nfunction getStateDir(): string {\n const raw = process.env.MAMA_STATE_DIR?.trim();\n return raw ? resolve(raw) : join(homedir(), \".mama\");\n}\n\nfunction normalizeSettingsConfig(config: SettingsFileConfig): Partial<AgentConfig> {\n return {\n ...(config.llm?.provider !== undefined ? { provider: config.llm.provider } : {}),\n ...(config.llm?.model !== undefined ? { model: config.llm.model } : {}),\n ...(config.llm?.thinkingLevel !== undefined ? { thinkingLevel: config.llm.thinkingLevel } : {}),\n ...(config.log?.format !== undefined ? { logFormat: config.log.format } : {}),\n ...(config.log?.level !== undefined ? { logLevel: config.log.level } : {}),\n ...(config.sentry?.dsn !== undefined ? { sentryDsn: config.sentry.dsn } : {}),\n ...(config.sandbox?.cpus !== undefined ? { sandboxCpus: config.sandbox.cpus } : {}),\n ...(config.sandbox?.memory !== undefined ? { sandboxMemory: config.sandbox.memory } : {}),\n ...(config.sandbox?.boost?.cpus !== undefined\n ? { sandboxBoostCpus: config.sandbox.boost.cpus }\n : {}),\n ...(config.sandbox?.boost?.memory !== undefined\n ? { sandboxBoostMemory: config.sandbox.boost.memory }\n : {}),\n };\n}\n\nfunction getSettingsPath(): string {\n return join(getStateDir(), \"settings.json\");\n}\n\nfunction requireGlobalSettings(): SettingsFileConfig {\n const settingsPath = getSettingsPath();\n const config = loadSettingsFile(settingsPath);\n if (!config) {\n throw new MissingGlobalSettingsError(settingsPath);\n }\n return config;\n}\n\nfunction requireString(value: string | undefined, path: string): string {\n if (!value) {\n throw new Error(\n `Missing required global setting: ${path}. Run \\`mama --onboard\\` to create settings.json.`,\n );\n }\n return value;\n}\n\nfunction requireThinkingLevel(value: ThinkingLevel | undefined): ThinkingLevel {\n return requireString(value, \"llm.thinkingLevel\") as ThinkingLevel;\n}\n\nfunction requireLogFormat(value: AgentConfig[\"logFormat\"] | undefined): AgentConfig[\"logFormat\"] {\n if (value !== \"console\" && value !== \"json\") {\n throw new Error(\"Missing or invalid required global setting: log.format\");\n }\n return value;\n}\n\nfunction requireLogLevel(value: AgentConfig[\"logLevel\"] | undefined): AgentConfig[\"logLevel\"] {\n const allowed = [\"trace\", \"debug\", \"info\", \"warn\", \"error\"];\n if (!value || !allowed.includes(value)) {\n throw new Error(\"Missing or invalid required global setting: log.level\");\n }\n return value;\n}\n\nfunction toAgentConfig(fromFile: Partial<AgentConfig>): AgentConfig {\n const provider = requireString(fromFile.provider, \"llm.provider\");\n const model = requireString(fromFile.model, \"llm.model\");\n const thinkingLevel = requireThinkingLevel(fromFile.thinkingLevel);\n const logFormat = requireLogFormat(fromFile.logFormat);\n const logLevel = requireLogLevel(fromFile.logLevel);\n const sentryDsn = fromFile.sentryDsn ?? process.env.SENTRY_DSN;\n const sandboxCpus = fromFile.sandboxCpus;\n const sandboxMemory = fromFile.sandboxMemory;\n const sandboxBoostCpus = fromFile.sandboxBoostCpus;\n const sandboxBoostMemory = fromFile.sandboxBoostMemory;\n\n return {\n provider,\n model,\n thinkingLevel,\n logFormat,\n logLevel,\n sentryDsn,\n sandboxCpus,\n sandboxMemory,\n sandboxBoostCpus,\n sandboxBoostMemory,\n };\n}\n\nfunction loadRawAgentConfig(): Partial<AgentConfig> {\n return normalizeSettingsConfig(requireGlobalSettings());\n}\n\nexport function loadAgentConfig(): AgentConfig {\n return toAgentConfig(loadRawAgentConfig());\n}\n\nexport function loadAgentConfigForConversation(conversationDir: string): AgentConfig {\n const globalConfig = loadRawAgentConfig();\n const conversationConfig = normalizeSettingsConfig(\n loadSettingsFile(join(conversationDir, \"settings.json\")) ?? {},\n );\n return toAgentConfig({ ...globalConfig, ...conversationConfig });\n}\n\nexport function saveConversationModelConfig(\n conversationDir: string,\n config: Pick<AgentConfig, \"provider\" | \"model\"> & Partial<Pick<AgentConfig, \"thinkingLevel\">>,\n): void {\n if (!existsSync(conversationDir)) {\n mkdirSync(conversationDir, { recursive: true });\n }\n const settingsPath = join(conversationDir, \"settings.json\");\n const existing = loadSettingsFile(settingsPath) ?? {};\n const scopedConfig: SettingsFileConfig = {\n ...existing,\n llm: { ...existing.llm, ...config },\n };\n atomicWritePrivateFile(settingsPath, JSON.stringify(scopedConfig, null, 2));\n}\n\nexport function resolveWorkspaceDirFromArgv(args = process.argv.slice(2)): string | undefined {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === \"--sandbox\" || arg === \"--download\" || arg === \"--state-dir\") {\n i += 1;\n continue;\n }\n\n if (arg === \"--version\" || arg === \"-v\" || arg === \"-V\" || arg === \"--onboard\") {\n continue;\n }\n\n if (\n arg.startsWith(\"--sandbox=\") ||\n arg.startsWith(\"--download=\") ||\n arg.startsWith(\"--state-dir=\")\n ) {\n continue;\n }\n\n if (!arg.startsWith(\"-\")) {\n return arg;\n }\n }\n\n return undefined;\n}\n\nexport function resolveStateDirFromArgv(args = process.argv.slice(2)): string {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg.startsWith(\"--state-dir=\")) {\n return resolve(arg.slice(\"--state-dir=\".length));\n }\n if (arg === \"--state-dir\") {\n return resolve(args[++i] || \"\");\n }\n }\n\n return join(homedir(), \".mama\");\n}\n\nexport function resolveSentryDsn(): string | undefined {\n const fromFile = normalizeSettingsConfig(loadSettingsFile(getSettingsPath()) ?? {});\n if (fromFile.sentryDsn) {\n return fromFile.sentryDsn;\n }\n\n return process.env.SENTRY_DSN;\n}\n\nexport function createGlobalSettingsFile(stateDir: string): string {\n const settingsPath = join(stateDir, \"settings.json\");\n if (existsSync(settingsPath)) {\n throw new Error(`Global settings already exists at ${settingsPath}`);\n }\n if (!existsSync(stateDir)) {\n mkdirSync(stateDir, { recursive: true });\n }\n atomicWritePrivateFile(settingsPath, JSON.stringify(ONBOARD_SETTINGS, null, 2));\n return settingsPath;\n}\n\n/**\n * Externally-visible base URL of the link/OAuth server, e.g.\n * `https://mama.example.com` (no trailing slash). Read from `MAMA_LINK_URL`,\n * the same env var the bot uses to build credential onboarding links.\n */\nexport function resolveLinkBaseUrl(): string | undefined {\n const raw = process.env.MAMA_LINK_URL?.trim();\n if (!raw) return undefined;\n return raw.replace(/\\/+$/, \"\");\n}\n\nfunction hasDefinedValue(values: Record<string, unknown> | undefined): boolean {\n return values !== undefined && Object.values(values).some((value) => value !== undefined);\n}\n\nfunction compactSettingsConfig(config: SettingsFileConfig): SettingsFileConfig {\n return {\n ...(hasDefinedValue(config.llm) ? { llm: config.llm } : {}),\n ...(hasDefinedValue(config.log) ? { log: config.log } : {}),\n ...(hasDefinedValue(config.sentry) ? { sentry: config.sentry } : {}),\n ...(hasDefinedValue(config.sandbox) ? { sandbox: config.sandbox } : {}),\n };\n}\n\nfunction patchSettingsConfig(\n existing: SettingsFileConfig,\n config: Partial<AgentConfig>,\n): SettingsFileConfig {\n const patched: SettingsFileConfig = {\n ...existing,\n llm: {\n ...existing.llm,\n ...(config.provider !== undefined ? { provider: config.provider } : {}),\n ...(config.model !== undefined ? { model: config.model } : {}),\n ...(config.thinkingLevel !== undefined ? { thinkingLevel: config.thinkingLevel } : {}),\n },\n log: {\n ...existing.log,\n ...(config.logFormat !== undefined ? { format: config.logFormat } : {}),\n ...(config.logLevel !== undefined ? { level: config.logLevel } : {}),\n },\n sentry: {\n ...existing.sentry,\n ...(config.sentryDsn !== undefined ? { dsn: config.sentryDsn } : {}),\n },\n sandbox: {\n ...existing.sandbox,\n ...(config.sandboxCpus !== undefined ? { cpus: config.sandboxCpus } : {}),\n ...(config.sandboxMemory !== undefined ? { memory: config.sandboxMemory } : {}),\n ...(config.sandboxBoostCpus !== undefined || config.sandboxBoostMemory !== undefined\n ? {\n boost: {\n ...existing.sandbox?.boost,\n ...(config.sandboxBoostCpus !== undefined ? { cpus: config.sandboxBoostCpus } : {}),\n ...(config.sandboxBoostMemory !== undefined\n ? { memory: config.sandboxBoostMemory }\n : {}),\n },\n }\n : {}),\n },\n };\n return compactSettingsConfig(patched);\n}\n\nexport function saveAgentConfig(config: Partial<AgentConfig>): void {\n const settingsPath = join(getStateDir(), \"settings.json\");\n\n let existing: SettingsFileConfig = ONBOARD_SETTINGS;\n if (existsSync(settingsPath)) {\n try {\n existing = loadSettingsFile(settingsPath) ?? {};\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n const message = detail.startsWith(\"Malformed settings file\")\n ? detail.replace(\"Malformed settings file\", \"Refusing to overwrite malformed settings file\")\n : detail;\n throw new Error(message);\n }\n }\n\n const merged = patchSettingsConfig(existing, config);\n\n const dir = dirname(settingsPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n atomicWritePrivateFile(settingsPath, JSON.stringify(merged, null, 2));\n}\n"]}
|