agentweaver 0.1.9 → 0.1.11

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 (101) hide show
  1. package/README.md +226 -200
  2. package/dist/artifacts.js +101 -56
  3. package/dist/errors.js +7 -0
  4. package/dist/executors/{codex-local-executor.js → codex-executor.js} +4 -4
  5. package/dist/executors/configs/{codex-local-config.js → codex-config.js} +1 -1
  6. package/dist/executors/configs/jira-fetch-config.js +2 -0
  7. package/dist/executors/configs/telegram-notifier-config.js +3 -0
  8. package/dist/executors/fetch-gitlab-diff-executor.js +1 -1
  9. package/dist/executors/fetch-gitlab-review-executor.js +1 -1
  10. package/dist/executors/git-commit-executor.js +25 -0
  11. package/dist/executors/telegram-notifier-executor.js +54 -0
  12. package/dist/flow-state.js +46 -1
  13. package/dist/gitlab.js +13 -8
  14. package/dist/index.js +507 -520
  15. package/dist/interactive-ui.js +495 -87
  16. package/dist/jira.js +52 -5
  17. package/dist/pipeline/auto-flow.js +6 -6
  18. package/dist/pipeline/context.js +1 -0
  19. package/dist/pipeline/declarative-flows.js +7 -4
  20. package/dist/pipeline/flow-catalog.js +60 -23
  21. package/dist/pipeline/flow-model-settings.js +77 -0
  22. package/dist/pipeline/flow-specs/auto-common.json +446 -0
  23. package/dist/pipeline/flow-specs/auto-golang.json +563 -0
  24. package/dist/pipeline/flow-specs/{bug-analyze.json → bugz/bug-analyze.json} +43 -25
  25. package/dist/pipeline/flow-specs/{bug-fix.json → bugz/bug-fix.json} +5 -4
  26. package/dist/pipeline/flow-specs/git-commit.json +196 -0
  27. package/dist/pipeline/flow-specs/{gitlab-diff-review.json → gitlab/gitlab-diff-review.json} +20 -50
  28. package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +165 -0
  29. package/dist/pipeline/flow-specs/{mr-description.json → gitlab/mr-description.json} +17 -10
  30. package/dist/pipeline/flow-specs/{run-go-linter-loop.json → go/run-go-linter-loop.json} +40 -14
  31. package/dist/pipeline/flow-specs/{run-go-tests-loop.json → go/run-go-tests-loop.json} +40 -14
  32. package/dist/pipeline/flow-specs/implement.json +5 -4
  33. package/dist/pipeline/flow-specs/plan.json +40 -148
  34. package/dist/pipeline/flow-specs/{review-fix.json → review/review-fix.json} +73 -13
  35. package/dist/pipeline/flow-specs/review/review-loop.json +280 -0
  36. package/dist/pipeline/flow-specs/review/review-project.json +87 -0
  37. package/dist/pipeline/flow-specs/review/review.json +126 -0
  38. package/dist/pipeline/flow-specs/task-describe.json +191 -11
  39. package/dist/pipeline/launch-profile-config.js +38 -0
  40. package/dist/pipeline/node-registry.js +75 -45
  41. package/dist/pipeline/nodes/build-failure-summary-node.js +16 -29
  42. package/dist/pipeline/nodes/build-review-fix-prompt-node.js +36 -0
  43. package/dist/pipeline/nodes/codex-prompt-node.js +41 -0
  44. package/dist/pipeline/nodes/commit-message-form-node.js +79 -0
  45. package/dist/pipeline/nodes/git-commit-form-node.js +138 -0
  46. package/dist/pipeline/nodes/git-commit-node.js +28 -0
  47. package/dist/pipeline/nodes/git-status-node.js +221 -0
  48. package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +10 -6
  49. package/dist/pipeline/nodes/jira-context-node.js +10 -0
  50. package/dist/pipeline/nodes/llm-prompt-node.js +62 -0
  51. package/dist/pipeline/nodes/plan-codex-node.js +1 -1
  52. package/dist/pipeline/nodes/read-file-node.js +11 -0
  53. package/dist/pipeline/nodes/review-findings-form-node.js +18 -14
  54. package/dist/pipeline/nodes/select-files-form-node.js +72 -0
  55. package/dist/pipeline/nodes/telegram-notifier-node.js +28 -0
  56. package/dist/pipeline/nodes/user-input-node.js +29 -8
  57. package/dist/pipeline/nodes/write-selection-file-node.js +46 -0
  58. package/dist/pipeline/prompt-registry.js +2 -4
  59. package/dist/pipeline/prompt-runtime.js +13 -3
  60. package/dist/pipeline/registry.js +6 -8
  61. package/dist/pipeline/spec-compiler.js +5 -0
  62. package/dist/pipeline/spec-loader.js +18 -7
  63. package/dist/pipeline/spec-types.js +7 -3
  64. package/dist/pipeline/spec-validator.js +4 -0
  65. package/dist/pipeline/types.js +1 -0
  66. package/dist/pipeline/value-resolver.js +40 -38
  67. package/dist/prompts.js +104 -110
  68. package/dist/runtime/agentweaver-home.js +8 -0
  69. package/dist/runtime/command-resolution.js +0 -38
  70. package/dist/runtime/env-loader.js +43 -0
  71. package/dist/runtime/process-runner.js +45 -1
  72. package/dist/structured-artifact-schema-registry.js +53 -0
  73. package/dist/structured-artifact-schemas.json +0 -20
  74. package/dist/structured-artifacts.js +3 -43
  75. package/dist/user-input.js +30 -2
  76. package/package.json +2 -6
  77. package/Dockerfile.codex +0 -56
  78. package/dist/executors/claude-executor.js +0 -46
  79. package/dist/executors/codex-docker-executor.js +0 -27
  80. package/dist/executors/configs/claude-config.js +0 -12
  81. package/dist/executors/configs/codex-docker-config.js +0 -10
  82. package/dist/executors/configs/verify-build-config.js +0 -7
  83. package/dist/executors/verify-build-executor.js +0 -123
  84. package/dist/pipeline/flow-specs/auto.json +0 -979
  85. package/dist/pipeline/flow-specs/gitlab-review.json +0 -317
  86. package/dist/pipeline/flow-specs/plan-opencode.json +0 -603
  87. package/dist/pipeline/flow-specs/preflight.json +0 -206
  88. package/dist/pipeline/flow-specs/review-project.json +0 -243
  89. package/dist/pipeline/flow-specs/review.json +0 -312
  90. package/dist/pipeline/flow-specs/run-linter-loop.json +0 -155
  91. package/dist/pipeline/flow-specs/run-tests-loop.json +0 -155
  92. package/dist/pipeline/flows/preflight-flow.js +0 -19
  93. package/dist/pipeline/nodes/claude-prompt-node.js +0 -54
  94. package/dist/pipeline/nodes/codex-docker-prompt-node.js +0 -32
  95. package/dist/pipeline/nodes/codex-local-prompt-node.js +0 -32
  96. package/dist/pipeline/nodes/review-claude-node.js +0 -38
  97. package/dist/pipeline/nodes/review-reply-codex-node.js +0 -40
  98. package/dist/pipeline/nodes/verify-build-node.js +0 -15
  99. package/dist/runtime/docker-runtime.js +0 -51
  100. package/docker-compose.yml +0 -445
  101. package/verify_build.sh +0 -105
