@basaltbytes/odoo-agentic-dev 0.1.0-beta.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 (134) hide show
  1. package/README.md +464 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +73 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/compose.d.ts +19 -0
  6. package/dist/commands/compose.js +29 -0
  7. package/dist/commands/compose.js.map +1 -0
  8. package/dist/commands/doctor.d.ts +35 -0
  9. package/dist/commands/doctor.js +209 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/down.d.ts +23 -0
  12. package/dist/commands/down.js +46 -0
  13. package/dist/commands/down.js.map +1 -0
  14. package/dist/commands/info.d.ts +10 -0
  15. package/dist/commands/info.js +42 -0
  16. package/dist/commands/info.js.map +1 -0
  17. package/dist/commands/json-report.d.ts +45 -0
  18. package/dist/commands/json-report.js +55 -0
  19. package/dist/commands/json-report.js.map +1 -0
  20. package/dist/commands/link-source.d.ts +33 -0
  21. package/dist/commands/link-source.js +116 -0
  22. package/dist/commands/link-source.js.map +1 -0
  23. package/dist/commands/list.d.ts +29 -0
  24. package/dist/commands/list.js +117 -0
  25. package/dist/commands/list.js.map +1 -0
  26. package/dist/commands/logs.d.ts +17 -0
  27. package/dist/commands/logs.js +28 -0
  28. package/dist/commands/logs.js.map +1 -0
  29. package/dist/commands/prune.d.ts +54 -0
  30. package/dist/commands/prune.js +118 -0
  31. package/dist/commands/prune.js.map +1 -0
  32. package/dist/commands/psql.d.ts +8 -0
  33. package/dist/commands/psql.js +24 -0
  34. package/dist/commands/psql.js.map +1 -0
  35. package/dist/commands/reset-db.d.ts +34 -0
  36. package/dist/commands/reset-db.js +86 -0
  37. package/dist/commands/reset-db.js.map +1 -0
  38. package/dist/commands/resolve-context.d.ts +17 -0
  39. package/dist/commands/resolve-context.js +30 -0
  40. package/dist/commands/resolve-context.js.map +1 -0
  41. package/dist/commands/run.d.ts +31 -0
  42. package/dist/commands/run.js +83 -0
  43. package/dist/commands/run.js.map +1 -0
  44. package/dist/commands/setup.d.ts +46 -0
  45. package/dist/commands/setup.js +93 -0
  46. package/dist/commands/setup.js.map +1 -0
  47. package/dist/commands/shell.d.ts +20 -0
  48. package/dist/commands/shell.js +46 -0
  49. package/dist/commands/shell.js.map +1 -0
  50. package/dist/commands/state-hooks.d.ts +28 -0
  51. package/dist/commands/state-hooks.js +86 -0
  52. package/dist/commands/state-hooks.js.map +1 -0
  53. package/dist/commands/test.d.ts +24 -0
  54. package/dist/commands/test.js +70 -0
  55. package/dist/commands/test.js.map +1 -0
  56. package/dist/commands/trailing-args.d.ts +10 -0
  57. package/dist/commands/trailing-args.js +14 -0
  58. package/dist/commands/trailing-args.js.map +1 -0
  59. package/dist/commands/up.d.ts +23 -0
  60. package/dist/commands/up.js +62 -0
  61. package/dist/commands/up.js.map +1 -0
  62. package/dist/commands/update.d.ts +7 -0
  63. package/dist/commands/update.js +31 -0
  64. package/dist/commands/update.js.map +1 -0
  65. package/dist/commands/worktree.d.ts +97 -0
  66. package/dist/commands/worktree.js +355 -0
  67. package/dist/commands/worktree.js.map +1 -0
  68. package/dist/config/load-recipe.d.ts +14 -0
  69. package/dist/config/load-recipe.js +75 -0
  70. package/dist/config/load-recipe.js.map +1 -0
  71. package/dist/config/schema.d.ts +9 -0
  72. package/dist/config/schema.js +190 -0
  73. package/dist/config/schema.js.map +1 -0
  74. package/dist/core/command-plan.d.ts +34 -0
  75. package/dist/core/command-plan.js +112 -0
  76. package/dist/core/command-plan.js.map +1 -0
  77. package/dist/core/compose-model.d.ts +18 -0
  78. package/dist/core/compose-model.js +80 -0
  79. package/dist/core/compose-model.js.map +1 -0
  80. package/dist/core/compose-project.d.ts +3 -0
  81. package/dist/core/compose-project.js +15 -0
  82. package/dist/core/compose-project.js.map +1 -0
  83. package/dist/core/database-name.d.ts +15 -0
  84. package/dist/core/database-name.js +59 -0
  85. package/dist/core/database-name.js.map +1 -0
  86. package/dist/core/environment.d.ts +84 -0
  87. package/dist/core/environment.js +78 -0
  88. package/dist/core/environment.js.map +1 -0
  89. package/dist/core/port-allocator.d.ts +30 -0
  90. package/dist/core/port-allocator.js +58 -0
  91. package/dist/core/port-allocator.js.map +1 -0
  92. package/dist/core/project-recipe.d.ts +163 -0
  93. package/dist/core/project-recipe.js +13 -0
  94. package/dist/core/project-recipe.js.map +1 -0
  95. package/dist/core/safety.d.ts +11 -0
  96. package/dist/core/safety.js +16 -0
  97. package/dist/core/safety.js.map +1 -0
  98. package/dist/core/worktree-context.d.ts +32 -0
  99. package/dist/core/worktree-context.js +94 -0
  100. package/dist/core/worktree-context.js.map +1 -0
  101. package/dist/errors/errors.d.ts +124 -0
  102. package/dist/errors/errors.js +129 -0
  103. package/dist/errors/errors.js.map +1 -0
  104. package/dist/index.d.ts +3 -0
  105. package/dist/index.js +2 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/platform/command-runner.d.ts +30 -0
  108. package/dist/platform/command-runner.js +65 -0
  109. package/dist/platform/command-runner.js.map +1 -0
  110. package/dist/platform/docker-compose.d.ts +69 -0
  111. package/dist/platform/docker-compose.js +239 -0
  112. package/dist/platform/docker-compose.js.map +1 -0
  113. package/dist/platform/git.d.ts +10 -0
  114. package/dist/platform/git.js +35 -0
  115. package/dist/platform/git.js.map +1 -0
  116. package/dist/platform/odoo-lifecycle.d.ts +30 -0
  117. package/dist/platform/odoo-lifecycle.js +109 -0
  118. package/dist/platform/odoo-lifecycle.js.map +1 -0
  119. package/dist/platform/port-probe.d.ts +7 -0
  120. package/dist/platform/port-probe.js +13 -0
  121. package/dist/platform/port-probe.js.map +1 -0
  122. package/dist/platform/process-supervisor.d.ts +16 -0
  123. package/dist/platform/process-supervisor.js +23 -0
  124. package/dist/platform/process-supervisor.js.map +1 -0
  125. package/dist/platform/state-store.d.ts +37 -0
  126. package/dist/platform/state-store.js +122 -0
  127. package/dist/platform/state-store.js.map +1 -0
  128. package/dist/suppress-sqlite-warning.d.ts +1 -0
  129. package/dist/suppress-sqlite-warning.js +18 -0
  130. package/dist/suppress-sqlite-warning.js.map +1 -0
  131. package/dist/testing/fake-adapters.d.ts +34 -0
  132. package/dist/testing/fake-adapters.js +90 -0
  133. package/dist/testing/fake-adapters.js.map +1 -0
  134. package/package.json +78 -0
