@openlife/cli 1.7.6 → 1.7.9
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/LogsCommand.js +181 -0
- package/dist/cli/StatusCommand.js +217 -0
- package/dist/index.js +230 -42
- package/dist/orchestrator/AgentCreator.js +362 -0
- package/dist/test_agent_creator.js +237 -0
- package/dist/test_install_wizard.js +28 -2
- package/dist/test_logs_command.js +177 -0
- package/dist/test_status_command.js +218 -0
- package/package.json +5 -2
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseDurationToSeconds = parseDurationToSeconds;
|
|
37
|
+
exports.collectLogs = collectLogs;
|
|
38
|
+
exports.renderLogsHuman = renderLogsHuman;
|
|
39
|
+
exports.renderLogsJson = renderLogsJson;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const DEFAULT_TAIL = 20;
|
|
43
|
+
const KNOWN_JSONL_FILES = [
|
|
44
|
+
'governance-ledger.jsonl',
|
|
45
|
+
'media-routing.log.jsonl',
|
|
46
|
+
'capability-lifecycle.jsonl',
|
|
47
|
+
'canonization-log.jsonl',
|
|
48
|
+
];
|
|
49
|
+
function parseDurationToSeconds(input) {
|
|
50
|
+
const trimmed = input.trim().toLowerCase();
|
|
51
|
+
const m = trimmed.match(/^(\d+)\s*(s|m|h|d)$/);
|
|
52
|
+
if (!m)
|
|
53
|
+
return null;
|
|
54
|
+
const n = Number.parseInt(m[1], 10);
|
|
55
|
+
if (!Number.isFinite(n) || n < 0)
|
|
56
|
+
return null;
|
|
57
|
+
const unit = m[2];
|
|
58
|
+
const mult = unit === 's' ? 1 : unit === 'm' ? 60 : unit === 'h' ? 3600 : 86400;
|
|
59
|
+
return n * mult;
|
|
60
|
+
}
|
|
61
|
+
function entrySubsystem(filePath) {
|
|
62
|
+
const base = path.basename(filePath);
|
|
63
|
+
return base.replace(/\.log\.jsonl$/, '').replace(/\.jsonl$/, '');
|
|
64
|
+
}
|
|
65
|
+
function extractTs(parsed, fallbackMtime) {
|
|
66
|
+
if (!parsed)
|
|
67
|
+
return fallbackMtime;
|
|
68
|
+
if (typeof parsed.ts === 'string') {
|
|
69
|
+
const t = Date.parse(parsed.ts);
|
|
70
|
+
if (!Number.isNaN(t))
|
|
71
|
+
return t;
|
|
72
|
+
}
|
|
73
|
+
if (typeof parsed.ts === 'number' && Number.isFinite(parsed.ts)) {
|
|
74
|
+
return parsed.ts > 1e12 ? parsed.ts : parsed.ts * 1000;
|
|
75
|
+
}
|
|
76
|
+
if (typeof parsed.timestamp === 'string') {
|
|
77
|
+
const t = Date.parse(parsed.timestamp);
|
|
78
|
+
if (!Number.isNaN(t))
|
|
79
|
+
return t;
|
|
80
|
+
}
|
|
81
|
+
return fallbackMtime;
|
|
82
|
+
}
|
|
83
|
+
function summarizeEntry(parsed) {
|
|
84
|
+
if (!parsed)
|
|
85
|
+
return '<unparseable line>';
|
|
86
|
+
const candidates = [
|
|
87
|
+
parsed.summary,
|
|
88
|
+
parsed.decision?.auditSummary,
|
|
89
|
+
parsed.message,
|
|
90
|
+
parsed.action,
|
|
91
|
+
parsed.event,
|
|
92
|
+
parsed.type,
|
|
93
|
+
];
|
|
94
|
+
for (const c of candidates) {
|
|
95
|
+
if (typeof c === 'string' && c.trim().length > 0) {
|
|
96
|
+
return c.length > 140 ? `${c.slice(0, 137)}...` : c;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Fall back to a one-line preview of the parsed object.
|
|
100
|
+
const compact = JSON.stringify(parsed);
|
|
101
|
+
return compact.length > 140 ? `${compact.slice(0, 137)}...` : compact;
|
|
102
|
+
}
|
|
103
|
+
function listJsonlFiles(stateDir) {
|
|
104
|
+
if (!fs.existsSync(stateDir))
|
|
105
|
+
return [];
|
|
106
|
+
try {
|
|
107
|
+
const entries = fs.readdirSync(stateDir);
|
|
108
|
+
const found = [];
|
|
109
|
+
for (const e of entries) {
|
|
110
|
+
if (KNOWN_JSONL_FILES.includes(e) || e.endsWith('.jsonl')) {
|
|
111
|
+
found.push(path.join(stateDir, e));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return found;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function collectLogs(opts = {}) {
|
|
121
|
+
const root = opts.root || process.cwd();
|
|
122
|
+
const stateDir = process.env.OPENLIFE_STATE_DIR
|
|
123
|
+
? path.resolve(process.env.OPENLIFE_STATE_DIR)
|
|
124
|
+
: path.join(root, '.openlife');
|
|
125
|
+
const files = listJsonlFiles(stateDir);
|
|
126
|
+
const filter = opts.filter ? opts.filter.toLowerCase() : null;
|
|
127
|
+
const tail = opts.tail && opts.tail > 0 ? opts.tail : DEFAULT_TAIL;
|
|
128
|
+
const sinceSeconds = opts.since ? parseDurationToSeconds(opts.since) : null;
|
|
129
|
+
const minTs = sinceSeconds !== null ? Date.now() - sinceSeconds * 1000 : null;
|
|
130
|
+
const out = [];
|
|
131
|
+
for (const fp of files) {
|
|
132
|
+
const subsystem = entrySubsystem(fp);
|
|
133
|
+
if (filter && !subsystem.toLowerCase().includes(filter))
|
|
134
|
+
continue;
|
|
135
|
+
let mtime = Date.now();
|
|
136
|
+
try {
|
|
137
|
+
mtime = fs.statSync(fp).mtimeMs;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
let raw = '';
|
|
143
|
+
try {
|
|
144
|
+
raw = fs.readFileSync(fp, 'utf-8');
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const lines = raw.split('\n').filter((l) => l.trim().length > 0);
|
|
150
|
+
for (const line of lines) {
|
|
151
|
+
let parsed = null;
|
|
152
|
+
try {
|
|
153
|
+
parsed = JSON.parse(line);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
parsed = null;
|
|
157
|
+
}
|
|
158
|
+
const ts = extractTs(parsed, mtime);
|
|
159
|
+
if (minTs !== null && ts < minTs)
|
|
160
|
+
continue;
|
|
161
|
+
out.push({ subsystem, rawLine: line, parsed, ts });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
out.sort((a, b) => a.ts - b.ts);
|
|
165
|
+
return out.slice(-tail);
|
|
166
|
+
}
|
|
167
|
+
function renderLogsHuman(entries) {
|
|
168
|
+
if (entries.length === 0)
|
|
169
|
+
return '(no entries match filter)';
|
|
170
|
+
return entries
|
|
171
|
+
.map((e) => {
|
|
172
|
+
const tsIso = new Date(e.ts).toISOString();
|
|
173
|
+
return `${tsIso} [${e.subsystem}] ${summarizeEntry(e.parsed)}`;
|
|
174
|
+
})
|
|
175
|
+
.join('\n');
|
|
176
|
+
}
|
|
177
|
+
function renderLogsJson(entries) {
|
|
178
|
+
return entries
|
|
179
|
+
.map((e) => JSON.stringify({ ts: new Date(e.ts).toISOString(), subsystem: e.subsystem, entry: e.parsed ?? e.rawLine }))
|
|
180
|
+
.join('\n');
|
|
181
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.HEARTBEAT_STALE_S = exports.HEARTBEAT_FRESH_S = void 0;
|
|
37
|
+
exports.buildStatusReport = buildStatusReport;
|
|
38
|
+
exports.renderStatusReport = renderStatusReport;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// `openlife status` — consolidated runtime state report.
|
|
43
|
+
//
|
|
44
|
+
// Aggregates the 6 most operationally useful files under `.openlife/`:
|
|
45
|
+
// - install-manifest.json → installed profile + providers detected
|
|
46
|
+
// - heartbeat.json → daemon uptime + last beat timestamp
|
|
47
|
+
// - runtime-health.json → per-executor failure budget + cooldown
|
|
48
|
+
// - runtime-policy-status.json → per-executor availability decision
|
|
49
|
+
// - agent-queue.json → queued objectives + last flush
|
|
50
|
+
// - governance-ledger.jsonl → tail entry hash + chain length
|
|
51
|
+
//
|
|
52
|
+
// Computes an `overall` rollup:
|
|
53
|
+
// - "healthy" → daemon beat within HEARTBEAT_FRESH_S AND at least one
|
|
54
|
+
// executor available AND governance ledger parseable
|
|
55
|
+
// - "degraded" → heartbeat stale (FRESH..STALE) OR some executors down
|
|
56
|
+
// - "down" → heartbeat older than STALE OR all executors down
|
|
57
|
+
//
|
|
58
|
+
// Returns JSON. The CLI wrapper handles --watch (re-print every 5s).
|
|
59
|
+
// ============================================================================
|
|
60
|
+
exports.HEARTBEAT_FRESH_S = 60;
|
|
61
|
+
exports.HEARTBEAT_STALE_S = 300;
|
|
62
|
+
function readJsonSafe(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
if (!fs.existsSync(filePath))
|
|
65
|
+
return null;
|
|
66
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function readJsonlTailSafe(filePath, maxLines = 1) {
|
|
73
|
+
try {
|
|
74
|
+
if (!fs.existsSync(filePath))
|
|
75
|
+
return { count: 0 };
|
|
76
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
77
|
+
const lines = raw.split('\n').filter((l) => l.trim().length > 0);
|
|
78
|
+
if (lines.length === 0)
|
|
79
|
+
return { count: 0 };
|
|
80
|
+
const tail = lines.slice(-Math.max(1, maxLines));
|
|
81
|
+
const lastRaw = tail[tail.length - 1];
|
|
82
|
+
try {
|
|
83
|
+
return { count: lines.length, last: JSON.parse(lastRaw) };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { count: lines.length };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return { count: 0 };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function readPackageVersion(packageRoot) {
|
|
94
|
+
const candidate = path.join(packageRoot, 'package.json');
|
|
95
|
+
const pkg = readJsonSafe(candidate);
|
|
96
|
+
return pkg?.version || '0.0.0';
|
|
97
|
+
}
|
|
98
|
+
function normalizeExecutors(policyStatus, runtimeHealth) {
|
|
99
|
+
const result = {};
|
|
100
|
+
const policy = (policyStatus?.executors ?? {});
|
|
101
|
+
for (const [name, raw] of Object.entries(policy)) {
|
|
102
|
+
const r = raw;
|
|
103
|
+
result[name] = {
|
|
104
|
+
executor: name,
|
|
105
|
+
available: Boolean(r.available),
|
|
106
|
+
reason: typeof r.reason === 'string' ? r.reason : undefined,
|
|
107
|
+
category: typeof r.category === 'string' ? r.category : undefined,
|
|
108
|
+
updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (runtimeHealth) {
|
|
112
|
+
for (const [name, raw] of Object.entries(runtimeHealth)) {
|
|
113
|
+
if (name === 'type' || name === 'status' || name === 'updatedAt')
|
|
114
|
+
continue;
|
|
115
|
+
const r = raw;
|
|
116
|
+
const current = result[name] || { executor: name, available: true };
|
|
117
|
+
if (typeof r.failures === 'number')
|
|
118
|
+
current.failures = r.failures;
|
|
119
|
+
if (typeof r.until === 'string')
|
|
120
|
+
current.cooldownUntil = r.until;
|
|
121
|
+
if (!current.reason && typeof r.reason === 'string')
|
|
122
|
+
current.reason = r.reason;
|
|
123
|
+
result[name] = current;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return Object.values(result);
|
|
127
|
+
}
|
|
128
|
+
function deriveOverall(report) {
|
|
129
|
+
const notes = [];
|
|
130
|
+
const age = report.heartbeat.ageSeconds;
|
|
131
|
+
let heartbeatTier = 'fresh';
|
|
132
|
+
if (!report.heartbeat.present || age === null) {
|
|
133
|
+
heartbeatTier = 'dead';
|
|
134
|
+
notes.push('no_heartbeat');
|
|
135
|
+
}
|
|
136
|
+
else if (age > exports.HEARTBEAT_STALE_S) {
|
|
137
|
+
heartbeatTier = 'dead';
|
|
138
|
+
notes.push(`heartbeat_stale_${age}s`);
|
|
139
|
+
}
|
|
140
|
+
else if (age > exports.HEARTBEAT_FRESH_S) {
|
|
141
|
+
heartbeatTier = 'stale';
|
|
142
|
+
notes.push(`heartbeat_warning_${age}s`);
|
|
143
|
+
}
|
|
144
|
+
const executors = report.executors;
|
|
145
|
+
const anyExecutor = executors.length > 0;
|
|
146
|
+
const anyAvailable = executors.some((e) => e.available);
|
|
147
|
+
if (anyExecutor && !anyAvailable) {
|
|
148
|
+
notes.push('all_executors_down');
|
|
149
|
+
}
|
|
150
|
+
if (anyExecutor && !executors.every((e) => e.available)) {
|
|
151
|
+
const down = executors.filter((e) => !e.available).map((e) => e.executor);
|
|
152
|
+
notes.push(`executors_down:${down.join(',')}`);
|
|
153
|
+
}
|
|
154
|
+
if (!report.governance.ledgerPresent) {
|
|
155
|
+
notes.push('no_governance_ledger');
|
|
156
|
+
}
|
|
157
|
+
let overall;
|
|
158
|
+
if (heartbeatTier === 'dead' || (anyExecutor && !anyAvailable)) {
|
|
159
|
+
overall = 'down';
|
|
160
|
+
}
|
|
161
|
+
else if (heartbeatTier === 'stale' || (anyExecutor && !executors.every((e) => e.available))) {
|
|
162
|
+
overall = 'degraded';
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
overall = 'healthy';
|
|
166
|
+
}
|
|
167
|
+
return { overall, notes };
|
|
168
|
+
}
|
|
169
|
+
function buildStatusReport(root = process.cwd(), packageRoot) {
|
|
170
|
+
const stateDir = process.env.OPENLIFE_STATE_DIR
|
|
171
|
+
? path.resolve(process.env.OPENLIFE_STATE_DIR)
|
|
172
|
+
: path.join(root, '.openlife');
|
|
173
|
+
const heartbeat = readJsonSafe(path.join(stateDir, 'heartbeat.json'));
|
|
174
|
+
const runtimeHealth = readJsonSafe(path.join(stateDir, 'runtime-health.json'));
|
|
175
|
+
const policyStatus = readJsonSafe(path.join(stateDir, 'runtime-policy-status.json'));
|
|
176
|
+
const queue = readJsonSafe(path.join(stateDir, 'agent-queue.json'));
|
|
177
|
+
const manifest = readJsonSafe(path.join(stateDir, 'install-manifest.json'));
|
|
178
|
+
const ledgerTail = readJsonlTailSafe(path.join(stateDir, 'governance-ledger.jsonl'), 1);
|
|
179
|
+
const lastLedger = ledgerTail.last;
|
|
180
|
+
const ageSeconds = heartbeat?.ts ? Math.floor((Date.now() - heartbeat.ts) / 1000) : null;
|
|
181
|
+
const uptimeSeconds = heartbeat?.startedAt ? Math.floor((Date.now() - heartbeat.startedAt) / 1000) : null;
|
|
182
|
+
const partial = {
|
|
183
|
+
ts: new Date().toISOString(),
|
|
184
|
+
version: readPackageVersion(packageRoot || path.join(__dirname, '..', '..')),
|
|
185
|
+
profile: manifest?.profile ?? null,
|
|
186
|
+
uptimeSeconds,
|
|
187
|
+
heartbeat: {
|
|
188
|
+
present: Boolean(heartbeat),
|
|
189
|
+
pid: heartbeat?.pid,
|
|
190
|
+
host: heartbeat?.host,
|
|
191
|
+
ageSeconds,
|
|
192
|
+
fresh: ageSeconds !== null && ageSeconds <= exports.HEARTBEAT_FRESH_S,
|
|
193
|
+
},
|
|
194
|
+
governance: {
|
|
195
|
+
ledgerPresent: ledgerTail.count > 0,
|
|
196
|
+
entryCount: ledgerTail.count,
|
|
197
|
+
lastEntryHash: lastLedger?.entryHash,
|
|
198
|
+
lastEntryTs: lastLedger?.ts,
|
|
199
|
+
},
|
|
200
|
+
queue: {
|
|
201
|
+
depth: Array.isArray(queue?.queuedObjectives) ? queue.queuedObjectives.length : 0,
|
|
202
|
+
lastFlushedAt: queue?.lastFlushedAt,
|
|
203
|
+
},
|
|
204
|
+
executors: normalizeExecutors(policyStatus, runtimeHealth),
|
|
205
|
+
install: {
|
|
206
|
+
installedAt: manifest?.installedAt,
|
|
207
|
+
providers: manifest?.providers,
|
|
208
|
+
clis: manifest?.clis,
|
|
209
|
+
hasTelegramToken: manifest?.envChecks?.hasTelegramToken,
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
const { overall, notes } = deriveOverall(partial);
|
|
213
|
+
return { ...partial, overall, notes };
|
|
214
|
+
}
|
|
215
|
+
function renderStatusReport(report) {
|
|
216
|
+
return JSON.stringify(report, null, 2);
|
|
217
|
+
}
|