@artemiskit/core 0.1.2

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.
Files changed (127) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/adapters/factory.d.ts +23 -0
  3. package/dist/adapters/factory.d.ts.map +1 -0
  4. package/dist/adapters/index.d.ts +7 -0
  5. package/dist/adapters/index.d.ts.map +1 -0
  6. package/dist/adapters/registry.d.ts +56 -0
  7. package/dist/adapters/registry.d.ts.map +1 -0
  8. package/dist/adapters/types.d.ts +151 -0
  9. package/dist/adapters/types.d.ts.map +1 -0
  10. package/dist/artifacts/index.d.ts +6 -0
  11. package/dist/artifacts/index.d.ts.map +1 -0
  12. package/dist/artifacts/manifest.d.ts +19 -0
  13. package/dist/artifacts/manifest.d.ts.map +1 -0
  14. package/dist/artifacts/types.d.ts +368 -0
  15. package/dist/artifacts/types.d.ts.map +1 -0
  16. package/dist/evaluators/contains.d.ts +10 -0
  17. package/dist/evaluators/contains.d.ts.map +1 -0
  18. package/dist/evaluators/exact.d.ts +10 -0
  19. package/dist/evaluators/exact.d.ts.map +1 -0
  20. package/dist/evaluators/fuzzy.d.ts +10 -0
  21. package/dist/evaluators/fuzzy.d.ts.map +1 -0
  22. package/dist/evaluators/index.d.ts +24 -0
  23. package/dist/evaluators/index.d.ts.map +1 -0
  24. package/dist/evaluators/json-schema.d.ts +11 -0
  25. package/dist/evaluators/json-schema.d.ts.map +1 -0
  26. package/dist/evaluators/llm-grader.d.ts +11 -0
  27. package/dist/evaluators/llm-grader.d.ts.map +1 -0
  28. package/dist/evaluators/regex.d.ts +10 -0
  29. package/dist/evaluators/regex.d.ts.map +1 -0
  30. package/dist/evaluators/types.d.ts +29 -0
  31. package/dist/evaluators/types.d.ts.map +1 -0
  32. package/dist/index.d.ts +14 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +26021 -0
  35. package/dist/provenance/environment.d.ts +12 -0
  36. package/dist/provenance/environment.d.ts.map +1 -0
  37. package/dist/provenance/git.d.ts +9 -0
  38. package/dist/provenance/git.d.ts.map +1 -0
  39. package/dist/provenance/index.d.ts +6 -0
  40. package/dist/provenance/index.d.ts.map +1 -0
  41. package/dist/redaction/index.d.ts +3 -0
  42. package/dist/redaction/index.d.ts.map +1 -0
  43. package/dist/redaction/redactor.d.ts +79 -0
  44. package/dist/redaction/redactor.d.ts.map +1 -0
  45. package/dist/redaction/types.d.ts +120 -0
  46. package/dist/redaction/types.d.ts.map +1 -0
  47. package/dist/runner/executor.d.ts +11 -0
  48. package/dist/runner/executor.d.ts.map +1 -0
  49. package/dist/runner/index.d.ts +7 -0
  50. package/dist/runner/index.d.ts.map +1 -0
  51. package/dist/runner/runner.d.ts +13 -0
  52. package/dist/runner/runner.d.ts.map +1 -0
  53. package/dist/runner/types.d.ts +57 -0
  54. package/dist/runner/types.d.ts.map +1 -0
  55. package/dist/scenario/index.d.ts +7 -0
  56. package/dist/scenario/index.d.ts.map +1 -0
  57. package/dist/scenario/parser.d.ts +17 -0
  58. package/dist/scenario/parser.d.ts.map +1 -0
  59. package/dist/scenario/schema.d.ts +945 -0
  60. package/dist/scenario/schema.d.ts.map +1 -0
  61. package/dist/scenario/variables.d.ts +19 -0
  62. package/dist/scenario/variables.d.ts.map +1 -0
  63. package/dist/storage/factory.d.ts +13 -0
  64. package/dist/storage/factory.d.ts.map +1 -0
  65. package/dist/storage/index.d.ts +8 -0
  66. package/dist/storage/index.d.ts.map +1 -0
  67. package/dist/storage/local.d.ts +20 -0
  68. package/dist/storage/local.d.ts.map +1 -0
  69. package/dist/storage/supabase.d.ts +21 -0
  70. package/dist/storage/supabase.d.ts.map +1 -0
  71. package/dist/storage/types.d.ts +86 -0
  72. package/dist/storage/types.d.ts.map +1 -0
  73. package/dist/utils/errors.d.ts +25 -0
  74. package/dist/utils/errors.d.ts.map +1 -0
  75. package/dist/utils/index.d.ts +6 -0
  76. package/dist/utils/index.d.ts.map +1 -0
  77. package/dist/utils/logger.d.ts +21 -0
  78. package/dist/utils/logger.d.ts.map +1 -0
  79. package/package.json +56 -0
  80. package/src/adapters/factory.ts +75 -0
  81. package/src/adapters/index.ts +7 -0
  82. package/src/adapters/registry.ts +143 -0
  83. package/src/adapters/types.ts +184 -0
  84. package/src/artifacts/index.ts +6 -0
  85. package/src/artifacts/manifest.test.ts +206 -0
  86. package/src/artifacts/manifest.ts +136 -0
  87. package/src/artifacts/types.ts +426 -0
  88. package/src/evaluators/contains.test.ts +58 -0
  89. package/src/evaluators/contains.ts +41 -0
  90. package/src/evaluators/exact.test.ts +48 -0
  91. package/src/evaluators/exact.ts +33 -0
  92. package/src/evaluators/fuzzy.test.ts +50 -0
  93. package/src/evaluators/fuzzy.ts +39 -0
  94. package/src/evaluators/index.ts +53 -0
  95. package/src/evaluators/json-schema.ts +98 -0
  96. package/src/evaluators/llm-grader.ts +100 -0
  97. package/src/evaluators/regex.test.ts +73 -0
  98. package/src/evaluators/regex.ts +43 -0
  99. package/src/evaluators/types.ts +37 -0
  100. package/src/index.ts +31 -0
  101. package/src/provenance/environment.ts +18 -0
  102. package/src/provenance/git.ts +48 -0
  103. package/src/provenance/index.ts +6 -0
  104. package/src/redaction/index.ts +23 -0
  105. package/src/redaction/redactor.test.ts +258 -0
  106. package/src/redaction/redactor.ts +246 -0
  107. package/src/redaction/types.ts +135 -0
  108. package/src/runner/executor.ts +251 -0
  109. package/src/runner/index.ts +7 -0
  110. package/src/runner/runner.ts +153 -0
  111. package/src/runner/types.ts +60 -0
  112. package/src/scenario/index.ts +7 -0
  113. package/src/scenario/parser.test.ts +99 -0
  114. package/src/scenario/parser.ts +108 -0
  115. package/src/scenario/schema.ts +176 -0
  116. package/src/scenario/variables.test.ts +150 -0
  117. package/src/scenario/variables.ts +60 -0
  118. package/src/storage/factory.ts +52 -0
  119. package/src/storage/index.ts +8 -0
  120. package/src/storage/local.test.ts +165 -0
  121. package/src/storage/local.ts +194 -0
  122. package/src/storage/supabase.ts +151 -0
  123. package/src/storage/types.ts +98 -0
  124. package/src/utils/errors.ts +76 -0
  125. package/src/utils/index.ts +6 -0
  126. package/src/utils/logger.ts +59 -0
  127. package/tsconfig.json +13 -0
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Local filesystem storage adapter
3
+ */
4
+
5
+ import { mkdir, readFile, readdir, unlink, writeFile } from 'node:fs/promises';
6
+ import { join, resolve } from 'node:path';
7
+ import type { AnyManifest, RedTeamManifest, RunManifest, StressManifest } from '../artifacts/types';
8
+ import type { ComparisonResult, ListOptions, RunListItem, StorageAdapter } from './types';
9
+
10
+ /**
11
+ * Get manifest type from a manifest object
12
+ */
13
+ function getManifestType(manifest: AnyManifest): 'run' | 'redteam' | 'stress' {
14
+ if ('type' in manifest) {
15
+ if (manifest.type === 'redteam') return 'redteam';
16
+ if (manifest.type === 'stress') return 'stress';
17
+ }
18
+ return 'run';
19
+ }
20
+
21
+ /**
22
+ * Get success/defense rate from any manifest type
23
+ */
24
+ function getSuccessRate(manifest: AnyManifest): number {
25
+ const type = getManifestType(manifest);
26
+ if (type === 'redteam') {
27
+ return (manifest as RedTeamManifest).metrics.defense_rate;
28
+ }
29
+ if (type === 'stress') {
30
+ return (manifest as StressManifest).metrics.success_rate;
31
+ }
32
+ return (manifest as RunManifest).metrics.success_rate;
33
+ }
34
+
35
+ /**
36
+ * Get scenario name from any manifest type
37
+ */
38
+ function getScenario(manifest: AnyManifest): string {
39
+ return manifest.config.scenario;
40
+ }
41
+
42
+ export class LocalStorageAdapter implements StorageAdapter {
43
+ private basePath: string;
44
+
45
+ constructor(basePath = './artemis-runs') {
46
+ this.basePath = resolve(basePath);
47
+ }
48
+
49
+ async save(manifest: AnyManifest): Promise<string> {
50
+ const dir = join(this.basePath, manifest.project);
51
+ await mkdir(dir, { recursive: true });
52
+
53
+ const filePath = join(dir, `${manifest.run_id}.json`);
54
+ await writeFile(filePath, JSON.stringify(manifest, null, 2));
55
+
56
+ return filePath;
57
+ }
58
+
59
+ async load(runId: string): Promise<AnyManifest> {
60
+ const projects = await this.listDirectories(this.basePath);
61
+
62
+ for (const project of projects) {
63
+ const filePath = join(this.basePath, project, `${runId}.json`);
64
+ try {
65
+ const content = await readFile(filePath, 'utf-8');
66
+ return JSON.parse(content);
67
+ } catch {}
68
+ }
69
+
70
+ throw new Error(`Run not found: ${runId}`);
71
+ }
72
+
73
+ async loadRun(runId: string): Promise<RunManifest> {
74
+ const manifest = await this.load(runId);
75
+ if (getManifestType(manifest) !== 'run') {
76
+ throw new Error(`Run ${runId} is not a standard run manifest`);
77
+ }
78
+ return manifest as RunManifest;
79
+ }
80
+
81
+ async loadRedTeam(runId: string): Promise<RedTeamManifest> {
82
+ const manifest = await this.load(runId);
83
+ if (getManifestType(manifest) !== 'redteam') {
84
+ throw new Error(`Run ${runId} is not a red team manifest`);
85
+ }
86
+ return manifest as RedTeamManifest;
87
+ }
88
+
89
+ async loadStress(runId: string): Promise<StressManifest> {
90
+ const manifest = await this.load(runId);
91
+ if (getManifestType(manifest) !== 'stress') {
92
+ throw new Error(`Run ${runId} is not a stress test manifest`);
93
+ }
94
+ return manifest as StressManifest;
95
+ }
96
+
97
+ async list(options?: ListOptions): Promise<RunListItem[]> {
98
+ const results: RunListItem[] = [];
99
+
100
+ const projects = options?.project
101
+ ? [options.project]
102
+ : await this.listDirectories(this.basePath);
103
+
104
+ for (const project of projects) {
105
+ const projectDir = join(this.basePath, project);
106
+ const files = await this.listFiles(projectDir);
107
+
108
+ for (const file of files) {
109
+ if (!file.endsWith('.json')) continue;
110
+
111
+ try {
112
+ const content = await readFile(join(projectDir, file), 'utf-8');
113
+ const manifest: AnyManifest = JSON.parse(content);
114
+ const manifestType = getManifestType(manifest);
115
+
116
+ // Filter by type if specified
117
+ if (options?.type && manifestType !== options.type) {
118
+ continue;
119
+ }
120
+
121
+ if (options?.scenario && getScenario(manifest) !== options.scenario) {
122
+ continue;
123
+ }
124
+
125
+ results.push({
126
+ runId: manifest.run_id,
127
+ scenario: getScenario(manifest),
128
+ successRate: getSuccessRate(manifest),
129
+ createdAt: manifest.start_time,
130
+ type: manifestType,
131
+ });
132
+ } catch {}
133
+ }
134
+ }
135
+
136
+ results.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
137
+
138
+ const offset = options?.offset || 0;
139
+ const limit = options?.limit;
140
+
141
+ if (limit) {
142
+ return results.slice(offset, offset + limit);
143
+ }
144
+
145
+ return results.slice(offset);
146
+ }
147
+
148
+ async delete(runId: string): Promise<void> {
149
+ const projects = await this.listDirectories(this.basePath);
150
+
151
+ for (const project of projects) {
152
+ const filePath = join(this.basePath, project, `${runId}.json`);
153
+ try {
154
+ await unlink(filePath);
155
+ return;
156
+ } catch {}
157
+ }
158
+ }
159
+
160
+ async compare(baselineId: string, currentId: string): Promise<ComparisonResult> {
161
+ const [baseline, current] = await Promise.all([
162
+ this.loadRun(baselineId),
163
+ this.loadRun(currentId),
164
+ ]);
165
+
166
+ return {
167
+ baseline,
168
+ current,
169
+ delta: {
170
+ successRate: current.metrics.success_rate - baseline.metrics.success_rate,
171
+ latency: current.metrics.median_latency_ms - baseline.metrics.median_latency_ms,
172
+ tokens: current.metrics.total_tokens - baseline.metrics.total_tokens,
173
+ },
174
+ };
175
+ }
176
+
177
+ private async listDirectories(path: string): Promise<string[]> {
178
+ try {
179
+ const entries = await readdir(path, { withFileTypes: true });
180
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
181
+ } catch {
182
+ return [];
183
+ }
184
+ }
185
+
186
+ private async listFiles(path: string): Promise<string[]> {
187
+ try {
188
+ const entries = await readdir(path, { withFileTypes: true });
189
+ return entries.filter((e) => e.isFile()).map((e) => e.name);
190
+ } catch {
191
+ return [];
192
+ }
193
+ }
194
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Supabase storage adapter
3
+ */
4
+
5
+ import { type SupabaseClient, createClient } from '@supabase/supabase-js';
6
+ import type { RunManifest } from '../artifacts/types';
7
+ import type { ComparisonResult, ListOptions, RunListItem, StorageAdapter } from './types';
8
+
9
+ export interface SupabaseStorageConfig {
10
+ url: string;
11
+ anonKey: string;
12
+ bucket?: string;
13
+ }
14
+
15
+ export class SupabaseStorageAdapter implements StorageAdapter {
16
+ private client: SupabaseClient;
17
+ private bucket: string;
18
+
19
+ constructor(config: SupabaseStorageConfig) {
20
+ this.client = createClient(config.url, config.anonKey);
21
+ this.bucket = config.bucket || 'artemis-runs';
22
+ }
23
+
24
+ async save(manifest: RunManifest): Promise<string> {
25
+ const filePath = `${manifest.project}/${manifest.run_id}.json`;
26
+
27
+ const { error: uploadError } = await this.client.storage
28
+ .from(this.bucket)
29
+ .upload(filePath, JSON.stringify(manifest, null, 2), {
30
+ contentType: 'application/json',
31
+ upsert: true,
32
+ });
33
+
34
+ if (uploadError) {
35
+ throw new Error(`Failed to upload manifest: ${uploadError.message}`);
36
+ }
37
+
38
+ const { error: dbError } = await this.client.from('runs').upsert({
39
+ run_id: manifest.run_id,
40
+ project: manifest.project,
41
+ scenario: manifest.config.scenario,
42
+ provider: manifest.config.provider,
43
+ model: manifest.config.model,
44
+ success_rate: manifest.metrics.success_rate,
45
+ total_cases: manifest.metrics.total_cases,
46
+ passed_cases: manifest.metrics.passed_cases,
47
+ failed_cases: manifest.metrics.failed_cases,
48
+ median_latency_ms: manifest.metrics.median_latency_ms,
49
+ p95_latency_ms: manifest.metrics.p95_latency_ms,
50
+ total_tokens: manifest.metrics.total_tokens,
51
+ git_commit: manifest.git.commit,
52
+ git_branch: manifest.git.branch,
53
+ git_dirty: manifest.git.dirty,
54
+ run_by: manifest.provenance.run_by,
55
+ run_reason: manifest.provenance.run_reason,
56
+ started_at: manifest.start_time,
57
+ ended_at: manifest.end_time,
58
+ manifest_path: filePath,
59
+ });
60
+
61
+ if (dbError) {
62
+ throw new Error(`Failed to save run metadata: ${dbError.message}`);
63
+ }
64
+
65
+ return filePath;
66
+ }
67
+
68
+ async load(runId: string): Promise<RunManifest> {
69
+ const { data: run, error: dbError } = await this.client
70
+ .from('runs')
71
+ .select('manifest_path')
72
+ .eq('run_id', runId)
73
+ .single();
74
+
75
+ if (dbError || !run) {
76
+ throw new Error(`Run not found: ${runId}`);
77
+ }
78
+
79
+ const { data, error: downloadError } = await this.client.storage
80
+ .from(this.bucket)
81
+ .download(run.manifest_path);
82
+
83
+ if (downloadError || !data) {
84
+ throw new Error(`Failed to download manifest: ${downloadError?.message}`);
85
+ }
86
+
87
+ const text = await data.text();
88
+ return JSON.parse(text);
89
+ }
90
+
91
+ async list(options?: ListOptions): Promise<RunListItem[]> {
92
+ let query = this.client
93
+ .from('runs')
94
+ .select('run_id, scenario, success_rate, started_at')
95
+ .order('started_at', { ascending: false });
96
+
97
+ if (options?.project) {
98
+ query = query.eq('project', options.project);
99
+ }
100
+ if (options?.scenario) {
101
+ query = query.eq('scenario', options.scenario);
102
+ }
103
+ if (options?.limit) {
104
+ query = query.limit(options.limit);
105
+ }
106
+ if (options?.offset) {
107
+ query = query.range(options.offset, options.offset + (options.limit || 10) - 1);
108
+ }
109
+
110
+ const { data, error } = await query;
111
+
112
+ if (error) {
113
+ throw new Error(`Failed to list runs: ${error.message}`);
114
+ }
115
+
116
+ return (data || []).map((r) => ({
117
+ runId: r.run_id,
118
+ scenario: r.scenario,
119
+ successRate: r.success_rate,
120
+ createdAt: r.started_at,
121
+ }));
122
+ }
123
+
124
+ async delete(runId: string): Promise<void> {
125
+ const { data: run } = await this.client
126
+ .from('runs')
127
+ .select('manifest_path')
128
+ .eq('run_id', runId)
129
+ .single();
130
+
131
+ if (run) {
132
+ await this.client.storage.from(this.bucket).remove([run.manifest_path]);
133
+ }
134
+
135
+ await this.client.from('runs').delete().eq('run_id', runId);
136
+ }
137
+
138
+ async compare(baselineId: string, currentId: string): Promise<ComparisonResult> {
139
+ const [baseline, current] = await Promise.all([this.load(baselineId), this.load(currentId)]);
140
+
141
+ return {
142
+ baseline,
143
+ current,
144
+ delta: {
145
+ successRate: current.metrics.success_rate - baseline.metrics.success_rate,
146
+ latency: current.metrics.median_latency_ms - baseline.metrics.median_latency_ms,
147
+ tokens: current.metrics.total_tokens - baseline.metrics.total_tokens,
148
+ },
149
+ };
150
+ }
151
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Storage types and interfaces
3
+ */
4
+
5
+ import type { AnyManifest, RedTeamManifest, RunManifest, StressManifest } from '../artifacts/types';
6
+
7
+ /**
8
+ * Run listing item
9
+ */
10
+ export interface RunListItem {
11
+ runId: string;
12
+ scenario: string;
13
+ successRate: number;
14
+ createdAt: string;
15
+ /** Type of manifest (run, redteam, stress) */
16
+ type?: 'run' | 'redteam' | 'stress';
17
+ }
18
+
19
+ /**
20
+ * Comparison result between two runs
21
+ */
22
+ export interface ComparisonResult {
23
+ baseline: RunManifest;
24
+ current: RunManifest;
25
+ delta: {
26
+ successRate: number;
27
+ latency: number;
28
+ tokens: number;
29
+ };
30
+ }
31
+
32
+ /**
33
+ * List options for filtering runs
34
+ */
35
+ export interface ListOptions {
36
+ project?: string;
37
+ scenario?: string;
38
+ limit?: number;
39
+ offset?: number;
40
+ /** Filter by manifest type */
41
+ type?: 'run' | 'redteam' | 'stress';
42
+ }
43
+
44
+ /**
45
+ * Storage adapter interface - implement to create custom storage backends
46
+ */
47
+ export interface StorageAdapter {
48
+ /**
49
+ * Save a run manifest (any type)
50
+ */
51
+ save(manifest: AnyManifest): Promise<string>;
52
+
53
+ /**
54
+ * Load a run manifest by ID
55
+ */
56
+ load(runId: string): Promise<AnyManifest>;
57
+
58
+ /**
59
+ * Load a standard run manifest by ID
60
+ */
61
+ loadRun?(runId: string): Promise<RunManifest>;
62
+
63
+ /**
64
+ * Load a red team manifest by ID
65
+ */
66
+ loadRedTeam?(runId: string): Promise<RedTeamManifest>;
67
+
68
+ /**
69
+ * Load a stress manifest by ID
70
+ */
71
+ loadStress?(runId: string): Promise<StressManifest>;
72
+
73
+ /**
74
+ * List runs with optional filters
75
+ */
76
+ list(options?: ListOptions): Promise<RunListItem[]>;
77
+
78
+ /**
79
+ * Delete a run
80
+ */
81
+ delete(runId: string): Promise<void>;
82
+
83
+ /**
84
+ * Compare two runs
85
+ */
86
+ compare?(baselineId: string, currentId: string): Promise<ComparisonResult>;
87
+ }
88
+
89
+ /**
90
+ * Storage configuration
91
+ */
92
+ export interface StorageConfig {
93
+ type: 'supabase' | 'local';
94
+ url?: string;
95
+ anonKey?: string;
96
+ bucket?: string;
97
+ basePath?: string;
98
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Error handling utilities for Artemis
3
+ */
4
+
5
+ /**
6
+ * Error codes used throughout Artemis
7
+ */
8
+ export type ArtemisErrorCode =
9
+ | 'UNKNOWN_PROVIDER'
10
+ | 'PROVIDER_UNAVAILABLE'
11
+ | 'SCENARIO_READ_ERROR'
12
+ | 'SCENARIO_PARSE_ERROR'
13
+ | 'SCENARIO_VALIDATION_ERROR'
14
+ | 'ADAPTER_ERROR'
15
+ | 'GENERATION_ERROR'
16
+ | 'EVALUATION_ERROR'
17
+ | 'STORAGE_ERROR'
18
+ | 'CONFIG_ERROR'
19
+ | 'UNKNOWN_ERROR';
20
+
21
+ /**
22
+ * Custom error class for Artemis
23
+ */
24
+ export class ArtemisError extends Error {
25
+ readonly code: ArtemisErrorCode;
26
+ readonly details?: Record<string, unknown>;
27
+
28
+ constructor(
29
+ message: string,
30
+ code: ArtemisErrorCode = 'UNKNOWN_ERROR',
31
+ details?: Record<string, unknown>
32
+ ) {
33
+ super(message);
34
+ this.name = 'ArtemisError';
35
+ this.code = code;
36
+ this.details = details;
37
+
38
+ Error.captureStackTrace(this, this.constructor);
39
+ }
40
+
41
+ toJSON(): Record<string, unknown> {
42
+ return {
43
+ name: this.name,
44
+ code: this.code,
45
+ message: this.message,
46
+ details: this.details,
47
+ stack: this.stack,
48
+ };
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Check if error is an ArtemisError
54
+ */
55
+ export function isArtemisError(error: unknown): error is ArtemisError {
56
+ return error instanceof ArtemisError;
57
+ }
58
+
59
+ /**
60
+ * Wrap unknown errors in ArtemisError
61
+ */
62
+ export function wrapError(
63
+ error: unknown,
64
+ code: ArtemisErrorCode = 'UNKNOWN_ERROR',
65
+ context?: string
66
+ ): ArtemisError {
67
+ if (error instanceof ArtemisError) {
68
+ return error;
69
+ }
70
+
71
+ const message = error instanceof Error ? error.message : String(error);
72
+
73
+ return new ArtemisError(context ? `${context}: ${message}` : message, code, {
74
+ originalError: error,
75
+ });
76
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Utilities module exports
3
+ */
4
+
5
+ export * from './errors';
6
+ export * from './logger';
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Logger utility for Artemis
3
+ */
4
+
5
+ import pino from 'pino';
6
+
7
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
8
+
9
+ const level = process.env.ARTEMIS_LOG_LEVEL || 'info';
10
+
11
+ const baseLogger = pino({
12
+ level,
13
+ transport:
14
+ process.env.NODE_ENV === 'development'
15
+ ? { target: 'pino-pretty', options: { colorize: true } }
16
+ : undefined,
17
+ });
18
+
19
+ /**
20
+ * Logger class for consistent logging across Artemis
21
+ */
22
+ export class Logger {
23
+ private logger: pino.Logger;
24
+
25
+ constructor(name: string) {
26
+ this.logger = baseLogger.child({ name });
27
+ }
28
+
29
+ debug(message: string, data?: Record<string, unknown>): void {
30
+ this.logger.debug(data, message);
31
+ }
32
+
33
+ info(message: string, data?: Record<string, unknown>): void {
34
+ this.logger.info(data, message);
35
+ }
36
+
37
+ warn(message: string, data?: Record<string, unknown>): void {
38
+ this.logger.warn(data, message);
39
+ }
40
+
41
+ error(message: string, error?: Error | unknown, data?: Record<string, unknown>): void {
42
+ const errorData =
43
+ error instanceof Error
44
+ ? { error: { message: error.message, stack: error.stack, name: error.name } }
45
+ : { error };
46
+ this.logger.error({ ...data, ...errorData }, message);
47
+ }
48
+
49
+ child(bindings: Record<string, unknown>): Logger {
50
+ const childLogger = new Logger('');
51
+ childLogger.logger = this.logger.child(bindings);
52
+ return childLogger;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Default logger instance
58
+ */
59
+ export const logger = new Logger('artemis');
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "noEmit": false,
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "emitDeclarationOnly": true
10
+ },
11
+ "include": ["src/**/*"],
12
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
13
+ }