@kl-c/matrixos 0.1.17 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js
CHANGED
|
@@ -2145,7 +2145,7 @@ var package_default;
|
|
|
2145
2145
|
var init_package = __esm(() => {
|
|
2146
2146
|
package_default = {
|
|
2147
2147
|
name: "@kl-c/matrixos",
|
|
2148
|
-
version: "0.1.
|
|
2148
|
+
version: "0.1.19",
|
|
2149
2149
|
description: "MaTrixOS \u2014 Agentic OS for OpenCode. Personalizable, communicating, self-improving, resilient.",
|
|
2150
2150
|
main: "./dist/index.js",
|
|
2151
2151
|
types: "dist/index.d.ts",
|
|
@@ -141031,101 +141031,63 @@ var AGENT_ROLES = {
|
|
|
141031
141031
|
Neo: "Executor",
|
|
141032
141032
|
Tank: "Deep Work"
|
|
141033
141033
|
};
|
|
141034
|
+
var OPENCODE_DB_PATH = path24.resolve(os12.homedir(), ".local", "share", "opencode", "opencode.db");
|
|
141035
|
+
var THIRTY_MIN_MS = 30 * 60 * 1000;
|
|
141036
|
+
var FIVE_MIN_MS = 5 * 60 * 1000;
|
|
141037
|
+
var BUDGET_TOTAL = 5000;
|
|
141034
141038
|
function randomBetween(min2, max2) {
|
|
141035
141039
|
return Math.floor(Math.random() * (max2 - min2 + 1)) + min2;
|
|
141036
141040
|
}
|
|
141037
141041
|
function randomChoice(arr) {
|
|
141038
141042
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
141039
141043
|
}
|
|
141040
|
-
function
|
|
141041
|
-
|
|
141042
|
-
|
|
141043
|
-
|
|
141044
|
-
|
|
141045
|
-
|
|
141046
|
-
|
|
141047
|
-
|
|
141048
|
-
|
|
141049
|
-
|
|
141050
|
-
|
|
141051
|
-
|
|
141052
|
-
|
|
141053
|
-
|
|
141054
|
-
|
|
141055
|
-
|
|
141056
|
-
|
|
141057
|
-
|
|
141058
|
-
cost: Math.round(randomBetween(1, 500) * 100) / 100,
|
|
141059
|
-
status: randomChoice(["active", "completed", "completed", "completed", "failed", "timeout"]),
|
|
141060
|
-
startedAt: new Date(Date.now() - randomBetween(0, 86400000)).toISOString()
|
|
141061
|
-
}));
|
|
141044
|
+
function parseModel(modelRaw) {
|
|
141045
|
+
if (!modelRaw)
|
|
141046
|
+
return "unknown";
|
|
141047
|
+
try {
|
|
141048
|
+
const parsed = JSON.parse(modelRaw);
|
|
141049
|
+
if (parsed.id)
|
|
141050
|
+
return parsed.id;
|
|
141051
|
+
} catch {}
|
|
141052
|
+
return modelRaw;
|
|
141053
|
+
}
|
|
141054
|
+
function getDb() {
|
|
141055
|
+
if (!fs20.existsSync(OPENCODE_DB_PATH))
|
|
141056
|
+
return;
|
|
141057
|
+
try {
|
|
141058
|
+
return new Database5(OPENCODE_DB_PATH, { readonly: true });
|
|
141059
|
+
} catch {
|
|
141060
|
+
return;
|
|
141061
|
+
}
|
|
141062
141062
|
}
|
|
141063
|
-
function
|
|
141064
|
-
const
|
|
141065
|
-
|
|
141066
|
-
|
|
141067
|
-
percentage: 0
|
|
141068
|
-
}));
|
|
141069
|
-
const total = agents.reduce((s, a2) => s + a2.cost, 0);
|
|
141070
|
-
agents.forEach((a2) => {
|
|
141071
|
-
a2.percentage = Math.round(a2.cost / total * 100);
|
|
141072
|
-
});
|
|
141073
|
-
return {
|
|
141074
|
-
total: Math.round(total * 100) / 100,
|
|
141075
|
-
daily: Math.round(randomBetween(50, 300) * 100) / 100,
|
|
141076
|
-
monthly: Math.round(randomBetween(1500, 9000) * 100) / 100,
|
|
141077
|
-
budgetRemaining: Math.round(randomBetween(500, 5000) * 100) / 100,
|
|
141078
|
-
byAgent: agents,
|
|
141079
|
-
byDay: Array.from({ length: 30 }, (_2, i3) => ({
|
|
141080
|
-
date: new Date(Date.now() - (29 - i3) * 86400000).toISOString().slice(0, 10),
|
|
141081
|
-
cost: Math.round(randomBetween(20, 300) * 100) / 100
|
|
141082
|
-
})),
|
|
141083
|
-
alerts: [
|
|
141084
|
-
{ severity: "warning", message: "Morpheus approaching budget threshold (85%)" },
|
|
141085
|
-
{ severity: "critical", message: "Tank exceeded daily budget cap ($500)" }
|
|
141086
|
-
]
|
|
141087
|
-
};
|
|
141063
|
+
function midnightMs() {
|
|
141064
|
+
const d = new Date;
|
|
141065
|
+
d.setHours(0, 0, 0, 0);
|
|
141066
|
+
return d.getTime();
|
|
141088
141067
|
}
|
|
141089
|
-
function
|
|
141090
|
-
|
|
141091
|
-
const columns = {};
|
|
141092
|
-
for (const s of statuses) {
|
|
141093
|
-
columns[s] = Array.from({ length: randomBetween(2, 6) }, (_2, i3) => ({
|
|
141094
|
-
id: `task_${randomBetween(1000, 9999)}`,
|
|
141095
|
-
title: randomChoice([
|
|
141096
|
-
"Implement Watchdog daemon",
|
|
141097
|
-
"Design System tokens",
|
|
141098
|
-
"API rate limiting",
|
|
141099
|
-
"Session persistence",
|
|
141100
|
-
"User auth flow",
|
|
141101
|
-
"Database migration",
|
|
141102
|
-
"Cache invalidation",
|
|
141103
|
-
"Webhook integration",
|
|
141104
|
-
"Telegram notifications",
|
|
141105
|
-
"Cost optimization",
|
|
141106
|
-
"Security audit",
|
|
141107
|
-
"Performance profiling"
|
|
141108
|
-
]),
|
|
141109
|
-
agent: randomChoice(AGENT_NAMES),
|
|
141110
|
-
priority: randomChoice(["low", "medium", "high", "critical"]),
|
|
141111
|
-
status: s,
|
|
141112
|
-
createdAt: new Date(Date.now() - randomBetween(0, 604800000)).toISOString(),
|
|
141113
|
-
updatedAt: new Date(Date.now() - randomBetween(0, 86400000)).toISOString()
|
|
141114
|
-
}));
|
|
141115
|
-
}
|
|
141116
|
-
return { columns };
|
|
141068
|
+
function thirtyDaysAgoMs() {
|
|
141069
|
+
return Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
141117
141070
|
}
|
|
141118
|
-
function
|
|
141119
|
-
|
|
141120
|
-
|
|
141121
|
-
|
|
141122
|
-
|
|
141123
|
-
|
|
141124
|
-
|
|
141125
|
-
|
|
141071
|
+
function readPackageVersion2() {
|
|
141072
|
+
try {
|
|
141073
|
+
const candidates = [
|
|
141074
|
+
new URL("../package.json", import.meta.url).pathname,
|
|
141075
|
+
new URL("../../package.json", import.meta.url).pathname,
|
|
141076
|
+
path24.resolve(process.cwd(), "package.json")
|
|
141077
|
+
];
|
|
141078
|
+
for (const candidate of candidates) {
|
|
141079
|
+
if (fs20.existsSync(candidate)) {
|
|
141080
|
+
const pkg = JSON.parse(fs20.readFileSync(candidate, "utf-8"));
|
|
141081
|
+
if (pkg.version)
|
|
141082
|
+
return pkg.version;
|
|
141083
|
+
}
|
|
141084
|
+
}
|
|
141085
|
+
} catch {}
|
|
141086
|
+
return "0.0.0";
|
|
141126
141087
|
}
|
|
141127
141088
|
function createDataProvider(config4) {
|
|
141128
141089
|
const matrixosHome = config4?.matrixosHome ?? ".matrixos";
|
|
141090
|
+
const version3 = readPackageVersion2();
|
|
141129
141091
|
function readJsonl(filePath) {
|
|
141130
141092
|
try {
|
|
141131
141093
|
const fullPath = path24.resolve(matrixosHome, "logs", filePath);
|
|
@@ -141141,13 +141103,33 @@ function createDataProvider(config4) {
|
|
|
141141
141103
|
async getHealth() {
|
|
141142
141104
|
const mem = process.memoryUsage();
|
|
141143
141105
|
const totalMem = os12.totalmem();
|
|
141106
|
+
const db = getDb();
|
|
141107
|
+
let agentsOnline = AGENT_NAMES.length;
|
|
141108
|
+
let activeSessions = 0;
|
|
141109
|
+
let tasksQueued = 0;
|
|
141110
|
+
if (db) {
|
|
141111
|
+
try {
|
|
141112
|
+
const agentRow = db.query("SELECT COUNT(DISTINCT agent) as count FROM session").get();
|
|
141113
|
+
if (agentRow)
|
|
141114
|
+
agentsOnline = agentRow.count;
|
|
141115
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141116
|
+
const sessionRows = db.query("SELECT COUNT(*) as count FROM session WHERE time_archived IS NULL AND time_updated > ?").all(cutoff);
|
|
141117
|
+
if (sessionRows.length > 0)
|
|
141118
|
+
activeSessions = sessionRows[0].count;
|
|
141119
|
+
const todoRows = db.query("SELECT COUNT(*) as count FROM todo WHERE status = 'pending'").all();
|
|
141120
|
+
if (todoRows.length > 0)
|
|
141121
|
+
tasksQueued = todoRows[0].count;
|
|
141122
|
+
} catch {} finally {
|
|
141123
|
+
db.close();
|
|
141124
|
+
}
|
|
141125
|
+
}
|
|
141144
141126
|
return {
|
|
141145
|
-
agentsOnline
|
|
141127
|
+
agentsOnline,
|
|
141146
141128
|
totalAgents: AGENT_NAMES.length,
|
|
141147
|
-
activeSessions
|
|
141148
|
-
tasksQueued
|
|
141129
|
+
activeSessions,
|
|
141130
|
+
tasksQueued,
|
|
141149
141131
|
uptime: process.uptime(),
|
|
141150
|
-
version:
|
|
141132
|
+
version: version3,
|
|
141151
141133
|
memoryUsage: {
|
|
141152
141134
|
used: Math.round(mem.rss / 1024 / 1024),
|
|
141153
141135
|
total: Math.round(totalMem / 1024 / 1024),
|
|
@@ -141157,16 +141139,181 @@ function createDataProvider(config4) {
|
|
|
141157
141139
|
};
|
|
141158
141140
|
},
|
|
141159
141141
|
async getAgents() {
|
|
141160
|
-
|
|
141142
|
+
const db = getDb();
|
|
141143
|
+
if (db) {
|
|
141144
|
+
try {
|
|
141145
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141146
|
+
const rows = db.query(`SELECT agent, COUNT(*) as sessions
|
|
141147
|
+
FROM session
|
|
141148
|
+
WHERE agent IS NOT NULL AND agent != ''
|
|
141149
|
+
GROUP BY agent`).all();
|
|
141150
|
+
if (rows.length > 0) {
|
|
141151
|
+
const result = [];
|
|
141152
|
+
for (const row of rows) {
|
|
141153
|
+
const name2 = row.agent;
|
|
141154
|
+
const onlineRows = db.query("SELECT COUNT(*) as count FROM session WHERE agent = ? AND time_updated > ?").all(name2, cutoff);
|
|
141155
|
+
const status2 = onlineRows.length > 0 && onlineRows[0].count > 0 ? "online" : "idle";
|
|
141156
|
+
result.push({
|
|
141157
|
+
name: name2,
|
|
141158
|
+
role: AGENT_ROLES[name2] ?? "Agent",
|
|
141159
|
+
status: status2,
|
|
141160
|
+
sessions: row.sessions,
|
|
141161
|
+
uptime: 0,
|
|
141162
|
+
cpuPercent: 0,
|
|
141163
|
+
memoryPercent: 0
|
|
141164
|
+
});
|
|
141165
|
+
}
|
|
141166
|
+
db.close();
|
|
141167
|
+
return result;
|
|
141168
|
+
}
|
|
141169
|
+
} catch {} finally {
|
|
141170
|
+
db.close();
|
|
141171
|
+
}
|
|
141172
|
+
}
|
|
141173
|
+
return AGENT_NAMES.map((name2) => ({
|
|
141174
|
+
name: name2,
|
|
141175
|
+
role: AGENT_ROLES[name2] ?? "Agent",
|
|
141176
|
+
status: "idle",
|
|
141177
|
+
sessions: 0,
|
|
141178
|
+
uptime: 0,
|
|
141179
|
+
cpuPercent: 0,
|
|
141180
|
+
memoryPercent: 0
|
|
141181
|
+
}));
|
|
141161
141182
|
},
|
|
141162
141183
|
async getSessions(limit = 20) {
|
|
141163
|
-
|
|
141184
|
+
const db = getDb();
|
|
141185
|
+
if (!db)
|
|
141186
|
+
return [];
|
|
141187
|
+
try {
|
|
141188
|
+
const now = Date.now();
|
|
141189
|
+
const rows = db.query(`SELECT id, title, agent, model, cost, time_created, time_updated, time_archived
|
|
141190
|
+
FROM session
|
|
141191
|
+
ORDER BY time_updated DESC
|
|
141192
|
+
LIMIT ?`).all(limit);
|
|
141193
|
+
const result = [];
|
|
141194
|
+
for (const row of rows) {
|
|
141195
|
+
const duration3 = Math.max(0, Math.round((row.time_updated - row.time_created) / 1000));
|
|
141196
|
+
let status2 = "completed";
|
|
141197
|
+
if (row.time_archived !== null) {
|
|
141198
|
+
status2 = "completed";
|
|
141199
|
+
} else if (row.time_updated > now - FIVE_MIN_MS) {
|
|
141200
|
+
status2 = "active";
|
|
141201
|
+
} else {
|
|
141202
|
+
status2 = "completed";
|
|
141203
|
+
}
|
|
141204
|
+
result.push({
|
|
141205
|
+
id: row.id,
|
|
141206
|
+
agent: row.agent ?? "unknown",
|
|
141207
|
+
model: parseModel(row.model),
|
|
141208
|
+
duration: duration3,
|
|
141209
|
+
cost: row.cost,
|
|
141210
|
+
status: status2,
|
|
141211
|
+
startedAt: new Date(row.time_created).toISOString()
|
|
141212
|
+
});
|
|
141213
|
+
}
|
|
141214
|
+
return result;
|
|
141215
|
+
} catch {
|
|
141216
|
+
return [];
|
|
141217
|
+
} finally {
|
|
141218
|
+
db.close();
|
|
141219
|
+
}
|
|
141164
141220
|
},
|
|
141165
141221
|
async getCost() {
|
|
141166
|
-
|
|
141222
|
+
const db = getDb();
|
|
141223
|
+
if (!db) {
|
|
141224
|
+
return {
|
|
141225
|
+
total: 0,
|
|
141226
|
+
daily: 0,
|
|
141227
|
+
monthly: 0,
|
|
141228
|
+
budgetRemaining: BUDGET_TOTAL,
|
|
141229
|
+
byAgent: [],
|
|
141230
|
+
byDay: [],
|
|
141231
|
+
alerts: []
|
|
141232
|
+
};
|
|
141233
|
+
}
|
|
141234
|
+
try {
|
|
141235
|
+
const totalRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session").all();
|
|
141236
|
+
const total = totalRows.length > 0 ? totalRows[0].sum ?? 0 : 0;
|
|
141237
|
+
const dailyRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session WHERE time_created > ?").all(midnightMs());
|
|
141238
|
+
const daily = dailyRows.length > 0 ? dailyRows[0].sum ?? 0 : 0;
|
|
141239
|
+
const monthlyRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session WHERE time_created > ?").all(thirtyDaysAgoMs());
|
|
141240
|
+
const monthly = monthlyRows.length > 0 ? monthlyRows[0].sum ?? 0 : 0;
|
|
141241
|
+
const agentRows = db.query(`SELECT agent, COALESCE(SUM(cost), 0) as cost
|
|
141242
|
+
FROM session
|
|
141243
|
+
WHERE agent IS NOT NULL AND agent != ''
|
|
141244
|
+
GROUP BY agent`).all();
|
|
141245
|
+
const byAgent = agentRows.map((r2) => ({
|
|
141246
|
+
agent: r2.agent,
|
|
141247
|
+
cost: r2.cost,
|
|
141248
|
+
percentage: total > 0 ? Math.round(r2.cost / total * 100) : 0
|
|
141249
|
+
}));
|
|
141250
|
+
const byDayRows = db.query(`SELECT strftime('%Y-%m-%d', time_created / 1000, 'unixepoch') as day,
|
|
141251
|
+
COALESCE(SUM(cost), 0) as cost
|
|
141252
|
+
FROM session
|
|
141253
|
+
WHERE time_created > ?
|
|
141254
|
+
GROUP BY day
|
|
141255
|
+
ORDER BY day ASC`).all(thirtyDaysAgoMs());
|
|
141256
|
+
const byDayMap = new Map;
|
|
141257
|
+
for (const r2 of byDayRows) {
|
|
141258
|
+
byDayMap.set(r2.day, r2.cost);
|
|
141259
|
+
}
|
|
141260
|
+
const byDay = [];
|
|
141261
|
+
const today = new Date;
|
|
141262
|
+
for (let i3 = 29;i3 >= 0; i3--) {
|
|
141263
|
+
const d = new Date(today);
|
|
141264
|
+
d.setDate(d.getDate() - i3);
|
|
141265
|
+
const key = d.toISOString().slice(0, 10);
|
|
141266
|
+
byDay.push({ date: key, cost: byDayMap.get(key) ?? 0 });
|
|
141267
|
+
}
|
|
141268
|
+
const budgetRemaining = Math.max(0, BUDGET_TOTAL - total);
|
|
141269
|
+
const alerts = [];
|
|
141270
|
+
if (total > BUDGET_TOTAL) {
|
|
141271
|
+
alerts.push({ severity: "critical", message: `Total cost $${total.toFixed(2)} exceeds budget $${BUDGET_TOTAL}` });
|
|
141272
|
+
} else if (total > BUDGET_TOTAL * 0.85) {
|
|
141273
|
+
alerts.push({ severity: "warning", message: `Cost $${total.toFixed(2)} approaching budget threshold (85%)` });
|
|
141274
|
+
}
|
|
141275
|
+
return { total, daily, monthly, budgetRemaining, byAgent, byDay, alerts };
|
|
141276
|
+
} catch {
|
|
141277
|
+
return {
|
|
141278
|
+
total: 0,
|
|
141279
|
+
daily: 0,
|
|
141280
|
+
monthly: 0,
|
|
141281
|
+
budgetRemaining: BUDGET_TOTAL,
|
|
141282
|
+
byAgent: [],
|
|
141283
|
+
byDay: [],
|
|
141284
|
+
alerts: []
|
|
141285
|
+
};
|
|
141286
|
+
} finally {
|
|
141287
|
+
db.close();
|
|
141288
|
+
}
|
|
141167
141289
|
},
|
|
141168
141290
|
async getKanbanTasks() {
|
|
141169
|
-
|
|
141291
|
+
const board = { columns: { pending: [], in_progress: [], completed: [], failed: [] } };
|
|
141292
|
+
const db = getDb();
|
|
141293
|
+
if (!db)
|
|
141294
|
+
return board;
|
|
141295
|
+
try {
|
|
141296
|
+
const rows = db.query("SELECT content, status, priority, time_created, time_updated FROM todo ORDER BY time_updated DESC").all();
|
|
141297
|
+
let idx = 0;
|
|
141298
|
+
for (const row of rows) {
|
|
141299
|
+
const status2 = row.status;
|
|
141300
|
+
if (!board.columns[status2])
|
|
141301
|
+
continue;
|
|
141302
|
+
const task2 = {
|
|
141303
|
+
id: `todo_${idx++}`,
|
|
141304
|
+
title: row.content,
|
|
141305
|
+
agent: "Unknown",
|
|
141306
|
+
priority: row.priority ?? "medium",
|
|
141307
|
+
status: status2,
|
|
141308
|
+
createdAt: new Date(row.time_created).toISOString(),
|
|
141309
|
+
updatedAt: new Date(row.time_updated).toISOString()
|
|
141310
|
+
};
|
|
141311
|
+
board.columns[status2].push(task2);
|
|
141312
|
+
}
|
|
141313
|
+
} catch {} finally {
|
|
141314
|
+
db.close();
|
|
141315
|
+
}
|
|
141316
|
+
return board;
|
|
141170
141317
|
},
|
|
141171
141318
|
async getIncidents(limit = 50) {
|
|
141172
141319
|
const lines = readJsonl("anti-loop.jsonl");
|
|
@@ -141190,7 +141337,33 @@ function createDataProvider(config4) {
|
|
|
141190
141337
|
}).filter(Boolean);
|
|
141191
141338
|
},
|
|
141192
141339
|
async getMetrics() {
|
|
141193
|
-
|
|
141340
|
+
const db = getDb();
|
|
141341
|
+
let activeSessions = 0;
|
|
141342
|
+
if (db) {
|
|
141343
|
+
try {
|
|
141344
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141345
|
+
const rows = db.query("SELECT COUNT(*) as count FROM session WHERE time_archived IS NULL AND time_updated > ?").all(cutoff);
|
|
141346
|
+
if (rows.length > 0)
|
|
141347
|
+
activeSessions = rows[0].count;
|
|
141348
|
+
} catch {} finally {
|
|
141349
|
+
db.close();
|
|
141350
|
+
}
|
|
141351
|
+
}
|
|
141352
|
+
const totalMem = os12.totalmem();
|
|
141353
|
+
const mem = process.memoryUsage();
|
|
141354
|
+
const memPercent = Math.round(mem.rss / totalMem * 100);
|
|
141355
|
+
const cpuPercent = Math.round(os12.loadavg()[0] * 100);
|
|
141356
|
+
const snapshots = [];
|
|
141357
|
+
for (let i3 = 0;i3 < 60; i3++) {
|
|
141358
|
+
snapshots.push({
|
|
141359
|
+
timestamp: Date.now() - (59 - i3) * 60000,
|
|
141360
|
+
cpuPercent,
|
|
141361
|
+
memoryPercent: memPercent,
|
|
141362
|
+
diskPercent: 0,
|
|
141363
|
+
sessionsActive: activeSessions
|
|
141364
|
+
});
|
|
141365
|
+
}
|
|
141366
|
+
return snapshots;
|
|
141194
141367
|
},
|
|
141195
141368
|
getLogs() {
|
|
141196
141369
|
const logFiles = [
|
|
@@ -141537,7 +141710,7 @@ function createDashboardServer(dataProvider, config4) {
|
|
|
141537
141710
|
// packages/omo-opencode/src/cli/dashboard.ts
|
|
141538
141711
|
async function dashboardCli(options) {
|
|
141539
141712
|
const port = options.port ?? 9123;
|
|
141540
|
-
const host = options.host ?? "
|
|
141713
|
+
const host = options.host ?? "0.0.0.0";
|
|
141541
141714
|
const frontendDir = path25.resolve(import.meta.dir, "../features/dashboard/frontend");
|
|
141542
141715
|
console.log(`[dashboard] starting MaTrixOS dashboard on http://${host}:${port}`);
|
|
141543
141716
|
console.log(`[dashboard] frontend: ${frontendDir}`);
|
|
@@ -141859,7 +142032,7 @@ function configureRuntimeCommands(program2) {
|
|
|
141859
142032
|
const exitCode = await architectRollback({ ...options, id });
|
|
141860
142033
|
process.exit(exitCode);
|
|
141861
142034
|
});
|
|
141862
|
-
program2.command("dashboard").description("Start the MaTrixOS web dashboard (port 9123)").option("-p, --port <number>", "HTTP port", "9123").option("--host <addr>", "Bind address", "
|
|
142035
|
+
program2.command("dashboard").description("Start the MaTrixOS web dashboard (port 9123)").option("-p, --port <number>", "HTTP port", "9123").option("--host <addr>", "Bind address", "0.0.0.0").option("--daemon", "Run in background (no Ctrl+C)").action(async (options) => {
|
|
141863
142036
|
const exitCode = await dashboardCli({
|
|
141864
142037
|
port: parseInt(options.port ?? "9123", 10),
|
|
141865
142038
|
host: options.host,
|
package/dist/cli-node/index.js
CHANGED
|
@@ -2145,7 +2145,7 @@ var package_default;
|
|
|
2145
2145
|
var init_package = __esm(() => {
|
|
2146
2146
|
package_default = {
|
|
2147
2147
|
name: "@kl-c/matrixos",
|
|
2148
|
-
version: "0.1.
|
|
2148
|
+
version: "0.1.19",
|
|
2149
2149
|
description: "MaTrixOS \u2014 Agentic OS for OpenCode. Personalizable, communicating, self-improving, resilient.",
|
|
2150
2150
|
main: "./dist/index.js",
|
|
2151
2151
|
types: "dist/index.d.ts",
|
|
@@ -141086,101 +141086,63 @@ var AGENT_ROLES = {
|
|
|
141086
141086
|
Neo: "Executor",
|
|
141087
141087
|
Tank: "Deep Work"
|
|
141088
141088
|
};
|
|
141089
|
+
var OPENCODE_DB_PATH = path24.resolve(os12.homedir(), ".local", "share", "opencode", "opencode.db");
|
|
141090
|
+
var THIRTY_MIN_MS = 30 * 60 * 1000;
|
|
141091
|
+
var FIVE_MIN_MS = 5 * 60 * 1000;
|
|
141092
|
+
var BUDGET_TOTAL = 5000;
|
|
141089
141093
|
function randomBetween(min2, max2) {
|
|
141090
141094
|
return Math.floor(Math.random() * (max2 - min2 + 1)) + min2;
|
|
141091
141095
|
}
|
|
141092
141096
|
function randomChoice(arr) {
|
|
141093
141097
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
141094
141098
|
}
|
|
141095
|
-
function
|
|
141096
|
-
|
|
141097
|
-
|
|
141098
|
-
|
|
141099
|
-
|
|
141100
|
-
|
|
141101
|
-
|
|
141102
|
-
|
|
141103
|
-
|
|
141104
|
-
|
|
141105
|
-
|
|
141106
|
-
|
|
141107
|
-
|
|
141108
|
-
|
|
141109
|
-
|
|
141110
|
-
|
|
141111
|
-
|
|
141112
|
-
|
|
141113
|
-
cost: Math.round(randomBetween(1, 500) * 100) / 100,
|
|
141114
|
-
status: randomChoice(["active", "completed", "completed", "completed", "failed", "timeout"]),
|
|
141115
|
-
startedAt: new Date(Date.now() - randomBetween(0, 86400000)).toISOString()
|
|
141116
|
-
}));
|
|
141099
|
+
function parseModel(modelRaw) {
|
|
141100
|
+
if (!modelRaw)
|
|
141101
|
+
return "unknown";
|
|
141102
|
+
try {
|
|
141103
|
+
const parsed = JSON.parse(modelRaw);
|
|
141104
|
+
if (parsed.id)
|
|
141105
|
+
return parsed.id;
|
|
141106
|
+
} catch {}
|
|
141107
|
+
return modelRaw;
|
|
141108
|
+
}
|
|
141109
|
+
function getDb() {
|
|
141110
|
+
if (!fs20.existsSync(OPENCODE_DB_PATH))
|
|
141111
|
+
return;
|
|
141112
|
+
try {
|
|
141113
|
+
return new Database5(OPENCODE_DB_PATH, { readonly: true });
|
|
141114
|
+
} catch {
|
|
141115
|
+
return;
|
|
141116
|
+
}
|
|
141117
141117
|
}
|
|
141118
|
-
function
|
|
141119
|
-
const
|
|
141120
|
-
|
|
141121
|
-
|
|
141122
|
-
percentage: 0
|
|
141123
|
-
}));
|
|
141124
|
-
const total = agents.reduce((s, a2) => s + a2.cost, 0);
|
|
141125
|
-
agents.forEach((a2) => {
|
|
141126
|
-
a2.percentage = Math.round(a2.cost / total * 100);
|
|
141127
|
-
});
|
|
141128
|
-
return {
|
|
141129
|
-
total: Math.round(total * 100) / 100,
|
|
141130
|
-
daily: Math.round(randomBetween(50, 300) * 100) / 100,
|
|
141131
|
-
monthly: Math.round(randomBetween(1500, 9000) * 100) / 100,
|
|
141132
|
-
budgetRemaining: Math.round(randomBetween(500, 5000) * 100) / 100,
|
|
141133
|
-
byAgent: agents,
|
|
141134
|
-
byDay: Array.from({ length: 30 }, (_2, i3) => ({
|
|
141135
|
-
date: new Date(Date.now() - (29 - i3) * 86400000).toISOString().slice(0, 10),
|
|
141136
|
-
cost: Math.round(randomBetween(20, 300) * 100) / 100
|
|
141137
|
-
})),
|
|
141138
|
-
alerts: [
|
|
141139
|
-
{ severity: "warning", message: "Morpheus approaching budget threshold (85%)" },
|
|
141140
|
-
{ severity: "critical", message: "Tank exceeded daily budget cap ($500)" }
|
|
141141
|
-
]
|
|
141142
|
-
};
|
|
141118
|
+
function midnightMs() {
|
|
141119
|
+
const d = new Date;
|
|
141120
|
+
d.setHours(0, 0, 0, 0);
|
|
141121
|
+
return d.getTime();
|
|
141143
141122
|
}
|
|
141144
|
-
function
|
|
141145
|
-
|
|
141146
|
-
const columns = {};
|
|
141147
|
-
for (const s of statuses) {
|
|
141148
|
-
columns[s] = Array.from({ length: randomBetween(2, 6) }, (_2, i3) => ({
|
|
141149
|
-
id: `task_${randomBetween(1000, 9999)}`,
|
|
141150
|
-
title: randomChoice([
|
|
141151
|
-
"Implement Watchdog daemon",
|
|
141152
|
-
"Design System tokens",
|
|
141153
|
-
"API rate limiting",
|
|
141154
|
-
"Session persistence",
|
|
141155
|
-
"User auth flow",
|
|
141156
|
-
"Database migration",
|
|
141157
|
-
"Cache invalidation",
|
|
141158
|
-
"Webhook integration",
|
|
141159
|
-
"Telegram notifications",
|
|
141160
|
-
"Cost optimization",
|
|
141161
|
-
"Security audit",
|
|
141162
|
-
"Performance profiling"
|
|
141163
|
-
]),
|
|
141164
|
-
agent: randomChoice(AGENT_NAMES),
|
|
141165
|
-
priority: randomChoice(["low", "medium", "high", "critical"]),
|
|
141166
|
-
status: s,
|
|
141167
|
-
createdAt: new Date(Date.now() - randomBetween(0, 604800000)).toISOString(),
|
|
141168
|
-
updatedAt: new Date(Date.now() - randomBetween(0, 86400000)).toISOString()
|
|
141169
|
-
}));
|
|
141170
|
-
}
|
|
141171
|
-
return { columns };
|
|
141123
|
+
function thirtyDaysAgoMs() {
|
|
141124
|
+
return Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
141172
141125
|
}
|
|
141173
|
-
function
|
|
141174
|
-
|
|
141175
|
-
|
|
141176
|
-
|
|
141177
|
-
|
|
141178
|
-
|
|
141179
|
-
|
|
141180
|
-
|
|
141126
|
+
function readPackageVersion2() {
|
|
141127
|
+
try {
|
|
141128
|
+
const candidates = [
|
|
141129
|
+
new URL("../package.json", import.meta.url).pathname,
|
|
141130
|
+
new URL("../../package.json", import.meta.url).pathname,
|
|
141131
|
+
path24.resolve(process.cwd(), "package.json")
|
|
141132
|
+
];
|
|
141133
|
+
for (const candidate of candidates) {
|
|
141134
|
+
if (fs20.existsSync(candidate)) {
|
|
141135
|
+
const pkg = JSON.parse(fs20.readFileSync(candidate, "utf-8"));
|
|
141136
|
+
if (pkg.version)
|
|
141137
|
+
return pkg.version;
|
|
141138
|
+
}
|
|
141139
|
+
}
|
|
141140
|
+
} catch {}
|
|
141141
|
+
return "0.0.0";
|
|
141181
141142
|
}
|
|
141182
141143
|
function createDataProvider(config4) {
|
|
141183
141144
|
const matrixosHome = config4?.matrixosHome ?? ".matrixos";
|
|
141145
|
+
const version3 = readPackageVersion2();
|
|
141184
141146
|
function readJsonl(filePath) {
|
|
141185
141147
|
try {
|
|
141186
141148
|
const fullPath = path24.resolve(matrixosHome, "logs", filePath);
|
|
@@ -141196,13 +141158,33 @@ function createDataProvider(config4) {
|
|
|
141196
141158
|
async getHealth() {
|
|
141197
141159
|
const mem = process.memoryUsage();
|
|
141198
141160
|
const totalMem = os12.totalmem();
|
|
141161
|
+
const db = getDb();
|
|
141162
|
+
let agentsOnline = AGENT_NAMES.length;
|
|
141163
|
+
let activeSessions = 0;
|
|
141164
|
+
let tasksQueued = 0;
|
|
141165
|
+
if (db) {
|
|
141166
|
+
try {
|
|
141167
|
+
const agentRow = db.query("SELECT COUNT(DISTINCT agent) as count FROM session").get();
|
|
141168
|
+
if (agentRow)
|
|
141169
|
+
agentsOnline = agentRow.count;
|
|
141170
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141171
|
+
const sessionRows = db.query("SELECT COUNT(*) as count FROM session WHERE time_archived IS NULL AND time_updated > ?").all(cutoff);
|
|
141172
|
+
if (sessionRows.length > 0)
|
|
141173
|
+
activeSessions = sessionRows[0].count;
|
|
141174
|
+
const todoRows = db.query("SELECT COUNT(*) as count FROM todo WHERE status = 'pending'").all();
|
|
141175
|
+
if (todoRows.length > 0)
|
|
141176
|
+
tasksQueued = todoRows[0].count;
|
|
141177
|
+
} catch {} finally {
|
|
141178
|
+
db.close();
|
|
141179
|
+
}
|
|
141180
|
+
}
|
|
141199
141181
|
return {
|
|
141200
|
-
agentsOnline
|
|
141182
|
+
agentsOnline,
|
|
141201
141183
|
totalAgents: AGENT_NAMES.length,
|
|
141202
|
-
activeSessions
|
|
141203
|
-
tasksQueued
|
|
141184
|
+
activeSessions,
|
|
141185
|
+
tasksQueued,
|
|
141204
141186
|
uptime: process.uptime(),
|
|
141205
|
-
version:
|
|
141187
|
+
version: version3,
|
|
141206
141188
|
memoryUsage: {
|
|
141207
141189
|
used: Math.round(mem.rss / 1024 / 1024),
|
|
141208
141190
|
total: Math.round(totalMem / 1024 / 1024),
|
|
@@ -141212,16 +141194,181 @@ function createDataProvider(config4) {
|
|
|
141212
141194
|
};
|
|
141213
141195
|
},
|
|
141214
141196
|
async getAgents() {
|
|
141215
|
-
|
|
141197
|
+
const db = getDb();
|
|
141198
|
+
if (db) {
|
|
141199
|
+
try {
|
|
141200
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141201
|
+
const rows = db.query(`SELECT agent, COUNT(*) as sessions
|
|
141202
|
+
FROM session
|
|
141203
|
+
WHERE agent IS NOT NULL AND agent != ''
|
|
141204
|
+
GROUP BY agent`).all();
|
|
141205
|
+
if (rows.length > 0) {
|
|
141206
|
+
const result = [];
|
|
141207
|
+
for (const row of rows) {
|
|
141208
|
+
const name2 = row.agent;
|
|
141209
|
+
const onlineRows = db.query("SELECT COUNT(*) as count FROM session WHERE agent = ? AND time_updated > ?").all(name2, cutoff);
|
|
141210
|
+
const status2 = onlineRows.length > 0 && onlineRows[0].count > 0 ? "online" : "idle";
|
|
141211
|
+
result.push({
|
|
141212
|
+
name: name2,
|
|
141213
|
+
role: AGENT_ROLES[name2] ?? "Agent",
|
|
141214
|
+
status: status2,
|
|
141215
|
+
sessions: row.sessions,
|
|
141216
|
+
uptime: 0,
|
|
141217
|
+
cpuPercent: 0,
|
|
141218
|
+
memoryPercent: 0
|
|
141219
|
+
});
|
|
141220
|
+
}
|
|
141221
|
+
db.close();
|
|
141222
|
+
return result;
|
|
141223
|
+
}
|
|
141224
|
+
} catch {} finally {
|
|
141225
|
+
db.close();
|
|
141226
|
+
}
|
|
141227
|
+
}
|
|
141228
|
+
return AGENT_NAMES.map((name2) => ({
|
|
141229
|
+
name: name2,
|
|
141230
|
+
role: AGENT_ROLES[name2] ?? "Agent",
|
|
141231
|
+
status: "idle",
|
|
141232
|
+
sessions: 0,
|
|
141233
|
+
uptime: 0,
|
|
141234
|
+
cpuPercent: 0,
|
|
141235
|
+
memoryPercent: 0
|
|
141236
|
+
}));
|
|
141216
141237
|
},
|
|
141217
141238
|
async getSessions(limit = 20) {
|
|
141218
|
-
|
|
141239
|
+
const db = getDb();
|
|
141240
|
+
if (!db)
|
|
141241
|
+
return [];
|
|
141242
|
+
try {
|
|
141243
|
+
const now = Date.now();
|
|
141244
|
+
const rows = db.query(`SELECT id, title, agent, model, cost, time_created, time_updated, time_archived
|
|
141245
|
+
FROM session
|
|
141246
|
+
ORDER BY time_updated DESC
|
|
141247
|
+
LIMIT ?`).all(limit);
|
|
141248
|
+
const result = [];
|
|
141249
|
+
for (const row of rows) {
|
|
141250
|
+
const duration3 = Math.max(0, Math.round((row.time_updated - row.time_created) / 1000));
|
|
141251
|
+
let status2 = "completed";
|
|
141252
|
+
if (row.time_archived !== null) {
|
|
141253
|
+
status2 = "completed";
|
|
141254
|
+
} else if (row.time_updated > now - FIVE_MIN_MS) {
|
|
141255
|
+
status2 = "active";
|
|
141256
|
+
} else {
|
|
141257
|
+
status2 = "completed";
|
|
141258
|
+
}
|
|
141259
|
+
result.push({
|
|
141260
|
+
id: row.id,
|
|
141261
|
+
agent: row.agent ?? "unknown",
|
|
141262
|
+
model: parseModel(row.model),
|
|
141263
|
+
duration: duration3,
|
|
141264
|
+
cost: row.cost,
|
|
141265
|
+
status: status2,
|
|
141266
|
+
startedAt: new Date(row.time_created).toISOString()
|
|
141267
|
+
});
|
|
141268
|
+
}
|
|
141269
|
+
return result;
|
|
141270
|
+
} catch {
|
|
141271
|
+
return [];
|
|
141272
|
+
} finally {
|
|
141273
|
+
db.close();
|
|
141274
|
+
}
|
|
141219
141275
|
},
|
|
141220
141276
|
async getCost() {
|
|
141221
|
-
|
|
141277
|
+
const db = getDb();
|
|
141278
|
+
if (!db) {
|
|
141279
|
+
return {
|
|
141280
|
+
total: 0,
|
|
141281
|
+
daily: 0,
|
|
141282
|
+
monthly: 0,
|
|
141283
|
+
budgetRemaining: BUDGET_TOTAL,
|
|
141284
|
+
byAgent: [],
|
|
141285
|
+
byDay: [],
|
|
141286
|
+
alerts: []
|
|
141287
|
+
};
|
|
141288
|
+
}
|
|
141289
|
+
try {
|
|
141290
|
+
const totalRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session").all();
|
|
141291
|
+
const total = totalRows.length > 0 ? totalRows[0].sum ?? 0 : 0;
|
|
141292
|
+
const dailyRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session WHERE time_created > ?").all(midnightMs());
|
|
141293
|
+
const daily = dailyRows.length > 0 ? dailyRows[0].sum ?? 0 : 0;
|
|
141294
|
+
const monthlyRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session WHERE time_created > ?").all(thirtyDaysAgoMs());
|
|
141295
|
+
const monthly = monthlyRows.length > 0 ? monthlyRows[0].sum ?? 0 : 0;
|
|
141296
|
+
const agentRows = db.query(`SELECT agent, COALESCE(SUM(cost), 0) as cost
|
|
141297
|
+
FROM session
|
|
141298
|
+
WHERE agent IS NOT NULL AND agent != ''
|
|
141299
|
+
GROUP BY agent`).all();
|
|
141300
|
+
const byAgent = agentRows.map((r2) => ({
|
|
141301
|
+
agent: r2.agent,
|
|
141302
|
+
cost: r2.cost,
|
|
141303
|
+
percentage: total > 0 ? Math.round(r2.cost / total * 100) : 0
|
|
141304
|
+
}));
|
|
141305
|
+
const byDayRows = db.query(`SELECT strftime('%Y-%m-%d', time_created / 1000, 'unixepoch') as day,
|
|
141306
|
+
COALESCE(SUM(cost), 0) as cost
|
|
141307
|
+
FROM session
|
|
141308
|
+
WHERE time_created > ?
|
|
141309
|
+
GROUP BY day
|
|
141310
|
+
ORDER BY day ASC`).all(thirtyDaysAgoMs());
|
|
141311
|
+
const byDayMap = new Map;
|
|
141312
|
+
for (const r2 of byDayRows) {
|
|
141313
|
+
byDayMap.set(r2.day, r2.cost);
|
|
141314
|
+
}
|
|
141315
|
+
const byDay = [];
|
|
141316
|
+
const today = new Date;
|
|
141317
|
+
for (let i3 = 29;i3 >= 0; i3--) {
|
|
141318
|
+
const d = new Date(today);
|
|
141319
|
+
d.setDate(d.getDate() - i3);
|
|
141320
|
+
const key = d.toISOString().slice(0, 10);
|
|
141321
|
+
byDay.push({ date: key, cost: byDayMap.get(key) ?? 0 });
|
|
141322
|
+
}
|
|
141323
|
+
const budgetRemaining = Math.max(0, BUDGET_TOTAL - total);
|
|
141324
|
+
const alerts = [];
|
|
141325
|
+
if (total > BUDGET_TOTAL) {
|
|
141326
|
+
alerts.push({ severity: "critical", message: `Total cost $${total.toFixed(2)} exceeds budget $${BUDGET_TOTAL}` });
|
|
141327
|
+
} else if (total > BUDGET_TOTAL * 0.85) {
|
|
141328
|
+
alerts.push({ severity: "warning", message: `Cost $${total.toFixed(2)} approaching budget threshold (85%)` });
|
|
141329
|
+
}
|
|
141330
|
+
return { total, daily, monthly, budgetRemaining, byAgent, byDay, alerts };
|
|
141331
|
+
} catch {
|
|
141332
|
+
return {
|
|
141333
|
+
total: 0,
|
|
141334
|
+
daily: 0,
|
|
141335
|
+
monthly: 0,
|
|
141336
|
+
budgetRemaining: BUDGET_TOTAL,
|
|
141337
|
+
byAgent: [],
|
|
141338
|
+
byDay: [],
|
|
141339
|
+
alerts: []
|
|
141340
|
+
};
|
|
141341
|
+
} finally {
|
|
141342
|
+
db.close();
|
|
141343
|
+
}
|
|
141222
141344
|
},
|
|
141223
141345
|
async getKanbanTasks() {
|
|
141224
|
-
|
|
141346
|
+
const board = { columns: { pending: [], in_progress: [], completed: [], failed: [] } };
|
|
141347
|
+
const db = getDb();
|
|
141348
|
+
if (!db)
|
|
141349
|
+
return board;
|
|
141350
|
+
try {
|
|
141351
|
+
const rows = db.query("SELECT content, status, priority, time_created, time_updated FROM todo ORDER BY time_updated DESC").all();
|
|
141352
|
+
let idx = 0;
|
|
141353
|
+
for (const row of rows) {
|
|
141354
|
+
const status2 = row.status;
|
|
141355
|
+
if (!board.columns[status2])
|
|
141356
|
+
continue;
|
|
141357
|
+
const task2 = {
|
|
141358
|
+
id: `todo_${idx++}`,
|
|
141359
|
+
title: row.content,
|
|
141360
|
+
agent: "Unknown",
|
|
141361
|
+
priority: row.priority ?? "medium",
|
|
141362
|
+
status: status2,
|
|
141363
|
+
createdAt: new Date(row.time_created).toISOString(),
|
|
141364
|
+
updatedAt: new Date(row.time_updated).toISOString()
|
|
141365
|
+
};
|
|
141366
|
+
board.columns[status2].push(task2);
|
|
141367
|
+
}
|
|
141368
|
+
} catch {} finally {
|
|
141369
|
+
db.close();
|
|
141370
|
+
}
|
|
141371
|
+
return board;
|
|
141225
141372
|
},
|
|
141226
141373
|
async getIncidents(limit = 50) {
|
|
141227
141374
|
const lines = readJsonl("anti-loop.jsonl");
|
|
@@ -141245,7 +141392,33 @@ function createDataProvider(config4) {
|
|
|
141245
141392
|
}).filter(Boolean);
|
|
141246
141393
|
},
|
|
141247
141394
|
async getMetrics() {
|
|
141248
|
-
|
|
141395
|
+
const db = getDb();
|
|
141396
|
+
let activeSessions = 0;
|
|
141397
|
+
if (db) {
|
|
141398
|
+
try {
|
|
141399
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141400
|
+
const rows = db.query("SELECT COUNT(*) as count FROM session WHERE time_archived IS NULL AND time_updated > ?").all(cutoff);
|
|
141401
|
+
if (rows.length > 0)
|
|
141402
|
+
activeSessions = rows[0].count;
|
|
141403
|
+
} catch {} finally {
|
|
141404
|
+
db.close();
|
|
141405
|
+
}
|
|
141406
|
+
}
|
|
141407
|
+
const totalMem = os12.totalmem();
|
|
141408
|
+
const mem = process.memoryUsage();
|
|
141409
|
+
const memPercent = Math.round(mem.rss / totalMem * 100);
|
|
141410
|
+
const cpuPercent = Math.round(os12.loadavg()[0] * 100);
|
|
141411
|
+
const snapshots = [];
|
|
141412
|
+
for (let i3 = 0;i3 < 60; i3++) {
|
|
141413
|
+
snapshots.push({
|
|
141414
|
+
timestamp: Date.now() - (59 - i3) * 60000,
|
|
141415
|
+
cpuPercent,
|
|
141416
|
+
memoryPercent: memPercent,
|
|
141417
|
+
diskPercent: 0,
|
|
141418
|
+
sessionsActive: activeSessions
|
|
141419
|
+
});
|
|
141420
|
+
}
|
|
141421
|
+
return snapshots;
|
|
141249
141422
|
},
|
|
141250
141423
|
getLogs() {
|
|
141251
141424
|
const logFiles = [
|
|
@@ -141592,7 +141765,7 @@ function createDashboardServer(dataProvider, config4) {
|
|
|
141592
141765
|
// packages/omo-opencode/src/cli/dashboard.ts
|
|
141593
141766
|
async function dashboardCli(options) {
|
|
141594
141767
|
const port = options.port ?? 9123;
|
|
141595
|
-
const host = options.host ?? "
|
|
141768
|
+
const host = options.host ?? "0.0.0.0";
|
|
141596
141769
|
const frontendDir = path25.resolve(import.meta.dir, "../features/dashboard/frontend");
|
|
141597
141770
|
console.log(`[dashboard] starting MaTrixOS dashboard on http://${host}:${port}`);
|
|
141598
141771
|
console.log(`[dashboard] frontend: ${frontendDir}`);
|
|
@@ -141914,7 +142087,7 @@ function configureRuntimeCommands(program2) {
|
|
|
141914
142087
|
const exitCode = await architectRollback({ ...options, id });
|
|
141915
142088
|
process.exit(exitCode);
|
|
141916
142089
|
});
|
|
141917
|
-
program2.command("dashboard").description("Start the MaTrixOS web dashboard (port 9123)").option("-p, --port <number>", "HTTP port", "9123").option("--host <addr>", "Bind address", "
|
|
142090
|
+
program2.command("dashboard").description("Start the MaTrixOS web dashboard (port 9123)").option("-p, --port <number>", "HTTP port", "9123").option("--host <addr>", "Bind address", "0.0.0.0").option("--daemon", "Run in background (no Ctrl+C)").action(async (options) => {
|
|
141918
142091
|
const exitCode = await dashboardCli({
|
|
141919
142092
|
port: parseInt(options.port ?? "9123", 10),
|
|
141920
142093
|
host: options.host,
|
|
@@ -23,11 +23,13 @@
|
|
|
23
23
|
<div class="sidebar-section">Operations</div>
|
|
24
24
|
<a href="#" class="sidebar-item" data-tab="health"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 2L1.5 13h13L8 2z"/><path d="M8 7v2.5M8 11.5v.5"/></svg>Incidents<span class="sidebar-badge danger" id="badgeIncidents">3</span></a>
|
|
25
25
|
<a href="#" class="sidebar-item" data-tab="logs"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M14 2H2v12h12V2z"/><path d="M5 8h6M5 11h4"/></svg>Logs</a>
|
|
26
|
-
<a href="#" class="sidebar-item"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 14A6 6 0 108 2a6 6 0 000 12z"/><path d="M8 5v3l2 2"/></svg>Schedule</a>
|
|
26
|
+
<a href="#" class="sidebar-item" data-tab="schedule"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 14A6 6 0 108 2a6 6 0 000 12z"/><path d="M8 5v3l2 2"/></svg>Schedule</a>
|
|
27
|
+
<a href="#" class="sidebar-item" data-tab="memory"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><rect x="2" y="3" width="12" height="11" rx="1"/><path d="M2 6h12"/><circle cx="8" cy="10" r="1.5"/></svg>Memory</a>
|
|
28
|
+
<a href="#" class="sidebar-item" data-tab="skills"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="6" r="3"/><path d="M3 14c0-3 2.5-5 5-5s5 2 5 5"/></svg>Skills</a>
|
|
27
29
|
<div class="sidebar-section">System</div>
|
|
28
30
|
<a href="#" class="sidebar-item" data-tab="config"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><rect x="2" y="2" width="12" height="12" rx="2"/><path d="M6 6h4M6 8h4M6 10h2"/></svg>Config</a>
|
|
29
|
-
<a href="#" class="sidebar-item"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M12 8A4 4 0 114 8a4 4 0 018 0z"/><path d="M8 2v1M8 13v1M2 8h1M13 8h1"/></svg>Settings</a>
|
|
30
|
-
<a href="#" class="sidebar-item"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M9 2H4a1 1 0 00-1 1v10a1 1 0 001 1h8a1 1 0 001-1V7l-5-5z"/><path d="M9 2v5h5"/></svg>Audit Trail</a>
|
|
31
|
+
<a href="#" class="sidebar-item" data-tab="config"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M12 8A4 4 0 114 8a4 4 0 018 0z"/><path d="M8 2v1M8 13v1M2 8h1M13 8h1"/></svg>Settings</a>
|
|
32
|
+
<a href="#" class="sidebar-item" data-tab="logs"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M9 2H4a1 1 0 00-1 1v10a1 1 0 001 1h8a1 1 0 001-1V7l-5-5z"/><path d="M9 2v5h5"/></svg>Audit Trail</a>
|
|
31
33
|
</nav>
|
|
32
34
|
<div class="sidebar-footer">
|
|
33
35
|
<div class="sidebar-avatar">SM</div>
|
|
@@ -165,6 +167,13 @@
|
|
|
165
167
|
</div>
|
|
166
168
|
<div class="card"><div class="card-header"><span class="card-title">matrixos.jsonc</span><span id="configStatus" style="font-size:var(--text-xs);color:var(--text-tertiary)"></span></div><div class="card-body"><textarea id="configEditor" class="config-editor" spellcheck="false"></textarea></div></div>
|
|
167
169
|
</div>
|
|
170
|
+
|
|
171
|
+
<div id="tab-schedule" class="tab-panel">
|
|
172
|
+
<div class="page-header">
|
|
173
|
+
<div><h1 class="page-title">Schedule</h1><p class="page-subtitle">Cron jobs and scheduled tasks for MaTrixOS agents</p></div>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="card"><div class="card-body" id="scheduleBody"></div></div>
|
|
176
|
+
</div>
|
|
168
177
|
</div>
|
|
169
178
|
</div>
|
|
170
179
|
<script src="js/api.js"></script>
|
|
@@ -10,7 +10,7 @@ document.querySelectorAll('[data-tab]').forEach(el=>{
|
|
|
10
10
|
el.addEventListener('click',e=>{
|
|
11
11
|
e.preventDefault()
|
|
12
12
|
const tab=el.dataset.tab
|
|
13
|
-
if(!tab||!['architect','health','cost','kanban','agents','sessions','logs','config'].includes(tab))return
|
|
13
|
+
if(!tab||!['architect','health','cost','kanban','agents','sessions','logs','config','memory','skills','schedule'].includes(tab))return
|
|
14
14
|
const realTab=tab==='agents'||tab==='sessions'?'architect':tab
|
|
15
15
|
document.querySelectorAll('.sidebar-item.active,.header-tab.active,.tab-panel.active').forEach(n=>n.classList.remove('active'))
|
|
16
16
|
document.querySelectorAll(`.sidebar-item[data-tab="${tab}"],.header-tab[data-tab="${realTab}"]`).forEach(n=>n.classList.add('active'))
|
|
@@ -20,7 +20,7 @@ document.querySelectorAll('[data-tab]').forEach(el=>{
|
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
// Tab loading dispatch
|
|
23
|
-
function loadTab(tab){const f={architect:loadArchitect,health:loadHealth,cost:loadCost,kanban:loadKanban,logs:loadLogs,config:loadConfig,memory:loadMemory,skills:loadSkills}[tab];f&&f()}
|
|
23
|
+
function loadTab(tab){const f={architect:loadArchitect,health:loadHealth,cost:loadCost,kanban:loadKanban,logs:loadLogs,config:loadConfig,memory:loadMemory,skills:loadSkills,schedule:loadSchedule}[tab];f&&f()}
|
|
24
24
|
|
|
25
25
|
// Skeleton helpers
|
|
26
26
|
function showSkeleton(container,type,count=4){
|
|
@@ -338,6 +338,16 @@ async function loadSkills(){
|
|
|
338
338
|
}catch(e){grid.innerHTML=`<div class="error"><p>Failed to load skills: ${e.message}</p></div>`}
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
// ===== SCHEDULE =====
|
|
342
|
+
function loadSchedule(){
|
|
343
|
+
const body=document.getElementById('scheduleBody')
|
|
344
|
+
if(!body)return
|
|
345
|
+
body.innerHTML=`<div class="empty" style="padding:40px">
|
|
346
|
+
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
|
347
|
+
<p>Schedule feature not configured — enable cron in matrixos.jsonc to start scheduling agent tasks.</p>
|
|
348
|
+
</div>`
|
|
349
|
+
}
|
|
350
|
+
|
|
341
351
|
// ===== INIT =====
|
|
342
352
|
document.getElementById('refreshBtn').onclick=()=>{const t=document.querySelector('.header-tab.active');t&&loadTab(t.dataset.tab)}
|
|
343
353
|
document.getElementById('archRefresh').onclick=()=>loadArchitect()
|
package/dist/index.js
CHANGED
|
@@ -367932,7 +367932,7 @@ function getCachedVersion(options = {}) {
|
|
|
367932
367932
|
// package.json
|
|
367933
367933
|
var package_default = {
|
|
367934
367934
|
name: "@kl-c/matrixos",
|
|
367935
|
-
version: "0.1.
|
|
367935
|
+
version: "0.1.19",
|
|
367936
367936
|
description: "MaTrixOS \u2014 Agentic OS for OpenCode. Personalizable, communicating, self-improving, resilient.",
|
|
367937
367937
|
main: "./dist/index.js",
|
|
367938
367938
|
types: "dist/index.d.ts",
|
package/package.json
CHANGED