@agi-cli/sdk 0.1.93 → 0.1.94
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/package.json +1 -1
- package/src/core/src/index.ts +6 -0
- package/src/core/src/terminals/manager.ts +10 -10
- package/src/core/src/utils/debug.ts +40 -0
- package/src/core/src/utils/logger.ts +150 -0
- package/src/index.ts +12 -0
package/package.json
CHANGED
package/src/core/src/index.ts
CHANGED
|
@@ -100,3 +100,9 @@ export {
|
|
|
100
100
|
NotFoundError,
|
|
101
101
|
ServiceError,
|
|
102
102
|
} from './errors';
|
|
103
|
+
|
|
104
|
+
// =======================
|
|
105
|
+
// Logging & Debug
|
|
106
|
+
// =======================
|
|
107
|
+
export { logger, debug, info, warn, error, time } from './utils/logger.ts';
|
|
108
|
+
export { isDebugEnabled, isTraceEnabled } from './utils/debug.ts';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { spawn as spawnPty } from './bun-pty.ts';
|
|
2
1
|
import { randomBytes } from 'node:crypto';
|
|
3
|
-
import { Terminal } from './terminal.ts';
|
|
4
2
|
import type { PtyOptions } from './bun-pty.ts';
|
|
3
|
+
import { spawn as spawnPty } from './bun-pty.ts';
|
|
4
|
+
import { Terminal } from './terminal.ts';
|
|
5
|
+
import { logger } from '../utils/logger.ts';
|
|
5
6
|
|
|
6
7
|
const MAX_TERMINALS = 10;
|
|
7
8
|
const CLEANUP_DELAY_MS = 5 * 60 * 1000;
|
|
@@ -19,10 +20,7 @@ export class TerminalManager {
|
|
|
19
20
|
private terminals = new Map<string, Terminal>();
|
|
20
21
|
private cleanupTimers = new Map<string, NodeJS.Timeout>();
|
|
21
22
|
|
|
22
|
-
constructor() {
|
|
23
|
-
process.on('SIGTERM', () => this.killAll());
|
|
24
|
-
process.on('SIGINT', () => this.killAll());
|
|
25
|
-
}
|
|
23
|
+
constructor() {}
|
|
26
24
|
|
|
27
25
|
create(options: CreateTerminalOptions): Terminal {
|
|
28
26
|
if (this.terminals.size >= MAX_TERMINALS) {
|
|
@@ -32,7 +30,7 @@ export class TerminalManager {
|
|
|
32
30
|
const id = this.generateId();
|
|
33
31
|
|
|
34
32
|
try {
|
|
35
|
-
|
|
33
|
+
logger.debug('TerminalManager: creating terminal', {
|
|
36
34
|
id,
|
|
37
35
|
command: options.command,
|
|
38
36
|
args: options.args,
|
|
@@ -50,7 +48,9 @@ export class TerminalManager {
|
|
|
50
48
|
|
|
51
49
|
const pty = spawnPty(options.command, options.args || [], ptyOptions);
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
logger.debug('TerminalManager: PTY created', {
|
|
52
|
+
pid: pty.pid,
|
|
53
|
+
});
|
|
54
54
|
|
|
55
55
|
const terminal = new Terminal(id, pty, options);
|
|
56
56
|
|
|
@@ -64,11 +64,11 @@ export class TerminalManager {
|
|
|
64
64
|
|
|
65
65
|
this.terminals.set(id, terminal);
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
logger.debug('TerminalManager: terminal added to map', { id });
|
|
68
68
|
|
|
69
69
|
return terminal;
|
|
70
70
|
} catch (error) {
|
|
71
|
-
|
|
71
|
+
logger.error('TerminalManager: failed to create terminal', error);
|
|
72
72
|
throw error;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const TRUTHY = new Set(['1', 'true', 'yes', 'on']);
|
|
2
|
+
|
|
3
|
+
type GlobalDebugFlags = {
|
|
4
|
+
__AGI_DEBUG_ENABLED__?: boolean;
|
|
5
|
+
__AGI_TRACE_ENABLED__?: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function readGlobalFlag(
|
|
9
|
+
key: '__AGI_DEBUG_ENABLED__' | '__AGI_TRACE_ENABLED__',
|
|
10
|
+
) {
|
|
11
|
+
const globalState = globalThis as GlobalDebugFlags;
|
|
12
|
+
return globalState[key];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function envEnabled(keys: string[]): boolean {
|
|
16
|
+
for (const key of keys) {
|
|
17
|
+
const raw = typeof process !== 'undefined' ? process.env?.[key] : undefined;
|
|
18
|
+
if (!raw) continue;
|
|
19
|
+
const trimmed = raw.trim().toLowerCase();
|
|
20
|
+
if (!trimmed) continue;
|
|
21
|
+
if (TRUTHY.has(trimmed) || trimmed === 'all') return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function isDebugEnabled(): boolean {
|
|
27
|
+
const globalFlag = readGlobalFlag('__AGI_DEBUG_ENABLED__');
|
|
28
|
+
if (typeof globalFlag === 'boolean') {
|
|
29
|
+
return globalFlag;
|
|
30
|
+
}
|
|
31
|
+
return envEnabled(['AGI_DEBUG', 'DEBUG_AGI']);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isTraceEnabled(): boolean {
|
|
35
|
+
const globalFlag = readGlobalFlag('__AGI_TRACE_ENABLED__');
|
|
36
|
+
if (typeof globalFlag === 'boolean') {
|
|
37
|
+
return Boolean(globalFlag) && isDebugEnabled();
|
|
38
|
+
}
|
|
39
|
+
return envEnabled(['AGI_TRACE', 'TRACE_AGI']) && isDebugEnabled();
|
|
40
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { isDebugEnabled, isTraceEnabled } from './debug.ts';
|
|
2
|
+
|
|
3
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
4
|
+
|
|
5
|
+
function safeHasMeta(
|
|
6
|
+
meta?: Record<string, unknown>,
|
|
7
|
+
): meta is Record<string, unknown> {
|
|
8
|
+
return Boolean(meta && Object.keys(meta).length);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function debug(message: string, meta?: Record<string, unknown>): void {
|
|
12
|
+
if (!isDebugEnabled()) return;
|
|
13
|
+
try {
|
|
14
|
+
if (safeHasMeta(meta)) {
|
|
15
|
+
console.log(`[debug] ${message}`, meta);
|
|
16
|
+
} else {
|
|
17
|
+
console.log(`[debug] ${message}`);
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
// ignore logging errors
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function info(message: string, meta?: Record<string, unknown>): void {
|
|
25
|
+
if (!isDebugEnabled() && !isTraceEnabled()) return;
|
|
26
|
+
try {
|
|
27
|
+
if (safeHasMeta(meta)) {
|
|
28
|
+
console.log(`[info] ${message}`, meta);
|
|
29
|
+
} else {
|
|
30
|
+
console.log(`[info] ${message}`);
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// ignore logging errors
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function warn(message: string, meta?: Record<string, unknown>): void {
|
|
38
|
+
try {
|
|
39
|
+
if (safeHasMeta(meta)) {
|
|
40
|
+
console.warn(`[warn] ${message}`, meta);
|
|
41
|
+
} else {
|
|
42
|
+
console.warn(`[warn] ${message}`);
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
// ignore logging errors
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function error(
|
|
50
|
+
message: string,
|
|
51
|
+
err?: unknown,
|
|
52
|
+
meta?: Record<string, unknown>,
|
|
53
|
+
): void {
|
|
54
|
+
if (!isDebugEnabled()) return;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const logMeta: Record<string, unknown> = meta ? { ...meta } : {};
|
|
58
|
+
|
|
59
|
+
if (err) {
|
|
60
|
+
if (err instanceof Error) {
|
|
61
|
+
logMeta.error = {
|
|
62
|
+
name: err.name,
|
|
63
|
+
message: err.message,
|
|
64
|
+
};
|
|
65
|
+
if (isTraceEnabled() && err.stack) {
|
|
66
|
+
(logMeta.error as { stack?: string }).stack = err.stack;
|
|
67
|
+
}
|
|
68
|
+
} else if (typeof err === 'string') {
|
|
69
|
+
logMeta.error = err;
|
|
70
|
+
} else if (typeof err === 'object') {
|
|
71
|
+
const errObj = err as Record<string, unknown>;
|
|
72
|
+
const details: Record<string, unknown> = {};
|
|
73
|
+
if (typeof errObj.name === 'string') details.name = errObj.name;
|
|
74
|
+
if (typeof errObj.message === 'string')
|
|
75
|
+
details.message = errObj.message;
|
|
76
|
+
if (typeof errObj.code === 'string') details.code = errObj.code;
|
|
77
|
+
if (typeof errObj.status === 'number') details.status = errObj.status;
|
|
78
|
+
if (typeof errObj.statusCode === 'number')
|
|
79
|
+
details.statusCode = errObj.statusCode;
|
|
80
|
+
if (
|
|
81
|
+
isTraceEnabled() &&
|
|
82
|
+
typeof errObj.stack === 'string' &&
|
|
83
|
+
!details.stack
|
|
84
|
+
) {
|
|
85
|
+
details.stack = errObj.stack;
|
|
86
|
+
}
|
|
87
|
+
logMeta.error = Object.keys(details).length ? details : errObj;
|
|
88
|
+
} else {
|
|
89
|
+
logMeta.error = String(err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (safeHasMeta(logMeta)) {
|
|
94
|
+
console.error(`[error] ${message}`, logMeta);
|
|
95
|
+
} else {
|
|
96
|
+
console.error(`[error] ${message}`);
|
|
97
|
+
}
|
|
98
|
+
} catch (logErr) {
|
|
99
|
+
try {
|
|
100
|
+
console.error(`[error] ${message} (logging failed)`, logErr);
|
|
101
|
+
} catch {
|
|
102
|
+
// ignore
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const logger = {
|
|
108
|
+
debug,
|
|
109
|
+
info,
|
|
110
|
+
warn,
|
|
111
|
+
error,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function nowMs(): number {
|
|
115
|
+
const perf = (globalThis as { performance?: { now?: () => number } })
|
|
116
|
+
.performance;
|
|
117
|
+
if (perf && typeof perf.now === 'function') return perf.now();
|
|
118
|
+
return Date.now();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
type Timer = {
|
|
122
|
+
end(meta?: Record<string, unknown>): void;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export function time(label: string): Timer {
|
|
126
|
+
if (!isDebugEnabled()) {
|
|
127
|
+
return { end() {} };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const start = nowMs();
|
|
131
|
+
let finished = false;
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
end(meta?: Record<string, unknown>) {
|
|
135
|
+
if (finished) return;
|
|
136
|
+
finished = true;
|
|
137
|
+
const duration = nowMs() - start;
|
|
138
|
+
try {
|
|
139
|
+
const base = `[timing] ${label} ${duration.toFixed(1)}ms`;
|
|
140
|
+
if (safeHasMeta(meta)) {
|
|
141
|
+
console.log(base, meta);
|
|
142
|
+
} else {
|
|
143
|
+
console.log(base);
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
// ignore timing log errors
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -161,6 +161,18 @@ export {
|
|
|
161
161
|
ServiceError,
|
|
162
162
|
} from './core/src/index.ts';
|
|
163
163
|
|
|
164
|
+
// Logging & Debug
|
|
165
|
+
export {
|
|
166
|
+
logger,
|
|
167
|
+
debug,
|
|
168
|
+
info,
|
|
169
|
+
warn,
|
|
170
|
+
error,
|
|
171
|
+
time,
|
|
172
|
+
isDebugEnabled,
|
|
173
|
+
isTraceEnabled,
|
|
174
|
+
} from './core/src/index.ts';
|
|
175
|
+
|
|
164
176
|
// Schema Validation
|
|
165
177
|
export { z } from './core/src/index.ts';
|
|
166
178
|
|