@grovina/brian 0.1.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.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @grovina/brian
2
+
3
+ Brian runtime worker used by managed and self-hosted Brian agents.
4
+
5
+ ## Environment
6
+
7
+ - `BRIAN_BACKEND_URL`
8
+ - `BRIAN_AGENT_ID`
9
+ - `BRIAN_AGENT_TOKEN`
10
+ - `BRIAN_STATE_DIR` (optional)
11
+ - `BRIAN_HEARTBEAT_SECONDS` (optional)
12
+ - `BRIAN_POLL_SECONDS` (optional)
13
+
14
+ ## Run
15
+
16
+ ```bash
17
+ node dist/start.js
18
+ ```
package/dist/agent.js ADDED
@@ -0,0 +1,217 @@
1
+ import { formatTime, sanitizeHistory, } from './types.js';
2
+ import { buildSystemPrompt } from './prompt.js';
3
+ import { formatUpdates, collectImages } from './updates.js';
4
+ import { TurnStore, maybeTruncateInput, trimHistory } from './turns.js';
5
+ import { clip, formatErrorMessage, log, logError, oneLine } from './logs.js';
6
+ import { workerApi } from './client.js';
7
+ const MAX_RETRIES = 3;
8
+ const MAX_HISTORY_MESSAGES = 100;
9
+ function toolPrefix(turnId, toolId, toolName) {
10
+ return `[turn ${turnId}] [tool ${toolId}] ${toolName}`;
11
+ }
12
+ function formatToolResultLog(result) {
13
+ if (typeof result === 'string')
14
+ return result;
15
+ const imageCount = result.images?.length ?? 0;
16
+ if (imageCount === 0)
17
+ return result.text;
18
+ return `${result.text}\n[images: ${imageCount}]`;
19
+ }
20
+ export class Agent {
21
+ history = [];
22
+ cfg;
23
+ toolMap;
24
+ turnStore;
25
+ turnSeq = 0;
26
+ constructor(cfg) {
27
+ this.cfg = cfg;
28
+ this.toolMap = new Map(cfg.tools.map((t) => [t.name, t]));
29
+ this.turnStore = new TurnStore(cfg.stateDir);
30
+ }
31
+ async loop() {
32
+ const hasPriorHistory = await this.loadHistory();
33
+ const hasConsciousness = await this.hasConsciousness();
34
+ const toolDefs = this.cfg.tools.map((t) => t.definition);
35
+ const startupUpdates = this.cfg.updates.drain();
36
+ const startupText = this.buildStartupMessage(hasPriorHistory, hasConsciousness);
37
+ this.history.push({
38
+ role: 'user',
39
+ text: startupUpdates.length > 0
40
+ ? `${startupText}\n\n${formatUpdates(startupUpdates)}`
41
+ : startupText,
42
+ images: startupUpdates.length > 0 ? collectImages(startupUpdates) : undefined,
43
+ });
44
+ while (true) {
45
+ const turnId = ++this.turnSeq;
46
+ const turnInput = await this.buildTurnInput(toolDefs);
47
+ const startTime = Date.now();
48
+ log(`[turn ${turnId}] model start`);
49
+ const response = await this.callWithRetry(turnInput, turnId);
50
+ const durationMs = Date.now() - startTime;
51
+ const requestedTools = response.toolCalls?.length ?? 0;
52
+ log(`[turn ${turnId}] model done ${durationMs}ms (tools: ${requestedTools})`);
53
+ await this.turnStore.save({
54
+ ts: new Date().toISOString(),
55
+ durationMs,
56
+ input: turnInput,
57
+ response,
58
+ });
59
+ const hasContent = Boolean(response.text) ||
60
+ Boolean(response.toolCalls && response.toolCalls.length > 0) ||
61
+ response.metadata !== undefined;
62
+ if (hasContent) {
63
+ this.history.push({
64
+ role: 'assistant',
65
+ text: response.text,
66
+ toolCalls: response.toolCalls,
67
+ metadata: response.metadata,
68
+ });
69
+ }
70
+ if (response.toolCalls && response.toolCalls.length > 0) {
71
+ const results = await this.executeToolCalls(response.toolCalls, turnId);
72
+ const pending = this.cfg.updates.drain();
73
+ const updateText = pending.length > 0 ? formatUpdates(pending) : undefined;
74
+ const updateImages = collectImages(pending);
75
+ this.history.push({
76
+ role: 'user',
77
+ toolResults: results.map((r, i) => ({
78
+ toolCallId: response.toolCalls[i].id,
79
+ result: typeof r === 'string' ? r : r.text,
80
+ images: typeof r !== 'string' ? r.images : undefined,
81
+ })),
82
+ text: updateText,
83
+ images: updateImages.length > 0 ? updateImages : undefined,
84
+ });
85
+ }
86
+ else {
87
+ const pending = this.cfg.updates.drain();
88
+ this.history.push({
89
+ role: 'user',
90
+ text: pending.length > 0 ? formatUpdates(pending) : `[${formatTime()}]`,
91
+ });
92
+ }
93
+ this.history = trimHistory(this.history, MAX_HISTORY_MESSAGES);
94
+ await this.saveHistory();
95
+ }
96
+ }
97
+ async buildTurnInput(tools) {
98
+ const systemPrompt = await buildSystemPrompt({ name: this.cfg.name });
99
+ return maybeTruncateInput({
100
+ systemPrompt,
101
+ messages: this.history,
102
+ tools,
103
+ });
104
+ }
105
+ async callWithRetry(input, turnId) {
106
+ let lastError = null;
107
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
108
+ try {
109
+ return await this.cfg.model.generate({
110
+ systemPrompt: input.systemPrompt,
111
+ messages: input.messages,
112
+ tools: input.tools,
113
+ });
114
+ }
115
+ catch (err) {
116
+ lastError = err;
117
+ const mode = attempt < MAX_RETRIES - 1 ? 'retrying' : 'giving up';
118
+ logError(`[turn ${turnId}] model error attempt ${attempt + 1}/${MAX_RETRIES}: ${clip(formatErrorMessage(lastError))} (${mode})`, { error: lastError });
119
+ if (attempt < MAX_RETRIES - 1) {
120
+ const delay = Math.pow(2, attempt) * 1000;
121
+ await new Promise((r) => setTimeout(r, delay));
122
+ }
123
+ }
124
+ }
125
+ throw lastError;
126
+ }
127
+ async executeToolCalls(calls, turnId) {
128
+ const results = [];
129
+ for (const [index, call] of calls.entries()) {
130
+ const toolId = index + 1;
131
+ const prefix = toolPrefix(turnId, toolId, call.name);
132
+ log(prefix, call.args);
133
+ const startedAt = Date.now();
134
+ try {
135
+ const tool = this.toolMap.get(call.name);
136
+ if (!tool) {
137
+ const msg = `Unknown tool: ${call.name}`;
138
+ results.push(msg);
139
+ const elapsedMs = Date.now() - startedAt;
140
+ logError(`${prefix} done in ${elapsedMs}ms with ERROR: ${clip(oneLine(msg))}`, { message: msg });
141
+ }
142
+ else {
143
+ const result = await tool.execute(call.args);
144
+ results.push(result);
145
+ const elapsedMs = Date.now() - startedAt;
146
+ log(`${prefix} done in ${elapsedMs}ms`, formatToolResultLog(result));
147
+ }
148
+ }
149
+ catch (err) {
150
+ const msg = `Tool error: ${formatErrorMessage(err)}`;
151
+ results.push(msg);
152
+ const elapsedMs = Date.now() - startedAt;
153
+ logError(`${prefix} done in ${elapsedMs}ms with ERROR: ${clip(oneLine(msg))}`, { message: msg, details: err });
154
+ }
155
+ }
156
+ return results;
157
+ }
158
+ async loadHistory() {
159
+ try {
160
+ const fs = await import('fs/promises');
161
+ const path = await import('path');
162
+ const raw = await fs.readFile(path.join(this.cfg.stateDir, 'history.json'), 'utf-8');
163
+ this.history = sanitizeHistory(JSON.parse(raw));
164
+ log(`history restored (${this.history.length} messages)`);
165
+ return this.history.length > 0;
166
+ }
167
+ catch {
168
+ log('history is empty; starting fresh conversation');
169
+ return false;
170
+ }
171
+ }
172
+ async saveHistory() {
173
+ try {
174
+ const fs = await import('fs/promises');
175
+ const path = await import('path');
176
+ await fs.mkdir(this.cfg.stateDir, { recursive: true });
177
+ await fs.writeFile(path.join(this.cfg.stateDir, 'history.json'), JSON.stringify(this.history));
178
+ }
179
+ catch {
180
+ // History persistence is best-effort.
181
+ }
182
+ }
183
+ async hasConsciousness() {
184
+ try {
185
+ const minds = await workerApi.listMinds();
186
+ return minds.some((m) => m.path !== 'system' &&
187
+ m.path !== 'memory' &&
188
+ m.updatedBy === 'worker');
189
+ }
190
+ catch {
191
+ return false;
192
+ }
193
+ }
194
+ buildStartupMessage(hasPriorHistory, hasConsciousness) {
195
+ const time = formatTime();
196
+ if (hasPriorHistory) {
197
+ return `[Agent restarted at ${time} — resuming from prior conversation]`;
198
+ }
199
+ if (hasConsciousness) {
200
+ return [
201
+ `[Agent started at ${time} — no conversation history, but mind entries are present]`,
202
+ '',
203
+ '[You\'ve been restarted on a fresh context. Your mind entries contain your identity and knowledge. Review your journal and use mind_read to orient yourself, then check channels for anything you missed.]',
204
+ ].join('\n');
205
+ }
206
+ return [
207
+ `[Agent started at ${time} — first run, no history or consciousness]`,
208
+ '',
209
+ '[You\'re starting fresh. Create your consciousness using mind_write (identity, relationships, operations, learnings, journal), then introduce yourself on your connected channels and collaborate to understand your role and priorities.]',
210
+ ].join('\n');
211
+ }
212
+ emergencyStop(error) {
213
+ const message = error instanceof Error ? error.stack || error.message : String(error);
214
+ logError(`fatal runtime error: ${message}`);
215
+ }
216
+ }
217
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { WebClient } from '@slack/web-api';
2
+ export class SlackChannelAdapter {
3
+ client;
4
+ cursors = new Map();
5
+ events = new Map();
6
+ constructor(token) {
7
+ this.client = new WebClient(token);
8
+ }
9
+ async poll() {
10
+ const channels = await this.listChannels();
11
+ const messages = [];
12
+ for (const channelId of channels) {
13
+ const newest = this.cursors.get(channelId);
14
+ const result = await this.client.conversations.history({
15
+ channel: channelId,
16
+ oldest: newest,
17
+ limit: 50,
18
+ });
19
+ for (const msg of result.messages ?? []) {
20
+ if (!msg.ts || !msg.text || !msg.user)
21
+ continue;
22
+ if (newest && msg.ts === newest)
23
+ continue;
24
+ const eventId = `${channelId}:${msg.ts}`;
25
+ const threadTs = msg.thread_ts ?? undefined;
26
+ this.events.set(eventId, { channelId, threadTs, ts: msg.ts });
27
+ messages.push({
28
+ eventId,
29
+ channel: 'slack',
30
+ conversationId: channelId,
31
+ userId: msg.user,
32
+ text: msg.text,
33
+ receivedAt: new Date().toISOString(),
34
+ replyContext: { eventId, channelId, threadTs: threadTs ?? '' },
35
+ });
36
+ }
37
+ const latestTs = result.messages?.[0]?.ts;
38
+ if (latestTs)
39
+ this.cursors.set(channelId, latestTs);
40
+ }
41
+ return messages.sort((a, b) => (a.replyContext?.eventId ?? '').localeCompare(b.replyContext?.eventId ?? ''));
42
+ }
43
+ async sendReply(message, text) {
44
+ const eventId = message.replyContext?.eventId;
45
+ const route = eventId ? this.events.get(eventId) : null;
46
+ const channel = route?.channelId ?? message.replyContext?.channelId ?? message.conversationId;
47
+ const threadTs = route?.threadTs || message.replyContext?.threadTs || undefined;
48
+ await this.client.chat.postMessage({
49
+ channel,
50
+ text,
51
+ thread_ts: threadTs || undefined,
52
+ });
53
+ }
54
+ async sendToConversation(conversationId, text) {
55
+ await this.client.chat.postMessage({
56
+ channel: conversationId,
57
+ text,
58
+ });
59
+ }
60
+ async addReaction(message, emoji) {
61
+ const eventId = message.replyContext?.eventId;
62
+ const route = eventId ? this.events.get(eventId) : null;
63
+ const channel = route?.channelId ?? message.replyContext?.channelId ?? message.conversationId;
64
+ const ts = route?.ts ?? message.replyContext?.eventId?.split(':')[1];
65
+ if (!ts)
66
+ throw new Error('Cannot determine message timestamp for reaction');
67
+ await this.client.reactions.add({
68
+ channel,
69
+ timestamp: ts,
70
+ name: emoji,
71
+ });
72
+ }
73
+ async getHistory(conversationId, limit) {
74
+ const result = await this.client.conversations.history({
75
+ channel: conversationId,
76
+ limit,
77
+ });
78
+ const entries = [];
79
+ for (const msg of result.messages ?? []) {
80
+ if (!msg.ts || !msg.user)
81
+ continue;
82
+ entries.push({
83
+ ts: msg.ts,
84
+ userId: msg.user,
85
+ text: msg.text ?? '',
86
+ });
87
+ }
88
+ return entries.reverse();
89
+ }
90
+ async listChannels() {
91
+ const ids = [];
92
+ let cursor;
93
+ do {
94
+ const res = await this.client.conversations.list({
95
+ types: 'public_channel,private_channel,im,mpim',
96
+ limit: 200,
97
+ exclude_archived: true,
98
+ cursor,
99
+ });
100
+ for (const ch of res.channels ?? []) {
101
+ if (!ch.id || !ch.is_member)
102
+ continue;
103
+ ids.push(ch.id);
104
+ }
105
+ cursor = res.response_metadata?.next_cursor || undefined;
106
+ } while (cursor);
107
+ return ids;
108
+ }
109
+ }
110
+ //# sourceMappingURL=slack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack.js","sourceRoot":"","sources":["../../src/channels/slack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ export class TelegramChannelAdapter {
2
+ token;
3
+ allowedUserId;
4
+ offset = 0;
5
+ constructor(token, allowedUserId) {
6
+ this.token = token;
7
+ this.allowedUserId = allowedUserId;
8
+ }
9
+ async poll() {
10
+ const response = await fetch(this.url('getUpdates'), {
11
+ method: 'POST',
12
+ headers: { 'Content-Type': 'application/json' },
13
+ body: JSON.stringify({
14
+ offset: this.offset,
15
+ timeout: 1,
16
+ allowed_updates: ['message'],
17
+ }),
18
+ });
19
+ const payload = (await response.json());
20
+ if (!payload.ok)
21
+ return [];
22
+ const out = [];
23
+ for (const update of payload.result) {
24
+ this.offset = Math.max(this.offset, update.update_id + 1);
25
+ const message = update.message;
26
+ const fromId = String(message?.from?.id ?? '');
27
+ const chatId = String(message?.chat?.id ?? '');
28
+ if (!message?.text || !chatId || fromId !== this.allowedUserId)
29
+ continue;
30
+ out.push({
31
+ eventId: `tg_${update.update_id}`,
32
+ channel: 'telegram',
33
+ conversationId: chatId,
34
+ userId: fromId,
35
+ text: message.text,
36
+ receivedAt: new Date().toISOString(),
37
+ replyContext: {
38
+ chatId,
39
+ messageId: String(message.message_id),
40
+ },
41
+ });
42
+ }
43
+ return out;
44
+ }
45
+ async sendReply(message, text) {
46
+ const chatId = message.replyContext?.chatId ?? message.conversationId;
47
+ await fetch(this.url('sendMessage'), {
48
+ method: 'POST',
49
+ headers: { 'Content-Type': 'application/json' },
50
+ body: JSON.stringify({
51
+ chat_id: chatId,
52
+ text,
53
+ reply_to_message_id: message.replyContext?.messageId
54
+ ? Number(message.replyContext.messageId)
55
+ : undefined,
56
+ }),
57
+ });
58
+ }
59
+ async sendToConversation(conversationId, text) {
60
+ await fetch(this.url('sendMessage'), {
61
+ method: 'POST',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify({
64
+ chat_id: conversationId,
65
+ text,
66
+ }),
67
+ });
68
+ }
69
+ url(method) {
70
+ return `https://api.telegram.org/bot${this.token}/${method}`;
71
+ }
72
+ }
73
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/channels/telegram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs/promises';
3
+ import { stdin, stdout } from 'process';
4
+ import { workerApi } from '../client.js';
5
+ function usage() {
6
+ stdout.write(`self - introspection CLI
7
+
8
+ Commands:
9
+ self ls
10
+ self get <path>
11
+ self put <path> [--file <file>]
12
+ `);
13
+ }
14
+ async function readStdin() {
15
+ const chunks = [];
16
+ for await (const chunk of stdin) {
17
+ chunks.push(Buffer.from(chunk));
18
+ }
19
+ return Buffer.concat(chunks).toString('utf-8');
20
+ }
21
+ async function main() {
22
+ const [command, arg1, arg2, arg3] = process.argv.slice(2);
23
+ if (!command || command === 'help' || command === '--help' || command === '-h') {
24
+ usage();
25
+ return;
26
+ }
27
+ if (command === 'ls') {
28
+ const items = await workerApi.listMinds();
29
+ stdout.write(`${JSON.stringify(items, null, 2)}\n`);
30
+ return;
31
+ }
32
+ if (command === 'get') {
33
+ if (!arg1) {
34
+ throw new Error('Usage: self get <path>');
35
+ }
36
+ const item = await workerApi.getMind(arg1);
37
+ stdout.write(item.content ?? '');
38
+ if (!item.content.endsWith('\n'))
39
+ stdout.write('\n');
40
+ return;
41
+ }
42
+ if (command === 'put') {
43
+ if (!arg1) {
44
+ throw new Error('Usage: self put <path> [--file <file>]');
45
+ }
46
+ let content = '';
47
+ if (arg2 === '--file' && arg3) {
48
+ content = await fs.readFile(arg3, 'utf-8');
49
+ }
50
+ else {
51
+ content = await readStdin();
52
+ }
53
+ await workerApi.putMind(arg1, content);
54
+ stdout.write('ok\n');
55
+ return;
56
+ }
57
+ throw new Error(`Unknown command: ${command}`);
58
+ }
59
+ main().catch((error) => {
60
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
61
+ process.exit(1);
62
+ });
63
+ //# sourceMappingURL=self.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self.js","sourceRoot":"","sources":["../../src/cli/self.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,68 @@
1
+ import { runtimeEnv } from './env.js';
2
+ async function request(method, path, body, query) {
3
+ const url = new URL(path, runtimeEnv.backendUrl);
4
+ if (query) {
5
+ for (const [key, value] of Object.entries(query)) {
6
+ url.searchParams.set(key, value);
7
+ }
8
+ }
9
+ const response = await fetch(url, {
10
+ method,
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ 'x-agent-id': runtimeEnv.agentId,
14
+ 'x-agent-token': runtimeEnv.agentToken,
15
+ },
16
+ body: body ? JSON.stringify(body) : undefined,
17
+ });
18
+ if (!response.ok) {
19
+ let details = '';
20
+ try {
21
+ const json = (await response.json());
22
+ details = json.message ?? '';
23
+ }
24
+ catch {
25
+ details = await response.text().catch(() => '');
26
+ }
27
+ throw new Error(`HTTP ${response.status}${details ? `: ${details}` : ''}`);
28
+ }
29
+ if (response.status === 204) {
30
+ return null;
31
+ }
32
+ return (await response.json());
33
+ }
34
+ export const workerApi = {
35
+ register() {
36
+ return request('POST', '/worker/register');
37
+ },
38
+ heartbeat(kind, message, meta) {
39
+ return request('POST', '/worker/heartbeat', { kind, message, meta });
40
+ },
41
+ listMinds() {
42
+ return request('GET', '/worker/minds');
43
+ },
44
+ getMind(path) {
45
+ return request('GET', '/worker/mind', undefined, { path });
46
+ },
47
+ putMind(path, content) {
48
+ return request('PUT', '/worker/mind', { content }, { path });
49
+ },
50
+ async getAllMinds() {
51
+ const list = await this.listMinds();
52
+ const minds = new Map();
53
+ const entries = await Promise.all(list.map(async (item) => {
54
+ const mind = await this.getMind(item.path);
55
+ return { path: item.path, content: mind.content };
56
+ }));
57
+ for (const entry of entries) {
58
+ if (entry.content.trim()) {
59
+ minds.set(entry.path, entry.content.trim());
60
+ }
61
+ }
62
+ return minds;
63
+ },
64
+ getConfig() {
65
+ return request('GET', '/worker/config');
66
+ },
67
+ };
68
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/env.js ADDED
@@ -0,0 +1,16 @@
1
+ function required(name) {
2
+ const value = process.env[name];
3
+ if (!value) {
4
+ throw new Error(`Missing environment variable: ${name}`);
5
+ }
6
+ return value;
7
+ }
8
+ export const runtimeEnv = {
9
+ backendUrl: required('BRIAN_BACKEND_URL'),
10
+ agentId: required('BRIAN_AGENT_ID'),
11
+ agentToken: required('BRIAN_AGENT_TOKEN'),
12
+ stateDir: process.env.BRIAN_STATE_DIR ?? `${process.env.HOME}/.brian`,
13
+ heartbeatSeconds: Number(process.env.BRIAN_HEARTBEAT_SECONDS ?? '30'),
14
+ pollSeconds: Number(process.env.BRIAN_POLL_SECONDS ?? '5'),
15
+ };
16
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,UAAU,EAAE,QAAQ,CAAC,mBAAmB,CAAC;IACzC,OAAO,EAAE,QAAQ,CAAC,gBAAgB,CAAC;IACnC,UAAU,EAAE,QAAQ,CAAC,mBAAmB,CAAC;IACzC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,SAAS;IACrE,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,IAAI,CAAC;IACrE,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,GAAG,CAAC;CAC3D,CAAC"}
package/dist/logs.js ADDED
@@ -0,0 +1,32 @@
1
+ const MAX_CLIP_CHARS = 200;
2
+ export function log(message, detail) {
3
+ const prefix = `[${new Date().toISOString()}] `;
4
+ if (detail === undefined) {
5
+ process.stdout.write(`${prefix}${message}\n`);
6
+ }
7
+ else {
8
+ const detailStr = typeof detail === 'string' ? detail : JSON.stringify(detail);
9
+ process.stdout.write(`${prefix}${message}: ${clip(oneLine(detailStr))}\n`);
10
+ }
11
+ }
12
+ export function logError(message, context) {
13
+ const prefix = `[${new Date().toISOString()}] `;
14
+ process.stderr.write(`${prefix}${message}\n`);
15
+ if (context?.error && context.error instanceof Error && context.error.stack) {
16
+ process.stderr.write(`${prefix} ${context.error.stack}\n`);
17
+ }
18
+ }
19
+ export function clip(text, maxChars = MAX_CLIP_CHARS) {
20
+ if (text.length <= maxChars)
21
+ return text;
22
+ return text.slice(0, maxChars) + '…';
23
+ }
24
+ export function oneLine(text) {
25
+ return text.replace(/\n/g, '\\n');
26
+ }
27
+ export function formatErrorMessage(error) {
28
+ if (error instanceof Error)
29
+ return error.message;
30
+ return String(error);
31
+ }
32
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../src/logs.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,MAAgB;IACnD,MAAM,MAAM,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;IAChD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,OAAO,IAAI,CAAC,CAAC;IAChD,CAAC;SAAM,CAAC;QACN,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC/E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,OAAkE;IAC1G,MAAM,MAAM,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;IAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,OAAO,IAAI,CAAC,CAAC;IAC9C,IAAI,OAAO,EAAE,KAAK,IAAI,OAAO,CAAC,KAAK,YAAY,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAC5E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY,EAAE,QAAQ,GAAG,cAAc;IAC1D,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,GAAG,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IACjD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}