@jungjaehoon/mama-os 0.76.0 → 0.78.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/CHANGELOG.md +182 -0
- package/README.md +25 -10
- package/dist/api/attachment-actions.d.ts.map +1 -1
- package/dist/api/attachment-actions.js +8 -7
- package/dist/api/attachment-actions.js.map +1 -1
- package/dist/api/code-act-actions.d.ts.map +1 -1
- package/dist/api/code-act-actions.js +5 -2
- package/dist/api/code-act-actions.js.map +1 -1
- package/dist/api/drive-actions.d.ts +4 -0
- package/dist/api/drive-actions.d.ts.map +1 -1
- package/dist/api/drive-actions.js +15 -7
- package/dist/api/drive-actions.js.map +1 -1
- package/dist/api/drive-delivery.d.ts.map +1 -1
- package/dist/api/drive-delivery.js +8 -3
- package/dist/api/drive-delivery.js.map +1 -1
- package/dist/api/member-enrollment.d.ts +2 -0
- package/dist/api/member-enrollment.d.ts.map +1 -1
- package/dist/api/member-enrollment.js +1 -1
- package/dist/api/member-enrollment.js.map +1 -1
- package/dist/api/member-lifecycle.d.ts +2 -0
- package/dist/api/member-lifecycle.d.ts.map +1 -1
- package/dist/api/member-lifecycle.js +1 -1
- package/dist/api/member-lifecycle.js.map +1 -1
- package/dist/api/owner-timezone-actions.d.ts +2 -0
- package/dist/api/owner-timezone-actions.d.ts.map +1 -1
- package/dist/api/owner-timezone-actions.js.map +1 -1
- package/dist/api/security-events.d.ts.map +1 -1
- package/dist/api/security-events.js +10 -1
- package/dist/api/security-events.js.map +1 -1
- package/dist/api/viewer-server.d.ts +1 -0
- package/dist/api/viewer-server.d.ts.map +1 -1
- package/dist/api/viewer-server.js +34 -0
- package/dist/api/viewer-server.js.map +1 -1
- package/dist/api/work-actions.d.ts.map +1 -1
- package/dist/api/work-actions.js +27 -2
- package/dist/api/work-actions.js.map +1 -1
- package/dist/cli/commands/backfill.d.ts.map +1 -1
- package/dist/cli/commands/backfill.js +1 -0
- package/dist/cli/commands/backfill.js.map +1 -1
- package/dist/cli/commands/backup.d.ts +13 -0
- package/dist/cli/commands/backup.d.ts.map +1 -0
- package/dist/cli/commands/backup.js +137 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/control.d.ts +26 -0
- package/dist/cli/commands/control.d.ts.map +1 -0
- package/dist/cli/commands/control.js +193 -0
- package/dist/cli/commands/control.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +49 -3
- package/dist/cli/commands/daemon.d.ts.map +1 -1
- package/dist/cli/commands/daemon.js +253 -75
- package/dist/cli/commands/daemon.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +13 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +216 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +3 -2
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/logs.d.ts +9 -0
- package/dist/cli/commands/logs.d.ts.map +1 -0
- package/dist/cli/commands/logs.js +109 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/register-owner.d.ts.map +1 -1
- package/dist/cli/commands/register-owner.js +12 -0
- package/dist/cli/commands/register-owner.js.map +1 -1
- package/dist/cli/commands/restore.d.ts +14 -0
- package/dist/cli/commands/restore.d.ts.map +1 -0
- package/dist/cli/commands/restore.js +130 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/service.d.ts +28 -0
- package/dist/cli/commands/service.d.ts.map +1 -0
- package/dist/cli/commands/service.js +278 -0
- package/dist/cli/commands/service.js.map +1 -0
- package/dist/cli/commands/uninstall.d.ts +7 -0
- package/dist/cli/commands/uninstall.d.ts.map +1 -0
- package/dist/cli/commands/uninstall.js +262 -0
- package/dist/cli/commands/uninstall.js.map +1 -0
- package/dist/cli/index.js +67 -20
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/launch-files.d.ts +83 -0
- package/dist/cli/launch-files.d.ts.map +1 -1
- package/dist/cli/launch-files.js +231 -15
- package/dist/cli/launch-files.js.map +1 -1
- package/dist/cli/runtime/backend-probe.d.ts +34 -0
- package/dist/cli/runtime/backend-probe.d.ts.map +1 -0
- package/dist/cli/runtime/backend-probe.js +83 -0
- package/dist/cli/runtime/backend-probe.js.map +1 -0
- package/dist/cli/runtime/backup.d.ts +114 -0
- package/dist/cli/runtime/backup.d.ts.map +1 -0
- package/dist/cli/runtime/backup.js +564 -0
- package/dist/cli/runtime/backup.js.map +1 -0
- package/dist/cli/runtime/claude-caller-config.d.ts +3 -1
- package/dist/cli/runtime/claude-caller-config.d.ts.map +1 -1
- package/dist/cli/runtime/claude-caller-config.js +14 -2
- package/dist/cli/runtime/claude-caller-config.js.map +1 -1
- package/dist/cli/runtime/daemon-control.d.ts +84 -0
- package/dist/cli/runtime/daemon-control.d.ts.map +1 -0
- package/dist/cli/runtime/daemon-control.js +266 -0
- package/dist/cli/runtime/daemon-control.js.map +1 -0
- package/dist/cli/runtime/diagnostics.d.ts +43 -0
- package/dist/cli/runtime/diagnostics.d.ts.map +1 -0
- package/dist/cli/runtime/diagnostics.js +953 -0
- package/dist/cli/runtime/diagnostics.js.map +1 -0
- package/dist/cli/runtime/restore.d.ts +98 -0
- package/dist/cli/runtime/restore.d.ts.map +1 -0
- package/dist/cli/runtime/restore.js +756 -0
- package/dist/cli/runtime/restore.js.map +1 -0
- package/dist/cli/runtime/systemd-service.d.ts +13 -0
- package/dist/cli/runtime/systemd-service.d.ts.map +1 -0
- package/dist/cli/runtime/systemd-service.js +32 -0
- package/dist/cli/runtime/systemd-service.js.map +1 -0
- package/dist/connectors/chatwork/index.js +1 -1
- package/dist/connectors/chatwork/index.js.map +1 -1
- package/dist/connectors/config-loader.d.ts +2 -1
- package/dist/connectors/config-loader.d.ts.map +1 -1
- package/dist/connectors/config-loader.js +3 -2
- package/dist/connectors/config-loader.js.map +1 -1
- package/dist/connectors/framework/polling-scheduler.d.ts.map +1 -1
- package/dist/connectors/framework/polling-scheduler.js +12 -11
- package/dist/connectors/framework/polling-scheduler.js.map +1 -1
- package/dist/connectors/imessage/index.d.ts.map +1 -1
- package/dist/connectors/imessage/index.js +2 -1
- package/dist/connectors/imessage/index.js.map +1 -1
- package/dist/connectors/slack/index.js +1 -1
- package/dist/connectors/slack/index.js.map +1 -1
- package/dist/runtime/action-surface.d.ts.map +1 -1
- package/dist/runtime/action-surface.js +6 -0
- package/dist/runtime/action-surface.js.map +1 -1
- package/dist/runtime/backend-security.d.ts +3 -0
- package/dist/runtime/backend-security.d.ts.map +1 -1
- package/dist/runtime/backend-security.js +13 -5
- package/dist/runtime/backend-security.js.map +1 -1
- package/dist/runtime/claude-caller-hook.d.ts +14 -0
- package/dist/runtime/claude-caller-hook.d.ts.map +1 -1
- package/dist/runtime/claude-caller-hook.js +21 -1
- package/dist/runtime/claude-caller-hook.js.map +1 -1
- package/dist/runtime/config.d.ts +8 -0
- package/dist/runtime/config.d.ts.map +1 -1
- package/dist/runtime/config.js +17 -4
- package/dist/runtime/config.js.map +1 -1
- package/dist/runtime/connectors.d.ts +21 -1
- package/dist/runtime/connectors.d.ts.map +1 -1
- package/dist/runtime/connectors.js +28 -17
- package/dist/runtime/connectors.js.map +1 -1
- package/dist/runtime/core-db.d.ts.map +1 -1
- package/dist/runtime/core-db.js +2 -0
- package/dist/runtime/core-db.js.map +1 -1
- package/dist/runtime/member-session.d.ts.map +1 -1
- package/dist/runtime/member-session.js +11 -3
- package/dist/runtime/member-session.js.map +1 -1
- package/dist/runtime/member-system-prompt.d.ts +2 -0
- package/dist/runtime/member-system-prompt.d.ts.map +1 -1
- package/dist/runtime/member-system-prompt.js +11 -3
- package/dist/runtime/member-system-prompt.js.map +1 -1
- package/dist/runtime/native-session.d.ts +2 -0
- package/dist/runtime/native-session.d.ts.map +1 -1
- package/dist/runtime/native-session.js +25 -4
- package/dist/runtime/native-session.js.map +1 -1
- package/dist/runtime/owner-authority.d.ts +2 -0
- package/dist/runtime/owner-authority.d.ts.map +1 -1
- package/dist/runtime/owner-authority.js +4 -0
- package/dist/runtime/owner-authority.js.map +1 -1
- package/dist/runtime/owner-runtime.d.ts +2 -0
- package/dist/runtime/owner-runtime.d.ts.map +1 -1
- package/dist/runtime/owner-runtime.js +3 -0
- package/dist/runtime/owner-runtime.js.map +1 -1
- package/dist/runtime/owner-system-prompt.d.ts.map +1 -1
- package/dist/runtime/owner-system-prompt.js +9 -7
- package/dist/runtime/owner-system-prompt.js.map +1 -1
- package/dist/runtime/principal-access.d.ts.map +1 -1
- package/dist/runtime/principal-access.js +1 -0
- package/dist/runtime/principal-access.js.map +1 -1
- package/dist/runtime/rotating-log.d.ts +44 -0
- package/dist/runtime/rotating-log.d.ts.map +1 -0
- package/dist/runtime/rotating-log.js +124 -0
- package/dist/runtime/rotating-log.js.map +1 -0
- package/dist/runtime/web-fetch-scope.d.ts +20 -0
- package/dist/runtime/web-fetch-scope.d.ts.map +1 -0
- package/dist/runtime/web-fetch-scope.js +68 -0
- package/dist/runtime/web-fetch-scope.js.map +1 -0
- package/dist/tools/document-toolkit.d.ts +2 -0
- package/dist/tools/document-toolkit.d.ts.map +1 -1
- package/dist/tools/document-toolkit.js +3 -1
- package/dist/tools/document-toolkit.js.map +1 -1
- package/dist/tools/office-render.d.ts.map +1 -1
- package/dist/tools/office-render.js +3 -0
- package/dist/tools/office-render.js.map +1 -1
- package/package.json +4 -3
- package/toolkit/requirements.in +11 -0
- package/toolkit/requirements.lock +984 -5
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OWNER_PRINCIPAL_ID = void 0;
|
|
3
|
+
exports.MESSENGER_SECRETS = exports.BOOT_STAGES = exports.StopDuringBootError = exports.OWNER_PRINCIPAL_ID = void 0;
|
|
4
|
+
exports.pathsOverlap = pathsOverlap;
|
|
4
5
|
exports.validateDeliveryRoutes = validateDeliveryRoutes;
|
|
6
|
+
exports.pathsFor = pathsFor;
|
|
5
7
|
exports.ensureDaemonIsolation = ensureDaemonIsolation;
|
|
6
8
|
exports.loggedOwnerIntake = loggedOwnerIntake;
|
|
7
9
|
exports.bootDaemon = bootDaemon;
|
|
8
10
|
exports.runDaemon = runDaemon;
|
|
9
|
-
exports.
|
|
10
|
-
exports.
|
|
11
|
+
exports.installControlPaths = installControlPaths;
|
|
12
|
+
exports.askDaemon = askDaemon;
|
|
13
|
+
exports.launchdService = launchdService;
|
|
14
|
+
exports.bootoutLaunchdService = bootoutLaunchdService;
|
|
11
15
|
const mama_core_1 = require("@jungjaehoon/mama-core");
|
|
12
16
|
const document_toolkit_js_1 = require("../../tools/document-toolkit.js");
|
|
13
17
|
const node_fs_1 = require("node:fs");
|
|
@@ -24,6 +28,7 @@ const action_surface_js_1 = require("../../runtime/action-surface.js");
|
|
|
24
28
|
const connectors_js_1 = require("../../runtime/connectors.js");
|
|
25
29
|
const config_js_1 = require("../../runtime/config.js");
|
|
26
30
|
const model_cache_js_1 = require("../../runtime/model-cache.js");
|
|
31
|
+
const backend_security_js_1 = require("../../runtime/backend-security.js");
|
|
27
32
|
const session_credential_js_1 = require("../../runtime/session-credential.js");
|
|
28
33
|
const action_mcp_config_js_1 = require("../runtime/action-mcp-config.js");
|
|
29
34
|
const index_js_1 = require("../../connectors/index.js");
|
|
@@ -38,6 +43,7 @@ const wiki_read_js_1 = require("../../wiki/wiki-read.js");
|
|
|
38
43
|
const timezone_js_1 = require("../../runtime/timezone.js");
|
|
39
44
|
const security_events_js_1 = require("../../api/security-events.js");
|
|
40
45
|
const egress_proxy_js_1 = require("../../runtime/egress-proxy.js");
|
|
46
|
+
const daemon_control_js_1 = require("../runtime/daemon-control.js");
|
|
41
47
|
exports.OWNER_PRINCIPAL_ID = 'owner';
|
|
42
48
|
const OWNER_AGENT_ID = 'owner-agent';
|
|
43
49
|
const OWNER_MEMORY_SCOPES = (0, action_surface_js_1.ownerMemoryScopes)(exports.OWNER_PRINCIPAL_ID, index_js_1.LOADABLE_CONNECTORS);
|
|
@@ -55,9 +61,49 @@ function errorName(error) {
|
|
|
55
61
|
const name = error.name.trim() !== '' ? error.name : 'Error';
|
|
56
62
|
return `${name}: ${error.message}`;
|
|
57
63
|
}
|
|
58
|
-
|
|
64
|
+
class StopDuringBootError extends Error {
|
|
65
|
+
constructor() {
|
|
66
|
+
super('Stopped during boot');
|
|
67
|
+
this.name = 'StopDuringBootError';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.StopDuringBootError = StopDuringBootError;
|
|
71
|
+
/** Every stage boot logs as `boot stage=<name>`; `mama doctor` reports only these names. */
|
|
72
|
+
exports.BOOT_STAGES = [
|
|
73
|
+
'config',
|
|
74
|
+
'isolation',
|
|
75
|
+
'owner_runtime',
|
|
76
|
+
'viewer',
|
|
77
|
+
'replay',
|
|
78
|
+
'connectors',
|
|
79
|
+
'messengers',
|
|
80
|
+
'telegram',
|
|
81
|
+
'telegram:disabled',
|
|
82
|
+
'discord',
|
|
83
|
+
'slack',
|
|
84
|
+
'report_scheduler',
|
|
85
|
+
'report_scheduler:route_unavailable',
|
|
86
|
+
'personal_job_scheduler',
|
|
87
|
+
];
|
|
88
|
+
function stage(logger, name, signal) {
|
|
89
|
+
if (signal?.aborted)
|
|
90
|
+
throw new StopDuringBootError();
|
|
59
91
|
logger.info(`boot stage=${name}`);
|
|
60
92
|
}
|
|
93
|
+
/** The auth.env names each messenger needs before it starts; `mama doctor` checks the same. */
|
|
94
|
+
exports.MESSENGER_SECRETS = {
|
|
95
|
+
telegram: ['MAMA_TELEGRAM_TOKEN'],
|
|
96
|
+
discord: ['MAMA_DISCORD_TOKEN'],
|
|
97
|
+
slack: ['MAMA_SLACK_TOKEN', 'MAMA_SLACK_APP_TOKEN'],
|
|
98
|
+
};
|
|
99
|
+
function messengerSecrets(messenger) {
|
|
100
|
+
return exports.MESSENGER_SECRETS[messenger].map((name) => {
|
|
101
|
+
const value = process.env[name];
|
|
102
|
+
if (!value?.trim())
|
|
103
|
+
throw new Error(`${name} is required`);
|
|
104
|
+
return value;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
61
107
|
function stageFailed(logger, name, error) {
|
|
62
108
|
logger.error(`boot stage=${name} failed error=${errorName(error)}`);
|
|
63
109
|
}
|
|
@@ -70,9 +116,10 @@ function stimulusFailed(logger, kind, id, error, modelRunId = null) {
|
|
|
70
116
|
function stimulusDelivered(logger, row, modelRunId) {
|
|
71
117
|
logger.info(`stimulus delivered kind=${row.kind ?? 'unknown'} id=${row.stimulusId} model_run_id=${modelRunId}`);
|
|
72
118
|
}
|
|
119
|
+
/** Physical paths: a symlinked workspace must not reach the directories it is kept apart from. */
|
|
73
120
|
function pathsOverlap(left, right) {
|
|
74
121
|
const inside = (parent, child) => {
|
|
75
|
-
const local = (0, node_path_1.relative)((0,
|
|
122
|
+
const local = (0, node_path_1.relative)((0, backend_security_js_1.physicalReadPath)(parent), (0, backend_security_js_1.physicalReadPath)(child));
|
|
76
123
|
return local === '' || (local !== '..' && !local.startsWith(`..${node_path_1.sep}`) && !(0, node_path_1.isAbsolute)(local));
|
|
77
124
|
};
|
|
78
125
|
return inside(left, right) || inside(right, left);
|
|
@@ -122,6 +169,16 @@ function pathsFor(configPath, config) {
|
|
|
122
169
|
if (pathsOverlap(workspaceDir, connectorsRoot)) {
|
|
123
170
|
throw new Error(`agent.codex_cwd must not contain or sit inside ${connectorsRoot}`);
|
|
124
171
|
}
|
|
172
|
+
// The runtime directory holds the session credential and the running turn's web fetch mode;
|
|
173
|
+
// an agent that could write there could open web fetch for a turn with no person in it.
|
|
174
|
+
if (pathsOverlap(workspaceDir, runtimeRoot)) {
|
|
175
|
+
throw new Error(`agent.codex_cwd must not contain or sit inside ${runtimeRoot}`);
|
|
176
|
+
}
|
|
177
|
+
// Backups are denied to the agents; a workspace there could not be read by its own agent.
|
|
178
|
+
const backupsRoot = (0, node_path_1.join)(mamaRoot, 'backups');
|
|
179
|
+
if (pathsOverlap(workspaceDir, backupsRoot)) {
|
|
180
|
+
throw new Error(`agent.codex_cwd must not contain or sit inside ${backupsRoot}`);
|
|
181
|
+
}
|
|
125
182
|
return {
|
|
126
183
|
mamaRoot,
|
|
127
184
|
runtimeRoot,
|
|
@@ -189,7 +246,15 @@ async function stopOne(logger, name, stop, errors) {
|
|
|
189
246
|
}
|
|
190
247
|
/** Open the W1 owner subject, then activate producers in their dependency order. */
|
|
191
248
|
async function bootDaemon(options = {}) {
|
|
192
|
-
const
|
|
249
|
+
const baseLogger = options.logger ?? defaultLogger;
|
|
250
|
+
let lastError = null;
|
|
251
|
+
const logger = {
|
|
252
|
+
info: (line) => baseLogger.info(line),
|
|
253
|
+
error: (line) => {
|
|
254
|
+
lastError = { at: Date.now(), line: line.slice(0, 500) };
|
|
255
|
+
baseLogger.error(line);
|
|
256
|
+
},
|
|
257
|
+
};
|
|
193
258
|
const dependencies = options.dependencies ?? {};
|
|
194
259
|
const configPath = options.configPath ?? (0, config_js_1.defaultConfigPath)(options.home ?? (0, node_os_1.homedir)());
|
|
195
260
|
let config;
|
|
@@ -206,10 +271,49 @@ async function bootDaemon(options = {}) {
|
|
|
206
271
|
let deliveryReady = options.mode === 'replay';
|
|
207
272
|
const startedAt = Date.now();
|
|
208
273
|
let currentStage = 'config';
|
|
209
|
-
|
|
274
|
+
let booted = false;
|
|
275
|
+
const command = options.command ?? (options.mode === 'replay' ? 'replay' : 'daemon');
|
|
276
|
+
// The install's one process: taken before anything opens the database or rewrites settings.
|
|
277
|
+
const control = (0, daemon_control_js_1.daemonControlPaths)((0, node_path_1.dirname)(configPath));
|
|
278
|
+
const lock = (0, daemon_control_js_1.acquireInstallLock)(control, command);
|
|
279
|
+
let controlServer = null;
|
|
280
|
+
const statusReport = async () => {
|
|
281
|
+
const report = {
|
|
282
|
+
pid: process.pid,
|
|
283
|
+
command,
|
|
284
|
+
version: (0, package_version_js_1.resolvePackageVersion)(),
|
|
285
|
+
startedAt,
|
|
286
|
+
stage: stopped ? 'stopping' : booted ? 'running' : currentStage,
|
|
287
|
+
lastError,
|
|
288
|
+
};
|
|
289
|
+
// Stores close during a stop; what it reports then is that it is stopping.
|
|
210
290
|
if (stopped)
|
|
211
|
-
return;
|
|
212
|
-
|
|
291
|
+
return report;
|
|
292
|
+
if (owner) {
|
|
293
|
+
const adapter = owner.database.adapter;
|
|
294
|
+
report.runningTurns = adapter
|
|
295
|
+
.prepare(`SELECT COALESCE(json_extract(input_refs_json, '$.sessionKey'), 'unknown') AS session,
|
|
296
|
+
COUNT(*) AS count
|
|
297
|
+
FROM model_runs WHERE status = 'running' AND created_at >= ? GROUP BY 1 ORDER BY 1`)
|
|
298
|
+
.all(startedAt);
|
|
299
|
+
report.inputs = adapter
|
|
300
|
+
.prepare(`SELECT status, COUNT(*) AS count FROM mailbox_inputs
|
|
301
|
+
WHERE status IN ('pending', 'claimed') GROUP BY status ORDER BY status`)
|
|
302
|
+
.all();
|
|
303
|
+
}
|
|
304
|
+
if (connectors) {
|
|
305
|
+
const connectorRuntime = connectors;
|
|
306
|
+
const health = await connectorRuntime.registry.healthCheckAll();
|
|
307
|
+
report.connectors = connectorRuntime.enabledConnectorNames.map((name) => ({
|
|
308
|
+
name,
|
|
309
|
+
healthy: health[name]?.healthy === true,
|
|
310
|
+
lastPoll: health[name]?.lastPollTime?.toISOString() ?? null,
|
|
311
|
+
error: health[name]?.error ?? null,
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
return report;
|
|
315
|
+
};
|
|
316
|
+
const stopOwned = async () => {
|
|
213
317
|
const errors = [];
|
|
214
318
|
owner?.beginStop?.();
|
|
215
319
|
// Quiesce every producer before awaiting any one of them. Transports and the database
|
|
@@ -236,22 +340,50 @@ async function bootDaemon(options = {}) {
|
|
|
236
340
|
if (errors.length > 0)
|
|
237
341
|
throw new AggregateError(errors, 'Daemon shutdown failed');
|
|
238
342
|
};
|
|
343
|
+
const stopResources = async () => {
|
|
344
|
+
if (stopped)
|
|
345
|
+
return;
|
|
346
|
+
stopped = true;
|
|
347
|
+
let closed = false;
|
|
348
|
+
try {
|
|
349
|
+
await stopOwned();
|
|
350
|
+
closed = true;
|
|
351
|
+
}
|
|
352
|
+
finally {
|
|
353
|
+
if (controlServer) {
|
|
354
|
+
await controlServer
|
|
355
|
+
.close()
|
|
356
|
+
.catch((error) => logger.error(`shutdown stage=control failed error=${errorName(error)}`));
|
|
357
|
+
}
|
|
358
|
+
// Released only once every store closed: after a failed stop something may still run, so
|
|
359
|
+
// the install stays held until this process ends and the operating system lets it go.
|
|
360
|
+
if (closed)
|
|
361
|
+
lock.release();
|
|
362
|
+
}
|
|
363
|
+
};
|
|
239
364
|
try {
|
|
240
365
|
currentStage = 'config';
|
|
241
|
-
|
|
366
|
+
// A half-switched install is neither the old one nor the restored one.
|
|
367
|
+
(0, daemon_control_js_1.refuseWhileRestoring)((0, node_path_1.dirname)(configPath));
|
|
368
|
+
controlServer = await (0, daemon_control_js_1.startDaemonControl)(control, {
|
|
369
|
+
status: statusReport,
|
|
370
|
+
stop: () => {
|
|
371
|
+
if (!options.requestStop)
|
|
372
|
+
throw new Error(`${command} stops when it finishes`);
|
|
373
|
+
options.requestStop();
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
if (options.signal?.aborted)
|
|
377
|
+
throw new StopDuringBootError();
|
|
242
378
|
config = options.config ?? (0, config_js_1.loadConfig)({ path: configPath, home: options.home });
|
|
379
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(config.logging.file), { recursive: true });
|
|
380
|
+
// The service manager redirects only what comes before this point, to the crash log. From here
|
|
381
|
+
// on the daemon's own log holds everything, from the first boot stage on.
|
|
382
|
+
options.captureOutput?.(config.logging.file);
|
|
383
|
+
stage(logger, 'config', options.signal);
|
|
243
384
|
logger.info(`document toolkit: ${(0, document_toolkit_js_1.readDocumentToolkit)(config.tools_root, (error) => logger.error(error.message)) ? 'present' : 'absent (run mama tools install)'}`);
|
|
244
385
|
const timeZone = (0, timezone_js_1.createTimeZoneSetting)(config.timezone);
|
|
245
386
|
validateDeliveryRoutes(config);
|
|
246
|
-
// launchd may have created the redirected log already; preserve its contents and tighten it.
|
|
247
|
-
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(config.logging.file), { recursive: true });
|
|
248
|
-
const logDescriptor = (0, node_fs_1.openSync)(config.logging.file, 'a', 0o600);
|
|
249
|
-
try {
|
|
250
|
-
(0, node_fs_1.fchmodSync)(logDescriptor, 0o600);
|
|
251
|
-
}
|
|
252
|
-
finally {
|
|
253
|
-
(0, node_fs_1.closeSync)(logDescriptor);
|
|
254
|
-
}
|
|
255
387
|
(0, model_cache_js_1.declareModelCache)();
|
|
256
388
|
paths = pathsFor(configPath, config);
|
|
257
389
|
currentStage = 'isolation';
|
|
@@ -261,7 +393,7 @@ async function bootDaemon(options = {}) {
|
|
|
261
393
|
paths,
|
|
262
394
|
...(options.mcpServerPath ? { mcpServerPath: options.mcpServerPath } : {}),
|
|
263
395
|
});
|
|
264
|
-
stage(logger, 'isolation');
|
|
396
|
+
stage(logger, 'isolation', options.signal);
|
|
265
397
|
currentStage = 'owner_runtime';
|
|
266
398
|
const ownerPolicyProvider = (0, owner_policy_js_1.createOwnerPolicyProvider)(paths.mamaRoot);
|
|
267
399
|
const ownerPolicy = ownerPolicyProvider();
|
|
@@ -381,6 +513,7 @@ async function bootDaemon(options = {}) {
|
|
|
381
513
|
connectors: index_js_1.LOADABLE_CONNECTORS,
|
|
382
514
|
rawPath: paths.connectorsRoot,
|
|
383
515
|
effort: config.agent.effort,
|
|
516
|
+
autonomousWebFetch: config.agent.autonomous_web_fetch ?? [],
|
|
384
517
|
maxTurns: config.agent.max_turns,
|
|
385
518
|
timeout: config.agent.timeout,
|
|
386
519
|
maxTurnMs: config.agent.max_turn_ms,
|
|
@@ -486,7 +619,7 @@ async function bootDaemon(options = {}) {
|
|
|
486
619
|
onStimulusDelivered: (row, modelRunId) => stimulusDelivered(logger, row, modelRunId),
|
|
487
620
|
onStimulusFailed: (row, reason, modelRunId) => stimulusFailed(logger, row.kind ?? 'unknown', row.stimulusId, reason, modelRunId),
|
|
488
621
|
});
|
|
489
|
-
stage(logger, 'owner_runtime');
|
|
622
|
+
stage(logger, 'owner_runtime', options.signal);
|
|
490
623
|
currentStage = 'viewer';
|
|
491
624
|
const viewerFactory = dependencies.createViewerServer ?? viewer_server_js_1.createViewerServer;
|
|
492
625
|
viewer = viewerFactory({
|
|
@@ -537,14 +670,14 @@ async function bootDaemon(options = {}) {
|
|
|
537
670
|
});
|
|
538
671
|
await viewer.start();
|
|
539
672
|
logger.info(`viewer server listening on port=${String(viewer.port)}`);
|
|
540
|
-
stage(logger, 'viewer');
|
|
673
|
+
stage(logger, 'viewer', options.signal);
|
|
541
674
|
if (options.mode === 'replay') {
|
|
542
675
|
logger.info('replay collectors: disabled');
|
|
543
676
|
currentStage = 'replay';
|
|
544
677
|
if (!options.replay)
|
|
545
678
|
throw new Error('Replay mode requires a replay feeder');
|
|
546
679
|
await options.replay({ config, paths, owner, logger, timeZone });
|
|
547
|
-
stage(logger, 'replay');
|
|
680
|
+
stage(logger, 'replay', options.signal);
|
|
548
681
|
return {
|
|
549
682
|
config,
|
|
550
683
|
paths,
|
|
@@ -581,16 +714,14 @@ async function bootDaemon(options = {}) {
|
|
|
581
714
|
coreAdapter: owner.database?.adapter,
|
|
582
715
|
acceptSourceDelta,
|
|
583
716
|
});
|
|
584
|
-
stage(logger, 'connectors');
|
|
717
|
+
stage(logger, 'connectors', options.signal);
|
|
585
718
|
currentStage = 'messengers';
|
|
586
719
|
const intake = loggedOwnerIntake(owner.intake, logger);
|
|
587
720
|
const ledgerPath = paths.ownerMessageLedgerPath;
|
|
588
721
|
messageLedger = new telegram_message_ledger_js_1.OwnerMessageLedger(ledgerPath, { log: (line) => logger.info(line) });
|
|
589
722
|
const filesRoot = (0, node_path_1.join)(paths.workspaceDir, 'files');
|
|
590
723
|
if (config.telegram.enabled) {
|
|
591
|
-
const token =
|
|
592
|
-
if (!token?.trim())
|
|
593
|
-
throw new Error('MAMA_TELEGRAM_TOKEN is required');
|
|
724
|
+
const [token] = messengerSecrets('telegram');
|
|
594
725
|
const factory = dependencies.createTelegramGateway ?? ((options) => new telegram_js_1.TelegramGateway(options));
|
|
595
726
|
gateway = factory({
|
|
596
727
|
token,
|
|
@@ -625,14 +756,12 @@ async function bootDaemon(options = {}) {
|
|
|
625
756
|
});
|
|
626
757
|
gateways.set('telegram', gateway);
|
|
627
758
|
await gateway.start();
|
|
628
|
-
stage(logger, 'telegram');
|
|
759
|
+
stage(logger, 'telegram', options.signal);
|
|
629
760
|
}
|
|
630
761
|
else
|
|
631
|
-
stage(logger, 'telegram:disabled');
|
|
762
|
+
stage(logger, 'telegram:disabled', options.signal);
|
|
632
763
|
if (config.discord?.enabled) {
|
|
633
|
-
const token =
|
|
634
|
-
if (!token?.trim())
|
|
635
|
-
throw new Error('MAMA_DISCORD_TOKEN is required');
|
|
764
|
+
const [token] = messengerSecrets('discord');
|
|
636
765
|
const factory = dependencies.createDiscordGateway ?? ((options) => new discord_js_1.DiscordGateway(options));
|
|
637
766
|
const active = factory({
|
|
638
767
|
token,
|
|
@@ -654,15 +783,10 @@ async function bootDaemon(options = {}) {
|
|
|
654
783
|
});
|
|
655
784
|
gateways.set('discord', active);
|
|
656
785
|
await active.start();
|
|
657
|
-
stage(logger, 'discord');
|
|
786
|
+
stage(logger, 'discord', options.signal);
|
|
658
787
|
}
|
|
659
788
|
if (config.slack?.enabled) {
|
|
660
|
-
const token =
|
|
661
|
-
const appToken = process.env.MAMA_SLACK_APP_TOKEN;
|
|
662
|
-
if (!token?.trim())
|
|
663
|
-
throw new Error('MAMA_SLACK_TOKEN is required');
|
|
664
|
-
if (!appToken?.trim())
|
|
665
|
-
throw new Error('MAMA_SLACK_APP_TOKEN is required');
|
|
789
|
+
const [token, appToken] = messengerSecrets('slack');
|
|
666
790
|
const factory = dependencies.createSlackGateway ?? ((options) => new slack_js_1.SlackGateway(options));
|
|
667
791
|
const active = factory({
|
|
668
792
|
token,
|
|
@@ -685,7 +809,7 @@ async function bootDaemon(options = {}) {
|
|
|
685
809
|
});
|
|
686
810
|
gateways.set('slack', active);
|
|
687
811
|
await active.start();
|
|
688
|
-
stage(logger, 'slack');
|
|
812
|
+
stage(logger, 'slack', options.signal);
|
|
689
813
|
}
|
|
690
814
|
const reportRoute = config.delivery?.reports ?? 'telegram';
|
|
691
815
|
if (gateways.has(reportRoute)) {
|
|
@@ -716,18 +840,19 @@ async function bootDaemon(options = {}) {
|
|
|
716
840
|
onError: (error) => logger.error(`report scheduler failed reason=${(0, stimulus_delivery_js_1.stimulusFailureReason)(error)}`),
|
|
717
841
|
});
|
|
718
842
|
reportScheduler.start();
|
|
719
|
-
stage(logger, 'report_scheduler');
|
|
843
|
+
stage(logger, 'report_scheduler', options.signal);
|
|
720
844
|
}
|
|
721
845
|
else
|
|
722
|
-
stage(logger, 'report_scheduler:route_unavailable');
|
|
846
|
+
stage(logger, 'report_scheduler:route_unavailable', options.signal);
|
|
723
847
|
if (gateway?.sendMemberText) {
|
|
724
848
|
owner.personalJobs.start();
|
|
725
849
|
await owner.personalJobs
|
|
726
850
|
.tick()
|
|
727
851
|
.catch((error) => logger.error(`personal job scheduler failed reason=${(0, stimulus_delivery_js_1.stimulusFailureReason)(error)}`));
|
|
728
|
-
stage(logger, 'personal_job_scheduler');
|
|
852
|
+
stage(logger, 'personal_job_scheduler', options.signal);
|
|
729
853
|
}
|
|
730
854
|
deliveryReady = true;
|
|
855
|
+
booted = true;
|
|
731
856
|
return {
|
|
732
857
|
config,
|
|
733
858
|
paths,
|
|
@@ -740,7 +865,16 @@ async function bootDaemon(options = {}) {
|
|
|
740
865
|
};
|
|
741
866
|
}
|
|
742
867
|
catch (error) {
|
|
743
|
-
|
|
868
|
+
if (error instanceof StopDuringBootError) {
|
|
869
|
+
logger.info(`boot stopped before stage=${currentStage}`);
|
|
870
|
+
}
|
|
871
|
+
else {
|
|
872
|
+
stageFailed(logger, currentStage, error);
|
|
873
|
+
// A frozen error keeps its own shape; the stage is still in the log line above.
|
|
874
|
+
if (error !== null && typeof error === 'object' && Object.isExtensible(error)) {
|
|
875
|
+
Object.defineProperty(error, 'bootStage', { value: currentStage, enumerable: false });
|
|
876
|
+
}
|
|
877
|
+
}
|
|
744
878
|
try {
|
|
745
879
|
await stopResources();
|
|
746
880
|
}
|
|
@@ -750,48 +884,92 @@ async function bootDaemon(options = {}) {
|
|
|
750
884
|
throw error;
|
|
751
885
|
}
|
|
752
886
|
}
|
|
753
|
-
/**
|
|
887
|
+
/**
|
|
888
|
+
* Foreground entry for launchd, systemd, Docker or a terminal. Signals are taken before boot, so a
|
|
889
|
+
* stop during boot unwinds what has started; `mama stop` asks through the control socket.
|
|
890
|
+
*/
|
|
754
891
|
async function runDaemon(options = {}) {
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
892
|
+
const controller = new AbortController();
|
|
893
|
+
// A stop asked for through `mama stop` exits 0; one by a signal exits 128 + its number, as an
|
|
894
|
+
// unhandled signal would, so a service that restarts only after a failure brings back a daemon
|
|
895
|
+
// that was killed (a service manager's own stop does not restart it whatever the code).
|
|
896
|
+
let signalExit;
|
|
897
|
+
const requestStop = () => controller.abort();
|
|
898
|
+
const onSigterm = () => {
|
|
899
|
+
signalExit = 143;
|
|
900
|
+
controller.abort();
|
|
901
|
+
};
|
|
902
|
+
const onSigint = () => {
|
|
903
|
+
signalExit = 130;
|
|
904
|
+
controller.abort();
|
|
905
|
+
};
|
|
906
|
+
// Once each: a second signal falls to Node's default and ends the process at once, a force stop;
|
|
907
|
+
// the operating system releases the install lock with it.
|
|
908
|
+
process.once('SIGTERM', onSigterm);
|
|
909
|
+
process.once('SIGINT', onSigint);
|
|
910
|
+
try {
|
|
911
|
+
let daemon;
|
|
912
|
+
try {
|
|
913
|
+
daemon = await bootDaemon({ ...options, signal: controller.signal, requestStop });
|
|
914
|
+
}
|
|
915
|
+
catch (error) {
|
|
916
|
+
if (error instanceof StopDuringBootError) {
|
|
917
|
+
if (signalExit !== undefined)
|
|
918
|
+
process.exitCode = signalExit;
|
|
760
919
|
return;
|
|
761
|
-
finished = true;
|
|
762
|
-
try {
|
|
763
|
-
await daemon.stop();
|
|
764
|
-
resolve();
|
|
765
|
-
}
|
|
766
|
-
catch (error) {
|
|
767
|
-
reject(error);
|
|
768
920
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
921
|
+
throw error;
|
|
922
|
+
}
|
|
923
|
+
if (!controller.signal.aborted) {
|
|
924
|
+
await new Promise((resolve) => controller.signal.addEventListener('abort', () => resolve(), { once: true }));
|
|
925
|
+
}
|
|
926
|
+
await daemon.stop();
|
|
927
|
+
if (signalExit !== undefined)
|
|
928
|
+
process.exitCode = signalExit;
|
|
929
|
+
}
|
|
930
|
+
finally {
|
|
931
|
+
process.off('SIGTERM', onSigterm);
|
|
932
|
+
process.off('SIGINT', onSigint);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
/** The install's control paths from its config path, as boot derives them. */
|
|
936
|
+
function installControlPaths(options = {}) {
|
|
937
|
+
const configPath = options.configPath ?? (0, config_js_1.defaultConfigPath)(options.home ?? (0, node_os_1.homedir)());
|
|
938
|
+
return (0, daemon_control_js_1.daemonControlPaths)((0, node_path_1.dirname)(configPath));
|
|
939
|
+
}
|
|
940
|
+
async function askDaemon(op, options = {}) {
|
|
941
|
+
return await (0, daemon_control_js_1.requestDaemonControl)(installControlPaths(options), op);
|
|
773
942
|
}
|
|
774
|
-
|
|
775
|
-
|
|
943
|
+
/** macOS only: this user's LaunchAgent (`launchctl print` exits 113 when it is not loaded). */
|
|
944
|
+
function launchdService() {
|
|
945
|
+
if (process.platform !== 'darwin')
|
|
946
|
+
return { loaded: false, pid: null };
|
|
947
|
+
const result = (0, node_child_process_1.spawnSync)('launchctl', ['print', `gui/${process.getuid()}/com.mama.server`], {
|
|
776
948
|
encoding: 'utf8',
|
|
777
949
|
});
|
|
778
950
|
if (result.error)
|
|
779
951
|
throw result.error;
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
952
|
+
if (result.status === 113)
|
|
953
|
+
return { loaded: false, pid: null };
|
|
954
|
+
if (result.status !== 0) {
|
|
955
|
+
throw Object.assign(new Error('launchctl print failed'), {
|
|
783
956
|
code: `LAUNCHCTL_${result.status ?? result.signal ?? 'UNKNOWN'}`,
|
|
784
957
|
});
|
|
785
958
|
}
|
|
786
|
-
|
|
959
|
+
const pid = /^\s*pid = (\d+)\s*$/m.exec(result.stdout)?.[1];
|
|
960
|
+
return { loaded: true, pid: pid === undefined ? null : Number(pid) };
|
|
787
961
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
962
|
+
/** Unload the LaunchAgent so KeepAlive cannot restart it; launchd sends the stop signal. */
|
|
963
|
+
function bootoutLaunchdService() {
|
|
964
|
+
const result = (0, node_child_process_1.spawnSync)('launchctl', ['bootout', `gui/${process.getuid()}/com.mama.server`], {
|
|
965
|
+
encoding: 'utf8',
|
|
966
|
+
});
|
|
967
|
+
if (result.error)
|
|
968
|
+
throw result.error;
|
|
969
|
+
if (result.status !== 0) {
|
|
970
|
+
throw Object.assign(new Error('launchctl bootout failed'), {
|
|
971
|
+
code: `LAUNCHCTL_${result.status ?? result.signal ?? 'UNKNOWN'}`,
|
|
972
|
+
});
|
|
973
|
+
}
|
|
796
974
|
}
|
|
797
975
|
//# sourceMappingURL=daemon.js.map
|