@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,75 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { Effect } from "effect";
5
+ import { createJiti } from "jiti";
6
+ import { ConfigLoadError } from "../errors/errors.js";
7
+ import { normalizeConfig, validateConfigInput } from "./schema.js";
8
+ const CONFIG_FILENAMES = [
9
+ "odoo-agentic-dev.config.ts",
10
+ "odoo-agentic-dev.config.mts",
11
+ "odoo-agentic-dev.config.js",
12
+ "odoo-agentic-dev.config.mjs",
13
+ ];
14
+ /** Walk from startDir upward; first directory containing a config file wins. */
15
+ export const discoverConfigPath = (startDir) => Effect.sync(() => {
16
+ let dir = resolve(startDir);
17
+ for (;;) {
18
+ for (const filename of CONFIG_FILENAMES) {
19
+ const candidate = join(dir, filename);
20
+ if (existsSync(candidate))
21
+ return candidate;
22
+ }
23
+ const parent = dirname(dir);
24
+ if (parent === dir)
25
+ return undefined;
26
+ dir = parent;
27
+ }
28
+ });
29
+ // Configs `import { defineConfig } from "@basaltbytes/odoo-agentic-dev"`, but
30
+ // they must load even where node_modules does not exist yet (fresh worktrees,
31
+ // `pnpm dlx` bootstraps, CI). The CLI IS that package — alias the bare name to
32
+ // our own entry point so config loading never depends on an install step.
33
+ // (Resolved as a file path: ../index.js next to this module in dist/, or
34
+ // ../index.ts when running from src/ under vitest.)
35
+ const selfPackageEntry = () => {
36
+ for (const candidate of ["../index.js", "../index.ts"]) {
37
+ const path = fileURLToPath(new URL(candidate, import.meta.url));
38
+ if (existsSync(path))
39
+ return path;
40
+ }
41
+ // unreachable in practice; fall back to the bare name (normal resolution)
42
+ return "@basaltbytes/odoo-agentic-dev";
43
+ };
44
+ const jiti = createJiti(import.meta.url, {
45
+ interopDefault: true,
46
+ alias: { "@basaltbytes/odoo-agentic-dev": selfPackageEntry() },
47
+ });
48
+ export const loadRecipe = (options) => Effect.gen(function* () {
49
+ // shell `[[ -n ]]` semantics: an empty-string env override counts as unset
50
+ const envOverride = options.env["ODOO_WORKTREE_CONFIG"];
51
+ const override = options.explicitPath ??
52
+ (envOverride === undefined || envOverride === "" ? undefined : envOverride);
53
+ const path = override !== undefined
54
+ ? isAbsolute(override)
55
+ ? override
56
+ : resolve(options.cwd, override)
57
+ : yield* discoverConfigPath(options.cwd);
58
+ if (path === undefined) {
59
+ return yield* Effect.fail(new ConfigLoadError({
60
+ path: options.cwd,
61
+ reason: `No odoo-agentic-dev config found from ${options.cwd} upward (looked for ${CONFIG_FILENAMES.join(", ")})`,
62
+ }));
63
+ }
64
+ if (!existsSync(path)) {
65
+ return yield* Effect.fail(new ConfigLoadError({ path, reason: "file does not exist" }));
66
+ }
67
+ const raw = yield* Effect.tryPromise({
68
+ try: () => jiti.import(path, { default: true }),
69
+ catch: (cause) => new ConfigLoadError({ path, reason: String(cause) }),
70
+ });
71
+ const input = yield* validateConfigInput(raw);
72
+ const recipe = yield* normalizeConfig(input);
73
+ return { rootDir: dirname(path), recipe };
74
+ });
75
+ //# sourceMappingURL=load-recipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-recipe.js","sourceRoot":"","sources":["../../src/config/load-recipe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Effect } from "effect";
2
+ import { ConfigValidationError } from "../errors/errors.js";
3
+ import type { OdooAgenticDevConfig, OdooAgenticDevConfigInput } from "../core/project-recipe.js";
4
+ /** Structural validation. Fails with ConfigValidationError carrying readable issues. */
5
+ export declare const validateConfigInput: (input: unknown) => Effect.Effect<OdooAgenticDevConfigInput, ConfigValidationError>;
6
+ export declare const DB_PREFIX_PATTERN: RegExp;
7
+ export declare const COMPANION_NAME_PATTERN: RegExp;
8
+ /** Defaults + cross-field rules. Fails with one ConfigValidationError listing every issue. */
9
+ export declare const normalizeConfig: (input: OdooAgenticDevConfigInput) => Effect.Effect<OdooAgenticDevConfig, ConfigValidationError>;
@@ -0,0 +1,190 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { ConfigValidationError } from "../errors/errors.js";
3
+ import { DEFAULT_STRIP_BRANCH_PREFIXES } from "../core/database-name.js";
4
+ const AddonMountSchema = Schema.Struct({
5
+ host: Schema.String,
6
+ container: Schema.String,
7
+ allowOutsideRepo: Schema.optional(Schema.Boolean),
8
+ });
9
+ const HookSchema = Schema.Union([
10
+ Schema.Struct({ type: Schema.Literal("odoo-shell-file"), file: Schema.String }),
11
+ Schema.Struct({ type: Schema.Literal("odoo-shell-inline"), code: Schema.String }),
12
+ Schema.Struct({
13
+ type: Schema.Literal("set-ir-config-parameter"),
14
+ key: Schema.String,
15
+ value: Schema.String,
16
+ }),
17
+ Schema.Struct({
18
+ type: Schema.Literal("command"),
19
+ command: Schema.String,
20
+ args: Schema.Array(Schema.String),
21
+ cwd: Schema.optional(Schema.String),
22
+ }),
23
+ ]);
24
+ const CompanionAppSchema = Schema.Struct({
25
+ name: Schema.String,
26
+ cwd: Schema.String,
27
+ command: Schema.String,
28
+ args: Schema.Array(Schema.String),
29
+ portEnv: Schema.optional(Schema.String),
30
+ urlEnv: Schema.optional(Schema.String),
31
+ env: Schema.optional(Schema.Record(Schema.String, Schema.String)),
32
+ });
33
+ const ConfigInputSchema = Schema.Struct({
34
+ project: Schema.Struct({
35
+ id: Schema.String,
36
+ dbPrefix: Schema.String,
37
+ sharedDatabase: Schema.optional(Schema.String),
38
+ sharedBranches: Schema.optional(Schema.Array(Schema.String)),
39
+ stripBranchPrefixes: Schema.optional(Schema.Array(Schema.String)),
40
+ }),
41
+ ports: Schema.optional(Schema.Struct({
42
+ odooBase: Schema.optional(Schema.Number),
43
+ companionBase: Schema.optional(Schema.Number),
44
+ range: Schema.optional(Schema.Number),
45
+ hashAlgorithm: Schema.optional(Schema.Literals(["fnv1a32", "posix-cksum"])),
46
+ })),
47
+ odoo: Schema.Struct({
48
+ version: Schema.String,
49
+ serviceName: Schema.optional(Schema.String),
50
+ databaseServiceName: Schema.optional(Schema.String),
51
+ postgresImage: Schema.optional(Schema.String),
52
+ configFile: Schema.optional(Schema.String),
53
+ dockerfile: Schema.optional(Schema.String),
54
+ imageName: Schema.optional(Schema.String),
55
+ addons: Schema.Array(AddonMountSchema),
56
+ source: Schema.optional(Schema.String),
57
+ }),
58
+ database: Schema.optional(Schema.Struct({
59
+ initialModules: Schema.optional(Schema.Array(Schema.String)),
60
+ withoutDemo: Schema.optional(Schema.Union([Schema.String, Schema.Literal(false)])),
61
+ postInit: Schema.optional(Schema.Array(HookSchema)),
62
+ })),
63
+ setup: Schema.optional(Schema.Struct({
64
+ submodules: Schema.optional(Schema.Boolean),
65
+ packageManagers: Schema.optional(Schema.Array(Schema.Struct({
66
+ cwd: Schema.String,
67
+ command: Schema.String,
68
+ args: Schema.Array(Schema.String),
69
+ }))),
70
+ })),
71
+ compose: Schema.optional(Schema.Struct({ file: Schema.optional(Schema.String) })),
72
+ worktree: Schema.optional(Schema.Struct({
73
+ copyFiles: Schema.optional(Schema.Array(Schema.String)),
74
+ branchPrefix: Schema.optional(Schema.String),
75
+ })),
76
+ test: Schema.optional(Schema.Struct({
77
+ profiles: Schema.optional(Schema.Record(Schema.String, Schema.Array(Schema.String))),
78
+ })),
79
+ // alias targets are validated against the assembled env at context-build
80
+ // time (companion portEnv/urlEnv keys are legal targets, not just canonical)
81
+ envAliases: Schema.optional(Schema.Record(Schema.String, Schema.String)),
82
+ companionApps: Schema.optional(Schema.Array(CompanionAppSchema)),
83
+ cleanup: Schema.optional(Schema.Struct({
84
+ maxAgeDays: Schema.optional(Schema.Number),
85
+ auto: Schema.optional(Schema.Boolean),
86
+ })),
87
+ });
88
+ /** Structural validation. Fails with ConfigValidationError carrying readable issues. */
89
+ export const validateConfigInput = (input) => Schema.decodeUnknownEffect(ConfigInputSchema)(input).pipe(Effect.mapError((error) => new ConfigValidationError({ issues: [error.message] })),
90
+ // exactOptionalPropertyTypes: the decoded optional props are `?: T | undefined`
91
+ // while the public input type declares `?: T`, so the cast must stay.
92
+ Effect.map((decoded) => decoded));
93
+ export const DB_PREFIX_PATTERN = /^[a-z][a-z0-9]*$/;
94
+ export const COMPANION_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
95
+ /** Defaults + cross-field rules. Fails with one ConfigValidationError listing every issue. */
96
+ export const normalizeConfig = (input) => {
97
+ const issues = [];
98
+ if (!DB_PREFIX_PATTERN.test(input.project.dbPrefix)) {
99
+ issues.push(`project.dbPrefix "${input.project.dbPrefix}" must match ${DB_PREFIX_PATTERN}`);
100
+ }
101
+ const ports = {
102
+ odooBase: input.ports?.odooBase ?? 18069,
103
+ companionBase: input.ports?.companionBase ?? 28000,
104
+ range: input.ports?.range ?? 1000,
105
+ hashAlgorithm: input.ports?.hashAlgorithm ?? "fnv1a32",
106
+ };
107
+ if (!Number.isInteger(ports.range) || ports.range < 10) {
108
+ issues.push(`ports.range must be an integer >= 10, got ${ports.range}`);
109
+ }
110
+ for (const [key, value] of [
111
+ ["odooBase", ports.odooBase],
112
+ ["companionBase", ports.companionBase],
113
+ ]) {
114
+ if (!Number.isInteger(value) || value < 1024 || value + ports.range > 65535) {
115
+ issues.push(`ports.${key} must keep the whole range within 1024..65535, got ${value}`);
116
+ }
117
+ }
118
+ const seenContainers = new Set();
119
+ for (const mount of input.odoo.addons) {
120
+ if (seenContainers.has(mount.container)) {
121
+ issues.push(`duplicate addon container mount path: ${mount.container}`);
122
+ }
123
+ seenContainers.add(mount.container);
124
+ const escapesRepo = mount.host.startsWith("/") || mount.host === ".." || mount.host.startsWith("../");
125
+ if (escapesRepo && mount.allowOutsideRepo !== true) {
126
+ issues.push(`addon host path "${mount.host}" is outside the repo; set allowOutsideRepo: true to permit it`);
127
+ }
128
+ }
129
+ if (input.odoo.addons.length === 0)
130
+ issues.push("odoo.addons must not be empty");
131
+ const companionNames = new Set();
132
+ for (const app of input.companionApps ?? []) {
133
+ if (!COMPANION_NAME_PATTERN.test(app.name)) {
134
+ issues.push(`companion app name "${app.name}" must match ${COMPANION_NAME_PATTERN}`);
135
+ }
136
+ if (companionNames.has(app.name))
137
+ issues.push(`duplicate companion app name: ${app.name}`);
138
+ companionNames.add(app.name);
139
+ }
140
+ if ((input.project.sharedBranches?.length ?? 0) > 0 &&
141
+ input.project.sharedDatabase === undefined) {
142
+ issues.push("project.sharedBranches is set but project.sharedDatabase is missing");
143
+ }
144
+ if (issues.length > 0)
145
+ return Effect.fail(new ConfigValidationError({ issues }));
146
+ return Effect.succeed({
147
+ project: {
148
+ id: input.project.id,
149
+ dbPrefix: input.project.dbPrefix,
150
+ sharedDatabase: input.project.sharedDatabase ?? null,
151
+ sharedBranches: input.project.sharedBranches ??
152
+ (input.project.sharedDatabase !== undefined ? ["main", "master"] : []),
153
+ stripBranchPrefixes: input.project.stripBranchPrefixes ?? DEFAULT_STRIP_BRANCH_PREFIXES,
154
+ },
155
+ ports,
156
+ odoo: {
157
+ version: input.odoo.version,
158
+ serviceName: input.odoo.serviceName ?? "odoo",
159
+ databaseServiceName: input.odoo.databaseServiceName ?? "db",
160
+ postgresImage: input.odoo.postgresImage ?? "postgres:16",
161
+ configFile: input.odoo.configFile ?? null,
162
+ dockerfile: input.odoo.dockerfile ?? null,
163
+ imageName: input.odoo.imageName ?? null,
164
+ addons: input.odoo.addons,
165
+ source: input.odoo.source ?? null,
166
+ },
167
+ database: {
168
+ initialModules: input.database?.initialModules ?? [],
169
+ withoutDemo: input.database?.withoutDemo ?? "all",
170
+ postInit: input.database?.postInit ?? [],
171
+ },
172
+ setup: {
173
+ submodules: input.setup?.submodules ?? false,
174
+ packageManagers: input.setup?.packageManagers ?? [],
175
+ },
176
+ compose: { file: input.compose?.file ?? null },
177
+ worktree: {
178
+ copyFiles: input.worktree?.copyFiles ?? [],
179
+ branchPrefix: input.worktree?.branchPrefix ?? "worktree-",
180
+ },
181
+ test: { profiles: input.test?.profiles ?? {} },
182
+ envAliases: input.envAliases ?? {},
183
+ companionApps: input.companionApps ?? [],
184
+ cleanup: {
185
+ maxAgeDays: input.cleanup?.maxAgeDays ?? 30,
186
+ auto: input.cleanup?.auto ?? false,
187
+ },
188
+ });
189
+ };
190
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { PostInitHook } from "./project-recipe.js";
2
+ export declare const psqlArgs: (dbService: string, sql: string) => Array<string>;
3
+ export declare const terminateSessionsSql: (databaseName: string) => string;
4
+ export declare const dropDatabaseSql: (databaseName: string) => string;
5
+ export declare const createDatabaseSql: (databaseName: string) => string;
6
+ export declare const createFromTemplateSql: (databaseName: string, templateName: string) => string;
7
+ /** Replace `to`'s filestore with a copy of `from`'s (guarded: `from` may not exist). */
8
+ export declare const copyFilestoreArgs: (odooService: string, from: string, to: string) => Array<string>;
9
+ export declare const removeFilestoreArgs: (odooService: string, databaseName: string) => Array<string>;
10
+ export declare const odooInitArgs: (odooService: string, databaseName: string, modules: ReadonlyArray<string>, withoutDemo: string | false) => Array<string>;
11
+ export declare const odooUpdateArgs: (odooService: string, databaseName: string, modules: ReadonlyArray<string>) => Array<string>;
12
+ export declare const odooShellArgs: (odooService: string, databaseName: string) => Array<string>;
13
+ export type OdooTestOptions = {
14
+ readonly tags?: string | undefined;
15
+ readonly file?: string | undefined;
16
+ readonly module?: string | undefined;
17
+ readonly logLevel?: string | undefined;
18
+ readonly extraArgs?: ReadonlyArray<string> | undefined;
19
+ };
20
+ export declare const odooTestArgs: (odooService: string, databaseName: string, options: OdooTestOptions) => Array<string>;
21
+ export declare const setIrConfigParameterCode: (key: string, value: string) => string;
22
+ export type ExpandedHook = {
23
+ readonly kind: "odoo-shell";
24
+ readonly code: string;
25
+ } | {
26
+ readonly kind: "odoo-shell-file";
27
+ readonly file: string;
28
+ } | {
29
+ readonly kind: "host-command";
30
+ readonly command: string;
31
+ readonly args: ReadonlyArray<string>;
32
+ readonly cwd: string | undefined;
33
+ };
34
+ export declare const expandHook: (hook: PostInitHook, env?: Record<string, string>) => ExpandedHook;
@@ -0,0 +1,112 @@
1
+ import { substituteEnvTokens } from "./worktree-context.js";
2
+ // All database names reaching these builders have passed assertSafeDatabaseName
3
+ // (^[a-z][a-z0-9_]*$), which is what makes the string interpolation safe.
4
+ export const psqlArgs = (dbService, sql) => [
5
+ "exec",
6
+ "-T",
7
+ dbService,
8
+ "psql",
9
+ "-U",
10
+ "odoo",
11
+ "-d",
12
+ "postgres",
13
+ "-v",
14
+ "ON_ERROR_STOP=1",
15
+ "-c",
16
+ sql,
17
+ ];
18
+ export const terminateSessionsSql = (databaseName) => `SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${databaseName}' AND pid <> pg_backend_pid()`;
19
+ export const dropDatabaseSql = (databaseName) => `DROP DATABASE IF EXISTS "${databaseName}"`;
20
+ export const createDatabaseSql = (databaseName) => `CREATE DATABASE "${databaseName}" OWNER "odoo"`;
21
+ export const createFromTemplateSql = (databaseName, templateName) => `CREATE DATABASE "${databaseName}" TEMPLATE "${templateName}"`;
22
+ /** Replace `to`'s filestore with a copy of `from`'s (guarded: `from` may not exist). */
23
+ export const copyFilestoreArgs = (odooService, from, to) => [
24
+ "run",
25
+ "--rm",
26
+ "--no-deps",
27
+ "--entrypoint",
28
+ "/bin/sh",
29
+ odooService,
30
+ "-c",
31
+ `rm -rf /var/lib/odoo/filestore/${to} && if [ -d /var/lib/odoo/filestore/${from} ]; then cp -a /var/lib/odoo/filestore/${from} /var/lib/odoo/filestore/${to}; fi`,
32
+ ];
33
+ export const removeFilestoreArgs = (odooService, databaseName) => [
34
+ "run",
35
+ "--rm",
36
+ "--no-deps",
37
+ "--entrypoint",
38
+ "/bin/sh",
39
+ odooService,
40
+ "-c",
41
+ `rm -rf /var/lib/odoo/filestore/${databaseName}`,
42
+ ];
43
+ export const odooInitArgs = (odooService, databaseName, modules, withoutDemo) => [
44
+ "run",
45
+ "--rm",
46
+ odooService,
47
+ "odoo",
48
+ "-d",
49
+ databaseName,
50
+ "-i",
51
+ (modules.length > 0 ? modules : ["base"]).join(","),
52
+ ...(withoutDemo === false ? [] : [`--without-demo=${withoutDemo}`]),
53
+ "--stop-after-init",
54
+ ];
55
+ export const odooUpdateArgs = (odooService, databaseName, modules) => [
56
+ "run",
57
+ "--rm",
58
+ odooService,
59
+ "odoo",
60
+ "-d",
61
+ databaseName,
62
+ "-u",
63
+ modules.join(","),
64
+ "--stop-after-init",
65
+ ];
66
+ export const odooShellArgs = (odooService, databaseName) => [
67
+ "run",
68
+ "--rm",
69
+ "-T",
70
+ odooService,
71
+ "odoo",
72
+ "shell",
73
+ "-d",
74
+ databaseName,
75
+ "--no-http",
76
+ ];
77
+ export const odooTestArgs = (odooService, databaseName, options) => [
78
+ "run",
79
+ "--rm",
80
+ odooService,
81
+ "odoo",
82
+ "-d",
83
+ databaseName,
84
+ "--test-enable",
85
+ ...(options.tags !== undefined ? ["--test-tags", options.tags] : []),
86
+ ...(options.file !== undefined ? ["--test-file", options.file] : []),
87
+ ...(options.module !== undefined ? ["--test-tags", `/${options.module}`] : []),
88
+ ...(options.logLevel !== undefined ? ["--log-level", options.logLevel] : []),
89
+ ...(options.extraArgs ?? []),
90
+ "--stop-after-init",
91
+ ];
92
+ const pythonString = (value) => JSON.stringify(value);
93
+ export const setIrConfigParameterCode = (key, value) => [
94
+ `env["ir.config_parameter"].sudo().set_param(${pythonString(key)}, ${pythonString(value)})`,
95
+ "env.cr.commit()",
96
+ ].join("\n");
97
+ export const expandHook = (hook, env = {}) => {
98
+ switch (hook.type) {
99
+ case "odoo-shell-file":
100
+ return { kind: "odoo-shell-file", file: hook.file };
101
+ case "odoo-shell-inline":
102
+ return { kind: "odoo-shell", code: hook.code };
103
+ case "set-ir-config-parameter":
104
+ return {
105
+ kind: "odoo-shell",
106
+ code: setIrConfigParameterCode(hook.key, substituteEnvTokens(hook.value, env)),
107
+ };
108
+ case "command":
109
+ return { kind: "host-command", command: hook.command, args: hook.args, cwd: hook.cwd };
110
+ }
111
+ };
112
+ //# sourceMappingURL=command-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-plan.js","sourceRoot":"","sources":["../../src/core/command-plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { OdooAgenticDevConfig } from "./project-recipe.js";
2
+ import type { WorktreeContext } from "./worktree-context.js";
3
+ export type ComposeModel = {
4
+ readonly services: Record<string, Record<string, unknown>>;
5
+ readonly volumes: Record<string, {
6
+ readonly labels: Record<string, string>;
7
+ }>;
8
+ };
9
+ export declare const GENERATED_COMPOSE_RELATIVE_PATH = ".odoo-agentic-dev/compose.generated.yml";
10
+ /**
11
+ * Drift-proofing labels stamped on every generated service and volume so
12
+ * `list`/`prune`/`doctor` can reconcile Docker reality against the state
13
+ * registry (and adopt stacks whose rows were lost) without the compose file.
14
+ */
15
+ export declare const buildOadLabels: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext) => Record<string, string>;
16
+ export declare const buildComposeModel: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext) => ComposeModel;
17
+ /** Deterministic hand-rolled YAML for the fixed compose shape (no YAML runtime dep). */
18
+ export declare const renderComposeYaml: (model: ComposeModel) => string;
@@ -0,0 +1,80 @@
1
+ export const GENERATED_COMPOSE_RELATIVE_PATH = ".odoo-agentic-dev/compose.generated.yml";
2
+ const hostPath = (host) => host.startsWith("/") || host.startsWith("./") || host.startsWith("../") ? host : `./${host}`;
3
+ /**
4
+ * Drift-proofing labels stamped on every generated service and volume so
5
+ * `list`/`prune`/`doctor` can reconcile Docker reality against the state
6
+ * registry (and adopt stacks whose rows were lost) without the compose file.
7
+ */
8
+ export const buildOadLabels = (recipe, ctx) => ({
9
+ "dev.basaltbytes.oad": "1",
10
+ "dev.basaltbytes.oad.project-id": recipe.project.id,
11
+ "dev.basaltbytes.oad.database": ctx.databaseName,
12
+ "dev.basaltbytes.oad.root-dir": ctx.rootDir,
13
+ "dev.basaltbytes.oad.branch": ctx.branch ?? "",
14
+ });
15
+ export const buildComposeModel = (recipe, ctx) => {
16
+ const dbService = recipe.odoo.databaseServiceName;
17
+ const odooService = recipe.odoo.serviceName;
18
+ const labels = buildOadLabels(recipe, ctx);
19
+ const imageOrBuild = recipe.odoo.dockerfile !== null
20
+ ? {
21
+ build: { context: ".", dockerfile: recipe.odoo.dockerfile },
22
+ ...(recipe.odoo.imageName !== null ? { image: recipe.odoo.imageName } : {}),
23
+ }
24
+ : { image: `odoo:${recipe.odoo.version}` };
25
+ return {
26
+ services: {
27
+ [dbService]: {
28
+ image: recipe.odoo.postgresImage,
29
+ environment: { POSTGRES_USER: "odoo", POSTGRES_PASSWORD: "odoo", POSTGRES_DB: "postgres" },
30
+ healthcheck: {
31
+ test: ["CMD-SHELL", "pg_isready -U odoo -d postgres"],
32
+ interval: "2s",
33
+ timeout: "5s",
34
+ retries: 30,
35
+ },
36
+ volumes: ["db-data:/var/lib/postgresql/data"],
37
+ labels,
38
+ },
39
+ [odooService]: {
40
+ ...imageOrBuild,
41
+ depends_on: { [dbService]: { condition: "service_healthy" } },
42
+ environment: { HOST: dbService, USER: "odoo", PASSWORD: "odoo" },
43
+ // loopback-only: never expose the dev Odoo on the LAN by default
44
+ ports: [`127.0.0.1:${ctx.odooHttpPort}:8069`],
45
+ volumes: [
46
+ "web-data:/var/lib/odoo",
47
+ ...recipe.odoo.addons.map((mount) => `${hostPath(mount.host)}:${mount.container}`),
48
+ ...(recipe.odoo.configFile !== null
49
+ ? [`${hostPath(recipe.odoo.configFile)}:/etc/odoo/odoo.conf`]
50
+ : []),
51
+ ],
52
+ labels,
53
+ },
54
+ },
55
+ volumes: { "db-data": { labels }, "web-data": { labels } },
56
+ };
57
+ };
58
+ const renderScalar = (value) => typeof value === "string" ? JSON.stringify(value) : String(value);
59
+ const renderNode = (node, indent) => {
60
+ const pad = " ".repeat(indent);
61
+ if (Array.isArray(node)) {
62
+ return node.map((item) => `${pad}- ${renderScalar(item)}`);
63
+ }
64
+ if (typeof node === "object" && node !== null) {
65
+ return Object.entries(node).flatMap(([key, value]) => {
66
+ if (value === undefined)
67
+ return [];
68
+ if (typeof value === "object" && value !== null) {
69
+ if (!Array.isArray(value) && Object.keys(value).length === 0)
70
+ return [`${pad}${key}: {}`];
71
+ return [`${pad}${key}:`, ...renderNode(value, indent + 1)];
72
+ }
73
+ return [`${pad}${key}: ${renderScalar(value)}`];
74
+ });
75
+ }
76
+ return [`${pad}${renderScalar(node)}`];
77
+ };
78
+ /** Deterministic hand-rolled YAML for the fixed compose shape (no YAML runtime dep). */
79
+ export const renderComposeYaml = (model) => renderNode(model, 0).join("\n") + "\n";
80
+ //# sourceMappingURL=compose-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-model.js","sourceRoot":"","sources":["../../src/core/compose-model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Effect } from "effect";
2
+ import { ConfigValidationError } from "../errors/errors.js";
3
+ export declare const deriveComposeProjectName: (projectId: string, databaseName: string) => Effect.Effect<string, ConfigValidationError>;
@@ -0,0 +1,15 @@
1
+ import { Effect } from "effect";
2
+ import { ConfigValidationError } from "../errors/errors.js";
3
+ import { sanitizeNamePart } from "./database-name.js";
4
+ import { assertComposeProjectName } from "./safety.js";
5
+ export const deriveComposeProjectName = (projectId, databaseName) => {
6
+ const id = sanitizeNamePart(projectId);
7
+ const name = [id, databaseName].filter((part) => part.length > 0).join("_");
8
+ if (id.length === 0) {
9
+ return Effect.fail(new ConfigValidationError({
10
+ issues: [`project.id "${projectId}" sanitizes to an empty compose project name`],
11
+ }));
12
+ }
13
+ return assertComposeProjectName(name);
14
+ };
15
+ //# sourceMappingURL=compose-project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-project.js","sourceRoot":"","sources":["../../src/core/compose-project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,SAAiB,EACjB,YAAoB,EAC0B,EAAE;IAChD,MAAM,EAAE,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5E,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,qBAAqB,CAAC;YACxB,MAAM,EAAE,CAAC,eAAe,SAAS,8CAA8C,CAAC;SACjF,CAAC,CACH,CAAC;IACJ,CAAC;IACD,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { Effect } from "effect";
2
+ import { UnsafeDatabaseNameError } from "../errors/errors.js";
3
+ export declare const DB_NAME_PATTERN: RegExp;
4
+ /** Default leading branch type segments dropped (once) before deriving a name. */
5
+ export declare const DEFAULT_STRIP_BRANCH_PREFIXES: ReadonlyArray<string>;
6
+ export declare const sanitizeNamePart: (raw: string) => string;
7
+ export declare const deriveDatabaseName: (options: {
8
+ readonly branch: string | undefined;
9
+ readonly worktreeName: string;
10
+ readonly dbPrefix: string;
11
+ readonly sharedDatabase: string | null;
12
+ readonly sharedBranches: ReadonlyArray<string>;
13
+ readonly stripBranchPrefixes: ReadonlyArray<string>;
14
+ readonly envDatabase: string | undefined;
15
+ }) => Effect.Effect<string, UnsafeDatabaseNameError>;