@mcoda/agents 0.1.35 → 0.1.36

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.
@@ -20,7 +20,7 @@ export class CodexAdapter {
20
20
  async invoke(request) {
21
21
  const health = cliHealthy(true);
22
22
  const cliDetails = health.details;
23
- const result = runCodexExec(request.input, this.config.model);
23
+ const result = await runCodexExec(request.input, this.config.model);
24
24
  return {
25
25
  output: result.output,
26
26
  adapter: this.config.adapter ?? "codex-cli",
@@ -2,10 +2,10 @@ export declare const cliHealthy: (throwOnError?: boolean) => {
2
2
  ok: boolean;
3
3
  details?: Record<string, unknown>;
4
4
  };
5
- export declare const runCodexExec: (prompt: string, model?: string) => {
5
+ export declare const runCodexExec: (prompt: string, model?: string) => Promise<{
6
6
  output: string;
7
7
  raw: string;
8
- };
8
+ }>;
9
9
  export declare function runCodexExecStream(prompt: string, model?: string): AsyncGenerator<{
10
10
  output: string;
11
11
  raw: string;
@@ -1 +1 @@
1
- {"version":3,"file":"CodexCliRunner.d.ts","sourceRoot":"","sources":["../../../src/adapters/codex/CodexCliRunner.ts"],"names":[],"mappings":"AA4hBA,eAAO,MAAM,UAAU,GAAI,sBAAoB,KAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CA2BjG,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,QAAQ,MAAM,KAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAuD1F,CAAC;AAEF,wBAAuB,kBAAkB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,GACb,cAAc,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CA+GhE"}
1
+ {"version":3,"file":"CodexCliRunner.d.ts","sourceRoot":"","sources":["../../../src/adapters/codex/CodexCliRunner.ts"],"names":[],"mappings":"AAuyBA,eAAO,MAAM,UAAU,GAAI,sBAAoB,KAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CA+BjG,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,QAAQ,MAAM,EAAE,QAAQ,MAAM,KAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAS1G,CAAC;AAEF,wBAAuB,kBAAkB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,GACb,cAAc,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAgEhE"}
@@ -7,6 +7,12 @@ const CODEX_STREAM_IO_ENV = "MCODA_STREAM_IO";
7
7
  const CODEX_STREAM_IO_FORMAT_ENV = "MCODA_STREAM_IO_FORMAT";
8
8
  const CODEX_STREAM_IO_COLOR_ENV = "MCODA_STREAM_IO_COLOR";
9
9
  const CODEX_STREAM_IO_PREFIX = "codex-cli";
10
+ const CODEX_COMMAND_ENV = "MCODA_CODEX_COMMAND";
11
+ const CODEX_COMMAND_ARGS_ENV = "MCODA_CODEX_COMMAND_ARGS";
12
+ const CODEX_TIMEOUT_ENV = "MCODA_CODEX_TIMEOUT_MS";
13
+ const CODEX_EXIT_GRACE_ENV = "MCODA_CODEX_EXIT_GRACE_MS";
14
+ const CODEX_DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
15
+ const CODEX_DEFAULT_EXIT_GRACE_MS = 5000;
10
16
  const ANSI = {
11
17
  reset: "\u001b[0m",
12
18
  bold: "\u001b[1m",
@@ -114,6 +120,94 @@ const extractItemText = (item) => {
114
120
  }
115
121
  return "";
116
122
  };
123
+ const parseCommandArgs = (raw) => {
124
+ const trimmed = raw?.trim();
125
+ if (!trimmed)
126
+ return [];
127
+ if (trimmed.startsWith("[")) {
128
+ try {
129
+ const parsed = JSON.parse(trimmed);
130
+ if (Array.isArray(parsed)) {
131
+ return parsed
132
+ .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
133
+ .filter(Boolean);
134
+ }
135
+ }
136
+ catch {
137
+ // fall through to whitespace parsing
138
+ }
139
+ }
140
+ return trimmed.split(/\s+/).filter(Boolean);
141
+ };
142
+ const resolveCodexCommand = () => {
143
+ const command = process.env[CODEX_COMMAND_ENV]?.trim() || "codex";
144
+ const preArgs = parseCommandArgs(process.env[CODEX_COMMAND_ARGS_ENV]);
145
+ return { command, preArgs };
146
+ };
147
+ const parsePositiveIntEnv = (name, fallback) => {
148
+ const raw = process.env[name]?.trim();
149
+ if (!raw)
150
+ return fallback;
151
+ const parsed = Number(raw);
152
+ return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
153
+ };
154
+ const resolveCodexTimeoutMs = () => parsePositiveIntEnv(CODEX_TIMEOUT_ENV, CODEX_DEFAULT_TIMEOUT_MS);
155
+ const resolveCodexExitGraceMs = () => parsePositiveIntEnv(CODEX_EXIT_GRACE_ENV, CODEX_DEFAULT_EXIT_GRACE_MS);
156
+ const isIgnorableStdinError = (error) => error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED";
157
+ const safeKill = (child, signal) => {
158
+ try {
159
+ if (!child.killed) {
160
+ child.kill(signal);
161
+ }
162
+ }
163
+ catch {
164
+ /* ignore kill errors */
165
+ }
166
+ };
167
+ const scheduleHardKill = (child, delayMs = 250) => {
168
+ const timer = setTimeout(() => {
169
+ safeKill(child, "SIGKILL");
170
+ }, delayMs);
171
+ timer.unref();
172
+ };
173
+ const extractProtocolError = (parsed) => {
174
+ if (!parsed || typeof parsed !== "object")
175
+ return undefined;
176
+ const type = typeof parsed.type === "string" ? parsed.type : "";
177
+ if (type === "turn.failed") {
178
+ const message = parsed?.error?.message ?? parsed?.error;
179
+ return message ? String(message) : "codex turn failed";
180
+ }
181
+ if (type === "error" && parsed?.message) {
182
+ return String(parsed.message);
183
+ }
184
+ return undefined;
185
+ };
186
+ const isCompletionEvent = (parsed) => {
187
+ if (!parsed || typeof parsed !== "object")
188
+ return false;
189
+ const type = typeof parsed.type === "string" ? parsed.type : "";
190
+ return type === "turn.completed" || type === "response.completed";
191
+ };
192
+ const parseCodexOutput = (raw) => {
193
+ const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
194
+ let message = "";
195
+ for (const line of lines) {
196
+ const parsed = safeJsonParse(line);
197
+ const event = extractAssistantText(parsed);
198
+ if (!event)
199
+ continue;
200
+ if (event.kind === "delta") {
201
+ message += event.text;
202
+ continue;
203
+ }
204
+ message = event.text;
205
+ }
206
+ if (!message) {
207
+ return lines[lines.length - 1] ?? "";
208
+ }
209
+ return message;
210
+ };
117
211
  const normalizeValue = (value) => {
118
212
  if (typeof value !== "string")
119
213
  return value;
@@ -541,6 +635,155 @@ const extractAssistantText = (parsed) => {
541
635
  }
542
636
  return null;
543
637
  };
638
+ const invokeCodexProcess = async (prompt, model, hooks) => {
639
+ const resolvedModel = model ?? "gpt-5.1-codex-max";
640
+ const sandboxArgs = resolveSandboxArgs();
641
+ const reasoningEffort = resolveReasoningEffort(resolvedModel);
642
+ const codexCommand = resolveCodexCommand();
643
+ const args = [...codexCommand.preArgs, ...sandboxArgs.args, "exec", "--model", resolvedModel, "--json"];
644
+ if (!sandboxArgs.bypass) {
645
+ args.push("--full-auto");
646
+ }
647
+ if (reasoningEffort) {
648
+ args.push("-c", `model_reasoning_effort=${reasoningEffort}`);
649
+ }
650
+ args.push("-");
651
+ const timeoutMs = resolveCodexTimeoutMs();
652
+ const exitGraceMs = resolveCodexExitGraceMs();
653
+ return await new Promise((resolve, reject) => {
654
+ const child = spawn(codexCommand.command, args, { stdio: ["pipe", "pipe", "pipe"] });
655
+ let raw = "";
656
+ let stderr = "";
657
+ let lineBuffer = "";
658
+ let message = "";
659
+ let protocolError;
660
+ let settled = false;
661
+ let forcedExit = false;
662
+ let completionTimer;
663
+ const clearTimers = () => {
664
+ if (completionTimer) {
665
+ clearTimeout(completionTimer);
666
+ completionTimer = undefined;
667
+ }
668
+ clearTimeout(timeoutHandle);
669
+ };
670
+ const finalizeOutput = () => {
671
+ const parsedOutput = parseCodexOutput(raw).trim();
672
+ return parsedOutput || message.trim();
673
+ };
674
+ const finishResolve = () => {
675
+ if (settled)
676
+ return;
677
+ settled = true;
678
+ clearTimers();
679
+ resolve({ output: finalizeOutput(), raw, forcedExit });
680
+ };
681
+ const finishReject = (error) => {
682
+ if (settled)
683
+ return;
684
+ settled = true;
685
+ clearTimers();
686
+ reject(error);
687
+ };
688
+ const scheduleCompletionGrace = () => {
689
+ if (settled)
690
+ return;
691
+ if (completionTimer) {
692
+ clearTimeout(completionTimer);
693
+ }
694
+ completionTimer = setTimeout(() => {
695
+ if (settled)
696
+ return;
697
+ forcedExit = true;
698
+ safeKill(child, "SIGTERM");
699
+ scheduleHardKill(child);
700
+ finishResolve();
701
+ }, exitGraceMs);
702
+ completionTimer.unref();
703
+ };
704
+ const handleLine = (line) => {
705
+ const normalized = line.replace(/\r$/, "");
706
+ hooks?.onLine?.(normalized);
707
+ const parsed = safeJsonParse(normalized);
708
+ const event = extractAssistantText(parsed);
709
+ if (event) {
710
+ if (event.kind === "delta") {
711
+ message += event.text;
712
+ }
713
+ else {
714
+ message = event.text;
715
+ scheduleCompletionGrace();
716
+ }
717
+ hooks?.onAssistantEvent?.(event, normalized);
718
+ }
719
+ const parsedError = extractProtocolError(parsed);
720
+ if (parsedError) {
721
+ protocolError = parsedError;
722
+ }
723
+ if (isCompletionEvent(parsed)) {
724
+ scheduleCompletionGrace();
725
+ }
726
+ };
727
+ const timeoutHandle = setTimeout(() => {
728
+ if (settled)
729
+ return;
730
+ forcedExit = true;
731
+ safeKill(child, "SIGTERM");
732
+ scheduleHardKill(child);
733
+ const detail = stderr.trim().length > 0 ? `: ${stderr.trim()}` : "";
734
+ finishReject(new Error(`AUTH_ERROR: codex CLI timed out after ${timeoutMs}ms${detail}`));
735
+ }, timeoutMs);
736
+ timeoutHandle.unref();
737
+ child.stdout?.setEncoding("utf8");
738
+ child.stdout?.on("data", (chunk) => {
739
+ const text = chunk.toString();
740
+ raw += text;
741
+ lineBuffer += text;
742
+ let idx;
743
+ while ((idx = lineBuffer.indexOf("\n")) !== -1) {
744
+ const line = lineBuffer.slice(0, idx);
745
+ lineBuffer = lineBuffer.slice(idx + 1);
746
+ handleLine(line);
747
+ }
748
+ });
749
+ child.stderr?.setEncoding("utf8");
750
+ child.stderr?.on("data", (chunk) => {
751
+ stderr += chunk.toString();
752
+ });
753
+ child.stdin?.on("error", (error) => {
754
+ if (settled || isIgnorableStdinError(error))
755
+ return;
756
+ finishReject(new Error(`AUTH_ERROR: codex CLI stdin failed (${error.message})`));
757
+ });
758
+ child.on("error", (error) => {
759
+ finishReject(new Error(`AUTH_ERROR: codex CLI failed (${error.message})`));
760
+ });
761
+ child.on("close", (code) => {
762
+ if (lineBuffer.trim()) {
763
+ handleLine(lineBuffer);
764
+ lineBuffer = "";
765
+ }
766
+ if (settled)
767
+ return;
768
+ if ((code ?? 0) !== 0) {
769
+ finishReject(new Error(`AUTH_ERROR: codex CLI failed (exit ${code ?? 0}): ${stderr || protocolError || "no output"}`));
770
+ return;
771
+ }
772
+ if (protocolError && !finalizeOutput()) {
773
+ finishReject(new Error(`AUTH_ERROR: codex CLI failed (${protocolError})`));
774
+ return;
775
+ }
776
+ finishResolve();
777
+ });
778
+ try {
779
+ child.stdin?.write(prompt);
780
+ child.stdin?.end();
781
+ }
782
+ catch (error) {
783
+ finishReject(new Error(`AUTH_ERROR: codex CLI stdin failed (${error.message})`));
784
+ }
785
+ });
786
+ };
544
787
  export const cliHealthy = (throwOnError = false) => {
545
788
  if (process.env.MCODA_CLI_STUB === "1") {
546
789
  return { ok: true, details: { stub: true } };
@@ -548,7 +791,11 @@ export const cliHealthy = (throwOnError = false) => {
548
791
  if (process.env.MCODA_SKIP_CLI_CHECKS === "1") {
549
792
  return { ok: true, details: { skipped: true } };
550
793
  }
551
- const result = spawnSync("codex", ["--version"], { encoding: "utf8", maxBuffer: CODEX_MAX_BUFFER_BYTES });
794
+ const codexCommand = resolveCodexCommand();
795
+ const result = spawnSync(codexCommand.command, [...codexCommand.preArgs, "--version"], {
796
+ encoding: "utf8",
797
+ maxBuffer: CODEX_MAX_BUFFER_BYTES,
798
+ });
552
799
  if (result.error) {
553
800
  const details = { reason: "missing_cli", error: result.error.message };
554
801
  if (throwOnError) {
@@ -569,62 +816,15 @@ export const cliHealthy = (throwOnError = false) => {
569
816
  }
570
817
  return { ok: true, details: { version: result.stdout?.toString().trim() } };
571
818
  };
572
- export const runCodexExec = (prompt, model) => {
819
+ export const runCodexExec = async (prompt, model) => {
573
820
  if (process.env.MCODA_CLI_STUB === "1") {
574
821
  const output = `qa-stub:${prompt}`;
575
822
  const raw = JSON.stringify({ type: "item.completed", item: { type: "agent_message", text: output } });
576
823
  return { output, raw };
577
824
  }
578
- const health = cliHealthy(true);
579
- const resolvedModel = model ?? "gpt-5.1-codex-max";
580
- const sandboxArgs = resolveSandboxArgs();
581
- const args = [...sandboxArgs.args, "exec", "--model", resolvedModel, "--json"];
582
- if (!sandboxArgs.bypass) {
583
- args.push("--full-auto");
584
- }
585
- const reasoningEffort = resolveReasoningEffort(resolvedModel);
586
- if (reasoningEffort) {
587
- args.push("-c", `model_reasoning_effort=${reasoningEffort}`);
588
- }
589
- args.push("-");
590
- const result = spawnSync("codex", args, {
591
- input: prompt,
592
- encoding: "utf8",
593
- maxBuffer: CODEX_MAX_BUFFER_BYTES,
594
- });
595
- if (result.error) {
596
- const error = new Error(`AUTH_ERROR: codex CLI failed (${result.error.message})`);
597
- error.details = { reason: "cli_error", cli: health.details };
598
- throw error;
599
- }
600
- if (result.status !== 0) {
601
- const error = new Error(`AUTH_ERROR: codex CLI failed (exit ${result.status}): ${result.stderr ?? result.stdout ?? ""}`);
602
- error.details = { reason: "cli_error", exitCode: result.status, stderr: result.stderr };
603
- throw error;
604
- }
605
- const raw = result.stdout?.toString() ?? "";
606
- const lines = raw.split(/\r?\n/).filter((l) => l.trim().length > 0);
607
- let message = "";
608
- for (const line of lines) {
609
- try {
610
- const parsed = JSON.parse(line);
611
- const event = extractAssistantText(parsed);
612
- if (!event)
613
- continue;
614
- if (event.kind === "delta") {
615
- message += event.text;
616
- continue;
617
- }
618
- message = event.text;
619
- }
620
- catch {
621
- /* ignore parse errors */
622
- }
623
- }
624
- if (!message) {
625
- message = lines[lines.length - 1] ?? "";
626
- }
627
- return { output: message.trim(), raw };
825
+ cliHealthy(true);
826
+ const result = await invokeCodexProcess(prompt, model);
827
+ return { output: result.output, raw: result.raw };
628
828
  };
629
829
  export async function* runCodexExecStream(prompt, model) {
630
830
  if (process.env.MCODA_CLI_STUB === "1") {
@@ -635,103 +835,55 @@ export async function* runCodexExecStream(prompt, model) {
635
835
  }
636
836
  cliHealthy(true);
637
837
  const resolvedModel = model ?? "gpt-5.1-codex-max";
638
- const sandboxArgs = resolveSandboxArgs();
639
- const args = [...sandboxArgs.args, "exec", "--model", resolvedModel, "--json"];
640
- if (!sandboxArgs.bypass) {
641
- args.push("--full-auto");
642
- }
643
- const reasoningEffort = resolveReasoningEffort(resolvedModel);
644
- if (reasoningEffort) {
645
- args.push("-c", `model_reasoning_effort=${reasoningEffort}`);
646
- }
647
- args.push("-");
648
- const child = spawn("codex", args, { stdio: ["pipe", "pipe", "pipe"] });
649
- child.stdin.write(prompt);
650
- child.stdin.end();
651
- let stderr = "";
652
- child.stderr?.setEncoding("utf8");
653
- child.stderr?.on("data", (chunk) => {
654
- stderr += chunk.toString();
655
- });
656
- const closePromise = new Promise((resolve, reject) => {
657
- child.on("error", (err) => reject(err));
658
- child.on("close", (code) => resolve(code ?? 0));
659
- });
660
- const parseLine = (line) => {
661
- try {
662
- const parsed = JSON.parse(line);
663
- return extractAssistantText(parsed);
664
- }
665
- catch {
666
- return null;
667
- }
668
- };
669
- const stream = child.stdout;
670
- stream?.setEncoding("utf8");
671
838
  const formatter = createStreamFormatter(resolvedModel);
672
- let buffer = "";
839
+ const queue = [];
840
+ const waiters = [];
841
+ let done = false;
842
+ let failure;
673
843
  let sawDelta = false;
674
- let streamError = null;
675
- try {
676
- for await (const chunk of stream ?? []) {
677
- buffer += chunk;
678
- let idx;
679
- while ((idx = buffer.indexOf("\n")) !== -1) {
680
- const line = buffer.slice(0, idx);
681
- buffer = buffer.slice(idx + 1);
682
- const normalized = line.replace(/\r$/, "");
683
- formatter.handleLine(normalized);
684
- const parsed = parseLine(normalized);
685
- if (!parsed)
686
- continue;
687
- if (parsed.kind === "delta") {
688
- sawDelta = true;
689
- yield { output: parsed.text, raw: normalized };
690
- continue;
691
- }
692
- if (!sawDelta) {
693
- const output = parsed.text.endsWith("\n") ? parsed.text : `${parsed.text}\n`;
694
- yield { output, raw: normalized };
695
- }
696
- sawDelta = false;
697
- }
844
+ const notify = () => {
845
+ while (waiters.length) {
846
+ waiters.shift()?.();
698
847
  }
699
- const trailing = buffer.replace(/\r$/, "");
700
- if (trailing) {
701
- formatter.handleLine(trailing);
702
- const parsed = parseLine(trailing);
703
- if (parsed) {
704
- if (parsed.kind === "delta") {
705
- sawDelta = true;
706
- yield { output: parsed.text, raw: trailing };
707
- }
708
- else if (!sawDelta) {
709
- const output = parsed.text.endsWith("\n") ? parsed.text : `${parsed.text}\n`;
710
- yield { output, raw: trailing };
711
- sawDelta = false;
712
- }
848
+ };
849
+ void invokeCodexProcess(prompt, model, {
850
+ onLine: (line) => {
851
+ formatter.handleLine(line);
852
+ },
853
+ onAssistantEvent: (event, rawLine) => {
854
+ if (event.kind === "delta") {
855
+ sawDelta = true;
856
+ queue.push({ output: event.text, raw: rawLine });
857
+ notify();
858
+ return;
713
859
  }
714
- }
715
- }
716
- catch (error) {
717
- streamError = error;
718
- }
719
- const exitCode = await closePromise;
720
- if (exitCode !== 0) {
721
- formatter.handleLine(JSON.stringify({
722
- type: "error",
723
- message: `codex exec failed with exit ${exitCode}: ${stderr || "no output"}`,
724
- }));
725
- const error = new Error(`AUTH_ERROR: codex CLI failed (exit ${exitCode}): ${stderr || "no output"}`);
726
- error.details = { reason: "cli_error", exitCode, stderr };
860
+ if (!sawDelta) {
861
+ const output = event.text.endsWith("\n") ? event.text : `${event.text}\n`;
862
+ queue.push({ output, raw: rawLine });
863
+ notify();
864
+ }
865
+ sawDelta = false;
866
+ },
867
+ })
868
+ .catch((error) => {
869
+ failure = error;
870
+ formatter.handleLine(JSON.stringify({ type: "error", message: failure.message }));
871
+ })
872
+ .finally(() => {
873
+ done = true;
727
874
  formatter.end();
728
- if (streamError) {
729
- throw streamError;
875
+ notify();
876
+ });
877
+ while (!done || queue.length > 0) {
878
+ if (queue.length > 0) {
879
+ yield queue.shift();
880
+ continue;
730
881
  }
731
- throw error;
882
+ await new Promise((resolve) => {
883
+ waiters.push(resolve);
884
+ });
732
885
  }
733
- formatter.end();
734
- if (streamError) {
735
- throw streamError;
886
+ if (failure) {
887
+ throw failure;
736
888
  }
737
889
  }
@@ -19,7 +19,7 @@ export class OpenAiCliAdapter {
19
19
  }
20
20
  async invoke(request) {
21
21
  const cliDetails = codexCliHealthy(true);
22
- const result = runCodexExec(request.input, this.config.model);
22
+ const result = await runCodexExec(request.input, this.config.model);
23
23
  return {
24
24
  output: result.output,
25
25
  adapter: this.config.adapter ?? "codex-cli",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcoda/agents",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "description": "Agent registry and capabilities for mcoda.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,8 +30,8 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@mcoda/shared": "0.1.35",
34
- "@mcoda/db": "0.1.35"
33
+ "@mcoda/shared": "0.1.36",
34
+ "@mcoda/db": "0.1.36"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "tsc -p tsconfig.json",