@@ -0,0 +1,29 @@
1
+ import { Effect } from "effect";
2
+ import { Command } from "effect/unstable/cli";
3
+ import type { EnvironmentRow, EnvStatus } from "../core/environment.js";
4
+ import type { StateError } from "../errors/errors.js";
5
+ import type { DockerComposeApi } from "../platform/docker-compose.js";
6
+ import type { StateStoreApi } from "../platform/state-store.js";
7
+ export type ListEntry = {
8
+ readonly row: EnvironmentRow;
9
+ readonly status: EnvStatus;
10
+ };
11
+ /** Coarse relative age for table output; unparseable timestamps read as fresh. */
12
+ export declare const relativeAge: (iso: string, nowIso: string) => string;
13
+ /** Plain padded-column table (no table dependency). */
14
+ export declare const buildListTable: (entries: ReadonlyArray<ListEntry>, nowIso: string) => string;
15
+ /**
16
+ * State rows reconciled against docker reality. Labeled stacks unknown to the
17
+ * registry are adopted (inserted best-effort from their labels) before
18
+ * listing. When docker itself is unreachable the registry is still listed,
19
+ * with every stack necessarily reading as `vanished` — callers should warn.
20
+ */
21
+ export declare const collectListEntries: (projectId: string | undefined) => Effect.Effect<{
22
+ readonly entries: ReadonlyArray<ListEntry>;
23
+ readonly dockerAvailable: boolean;
24
+ }, StateError, StateStoreApi | DockerComposeApi>;
25
+ export declare const listCommand: Command.Command<"list", {
26
+ readonly allProjects: boolean;
27
+ readonly json: boolean;
28
+ readonly config: import("effect/Option").Option<string>;
29
+ }, {}, import("../errors/errors.js").ConfigLoadError | import("../errors/errors.js").ConfigValidationError | StateError, DockerComposeApi | StateStoreApi>;
@@ -0,0 +1,117 @@
1
+ import { basename } from "node:path";
2
+ import { Console, Effect } from "effect";
3
+ import { Command, Flag } from "effect/unstable/cli";
4
+ import { classifyEnvironments } from "../core/environment.js";
5
+ import { DockerCompose } from "../platform/docker-compose.js";
6
+ import { StateStore } from "../platform/state-store.js";
7
+ import { resolveProjectId } from "./resolve-context.js";
8
+ /** Coarse relative age for table output; unparseable timestamps read as fresh. */
9
+ export const relativeAge = (iso, nowIso) => {
10
+ const ms = Date.parse(nowIso) - Date.parse(iso);
11
+ if (!Number.isFinite(ms) || ms < 60_000)
12
+ return "just now";
13
+ const minutes = Math.floor(ms / 60_000);
14
+ if (minutes < 60)
15
+ return `${minutes}m ago`;
16
+ const hours = Math.floor(minutes / 60);
17
+ if (hours < 24)
18
+ return `${hours}h ago`;
19
+ return `${Math.floor(hours / 24)}d ago`;
20
+ };
21
+ const flagsCell = (row) => {
22
+ const flags = [row.shared ? "shared" : null, row.templateDb !== null ? "template" : null].filter((flag) => flag !== null);
23
+ return flags.length === 0 ? "-" : flags.join(" ");
24
+ };
25
+ /** Plain padded-column table (no table dependency). */
26
+ export const buildListTable = (entries, nowIso) => {
27
+ const header = ["WORKTREE", "DATABASE", "PORT", "STATUS", "LAST USED", "FLAGS"];
28
+ const body = entries.map(({ row, status }) => [
29
+ row.worktreeName,
30
+ row.databaseName,
31
+ // adopted rows carry port 0: the labels do not record the port
32
+ row.odooHttpPort === 0 ? "?" : String(row.odooHttpPort),
33
+ status,
34
+ relativeAge(row.lastUsedAt, nowIso),
35
+ flagsCell(row),
36
+ ]);
37
+ const widths = header.map((cell, i) => Math.max(cell.length, ...body.map((r) => r[i].length)));
38
+ return [header, ...body]
39
+ .map((cells) => cells
40
+ .map((cell, i) => cell.padEnd(widths[i]))
41
+ .join(" ")
42
+ .trimEnd())
43
+ .join("\n");
44
+ };
45
+ const adoptionUpsert = (stack) => stack.projectId === null || stack.database === null || stack.rootDir === null
46
+ ? null
47
+ : {
48
+ composeProject: stack.composeProject,
49
+ projectId: stack.projectId,
50
+ databaseName: stack.database,
51
+ rootDir: stack.rootDir,
52
+ // the branch label is "" for "no branch" — map it back to null
53
+ worktreeName: stack.branch !== null && stack.branch !== "" ? stack.branch : basename(stack.rootDir),
54
+ branch: stack.branch === "" ? null : stack.branch,
55
+ odooHttpPort: 0,
56
+ shared: false,
57
+ };
58
+ /**
59
+ * State rows reconciled against docker reality. Labeled stacks unknown to the
60
+ * registry are adopted (inserted best-effort from their labels) before
61
+ * listing. When docker itself is unreachable the registry is still listed,
62
+ * with every stack necessarily reading as `vanished` — callers should warn.
63
+ */
64
+ export const collectListEntries = (projectId) => Effect.gen(function* () {
65
+ const store = yield* StateStore;
66
+ const compose = yield* DockerCompose;
67
+ const docker = yield* Effect.all([
68
+ compose.listProjects(),
69
+ compose.listLabeledContainers(),
70
+ ]).pipe(Effect.map(([projects, labeled]) => ({ projects, labeled, available: true })), Effect.catch(() => Effect.succeed({
71
+ projects: [],
72
+ labeled: [],
73
+ available: false,
74
+ })));
75
+ const known = new Set((yield* store.list({})).map((row) => row.composeProject));
76
+ for (const stack of docker.labeled) {
77
+ if (known.has(stack.composeProject))
78
+ continue;
79
+ const upsert = adoptionUpsert(stack);
80
+ if (upsert !== null)
81
+ yield* store.upsert(upsert);
82
+ }
83
+ const rows = yield* store.list({ projectId });
84
+ const classified = classifyEnvironments({
85
+ rows,
86
+ dockerProjects: docker.projects,
87
+ probes: new Map(),
88
+ olderThanDays: null,
89
+ allowShared: true,
90
+ now: new Date().toISOString(),
91
+ });
92
+ return {
93
+ entries: classified.map(({ row, status }) => ({ row, status })),
94
+ dockerAvailable: docker.available,
95
+ };
96
+ });
97
+ export const listCommand = Command.make("list", {
98
+ allProjects: Flag.boolean("all-projects").pipe(Flag.withDescription("every project in the registry (works without a config)")),
99
+ json: Flag.boolean("json").pipe(Flag.withDescription("print machine-readable JSON")),
100
+ config: Flag.string("config").pipe(Flag.optional),
101
+ }, (flags) => Effect.gen(function* () {
102
+ const projectId = yield* resolveProjectId(flags.config, flags.allProjects);
103
+ const { dockerAvailable, entries } = yield* collectListEntries(projectId);
104
+ if (!dockerAvailable) {
105
+ yield* Console.error("warning: docker is unreachable — every stack reads as vanished");
106
+ }
107
+ if (flags.json) {
108
+ yield* Console.log(JSON.stringify(entries.map(({ row, status }) => ({ ...row, status })), null, 2));
109
+ }
110
+ else if (entries.length === 0) {
111
+ yield* Console.log("No environments recorded.");
112
+ }
113
+ else {
114
+ yield* Console.log(buildListTable(entries, new Date().toISOString()));
115
+ }
116
+ }));
117
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { Effect, Option } from "effect";
2
+ import { Command } from "effect/unstable/cli";
3
+ import type { OdooAgenticDevConfig } from "../core/project-recipe.js";
4
+ import type { WorktreeContext } from "../core/worktree-context.js";
5
+ import type { ComposeCommandError, DockerUnavailableError } from "../errors/errors.js";
6
+ import type { DockerComposeApi } from "../platform/docker-compose.js";
7
+ export declare const buildLogsArgs: (serviceName: string, follow: boolean) => Array<string>;
8
+ /** Stream `docker compose logs` for one service (default: the odoo service). */
9
+ export declare const runLogs: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, options: {
10
+ readonly service: string | undefined;
11
+ readonly follow: boolean;
12
+ }) => Effect.Effect<void, ComposeCommandError | DockerUnavailableError, DockerComposeApi>;
13
+ export declare const logsCommand: Command.Command<"logs", {
14
+ readonly service: Option.Option<string>;
15
+ readonly follow: boolean;
16
+ readonly config: Option.Option<string>;
17
+ }, {}, import("../errors/errors.js").RuntimeError, import("../platform/git.js").GitApi | DockerComposeApi>;
@@ -0,0 +1,28 @@
1
+ import { Effect, Option } from "effect";
2
+ import { Argument, Command, Flag } from "effect/unstable/cli";
3
+ import { DockerCompose } from "../platform/docker-compose.js";
4
+ import { resolveContext } from "./resolve-context.js";
5
+ export const buildLogsArgs = (serviceName, follow) => [
6
+ "logs",
7
+ ...(follow ? ["-f"] : []),
8
+ serviceName,
9
+ ];
10
+ /** Stream `docker compose logs` for one service (default: the odoo service). */
11
+ export const runLogs = (recipe, ctx, options) => Effect.gen(function* () {
12
+ const compose = yield* DockerCompose;
13
+ yield* compose.ensureAvailable();
14
+ const ref = yield* compose.prepareComposeFile(recipe, ctx);
15
+ yield* compose.stream(ref, buildLogsArgs(options.service ?? recipe.odoo.serviceName, options.follow));
16
+ });
17
+ export const logsCommand = Command.make("logs", {
18
+ service: Argument.string("service").pipe(Argument.optional, Argument.withDescription("compose service (defaults to the odoo service)")),
19
+ follow: Flag.boolean("follow").pipe(Flag.withDescription("follow log output")),
20
+ config: Flag.string("config").pipe(Flag.optional),
21
+ }, (flags) => Effect.gen(function* () {
22
+ const { ctx, recipe } = yield* resolveContext(flags.config);
23
+ yield* runLogs(recipe, ctx, {
24
+ service: Option.getOrUndefined(flags.service),
25
+ follow: flags.follow,
26
+ });
27
+ }));
28
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAI9D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,WAAmB,EAAE,MAAe,EAAiB,EAAE,CAAC;IACpF,MAAM;IACN,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzB,WAAW;CACZ,CAAC;AAEF,gFAAgF;AAChF,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,MAA4B,EAC5B,GAAoB,EACpB,OAA2E,EACU,EAAE,CACvF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC;IACrC,KAAK,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3D,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CACnB,GAAG,EACH,aAAa,CAAC,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAC1E,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CACrC,MAAM,EACN;IACE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CACtC,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,eAAe,CAAC,gDAAgD,CAAC,CAC3E;IACD,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC;IAC9E,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;CAClD,EACD,CAAC,KAAK,EAAE,EAAE,CACR,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5D,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE;QAC1B,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC;QAC7C,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC,CAAC;AACL,CAAC,CAAC,CACL,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { Effect, Option } from "effect";
2
+ import { Command } from "effect/unstable/cli";
3
+ import type { ClassifiedEnvironment, EnvironmentProbe, EnvironmentRow, PruneReason } from "../core/environment.js";
4
+ import type { ComposeCommandError, StateError } from "../errors/errors.js";
5
+ import type { DockerComposeApi } from "../platform/docker-compose.js";
6
+ import type { GitApi } from "../platform/git.js";
7
+ import type { StateStoreApi } from "../platform/state-store.js";
8
+ /**
9
+ * Filesystem/git facts per registry row. A probe failure (root dir exists but
10
+ * is no longer a repo, odd git states) maps to `branchExists: null` — the
11
+ * conservative "branch rule does not apply" answer — never to a prune.
12
+ */
13
+ export declare const buildProbes: (rows: ReadonlyArray<EnvironmentRow>) => Effect.Effect<ReadonlyMap<string, EnvironmentProbe>, never, GitApi>;
14
+ export type PruneOptions = {
15
+ /** stale threshold; null = staleness never makes a row a candidate */
16
+ readonly olderThanDays: number | null;
17
+ /** without it the run is a dry run: candidates are computed, nothing acts */
18
+ readonly yes: boolean;
19
+ readonly allowShared: boolean;
20
+ /** undefined = all projects */
21
+ readonly projectId: string | undefined;
22
+ /** auto-clean shield: the environment the calling command runs in is never a candidate */
23
+ readonly excludeComposeProject?: string | undefined;
24
+ };
25
+ export type PruneRemoval = {
26
+ readonly composeProject: string;
27
+ readonly reason: PruneReason;
28
+ };
29
+ export type PruneReport = {
30
+ readonly candidates: ReadonlyArray<ClassifiedEnvironment>;
31
+ readonly removed: ReadonlyArray<PruneRemoval>;
32
+ };
33
+ /**
34
+ * The prune routine shared by the `prune` command and `cleanup.auto`. Unlike
35
+ * the auto-clean warning path this has no notion of a "current" environment;
36
+ * callers that have one pass `excludeComposeProject`.
37
+ */
38
+ export declare const runPrune: (options: PruneOptions) => Effect.Effect<PruneReport, StateError | ComposeCommandError, StateStoreApi | DockerComposeApi | GitApi>;
39
+ /**
40
+ * Render a prune report and apply the exit-code contract: a dry run that
41
+ * found candidates exits 1 (via process.exitCode — the Effect still succeeds).
42
+ */
43
+ export declare const reportPrune: (report: PruneReport, options: {
44
+ readonly yes: boolean;
45
+ readonly json: boolean;
46
+ }) => Effect.Effect<void>;
47
+ export declare const pruneCommand: Command.Command<"prune", {
48
+ readonly olderThan: Option.Option<number>;
49
+ readonly allProjects: boolean;
50
+ readonly yes: boolean;
51
+ readonly allowShared: boolean;
52
+ readonly json: boolean;
53
+ readonly config: Option.Option<string>;
54
+ }, {}, import("../errors/errors.js").ConfigLoadError | import("../errors/errors.js").ConfigValidationError | ComposeCommandError | StateError, GitApi | DockerComposeApi | StateStoreApi>;
@@ -0,0 +1,118 @@
1
+ import { existsSync } from "node:fs";
2
+ import { Console, Effect, Option } from "effect";
3
+ import { Command, Flag } from "effect/unstable/cli";
4
+ import { classifyEnvironments } from "../core/environment.js";
5
+ import { DockerCompose } from "../platform/docker-compose.js";
6
+ import { Git } from "../platform/git.js";
7
+ import { StateStore } from "../platform/state-store.js";
8
+ import { relativeAge } from "./list.js";
9
+ import { resolveProjectId } from "./resolve-context.js";
10
+ /**
11
+ * Filesystem/git facts per registry row. A probe failure (root dir exists but
12
+ * is no longer a repo, odd git states) maps to `branchExists: null` — the
13
+ * conservative "branch rule does not apply" answer — never to a prune.
14
+ */
15
+ export const buildProbes = (rows) => Effect.gen(function* () {
16
+ const git = yield* Git;
17
+ const probes = new Map();
18
+ for (const row of rows) {
19
+ const rootDirExists = existsSync(row.rootDir);
20
+ const branchExists = rootDirExists && row.branch !== null
21
+ ? yield* git
22
+ .branchExists(row.rootDir, row.branch)
23
+ .pipe(Effect.catch(() => Effect.succeed(null)))
24
+ : null;
25
+ probes.set(row.composeProject, { rootDirExists, branchExists });
26
+ }
27
+ return probes;
28
+ });
29
+ /**
30
+ * The prune routine shared by the `prune` command and `cleanup.auto`. Unlike
31
+ * the auto-clean warning path this has no notion of a "current" environment;
32
+ * callers that have one pass `excludeComposeProject`.
33
+ */
34
+ export const runPrune = (options) => Effect.gen(function* () {
35
+ const store = yield* StateStore;
36
+ const compose = yield* DockerCompose;
37
+ const rows = yield* store.list({ projectId: options.projectId });
38
+ // an empty registry can have no candidates: skip docker discovery entirely
39
+ // so `prune` still works (and exits 0) where docker is absent or down
40
+ if (rows.length === 0)
41
+ return { candidates: [], removed: [] };
42
+ const dockerProjects = yield* compose.listProjects();
43
+ const probes = yield* buildProbes(rows);
44
+ const classified = classifyEnvironments({
45
+ rows,
46
+ dockerProjects,
47
+ probes,
48
+ olderThanDays: options.olderThanDays,
49
+ allowShared: options.allowShared,
50
+ now: new Date().toISOString(),
51
+ });
52
+ const candidates = classified.filter((c) => c.reason !== "keep" &&
53
+ c.reason !== "shared-skipped" &&
54
+ c.row.composeProject !== options.excludeComposeProject);
55
+ if (!options.yes)
56
+ return { candidates, removed: [] };
57
+ const removed = [];
58
+ for (const candidate of candidates) {
59
+ // teardown order: docker resources first, then the row — failing midway
60
+ // leaves the row behind for the next run instead of orphaning containers.
61
+ // vanished rows are swept too: a manual `docker compose down` removes
62
+ // the containers but can leave labeled volumes behind (no-op otherwise)
63
+ yield* compose.removeByLabel(candidate.row.composeProject);
64
+ yield* store.remove(candidate.row.composeProject);
65
+ removed.push({ composeProject: candidate.row.composeProject, reason: candidate.reason });
66
+ }
67
+ return { candidates, removed };
68
+ });
69
+ /**
70
+ * Render a prune report and apply the exit-code contract: a dry run that
71
+ * found candidates exits 1 (via process.exitCode — the Effect still succeeds).
72
+ */
73
+ export const reportPrune = (report, options) => Effect.gen(function* () {
74
+ if (options.json) {
75
+ yield* Console.log(JSON.stringify({
76
+ applied: options.yes,
77
+ candidates: report.candidates.map((c) => ({
78
+ ...c.row,
79
+ status: c.status,
80
+ reason: c.reason,
81
+ })),
82
+ removed: report.removed,
83
+ }, null, 2));
84
+ }
85
+ else if (report.candidates.length === 0) {
86
+ yield* Console.log("Nothing to prune.");
87
+ }
88
+ else {
89
+ const now = new Date().toISOString();
90
+ for (const c of report.candidates) {
91
+ yield* Console.log(`${c.row.composeProject} ${c.row.databaseName} ${relativeAge(c.row.lastUsedAt, now)} ${c.reason}`);
92
+ }
93
+ yield* options.yes
94
+ ? Console.log(`Removed ${report.removed.length} environment(s).`)
95
+ : Console.log("Dry run — re-run with --yes to remove these environments.");
96
+ }
97
+ if (!options.yes && report.candidates.length > 0) {
98
+ process.exitCode = 1;
99
+ }
100
+ });
101
+ export const pruneCommand = Command.make("prune", {
102
+ olderThan: Flag.integer("older-than").pipe(Flag.optional, Flag.withDescription("also prune environments unused for more than <days>")),
103
+ allProjects: Flag.boolean("all-projects").pipe(Flag.withDescription("every project in the registry (works without a config)")),
104
+ yes: Flag.boolean("yes").pipe(Flag.withDescription("actually remove (without it: dry run, exit 1 when candidates exist)")),
105
+ allowShared: Flag.boolean("allow-shared"),
106
+ json: Flag.boolean("json").pipe(Flag.withDescription("print machine-readable JSON")),
107
+ config: Flag.string("config").pipe(Flag.optional),
108
+ }, (flags) => Effect.gen(function* () {
109
+ const projectId = yield* resolveProjectId(flags.config, flags.allProjects);
110
+ const report = yield* runPrune({
111
+ olderThanDays: Option.getOrUndefined(flags.olderThan) ?? null,
112
+ yes: flags.yes,
113
+ allowShared: flags.allowShared,
114
+ projectId,
115
+ });
116
+ yield* reportPrune(report, { yes: flags.yes, json: flags.json });
117
+ }));
118
+ //# sourceMappingURL=prune.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prune.js","sourceRoot":"","sources":["../../src/commands/prune.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { Command } from "effect/unstable/cli";
2
+ import type { OdooAgenticDevConfig } from "../core/project-recipe.js";
3
+ import type { WorktreeContext } from "../core/worktree-context.js";
4
+ export declare const buildPsqlArgs: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, extraArgs: ReadonlyArray<string>) => Array<string>;
5
+ export declare const psqlCommand: Command.Command<"psql", {
6
+ readonly args: readonly string[];
7
+ readonly config: import("effect/Option").Option<string>;
8
+ }, {}, import("../errors/errors.js").RuntimeError, import("../platform/command-runner.js").CommandRunnerApi | import("../platform/git.js").GitApi | import("../platform/docker-compose.js").DockerComposeApi | import("../platform/state-store.js").StateStoreApi>;
@@ -0,0 +1,24 @@
1
+ import { Effect } from "effect";
2
+ import { Argument, Command, Flag } from "effect/unstable/cli";
3
+ import { resolveContext } from "./resolve-context.js";
4
+ import { runInteractivePassthrough } from "./shell.js";
5
+ import { trailingOperands } from "./trailing-args.js";
6
+ export const buildPsqlArgs = (recipe, ctx, extraArgs) => [
7
+ "exec",
8
+ recipe.odoo.databaseServiceName,
9
+ "psql",
10
+ "-U",
11
+ "odoo",
12
+ "-d",
13
+ ctx.databaseName,
14
+ ...extraArgs,
15
+ ];
16
+ export const psqlCommand = Command.make("psql", {
17
+ args: Argument.string("args").pipe(Argument.variadic(), Argument.withDescription("extra psql arguments (pass them after --)")),
18
+ config: Flag.string("config").pipe(Flag.optional),
19
+ }, (flags) => Effect.gen(function* () {
20
+ const { ctx, recipe } = yield* resolveContext(flags.config);
21
+ const extraArgs = [...flags.args, ...trailingOperands()];
22
+ yield* runInteractivePassthrough(recipe, ctx, buildPsqlArgs(recipe, ctx, extraArgs));
23
+ }));
24
+ //# sourceMappingURL=psql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psql.js","sourceRoot":"","sources":["../../src/commands/psql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAG9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,MAA4B,EAC5B,GAAoB,EACpB,SAAgC,EACjB,EAAE,CAAC;IAClB,MAAM;IACN,MAAM,CAAC,IAAI,CAAC,mBAAmB;IAC/B,MAAM;IACN,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,GAAG,CAAC,YAAY;IAChB,GAAG,SAAS;CACb,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CACrC,MAAM,EACN;IACE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAChC,QAAQ,CAAC,QAAQ,EAAE,EACnB,QAAQ,CAAC,eAAe,CAAC,2CAA2C,CAAC,CACtE;IACD,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;CAClD,EACD,CAAC,KAAK,EAAE,EAAE,CACR,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,GAAG,gBAAgB,EAAE,CAAC,CAAC;IACzD,KAAK,CAAC,CAAC,yBAAyB,CAAC,MAAM,EAAE,GAAG,EAAE,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;AACvF,CAAC,CAAC,CACL,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { Effect, Option } from "effect";
2
+ import { Command } from "effect/unstable/cli";
3
+ import type { OdooAgenticDevConfig } from "../core/project-recipe.js";
4
+ import type { WorktreeContext } from "../core/worktree-context.js";
5
+ import type { ResetPath } from "../core/environment.js";
6
+ import type { OdooLifecycleApi } from "../platform/odoo-lifecycle.js";
7
+ import type { StateStoreApi } from "../platform/state-store.js";
8
+ import type { RuntimeError, SharedDatabaseProtectionError } from "../errors/errors.js";
9
+ export declare const guardReset: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, allowShared: boolean) => Effect.Effect<void, SharedDatabaseProtectionError>;
10
+ export declare const parseModulesFlag: (value: string | undefined) => Array<string> | undefined;
11
+ export type ResetFlowOptions = {
12
+ readonly noTemplate: boolean;
13
+ readonly refreshTemplate: boolean;
14
+ readonly modules: ReadonlyArray<string> | undefined;
15
+ readonly withoutDemo: string | undefined;
16
+ /** decorative-output sink; defaults to Console.log (json mode passes a recorder) */
17
+ readonly say?: ((line: string) => Effect.Effect<void>) | undefined;
18
+ };
19
+ /**
20
+ * The shared reset-db flow (used by `reset-db` and `setup`): decide between a
21
+ * fast template restore and a full init, run it, and snapshot after the
22
+ * post-init hooks when the decision calls for it. The pre-action line is
23
+ * printed before any destructive work on every path.
24
+ */
25
+ export declare const runResetFlow: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, options: ResetFlowOptions) => Effect.Effect<ResetPath, RuntimeError, OdooLifecycleApi | StateStoreApi>;
26
+ export declare const resetDbCommand: Command.Command<"reset-db", {
27
+ readonly allowShared: boolean;
28
+ readonly modules: Option.Option<string>;
29
+ readonly withoutDemo: Option.Option<string>;
30
+ readonly noTemplate: boolean;
31
+ readonly refreshTemplate: boolean;
32
+ readonly json: boolean;
33
+ readonly config: Option.Option<string>;
34
+ }, {}, RuntimeError, import("../platform/git.js").GitApi | OdooLifecycleApi | StateStoreApi>;
@@ -0,0 +1,86 @@
1
+ import { Console, Effect, Option } from "effect";
2
+ import { Command, Flag } from "effect/unstable/cli";
3
+ import { assertSharedDatabaseAllowed } from "../core/safety.js";
4
+ import { computeTemplateKey, decideResetPath, templateDbName } from "../core/environment.js";
5
+ import { OdooLifecycle } from "../platform/odoo-lifecycle.js";
6
+ import { StateStore } from "../platform/state-store.js";
7
+ import { resolveContext } from "./resolve-context.js";
8
+ import { recordEnvironment } from "./state-hooks.js";
9
+ import { resetPathActions, withJsonReport } from "./json-report.js";
10
+ export const guardReset = (recipe, ctx, allowShared) => assertSharedDatabaseAllowed({
11
+ databaseName: ctx.databaseName,
12
+ sharedDatabase: recipe.project.sharedDatabase,
13
+ allowShared,
14
+ action: "reset-db",
15
+ });
16
+ export const parseModulesFlag = (value) => value
17
+ ?.split(",")
18
+ .map((m) => m.trim())
19
+ .filter((m) => m.length > 0);
20
+ /**
21
+ * The shared reset-db flow (used by `reset-db` and `setup`): decide between a
22
+ * fast template restore and a full init, run it, and snapshot after the
23
+ * post-init hooks when the decision calls for it. The pre-action line is
24
+ * printed before any destructive work on every path.
25
+ */
26
+ export const runResetFlow = (recipe, ctx, options) => Effect.gen(function* () {
27
+ const say = options.say ?? Console.log;
28
+ const store = yield* StateStore;
29
+ const lifecycle = yield* OdooLifecycle;
30
+ const expectedKey = computeTemplateKey(recipe);
31
+ const row = yield* store.get(ctx.composeProjectName);
32
+ const path = decideResetPath({
33
+ row,
34
+ expectedKey,
35
+ databaseName: ctx.databaseName,
36
+ noTemplate: options.noTemplate,
37
+ refreshTemplate: options.refreshTemplate,
38
+ hasOverrides: options.modules !== undefined || options.withoutDemo !== undefined,
39
+ });
40
+ if (path === "restore") {
41
+ yield* say(`Restoring database: ${ctx.databaseName} (from template ${templateDbName(ctx.databaseName)})`);
42
+ yield* lifecycle.restoreFromTemplate(recipe, ctx);
43
+ yield* say("Restored from template (post-init hooks already baked in).");
44
+ return path;
45
+ }
46
+ yield* say(`Resetting database: ${ctx.databaseName}`);
47
+ yield* say(`Compose project: ${ctx.composeProjectName}`);
48
+ yield* lifecycle.resetDatabase(recipe, ctx, {
49
+ modules: options.modules,
50
+ withoutDemo: options.withoutDemo,
51
+ });
52
+ yield* lifecycle.runPostInitHooks(recipe, ctx);
53
+ if (path === "full-then-snapshot") {
54
+ yield* lifecycle.snapshotTemplate(recipe, ctx);
55
+ yield* store.setTemplate(ctx.composeProjectName, {
56
+ databaseName: templateDbName(ctx.databaseName),
57
+ key: expectedKey,
58
+ });
59
+ yield* say(`Template snapshot saved: ${templateDbName(ctx.databaseName)}`);
60
+ }
61
+ return path;
62
+ });
63
+ export const resetDbCommand = Command.make("reset-db", {
64
+ allowShared: Flag.boolean("allow-shared"),
65
+ modules: Flag.string("modules").pipe(Flag.optional, Flag.withDescription("comma-separated module list (defaults to recipe initialModules)")),
66
+ withoutDemo: Flag.string("without-demo").pipe(Flag.optional),
67
+ noTemplate: Flag.boolean("no-template").pipe(Flag.withDescription("full init even when a template snapshot exists (template kept)")),
68
+ refreshTemplate: Flag.boolean("refresh-template").pipe(Flag.withDescription("full init and take a fresh template snapshot")),
69
+ json: Flag.boolean("json").pipe(Flag.withDescription("suppress decorative output; print one final JSON report line")),
70
+ config: Flag.string("config").pipe(Flag.optional),
71
+ }, (flags) => withJsonReport("reset-db", flags.json, (report) => Effect.gen(function* () {
72
+ const { ctx, recipe } = yield* resolveContext(flags.config);
73
+ yield* report.setContext(ctx);
74
+ yield* guardReset(recipe, ctx, flags.allowShared);
75
+ yield* recordEnvironment(recipe, ctx);
76
+ const path = yield* runResetFlow(recipe, ctx, {
77
+ noTemplate: flags.noTemplate,
78
+ refreshTemplate: flags.refreshTemplate,
79
+ modules: parseModulesFlag(Option.getOrUndefined(flags.modules)),
80
+ withoutDemo: Option.getOrUndefined(flags.withoutDemo),
81
+ say: report.say,
82
+ });
83
+ yield* Effect.forEach(resetPathActions(path), report.action);
84
+ yield* report.say(`Done. Odoo URL: ${ctx.odooBaseUrl}/web?db=${ctx.databaseName}`);
85
+ })));
86
+ //# sourceMappingURL=reset-db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset-db.js","sourceRoot":"","sources":["../../src/commands/reset-db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { Effect, Option } from "effect";
2
+ import type { WorktreeContext } from "../core/worktree-context.js";
3
+ import type { OdooAgenticDevConfig } from "../core/project-recipe.js";
4
+ import type { GitApi } from "../platform/git.js";
5
+ import type { ConfigLoadError, ConfigValidationError, RuntimeError } from "../errors/errors.js";
6
+ export type ResolvedContext = {
7
+ readonly recipe: OdooAgenticDevConfig;
8
+ readonly ctx: WorktreeContext;
9
+ };
10
+ /** Shared preamble for every command: config + git -> context. */
11
+ export declare const resolveContext: (configFlag: Option.Option<string>) => Effect.Effect<ResolvedContext, RuntimeError, GitApi>;
12
+ /**
13
+ * Project scope for `list`/`prune`: `--all-projects` means "no filter" and
14
+ * deliberately skips config discovery entirely, so these commands work from
15
+ * any directory; otherwise the discovered recipe's project id is the scope.
16
+ */
17
+ export declare const resolveProjectId: (configFlag: Option.Option<string>, allProjects: boolean) => Effect.Effect<string | undefined, ConfigLoadError | ConfigValidationError>;
@@ -0,0 +1,30 @@
1
+ import { Effect, Option } from "effect";
2
+ import { loadRecipe } from "../config/load-recipe.js";
3
+ import { buildWorktreeContext } from "../core/worktree-context.js";
4
+ import { Git } from "../platform/git.js";
5
+ /** Shared preamble for every command: config + git -> context. */
6
+ export const resolveContext = (configFlag) => Effect.gen(function* () {
7
+ const env = process.env;
8
+ const { recipe, rootDir } = yield* loadRecipe({
9
+ cwd: process.cwd(),
10
+ explicitPath: Option.getOrUndefined(configFlag),
11
+ env,
12
+ });
13
+ const git = yield* Git;
14
+ const gitState = yield* git.state(rootDir);
15
+ const ctx = yield* buildWorktreeContext({ rootDir, recipe, env, git: gitState });
16
+ return { recipe, ctx };
17
+ });
18
+ /**
19
+ * Project scope for `list`/`prune`: `--all-projects` means "no filter" and
20
+ * deliberately skips config discovery entirely, so these commands work from
21
+ * any directory; otherwise the discovered recipe's project id is the scope.
22
+ */
23
+ export const resolveProjectId = (configFlag, allProjects) => allProjects
24
+ ? Effect.succeed(undefined)
25
+ : loadRecipe({
26
+ cwd: process.cwd(),
27
+ explicitPath: Option.getOrUndefined(configFlag),
28
+ env: process.env,
29
+ }).pipe(Effect.map(({ recipe }) => recipe.project.id));
30
+ //# sourceMappingURL=resolve-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-context.js","sourceRoot":"","sources":["../../src/commands/resolve-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AASzC,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,UAAiC,EACqB,EAAE,CACxD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC;QAC5C,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,YAAY,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC;QAC/C,GAAG;KACJ,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC;IACvB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjF,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC;AAEL;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,UAAiC,EACjC,WAAoB,EACwD,EAAE,CAC9E,WAAW;IACT,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;IAC3B,CAAC,CAAC,UAAU,CAAC;QACT,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,YAAY,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC;QAC/C,GAAG,EAAE,OAAO,CAAC,GAAG;KACjB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC"}