@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,209 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { Console, Effect, Option } from "effect";
3
+ import { Command, Flag } from "effect/unstable/cli";
4
+ import { loadRecipe } from "../config/load-recipe.js";
5
+ import { classifyEnvironments } from "../core/environment.js";
6
+ import { buildWorktreeContext } from "../core/worktree-context.js";
7
+ import { tail } from "../errors/errors.js";
8
+ import { CommandRunner } from "../platform/command-runner.js";
9
+ import { DockerCompose } from "../platform/docker-compose.js";
10
+ import { Git } from "../platform/git.js";
11
+ import { PortProbe } from "../platform/port-probe.js";
12
+ import { resolveStateDbPath, StateStore } from "../platform/state-store.js";
13
+ import { buildProbes } from "./prune.js";
14
+ /** Engines floor: Node >= 22.15 (first unflagged node:sqlite). */
15
+ export const nodeVersionOk = (version) => {
16
+ const [major = 0, minor = 0] = version.split(".").map((part) => Number.parseInt(part, 10));
17
+ return major > 22 || (major === 22 && minor >= 15);
18
+ };
19
+ /**
20
+ * "Compose v2" means the Go `docker compose` plugin (the python v1
21
+ * `docker-compose` is unsupported). Plugin releases moved past v2 numbering
22
+ * (v5.x today), so accept any major >= 2 instead of grepping for "v2".
23
+ */
24
+ export const composeVersionOk = (stdout) => {
25
+ const match = stdout.match(/version\s+v?(\d+)/i);
26
+ return match !== null && Number.parseInt(match[1], 10) >= 2;
27
+ };
28
+ export const detectWsl = (procVersion) => procVersion !== null && procVersion.toLowerCase().includes("microsoft");
29
+ /** Total: /proc/version is absent on non-Linux hosts, which reads as null. */
30
+ const readProcVersion = () => {
31
+ try {
32
+ return readFileSync("/proc/version", "utf8");
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ };
38
+ const WSL_GUIDANCE = "WSL2 detected — keep repos inside the WSL filesystem (not /mnt/c), " +
39
+ "install Node, pnpm, git, and the docker CLI inside WSL, enable Docker Desktop " +
40
+ "WSL integration, and pass Linux paths";
41
+ export const hasHardFailure = (checks) => checks.some((check) => check.hard && !check.ok);
42
+ export const formatDoctorReport = (checks) => checks
43
+ .map((check) => `${check.ok ? "✓" : "✗"} ${check.name} — ${check.detail}${check.ok ? "" : check.hard ? " (hard)" : " (soft)"}`)
44
+ .join("\n");
45
+ /**
46
+ * Run every doctor probe and fold each outcome — success or failure — into a
47
+ * check row. This never fails: a broken docker/state/config is a finding, not
48
+ * an error. The exit-code decision belongs to the command via hasHardFailure.
49
+ */
50
+ export const collectDoctorChecks = (explicitConfigPath) => Effect.gen(function* () {
51
+ const runner = yield* CommandRunner;
52
+ const compose = yield* DockerCompose;
53
+ const store = yield* StateStore;
54
+ const probe = yield* PortProbe;
55
+ const git = yield* Git;
56
+ const checks = [];
57
+ const exec = (command, args) => runner
58
+ .run({ command, args })
59
+ .pipe(Effect.catch((error) => Effect.succeed({ exitCode: -1, stdout: "", stderr: error.message })));
60
+ const dockerVersion = yield* exec("docker", ["version", "--format", "json"]);
61
+ checks.push({
62
+ name: "docker-daemon",
63
+ ok: dockerVersion.exitCode === 0,
64
+ hard: true,
65
+ detail: dockerVersion.exitCode === 0
66
+ ? "docker daemon responsive"
67
+ : tail(dockerVersion.stderr) || "docker CLI not found",
68
+ });
69
+ const composeVersion = yield* exec("docker", ["compose", "version"]);
70
+ const composeOk = composeVersion.exitCode === 0 && composeVersionOk(composeVersion.stdout);
71
+ checks.push({
72
+ name: "compose-v2",
73
+ ok: composeOk,
74
+ hard: true,
75
+ detail: composeOk
76
+ ? (composeVersion.stdout.trim().split("\n")[0] ?? "")
77
+ : "docker compose v2+ not found (python compose v1 is unsupported)",
78
+ });
79
+ checks.push({
80
+ name: "node-version",
81
+ ok: nodeVersionOk(process.versions.node),
82
+ hard: true,
83
+ detail: `node ${process.versions.node} (need >= 22.15)`,
84
+ });
85
+ const config = yield* loadRecipe({
86
+ cwd: process.cwd(),
87
+ explicitPath: explicitConfigPath,
88
+ env: process.env,
89
+ }).pipe(Effect.map((loaded) => ({ ok: true, ...loaded })), Effect.catch((error) => Effect.succeed({ ok: false, message: error.message })));
90
+ checks.push({
91
+ name: "config",
92
+ ok: config.ok,
93
+ hard: false,
94
+ detail: config.ok
95
+ ? `project "${config.recipe.project.id}" at ${config.rootDir}`
96
+ : config.message,
97
+ });
98
+ let ctx = null;
99
+ if (config.ok) {
100
+ const derived = yield* git.state(config.rootDir).pipe(Effect.flatMap((gitState) => buildWorktreeContext({
101
+ rootDir: config.rootDir,
102
+ recipe: config.recipe,
103
+ env: process.env,
104
+ git: gitState,
105
+ })), Effect.map((value) => ({ ok: true, ctx: value })), Effect.catch((error) => Effect.succeed({ ok: false, message: error.message })));
106
+ checks.push({
107
+ name: "context",
108
+ ok: derived.ok,
109
+ hard: false,
110
+ detail: derived.ok
111
+ ? `database ${derived.ctx.databaseName}, compose project ${derived.ctx.composeProjectName}, port ${derived.ctx.odooHttpPort}`
112
+ : derived.message,
113
+ });
114
+ if (derived.ok)
115
+ ctx = derived.ctx;
116
+ }
117
+ const rowsResult = yield* store.list({}).pipe(Effect.map((rows) => ({ ok: true, rows })), Effect.catch((error) => Effect.succeed({ ok: false, message: error.message })));
118
+ const rows = rowsResult.ok ? rowsResult.rows : null;
119
+ if (ctx !== null) {
120
+ const currentCtx = ctx;
121
+ const free = yield* probe.isFree(currentCtx.odooHttpPort);
122
+ const holder = rows?.find((row) => row.odooHttpPort === currentCtx.odooHttpPort)?.composeProject ?? null;
123
+ checks.push({
124
+ name: "port",
125
+ // a busy port is fine as long as we can name the stack sitting on it
126
+ ok: free || holder !== null,
127
+ hard: true,
128
+ detail: free
129
+ ? `port ${currentCtx.odooHttpPort} is free`
130
+ : holder !== null
131
+ ? `port ${currentCtx.odooHttpPort} in use by known stack "${holder}"`
132
+ : `port ${currentCtx.odooHttpPort} in use by an unknown process`,
133
+ });
134
+ }
135
+ if (rows !== null) {
136
+ const byPort = new Map();
137
+ for (const row of rows) {
138
+ if (row.odooHttpPort === 0)
139
+ continue; // adopted rows: port unknown
140
+ byPort.set(row.odooHttpPort, [...(byPort.get(row.odooHttpPort) ?? []), row.composeProject]);
141
+ }
142
+ const collisions = [...byPort.entries()].filter(([, stacks]) => stacks.length > 1);
143
+ checks.push({
144
+ name: "port-collisions",
145
+ ok: collisions.length === 0,
146
+ hard: false,
147
+ detail: collisions.length === 0
148
+ ? "no port collisions among known stacks"
149
+ : collisions.map(([port, stacks]) => `port ${port}: ${stacks.join(", ")}`).join("; "),
150
+ });
151
+ }
152
+ checks.push({
153
+ name: "state-db",
154
+ ok: rowsResult.ok,
155
+ hard: true,
156
+ detail: rowsResult.ok
157
+ ? `${resolveStateDbPath()} (${rowsResult.rows.length} environment(s))`
158
+ : rowsResult.message,
159
+ });
160
+ const gitVersion = yield* exec("git", ["--version"]);
161
+ checks.push({
162
+ name: "git",
163
+ ok: gitVersion.exitCode === 0,
164
+ hard: true,
165
+ detail: gitVersion.exitCode === 0 ? gitVersion.stdout.trim() : "git not found on PATH",
166
+ });
167
+ checks.push({
168
+ name: "wsl",
169
+ ok: true,
170
+ hard: false,
171
+ detail: detectWsl(readProcVersion()) ? WSL_GUIDANCE : "not running under WSL",
172
+ });
173
+ const scopedRows = rows === null
174
+ ? null
175
+ : config.ok
176
+ ? rows.filter((r) => r.projectId === config.recipe.project.id)
177
+ : rows;
178
+ const pruneCheck = yield* scopedRows === null
179
+ ? Effect.succeed({ ok: true, detail: "skipped (state registry unavailable)" })
180
+ : compose.listProjects().pipe(Effect.flatMap((dockerProjects) => buildProbes(scopedRows).pipe(Effect.map((probes) => {
181
+ const candidates = classifyEnvironments({
182
+ rows: scopedRows,
183
+ dockerProjects,
184
+ probes,
185
+ olderThanDays: null,
186
+ allowShared: false,
187
+ now: new Date().toISOString(),
188
+ }).filter((c) => c.reason !== "keep" && c.reason !== "shared-skipped");
189
+ return candidates.length === 0
190
+ ? { ok: true, detail: "no prune candidates" }
191
+ : {
192
+ ok: false,
193
+ detail: `${candidates.length} prune candidate(s) — run \`odoo-agentic-dev prune\``,
194
+ };
195
+ }))), Effect.catch(() => Effect.succeed({ ok: true, detail: "skipped (docker unavailable)" })));
196
+ checks.push({ name: "prune-candidates", hard: false, ...pruneCheck });
197
+ return checks;
198
+ });
199
+ export const doctorCommand = Command.make("doctor", {
200
+ json: Flag.boolean("json").pipe(Flag.withDescription("print machine-readable JSON")),
201
+ config: Flag.string("config").pipe(Flag.optional),
202
+ }, (flags) => Effect.gen(function* () {
203
+ const checks = yield* collectDoctorChecks(Option.getOrUndefined(flags.config));
204
+ yield* Console.log(flags.json ? JSON.stringify({ checks }, null, 2) : formatDoctorReport(checks));
205
+ if (hasHardFailure(checks)) {
206
+ process.exitCode = 1;
207
+ }
208
+ }));
209
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { Effect } 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 { StateStoreApi } from "../platform/state-store.js";
6
+ import type { SharedDatabaseProtectionError, StateError } from "../errors/errors.js";
7
+ export declare const guardDown: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, flags: {
8
+ volumes: boolean;
9
+ allowShared: boolean;
10
+ }) => Effect.Effect<void, SharedDatabaseProtectionError>;
11
+ export declare const buildDownArgs: (flags: {
12
+ volumes: boolean;
13
+ }) => Array<string>;
14
+ /** After a successful teardown: `--volumes` forgets the environment, plain down only touches it. */
15
+ export declare const finalizeDownState: (ctx: WorktreeContext, flags: {
16
+ volumes: boolean;
17
+ }) => Effect.Effect<void, StateError, StateStoreApi>;
18
+ export declare const downCommand: Command.Command<"down", {
19
+ readonly volumes: boolean;
20
+ readonly allowShared: boolean;
21
+ readonly json: boolean;
22
+ readonly config: import("effect/Option").Option<string>;
23
+ }, {}, import("../errors/errors.js").RuntimeError, import("../platform/git.js").GitApi | import("../platform/docker-compose.js").DockerComposeApi | StateStoreApi>;
@@ -0,0 +1,46 @@
1
+ import { Effect } from "effect";
2
+ import { Command, Flag } from "effect/unstable/cli";
3
+ import { assertSharedDatabaseAllowed } from "../core/safety.js";
4
+ import { DockerCompose } from "../platform/docker-compose.js";
5
+ import { StateStore } from "../platform/state-store.js";
6
+ import { resolveContext } from "./resolve-context.js";
7
+ import { withJsonReport } from "./json-report.js";
8
+ export const guardDown = (recipe, ctx, flags) => flags.volumes
9
+ ? assertSharedDatabaseAllowed({
10
+ databaseName: ctx.databaseName,
11
+ sharedDatabase: recipe.project.sharedDatabase,
12
+ allowShared: flags.allowShared,
13
+ action: "down --volumes",
14
+ })
15
+ : Effect.void;
16
+ export const buildDownArgs = (flags) => [
17
+ "down",
18
+ ...(flags.volumes ? ["--volumes"] : []),
19
+ ];
20
+ /** After a successful teardown: `--volumes` forgets the environment, plain down only touches it. */
21
+ export const finalizeDownState = (ctx, flags) => Effect.gen(function* () {
22
+ const store = yield* StateStore;
23
+ yield* flags.volumes
24
+ ? store.remove(ctx.composeProjectName)
25
+ : store.touch(ctx.composeProjectName);
26
+ });
27
+ export const downCommand = Command.make("down", {
28
+ volumes: Flag.boolean("volumes").pipe(Flag.withDescription("also remove this worktree's volumes")),
29
+ allowShared: Flag.boolean("allow-shared"),
30
+ json: Flag.boolean("json").pipe(Flag.withDescription("suppress decorative output; print one final JSON report line")),
31
+ config: Flag.string("config").pipe(Flag.optional),
32
+ }, (flags) => withJsonReport("down", flags.json, (report) => Effect.gen(function* () {
33
+ const { ctx, recipe } = yield* resolveContext(flags.config);
34
+ yield* report.setContext(ctx);
35
+ yield* guardDown(recipe, ctx, flags);
36
+ const compose = yield* DockerCompose;
37
+ yield* compose.ensureAvailable();
38
+ const ref = yield* compose.prepareComposeFile(recipe, ctx);
39
+ yield* report.say(`Stopping compose project: ${ctx.composeProjectName} (database: ${ctx.databaseName})`);
40
+ yield* compose.stream(ref, buildDownArgs(flags));
41
+ yield* report.action("compose-down");
42
+ if (flags.volumes)
43
+ yield* report.action("remove-volumes");
44
+ yield* finalizeDownState(ctx, flags);
45
+ })));
46
+ //# sourceMappingURL=down.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"down.js","sourceRoot":"","sources":["../../src/commands/down.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Command } from "effect/unstable/cli";
2
+ import type { WorktreeContext } from "../core/worktree-context.js";
3
+ export declare const buildInfoText: (ctx: WorktreeContext) => string;
4
+ export declare const buildInfoJson: (ctx: WorktreeContext) => string;
5
+ export declare const buildInfoEnv: (ctx: WorktreeContext) => string;
6
+ export declare const infoCommand: Command.Command<"info", {
7
+ readonly json: boolean;
8
+ readonly env: boolean;
9
+ readonly config: import("effect/Option").Option<string>;
10
+ }, {}, import("../errors/errors.js").RuntimeError, import("../platform/git.js").GitApi>;
@@ -0,0 +1,42 @@
1
+ import { Console, Effect } from "effect";
2
+ import { Command, Flag } from "effect/unstable/cli";
3
+ import { resolveContext } from "./resolve-context.js";
4
+ export const buildInfoText = (ctx) => {
5
+ const out = [
6
+ `Worktree: ${ctx.worktreeName}`,
7
+ `Database: ${ctx.databaseName}`,
8
+ `Compose project: ${ctx.composeProjectName}`,
9
+ `Odoo URL: ${ctx.odooBaseUrl}/web?db=${ctx.databaseName}`,
10
+ ];
11
+ for (const [name, port] of ctx.companionPorts)
12
+ out.push(`${name} URL: http://localhost:${port}`);
13
+ return out.join("\n");
14
+ };
15
+ export const buildInfoJson = (ctx) => JSON.stringify({
16
+ rootDir: ctx.rootDir,
17
+ worktreeName: ctx.worktreeName,
18
+ databaseName: ctx.databaseName,
19
+ composeProjectName: ctx.composeProjectName,
20
+ odooHttpPort: ctx.odooHttpPort,
21
+ odooBaseUrl: ctx.odooBaseUrl,
22
+ odooUrl: `${ctx.odooBaseUrl}/web?db=${ctx.databaseName}`,
23
+ companions: Object.fromEntries(ctx.companionPorts),
24
+ env: ctx.env,
25
+ }, null, 2);
26
+ export const buildInfoEnv = (ctx) => Object.entries(ctx.env)
27
+ .map(([key, value]) => `${key}=${value}`)
28
+ .join("\n");
29
+ export const infoCommand = Command.make("info", {
30
+ json: Flag.boolean("json").pipe(Flag.withDescription("print machine-readable JSON")),
31
+ env: Flag.boolean("env").pipe(Flag.withDescription("print KEY=value env lines")),
32
+ config: Flag.string("config").pipe(Flag.optional, Flag.withDescription("explicit config file path")),
33
+ }, (flags) => Effect.gen(function* () {
34
+ const { ctx } = yield* resolveContext(flags.config);
35
+ const output = flags.json
36
+ ? buildInfoJson(ctx)
37
+ : flags.env
38
+ ? buildInfoEnv(ctx)
39
+ : buildInfoText(ctx);
40
+ yield* Console.log(output);
41
+ }));
42
+ //# sourceMappingURL=info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info.js","sourceRoot":"","sources":["../../src/commands/info.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { Effect } from "effect";
2
+ import type { ResetPath } from "../core/environment.js";
3
+ import type { WorktreeContext } from "../core/worktree-context.js";
4
+ /**
5
+ * Semantic action names for the reset paths. The nightly real-Odoo workflow
6
+ * asserts these exact strings, so they are part of the CLI's contract.
7
+ */
8
+ export declare const resetPathActions: (path: ResetPath) => ReadonlyArray<string>;
9
+ /** The single object a lifecycle command prints to stdout under `--json`. */
10
+ export type JsonReport = {
11
+ readonly ok: boolean;
12
+ readonly command: string;
13
+ /** null when the command failed before the context resolved */
14
+ readonly database: string | null;
15
+ readonly composeProject: string | null;
16
+ readonly odooUrl: string | null;
17
+ readonly actions: ReadonlyArray<string>;
18
+ readonly durationMs: number;
19
+ /** child exit code, when the command ran one to completion (`test`) */
20
+ readonly exitCode?: number;
21
+ };
22
+ /**
23
+ * Per-invocation reporter threaded through a lifecycle command. In text mode
24
+ * `say` prints and everything else is bookkeeping; in json mode `say` is
25
+ * recorded into `actions` instead of printed so stdout stays parseable.
26
+ */
27
+ export interface CommandReporter {
28
+ readonly json: boolean;
29
+ /** decorative line: printed in text mode, recorded as an action in json mode */
30
+ readonly say: (line: string) => Effect.Effect<void>;
31
+ /** semantic action marker: always recorded, never printed */
32
+ readonly action: (name: string) => Effect.Effect<void>;
33
+ /** identity fields for the final report, set as soon as the context resolves */
34
+ readonly setContext: (ctx: WorktreeContext) => Effect.Effect<void>;
35
+ /** child exit code; a non-zero code makes the final report not-ok */
36
+ readonly setExitCode: (code: number) => Effect.Effect<void>;
37
+ }
38
+ /**
39
+ * Wrap a lifecycle command body with the `--json` reporting contract: collect
40
+ * actions while the body runs, then print ONE single-line JSON object to
41
+ * stdout — on success and on typed failure alike (the error still propagates,
42
+ * so it renders on stderr and drives the exit code). Streamed child output may
43
+ * precede it; consumers parse the LAST stdout line (`tail -n 1`).
44
+ */
45
+ export declare const withJsonReport: <A, E, R>(command: string, json: boolean, body: (report: CommandReporter) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
@@ -0,0 +1,55 @@
1
+ import { Console, Effect } from "effect";
2
+ /**
3
+ * Semantic action names for the reset paths. The nightly real-Odoo workflow
4
+ * asserts these exact strings, so they are part of the CLI's contract.
5
+ */
6
+ export const resetPathActions = (path) => path === "restore"
7
+ ? ["restore-from-template"]
8
+ : path === "full"
9
+ ? ["full-init"]
10
+ : ["full-init", "snapshot-template"];
11
+ /**
12
+ * Wrap a lifecycle command body with the `--json` reporting contract: collect
13
+ * actions while the body runs, then print ONE single-line JSON object to
14
+ * stdout — on success and on typed failure alike (the error still propagates,
15
+ * so it renders on stderr and drives the exit code). Streamed child output may
16
+ * precede it; consumers parse the LAST stdout line (`tail -n 1`).
17
+ */
18
+ export const withJsonReport = (command, json, body) => Effect.gen(function* () {
19
+ const startedAt = Date.now();
20
+ const actions = [];
21
+ let ctx = null;
22
+ let exitCode;
23
+ const record = (entry) => Effect.sync(() => {
24
+ actions.push(entry);
25
+ });
26
+ const reporter = {
27
+ json,
28
+ say: (line) => (json ? record(line) : Console.log(line)),
29
+ action: record,
30
+ setContext: (value) => Effect.sync(() => {
31
+ ctx = value;
32
+ }),
33
+ setExitCode: (code) => Effect.sync(() => {
34
+ exitCode = code;
35
+ }),
36
+ };
37
+ const emit = (succeeded) => {
38
+ if (!json)
39
+ return Effect.void;
40
+ const resolved = ctx;
41
+ const report = {
42
+ ok: succeeded && (exitCode ?? 0) === 0,
43
+ command,
44
+ database: resolved === null ? null : resolved.databaseName,
45
+ composeProject: resolved === null ? null : resolved.composeProjectName,
46
+ odooUrl: resolved === null ? null : `${resolved.odooBaseUrl}/web?db=${resolved.databaseName}`,
47
+ actions,
48
+ durationMs: Date.now() - startedAt,
49
+ ...(exitCode === undefined ? {} : { exitCode }),
50
+ };
51
+ return Console.log(JSON.stringify(report));
52
+ };
53
+ return yield* body(reporter).pipe(Effect.tap(() => emit(true)), Effect.tapError(() => emit(false)));
54
+ });
55
+ //# sourceMappingURL=json-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-report.js","sourceRoot":"","sources":["../../src/commands/json-report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIzC;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAe,EAAyB,EAAE,CACzE,IAAI,KAAK,SAAS;IAChB,CAAC,CAAC,CAAC,uBAAuB,CAAC;IAC3B,CAAC,CAAC,IAAI,KAAK,MAAM;QACf,CAAC,CAAC,CAAC,WAAW,CAAC;QACf,CAAC,CAAC,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;AAiC3C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,OAAe,EACf,IAAa,EACb,IAAyD,EACjC,EAAE,CAC1B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,IAAI,GAAG,GAA2B,IAAI,CAAC;IACvC,IAAI,QAA4B,CAAC;IAEjC,MAAM,MAAM,GAAG,CAAC,KAAa,EAAuB,EAAE,CACpD,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;IACL,MAAM,QAAQ,GAAoB;QAChC,IAAI;QACJ,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CACpB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,GAAG,GAAG,KAAK,CAAC;QACd,CAAC,CAAC;QACJ,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CACpB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC,CAAC;KACL,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,SAAkB,EAAuB,EAAE;QACvD,IAAI,CAAC,IAAI;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC;QAC9B,MAAM,QAAQ,GAA2B,GAAG,CAAC;QAC7C,MAAM,MAAM,GAAe;YACzB,EAAE,EAAE,SAAS,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC;YACtC,OAAO;YACP,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY;YAC1D,cAAc,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB;YACtE,OAAO,EACL,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,WAAW,WAAW,QAAQ,CAAC,YAAY,EAAE;YACtF,OAAO;YACP,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YAClC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;SAChD,CAAC;QACF,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC;IAEF,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAC5B,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CACnC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { Effect, Option } from "effect";
2
+ import { Command } from "effect/unstable/cli";
3
+ import { SourceResolverError } from "../errors/errors.js";
4
+ import type { CommandRunnerApi } from "../platform/command-runner.js";
5
+ /** The legacy script's test: an Odoo source checkout has odoo-bin, odoo/ and addons/. */
6
+ export declare const looksLikeOdooCheckout: (path: string) => boolean;
7
+ /** Extract the worktree paths from `git worktree list --porcelain` output. */
8
+ export declare const parseWorktreePorcelain: (stdout: string) => Array<string>;
9
+ /**
10
+ * Candidate discovery when nothing is configured: `<root>/../odoo` first,
11
+ * then `<dirname(worktree)>/odoo` for every git worktree of the project (a
12
+ * checkout cloned next to any worktree is found from all of them). The first
13
+ * candidate that LOOKS like an Odoo checkout wins; the full candidate list is
14
+ * returned for the error message. Git failure (not a repo) is not fatal —
15
+ * only the sibling candidate is checked then.
16
+ */
17
+ export declare const discoverOdooCheckout: (rootDir: string) => Effect.Effect<{
18
+ readonly resolved: string | undefined;
19
+ readonly candidates: Array<string>;
20
+ }, never, CommandRunnerApi>;
21
+ export declare const performLinkSource: (options: {
22
+ readonly rootDir: string;
23
+ readonly target: string | undefined;
24
+ readonly name: string;
25
+ readonly force: boolean;
26
+ readonly recipeSource: string | null;
27
+ }) => Effect.Effect<string, SourceResolverError, CommandRunnerApi>;
28
+ export declare const linkSourceCommand: Command.Command<"link-source", {
29
+ readonly target: Option.Option<string>;
30
+ readonly name: string;
31
+ readonly force: boolean;
32
+ readonly config: Option.Option<string>;
33
+ }, {}, import("../errors/errors.js").RuntimeError, CommandRunnerApi | import("../platform/git.js").GitApi>;
@@ -0,0 +1,116 @@
1
+ import { existsSync, lstatSync, statSync, symlinkSync, unlinkSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, resolve } from "node:path";
3
+ import { Console, Effect, Option } from "effect";
4
+ import { Command, Flag } from "effect/unstable/cli";
5
+ import { SourceResolverError } from "../errors/errors.js";
6
+ import { CommandRunner } from "../platform/command-runner.js";
7
+ import { resolveContext } from "./resolve-context.js";
8
+ const tryFs = (thunk) => Effect.try({ try: thunk, catch: (cause) => new SourceResolverError({ reason: String(cause) }) });
9
+ const isDirectory = (path) => {
10
+ try {
11
+ return statSync(path).isDirectory();
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ };
17
+ /** The legacy script's test: an Odoo source checkout has odoo-bin, odoo/ and addons/. */
18
+ export const looksLikeOdooCheckout = (path) => existsSync(join(path, "odoo-bin")) &&
19
+ isDirectory(join(path, "odoo")) &&
20
+ isDirectory(join(path, "addons"));
21
+ /** Extract the worktree paths from `git worktree list --porcelain` output. */
22
+ export const parseWorktreePorcelain = (stdout) => {
23
+ const paths = [];
24
+ for (const line of stdout.split(/\r?\n/)) {
25
+ if (line.startsWith("worktree "))
26
+ paths.push(line.slice("worktree ".length).trim());
27
+ }
28
+ return paths;
29
+ };
30
+ /**
31
+ * Candidate discovery when nothing is configured: `<root>/../odoo` first,
32
+ * then `<dirname(worktree)>/odoo` for every git worktree of the project (a
33
+ * checkout cloned next to any worktree is found from all of them). The first
34
+ * candidate that LOOKS like an Odoo checkout wins; the full candidate list is
35
+ * returned for the error message. Git failure (not a repo) is not fatal —
36
+ * only the sibling candidate is checked then.
37
+ */
38
+ export const discoverOdooCheckout = (rootDir) => Effect.gen(function* () {
39
+ const runner = yield* CommandRunner;
40
+ const result = yield* runner
41
+ .run({ command: "git", args: ["worktree", "list", "--porcelain"], cwd: rootDir })
42
+ .pipe(Effect.catch(() => Effect.succeed({ exitCode: -1, stdout: "", stderr: "" })));
43
+ const worktrees = result.exitCode === 0 ? parseWorktreePorcelain(result.stdout) : [];
44
+ const candidates = [];
45
+ for (const candidate of [
46
+ resolve(rootDir, "..", "odoo"),
47
+ ...worktrees.map((worktree) => join(dirname(worktree), "odoo")),
48
+ ]) {
49
+ if (!candidates.includes(candidate))
50
+ candidates.push(candidate);
51
+ }
52
+ return { resolved: candidates.find(looksLikeOdooCheckout), candidates };
53
+ });
54
+ export const performLinkSource = (options) => Effect.gen(function* () {
55
+ // explicit wins and skips validation: --target, then odoo.source
56
+ const configured = options.target ??
57
+ (options.recipeSource === "docker-only" ? undefined : (options.recipeSource ?? undefined));
58
+ let resolved;
59
+ if (configured !== undefined) {
60
+ resolved = isAbsolute(configured) ? configured : resolve(options.rootDir, configured);
61
+ if (!existsSync(resolved)) {
62
+ return yield* Effect.fail(new SourceResolverError({ reason: `resolved source path does not exist: ${resolved}` }));
63
+ }
64
+ }
65
+ else {
66
+ const discovery = yield* discoverOdooCheckout(options.rootDir);
67
+ if (discovery.resolved === undefined) {
68
+ return yield* Effect.fail(new SourceResolverError({
69
+ reason: "no --target given and no odoo.source configured; no Odoo checkout found at: " +
70
+ discovery.candidates.join(", ") +
71
+ " (a valid checkout contains odoo-bin, odoo/ and addons/)",
72
+ }));
73
+ }
74
+ resolved = discovery.resolved;
75
+ }
76
+ const linkPath = join(options.rootDir, options.name);
77
+ let existing;
78
+ try {
79
+ existing = lstatSync(linkPath);
80
+ }
81
+ catch {
82
+ existing = undefined;
83
+ }
84
+ if (existing !== undefined) {
85
+ if (!existing.isSymbolicLink()) {
86
+ return yield* Effect.fail(new SourceResolverError({
87
+ reason: `${linkPath} exists and is not a symlink; refusing to overwrite`,
88
+ }));
89
+ }
90
+ if (!options.force) {
91
+ return yield* Effect.fail(new SourceResolverError({
92
+ reason: `${linkPath} already exists; pass --force to replace it`,
93
+ }));
94
+ }
95
+ yield* tryFs(() => unlinkSync(linkPath));
96
+ }
97
+ yield* tryFs(() => symlinkSync(resolved, linkPath));
98
+ return linkPath;
99
+ });
100
+ export const linkSourceCommand = Command.make("link-source", {
101
+ target: Flag.string("target").pipe(Flag.optional),
102
+ name: Flag.string("name").pipe(Flag.withDefault(".odoo")),
103
+ force: Flag.boolean("force"),
104
+ config: Flag.string("config").pipe(Flag.optional),
105
+ }, (flags) => Effect.gen(function* () {
106
+ const { ctx, recipe } = yield* resolveContext(flags.config);
107
+ const linkPath = yield* performLinkSource({
108
+ rootDir: ctx.rootDir,
109
+ target: Option.getOrUndefined(flags.target),
110
+ name: flags.name,
111
+ force: flags.force,
112
+ recipeSource: recipe.odoo.source,
113
+ });
114
+ yield* Console.log(`Linked ${linkPath} -> resolved Odoo source`);
115
+ }));
116
+ //# sourceMappingURL=link-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-source.js","sourceRoot":"","sources":["../../src/commands/link-source.ts"],"names":[],"mappings":"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"}