@gaia-ai/core 0.0.1

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/src/core/conductor-id.d.ts +1 -0
  4. package/dist/src/core/conductor-id.js +8 -0
  5. package/dist/src/core/exec.d.ts +41 -0
  6. package/dist/src/core/exec.js +75 -0
  7. package/dist/src/core/logger.d.ts +31 -0
  8. package/dist/src/core/logger.js +36 -0
  9. package/dist/src/core/shell.d.ts +21 -0
  10. package/dist/src/core/shell.js +46 -0
  11. package/dist/src/core/slug.d.ts +9 -0
  12. package/dist/src/core/slug.js +18 -0
  13. package/dist/src/index.d.ts +12 -0
  14. package/dist/src/index.js +7 -0
  15. package/dist/src/plugins/agent/agent.d.ts +52 -0
  16. package/dist/src/plugins/agent/agent.js +10 -0
  17. package/dist/src/plugins/auth/basic.d.ts +11 -0
  18. package/dist/src/plugins/auth/basic.js +35 -0
  19. package/dist/src/plugins/executor/executor.d.ts +92 -0
  20. package/dist/src/plugins/executor/executor.js +1 -0
  21. package/dist/src/plugins/plugins.d.ts +44 -0
  22. package/dist/src/plugins/plugins.js +16 -0
  23. package/dist/src/plugins/registry-exports.d.ts +6 -0
  24. package/dist/src/plugins/registry-exports.js +6 -0
  25. package/dist/src/plugins/remote/drupal.d.ts +34 -0
  26. package/dist/src/plugins/remote/drupal.js +334 -0
  27. package/dist/src/plugins/remote/fake.d.ts +95 -0
  28. package/dist/src/plugins/remote/fake.js +227 -0
  29. package/dist/src/plugins/remote/remote.d.ts +175 -0
  30. package/dist/src/plugins/remote/remote.js +1 -0
  31. package/dist/src/plugins/workspace/fake.d.ts +6 -0
  32. package/dist/src/plugins/workspace/fake.js +16 -0
  33. package/dist/src/plugins/workspace/git.d.ts +37 -0
  34. package/dist/src/plugins/workspace/git.js +89 -0
  35. package/dist/src/plugins/workspace/instructions.d.ts +6 -0
  36. package/dist/src/plugins/workspace/instructions.js +16 -0
  37. package/dist/src/plugins/workspace/workspace.d.ts +35 -0
  38. package/dist/src/plugins/workspace/workspace.js +1 -0
  39. package/dist/src/plugins-index.d.ts +1 -0
  40. package/dist/src/plugins-index.js +1 -0
  41. package/dist/src/types.d.ts +51 -0
  42. package/dist/src/types.js +1 -0
  43. package/package.json +33 -0
