assistme 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -61,6 +61,7 @@ import chalk from "chalk";
|
|
|
61
61
|
import { randomUUID } from "crypto";
|
|
62
62
|
var currentLevel = "info";
|
|
63
63
|
var currentCorrelationId = null;
|
|
64
|
+
var logHook = null;
|
|
64
65
|
var LEVEL_ORDER = {
|
|
65
66
|
debug: 0,
|
|
66
67
|
info: 1,
|
|
@@ -70,6 +71,9 @@ var LEVEL_ORDER = {
|
|
|
70
71
|
function setLogLevel(level) {
|
|
71
72
|
currentLevel = level;
|
|
72
73
|
}
|
|
74
|
+
function setLogHook(hook) {
|
|
75
|
+
logHook = hook;
|
|
76
|
+
}
|
|
73
77
|
function setCorrelationId(id) {
|
|
74
78
|
currentCorrelationId = id;
|
|
75
79
|
}
|
|
@@ -91,39 +95,56 @@ function prefix() {
|
|
|
91
95
|
var log = {
|
|
92
96
|
debug(msg, ...args) {
|
|
93
97
|
if (shouldLog("debug")) {
|
|
98
|
+
const text = formatArgs(msg, args);
|
|
99
|
+
logHook?.("stdout", `[DEBUG] ${text}`);
|
|
94
100
|
console.log(chalk.gray(`[${prefix()}] DEBUG`), msg, ...args);
|
|
95
101
|
}
|
|
96
102
|
},
|
|
97
103
|
info(msg, ...args) {
|
|
98
104
|
if (shouldLog("info")) {
|
|
105
|
+
const text = formatArgs(msg, args);
|
|
106
|
+
logHook?.("stdout", text);
|
|
99
107
|
console.log(chalk.blue(`[${prefix()}]`), msg, ...args);
|
|
100
108
|
}
|
|
101
109
|
},
|
|
102
110
|
success(msg, ...args) {
|
|
103
111
|
if (shouldLog("info")) {
|
|
112
|
+
const text = formatArgs(msg, args);
|
|
113
|
+
logHook?.("stdout", `\u2713 ${text}`);
|
|
104
114
|
console.log(chalk.green(`[${prefix()}] \u2713`), msg, ...args);
|
|
105
115
|
}
|
|
106
116
|
},
|
|
107
117
|
warn(msg, ...args) {
|
|
108
118
|
if (shouldLog("warn")) {
|
|
119
|
+
const text = formatArgs(msg, args);
|
|
120
|
+
logHook?.("stderr", `[WARN] ${text}`);
|
|
109
121
|
console.log(chalk.yellow(`[${prefix()}] WARN`), msg, ...args);
|
|
110
122
|
}
|
|
111
123
|
},
|
|
112
124
|
error(msg, ...args) {
|
|
113
125
|
if (shouldLog("error")) {
|
|
126
|
+
const text = formatArgs(msg, args);
|
|
127
|
+
logHook?.("stderr", `[ERROR] ${text}`);
|
|
114
128
|
console.error(chalk.red(`[${prefix()}] ERROR`), msg, ...args);
|
|
115
129
|
}
|
|
116
130
|
},
|
|
117
131
|
agent(msg) {
|
|
132
|
+
logHook?.("stdout", `\u25B8 ${msg}`);
|
|
118
133
|
console.log(chalk.cyan(" \u25B8"), msg);
|
|
119
134
|
},
|
|
120
135
|
tool(name, msg) {
|
|
136
|
+
logHook?.("stdout", `\u26A1 ${name}: ${msg}`);
|
|
121
137
|
console.log(chalk.magenta(` \u26A1 ${name}:`), msg);
|
|
122
138
|
},
|
|
123
139
|
result(msg) {
|
|
140
|
+
logHook?.("stdout", `\u2190 ${msg}`);
|
|
124
141
|
console.log(chalk.green(" \u2190"), msg);
|
|
125
142
|
}
|
|
126
143
|
};
|
|
144
|
+
function formatArgs(msg, args) {
|
|
145
|
+
if (args.length === 0) return msg;
|
|
146
|
+
return `${msg} ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}`;
|
|
147
|
+
}
|
|
127
148
|
|
|
128
149
|
// src/utils/schemas.ts
|
|
129
150
|
import { z } from "zod";
|
|
@@ -424,6 +445,7 @@ export {
|
|
|
424
445
|
writeAuthStore,
|
|
425
446
|
callMcpHandler,
|
|
426
447
|
setLogLevel,
|
|
448
|
+
setLogHook,
|
|
427
449
|
setCorrelationId,
|
|
428
450
|
newCorrelationId,
|
|
429
451
|
log,
|
package/dist/index.js
CHANGED
|
@@ -35,9 +35,10 @@ import {
|
|
|
35
35
|
readAuthStore,
|
|
36
36
|
safeParse,
|
|
37
37
|
setCorrelationId,
|
|
38
|
+
setLogHook,
|
|
38
39
|
setLogLevel,
|
|
39
40
|
writeAuthStore
|
|
40
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-KAS2PTOX.js";
|
|
41
42
|
import {
|
|
42
43
|
clearConfig,
|
|
43
44
|
getConfig,
|
|
@@ -6119,6 +6120,57 @@ var TaskProcessor = class {
|
|
|
6119
6120
|
}
|
|
6120
6121
|
};
|
|
6121
6122
|
|
|
6123
|
+
// src/db/session-log.ts
|
|
6124
|
+
var FLUSH_INTERVAL_MS = 3e3;
|
|
6125
|
+
var MAX_BATCH_SIZE = 100;
|
|
6126
|
+
var SessionLogEmitter = class {
|
|
6127
|
+
constructor(sessionId) {
|
|
6128
|
+
this.sessionId = sessionId;
|
|
6129
|
+
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
6130
|
+
}
|
|
6131
|
+
sequence = 0;
|
|
6132
|
+
buffer = [];
|
|
6133
|
+
flushTimer = null;
|
|
6134
|
+
flushing = false;
|
|
6135
|
+
/** Queue a log entry for batch insertion */
|
|
6136
|
+
push(logType, message) {
|
|
6137
|
+
this.sequence++;
|
|
6138
|
+
this.buffer.push({ log_type: logType, message, seq: this.sequence });
|
|
6139
|
+
if (this.buffer.length >= MAX_BATCH_SIZE) {
|
|
6140
|
+
this.flush();
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
/** Flush buffered logs to Supabase */
|
|
6144
|
+
async flush() {
|
|
6145
|
+
if (this.flushing || this.buffer.length === 0) return;
|
|
6146
|
+
const batch = this.buffer.splice(0);
|
|
6147
|
+
this.flushing = true;
|
|
6148
|
+
try {
|
|
6149
|
+
await callMcpHandler("log.emit_batch", {
|
|
6150
|
+
session_id: this.sessionId,
|
|
6151
|
+
logs: batch
|
|
6152
|
+
});
|
|
6153
|
+
} catch (err) {
|
|
6154
|
+
log.debug(
|
|
6155
|
+
`Failed to flush session logs: ${err instanceof Error ? err.message : err}`
|
|
6156
|
+
);
|
|
6157
|
+
if (this.buffer.length < MAX_BATCH_SIZE * 5) {
|
|
6158
|
+
this.buffer.unshift(...batch);
|
|
6159
|
+
}
|
|
6160
|
+
} finally {
|
|
6161
|
+
this.flushing = false;
|
|
6162
|
+
}
|
|
6163
|
+
}
|
|
6164
|
+
/** Stop the emitter and flush remaining logs */
|
|
6165
|
+
async stop() {
|
|
6166
|
+
if (this.flushTimer) {
|
|
6167
|
+
clearInterval(this.flushTimer);
|
|
6168
|
+
this.flushTimer = null;
|
|
6169
|
+
}
|
|
6170
|
+
await this.flush();
|
|
6171
|
+
}
|
|
6172
|
+
};
|
|
6173
|
+
|
|
6122
6174
|
// src/commands/start.ts
|
|
6123
6175
|
function registerStartCommand(program2) {
|
|
6124
6176
|
program2.command("start", { isDefault: true, hidden: true }).description("Start the agent (default command)").option("-w, --workspace <path>", "Workspace path (default: current directory)").option("-n, --name <name>", "Session name").option("-v, --verbose", "Enable verbose/debug logging").action(runAgent);
|
|
@@ -6182,10 +6234,16 @@ async function runAgent(opts) {
|
|
|
6182
6234
|
const processor = new TaskProcessor();
|
|
6183
6235
|
processor.init(userId);
|
|
6184
6236
|
const sessionManager = new SessionManager();
|
|
6237
|
+
let logEmitter = null;
|
|
6185
6238
|
const browserRef = getBrowser();
|
|
6186
6239
|
const shutdown = async () => {
|
|
6187
6240
|
console.log();
|
|
6188
6241
|
log.info("Shutting down...");
|
|
6242
|
+
setLogHook(null);
|
|
6243
|
+
try {
|
|
6244
|
+
if (logEmitter) await logEmitter.stop();
|
|
6245
|
+
} catch {
|
|
6246
|
+
}
|
|
6189
6247
|
try {
|
|
6190
6248
|
if (browserRef.isConnected()) await browserRef.disconnect();
|
|
6191
6249
|
} catch {
|
|
@@ -6200,6 +6258,10 @@ async function runAgent(opts) {
|
|
|
6200
6258
|
await processor.processTask(task);
|
|
6201
6259
|
});
|
|
6202
6260
|
processor.setSessionId(session.id);
|
|
6261
|
+
logEmitter = new SessionLogEmitter(session.id);
|
|
6262
|
+
setLogHook((logType, message) => {
|
|
6263
|
+
logEmitter?.push(logType, message);
|
|
6264
|
+
});
|
|
6203
6265
|
log.info("Listening for tasks (chat + jobs) from web UI...");
|
|
6204
6266
|
log.info("Press Ctrl+C to stop.\n");
|
|
6205
6267
|
const rl = createInterface2({
|
|
@@ -6605,7 +6667,7 @@ function registerJobCommands(program2) {
|
|
|
6605
6667
|
jobCmd.command("list").description("List your defined jobs").action(async () => {
|
|
6606
6668
|
try {
|
|
6607
6669
|
const userId = await getCurrentUserId();
|
|
6608
|
-
const { JobRunner: JobRunner2 } = await import("./job-runner-
|
|
6670
|
+
const { JobRunner: JobRunner2 } = await import("./job-runner-AT3V6LAQ.js");
|
|
6609
6671
|
const runner = new JobRunner2();
|
|
6610
6672
|
const jobs = await runner.listJobs();
|
|
6611
6673
|
if (jobs.length === 0) {
|
|
@@ -6629,7 +6691,7 @@ function registerJobCommands(program2) {
|
|
|
6629
6691
|
jobCmd.command("status [name]").description("Show run history for a job (or all jobs)").option("-l, --limit <number>", "Max runs to show (default: 5)").action(async (name, opts) => {
|
|
6630
6692
|
try {
|
|
6631
6693
|
const userId = await getCurrentUserId();
|
|
6632
|
-
const { JobRunner: JobRunner2 } = await import("./job-runner-
|
|
6694
|
+
const { JobRunner: JobRunner2 } = await import("./job-runner-AT3V6LAQ.js");
|
|
6633
6695
|
const runner = new JobRunner2();
|
|
6634
6696
|
const runs = await runner.getRunHistory(name, parseInt(opts.limit || "5"));
|
|
6635
6697
|
if (runs.length === 0) {
|
|
@@ -6668,7 +6730,7 @@ Job Run History${name ? ` \u2014 ${name}` : ""}:`));
|
|
|
6668
6730
|
process.exit(1);
|
|
6669
6731
|
}
|
|
6670
6732
|
const userId = await getCurrentUserId();
|
|
6671
|
-
const { JobRunner: JobRunner2 } = await import("./job-runner-
|
|
6733
|
+
const { JobRunner: JobRunner2 } = await import("./job-runner-AT3V6LAQ.js");
|
|
6672
6734
|
const runner = new JobRunner2();
|
|
6673
6735
|
const job = await runner.loadJob(name);
|
|
6674
6736
|
if (!job) {
|
package/package.json
CHANGED
package/src/commands/start.ts
CHANGED
|
@@ -4,10 +4,11 @@ import ora from "ora";
|
|
|
4
4
|
import { createInterface } from "readline";
|
|
5
5
|
import { getCurrentUserId } from "../db/supabase.js";
|
|
6
6
|
import { setConfig } from "../utils/config.js";
|
|
7
|
-
import { log, setLogLevel } from "../utils/logger.js";
|
|
7
|
+
import { log, setLogLevel, setLogHook } from "../utils/logger.js";
|
|
8
8
|
import { SessionManager } from "../agent/session.js";
|
|
9
9
|
import { TaskProcessor } from "../agent/processor.js";
|
|
10
10
|
import { getBrowser, ensureBrowserAvailable } from "../tools/browser.js";
|
|
11
|
+
import { SessionLogEmitter } from "../db/session-log.js";
|
|
11
12
|
|
|
12
13
|
export function registerStartCommand(program: Command): void {
|
|
13
14
|
program
|
|
@@ -85,12 +86,19 @@ async function runAgent(opts: { workspace?: string; name?: string; verbose?: boo
|
|
|
85
86
|
const processor = new TaskProcessor();
|
|
86
87
|
processor.init(userId);
|
|
87
88
|
const sessionManager = new SessionManager();
|
|
89
|
+
let logEmitter: SessionLogEmitter | null = null;
|
|
88
90
|
|
|
89
91
|
// Graceful shutdown
|
|
90
92
|
const browserRef = getBrowser();
|
|
91
93
|
const shutdown = async () => {
|
|
92
94
|
console.log();
|
|
93
95
|
log.info("Shutting down...");
|
|
96
|
+
setLogHook(null);
|
|
97
|
+
try {
|
|
98
|
+
if (logEmitter) await logEmitter.stop();
|
|
99
|
+
} catch {
|
|
100
|
+
/* ignore */
|
|
101
|
+
}
|
|
94
102
|
try {
|
|
95
103
|
if (browserRef.isConnected()) await browserRef.disconnect();
|
|
96
104
|
} catch {
|
|
@@ -109,6 +117,12 @@ async function runAgent(opts: { workspace?: string; name?: string; verbose?: boo
|
|
|
109
117
|
});
|
|
110
118
|
processor.setSessionId(session.id);
|
|
111
119
|
|
|
120
|
+
// Start persisting logs to Supabase
|
|
121
|
+
logEmitter = new SessionLogEmitter(session.id);
|
|
122
|
+
setLogHook((logType, message) => {
|
|
123
|
+
logEmitter?.push(logType, message);
|
|
124
|
+
});
|
|
125
|
+
|
|
112
126
|
log.info("Listening for tasks (chat + jobs) from web UI...");
|
|
113
127
|
log.info("Press Ctrl+C to stop.\n");
|
|
114
128
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { callMcpHandler } from "./api-client.js";
|
|
2
|
+
import { log } from "../utils/logger.js";
|
|
3
|
+
|
|
4
|
+
const FLUSH_INTERVAL_MS = 3_000;
|
|
5
|
+
const MAX_BATCH_SIZE = 100;
|
|
6
|
+
|
|
7
|
+
interface PendingLog {
|
|
8
|
+
log_type: "stdout" | "stderr" | "status";
|
|
9
|
+
message: string;
|
|
10
|
+
seq: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Batches agent process logs and flushes them to Supabase periodically.
|
|
15
|
+
* Each session gets its own emitter with an auto-incrementing sequence.
|
|
16
|
+
*/
|
|
17
|
+
export class SessionLogEmitter {
|
|
18
|
+
private sequence = 0;
|
|
19
|
+
private buffer: PendingLog[] = [];
|
|
20
|
+
private flushTimer: ReturnType<typeof setInterval> | null = null;
|
|
21
|
+
private flushing = false;
|
|
22
|
+
|
|
23
|
+
constructor(private sessionId: string) {
|
|
24
|
+
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Queue a log entry for batch insertion */
|
|
28
|
+
push(logType: "stdout" | "stderr" | "status", message: string): void {
|
|
29
|
+
this.sequence++;
|
|
30
|
+
this.buffer.push({ log_type: logType, message, seq: this.sequence });
|
|
31
|
+
|
|
32
|
+
// Flush immediately if buffer is large
|
|
33
|
+
if (this.buffer.length >= MAX_BATCH_SIZE) {
|
|
34
|
+
this.flush();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Flush buffered logs to Supabase */
|
|
39
|
+
async flush(): Promise<void> {
|
|
40
|
+
if (this.flushing || this.buffer.length === 0) return;
|
|
41
|
+
|
|
42
|
+
const batch = this.buffer.splice(0);
|
|
43
|
+
this.flushing = true;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await callMcpHandler("log.emit_batch", {
|
|
47
|
+
session_id: this.sessionId,
|
|
48
|
+
logs: batch,
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
log.debug(
|
|
52
|
+
`Failed to flush session logs: ${err instanceof Error ? err.message : err}`
|
|
53
|
+
);
|
|
54
|
+
// Re-queue failed batch at the front (best-effort, drop if too old)
|
|
55
|
+
if (this.buffer.length < MAX_BATCH_SIZE * 5) {
|
|
56
|
+
this.buffer.unshift(...batch);
|
|
57
|
+
}
|
|
58
|
+
} finally {
|
|
59
|
+
this.flushing = false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Stop the emitter and flush remaining logs */
|
|
64
|
+
async stop(): Promise<void> {
|
|
65
|
+
if (this.flushTimer) {
|
|
66
|
+
clearInterval(this.flushTimer);
|
|
67
|
+
this.flushTimer = null;
|
|
68
|
+
}
|
|
69
|
+
await this.flush();
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/utils/logger.ts
CHANGED
|
@@ -2,9 +2,11 @@ import chalk from "chalk";
|
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
3
|
|
|
4
4
|
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
5
|
+
export type LogHook = (logType: "stdout" | "stderr", message: string) => void;
|
|
5
6
|
|
|
6
7
|
let currentLevel: LogLevel = "info";
|
|
7
8
|
let currentCorrelationId: string | null = null;
|
|
9
|
+
let logHook: LogHook | null = null;
|
|
8
10
|
|
|
9
11
|
const LEVEL_ORDER: Record<LogLevel, number> = {
|
|
10
12
|
debug: 0,
|
|
@@ -17,6 +19,14 @@ export function setLogLevel(level: LogLevel) {
|
|
|
17
19
|
currentLevel = level;
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Set a hook to capture all log output for persistence.
|
|
24
|
+
* Call with null to remove.
|
|
25
|
+
*/
|
|
26
|
+
export function setLogHook(hook: LogHook | null) {
|
|
27
|
+
logHook = hook;
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
/**
|
|
21
31
|
* Set a correlation ID that will be included in all subsequent log messages.
|
|
22
32
|
* Call with null to clear.
|
|
@@ -54,36 +64,54 @@ function prefix(): string {
|
|
|
54
64
|
export const log = {
|
|
55
65
|
debug(msg: string, ...args: unknown[]) {
|
|
56
66
|
if (shouldLog("debug")) {
|
|
67
|
+
const text = formatArgs(msg, args);
|
|
68
|
+
logHook?.("stdout", `[DEBUG] ${text}`);
|
|
57
69
|
console.log(chalk.gray(`[${prefix()}] DEBUG`), msg, ...args);
|
|
58
70
|
}
|
|
59
71
|
},
|
|
60
72
|
info(msg: string, ...args: unknown[]) {
|
|
61
73
|
if (shouldLog("info")) {
|
|
74
|
+
const text = formatArgs(msg, args);
|
|
75
|
+
logHook?.("stdout", text);
|
|
62
76
|
console.log(chalk.blue(`[${prefix()}]`), msg, ...args);
|
|
63
77
|
}
|
|
64
78
|
},
|
|
65
79
|
success(msg: string, ...args: unknown[]) {
|
|
66
80
|
if (shouldLog("info")) {
|
|
81
|
+
const text = formatArgs(msg, args);
|
|
82
|
+
logHook?.("stdout", `✓ ${text}`);
|
|
67
83
|
console.log(chalk.green(`[${prefix()}] ✓`), msg, ...args);
|
|
68
84
|
}
|
|
69
85
|
},
|
|
70
86
|
warn(msg: string, ...args: unknown[]) {
|
|
71
87
|
if (shouldLog("warn")) {
|
|
88
|
+
const text = formatArgs(msg, args);
|
|
89
|
+
logHook?.("stderr", `[WARN] ${text}`);
|
|
72
90
|
console.log(chalk.yellow(`[${prefix()}] WARN`), msg, ...args);
|
|
73
91
|
}
|
|
74
92
|
},
|
|
75
93
|
error(msg: string, ...args: unknown[]) {
|
|
76
94
|
if (shouldLog("error")) {
|
|
95
|
+
const text = formatArgs(msg, args);
|
|
96
|
+
logHook?.("stderr", `[ERROR] ${text}`);
|
|
77
97
|
console.error(chalk.red(`[${prefix()}] ERROR`), msg, ...args);
|
|
78
98
|
}
|
|
79
99
|
},
|
|
80
100
|
agent(msg: string) {
|
|
101
|
+
logHook?.("stdout", `▸ ${msg}`);
|
|
81
102
|
console.log(chalk.cyan(" ▸"), msg);
|
|
82
103
|
},
|
|
83
104
|
tool(name: string, msg: string) {
|
|
105
|
+
logHook?.("stdout", `⚡ ${name}: ${msg}`);
|
|
84
106
|
console.log(chalk.magenta(` ⚡ ${name}:`), msg);
|
|
85
107
|
},
|
|
86
108
|
result(msg: string) {
|
|
109
|
+
logHook?.("stdout", `← ${msg}`);
|
|
87
110
|
console.log(chalk.green(" ←"), msg);
|
|
88
111
|
},
|
|
89
112
|
};
|
|
113
|
+
|
|
114
|
+
function formatArgs(msg: string, args: unknown[]): string {
|
|
115
|
+
if (args.length === 0) return msg;
|
|
116
|
+
return `${msg} ${args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" ")}`;
|
|
117
|
+
}
|