@@ -1,27 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
2
  import { TaskRunnerError } from "./errors.js";
5
- export const STRUCTURED_ARTIFACT_SCHEMA_IDS = [
6
- "bug-analysis/v1",
7
- "bug-fix-design/v1",
8
- "bug-fix-plan/v1",
9
- "gitlab-mr-diff/v1",
10
- "gitlab-review/v1",
11
- "implementation-design/v1",
12
- "implementation-plan/v1",
13
- "jira-description/v1",
14
- "mr-description/v1",
15
- "planning-questions/v1",
16
- "qa-plan/v1",
17
- "review-findings/v1",
18
- "review-fix-report/v1",
19
- "review-reply/v1",
20
- "task-summary/v1",
21
- "user-input/v1",
22
- ];
23
- const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
24
- const SCHEMA_REGISTRY_PATH = path.join(MODULE_DIR, "structured-artifact-schemas.json");
3
+ import { STRUCTURED_ARTIFACT_SCHEMA_IDS, getStructuredArtifactSchema, } from "./structured-artifact-schema-registry.js";
4
+ export { STRUCTURED_ARTIFACT_SCHEMA_IDS };
25
5
  function isRecord(value) {
26
6
  return typeof value === "object" && value !== null && !Array.isArray(value);
27
7
  }
