@ziggs-ai/api-client 0.1.5 → 0.1.7

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.
@@ -1,450 +0,0 @@
1
- import 'dotenv/config';
2
- import { getBackendUrl } from '../utils/urlUtils.js';
3
- import { type Creds, type Task, type TaskState, type Agreement, ApiError } from '../types.js';
4
-
5
- function getTaskBaseUrl(): string { return `${getBackendUrl()}/tasks`; }
6
- function getMarketplaceBaseUrl(): string { return `${getBackendUrl()}/marketplace`; }
7
-
8
- function buildHeaders(creds: Creds): Record<string, string> {
9
- return {
10
- 'content-type': 'application/json',
11
- Authorization: `Bearer ${creds.operatorKey}`,
12
- 'X-Agent-Id': creds.agentId,
13
- };
14
- }
15
-
16
- function assertCreds(creds: Creds | undefined | null, op: string): asserts creds is Creds {
17
- if (!creds?.operatorKey) throw new Error(`operatorKey is required for ${op}`);
18
- if (!creds?.agentId) throw new Error(`agentId is required for ${op}`);
19
- }
20
-
21
- function parseErrorMessage(responseBody: string, defaultMessage: string): string {
22
- if (!responseBody) return defaultMessage;
23
- try {
24
- const d = JSON.parse(responseBody) as Record<string, unknown>;
25
- return (d['details'] as string) || (d['error'] as string) || (d['message'] as string) || defaultMessage;
26
- } catch {
27
- return responseBody || defaultMessage;
28
- }
29
- }
30
-
31
- function throwApiError(response: Response, responseBody: string, defaultMessage: string): never {
32
- throw new ApiError(parseErrorMessage(responseBody, defaultMessage), response.status, responseBody);
33
- }
34
-
35
- function extractTask(data: unknown): Task | null {
36
- if (!data || typeof data !== 'object') return null;
37
- const d = data as Record<string, unknown>;
38
- if (d['task'] && typeof d['task'] === 'object' && (d['task'] as Record<string, unknown>)['taskId']) {
39
- return d['task'] as Task;
40
- }
41
- if (d['taskId']) return d as unknown as Task;
42
- return null;
43
- }
44
-
45
- // ---------------------------------------------------------------------------
46
- // CRUD
47
- // ---------------------------------------------------------------------------
48
-
49
- export interface CreateTaskData {
50
- description: string;
51
- agreementId: string;
52
- parentTaskId?: string;
53
- plan?: unknown;
54
- idempotencyKey?: string;
55
- }
56
-
57
- export async function createTask(taskData: CreateTaskData, creds: Creds): Promise<Task> {
58
- if (!taskData) throw new Error('Task data is required for task creation');
59
- assertCreds(creds, 'task creation');
60
-
61
- const { idempotencyKey, ...bodyData } = taskData;
62
- const headers = buildHeaders(creds);
63
- if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;
64
-
65
- const res = await fetch(getTaskBaseUrl(), {
66
- method: 'POST',
67
- headers,
68
- body: JSON.stringify(bodyData),
69
- });
70
-
71
- if (!res.ok) {
72
- const body = await res.text().catch(() => '');
73
- throwApiError(res, body, `Task creation failed: ${res.status} ${res.statusText}`);
74
- }
75
-
76
- const data = await res.json().catch(() => null) as unknown;
77
- const task = extractTask(data);
78
- if (!task) throw new Error('Invalid response: task data not found');
79
- return task;
80
- }
81
-
82
- export async function getTask(taskId: string, creds: Creds): Promise<Task> {
83
- if (!taskId) throw new Error('getTask: taskId is required');
84
- assertCreds(creds, 'task retrieval');
85
-
86
- const res = await fetch(`${getTaskBaseUrl()}/${taskId}`, {
87
- method: 'GET',
88
- headers: buildHeaders(creds),
89
- });
90
-
91
- if (!res.ok) {
92
- const body = await res.text().catch(() => '');
93
- throwApiError(res, body, `Task get failed: ${res.status} ${res.statusText}`);
94
- }
95
-
96
- const data = await res.json().catch(() => null) as unknown;
97
- const task = extractTask(data);
98
- if (!task) throw new Error('Invalid response: task data not found');
99
- return task;
100
- }
101
-
102
- export interface UpdateTaskStateData {
103
- result?: unknown;
104
- errorMessage?: string;
105
- }
106
-
107
- export async function updateTaskState(
108
- taskId: string,
109
- state: TaskState,
110
- data: UpdateTaskStateData = {},
111
- creds: Creds,
112
- ): Promise<Task> {
113
- if (!taskId) throw new Error('updateTaskState: taskId is required');
114
- if (!state) throw new Error('updateTaskState: state is required');
115
- assertCreds(creds, 'task state update');
116
-
117
- const res = await fetch(`${getTaskBaseUrl()}/${taskId}/state`, {
118
- method: 'PATCH',
119
- headers: buildHeaders(creds),
120
- body: JSON.stringify({ state, ...data }),
121
- });
122
-
123
- if (!res.ok) {
124
- const body = await res.text().catch(() => '');
125
- throwApiError(res, body, `Task state update failed: ${res.status} ${res.statusText}`);
126
- }
127
-
128
- const result = await res.json().catch(() => null) as unknown;
129
- const task = extractTask(result);
130
- if (!task) throw new Error('Invalid response: task data not found');
131
- return task;
132
- }
133
-
134
- export async function getActiveTasksForAgent(
135
- agentId: string,
136
- creds: Creds,
137
- ): Promise<Task[]> {
138
- if (!agentId) throw new Error('getActiveTasksForAgent: agentId is required');
139
- assertCreds(creds, 'getting active tasks for agent');
140
-
141
- const url = new URL(`${getBackendUrl()}/agents/${encodeURIComponent(agentId)}/tasks`);
142
- url.searchParams.set('state', 'active');
143
-
144
- const res = await fetch(url.toString(), {
145
- method: 'GET',
146
- headers: buildHeaders(creds),
147
- });
148
-
149
- if (!res.ok) {
150
- const body = await res.text().catch(() => '');
151
- throwApiError(res, body, `Get active tasks for agent failed: ${res.status} ${res.statusText}`);
152
- }
153
-
154
- const data = await res.json().catch(() => null) as Record<string, unknown> | null;
155
- return Array.isArray(data?.['tasks']) ? (data['tasks'] as Task[]) : [];
156
- }
157
-
158
- // Backend deliberately has no `/chats/:id/tasks` route — it's composable
159
- // from links → per-agreement tasks. We do the composition here so callers
160
- // don't have to.
161
- export async function getActiveTasksForChat(
162
- chatId: string,
163
- creds: Creds,
164
- ): Promise<Task[]> {
165
- if (!chatId) throw new Error('getActiveTasksForChat: chatId is required');
166
- assertCreds(creds, 'getting active tasks for chat');
167
-
168
- const linksRes = await fetch(
169
- `${getBackendUrl()}/agreements/by-chat/${encodeURIComponent(chatId)}`,
170
- { method: 'GET', headers: buildHeaders(creds) },
171
- );
172
- if (!linksRes.ok) {
173
- const body = await linksRes.text().catch(() => '');
174
- throwApiError(linksRes, body, `Get agreements for chat failed: ${linksRes.status} ${linksRes.statusText}`);
175
- }
176
- const linksData = await linksRes.json().catch(() => null) as Record<string, unknown> | null;
177
- const rawLinks = linksData?.['links'] ?? linksData?.['agreements'];
178
- const agreements = Array.isArray(rawLinks) ? (rawLinks as Array<Record<string, unknown>>) : [];
179
- const agreementIds = Array.from(new Set(agreements.map((a) => a['agreementId'] as string).filter(Boolean)));
180
- if (agreementIds.length === 0) return [];
181
-
182
- const lists = await Promise.all(
183
- agreementIds.map(async (aid) => {
184
- const url = new URL(`${getBackendUrl()}/agreements/${encodeURIComponent(aid)}/tasks`);
185
- url.searchParams.set('state', 'active');
186
- const res = await fetch(url.toString(), { method: 'GET', headers: buildHeaders(creds) });
187
- if (!res.ok) {
188
- const body = await res.text().catch(() => '');
189
- throwApiError(res, body, `Get tasks for agreement ${aid} failed: ${res.status}`);
190
- }
191
- const data = await res.json().catch(() => null) as Record<string, unknown> | null;
192
- return Array.isArray(data?.['tasks']) ? (data['tasks'] as Task[]) : [];
193
- }),
194
- );
195
- return lists.flat();
196
- }
197
-
198
- export async function cancelTask(taskId: string, creds: Creds): Promise<Task> {
199
- if (!taskId) throw new Error('cancelTask: taskId is required');
200
- assertCreds(creds, 'task cancellation');
201
-
202
- const res = await fetch(`${getTaskBaseUrl()}/${taskId}/cancel`, {
203
- method: 'PATCH',
204
- headers: buildHeaders(creds),
205
- });
206
-
207
- if (!res.ok) {
208
- const body = await res.text().catch(() => '');
209
- throwApiError(res, body, `Task cancel failed: ${res.status} ${res.statusText}`);
210
- }
211
-
212
- const result = await res.json().catch(() => null) as unknown;
213
- const task = extractTask(result);
214
- if (!task) throw new Error('Invalid response: task data not found');
215
- return task;
216
- }
217
-
218
- export async function getSubtasks(parentTaskId: string, creds: Creds): Promise<Task[]> {
219
- if (!parentTaskId) throw new Error('getSubtasks: parentTaskId is required');
220
- assertCreds(creds, 'getting subtasks');
221
-
222
- const url = new URL(`${getTaskBaseUrl()}/${encodeURIComponent(parentTaskId)}/subtasks`);
223
-
224
- const res = await fetch(url.toString(), {
225
- method: 'GET',
226
- headers: buildHeaders(creds),
227
- });
228
-
229
- if (!res.ok) {
230
- const body = await res.text().catch(() => '');
231
- throwApiError(res, body, `Get subtasks failed: ${res.status} ${res.statusText}`);
232
- }
233
-
234
- const data = await res.json().catch(() => null) as Record<string, unknown> | null;
235
- return Array.isArray(data?.['tasks']) ? (data['tasks'] as Task[]) : [];
236
- }
237
-
238
- export interface PlanReplaceStep {
239
- stepId: string;
240
- description: string;
241
- order: number;
242
- }
243
-
244
- export async function replaceTaskPlan(
245
- taskId: string,
246
- steps: PlanReplaceStep[],
247
- creds: Creds,
248
- ): Promise<Task> {
249
- if (!taskId) throw new Error('replaceTaskPlan: taskId is required');
250
- if (!steps || !Array.isArray(steps)) throw new Error('replaceTaskPlan: steps must be an array');
251
- assertCreds(creds, 'plan replace');
252
-
253
- const res = await fetch(`${getTaskBaseUrl()}/${taskId}/plan`, {
254
- method: 'PATCH',
255
- headers: buildHeaders(creds),
256
- body: JSON.stringify({ steps }),
257
- });
258
-
259
- if (!res.ok) {
260
- const responseBody = await res.text().catch(() => '');
261
- throwApiError(res, responseBody, `Plan replace failed: ${res.status} ${res.statusText}`);
262
- }
263
-
264
- const data = await res.json().catch(() => null) as unknown;
265
- const task = extractTask(data);
266
- if (!task) throw new Error('Invalid response: task data not found');
267
- return task;
268
- }
269
-
270
- // ---------------------------------------------------------------------------
271
- // Marketplace: Quests (buyer-everyone)
272
- // ---------------------------------------------------------------------------
273
-
274
- export interface PublishToLedgerPayload {
275
- description: string;
276
- chatId?: string;
277
- payerId?: string;
278
- parentTaskId?: string;
279
- price?: number;
280
- lifecycle?: string;
281
- expiresAt?: string;
282
- maxExecutions?: number;
283
- agreementDescription?: string;
284
- }
285
-
286
- export async function publishToLedger(payload: PublishToLedgerPayload, creds: Creds): Promise<Agreement> {
287
- assertCreds(creds, 'quest publish');
288
- const res = await fetch(`${getMarketplaceBaseUrl()}/quests/publish`, {
289
- method: 'POST',
290
- headers: buildHeaders(creds),
291
- body: JSON.stringify(payload || {}),
292
- });
293
- if (!res.ok) {
294
- const body = await res.text().catch(() => '');
295
- throwApiError(res, body, `Quest publish failed: ${res.status}`);
296
- }
297
- const data = await res.json().catch(() => null) as Record<string, unknown> | null;
298
- if (!data?.['agreement']) throw new Error('Quest publish returned no agreement');
299
- return data['agreement'] as Agreement;
300
- }
301
-
302
- export interface PullFromLedgerOptions {
303
- limit?: number;
304
- since?: string;
305
- }
306
-
307
- export async function pullFromLedger(options: PullFromLedgerOptions | undefined, creds: Creds): Promise<Agreement[]> {
308
- assertCreds(creds, 'quest pull');
309
- const res = await fetch(`${getMarketplaceBaseUrl()}/quests/pull`, {
310
- method: 'POST',
311
- headers: buildHeaders(creds),
312
- body: JSON.stringify(options || {}),
313
- });
314
- if (!res.ok) {
315
- const body = await res.text().catch(() => '');
316
- throwApiError(res, body, `Quest pull failed: ${res.status}`);
317
- }
318
- const data = await res.json().catch(() => null) as Record<string, unknown> | null;
319
- return (data?.['agreements'] as Agreement[]) ?? [];
320
- }
321
-
322
- export async function claimLedgerTask(agreementId: string, creds: Creds, options?: { idempotencyKey?: string }): Promise<Agreement> {
323
- if (!agreementId) throw new Error('agreementId is required');
324
- assertCreds(creds, 'quest claim');
325
- const headers = buildHeaders(creds);
326
- if (options?.idempotencyKey) headers['Idempotency-Key'] = options.idempotencyKey;
327
- const res = await fetch(`${getMarketplaceBaseUrl()}/quests/claim`, {
328
- method: 'POST',
329
- headers,
330
- body: JSON.stringify({ agreementId }),
331
- });
332
- if (!res.ok) {
333
- const body = await res.text().catch(() => '');
334
- throwApiError(res, body, `Quest claim failed: ${res.status}`);
335
- }
336
- const data = await res.json().catch(() => null) as Record<string, unknown> | null;
337
- if (!data?.['ok']) throw new Error((data?.['error'] as string) || 'Claim failed');
338
- return data['agreement'] as Agreement;
339
- }
340
-
341
- // ---------------------------------------------------------------------------
342
- // Reporting & satisfaction
343
- // ---------------------------------------------------------------------------
344
-
345
- export async function reportTask(taskId: string, message = '', creds: Creds): Promise<Task> {
346
- if (!taskId) throw new Error('reportTask: taskId is required');
347
- assertCreds(creds, 'task report');
348
-
349
- const res = await fetch(`${getTaskBaseUrl()}/${taskId}/report`, {
350
- method: 'POST',
351
- headers: buildHeaders(creds),
352
- body: JSON.stringify({ message }),
353
- });
354
-
355
- if (!res.ok) {
356
- const body = await res.text().catch(() => '');
357
- throwApiError(res, body, `Task report failed: ${res.status} ${res.statusText}`);
358
- }
359
-
360
- const data = await res.json().catch(() => null) as unknown;
361
- const task = extractTask(data);
362
- if (!task) throw new Error('Invalid response: task data not found');
363
- return task;
364
- }
365
-
366
- export async function setTaskSatisfaction(
367
- taskId: string,
368
- satisfaction: 'positive' | 'negative',
369
- creds: Creds,
370
- ): Promise<Task> {
371
- if (!taskId) throw new Error('setTaskSatisfaction: taskId is required');
372
- if (!satisfaction) throw new Error('setTaskSatisfaction: satisfaction is required');
373
- assertCreds(creds, 'task satisfaction');
374
-
375
- const res = await fetch(`${getTaskBaseUrl()}/${taskId}/satisfaction`, {
376
- method: 'PATCH',
377
- headers: buildHeaders(creds),
378
- body: JSON.stringify({ satisfaction }),
379
- });
380
-
381
- if (!res.ok) {
382
- const body = await res.text().catch(() => '');
383
- throwApiError(res, body, `Task satisfaction failed: ${res.status} ${res.statusText}`);
384
- }
385
-
386
- const data = await res.json().catch(() => null) as unknown;
387
- const task = extractTask(data);
388
- if (!task) throw new Error('Invalid response: task data not found');
389
- return task;
390
- }
391
-
392
- export interface CountTasksFilters {
393
- chatId?: string;
394
- state?: string;
395
- userId?: string;
396
- agentId?: string;
397
- }
398
-
399
- export async function countTasks(filters: CountTasksFilters = {}, creds: Creds): Promise<number> {
400
- assertCreds(creds, 'count tasks');
401
-
402
- // Canonical REST: GET /tasks/count.
403
- const url = new URL(`${getTaskBaseUrl()}/count`);
404
- if (filters.chatId) url.searchParams.set('chatId', filters.chatId);
405
- if (filters.state) url.searchParams.set('state', filters.state);
406
- if (filters.userId) url.searchParams.set('userId', filters.userId);
407
- if (filters.agentId) url.searchParams.set('agentId', filters.agentId);
408
-
409
- const res = await fetch(url.toString(), {
410
- method: 'GET',
411
- headers: buildHeaders(creds),
412
- });
413
-
414
- if (!res.ok) {
415
- const body = await res.text().catch(() => '');
416
- throwApiError(res, body, `Count tasks failed: ${res.status} ${res.statusText}`);
417
- }
418
-
419
- const data = await res.json().catch(() => null) as Record<string, unknown> | null;
420
- return typeof data?.['count'] === 'number' ? data['count'] : 0;
421
- }
422
-
423
- // ---------------------------------------------------------------------------
424
- // TaskClient class — bind creds once
425
- // ---------------------------------------------------------------------------
426
-
427
- export class TaskClient {
428
- private creds: Creds;
429
-
430
- constructor(operatorKey: string, agentId: string) {
431
- if (!operatorKey) throw new Error('TaskClient: operatorKey is required');
432
- if (!agentId) throw new Error('TaskClient: agentId is required');
433
- this.creds = { operatorKey, agentId };
434
- }
435
-
436
- createTask(data: CreateTaskData) { return createTask(data, this.creds); }
437
- getTask(taskId: string) { return getTask(taskId, this.creds); }
438
- updateTaskState(taskId: string, state: TaskState, data?: UpdateTaskStateData) { return updateTaskState(taskId, state, data ?? {}, this.creds); }
439
- getActiveTasksForAgent(agentId: string) { return getActiveTasksForAgent(agentId, this.creds); }
440
- getActiveTasksForChat(chatId: string) { return getActiveTasksForChat(chatId, this.creds); }
441
- cancelTask(taskId: string) { return cancelTask(taskId, this.creds); }
442
- getSubtasks(parentTaskId: string) { return getSubtasks(parentTaskId, this.creds); }
443
- replaceTaskPlan(taskId: string, steps: PlanReplaceStep[]) { return replaceTaskPlan(taskId, steps, this.creds); }
444
- publishToLedger(payload: PublishToLedgerPayload) { return publishToLedger(payload, this.creds); }
445
- pullFromLedger(options?: PullFromLedgerOptions) { return pullFromLedger(options, this.creds); }
446
- claimLedgerTask(taskId: string, options?: { idempotencyKey?: string }) { return claimLedgerTask(taskId, this.creds, options); }
447
- reportTask(taskId: string, message?: string) { return reportTask(taskId, message, this.creds); }
448
- setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative') { return setTaskSatisfaction(taskId, satisfaction, this.creds); }
449
- countTasks(filters?: CountTasksFilters) { return countTasks(filters, this.creds); }
450
- }
@@ -1,57 +0,0 @@
1
- import 'dotenv/config';
2
- import { runtimeLog } from '../shared/runtimeLog.js';
3
- import { getBackendUrl } from '../utils/urlUtils.js';
4
-
5
- export class TelemetryClient {
6
- private readonly operatorKey: string;
7
- private readonly agentId: string;
8
- private _queue: unknown[];
9
- private _flushing: boolean;
10
-
11
- constructor(operatorKey: string, agentId: string) {
12
- if (!operatorKey) throw new Error('TelemetryClient: operatorKey is required');
13
- if (!agentId) throw new Error('TelemetryClient: agentId is required (operator-token impersonation)');
14
- this.operatorKey = operatorKey;
15
- this.agentId = agentId;
16
- this._queue = [];
17
- this._flushing = false;
18
- }
19
-
20
- async send(payload: unknown): Promise<void> {
21
- this._queue.push(payload);
22
- if (!this._flushing) this._flush();
23
- }
24
-
25
- private async _flush(): Promise<void> {
26
- this._flushing = true;
27
- while (this._queue.length > 0) {
28
- const batch = this._queue.splice(0, 10);
29
- await Promise.allSettled(batch.map((p) => this._post(p)));
30
- }
31
- this._flushing = false;
32
- }
33
-
34
- private async _post(payload: unknown): Promise<void> {
35
- const url = `${getBackendUrl()}/agents/monitoring/ingest`;
36
- try {
37
- const res = await fetch(url, {
38
- method: 'POST',
39
- headers: {
40
- 'content-type': 'application/json',
41
- Authorization: `Bearer ${this.operatorKey}`,
42
- 'X-Agent-Id': this.agentId,
43
- },
44
- body: JSON.stringify({ payload }),
45
- });
46
- if (!res.ok) {
47
- const body = await res.text().catch(() => '');
48
- runtimeLog.warn(
49
- 'TelemetryClient',
50
- `⚠️ ingest failed agent=${this.agentId} ${res.status} ${res.statusText} body=${body.slice(0, 200)}`,
51
- );
52
- }
53
- } catch (err) {
54
- runtimeLog.warn('TelemetryClient', `⚠️ ingest error agent=${this.agentId} message=${(err as Error).message}`);
55
- }
56
- }
57
- }
package/src/http/index.ts DELETED
@@ -1,26 +0,0 @@
1
- export * from './TaskClient.js';
2
- export * from './AgreementClient.js';
3
- export * from './MarketplaceClient.js';
4
- export * from './ChatClient.js';
5
- export { MessagesClient } from './MessagesClient.js';
6
- export type { ListMessagesOptions, ListMessagesResult } from './MessagesClient.js';
7
- export { ArtifactsClient } from './ArtifactsClient.js';
8
- export type {
9
- ArtifactVisibility,
10
- ListArtifactsOptions,
11
- ListArtifactsQuery,
12
- ListArtifactsResult,
13
- WriteArtifactInput,
14
- } from './ArtifactsClient.js';
15
- export { ScopeClient } from './ScopeClient.js';
16
- export type { ScopeKind, ScopeResult, PartyRef } from './ScopeClient.js';
17
- export { ContextReadClient } from './ContextReadClient.js';
18
- export type {
19
- ContextReadType,
20
- ContextReadQuery,
21
- ContextReadEnvelope,
22
- } from './ContextReadClient.js';
23
- export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
24
- export type { ContextReachDescriptor } from './ContextDiscoveryClient.js';
25
- export { AgentSearchClient } from './AgentSearchClient.js';
26
- export { TelemetryClient } from './TelemetryClient.js';
package/src/index.ts DELETED
@@ -1,27 +0,0 @@
1
- export * from './http/index.js';
2
- export { WebSocketClient } from './websocket/index.js';
3
- export { createControlSocket } from './websocket/ControlSocket.js';
4
- export { ConnectionManager } from './ConnectionManager.js';
5
- export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, AGREEMENT_ENGAGEMENT_KIND, isValidContentType } from './types.js';
6
- export { getBackendUrl, getWebSocketUrl } from './utils/urlUtils.js';
7
- export { runtimeLog, resetRuntimeLogLevelCache } from './shared/runtimeLog.js';
8
- export type {
9
- Creds,
10
- Task,
11
- TaskState,
12
- PlanStep,
13
- PlanStepStatus,
14
- Agreement,
15
- EngagementKind,
16
- EntryType,
17
- ContentType,
18
- MessageMetadata,
19
- MessageHandler,
20
- ApiError,
21
- } from './types.js';
22
- export type {
23
- ProposeTerms,
24
- ProposeDirectInput,
25
- ProposeBroadcastInput,
26
- ProposeAgreementData,
27
- } from './http/AgreementClient.js';
@@ -1,68 +0,0 @@
1
- /**
2
- * Same semantics as `@ziggs-ai/agent-sdk` `shared/runtimeLog.ts` (duplicated
3
- * here so this package stays dependency-free).
4
- *
5
- * @see agent-sdk/src/shared/runtimeLog.ts
6
- */
7
-
8
- export type RuntimeLogLevel = 'debug' | 'info' | 'warn' | 'error';
9
-
10
- const SEVERITY: Record<RuntimeLogLevel, number> = {
11
- debug: 0,
12
- info: 1,
13
- warn: 2,
14
- error: 3,
15
- };
16
-
17
- function parseThreshold(): number {
18
- if (process.env.DEBUG_AGENTPLUS === '1' || process.env.AGENTPLUS_DEBUG === '1') {
19
- return SEVERITY.debug;
20
- }
21
- const raw = (
22
- process.env.LOG_LEVEL ||
23
- process.env.AGENTPLUS_LOG_LEVEL ||
24
- 'info'
25
- ).toLowerCase();
26
- if (raw === 'silent' || raw === 'none') return SEVERITY.error;
27
- if (raw === 'debug' || raw === 'trace') return SEVERITY.debug;
28
- if (raw === 'warn') return SEVERITY.warn;
29
- if (raw === 'error') return SEVERITY.error;
30
- return SEVERITY.info;
31
- }
32
-
33
- let cachedThreshold: number | null = null;
34
-
35
- function threshold(): number {
36
- if (cachedThreshold === null) cachedThreshold = parseThreshold();
37
- return cachedThreshold;
38
- }
39
-
40
- export function resetRuntimeLogLevelCache(): void {
41
- cachedThreshold = null;
42
- }
43
-
44
- function shouldEmit(level: RuntimeLogLevel): boolean {
45
- return SEVERITY[level] >= threshold();
46
- }
47
-
48
- function fmt(scope: string, msg: string): string {
49
- return `[${scope}] ${msg}`;
50
- }
51
-
52
- export const runtimeLog = {
53
- debug(scope: string, msg: string, ...rest: unknown[]): void {
54
- if (!shouldEmit('debug')) return;
55
- console.log(fmt(scope, msg), ...rest);
56
- },
57
- info(scope: string, msg: string, ...rest: unknown[]): void {
58
- if (!shouldEmit('info')) return;
59
- console.log(fmt(scope, msg), ...rest);
60
- },
61
- warn(scope: string, msg: string, ...rest: unknown[]): void {
62
- if (!shouldEmit('warn')) return;
63
- console.warn(fmt(scope, msg), ...rest);
64
- },
65
- error(scope: string, msg: string, ...rest: unknown[]): void {
66
- console.error(fmt(scope, msg), ...rest);
67
- },
68
- };