@kl-c/matrixos 0.1.17 → 0.1.18
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.18",
|
|
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,55 @@ 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
|
-
|
|
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
|
+
}
|
|
141050
141062
|
}
|
|
141051
|
-
function
|
|
141052
|
-
const
|
|
141053
|
-
|
|
141054
|
-
|
|
141055
|
-
agent: randomChoice(AGENT_NAMES),
|
|
141056
|
-
model: randomChoice(models2),
|
|
141057
|
-
duration: randomBetween(5, 600),
|
|
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
|
-
}));
|
|
141063
|
+
function midnightMs() {
|
|
141064
|
+
const d = new Date;
|
|
141065
|
+
d.setHours(0, 0, 0, 0);
|
|
141066
|
+
return d.getTime();
|
|
141062
141067
|
}
|
|
141063
|
-
function
|
|
141064
|
-
|
|
141065
|
-
agent: name2,
|
|
141066
|
-
cost: Math.round(randomBetween(50, 2000) * 100) / 100,
|
|
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
|
-
};
|
|
141068
|
+
function thirtyDaysAgoMs() {
|
|
141069
|
+
return Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
141088
141070
|
}
|
|
141089
|
-
function
|
|
141090
|
-
|
|
141091
|
-
|
|
141092
|
-
|
|
141093
|
-
|
|
141094
|
-
|
|
141095
|
-
|
|
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
|
-
}));
|
|
141071
|
+
function readPackageVersion2() {
|
|
141072
|
+
try {
|
|
141073
|
+
const pkgPath = new URL("../../../../package.json", import.meta.url).pathname;
|
|
141074
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
|
|
141075
|
+
return pkg.version ?? "0.0.0";
|
|
141076
|
+
} catch {
|
|
141077
|
+
return "0.0.0";
|
|
141115
141078
|
}
|
|
141116
|
-
return { columns };
|
|
141117
|
-
}
|
|
141118
|
-
function generateMockMetrics() {
|
|
141119
|
-
return Array.from({ length: 60 }, (_2, i3) => ({
|
|
141120
|
-
timestamp: Date.now() - (59 - i3) * 60000,
|
|
141121
|
-
cpuPercent: randomBetween(10, 95),
|
|
141122
|
-
memoryPercent: randomBetween(30, 85),
|
|
141123
|
-
diskPercent: randomBetween(40, 70),
|
|
141124
|
-
sessionsActive: randomBetween(1, 8)
|
|
141125
|
-
}));
|
|
141126
141079
|
}
|
|
141127
141080
|
function createDataProvider(config4) {
|
|
141128
141081
|
const matrixosHome = config4?.matrixosHome ?? ".matrixos";
|
|
141082
|
+
const version3 = readPackageVersion2();
|
|
141129
141083
|
function readJsonl(filePath) {
|
|
141130
141084
|
try {
|
|
141131
141085
|
const fullPath = path24.resolve(matrixosHome, "logs", filePath);
|
|
@@ -141141,13 +141095,33 @@ function createDataProvider(config4) {
|
|
|
141141
141095
|
async getHealth() {
|
|
141142
141096
|
const mem = process.memoryUsage();
|
|
141143
141097
|
const totalMem = os12.totalmem();
|
|
141098
|
+
const db = getDb();
|
|
141099
|
+
let agentsOnline = AGENT_NAMES.length;
|
|
141100
|
+
let activeSessions = 0;
|
|
141101
|
+
let tasksQueued = 0;
|
|
141102
|
+
if (db) {
|
|
141103
|
+
try {
|
|
141104
|
+
const agentRow = db.query("SELECT COUNT(DISTINCT agent) as count FROM session").get();
|
|
141105
|
+
if (agentRow)
|
|
141106
|
+
agentsOnline = agentRow.count;
|
|
141107
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141108
|
+
const sessionRows = db.query("SELECT COUNT(*) as count FROM session WHERE time_archived IS NULL AND time_updated > ?").all(cutoff);
|
|
141109
|
+
if (sessionRows.length > 0)
|
|
141110
|
+
activeSessions = sessionRows[0].count;
|
|
141111
|
+
const todoRows = db.query("SELECT COUNT(*) as count FROM todo WHERE status = 'pending'").all();
|
|
141112
|
+
if (todoRows.length > 0)
|
|
141113
|
+
tasksQueued = todoRows[0].count;
|
|
141114
|
+
} catch {} finally {
|
|
141115
|
+
db.close();
|
|
141116
|
+
}
|
|
141117
|
+
}
|
|
141144
141118
|
return {
|
|
141145
|
-
agentsOnline
|
|
141119
|
+
agentsOnline,
|
|
141146
141120
|
totalAgents: AGENT_NAMES.length,
|
|
141147
|
-
activeSessions
|
|
141148
|
-
tasksQueued
|
|
141121
|
+
activeSessions,
|
|
141122
|
+
tasksQueued,
|
|
141149
141123
|
uptime: process.uptime(),
|
|
141150
|
-
version:
|
|
141124
|
+
version: version3,
|
|
141151
141125
|
memoryUsage: {
|
|
141152
141126
|
used: Math.round(mem.rss / 1024 / 1024),
|
|
141153
141127
|
total: Math.round(totalMem / 1024 / 1024),
|
|
@@ -141157,16 +141131,181 @@ function createDataProvider(config4) {
|
|
|
141157
141131
|
};
|
|
141158
141132
|
},
|
|
141159
141133
|
async getAgents() {
|
|
141160
|
-
|
|
141134
|
+
const db = getDb();
|
|
141135
|
+
if (db) {
|
|
141136
|
+
try {
|
|
141137
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141138
|
+
const rows = db.query(`SELECT agent, COUNT(*) as sessions
|
|
141139
|
+
FROM session
|
|
141140
|
+
WHERE agent IS NOT NULL AND agent != ''
|
|
141141
|
+
GROUP BY agent`).all();
|
|
141142
|
+
if (rows.length > 0) {
|
|
141143
|
+
const result = [];
|
|
141144
|
+
for (const row of rows) {
|
|
141145
|
+
const name2 = row.agent;
|
|
141146
|
+
const onlineRows = db.query("SELECT COUNT(*) as count FROM session WHERE agent = ? AND time_updated > ?").all(name2, cutoff);
|
|
141147
|
+
const status2 = onlineRows.length > 0 && onlineRows[0].count > 0 ? "online" : "idle";
|
|
141148
|
+
result.push({
|
|
141149
|
+
name: name2,
|
|
141150
|
+
role: AGENT_ROLES[name2] ?? "Agent",
|
|
141151
|
+
status: status2,
|
|
141152
|
+
sessions: row.sessions,
|
|
141153
|
+
uptime: 0,
|
|
141154
|
+
cpuPercent: 0,
|
|
141155
|
+
memoryPercent: 0
|
|
141156
|
+
});
|
|
141157
|
+
}
|
|
141158
|
+
db.close();
|
|
141159
|
+
return result;
|
|
141160
|
+
}
|
|
141161
|
+
} catch {} finally {
|
|
141162
|
+
db.close();
|
|
141163
|
+
}
|
|
141164
|
+
}
|
|
141165
|
+
return AGENT_NAMES.map((name2) => ({
|
|
141166
|
+
name: name2,
|
|
141167
|
+
role: AGENT_ROLES[name2] ?? "Agent",
|
|
141168
|
+
status: "idle",
|
|
141169
|
+
sessions: 0,
|
|
141170
|
+
uptime: 0,
|
|
141171
|
+
cpuPercent: 0,
|
|
141172
|
+
memoryPercent: 0
|
|
141173
|
+
}));
|
|
141161
141174
|
},
|
|
141162
141175
|
async getSessions(limit = 20) {
|
|
141163
|
-
|
|
141176
|
+
const db = getDb();
|
|
141177
|
+
if (!db)
|
|
141178
|
+
return [];
|
|
141179
|
+
try {
|
|
141180
|
+
const now = Date.now();
|
|
141181
|
+
const rows = db.query(`SELECT id, title, agent, model, cost, time_created, time_updated, time_archived
|
|
141182
|
+
FROM session
|
|
141183
|
+
ORDER BY time_updated DESC
|
|
141184
|
+
LIMIT ?`).all(limit);
|
|
141185
|
+
const result = [];
|
|
141186
|
+
for (const row of rows) {
|
|
141187
|
+
const duration3 = Math.max(0, Math.round((row.time_updated - row.time_created) / 1000));
|
|
141188
|
+
let status2 = "completed";
|
|
141189
|
+
if (row.time_archived !== null) {
|
|
141190
|
+
status2 = "completed";
|
|
141191
|
+
} else if (row.time_updated > now - FIVE_MIN_MS) {
|
|
141192
|
+
status2 = "active";
|
|
141193
|
+
} else {
|
|
141194
|
+
status2 = "completed";
|
|
141195
|
+
}
|
|
141196
|
+
result.push({
|
|
141197
|
+
id: row.id,
|
|
141198
|
+
agent: row.agent ?? "unknown",
|
|
141199
|
+
model: parseModel(row.model),
|
|
141200
|
+
duration: duration3,
|
|
141201
|
+
cost: row.cost,
|
|
141202
|
+
status: status2,
|
|
141203
|
+
startedAt: new Date(row.time_created).toISOString()
|
|
141204
|
+
});
|
|
141205
|
+
}
|
|
141206
|
+
return result;
|
|
141207
|
+
} catch {
|
|
141208
|
+
return [];
|
|
141209
|
+
} finally {
|
|
141210
|
+
db.close();
|
|
141211
|
+
}
|
|
141164
141212
|
},
|
|
141165
141213
|
async getCost() {
|
|
141166
|
-
|
|
141214
|
+
const db = getDb();
|
|
141215
|
+
if (!db) {
|
|
141216
|
+
return {
|
|
141217
|
+
total: 0,
|
|
141218
|
+
daily: 0,
|
|
141219
|
+
monthly: 0,
|
|
141220
|
+
budgetRemaining: BUDGET_TOTAL,
|
|
141221
|
+
byAgent: [],
|
|
141222
|
+
byDay: [],
|
|
141223
|
+
alerts: []
|
|
141224
|
+
};
|
|
141225
|
+
}
|
|
141226
|
+
try {
|
|
141227
|
+
const totalRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session").all();
|
|
141228
|
+
const total = totalRows.length > 0 ? totalRows[0].sum ?? 0 : 0;
|
|
141229
|
+
const dailyRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session WHERE time_created > ?").all(midnightMs());
|
|
141230
|
+
const daily = dailyRows.length > 0 ? dailyRows[0].sum ?? 0 : 0;
|
|
141231
|
+
const monthlyRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session WHERE time_created > ?").all(thirtyDaysAgoMs());
|
|
141232
|
+
const monthly = monthlyRows.length > 0 ? monthlyRows[0].sum ?? 0 : 0;
|
|
141233
|
+
const agentRows = db.query(`SELECT agent, COALESCE(SUM(cost), 0) as cost
|
|
141234
|
+
FROM session
|
|
141235
|
+
WHERE agent IS NOT NULL AND agent != ''
|
|
141236
|
+
GROUP BY agent`).all();
|
|
141237
|
+
const byAgent = agentRows.map((r2) => ({
|
|
141238
|
+
agent: r2.agent,
|
|
141239
|
+
cost: r2.cost,
|
|
141240
|
+
percentage: total > 0 ? Math.round(r2.cost / total * 100) : 0
|
|
141241
|
+
}));
|
|
141242
|
+
const byDayRows = db.query(`SELECT strftime('%Y-%m-%d', time_created / 1000, 'unixepoch') as day,
|
|
141243
|
+
COALESCE(SUM(cost), 0) as cost
|
|
141244
|
+
FROM session
|
|
141245
|
+
WHERE time_created > ?
|
|
141246
|
+
GROUP BY day
|
|
141247
|
+
ORDER BY day ASC`).all(thirtyDaysAgoMs());
|
|
141248
|
+
const byDayMap = new Map;
|
|
141249
|
+
for (const r2 of byDayRows) {
|
|
141250
|
+
byDayMap.set(r2.day, r2.cost);
|
|
141251
|
+
}
|
|
141252
|
+
const byDay = [];
|
|
141253
|
+
const today = new Date;
|
|
141254
|
+
for (let i3 = 29;i3 >= 0; i3--) {
|
|
141255
|
+
const d = new Date(today);
|
|
141256
|
+
d.setDate(d.getDate() - i3);
|
|
141257
|
+
const key = d.toISOString().slice(0, 10);
|
|
141258
|
+
byDay.push({ date: key, cost: byDayMap.get(key) ?? 0 });
|
|
141259
|
+
}
|
|
141260
|
+
const budgetRemaining = Math.max(0, BUDGET_TOTAL - total);
|
|
141261
|
+
const alerts = [];
|
|
141262
|
+
if (total > BUDGET_TOTAL) {
|
|
141263
|
+
alerts.push({ severity: "critical", message: `Total cost $${total.toFixed(2)} exceeds budget $${BUDGET_TOTAL}` });
|
|
141264
|
+
} else if (total > BUDGET_TOTAL * 0.85) {
|
|
141265
|
+
alerts.push({ severity: "warning", message: `Cost $${total.toFixed(2)} approaching budget threshold (85%)` });
|
|
141266
|
+
}
|
|
141267
|
+
return { total, daily, monthly, budgetRemaining, byAgent, byDay, alerts };
|
|
141268
|
+
} catch {
|
|
141269
|
+
return {
|
|
141270
|
+
total: 0,
|
|
141271
|
+
daily: 0,
|
|
141272
|
+
monthly: 0,
|
|
141273
|
+
budgetRemaining: BUDGET_TOTAL,
|
|
141274
|
+
byAgent: [],
|
|
141275
|
+
byDay: [],
|
|
141276
|
+
alerts: []
|
|
141277
|
+
};
|
|
141278
|
+
} finally {
|
|
141279
|
+
db.close();
|
|
141280
|
+
}
|
|
141167
141281
|
},
|
|
141168
141282
|
async getKanbanTasks() {
|
|
141169
|
-
|
|
141283
|
+
const board = { columns: { pending: [], in_progress: [], completed: [], failed: [] } };
|
|
141284
|
+
const db = getDb();
|
|
141285
|
+
if (!db)
|
|
141286
|
+
return board;
|
|
141287
|
+
try {
|
|
141288
|
+
const rows = db.query("SELECT content, status, priority, time_created, time_updated FROM todo ORDER BY time_updated DESC").all();
|
|
141289
|
+
let idx = 0;
|
|
141290
|
+
for (const row of rows) {
|
|
141291
|
+
const status2 = row.status;
|
|
141292
|
+
if (!board.columns[status2])
|
|
141293
|
+
continue;
|
|
141294
|
+
const task2 = {
|
|
141295
|
+
id: `todo_${idx++}`,
|
|
141296
|
+
title: row.content,
|
|
141297
|
+
agent: "Unknown",
|
|
141298
|
+
priority: row.priority ?? "medium",
|
|
141299
|
+
status: status2,
|
|
141300
|
+
createdAt: new Date(row.time_created).toISOString(),
|
|
141301
|
+
updatedAt: new Date(row.time_updated).toISOString()
|
|
141302
|
+
};
|
|
141303
|
+
board.columns[status2].push(task2);
|
|
141304
|
+
}
|
|
141305
|
+
} catch {} finally {
|
|
141306
|
+
db.close();
|
|
141307
|
+
}
|
|
141308
|
+
return board;
|
|
141170
141309
|
},
|
|
141171
141310
|
async getIncidents(limit = 50) {
|
|
141172
141311
|
const lines = readJsonl("anti-loop.jsonl");
|
|
@@ -141190,7 +141329,33 @@ function createDataProvider(config4) {
|
|
|
141190
141329
|
}).filter(Boolean);
|
|
141191
141330
|
},
|
|
141192
141331
|
async getMetrics() {
|
|
141193
|
-
|
|
141332
|
+
const db = getDb();
|
|
141333
|
+
let activeSessions = 0;
|
|
141334
|
+
if (db) {
|
|
141335
|
+
try {
|
|
141336
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141337
|
+
const rows = db.query("SELECT COUNT(*) as count FROM session WHERE time_archived IS NULL AND time_updated > ?").all(cutoff);
|
|
141338
|
+
if (rows.length > 0)
|
|
141339
|
+
activeSessions = rows[0].count;
|
|
141340
|
+
} catch {} finally {
|
|
141341
|
+
db.close();
|
|
141342
|
+
}
|
|
141343
|
+
}
|
|
141344
|
+
const totalMem = os12.totalmem();
|
|
141345
|
+
const mem = process.memoryUsage();
|
|
141346
|
+
const memPercent = Math.round(mem.rss / totalMem * 100);
|
|
141347
|
+
const cpuPercent = Math.round(os12.loadavg()[0] * 100);
|
|
141348
|
+
const snapshots = [];
|
|
141349
|
+
for (let i3 = 0;i3 < 60; i3++) {
|
|
141350
|
+
snapshots.push({
|
|
141351
|
+
timestamp: Date.now() - (59 - i3) * 60000,
|
|
141352
|
+
cpuPercent,
|
|
141353
|
+
memoryPercent: memPercent,
|
|
141354
|
+
diskPercent: 0,
|
|
141355
|
+
sessionsActive: activeSessions
|
|
141356
|
+
});
|
|
141357
|
+
}
|
|
141358
|
+
return snapshots;
|
|
141194
141359
|
},
|
|
141195
141360
|
getLogs() {
|
|
141196
141361
|
const logFiles = [
|
|
@@ -141537,7 +141702,7 @@ function createDashboardServer(dataProvider, config4) {
|
|
|
141537
141702
|
// packages/omo-opencode/src/cli/dashboard.ts
|
|
141538
141703
|
async function dashboardCli(options) {
|
|
141539
141704
|
const port = options.port ?? 9123;
|
|
141540
|
-
const host = options.host ?? "
|
|
141705
|
+
const host = options.host ?? "0.0.0.0";
|
|
141541
141706
|
const frontendDir = path25.resolve(import.meta.dir, "../features/dashboard/frontend");
|
|
141542
141707
|
console.log(`[dashboard] starting MaTrixOS dashboard on http://${host}:${port}`);
|
|
141543
141708
|
console.log(`[dashboard] frontend: ${frontendDir}`);
|
|
@@ -141859,7 +142024,7 @@ function configureRuntimeCommands(program2) {
|
|
|
141859
142024
|
const exitCode = await architectRollback({ ...options, id });
|
|
141860
142025
|
process.exit(exitCode);
|
|
141861
142026
|
});
|
|
141862
|
-
program2.command("dashboard").description("Start the MaTrixOS web dashboard (port 9123)").option("-p, --port <number>", "HTTP port", "9123").option("--host <addr>", "Bind address", "
|
|
142027
|
+
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
142028
|
const exitCode = await dashboardCli({
|
|
141864
142029
|
port: parseInt(options.port ?? "9123", 10),
|
|
141865
142030
|
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.18",
|
|
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,55 @@ 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
|
-
|
|
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
|
+
}
|
|
141105
141117
|
}
|
|
141106
|
-
function
|
|
141107
|
-
const
|
|
141108
|
-
|
|
141109
|
-
|
|
141110
|
-
agent: randomChoice(AGENT_NAMES),
|
|
141111
|
-
model: randomChoice(models2),
|
|
141112
|
-
duration: randomBetween(5, 600),
|
|
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
|
-
}));
|
|
141118
|
+
function midnightMs() {
|
|
141119
|
+
const d = new Date;
|
|
141120
|
+
d.setHours(0, 0, 0, 0);
|
|
141121
|
+
return d.getTime();
|
|
141117
141122
|
}
|
|
141118
|
-
function
|
|
141119
|
-
|
|
141120
|
-
agent: name2,
|
|
141121
|
-
cost: Math.round(randomBetween(50, 2000) * 100) / 100,
|
|
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
|
-
};
|
|
141123
|
+
function thirtyDaysAgoMs() {
|
|
141124
|
+
return Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
141143
141125
|
}
|
|
141144
|
-
function
|
|
141145
|
-
|
|
141146
|
-
|
|
141147
|
-
|
|
141148
|
-
|
|
141149
|
-
|
|
141150
|
-
|
|
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
|
-
}));
|
|
141126
|
+
function readPackageVersion2() {
|
|
141127
|
+
try {
|
|
141128
|
+
const pkgPath = new URL("../../../../package.json", import.meta.url).pathname;
|
|
141129
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
|
|
141130
|
+
return pkg.version ?? "0.0.0";
|
|
141131
|
+
} catch {
|
|
141132
|
+
return "0.0.0";
|
|
141170
141133
|
}
|
|
141171
|
-
return { columns };
|
|
141172
|
-
}
|
|
141173
|
-
function generateMockMetrics() {
|
|
141174
|
-
return Array.from({ length: 60 }, (_2, i3) => ({
|
|
141175
|
-
timestamp: Date.now() - (59 - i3) * 60000,
|
|
141176
|
-
cpuPercent: randomBetween(10, 95),
|
|
141177
|
-
memoryPercent: randomBetween(30, 85),
|
|
141178
|
-
diskPercent: randomBetween(40, 70),
|
|
141179
|
-
sessionsActive: randomBetween(1, 8)
|
|
141180
|
-
}));
|
|
141181
141134
|
}
|
|
141182
141135
|
function createDataProvider(config4) {
|
|
141183
141136
|
const matrixosHome = config4?.matrixosHome ?? ".matrixos";
|
|
141137
|
+
const version3 = readPackageVersion2();
|
|
141184
141138
|
function readJsonl(filePath) {
|
|
141185
141139
|
try {
|
|
141186
141140
|
const fullPath = path24.resolve(matrixosHome, "logs", filePath);
|
|
@@ -141196,13 +141150,33 @@ function createDataProvider(config4) {
|
|
|
141196
141150
|
async getHealth() {
|
|
141197
141151
|
const mem = process.memoryUsage();
|
|
141198
141152
|
const totalMem = os12.totalmem();
|
|
141153
|
+
const db = getDb();
|
|
141154
|
+
let agentsOnline = AGENT_NAMES.length;
|
|
141155
|
+
let activeSessions = 0;
|
|
141156
|
+
let tasksQueued = 0;
|
|
141157
|
+
if (db) {
|
|
141158
|
+
try {
|
|
141159
|
+
const agentRow = db.query("SELECT COUNT(DISTINCT agent) as count FROM session").get();
|
|
141160
|
+
if (agentRow)
|
|
141161
|
+
agentsOnline = agentRow.count;
|
|
141162
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141163
|
+
const sessionRows = db.query("SELECT COUNT(*) as count FROM session WHERE time_archived IS NULL AND time_updated > ?").all(cutoff);
|
|
141164
|
+
if (sessionRows.length > 0)
|
|
141165
|
+
activeSessions = sessionRows[0].count;
|
|
141166
|
+
const todoRows = db.query("SELECT COUNT(*) as count FROM todo WHERE status = 'pending'").all();
|
|
141167
|
+
if (todoRows.length > 0)
|
|
141168
|
+
tasksQueued = todoRows[0].count;
|
|
141169
|
+
} catch {} finally {
|
|
141170
|
+
db.close();
|
|
141171
|
+
}
|
|
141172
|
+
}
|
|
141199
141173
|
return {
|
|
141200
|
-
agentsOnline
|
|
141174
|
+
agentsOnline,
|
|
141201
141175
|
totalAgents: AGENT_NAMES.length,
|
|
141202
|
-
activeSessions
|
|
141203
|
-
tasksQueued
|
|
141176
|
+
activeSessions,
|
|
141177
|
+
tasksQueued,
|
|
141204
141178
|
uptime: process.uptime(),
|
|
141205
|
-
version:
|
|
141179
|
+
version: version3,
|
|
141206
141180
|
memoryUsage: {
|
|
141207
141181
|
used: Math.round(mem.rss / 1024 / 1024),
|
|
141208
141182
|
total: Math.round(totalMem / 1024 / 1024),
|
|
@@ -141212,16 +141186,181 @@ function createDataProvider(config4) {
|
|
|
141212
141186
|
};
|
|
141213
141187
|
},
|
|
141214
141188
|
async getAgents() {
|
|
141215
|
-
|
|
141189
|
+
const db = getDb();
|
|
141190
|
+
if (db) {
|
|
141191
|
+
try {
|
|
141192
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141193
|
+
const rows = db.query(`SELECT agent, COUNT(*) as sessions
|
|
141194
|
+
FROM session
|
|
141195
|
+
WHERE agent IS NOT NULL AND agent != ''
|
|
141196
|
+
GROUP BY agent`).all();
|
|
141197
|
+
if (rows.length > 0) {
|
|
141198
|
+
const result = [];
|
|
141199
|
+
for (const row of rows) {
|
|
141200
|
+
const name2 = row.agent;
|
|
141201
|
+
const onlineRows = db.query("SELECT COUNT(*) as count FROM session WHERE agent = ? AND time_updated > ?").all(name2, cutoff);
|
|
141202
|
+
const status2 = onlineRows.length > 0 && onlineRows[0].count > 0 ? "online" : "idle";
|
|
141203
|
+
result.push({
|
|
141204
|
+
name: name2,
|
|
141205
|
+
role: AGENT_ROLES[name2] ?? "Agent",
|
|
141206
|
+
status: status2,
|
|
141207
|
+
sessions: row.sessions,
|
|
141208
|
+
uptime: 0,
|
|
141209
|
+
cpuPercent: 0,
|
|
141210
|
+
memoryPercent: 0
|
|
141211
|
+
});
|
|
141212
|
+
}
|
|
141213
|
+
db.close();
|
|
141214
|
+
return result;
|
|
141215
|
+
}
|
|
141216
|
+
} catch {} finally {
|
|
141217
|
+
db.close();
|
|
141218
|
+
}
|
|
141219
|
+
}
|
|
141220
|
+
return AGENT_NAMES.map((name2) => ({
|
|
141221
|
+
name: name2,
|
|
141222
|
+
role: AGENT_ROLES[name2] ?? "Agent",
|
|
141223
|
+
status: "idle",
|
|
141224
|
+
sessions: 0,
|
|
141225
|
+
uptime: 0,
|
|
141226
|
+
cpuPercent: 0,
|
|
141227
|
+
memoryPercent: 0
|
|
141228
|
+
}));
|
|
141216
141229
|
},
|
|
141217
141230
|
async getSessions(limit = 20) {
|
|
141218
|
-
|
|
141231
|
+
const db = getDb();
|
|
141232
|
+
if (!db)
|
|
141233
|
+
return [];
|
|
141234
|
+
try {
|
|
141235
|
+
const now = Date.now();
|
|
141236
|
+
const rows = db.query(`SELECT id, title, agent, model, cost, time_created, time_updated, time_archived
|
|
141237
|
+
FROM session
|
|
141238
|
+
ORDER BY time_updated DESC
|
|
141239
|
+
LIMIT ?`).all(limit);
|
|
141240
|
+
const result = [];
|
|
141241
|
+
for (const row of rows) {
|
|
141242
|
+
const duration3 = Math.max(0, Math.round((row.time_updated - row.time_created) / 1000));
|
|
141243
|
+
let status2 = "completed";
|
|
141244
|
+
if (row.time_archived !== null) {
|
|
141245
|
+
status2 = "completed";
|
|
141246
|
+
} else if (row.time_updated > now - FIVE_MIN_MS) {
|
|
141247
|
+
status2 = "active";
|
|
141248
|
+
} else {
|
|
141249
|
+
status2 = "completed";
|
|
141250
|
+
}
|
|
141251
|
+
result.push({
|
|
141252
|
+
id: row.id,
|
|
141253
|
+
agent: row.agent ?? "unknown",
|
|
141254
|
+
model: parseModel(row.model),
|
|
141255
|
+
duration: duration3,
|
|
141256
|
+
cost: row.cost,
|
|
141257
|
+
status: status2,
|
|
141258
|
+
startedAt: new Date(row.time_created).toISOString()
|
|
141259
|
+
});
|
|
141260
|
+
}
|
|
141261
|
+
return result;
|
|
141262
|
+
} catch {
|
|
141263
|
+
return [];
|
|
141264
|
+
} finally {
|
|
141265
|
+
db.close();
|
|
141266
|
+
}
|
|
141219
141267
|
},
|
|
141220
141268
|
async getCost() {
|
|
141221
|
-
|
|
141269
|
+
const db = getDb();
|
|
141270
|
+
if (!db) {
|
|
141271
|
+
return {
|
|
141272
|
+
total: 0,
|
|
141273
|
+
daily: 0,
|
|
141274
|
+
monthly: 0,
|
|
141275
|
+
budgetRemaining: BUDGET_TOTAL,
|
|
141276
|
+
byAgent: [],
|
|
141277
|
+
byDay: [],
|
|
141278
|
+
alerts: []
|
|
141279
|
+
};
|
|
141280
|
+
}
|
|
141281
|
+
try {
|
|
141282
|
+
const totalRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session").all();
|
|
141283
|
+
const total = totalRows.length > 0 ? totalRows[0].sum ?? 0 : 0;
|
|
141284
|
+
const dailyRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session WHERE time_created > ?").all(midnightMs());
|
|
141285
|
+
const daily = dailyRows.length > 0 ? dailyRows[0].sum ?? 0 : 0;
|
|
141286
|
+
const monthlyRows = db.query("SELECT COALESCE(SUM(cost), 0) as sum FROM session WHERE time_created > ?").all(thirtyDaysAgoMs());
|
|
141287
|
+
const monthly = monthlyRows.length > 0 ? monthlyRows[0].sum ?? 0 : 0;
|
|
141288
|
+
const agentRows = db.query(`SELECT agent, COALESCE(SUM(cost), 0) as cost
|
|
141289
|
+
FROM session
|
|
141290
|
+
WHERE agent IS NOT NULL AND agent != ''
|
|
141291
|
+
GROUP BY agent`).all();
|
|
141292
|
+
const byAgent = agentRows.map((r2) => ({
|
|
141293
|
+
agent: r2.agent,
|
|
141294
|
+
cost: r2.cost,
|
|
141295
|
+
percentage: total > 0 ? Math.round(r2.cost / total * 100) : 0
|
|
141296
|
+
}));
|
|
141297
|
+
const byDayRows = db.query(`SELECT strftime('%Y-%m-%d', time_created / 1000, 'unixepoch') as day,
|
|
141298
|
+
COALESCE(SUM(cost), 0) as cost
|
|
141299
|
+
FROM session
|
|
141300
|
+
WHERE time_created > ?
|
|
141301
|
+
GROUP BY day
|
|
141302
|
+
ORDER BY day ASC`).all(thirtyDaysAgoMs());
|
|
141303
|
+
const byDayMap = new Map;
|
|
141304
|
+
for (const r2 of byDayRows) {
|
|
141305
|
+
byDayMap.set(r2.day, r2.cost);
|
|
141306
|
+
}
|
|
141307
|
+
const byDay = [];
|
|
141308
|
+
const today = new Date;
|
|
141309
|
+
for (let i3 = 29;i3 >= 0; i3--) {
|
|
141310
|
+
const d = new Date(today);
|
|
141311
|
+
d.setDate(d.getDate() - i3);
|
|
141312
|
+
const key = d.toISOString().slice(0, 10);
|
|
141313
|
+
byDay.push({ date: key, cost: byDayMap.get(key) ?? 0 });
|
|
141314
|
+
}
|
|
141315
|
+
const budgetRemaining = Math.max(0, BUDGET_TOTAL - total);
|
|
141316
|
+
const alerts = [];
|
|
141317
|
+
if (total > BUDGET_TOTAL) {
|
|
141318
|
+
alerts.push({ severity: "critical", message: `Total cost $${total.toFixed(2)} exceeds budget $${BUDGET_TOTAL}` });
|
|
141319
|
+
} else if (total > BUDGET_TOTAL * 0.85) {
|
|
141320
|
+
alerts.push({ severity: "warning", message: `Cost $${total.toFixed(2)} approaching budget threshold (85%)` });
|
|
141321
|
+
}
|
|
141322
|
+
return { total, daily, monthly, budgetRemaining, byAgent, byDay, alerts };
|
|
141323
|
+
} catch {
|
|
141324
|
+
return {
|
|
141325
|
+
total: 0,
|
|
141326
|
+
daily: 0,
|
|
141327
|
+
monthly: 0,
|
|
141328
|
+
budgetRemaining: BUDGET_TOTAL,
|
|
141329
|
+
byAgent: [],
|
|
141330
|
+
byDay: [],
|
|
141331
|
+
alerts: []
|
|
141332
|
+
};
|
|
141333
|
+
} finally {
|
|
141334
|
+
db.close();
|
|
141335
|
+
}
|
|
141222
141336
|
},
|
|
141223
141337
|
async getKanbanTasks() {
|
|
141224
|
-
|
|
141338
|
+
const board = { columns: { pending: [], in_progress: [], completed: [], failed: [] } };
|
|
141339
|
+
const db = getDb();
|
|
141340
|
+
if (!db)
|
|
141341
|
+
return board;
|
|
141342
|
+
try {
|
|
141343
|
+
const rows = db.query("SELECT content, status, priority, time_created, time_updated FROM todo ORDER BY time_updated DESC").all();
|
|
141344
|
+
let idx = 0;
|
|
141345
|
+
for (const row of rows) {
|
|
141346
|
+
const status2 = row.status;
|
|
141347
|
+
if (!board.columns[status2])
|
|
141348
|
+
continue;
|
|
141349
|
+
const task2 = {
|
|
141350
|
+
id: `todo_${idx++}`,
|
|
141351
|
+
title: row.content,
|
|
141352
|
+
agent: "Unknown",
|
|
141353
|
+
priority: row.priority ?? "medium",
|
|
141354
|
+
status: status2,
|
|
141355
|
+
createdAt: new Date(row.time_created).toISOString(),
|
|
141356
|
+
updatedAt: new Date(row.time_updated).toISOString()
|
|
141357
|
+
};
|
|
141358
|
+
board.columns[status2].push(task2);
|
|
141359
|
+
}
|
|
141360
|
+
} catch {} finally {
|
|
141361
|
+
db.close();
|
|
141362
|
+
}
|
|
141363
|
+
return board;
|
|
141225
141364
|
},
|
|
141226
141365
|
async getIncidents(limit = 50) {
|
|
141227
141366
|
const lines = readJsonl("anti-loop.jsonl");
|
|
@@ -141245,7 +141384,33 @@ function createDataProvider(config4) {
|
|
|
141245
141384
|
}).filter(Boolean);
|
|
141246
141385
|
},
|
|
141247
141386
|
async getMetrics() {
|
|
141248
|
-
|
|
141387
|
+
const db = getDb();
|
|
141388
|
+
let activeSessions = 0;
|
|
141389
|
+
if (db) {
|
|
141390
|
+
try {
|
|
141391
|
+
const cutoff = Date.now() - THIRTY_MIN_MS;
|
|
141392
|
+
const rows = db.query("SELECT COUNT(*) as count FROM session WHERE time_archived IS NULL AND time_updated > ?").all(cutoff);
|
|
141393
|
+
if (rows.length > 0)
|
|
141394
|
+
activeSessions = rows[0].count;
|
|
141395
|
+
} catch {} finally {
|
|
141396
|
+
db.close();
|
|
141397
|
+
}
|
|
141398
|
+
}
|
|
141399
|
+
const totalMem = os12.totalmem();
|
|
141400
|
+
const mem = process.memoryUsage();
|
|
141401
|
+
const memPercent = Math.round(mem.rss / totalMem * 100);
|
|
141402
|
+
const cpuPercent = Math.round(os12.loadavg()[0] * 100);
|
|
141403
|
+
const snapshots = [];
|
|
141404
|
+
for (let i3 = 0;i3 < 60; i3++) {
|
|
141405
|
+
snapshots.push({
|
|
141406
|
+
timestamp: Date.now() - (59 - i3) * 60000,
|
|
141407
|
+
cpuPercent,
|
|
141408
|
+
memoryPercent: memPercent,
|
|
141409
|
+
diskPercent: 0,
|
|
141410
|
+
sessionsActive: activeSessions
|
|
141411
|
+
});
|
|
141412
|
+
}
|
|
141413
|
+
return snapshots;
|
|
141249
141414
|
},
|
|
141250
141415
|
getLogs() {
|
|
141251
141416
|
const logFiles = [
|
|
@@ -141592,7 +141757,7 @@ function createDashboardServer(dataProvider, config4) {
|
|
|
141592
141757
|
// packages/omo-opencode/src/cli/dashboard.ts
|
|
141593
141758
|
async function dashboardCli(options) {
|
|
141594
141759
|
const port = options.port ?? 9123;
|
|
141595
|
-
const host = options.host ?? "
|
|
141760
|
+
const host = options.host ?? "0.0.0.0";
|
|
141596
141761
|
const frontendDir = path25.resolve(import.meta.dir, "../features/dashboard/frontend");
|
|
141597
141762
|
console.log(`[dashboard] starting MaTrixOS dashboard on http://${host}:${port}`);
|
|
141598
141763
|
console.log(`[dashboard] frontend: ${frontendDir}`);
|
|
@@ -141914,7 +142079,7 @@ function configureRuntimeCommands(program2) {
|
|
|
141914
142079
|
const exitCode = await architectRollback({ ...options, id });
|
|
141915
142080
|
process.exit(exitCode);
|
|
141916
142081
|
});
|
|
141917
|
-
program2.command("dashboard").description("Start the MaTrixOS web dashboard (port 9123)").option("-p, --port <number>", "HTTP port", "9123").option("--host <addr>", "Bind address", "
|
|
142082
|
+
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
142083
|
const exitCode = await dashboardCli({
|
|
141919
142084
|
port: parseInt(options.port ?? "9123", 10),
|
|
141920
142085
|
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.18",
|
|
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