@@ -99,23 +79,6 @@ function validateNode(value, schema, currentPath) {
99
79
  }
100
80
  }
101
81
  }
102
- function loadSchemaRegistry() {
103
- if (!existsSync(SCHEMA_REGISTRY_PATH)) {
104
- throw new TaskRunnerError(`Structured artifact schema registry not found: ${SCHEMA_REGISTRY_PATH}`);
105
- }
106
- let parsed;
107
- try {
108
- parsed = JSON.parse(readFileSync(SCHEMA_REGISTRY_PATH, "utf8"));
109
- }
110
- catch (error) {
111
- throw new TaskRunnerError(`Failed to parse structured artifact schema registry: ${error.message}`);
112
- }
113
- if (!isRecord(parsed)) {
114
- throw new TaskRunnerError(`Structured artifact schema registry ${SCHEMA_REGISTRY_PATH} must be a JSON object.`);
115
- }
116
- return parsed;
117
- }
118
- const schemas = loadSchemaRegistry();
119
82
  export function validateStructuredArtifact(path, schemaId) {
120
83
  if (!existsSync(path)) {
121
84
  throw new TaskRunnerError(`Structured artifact file not found: ${path}`);
@@ -127,10 +90,7 @@ export function validateStructuredArtifact(path, schemaId) {
127
90
  catch (error) {
128
91
  throw new TaskRunnerError(`Structured artifact ${path} is not valid JSON: ${error.message}`);
129
92
  }
130
- const schema = schemas[schemaId];
131
- if (!schema) {
132
- throw new TaskRunnerError(`Structured artifact schema is not registered: ${schemaId}`);
133
- }
93
+ const schema = getStructuredArtifactSchema(schemaId);
134
94
  const issues = validateNode(parsed, schema, path);
135
95
  if (issues.length > 0) {
136
96
  throw new TaskRunnerError(`Structured artifact ${path} failed schema ${schemaId} validation:\n${issues.join("\n")}`);
@@ -71,6 +71,16 @@ export function validateUserInputValues(form, values) {
71
71
  throw new TaskRunnerError("Select at least one finding or enable 'apply all'.");
72
72
  }
73
73
  }
74
+ if (form.formId === "task-describe-source-input") {
75
+ const jiraRef = typeof values.jira_ref === "string" ? normalizeText(values.jira_ref) : "";
76
+ const taskDescription = typeof values.task_description === "string" ? normalizeText(values.task_description) : "";
77
+ if (!jiraRef && !taskDescription) {
78
+ throw new TaskRunnerError("Provide either Jira URL/key or a short task description.");
79
+ }
80
+ if (jiraRef && taskDescription) {
81
+ throw new TaskRunnerError("Provide either Jira URL/key or a short task description, not both.");
82
+ }
83
+ }
74
84
  }
75
85
  function parseBoolean(value) {
76
86
  const normalized = normalizeText(value).toLowerCase();
@@ -120,8 +130,26 @@ export async function requestUserInputInTerminal(form) {
120
130
  }
121
131
  if (field.type === "text") {
122
132
  const current = String(values[field.id] ?? "");
123
- const answer = await rl.question(`${field.label}${current ? ` (${current})` : ""}: `);
124
- values[field.id] = answer.trim() ? answer : current;
133
+ if (field.multiline) {
134
+ process.stdout.write(`${field.label}${current ? " (leave empty to keep current value)" : ""}:\n`);
135
+ if (field.help?.trim()) {
136
+ process.stdout.write(`${field.help.trim()}\n`);
137
+ }
138
+ process.stdout.write("Finish input with an empty line.\n");
139
+ const lines = [];
140
+ while (true) {
141
+ const line = await rl.question(lines.length === 0 ? "> " : "... ");
142
+ if (!line.trim()) {
143
+ break;
144
+ }
145
+ lines.push(line);
146
+ }
147
+ values[field.id] = lines.length > 0 ? lines.join("\n") : current;
148
+ }
149
+ else {
150
+ const answer = await rl.question(`${field.label}${current ? ` (${current})` : ""}: `);
151
+ values[field.id] = answer.trim() ? answer : current;
152
+ }
125
153
  continue;
126
154
  }
127
155
  const options = field.options.map((option, index) => `${index + 1}. ${option.label}`).join("\n");
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "agentweaver",
3
- "version": "0.1.9",
4
- "description": "CLI orchestrator for Jira/Codex/Claude engineering workflows",
3
+ "version": "0.1.11",
4
+ "description": "CLI orchestrator for Jira/Codex engineering workflows",
5
5
  "keywords": [
6
6
  "agent",
7
7
  "cli",
8
8
  "jira",
9
9
  "codex",
10
- "claude",
11
10
  "tui",
12
11
  "developer-tools"
13
12
  ],
@@ -29,9 +28,6 @@
29
28
  "files": [
30
29
  "dist",
31
30
  "README.md",
32
- "docker-compose.yml",
33
- "Dockerfile.codex",
34
- "verify_build.sh",
35
31
  "run_go_tests.py",
36
32
  "run_go_linter.py",
37
33
  "run_go_coverage.sh"
package/Dockerfile.codex DELETED
@@ -1,56 +0,0 @@
1
- FROM golang:1.25.5-bookworm
2
-
3
- ARG GOLANGCI_LINT_VERSION=v2.7.2
4
- ARG MOCKGEN_VERSION=v1.6.0
5
- ARG SWAG_VERSION=latest
6
- ARG PROTOC_GEN_GO_VERSION=latest
7
- ARG PROTOC_GEN_GO_GRPC_VERSION=latest
8
-
9
- RUN apt-get update \
10
- && apt-get install -y --no-install-recommends \
11
- ca-certificates \
12
- nodejs \
13
- npm \
14
- curl \
15
- jq \
16
- less \
17
- file \
18
- make \
19
- procps \
20
- ripgrep \
21
- git \
22
- openssh-client \
23
- docker.io \
24
- protobuf-compiler \
25
- unzip \
26
- zip \
27
- findutils \
28
- && update-ca-certificates \
29
- && rm -rf /var/lib/apt/lists/*
30
-
31
- RUN if ! getent group 1000 >/dev/null; then groupadd -g 1000 codex; fi \
32
- && if ! getent passwd 1000 >/dev/null; then useradd -u 1000 -g 1000 -d /codex-home/home -M -s /bin/bash codex; fi
33
-
34
- RUN npm install -g @openai/codex@latest \
35
- && npm cache clean --force
36
-
37
- RUN GOBIN=/usr/local/bin go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${GOLANGCI_LINT_VERSION} \
38
- && GOBIN=/usr/local/bin go install github.com/golang/mock/mockgen@${MOCKGEN_VERSION} \
39
- && GOBIN=/usr/local/bin go install github.com/swaggo/swag/cmd/swag@${SWAG_VERSION} \
40
- && GOBIN=/usr/local/bin go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VERSION} \
41
- && GOBIN=/usr/local/bin go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${PROTOC_GEN_GO_GRPC_VERSION} \
42
- && ln -sf /usr/local/go/bin/go /usr/bin/go \
43
- && ln -sf /usr/local/go/bin/gofmt /usr/bin/gofmt
44
-
45
- ENV PATH="/usr/local/go/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
46
-
47
- COPY verify_build.sh /usr/local/bin/verify_build.sh
48
- COPY run_go_tests.py /usr/local/bin/run_go_tests.py
49
- COPY run_go_linter.py /usr/local/bin/run_go_linter.py
50
- COPY run_go_coverage.sh /usr/local/bin/run_go_coverage.sh
51
- RUN chmod +x /usr/local/bin/verify_build.sh /usr/local/bin/run_go_tests.py /usr/local/bin/run_go_linter.py /usr/local/bin/run_go_coverage.sh
52
-
53
- WORKDIR /workspace
54
-
55
- ENTRYPOINT ["codex"]
56
- CMD ["--dangerously-bypass-approvals-and-sandbox"]
@@ -1,46 +0,0 @@
1
- import { claudeExecutorDefaultConfig } from "./configs/claude-config.js";
2
- import { processExecutor } from "./process-executor.js";
3
- function resolveModel(config, env) {
4
- const primaryModel = env[config.modelEnvVar]?.trim();
5
- if (primaryModel) {
6
- return primaryModel;
7
- }
8
- for (const envVarName of config.legacyModelEnvVars ?? []) {
9
- const legacyModel = env[envVarName]?.trim();
10
- if (legacyModel) {
11
- return legacyModel;
12
- }
13
- }
14
- return config.defaultModel;
15
- }
16
- export const claudeExecutor = {
17
- kind: "claude",
18
- version: 1,
19
- defaultConfig: claudeExecutorDefaultConfig,
20
- async execute(context, input, config) {
21
- const env = input.env ?? context.env;
22
- const command = input.command ?? context.runtime.resolveCmd(config.defaultCommand, config.commandEnvVar);
23
- const model = input.model?.trim() || resolveModel(config, env);
24
- const argv = [command, "--model", model, config.promptFlag, `--allowedTools=${config.allowedTools}`];
25
- if (config.outputFormat) {
26
- argv.push("--output-format", config.outputFormat);
27
- }
28
- if (config.verboseMode) {
29
- argv.push("--verbose");
30
- }
31
- if (config.includePartialMessages) {
32
- argv.push("--include-partial-messages");
33
- }
34
- argv.push(input.prompt);
35
- const result = await processExecutor.execute(context, {
36
- argv,
37
- env,
38
- label: `claude:${model}`,
39
- }, processExecutor.defaultConfig);
40
- return {
41
- output: result.output,
42
- command,
43
- model,
44
- };
45
- },
46
- };
@@ -1,27 +0,0 @@
1
- import { codexDockerExecutorDefaultConfig } from "./configs/codex-docker-config.js";
2
- import { processExecutor } from "./process-executor.js";
3
- function resolveModel(config, env) {
4
- return env[config.modelEnvVar]?.trim() || config.defaultModel;
5
- }
6
- export const codexDockerExecutor = {
7
- kind: "codex-docker",
8
- version: 1,
9
- defaultConfig: codexDockerExecutorDefaultConfig,
10
- async execute(context, input, config) {
11
- const composeCommand = context.runtime.resolveDockerComposeCmd();
12
- const env = context.runtime.dockerRuntimeEnv();
13
- const model = input.model?.trim() || resolveModel(config, env);
14
- env[config.promptEnvVar] = input.prompt;
15
- env[config.flagsEnvVar] = config.execFlagsTemplate.replace("{model}", model);
16
- const result = await processExecutor.execute(context, {
17
- argv: [...composeCommand, config.composeFileFlag, input.dockerComposeFile, ...config.runArgs, config.service],
18
- env,
19
- label: `codex:${model}`,
20
- }, processExecutor.defaultConfig);
21
- return {
22
- output: result.output,
23
- composeCommand,
24
- model,
25
- };
26
- },
27
- };
@@ -1,12 +0,0 @@
1
- export const claudeExecutorDefaultConfig = {
2
- commandEnvVar: "CLAUDE_BIN",
3
- defaultCommand: "claude",
4
- modelEnvVar: "CLAUDE_MODEL",
5
- legacyModelEnvVars: ["CLAUDE_REVIEW_MODEL"],
6
- defaultModel: "opus",
7
- promptFlag: "-p",
8
- allowedTools: "Read,Write,Edit",
9
- outputFormat: "stream-json",
10
- includePartialMessages: true,
11
- verboseMode: true,
12
- };
@@ -1,10 +0,0 @@
1
- export const codexDockerExecutorDefaultConfig = {
2
- service: "codex-exec",
3
- composeFileFlag: "-f",
4
- runArgs: ["run", "--rm"],
5
- modelEnvVar: "CODEX_MODEL",
6
- defaultModel: "gpt-5.4",
7
- promptEnvVar: "CODEX_PROMPT",
8
- flagsEnvVar: "CODEX_EXEC_FLAGS",
9
- execFlagsTemplate: "--model {model} --dangerously-bypass-approvals-and-sandbox",
10
- };
@@ -1,7 +0,0 @@
1
- export const verifyBuildExecutorDefaultConfig = {
2
- service: "verify-build",
3
- composeFileFlag: "-f",
4
- runArgs: ["run", "--rm"],
5
- printFailureOutput: false,
6
- verbose: false,
7
- };
@@ -1,123 +0,0 @@
1
- import { verifyBuildExecutorDefaultConfig } from "./configs/verify-build-config.js";
2
- import { TaskRunnerError } from "../errors.js";
3
- import { processExecutor } from "./process-executor.js";
4
- function parseStructuredResult(output, service) {
5
- const lines = output
6
- .split(/\r?\n/)
7
- .map((line) => line.replace(/\u001b\[[0-9;]*m/g, "").trim())
8
- .filter(Boolean);
9
- if (lines.length === 0) {
10
- throw new TaskRunnerError(`Structured result is missing from service '${service}' output.`);
11
- }
12
- for (let index = lines.length - 1; index >= 0; index -= 1) {
13
- const line = lines[index];
14
- if (!line) {
15
- continue;
16
- }
17
- const candidates = [];
18
- if (line.startsWith("{") && line.endsWith("}")) {
19
- candidates.push(line);
20
- }
21
- const firstBrace = line.indexOf("{");
22
- const lastBrace = line.lastIndexOf("}");
23
- if (firstBrace >= 0 && lastBrace > firstBrace) {
24
- const slice = line.slice(firstBrace, lastBrace + 1).trim();
25
- if (slice && !candidates.includes(slice)) {
26
- candidates.push(slice);
27
- }
28
- }
29
- for (const rawJson of candidates) {
30
- let parsed;
31
- try {
32
- parsed = JSON.parse(rawJson);
33
- }
34
- catch {
35
- continue;
36
- }
37
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
38
- continue;
39
- }
40
- const candidate = parsed;
41
- if (typeof candidate.ok !== "boolean" ||
42
- typeof candidate.kind !== "string" ||
43
- typeof candidate.stage !== "string" ||
44
- typeof candidate.exitCode !== "number" ||
45
- typeof candidate.summary !== "string" ||
46
- typeof candidate.command !== "string") {
47
- continue;
48
- }
49
- const details = candidate.details;
50
- if (details !== undefined && (!details || typeof details !== "object" || Array.isArray(details))) {
51
- continue;
52
- }
53
- return {
54
- ok: candidate.ok,
55
- kind: candidate.kind,
56
- stage: candidate.stage,
57
- exitCode: candidate.exitCode,
58
- summary: candidate.summary,
59
- command: candidate.command,
60
- details: details ?? {},
61
- };
62
- }
63
- }
64
- throw new TaskRunnerError(`Structured result is missing or invalid in service '${service}' output.`);
65
- }
66
- export const verifyBuildExecutor = {
67
- kind: "verify-build",
68
- version: 1,
69
- defaultConfig: verifyBuildExecutorDefaultConfig,
70
- async execute(context, input, config) {
71
- const composeCommand = context.runtime.resolveDockerComposeCmd();
72
- const service = input.service ?? config.service;
73
- if (context.dryRun) {
74
- await processExecutor.execute(context, {
75
- argv: [...composeCommand, config.composeFileFlag, input.dockerComposeFile, ...config.runArgs, service],
76
- env: context.runtime.dockerRuntimeEnv(),
77
- verbose: config.verbose,
78
- label: service,
79
- }, {
80
- printFailureOutput: config.printFailureOutput,
81
- });
82
- return {
83
- output: "",
84
- composeCommand,
85
- parsed: {
86
- ok: true,
87
- kind: service,
88
- stage: "dry_run",
89
- exitCode: 0,
90
- summary: `Dry run for service '${service}'`,
91
- command: [...composeCommand, config.composeFileFlag, input.dockerComposeFile, ...config.runArgs, service].join(" "),
92
- details: {},
93
- },
94
- };
95
- }
96
- let output = "";
97
- let exitCode = 0;
98
- try {
99
- const result = await processExecutor.execute(context, {
100
- argv: [...composeCommand, config.composeFileFlag, input.dockerComposeFile, ...config.runArgs, service],
101
- env: context.runtime.dockerRuntimeEnv(),
102
- verbose: config.verbose,
103
- label: service,
104
- }, {
105
- printFailureOutput: config.printFailureOutput,
106
- });
107
- output = result.output;
108
- }
109
- catch (error) {
110
- output = String(error.output ?? "");
111
- exitCode = Number(error.returnCode ?? 1);
112
- }
113
- const parsed = parseStructuredResult(output, service);
114
- if (parsed.exitCode !== exitCode && exitCode !== 0) {
115
- throw new TaskRunnerError(`Structured result exit code mismatch for service '${service}': script=${parsed.exitCode}, runtime=${exitCode}.`);
116
- }
117
- return {
118
- output,
119
- composeCommand,
120
- parsed,
121
- };
122
- },
123
- };