@@ -0,0 +1,175 @@
1
+ export interface ConductorRegistration {
2
+ id: string;
3
+ project: string;
4
+ states: string[];
5
+ workspace: string;
6
+ label: string;
7
+ max_parallel: number;
8
+ }
9
+ export interface ClaimOptions {
10
+ leaseSeconds: number;
11
+ conductorId: string;
12
+ }
13
+ export interface ClaimedRun {
14
+ runUuid: string;
15
+ runId: number;
16
+ ticketUuid: string;
17
+ stateAtStart: string;
18
+ handler: string;
19
+ }
20
+ export interface Ticket {
21
+ uuid: string;
22
+ identifier: string;
23
+ title: string;
24
+ state: string;
25
+ branchName: string;
26
+ /** Branch the ticket's work is based on (computed server-side): parent branch, project default, or 'main'. */
27
+ baseBranch?: string;
28
+ /**
29
+ * Effective environment variables (GAIA-99), resolved server-side along the
30
+ * parent_id chain (child wins) as canonical `.env`-style `KEY=value` lines.
31
+ * The conductor parses this, strips reserved keys, overlays the core GAIA_*
32
+ * vars, and injects the result into the agent run env AND the workspace hooks.
33
+ * Omitted when the ticket (and its ancestors) set none. Values may be secret.
34
+ */
35
+ effectiveEnvVars?: string;
36
+ issueUrl?: string;
37
+ }
38
+ export interface ActiveRun {
39
+ runUuid: string;
40
+ ticketUuid: string;
41
+ ticketIdentifier: string;
42
+ branchName: string;
43
+ state: string;
44
+ stateAtStart: string;
45
+ /** Absolute path of the per-run git worktree, or '' when unset. */
46
+ worktreePath: string;
47
+ }
48
+ export interface ConductorStatus {
49
+ id: string;
50
+ project: string;
51
+ label: string;
52
+ status: string;
53
+ lastSeen: number;
54
+ load: number;
55
+ }
56
+ export interface FinalizableRun {
57
+ runUuid: string;
58
+ /** Absolute path of the per-run git worktree, or '' when unset. */
59
+ worktreePath: string;
60
+ /**
61
+ * Unix timestamp (seconds) the run started, or undefined when unset. The
62
+ * conductor derives the footprint `duration_s` as `now − startedAt` at close
63
+ * (GAIA-132).
64
+ */
65
+ startedAt?: number;
66
+ }
67
+ /**
68
+ * Per-run effort footprint the conductor writes onto `gaia_run` at close
69
+ * (GAIA-132). Six integer metrics: the five parsed from the agent transcript
70
+ * plus `duration_s` computed from `started_at → close`.
71
+ */
72
+ export interface RunMetrics {
73
+ tokens: number;
74
+ duration_s: number;
75
+ agent_turns: number;
76
+ tool_calls: number;
77
+ user_prompts: number;
78
+ user_prompt_words: number;
79
+ }
80
+ /**
81
+ * A finished ticket whose worktree has not yet been torn down — the
82
+ * conductor's teardown work list, driven by the durable `cleaned_up` flag
83
+ * (GAIA-89). "Finished" = reached `done` OR already `closed`; `cleaned_up=0`
84
+ * means the herdr worktree still needs reclaiming. The reconciliation is NOT
85
+ * scoped to a conductor's machine_id, so it also surfaces tickets whose
86
+ * conductor was down at `done` or whose `conductor_id` was reassigned.
87
+ */
88
+ export interface UncleanTicket {
89
+ ticketUuid: string;
90
+ /** The ticket's git branch — the key herdr resolves the worktree by. */
91
+ branchName: string;
92
+ /**
93
+ * Absolute path of the ticket's worktree (from its latest run's
94
+ * `worktree_path`), or '' when unresolved. The cwd the teardown runs in.
95
+ */
96
+ worktreePath: string;
97
+ /** Workflow state (e.g. `coding`, `done`). */
98
+ state: string;
99
+ /** Whether the ticket's lifecycle has already been closed out. */
100
+ closed: boolean;
101
+ }
102
+ /** Extra conductor-writable gaia_run attributes (besides state/lease). */
103
+ export interface RunWriteAttributes {
104
+ /** Absolute path of the per-run git worktree (set by the conductor). */
105
+ worktree_path?: string;
106
+ }
107
+ export interface GaiaRemote {
108
+ /** Upserts by machine_id; sets status=online. Returns the Drupal entity uuid. */
109
+ registerConductor(reg: ConductorRegistration): Promise<string>;
110
+ /**
111
+ * Records a heartbeat server-side: upserts by machine_id (recreating a
112
+ * vanished registration), refreshes last_seen/lease/load, and brings the
113
+ * conductor online. Keyed on machine_id, so it never 404s — the self-heal
114
+ * lives on the server, not the client. Returns the resulting status
115
+ * (`online` | `offline`).
116
+ */
117
+ heartbeat(reg: ConductorRegistration, currentLoad: number, leaseSeconds?: number): Promise<string>;
118
+ getConductorStatus(conductorId: string): Promise<string | null>;
119
+ setConductorStatus(conductorId: string, status: 'offline' | 'online'): Promise<void>;
120
+ listConductors(owner?: 'me'): Promise<ConductorStatus[]>;
121
+ activeRunCount(conductorId: string): Promise<number>;
122
+ fetchActiveRuns(conductorId: string): Promise<ActiveRun[]>;
123
+ claimNext(claim: ClaimOptions): Promise<ClaimedRun | null>;
124
+ getTicket(ticketUuid: string): Promise<Ticket>;
125
+ /** Returns the run's worktree_path, or '' when unset. */
126
+ getRunWorktree(runUuid: string): Promise<string>;
127
+ /**
128
+ * Returns the identifier of the run's ticket, or '' when unresolved. Lets the
129
+ * release path derive the tab matching ref (the ticket identifier) from a run
130
+ * uuid alone.
131
+ */
132
+ getRunTicketIdentifier(runUuid: string): Promise<string>;
133
+ /**
134
+ * Returns the branch_name of the run's ticket, or '' when unresolved. Used
135
+ * by dispatch for the per-(branch,state) herdr tab model.
136
+ */
137
+ getRunTicketBranchName(runUuid: string): Promise<string>;
138
+ markRunning(runUuid: string, attrs?: RunWriteAttributes): Promise<void>;
139
+ /**
140
+ * Runs this conductor owns that are state=done but not yet closed — the
141
+ * conductor's one-shot finalisation work list.
142
+ */
143
+ fetchFinalizableRuns(conductorId: string): Promise<FinalizableRun[]>;
144
+ /**
145
+ * Finalise a run: set closed + closed_date, (when non-empty) log, and (when
146
+ * given) the per-run footprint metrics (GAIA-132). No state change — the run
147
+ * is already done.
148
+ */
149
+ finalizeRun(uuid: string, log: string, metrics?: RunMetrics): Promise<void>;
150
+ /**
151
+ * Finished tickets (state=done OR closed) whose worktree is not yet torn
152
+ * down (cleaned_up=0) — the conductor's teardown work list (GAIA-89). Driven
153
+ * by the durable `cleaned_up` flag AND scoped to the reaping conductor
154
+ * (GAIA-121): only tickets assigned to `conductorId` are loaded, so every
155
+ * ticket on the list is unambiguously this conductor's — a missing worktree
156
+ * means "already torn down on this host", not "belongs to another host". The
157
+ * same query backs the live tick and the standalone `gaia conductor reap`.
158
+ * Empty once every finished ticket this conductor owns is cleaned.
159
+ */
160
+ fetchUncleanedTickets(conductorId: string): Promise<UncleanTicket[]>;
161
+ /**
162
+ * Close a ticket: set closed=true + closed_date. Ticket-lifecycle only, and
163
+ * decoupled from teardown — a done ticket is closed even if its worktree
164
+ * teardown later fails. No state change (the ticket is already done).
165
+ */
166
+ closeTicket(uuid: string): Promise<void>;
167
+ /**
168
+ * Mark a ticket's worktree torn down: set cleaned_up=true so it drops off the
169
+ * {@link fetchUncleanedTickets} work list. Written ONLY after a verified
170
+ * teardown, so a teardown miss leaves cleaned_up=0 and the next reconciliation
171
+ * retries — one miss never orphans the workspace, and a re-run on an
172
+ * already-cleaned ticket is a no-op (it is no longer on the list).
173
+ */
174
+ markTicketCleanedUp(uuid: string): Promise<void>;
175
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { WorkspacePlugin } from '../plugins.js';
2
+ import type { EnsuredWorkspace, GaiaWorkspace } from './workspace.js';
3
+ export declare class FakeWorkspace implements GaiaWorkspace {
4
+ ensure(identifier: string, _title?: string): Promise<EnsuredWorkspace>;
5
+ }
6
+ export declare function fakeWorkspace(): WorkspacePlugin;
@@ -0,0 +1,16 @@
1
+ export class FakeWorkspace {
2
+ async ensure(identifier, _title) {
3
+ return { path: `/fake/${identifier}`, instructions: null, created: true };
4
+ }
5
+ }
6
+ export function fakeWorkspace() {
7
+ const workspace = new FakeWorkspace();
8
+ return {
9
+ kind: 'workspace',
10
+ id: 'fake',
11
+ requiredModules: [],
12
+ async createWorkspace(_config) {
13
+ return workspace;
14
+ },
15
+ };
16
+ }
@@ -0,0 +1,37 @@
1
+ import type { WorkspacePlugin } from '../plugins.js';
2
+ import type { EnsuredWorkspace, GaiaWorkspace } from './workspace.js';
3
+ /** Default branch template: `<prefix><key>-<title-slug>` (readable). */
4
+ export declare const DEFAULT_BRANCH_TEMPLATE = "{branchPrefix}{key}-{titleSlug}";
5
+ export type GitRunner = (args: string[], cwd: string) => Promise<void>;
6
+ export interface GitWorkspaceOptions {
7
+ /** Main clone (a git repo): config source + worktree base. */
8
+ root: string;
9
+ /** Where per-ticket worktrees live. Default: `${root}-worktrees`. */
10
+ worktreesRoot?: string;
11
+ /** Branch name prefix per ticket. Default: 'gaia/'. */
12
+ branchPrefix?: string;
13
+ /**
14
+ * Branch name template. Vars: {branchPrefix}, {key} (sanitized identifier),
15
+ * {identifier}, {titleSlug}. Default: `{branchPrefix}{key}-{titleSlug}`.
16
+ */
17
+ branchTemplate?: string;
18
+ runGit?: GitRunner;
19
+ }
20
+ export declare class GitWorkspace implements GaiaWorkspace {
21
+ private readonly runGit;
22
+ private readonly root;
23
+ private readonly worktreesRoot;
24
+ private readonly branchPrefix;
25
+ private readonly branchTemplate;
26
+ constructor(options: GitWorkspaceOptions);
27
+ /** Render the branch name for a ticket from the configured template. */
28
+ branchFor(identifier: string, title?: string): string;
29
+ keyFor(identifier: string): string;
30
+ private pathFor;
31
+ ensure(identifier: string, title?: string, _baseRef?: string): Promise<EnsuredWorkspace>;
32
+ remove(identifier: string): Promise<void>;
33
+ }
34
+ export declare function gitWorkspace(opts?: {
35
+ branchPrefix?: string;
36
+ branchTemplate?: string;
37
+ }): WorkspacePlugin;
@@ -0,0 +1,89 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { exec } from '../../core/exec.js';
4
+ import { slugify } from '../../core/slug.js';
5
+ import { loadInstructions } from './instructions.js';
6
+ /** Default branch template: `<prefix><key>-<title-slug>` (readable). */
7
+ export const DEFAULT_BRANCH_TEMPLATE = '{branchPrefix}{key}-{titleSlug}';
8
+ function renderBranchTemplate(template, vars) {
9
+ const rendered = template.replace(/\{(\w+)\}/g, (whole, key) => {
10
+ const value = vars[key];
11
+ return value === undefined ? whole : value;
12
+ });
13
+ // An empty title slug leaves a dangling separator (e.g. `gaia/GL-9-`) —
14
+ // trim trailing branch separators so the branch stays a valid, tidy ref.
15
+ return rendered.replace(/[-_./]+$/g, '');
16
+ }
17
+ const defaultGitRunner = async (args, cwd) => {
18
+ await exec('git', args, { cwd });
19
+ };
20
+ export class GitWorkspace {
21
+ runGit;
22
+ root;
23
+ worktreesRoot;
24
+ branchPrefix;
25
+ branchTemplate;
26
+ constructor(options) {
27
+ this.runGit = options.runGit ?? defaultGitRunner;
28
+ this.root = resolve(options.root);
29
+ this.worktreesRoot = options.worktreesRoot
30
+ ? resolve(options.worktreesRoot)
31
+ : `${this.root}-worktrees`;
32
+ this.branchPrefix = options.branchPrefix ?? 'gaia/';
33
+ this.branchTemplate = options.branchTemplate ?? DEFAULT_BRANCH_TEMPLATE;
34
+ }
35
+ /** Render the branch name for a ticket from the configured template. */
36
+ branchFor(identifier, title) {
37
+ return renderBranchTemplate(this.branchTemplate, {
38
+ branchPrefix: this.branchPrefix,
39
+ key: this.keyFor(identifier),
40
+ identifier,
41
+ titleSlug: slugify(title ?? ''),
42
+ });
43
+ }
44
+ keyFor(identifier) {
45
+ return identifier.replace(/[^A-Za-z0-9._-]/g, '_');
46
+ }
47
+ pathFor(key) {
48
+ return resolve(join(this.worktreesRoot, key));
49
+ }
50
+ async ensure(identifier, title, _baseRef) {
51
+ const key = this.keyFor(identifier);
52
+ // Reject empty and dot-leading keys: a dot-leading key would produce an
53
+ // invalid git ref (e.g. `gaia/.hidden`) and covers '.'/'..' too.
54
+ if (key === '' || key.startsWith('.')) {
55
+ throw new Error(`invalid workspace identifier: ${identifier}`);
56
+ }
57
+ const path = this.pathFor(key);
58
+ if (existsSync(path)) {
59
+ return { path, instructions: loadInstructions(path), created: false };
60
+ }
61
+ const branch = this.branchFor(identifier, title);
62
+ await this.runGit(['-C', this.root, 'worktree', 'add', '--force', '-B', branch, path], this.root);
63
+ // The `after_create` hook is run by the executor (GAIA-84), not here — the
64
+ // workspace only reports that it created a fresh worktree.
65
+ return { path, instructions: loadInstructions(path), created: true };
66
+ }
67
+ async remove(identifier) {
68
+ const key = this.keyFor(identifier);
69
+ const path = this.pathFor(key);
70
+ await this.runGit(['-C', this.root, 'worktree', 'remove', '--force', path], this.root);
71
+ }
72
+ }
73
+ export function gitWorkspace(opts = {}) {
74
+ return {
75
+ kind: 'workspace',
76
+ id: 'git',
77
+ requiredModules: [],
78
+ async createWorkspace(config) {
79
+ const root = config.config_path
80
+ ? dirname(config.config_path)
81
+ : process.cwd();
82
+ return new GitWorkspace({
83
+ root,
84
+ ...(opts.branchPrefix ? { branchPrefix: opts.branchPrefix } : {}),
85
+ ...(opts.branchTemplate ? { branchTemplate: opts.branchTemplate } : {}),
86
+ });
87
+ },
88
+ };
89
+ }
@@ -0,0 +1,6 @@
1
+ /** Loads ./WORKFLOW.md from the workspace, hashing its content. */
2
+ export declare function loadInstructions(workspacePath: string): {
3
+ path: string;
4
+ sha256: string;
5
+ text: string;
6
+ } | null;
@@ -0,0 +1,16 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ /** Loads ./WORKFLOW.md from the workspace, hashing its content. */
5
+ export function loadInstructions(workspacePath) {
6
+ const path = resolve(workspacePath, 'WORKFLOW.md');
7
+ if (!existsSync(path)) {
8
+ return null;
9
+ }
10
+ const text = readFileSync(path, 'utf8');
11
+ return {
12
+ path,
13
+ sha256: createHash('sha256').update(text).digest('hex'),
14
+ text,
15
+ };
16
+ }
@@ -0,0 +1,35 @@
1
+ export interface EnsuredWorkspace {
2
+ path: string;
3
+ instructions: {
4
+ path: string;
5
+ sha256: string;
6
+ text: string;
7
+ } | null;
8
+ /**
9
+ * Whether this call CREATED the worktree (true) or reused an existing one
10
+ * (false). The core runs the `after_create` lifecycle hook only on a fresh
11
+ * worktree, so it needs to know which happened. Lifecycle hooks themselves
12
+ * are no longer a workspace concern (GAIA-84): the executor owns and runs
13
+ * them best-effort.
14
+ */
15
+ created: boolean;
16
+ }
17
+ export interface GaiaWorkspace {
18
+ /**
19
+ * Ensure a per-ticket worktree exists. `branch`, when given, is the branch
20
+ * name the dispatcher computed for the ticket. The git plugin treats it as a
21
+ * readable title to slug into its branch template; the herdr plugin uses it
22
+ * verbatim as the worktree branch. The worktree directory key/path stays
23
+ * identifier- or branch-derived so reuse is stable across runs.
24
+ *
25
+ * `baseRef`, when given, overrides the base the new branch is created from
26
+ * (e.g. `origin/<base_branch>` so a sub-ticket stacks on its parent); if it
27
+ * does not resolve on the remote the implementation falls back to its default
28
+ * base — never a hard error.
29
+ *
30
+ * Reports `created` so the caller can run the `after_create` hook only on a
31
+ * fresh worktree. Lifecycle-hook invocation is NOT a workspace responsibility
32
+ * anymore — the executor owns all hooks (GAIA-84).
33
+ */
34
+ ensure(identifier: string, branch?: string, baseRef?: string): Promise<EnsuredWorkspace>;
35
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from './plugins/registry-exports.js';
@@ -0,0 +1 @@
1
+ export * from './plugins/registry-exports.js';
@@ -0,0 +1,51 @@
1
+ import type { DropSHPlugin } from 'dropsh/plugin';
2
+ import type { AgentPlugin, ExecutorPlugin, RemotePlugin, WorkspacePlugin } from './plugins/plugins.js';
3
+ /** Normalized single conductor. */
4
+ export interface ConductorSettings {
5
+ /** Default: `${project} @ ${checkoutRoot}`. */
6
+ label: string;
7
+ /**
8
+ * Stable node identity (gaia_conductor.machine_id) this process registers as.
9
+ * Defaults to a hash of hostname + checkout path. Set it to pin a conductor to
10
+ * a known identity — e.g. so a ticket can be pre-assigned to it (conductor
11
+ * assignment is the run-start trigger), which the e2e fixtures rely on.
12
+ */
13
+ machine_id?: string;
14
+ /** Project name (gaia_project.name); resolved at registration. */
15
+ project: string;
16
+ /** Workflow states this conductor serves. */
17
+ states: string[];
18
+ /**
19
+ * Agent prompt template - the GAIA run contract, NOT project workflow. Bounds
20
+ * the agent to exactly one state and tells it to release + stop, keeping run
21
+ * mechanics out of the repo's WORKFLOW.md. Placeholders: `{identifier}`,
22
+ * `{state}`, `{runUuid}`. Defaults to `DEFAULT_AGENT_PROMPT`.
23
+ */
24
+ prompt: string;
25
+ /** Default: 1. */
26
+ max_parallel: number;
27
+ poll_interval_ms: number;
28
+ lease_seconds: number;
29
+ hooks?: {
30
+ after_create?: string;
31
+ before_run?: string;
32
+ after_run?: string;
33
+ after_done?: string;
34
+ };
35
+ /** Control-plane site settings used for dropsh and agent env. */
36
+ site: {
37
+ base_url: string;
38
+ jsonapi_prefix: string;
39
+ };
40
+ /** Absolute path of the loaded config. */
41
+ config_path: string;
42
+ }
43
+ /** Whole conductor.config.js (dropsh-superset): site + named plugin slots. */
44
+ export interface ConductorFileConfig extends ConductorSettings {
45
+ remote: RemotePlugin;
46
+ executor: ExecutorPlugin;
47
+ agent: AgentPlugin;
48
+ workspace: WorkspacePlugin;
49
+ /** dropsh-layer plugins (auth etc.); separate from the GAIA slots. */
50
+ plugins?: DropSHPlugin[];
51
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@gaia-ai/core",
3
+ "version": "0.0.1",
4
+ "description": "GAIA global contract: plugin API, built-in remotes/workspaces/auth, shared primitives.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "engines": {
8
+ "node": ">=20.19"
9
+ },
10
+ "exports": {
11
+ ".": "./dist/src/index.js",
12
+ "./plugins": "./dist/src/plugins-index.js",
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "dist/src",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://git.key-tec.de/keytec/gaia.git",
26
+ "directory": "conductor/packages/core"
27
+ },
28
+ "dependencies": {
29
+ "dropsh": "^0.5.7",
30
+ "pino": "^9.6.0",
31
+ "pino-pretty": "^13.0.0"
32
+ }
33
+ }