@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,94 @@
1
+ import { createHash } from "node:crypto";
2
+ import { basename } from "node:path";
3
+ import { Effect } from "effect";
4
+ import { ConfigValidationError } from "../errors/errors.js";
5
+ import { deriveDatabaseName } from "./database-name.js";
6
+ import { derivePorts } from "./port-allocator.js";
7
+ import { deriveComposeProjectName } from "./compose-project.js";
8
+ /** Shell `[[ -n ]]` semantics: an empty-string env var counts as unset. */
9
+ const envValue = (env, key) => {
10
+ const value = env[key];
11
+ return value === undefined || value === "" ? undefined : value;
12
+ };
13
+ const resolveEnvDatabase = (env) => {
14
+ const primary = envValue(env, "ODOO_DATABASE");
15
+ const alias = envValue(env, "E2E_ODOO_DB");
16
+ if (primary !== undefined && alias !== undefined && primary !== alias) {
17
+ return Effect.fail(new ConfigValidationError({
18
+ issues: [
19
+ `ODOO_DATABASE ("${primary}") and E2E_ODOO_DB ("${alias}") disagree; unset one of them`,
20
+ ],
21
+ }));
22
+ }
23
+ return Effect.succeed(primary ?? alias);
24
+ };
25
+ const fallbackWorktreeName = (rootDir) => `${basename(rootDir)}-${createHash("sha256").update(rootDir).digest("hex").slice(0, 8)}`;
26
+ export const buildWorktreeContext = (options) => Effect.gen(function* () {
27
+ const { env, git, recipe, rootDir } = options;
28
+ const branch = git._tag === "Branch" ? git.branch : undefined;
29
+ const nameOverride = envValue(env, "ODOO_WORKTREE_NAME");
30
+ const worktreeName = nameOverride ?? branch ?? fallbackWorktreeName(rootDir);
31
+ // an explicit ODOO_WORKTREE_NAME also redefines what "branch" means for naming
32
+ const effectiveBranch = nameOverride ?? branch;
33
+ const envDatabase = yield* resolveEnvDatabase(env);
34
+ const databaseName = yield* deriveDatabaseName({
35
+ branch: effectiveBranch,
36
+ worktreeName,
37
+ dbPrefix: recipe.project.dbPrefix,
38
+ sharedDatabase: recipe.project.sharedDatabase,
39
+ sharedBranches: recipe.project.sharedBranches,
40
+ stripBranchPrefixes: recipe.project.stripBranchPrefixes,
41
+ envDatabase,
42
+ });
43
+ const { companionPorts, odooHttpPort } = yield* derivePorts({
44
+ databaseName,
45
+ ports: recipe.ports,
46
+ companionApps: recipe.companionApps,
47
+ envHttpPort: envValue(env, "ODOO_HTTP_PORT"),
48
+ });
49
+ const composeProjectName = yield* deriveComposeProjectName(recipe.project.id, databaseName);
50
+ const odooBaseUrl = `http://127.0.0.1:${odooHttpPort}`;
51
+ // env assembly order: canonical → companion vars → aliases
52
+ const assembled = {
53
+ ODOO_DATABASE: databaseName,
54
+ E2E_ODOO_DB: databaseName,
55
+ ODOO_BASE_URL: odooBaseUrl,
56
+ ODOO_HTTP_PORT: String(odooHttpPort),
57
+ ODOO_COMPOSE_PROJECT_NAME: composeProjectName,
58
+ };
59
+ for (const app of recipe.companionApps) {
60
+ const port = companionPorts.get(app.name);
61
+ if (port === undefined)
62
+ continue;
63
+ if (app.portEnv !== undefined)
64
+ assembled[app.portEnv] = String(port);
65
+ if (app.urlEnv !== undefined)
66
+ assembled[app.urlEnv] = `http://localhost:${port}`;
67
+ }
68
+ const aliased = {};
69
+ for (const [alias, target] of Object.entries(recipe.envAliases)) {
70
+ const value = assembled[target];
71
+ if (value === undefined) {
72
+ return yield* Effect.fail(new ConfigValidationError({
73
+ issues: [
74
+ `envAliases: alias "${alias}" targets unknown env key "${target}"; available keys: ${Object.keys(assembled).join(", ")}`,
75
+ ],
76
+ }));
77
+ }
78
+ aliased[alias] = value;
79
+ }
80
+ return {
81
+ rootDir,
82
+ worktreeName,
83
+ branch: branch ?? null,
84
+ databaseName,
85
+ composeProjectName,
86
+ odooHttpPort,
87
+ odooBaseUrl,
88
+ companionPorts,
89
+ env: { ...assembled, ...aliased },
90
+ };
91
+ });
92
+ /** Replace $NAME tokens with values from `env`; unknown tokens are left intact. */
93
+ export const substituteEnvTokens = (value, env) => value.replace(/\$([A-Z][A-Z0-9_]*)/g, (token, name) => env[name] ?? token);
94
+ //# sourceMappingURL=worktree-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-context.js","sourceRoot":"","sources":["../../src/core/worktree-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ declare const ConfigLoadError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
2
+ readonly _tag: "ConfigLoadError";
3
+ } & Readonly<A>;
4
+ export declare class ConfigLoadError extends ConfigLoadError_base<{
5
+ readonly path: string;
6
+ readonly reason: string;
7
+ }> {
8
+ get message(): string;
9
+ }
10
+ declare const ConfigValidationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
11
+ readonly _tag: "ConfigValidationError";
12
+ } & Readonly<A>;
13
+ export declare class ConfigValidationError extends ConfigValidationError_base<{
14
+ readonly issues: ReadonlyArray<string>;
15
+ }> {
16
+ get message(): string;
17
+ }
18
+ declare const GitError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
19
+ readonly _tag: "GitError";
20
+ } & Readonly<A>;
21
+ export declare class GitError extends GitError_base<{
22
+ readonly reason: string;
23
+ }> {
24
+ get message(): string;
25
+ }
26
+ declare const UnsafeDatabaseNameError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
27
+ readonly _tag: "UnsafeDatabaseNameError";
28
+ } & Readonly<A>;
29
+ export declare class UnsafeDatabaseNameError extends UnsafeDatabaseNameError_base<{
30
+ readonly name: string;
31
+ }> {
32
+ get message(): string;
33
+ }
34
+ declare const SharedDatabaseProtectionError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
35
+ readonly _tag: "SharedDatabaseProtectionError";
36
+ } & Readonly<A>;
37
+ export declare class SharedDatabaseProtectionError extends SharedDatabaseProtectionError_base<{
38
+ readonly database: string;
39
+ /** the command the user attempted, e.g. "reset-db" */
40
+ readonly action: string;
41
+ }> {
42
+ get message(): string;
43
+ }
44
+ declare const DockerUnavailableError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
45
+ readonly _tag: "DockerUnavailableError";
46
+ } & Readonly<A>;
47
+ export declare class DockerUnavailableError extends DockerUnavailableError_base<{
48
+ readonly reason: string;
49
+ }> {
50
+ get message(): string;
51
+ }
52
+ declare const CommandFailedError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
53
+ readonly _tag: "CommandFailedError";
54
+ } & Readonly<A>;
55
+ export declare class CommandFailedError extends CommandFailedError_base<{
56
+ readonly command: string;
57
+ readonly args: ReadonlyArray<string>;
58
+ readonly cwd: string | undefined;
59
+ readonly exitCode: number;
60
+ readonly stderrTail: string;
61
+ }> {
62
+ get message(): string;
63
+ }
64
+ declare const ComposeCommandError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
65
+ readonly _tag: "ComposeCommandError";
66
+ } & Readonly<A>;
67
+ export declare class ComposeCommandError extends ComposeCommandError_base<{
68
+ readonly args: ReadonlyArray<string>;
69
+ readonly exitCode: number;
70
+ readonly stderrTail: string;
71
+ }> {
72
+ get message(): string;
73
+ }
74
+ declare const OdooCommandError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
75
+ readonly _tag: "OdooCommandError";
76
+ } & Readonly<A>;
77
+ export declare class OdooCommandError extends OdooCommandError_base<{
78
+ readonly args: ReadonlyArray<string>;
79
+ readonly exitCode: number;
80
+ readonly stderrTail: string;
81
+ }> {
82
+ get message(): string;
83
+ }
84
+ declare const CompanionProcessError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
85
+ readonly _tag: "CompanionProcessError";
86
+ } & Readonly<A>;
87
+ export declare class CompanionProcessError extends CompanionProcessError_base<{
88
+ readonly name: string;
89
+ readonly exitCode: number;
90
+ }> {
91
+ get message(): string;
92
+ }
93
+ declare const SourceResolverError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
94
+ readonly _tag: "SourceResolverError";
95
+ } & Readonly<A>;
96
+ export declare class SourceResolverError extends SourceResolverError_base<{
97
+ readonly reason: string;
98
+ }> {
99
+ get message(): string;
100
+ }
101
+ declare const StateError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
102
+ readonly _tag: "StateError";
103
+ } & Readonly<A>;
104
+ export declare class StateError extends StateError_base<{
105
+ readonly reason: string;
106
+ }> {
107
+ get message(): string;
108
+ }
109
+ declare const PortConflictError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
110
+ readonly _tag: "PortConflictError";
111
+ } & Readonly<A>;
112
+ export declare class PortConflictError extends PortConflictError_base<{
113
+ readonly port: number;
114
+ /** compose project holding the port, when the state registry knows it */
115
+ readonly holder: string | null;
116
+ }> {
117
+ get message(): string;
118
+ }
119
+ export type RuntimeError = ConfigLoadError | ConfigValidationError | GitError | UnsafeDatabaseNameError | SharedDatabaseProtectionError | DockerUnavailableError | CommandFailedError | ComposeCommandError | OdooCommandError | CompanionProcessError | SourceResolverError | StateError | PortConflictError;
120
+ export declare const isRuntimeError: (u: unknown) => u is RuntimeError;
121
+ export declare const renderError: (error: RuntimeError) => string;
122
+ /** Keep only the last `maxLines` lines of process output for error messages. */
123
+ export declare const tail: (text: string, maxLines?: number) => string;
124
+ export {};
@@ -0,0 +1,129 @@
1
+ import { Data } from "effect";
2
+ export class ConfigLoadError extends Data.TaggedError("ConfigLoadError") {
3
+ get message() {
4
+ return this.reason;
5
+ }
6
+ }
7
+ export class ConfigValidationError extends Data.TaggedError("ConfigValidationError") {
8
+ // v4 beta TaggedError leaves Error#message empty; expose the issues there so
9
+ // standard tooling (and assertions on the thrown message) see the details.
10
+ get message() {
11
+ return this.issues.join("; ");
12
+ }
13
+ }
14
+ export class GitError extends Data.TaggedError("GitError") {
15
+ get message() {
16
+ return this.reason;
17
+ }
18
+ }
19
+ export class UnsafeDatabaseNameError extends Data.TaggedError("UnsafeDatabaseNameError") {
20
+ get message() {
21
+ return `unsafe database name: "${this.name}"`;
22
+ }
23
+ }
24
+ export class SharedDatabaseProtectionError extends Data.TaggedError("SharedDatabaseProtectionError") {
25
+ get message() {
26
+ return `refusing to touch shared database "${this.database}" (re-run ${this.action} with --allow-shared)`;
27
+ }
28
+ }
29
+ export class DockerUnavailableError extends Data.TaggedError("DockerUnavailableError") {
30
+ get message() {
31
+ return this.reason;
32
+ }
33
+ }
34
+ export class CommandFailedError extends Data.TaggedError("CommandFailedError") {
35
+ get message() {
36
+ return `${this.command} ${this.args.join(" ")} exited ${this.exitCode}: ${this.stderrTail}`;
37
+ }
38
+ }
39
+ export class ComposeCommandError extends Data.TaggedError("ComposeCommandError") {
40
+ get message() {
41
+ return `docker ${this.args.join(" ")} exited ${this.exitCode}: ${this.stderrTail}`;
42
+ }
43
+ }
44
+ export class OdooCommandError extends Data.TaggedError("OdooCommandError") {
45
+ get message() {
46
+ return `docker ${this.args.join(" ")} exited ${this.exitCode}: ${this.stderrTail}`;
47
+ }
48
+ }
49
+ export class CompanionProcessError extends Data.TaggedError("CompanionProcessError") {
50
+ get message() {
51
+ return `companion "${this.name}" exited ${this.exitCode}`;
52
+ }
53
+ }
54
+ export class SourceResolverError extends Data.TaggedError("SourceResolverError") {
55
+ get message() {
56
+ return this.reason;
57
+ }
58
+ }
59
+ export class StateError extends Data.TaggedError("StateError") {
60
+ get message() {
61
+ return this.reason;
62
+ }
63
+ }
64
+ export class PortConflictError extends Data.TaggedError("PortConflictError") {
65
+ get message() {
66
+ return this.holder === null
67
+ ? `port ${this.port} is already in use`
68
+ : `port ${this.port} is already in use by "${this.holder}"`;
69
+ }
70
+ }
71
+ const RUNTIME_ERROR_TAGS = new Set([
72
+ "ConfigLoadError",
73
+ "ConfigValidationError",
74
+ "GitError",
75
+ "UnsafeDatabaseNameError",
76
+ "SharedDatabaseProtectionError",
77
+ "DockerUnavailableError",
78
+ "CommandFailedError",
79
+ "ComposeCommandError",
80
+ "OdooCommandError",
81
+ "CompanionProcessError",
82
+ "SourceResolverError",
83
+ "StateError",
84
+ "PortConflictError",
85
+ ]);
86
+ export const isRuntimeError = (u) => typeof u === "object" &&
87
+ u !== null &&
88
+ "_tag" in u &&
89
+ RUNTIME_ERROR_TAGS.has(u._tag);
90
+ const lines = (...xs) => xs.join("\n");
91
+ export const renderError = (error) => {
92
+ switch (error._tag) {
93
+ case "ConfigLoadError":
94
+ return lines(`Could not load config: ${error.path}`, `Reason: ${error.reason}`, `Next: check the file exists and its default export is defineConfig({...}).`);
95
+ case "ConfigValidationError":
96
+ return lines("Invalid odoo-agentic-dev config:", ...error.issues.map((issue) => ` - ${issue}`), "Next: fix the config file and re-run.");
97
+ case "GitError":
98
+ return lines(`Git inspection failed: ${error.reason}`, "Next: run the command inside the project worktree.");
99
+ case "UnsafeDatabaseNameError":
100
+ return lines(`Refusing unsafe database name: "${error.name}"`, "Database names must match ^[a-z][a-z0-9_]*$ and be at most 63 characters.", "Next: rename the branch or set ODOO_DATABASE to a safe name.");
101
+ case "SharedDatabaseProtectionError":
102
+ return lines(`Refusing to touch the shared database "${error.database}".`, `Next: re-run \`odoo-agentic-dev ${error.action} --allow-shared\` if you really mean it.`);
103
+ case "DockerUnavailableError":
104
+ return lines(`Docker is not available: ${error.reason}`, "Next: start Docker Desktop / the docker daemon and retry.");
105
+ case "CommandFailedError":
106
+ return lines(`Command failed (exit ${error.exitCode}): ${[error.command, ...error.args].join(" ")}`, `Working directory: ${error.cwd ?? process.cwd()}`, error.stderrTail.length > 0 ? `stderr (tail):\n${error.stderrTail}` : "stderr: (empty)", "Next: re-run the command above manually to investigate.");
107
+ case "ComposeCommandError":
108
+ return lines(`docker compose failed (exit ${error.exitCode}): docker ${error.args.join(" ")}`, error.stderrTail.length > 0 ? `stderr (tail):\n${error.stderrTail}` : "stderr: (empty)", "Next: check container logs with `odoo-agentic-dev up --logs`.");
109
+ case "OdooCommandError":
110
+ return lines(`Odoo command failed (exit ${error.exitCode}): docker ${error.args.join(" ")}`, error.stderrTail.length > 0 ? `output (tail):\n${error.stderrTail}` : "output: (empty)", "Next: inspect the Odoo log output above.");
111
+ case "CompanionProcessError":
112
+ return lines(`Companion app "${error.name}" exited with code ${error.exitCode}.`, "Next: check its logs above; other processes were stopped.");
113
+ case "SourceResolverError":
114
+ return lines(`Could not resolve Odoo source: ${error.reason}`, "Next: pass --target <path> or set odoo.source in the config.");
115
+ case "StateError":
116
+ return lines(`State registry error: ${error.reason}`, "Next: check the state database file (default ~/.local/share/odoo-agentic-dev/state.db,", "overridable via ODOO_AGENTIC_DEV_STATE_DB) and retry.");
117
+ case "PortConflictError":
118
+ return lines(error.holder === null
119
+ ? `Port ${error.port} is already in use by another process.`
120
+ : `Port ${error.port} is already in use by the "${error.holder}" stack.`, "Next: set ODOO_HTTP_PORT to a free port, or run `odoo-agentic-dev prune` to", "clean up environments you no longer need.");
121
+ }
122
+ };
123
+ /** Keep only the last `maxLines` lines of process output for error messages. */
124
+ export const tail = (text, maxLines = 20) => text
125
+ .split(/\r?\n/)
126
+ .filter((l) => l.length > 0)
127
+ .slice(-maxLines)
128
+ .join("\n");
129
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { CANONICAL_ENV_VARS, defineConfig } from "./core/project-recipe.js";
2
+ export type { CanonicalEnvVar, CompanionAppConfig, OdooAddonMount, OdooAgenticDevConfig, OdooAgenticDevConfigInput, OdooDatabaseConfig, OdooProjectConfig, OdooRuntimeConfig, PackageManagerStep, PostInitHook, } from "./core/project-recipe.js";
3
+ export type { PortHashAlgorithm } from "./core/port-allocator.js";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { CANONICAL_ENV_VARS, defineConfig } from "./core/project-recipe.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { ChildProcessSpawner } from "effect/unstable/process";
3
+ import { CommandFailedError } from "../errors/errors.js";
4
+ export type ExecSpec = {
5
+ readonly command: string;
6
+ readonly args: ReadonlyArray<string>;
7
+ readonly cwd?: string | undefined;
8
+ /** merged over the parent process env */
9
+ readonly env?: Record<string, string> | undefined;
10
+ readonly stdin?: string | undefined;
11
+ /** line prefix for runInherited streaming, e.g. "[pwa] " */
12
+ readonly prefix?: string | undefined;
13
+ };
14
+ export type ExecResult = {
15
+ readonly exitCode: number;
16
+ readonly stdout: string;
17
+ readonly stderr: string;
18
+ };
19
+ export interface CommandRunnerApi {
20
+ /** Run to completion capturing output. Non-zero exits RESOLVE (callers decide). Spawn failures FAIL. */
21
+ readonly run: (spec: ExecSpec) => Effect.Effect<ExecResult, CommandFailedError>;
22
+ /** Run streaming output lines to this process's stdout (with optional prefix). Resolves with exit code. */
23
+ readonly runInherited: (spec: ExecSpec) => Effect.Effect<number, CommandFailedError>;
24
+ /** Full stdio inheritance (TTY passthrough) for interactive children. Resolves with exit code. */
25
+ readonly runInteractive: (spec: ExecSpec) => Effect.Effect<number, CommandFailedError>;
26
+ }
27
+ export declare const CommandRunner: Context.Service<CommandRunnerApi, CommandRunnerApi>;
28
+ /** runInherited, but a non-zero exit becomes a typed CommandFailedError. */
29
+ export declare const runInheritedOrFail: (runner: CommandRunnerApi, spec: ExecSpec) => Effect.Effect<void, CommandFailedError>;
30
+ export declare const CommandRunnerLive: Layer.Layer<CommandRunnerApi, never, ChildProcessSpawner.ChildProcessSpawner>;
@@ -0,0 +1,65 @@
1
+ import { Context, Effect, Layer, Stream } from "effect";
2
+ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
3
+ import { CommandFailedError, tail } from "../errors/errors.js";
4
+ export const CommandRunner = Context.Service("odoo-agentic-dev/CommandRunner");
5
+ /** runInherited, but a non-zero exit becomes a typed CommandFailedError. */
6
+ export const runInheritedOrFail = (runner, spec) => runner.runInherited(spec).pipe(Effect.flatMap((exitCode) => exitCode === 0
7
+ ? Effect.void
8
+ : Effect.fail(new CommandFailedError({
9
+ command: spec.command,
10
+ args: spec.args,
11
+ cwd: spec.cwd,
12
+ exitCode,
13
+ stderrTail: "",
14
+ }))));
15
+ const collectText = (stream) => stream.pipe(Stream.decodeText(), Stream.mkString);
16
+ export const CommandRunnerLive = Layer.effect(CommandRunner, Effect.gen(function* () {
17
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
18
+ const spawn = (spec) => ChildProcess.make(spec.command, [...spec.args], {
19
+ cwd: spec.cwd,
20
+ env: spec.env,
21
+ extendEnv: true,
22
+ // the spawner pipes a stdin Stream into the child and closes stdin when done
23
+ stdin: spec.stdin === undefined
24
+ ? "ignore"
25
+ : Stream.succeed(new TextEncoder().encode(spec.stdin)),
26
+ });
27
+ const toFailure = (spec) => (cause) => new CommandFailedError({
28
+ command: spec.command,
29
+ args: spec.args,
30
+ cwd: spec.cwd,
31
+ exitCode: -1,
32
+ stderrTail: tail(String(cause)),
33
+ });
34
+ const run = (spec) => Effect.scoped(Effect.gen(function* () {
35
+ const handle = yield* spawn(spec);
36
+ const [stdout, stderr, exitCode] = yield* Effect.all([collectText(handle.stdout), collectText(handle.stderr), handle.exitCode], { concurrency: "unbounded" });
37
+ return { exitCode: Number(exitCode), stdout, stderr };
38
+ })).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.catch((cause) => Effect.fail(toFailure(spec)(cause))));
39
+ const runInherited = (spec) => Effect.scoped(Effect.gen(function* () {
40
+ const handle = yield* spawn(spec);
41
+ const prefix = spec.prefix ?? "";
42
+ const echo = handle.all.pipe(Stream.decodeText(), Stream.splitLines, Stream.runForEach((line) => Effect.sync(() => {
43
+ process.stdout.write(`${prefix}${line}\n`);
44
+ })));
45
+ const [, exitCode] = yield* Effect.all([echo, handle.exitCode], {
46
+ concurrency: "unbounded",
47
+ });
48
+ return Number(exitCode);
49
+ })).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.catch((cause) => Effect.fail(toFailure(spec)(cause))));
50
+ // verified v4 beta fact: ChildProcess CommandOptions accept "inherit" for
51
+ // stdin/stdout/stderr, so interactive passthrough needs no spawn fallback
52
+ const runInteractive = (spec) => Effect.scoped(Effect.gen(function* () {
53
+ const handle = yield* ChildProcess.make(spec.command, [...spec.args], {
54
+ cwd: spec.cwd,
55
+ env: spec.env,
56
+ extendEnv: true,
57
+ stdin: "inherit",
58
+ stdout: "inherit",
59
+ stderr: "inherit",
60
+ });
61
+ return Number(yield* handle.exitCode);
62
+ })).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.catch((cause) => Effect.fail(toFailure(spec)(cause))));
63
+ return { run, runInherited, runInteractive };
64
+ }));
65
+ //# sourceMappingURL=command-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-runner.js","sourceRoot":"","sources":["../../src/platform/command-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { ComposeCommandError, DockerUnavailableError } from "../errors/errors.js";
3
+ import type { OdooAgenticDevConfig } from "../core/project-recipe.js";
4
+ import type { WorktreeContext } from "../core/worktree-context.js";
5
+ import type { ExecResult } from "./command-runner.js";
6
+ export type ComposeRef = {
7
+ readonly projectName: string;
8
+ readonly composeFile: string;
9
+ readonly projectDir: string;
10
+ /**
11
+ * Context env handed to every compose subprocess (merged over the parent
12
+ * env): project-supplied compose files interpolate `${ODOO_DATABASE:?}` /
13
+ * `${ODOO_HTTP_PORT:?}` and would fail without it.
14
+ */
15
+ readonly env: Record<string, string>;
16
+ };
17
+ export declare const composeArgs: (ref: ComposeRef, rest: ReadonlyArray<string>) => Array<string>;
18
+ export type ComposeProject = {
19
+ readonly name: string;
20
+ readonly running: boolean;
21
+ };
22
+ /** Identity labels read back from a container the generated compose stamped. */
23
+ export type LabeledContainer = {
24
+ readonly composeProject: string;
25
+ readonly projectId: string | null;
26
+ readonly database: string | null;
27
+ readonly rootDir: string | null;
28
+ /** raw label value: "" means "no branch" (callers map it back to null) */
29
+ readonly branch: string | null;
30
+ };
31
+ export interface DockerComposeApi {
32
+ readonly ensureAvailable: () => Effect.Effect<void, DockerUnavailableError>;
33
+ /** All compose projects on this docker host (`docker compose ls -a`). */
34
+ readonly listProjects: () => Effect.Effect<ReadonlyArray<ComposeProject>, ComposeCommandError>;
35
+ /** All containers stamped with our oad labels, deduped per compose project. */
36
+ readonly listLabeledContainers: () => Effect.Effect<ReadonlyArray<LabeledContainer>, ComposeCommandError>;
37
+ /**
38
+ * Label-based teardown that works without the original compose file:
39
+ * `docker rm -f` every container of the project, then remove its volumes.
40
+ */
41
+ readonly removeByLabel: (composeProject: string) => Effect.Effect<void, ComposeCommandError>;
42
+ /** Write the generated compose file (or resolve the project-supplied one). */
43
+ readonly prepareComposeFile: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext) => Effect.Effect<ComposeRef, ComposeCommandError>;
44
+ /** Captured run (optionally fed stdin); fails ComposeCommandError on non-zero exit. */
45
+ readonly run: (ref: ComposeRef, args: ReadonlyArray<string>, stdin?: string) => Effect.Effect<ExecResult, ComposeCommandError>;
46
+ /** Streamed to our stdout; fails ComposeCommandError on non-zero exit. */
47
+ readonly stream: (ref: ComposeRef, args: ReadonlyArray<string>) => Effect.Effect<void, ComposeCommandError>;
48
+ /** Captured run returning the result even on non-zero exit (for polling). */
49
+ readonly tryRun: (ref: ComposeRef, args: ReadonlyArray<string>) => Effect.Effect<ExecResult, ComposeCommandError>;
50
+ readonly waitForDb: (ref: ComposeRef, dbService: string, options?: {
51
+ readonly intervalMillis?: number;
52
+ readonly maxAttempts?: number;
53
+ }) => Effect.Effect<void, ComposeCommandError>;
54
+ }
55
+ export declare const DockerCompose: Context.Service<DockerComposeApi, DockerComposeApi>;
56
+ /**
57
+ * Lenient parse of `docker compose ls -a --format json`: an array of
58
+ * `{ Name, Status, ConfigFiles }` where Status reads like "running(2)" or
59
+ * "exited(2)". Anything unrecognized is skipped rather than fatal.
60
+ */
61
+ export declare const parseComposeLs: (stdout: string) => Array<ComposeProject>;
62
+ /**
63
+ * Lenient parse of `docker ps --format json` output (NDJSON, one container per
64
+ * line; a top-level array is tolerated). `Labels` is a comma-joined `k=v`
65
+ * string, so label values containing commas are truncated — acceptable for the
66
+ * best-effort adoption this feeds. Unparseable lines are skipped.
67
+ */
68
+ export declare const parseLabeledPs: (stdout: string) => Array<LabeledContainer>;
69
+ export declare const DockerComposeLive: Layer.Layer<DockerComposeApi, never, import("./command-runner.js").CommandRunnerApi>;