@axiom-lattice/core 2.1.73 → 2.1.74

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/dist/index.js CHANGED
@@ -79,6 +79,7 @@ __export(index_exports, {
79
79
  MicrosandboxRemoteProvider: () => MicrosandboxRemoteProvider,
80
80
  MicrosandboxServiceClient: () => MicrosandboxServiceClient,
81
81
  ModelLatticeManager: () => ModelLatticeManager,
82
+ MysqlDatabase: () => MysqlDatabase,
82
83
  PinoLoggerClient: () => PinoLoggerClient,
83
84
  PostgresDatabase: () => PostgresDatabase,
84
85
  PrometheusClient: () => PrometheusClient,
@@ -143,6 +144,7 @@ __export(index_exports, {
143
144
  ensureBuiltinAgentsForTenant: () => ensureBuiltinAgentsForTenant,
144
145
  eventBus: () => eventBus,
145
146
  eventBusDefault: () => event_bus_default,
147
+ extractFetcherError: () => extractFetcherError,
146
148
  fileDataToString: () => fileDataToString,
147
149
  formatContentWithLineNumbers: () => formatContentWithLineNumbers,
148
150
  formatGrepMatches: () => formatGrepMatches,
@@ -3284,6 +3286,168 @@ var PostgresDatabase = class {
3284
3286
  }
3285
3287
  }
3286
3288
  };
3289
+ var MysqlDatabase = class {
3290
+ constructor(config) {
3291
+ // mysql2.Pool
3292
+ this.connected = false;
3293
+ this.config = config;
3294
+ }
3295
+ async connect() {
3296
+ if (this.connected && this.pool) return;
3297
+ try {
3298
+ const mysql = await import("mysql2/promise");
3299
+ const poolConfig = this.config.connectionString ? { uri: this.config.connectionString } : {
3300
+ host: this.config.host || "localhost",
3301
+ port: this.config.port || 3306,
3302
+ database: this.config.database,
3303
+ user: this.config.user,
3304
+ password: this.config.password,
3305
+ ssl: this.config.ssl ? { rejectUnauthorized: false } : void 0
3306
+ };
3307
+ this.pool = mysql.createPool({
3308
+ ...poolConfig,
3309
+ waitForConnections: true,
3310
+ connectionLimit: 10,
3311
+ maxIdle: 10,
3312
+ idleTimeout: 6e4,
3313
+ queueLimit: 0,
3314
+ enableKeepAlive: true,
3315
+ keepAliveInitialDelay: 0
3316
+ });
3317
+ const connection = await this.pool.getConnection();
3318
+ try {
3319
+ await connection.query("SELECT 1");
3320
+ } finally {
3321
+ connection.release();
3322
+ }
3323
+ this.connected = true;
3324
+ } catch (error) {
3325
+ this.connected = false;
3326
+ throw new Error(`Failed to connect to MySQL: ${error}`);
3327
+ }
3328
+ }
3329
+ async disconnect() {
3330
+ if (this.pool) {
3331
+ try {
3332
+ await this.pool.end();
3333
+ } catch (error) {
3334
+ console.warn("Warning: Error closing MySQL pool:", error);
3335
+ } finally {
3336
+ this.pool = null;
3337
+ this.connected = false;
3338
+ }
3339
+ }
3340
+ }
3341
+ async listTables() {
3342
+ await this.ensureConnected();
3343
+ const query = `
3344
+ SELECT TABLE_NAME, TABLE_SCHEMA
3345
+ FROM information_schema.TABLES
3346
+ WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
3347
+ AND TABLE_SCHEMA = DATABASE()
3348
+ AND TABLE_TYPE = 'BASE TABLE'
3349
+ ORDER BY TABLE_SCHEMA, TABLE_NAME
3350
+ `;
3351
+ const [rows] = await this.pool.query(query);
3352
+ return rows.map((row) => ({
3353
+ name: row.TABLE_NAME,
3354
+ schema: row.TABLE_SCHEMA
3355
+ }));
3356
+ }
3357
+ async getTableInfo(tables) {
3358
+ await this.ensureConnected();
3359
+ const schemas = [];
3360
+ for (const tableName of tables) {
3361
+ const rawName = tableName.includes(".") ? tableName.split(".").pop() : tableName;
3362
+ const columnQuery = `
3363
+ SELECT
3364
+ c.COLUMN_NAME,
3365
+ c.DATA_TYPE,
3366
+ c.IS_NULLABLE,
3367
+ c.COLUMN_DEFAULT
3368
+ FROM information_schema.COLUMNS c
3369
+ WHERE c.TABLE_NAME = ?
3370
+ AND c.TABLE_SCHEMA = DATABASE()
3371
+ ORDER BY c.ORDINAL_POSITION
3372
+ `;
3373
+ const pkQuery = `
3374
+ SELECT k.COLUMN_NAME
3375
+ FROM information_schema.TABLE_CONSTRAINTS t
3376
+ JOIN information_schema.KEY_COLUMN_USAGE k
3377
+ ON t.CONSTRAINT_NAME = k.CONSTRAINT_NAME
3378
+ AND t.TABLE_SCHEMA = k.TABLE_SCHEMA
3379
+ AND t.TABLE_NAME = k.TABLE_NAME
3380
+ WHERE t.CONSTRAINT_TYPE = 'PRIMARY KEY'
3381
+ AND t.TABLE_NAME = ?
3382
+ AND t.TABLE_SCHEMA = DATABASE()
3383
+ `;
3384
+ const fkQuery = `
3385
+ SELECT
3386
+ k.COLUMN_NAME,
3387
+ k.REFERENCED_TABLE_NAME,
3388
+ k.REFERENCED_COLUMN_NAME
3389
+ FROM information_schema.KEY_COLUMN_USAGE k
3390
+ WHERE k.TABLE_NAME = ?
3391
+ AND k.TABLE_SCHEMA = DATABASE()
3392
+ AND k.REFERENCED_TABLE_NAME IS NOT NULL
3393
+ `;
3394
+ const [columnRows] = await this.pool.query(columnQuery, [rawName]);
3395
+ const [pkRows] = await this.pool.query(pkQuery, [rawName]);
3396
+ const [fkRows] = await this.pool.query(fkQuery, [rawName]);
3397
+ const pkColumns = new Set(pkRows.map((r) => r.COLUMN_NAME));
3398
+ const fkMap = /* @__PURE__ */ new Map();
3399
+ for (const row of fkRows) {
3400
+ fkMap.set(row.COLUMN_NAME, {
3401
+ foreignTable: row.REFERENCED_TABLE_NAME,
3402
+ foreignColumn: row.REFERENCED_COLUMN_NAME
3403
+ });
3404
+ }
3405
+ const columns = columnRows.map((row) => {
3406
+ const fkRef = fkMap.get(row.COLUMN_NAME);
3407
+ return {
3408
+ name: row.COLUMN_NAME,
3409
+ type: row.DATA_TYPE,
3410
+ nullable: row.IS_NULLABLE === "YES",
3411
+ default: row.COLUMN_DEFAULT,
3412
+ isPrimaryKey: pkColumns.has(row.COLUMN_NAME),
3413
+ isForeignKey: fkRef !== void 0,
3414
+ foreignKeyRef: fkRef ? `${fkRef.foreignTable}.${fkRef.foreignColumn}` : void 0
3415
+ };
3416
+ });
3417
+ let sampleRows = [];
3418
+ try {
3419
+ const sampleQuery = `SELECT * FROM \`${rawName}\` LIMIT 3`;
3420
+ const [sampleResult] = await this.pool.query(sampleQuery);
3421
+ sampleRows = sampleResult;
3422
+ } catch {
3423
+ }
3424
+ schemas.push({
3425
+ tableName,
3426
+ columns,
3427
+ sampleRows
3428
+ });
3429
+ }
3430
+ return schemas;
3431
+ }
3432
+ async executeQuery(query) {
3433
+ await this.ensureConnected();
3434
+ const [rows, fields] = await this.pool.query(query);
3435
+ const isSelectResult = Array.isArray(rows);
3436
+ return {
3437
+ rows: isSelectResult ? rows : [],
3438
+ rowCount: isSelectResult ? rows.length : rows.affectedRows || 0,
3439
+ fields: fields?.map((f) => f.name)
3440
+ };
3441
+ }
3442
+ getDatabaseType() {
3443
+ return "mysql";
3444
+ }
3445
+ async ensureConnected() {
3446
+ if (!this.connected) {
3447
+ await this.connect();
3448
+ }
3449
+ }
3450
+ };
3287
3451
  var SqlDatabaseManager = class _SqlDatabaseManager {
3288
3452
  constructor() {
3289
3453
  this.databases = /* @__PURE__ */ new Map();
@@ -3323,7 +3487,8 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
3323
3487
  database = new PostgresDatabase(config);
3324
3488
  break;
3325
3489
  case "mysql":
3326
- throw new Error("MySQL support not yet implemented");
3490
+ database = new MysqlDatabase(config);
3491
+ break;
3327
3492
  case "sqlite":
3328
3493
  throw new Error("SQLite support not yet implemented");
3329
3494
  default:
@@ -5858,7 +6023,7 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
5858
6023
  const tenantId = config.tenantId ?? "default";
5859
6024
  const mapping = this._resolveVolumeForPath(config, tenantId, filePath);
5860
6025
  if (!mapping) return null;
5861
- const client = provider.createVolumeFsClient(mapping.volumeName);
6026
+ const client = provider.createVolumeFsClient(mapping.volumeName, mapping.pathPrefix);
5862
6027
  return new VolumeFilesystem(stripPrefixClient(client, mapping.pathPrefix), mapping.pathPrefix);
5863
6028
  }
5864
6029
  _resolveVolumeForPath(config, tenantId, filePath) {
@@ -21471,7 +21636,7 @@ var MicrosandboxRemoteProvider = class {
21471
21636
  this.instances.delete(name);
21472
21637
  await this.client.deleteSandbox(name);
21473
21638
  }
21474
- createVolumeFsClient(volumeName) {
21639
+ createVolumeFsClient(volumeName, _pathPrefix) {
21475
21640
  return {
21476
21641
  read: (path3) => this.client.volumeFsRead(volumeName, path3),
21477
21642
  write: (path3, content) => this.client.volumeFsWrite(volumeName, path3, content),
@@ -21528,50 +21693,84 @@ var MicrosandboxRemoteProvider = class {
21528
21693
  var import_sandbox23 = require("@agent-infra/sandbox");
21529
21694
 
21530
21695
  // src/sandbox_lattice/RemoteSandboxInstance.ts
21696
+ function extractFetcherError(error) {
21697
+ if (typeof error === "string") {
21698
+ return error;
21699
+ }
21700
+ if (error && typeof error === "object") {
21701
+ const e = error;
21702
+ if (typeof e.reason === "string") {
21703
+ switch (e.reason) {
21704
+ case "status-code":
21705
+ return `HTTP ${e.statusCode}: ${JSON.stringify(e.body)}`;
21706
+ case "non-json":
21707
+ return `HTTP ${e.statusCode}: ${e.rawBody}`;
21708
+ case "timeout":
21709
+ return "Request timed out";
21710
+ case "unknown":
21711
+ return typeof e.errorMessage === "string" ? e.errorMessage : "Unknown error";
21712
+ }
21713
+ }
21714
+ if (typeof e.message === "string") {
21715
+ return e.message;
21716
+ }
21717
+ if (typeof e.error === "string") {
21718
+ return e.error;
21719
+ }
21720
+ return JSON.stringify(error);
21721
+ }
21722
+ return String(error);
21723
+ }
21531
21724
  var RemoteSandboxInstance = class {
21532
- constructor(name, client) {
21725
+ constructor(name, client, workspace) {
21533
21726
  this.client = client;
21727
+ this.workspace = workspace;
21534
21728
  this.file = {
21535
21729
  readFile: async (file) => {
21536
- const result = await this.client.file.readFile({ file });
21730
+ const resolved = this.resolvePath(file);
21731
+ const result = await this.client.file.readFile({ file: resolved });
21537
21732
  if (!result.ok) {
21538
- throw new Error(String(result.error));
21733
+ throw new Error(`readFile failed: ${extractFetcherError(result.error)}`);
21539
21734
  }
21540
21735
  return { content: result.body.data?.content ?? "" };
21541
21736
  },
21542
21737
  writeFile: async (file, content) => {
21543
- const result = await this.client.file.writeFile({ file, content });
21738
+ const resolved = this.resolvePath(file);
21739
+ const result = await this.client.file.writeFile({ file: resolved, content });
21544
21740
  if (!result.ok) {
21545
- throw new Error(String(result.error));
21741
+ throw new Error(`writeFile failed: ${extractFetcherError(result.error)}`);
21546
21742
  }
21547
21743
  },
21548
21744
  listPath: async (path3, options) => {
21745
+ const resolved = this.resolvePath(path3);
21549
21746
  const result = await this.client.file.listPath({
21550
- path: path3,
21747
+ path: resolved,
21551
21748
  recursive: options?.recursive ?? false
21552
21749
  });
21553
21750
  if (!result.ok) {
21554
- throw new Error(String(result.error));
21751
+ throw new Error(`listPath failed: ${extractFetcherError(result.error)}`);
21555
21752
  }
21556
21753
  const files = (result.body.data?.files || []).map((f) => ({
21557
21754
  path: f.path,
21558
- is_dir: f.is_dir ?? false,
21559
- size: f.size,
21755
+ is_dir: f.is_dir === true || f.size === null,
21756
+ size: f.size ?? void 0,
21560
21757
  modified_at: f.modified_at
21561
21758
  }));
21562
21759
  return { files };
21563
21760
  },
21564
21761
  findFiles: async (path3, glob) => {
21565
- const result = await this.client.file.findFiles({ path: path3, glob });
21762
+ const resolved = this.resolvePath(path3);
21763
+ const result = await this.client.file.findFiles({ path: resolved, glob });
21566
21764
  if (!result.ok) {
21567
- throw new Error(String(result.error));
21765
+ throw new Error(`findFiles failed: ${extractFetcherError(result.error)}`);
21568
21766
  }
21569
21767
  return { files: result.body.data?.files || [] };
21570
21768
  },
21571
21769
  searchInFile: async (file, regex) => {
21572
- const result = await this.client.file.searchInFile({ file, regex });
21770
+ const resolved = this.resolvePath(file);
21771
+ const result = await this.client.file.searchInFile({ file: resolved, regex });
21573
21772
  if (!result.ok) {
21574
- throw new Error(String(result.error));
21773
+ throw new Error(`searchInFile failed: ${extractFetcherError(result.error)}`);
21575
21774
  }
21576
21775
  return {
21577
21776
  matches: result.body.data?.matches || [],
@@ -21579,30 +21778,33 @@ var RemoteSandboxInstance = class {
21579
21778
  };
21580
21779
  },
21581
21780
  strReplaceEditor: async (params) => {
21781
+ const resolved = this.resolvePath(params.path);
21582
21782
  const result = await this.client.file.strReplaceEditor({
21583
21783
  command: params.command,
21584
- path: params.path,
21784
+ path: resolved,
21585
21785
  old_str: params.old_str,
21586
21786
  new_str: params.new_str,
21587
21787
  replace_mode: params.replace_mode
21588
21788
  });
21589
21789
  if (!result.ok) {
21590
- throw new Error(String(result.error));
21790
+ throw new Error(`strReplaceEditor failed: ${extractFetcherError(result.error)}`);
21591
21791
  }
21592
21792
  },
21593
21793
  uploadFile: async (params) => {
21794
+ const resolved = this.resolvePath(params.file);
21594
21795
  const result = await this.client.file.uploadFile({
21595
21796
  file: params.data,
21596
- path: params.file
21797
+ path: resolved
21597
21798
  });
21598
21799
  if (!result.ok) {
21599
- throw new Error(String(result.error));
21800
+ throw new Error(`uploadFile failed: ${extractFetcherError(result.error)}`);
21600
21801
  }
21601
21802
  },
21602
21803
  downloadFile: async (params) => {
21603
- const result = await this.client.file.downloadFile({ path: params.file });
21804
+ const resolved = this.resolvePath(params.file);
21805
+ const result = await this.client.file.downloadFile({ path: resolved });
21604
21806
  if (!result.ok) {
21605
- throw new Error(String(result.error));
21807
+ throw new Error(`downloadFile failed: ${extractFetcherError(result.error)}`);
21606
21808
  }
21607
21809
  const buffer2 = await result.body.arrayBuffer();
21608
21810
  return Buffer.from(buffer2);
@@ -21612,11 +21814,11 @@ var RemoteSandboxInstance = class {
21612
21814
  execCommand: async (params) => {
21613
21815
  const result = await this.client.shell.execCommand({
21614
21816
  command: params.command,
21615
- exec_dir: params.exec_dir,
21817
+ exec_dir: params.exec_dir ? this.resolvePath(params.exec_dir) : void 0,
21616
21818
  timeout: params.timeout
21617
21819
  });
21618
21820
  if (!result.ok) {
21619
- throw new Error(String(result.error));
21821
+ throw new Error(`execCommand failed: ${extractFetcherError(result.error)}`);
21620
21822
  }
21621
21823
  return {
21622
21824
  output: result.body.data?.output ?? "",
@@ -21626,6 +21828,15 @@ var RemoteSandboxInstance = class {
21626
21828
  };
21627
21829
  this.name = name;
21628
21830
  }
21831
+ resolvePath(file) {
21832
+ if (file.startsWith(this.workspace)) {
21833
+ return file;
21834
+ }
21835
+ if (!file.startsWith("/")) {
21836
+ return `${this.workspace}/${file}`;
21837
+ }
21838
+ return `${this.workspace}${file}`;
21839
+ }
21629
21840
  async start() {
21630
21841
  }
21631
21842
  async stop() {
@@ -21646,23 +21857,58 @@ var RemoteSandboxInstance = class {
21646
21857
  };
21647
21858
 
21648
21859
  // src/sandbox_lattice/providers/RemoteSandboxProvider.ts
21860
+ var DEFAULT_WORKSPACE = "/home/gem";
21649
21861
  var RemoteSandboxProvider = class {
21650
21862
  constructor(config) {
21651
21863
  this.config = config;
21652
21864
  this.instances = /* @__PURE__ */ new Map();
21865
+ this.creating = /* @__PURE__ */ new Map();
21866
+ this.workspace = DEFAULT_WORKSPACE;
21867
+ this.workspaceResolved = false;
21653
21868
  this.client = new import_sandbox23.SandboxClient({
21654
21869
  baseUrl: config.baseURL,
21655
21870
  environment: ""
21656
21871
  });
21657
21872
  }
21873
+ async resolveWorkspace() {
21874
+ if (this.workspaceResolved) {
21875
+ return this.workspace;
21876
+ }
21877
+ try {
21878
+ const result = await this.client.sandbox.getContext();
21879
+ if (result.ok && result.body.home_dir) {
21880
+ this.workspace = result.body.home_dir;
21881
+ }
21882
+ } catch {
21883
+ }
21884
+ this.workspaceResolved = true;
21885
+ return this.workspace;
21886
+ }
21658
21887
  async createSandbox(name, _config) {
21659
21888
  const existing = this.instances.get(name);
21660
21889
  if (existing) {
21661
21890
  return existing;
21662
21891
  }
21663
- const instance = new RemoteSandboxInstance(name, this.client);
21664
- this.instances.set(name, instance);
21665
- return instance;
21892
+ const inFlight = this.creating.get(name);
21893
+ if (inFlight) {
21894
+ return inFlight;
21895
+ }
21896
+ const creation = (async () => {
21897
+ const workspace = await this.resolveWorkspace();
21898
+ const instance = new RemoteSandboxInstance(name, this.client, workspace);
21899
+ this.instances.set(name, instance);
21900
+ return instance;
21901
+ })();
21902
+ this.creating.set(name, creation);
21903
+ creation.then(
21904
+ () => {
21905
+ this.creating.delete(name);
21906
+ },
21907
+ () => {
21908
+ this.creating.delete(name);
21909
+ }
21910
+ );
21911
+ return creation;
21666
21912
  }
21667
21913
  async getSandbox(name) {
21668
21914
  const instance = this.instances.get(name);
@@ -21672,9 +21918,7 @@ var RemoteSandboxProvider = class {
21672
21918
  return instance;
21673
21919
  }
21674
21920
  async stopSandbox(name) {
21675
- const instance = this.instances.get(name);
21676
- if (instance) {
21677
- }
21921
+ this.instances.delete(name);
21678
21922
  }
21679
21923
  async deleteSandbox(name) {
21680
21924
  this.instances.delete(name);
@@ -21682,6 +21926,82 @@ var RemoteSandboxProvider = class {
21682
21926
  async listSandboxes() {
21683
21927
  return Array.from(this.instances.values());
21684
21928
  }
21929
+ createVolumeFsClient(_volumeName, pathPrefix) {
21930
+ const workspace = this.workspace;
21931
+ const resolve3 = (p) => {
21932
+ if (!p || p === "/") {
21933
+ if (pathPrefix) {
21934
+ return `${workspace}${pathPrefix}`;
21935
+ }
21936
+ return workspace;
21937
+ }
21938
+ if (p.startsWith(workspace)) {
21939
+ return p;
21940
+ }
21941
+ if (p.startsWith("/")) {
21942
+ return `${workspace}${p}`;
21943
+ }
21944
+ if (pathPrefix) {
21945
+ const mountDir = pathPrefix.replace(/^\//, "");
21946
+ return `${workspace}/${mountDir}/${p}`;
21947
+ }
21948
+ return `${workspace}/${p}`;
21949
+ };
21950
+ return {
21951
+ read: async (path3) => {
21952
+ const resolved = resolve3(path3);
21953
+ const result = await this.client.file.readFile({ file: resolved });
21954
+ if (!result.ok) {
21955
+ throw new Error(`Volume read failed: ${extractFetcherError(result.error)}`);
21956
+ }
21957
+ return result.body.data?.content ?? "";
21958
+ },
21959
+ write: async (path3, content) => {
21960
+ const resolved = resolve3(path3);
21961
+ const result = await this.client.file.writeFile({ file: resolved, content });
21962
+ if (!result.ok) {
21963
+ throw new Error(`Volume write failed: ${extractFetcherError(result.error)}`);
21964
+ }
21965
+ },
21966
+ list: async (path3) => {
21967
+ const resolved = resolve3(path3);
21968
+ const result = await this.client.file.listPath({
21969
+ path: resolved,
21970
+ recursive: false
21971
+ });
21972
+ if (!result.ok) {
21973
+ throw new Error(`Volume list failed: ${extractFetcherError(result.error)}`);
21974
+ }
21975
+ const entries = (result.body.data?.files || []).map((f) => ({
21976
+ path: f.path.startsWith(workspace) ? f.path.slice(workspace.length) : f.path,
21977
+ kind: f.is_dir === true || f.size === null ? "directory" : "file",
21978
+ size: f.size ?? 0,
21979
+ mode: 0,
21980
+ modified: f.modified_at ?? null
21981
+ }));
21982
+ return entries;
21983
+ },
21984
+ readRaw: async (path3) => {
21985
+ const resolved = resolve3(path3);
21986
+ const result = await this.client.file.downloadFile({ path: resolved });
21987
+ if (!result.ok) {
21988
+ throw new Error(`Volume download failed: ${extractFetcherError(result.error)}`);
21989
+ }
21990
+ const buffer2 = await result.body.arrayBuffer();
21991
+ return Buffer.from(buffer2);
21992
+ },
21993
+ writeRaw: async (path3, data) => {
21994
+ const resolved = resolve3(path3);
21995
+ const result = await this.client.file.uploadFile({
21996
+ file: data,
21997
+ path: resolved
21998
+ });
21999
+ if (!result.ok) {
22000
+ throw new Error(`Volume upload failed: ${extractFetcherError(result.error)}`);
22001
+ }
22002
+ }
22003
+ };
22004
+ }
21685
22005
  };
21686
22006
 
21687
22007
  // src/sandbox_lattice/providers/E2BProvider.ts
@@ -22323,6 +22643,7 @@ function clearEncryptionKeyCache() {
22323
22643
  MicrosandboxRemoteProvider,
22324
22644
  MicrosandboxServiceClient,
22325
22645
  ModelLatticeManager,
22646
+ MysqlDatabase,
22326
22647
  PinoLoggerClient,
22327
22648
  PostgresDatabase,
22328
22649
  PrometheusClient,
@@ -22387,6 +22708,7 @@ function clearEncryptionKeyCache() {
22387
22708
  ensureBuiltinAgentsForTenant,
22388
22709
  eventBus,
22389
22710
  eventBusDefault,
22711
+ extractFetcherError,
22390
22712
  fileDataToString,
22391
22713
  formatContentWithLineNumbers,
22392
22714
  formatGrepMatches,