@fitlab-ai/agent-infra 0.11.8 → 0.11.10
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.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +0 -6
- package/dist/lib/agent-clients/custom-tool-invocations.js +92 -0
- package/dist/lib/agent-clients/next-steps.js +1 -1
- package/dist/lib/agent-clients/reconcile.js +3 -3
- package/dist/lib/agent-clients/schema.js +18 -10
- package/dist/lib/git/workflow.js +1 -1
- package/dist/lib/init.js +2 -2
- package/dist/lib/internal/agent-client.js +3 -3
- package/dist/lib/internal/cli-route-inventory.js +0 -3
- package/dist/lib/internal/sandbox-control.js +30 -5
- package/dist/lib/internal/task-finalization.js +14 -23
- package/dist/lib/internal/task-operation-registry.js +2 -3
- package/dist/lib/internal/task-platform-recovery.js +9 -4
- package/dist/lib/platform/completion-backfill.js +2 -1
- package/dist/lib/platform/context.js +4 -2
- package/dist/lib/platform/github-client.js +20 -17
- package/dist/lib/platform/github-provider.js +42 -22
- package/dist/lib/platform/github-release-notes.js +4 -2
- package/dist/lib/platform/in-label-sync.js +2 -1
- package/dist/lib/platform/issue-comments.js +13 -9
- package/dist/lib/platform/operation-coordinator.js +1 -1
- package/dist/lib/platform/pr-review.js +2 -1
- package/dist/lib/platform/provider-bridge.js +4 -5
- package/dist/lib/platform/provider-contract.d.ts +3 -6
- package/dist/lib/platform/provider-contract.js +3 -15
- package/dist/lib/platform/provider-validation.js +9 -4
- package/dist/lib/platform/pull-requests.js +4 -2
- package/dist/lib/sandbox/control/client.js +54 -6
- package/dist/lib/sandbox/control/executor.js +8 -3
- package/dist/lib/sandbox/recovery.js +1 -22
- package/dist/lib/sandbox/removal.js +0 -1
- package/dist/lib/sandbox/tools.js +2 -0
- package/dist/lib/task/artifact-command.js +11 -1
- package/dist/lib/task/artifact-lifecycle.js +119 -62
- package/dist/lib/task/artifact-selection.js +1 -1
- package/dist/lib/task/capabilities.js +55 -21
- package/dist/lib/task/commands/status.js +0 -27
- package/dist/lib/task/commit-identity.js +22 -7
- package/dist/lib/task/commit-intent.js +67 -26
- package/dist/lib/task/commit-operation.js +19 -8
- package/dist/lib/task/completion-facts.js +32 -10
- package/dist/lib/task/control-authority.js +2 -4
- package/dist/lib/task/current-contract.js +1 -5
- package/dist/lib/task/events.js +8 -274
- package/dist/lib/task/execution-inputs.js +14 -0
- package/dist/lib/task/finalization-envelope.js +4 -0
- package/dist/lib/task/finalization.js +100 -129
- package/dist/lib/task/frontmatter.js +1 -1
- package/dist/lib/task/ledger-intents.js +4 -7
- package/dist/lib/task/orchestration.js +11 -10
- package/dist/lib/task/platform-operation-journal.js +17 -10
- package/dist/lib/task/platform-operation-recovery.js +95 -28
- package/dist/lib/task/review-fingerprint.js +4 -26
- package/dist/lib/task/verification-engine.js +95 -6
- package/dist/lib/task/verification.js +0 -1
- package/dist/lib/task/write.js +0 -11
- package/dist/lib/update.js +2 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/custom-tool-invocations.ts +129 -0
- package/lib/agent-clients/next-steps.ts +3 -3
- package/lib/agent-clients/reconcile.ts +3 -3
- package/lib/agent-clients/schema.ts +18 -10
- package/lib/git/workflow.ts +1 -1
- package/lib/init.ts +2 -2
- package/lib/internal/agent-client.ts +3 -3
- package/lib/internal/cli-route-inventory.ts +0 -2
- package/lib/internal/sandbox-control.ts +39 -6
- package/lib/internal/task-finalization.ts +16 -24
- package/lib/internal/task-operation-registry.ts +2 -3
- package/lib/internal/task-platform-recovery.ts +9 -4
- package/lib/platform/completion-backfill.ts +2 -1
- package/lib/platform/context.ts +8 -4
- package/lib/platform/github-client.ts +24 -20
- package/lib/platform/github-provider.ts +47 -26
- package/lib/platform/github-release-notes.ts +5 -3
- package/lib/platform/in-label-sync.ts +2 -1
- package/lib/platform/issue-comments.ts +14 -9
- package/lib/platform/operation-coordinator.ts +1 -1
- package/lib/platform/pr-review.ts +2 -1
- package/lib/platform/provider-bridge.ts +10 -9
- package/lib/platform/provider-contract.ts +6 -21
- package/lib/platform/provider-loader.ts +7 -6
- package/lib/platform/provider-validation.ts +30 -8
- package/lib/platform/pull-requests.ts +4 -2
- package/lib/platform/repository-metadata.ts +3 -2
- package/lib/platform/security-alerts.ts +1 -1
- package/lib/platform/types.ts +2 -0
- package/lib/sandbox/control/client.ts +57 -6
- package/lib/sandbox/control/executor.ts +8 -3
- package/lib/sandbox/recovery.ts +0 -25
- package/lib/sandbox/removal.ts +2 -10
- package/lib/sandbox/tool-types.ts +1 -0
- package/lib/sandbox/tools.ts +2 -0
- package/lib/task/artifact-command.ts +10 -1
- package/lib/task/artifact-lifecycle.ts +100 -52
- package/lib/task/artifact-selection.ts +1 -1
- package/lib/task/capabilities.ts +47 -24
- package/lib/task/commands/status.ts +0 -31
- package/lib/task/commit-identity.ts +24 -7
- package/lib/task/commit-intent.ts +58 -24
- package/lib/task/commit-operation.ts +17 -8
- package/lib/task/completion-facts.ts +31 -9
- package/lib/task/control-authority.ts +1 -4
- package/lib/task/current-contract.ts +1 -6
- package/lib/task/events.ts +8 -247
- package/lib/task/execution-inputs.ts +25 -0
- package/lib/task/finalization-envelope.ts +21 -0
- package/lib/task/finalization.ts +101 -123
- package/lib/task/frontmatter.ts +1 -1
- package/lib/task/ledger-intents.ts +4 -7
- package/lib/task/orchestration.ts +11 -17
- package/lib/task/platform-operation-journal.ts +18 -11
- package/lib/task/platform-operation-recovery.ts +90 -31
- package/lib/task/review-fingerprint.ts +4 -26
- package/lib/task/verification-engine.ts +103 -9
- package/lib/task/verification.ts +1 -2
- package/lib/task/write.ts +0 -19
- package/lib/update.ts +2 -3
- package/package.json +1 -1
- package/templates/.agents/README.en.md +2 -50
- package/templates/.agents/README.zh-CN.md +2 -50
- package/templates/.agents/rules/commit-and-pr.en.md +5 -5
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +5 -5
- package/templates/.agents/rules/decision-qualification.en.md +1 -1
- package/templates/.agents/rules/decision-qualification.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +1 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +1 -1
- package/templates/.agents/scripts/lib/agent-infra-package.js +0 -7
- package/templates/.agents/skills/code-task/SKILL.en.md +25 -9
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +26 -9
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +5 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +5 -16
- package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/SKILL.en.md +1 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +15 -12
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +11 -10
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +15 -6
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +15 -6
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +2 -268
- package/templates/.agents/templates/task.zh-CN.md +0 -12
- package/templates/.github/workflows/pr-label.yml +1 -2
- package/dist/lib/agent-clients/custom-tuis.js +0 -99
- package/dist/lib/internal/task-invalidation.js +0 -63
- package/dist/lib/task/intermediate-cleanup.js +0 -327
- package/dist/lib/task/invalidation-command.js +0 -88
- package/dist/lib/task/invalidation.js +0 -242
- package/lib/agent-clients/custom-tuis.ts +0 -149
- package/lib/internal/task-invalidation.ts +0 -41
- package/lib/task/intermediate-cleanup.ts +0 -393
- package/lib/task/invalidation-command.ts +0 -88
- package/lib/task/invalidation.ts +0 -313
package/README.md
CHANGED
|
@@ -240,7 +240,7 @@ In-depth guides live under [`docs/en/`](./docs/en/README.md):
|
|
|
240
240
|
- [Feishu Bridge](./docs/en/feishu-bridge.md) — configure the Feishu long-connection adapter, command routing, permissions, and TUI selection
|
|
241
241
|
- [Built-in AI Skills](./docs/en/skills.md) — the full skill catalog by use case
|
|
242
242
|
- [Custom Skills](./docs/en/custom-skills.md) — create and sync project-specific skills
|
|
243
|
-
- [Custom
|
|
243
|
+
- [Custom Tool Invocations](./docs/en/custom-tool-invocations.md) — configure custom-tool next-step commands
|
|
244
244
|
- [Prebuilt Workflows](./docs/en/workflows.md) — the gated delivery lifecycle and example flow
|
|
245
245
|
- [Configuration Reference](./docs/en/configuration.md) — `.agents/.airc.json`, external sources, version management
|
|
246
246
|
- [File Management Strategies](./docs/en/file-management.md) — managed / merged / ejected update strategies
|
package/README.zh-CN.md
CHANGED
|
@@ -239,7 +239,7 @@ my-project/
|
|
|
239
239
|
- [飞书桥接](./docs/zh-CN/feishu-bridge.md) — 配置飞书长连接 adapter、命令路由、权限和 TUI 选择
|
|
240
240
|
- [内置 AI Skills](./docs/zh-CN/skills.md) — 按使用场景分组的完整 skill 清单
|
|
241
241
|
- [自定义 Skills](./docs/zh-CN/custom-skills.md) — 创建并同步项目专属 skill
|
|
242
|
-
- [
|
|
242
|
+
- [自定义工具调用](./docs/zh-CN/custom-tool-invocations.md) — 配置自定义工具的下一步命令
|
|
243
243
|
- [预置工作流](./docs/zh-CN/workflows.md) — 分阶段交付链路与示例流程
|
|
244
244
|
- [配置参考](./docs/zh-CN/configuration.md) — `.agents/.airc.json`、外部源、版本管理
|
|
245
245
|
- [文件管理策略](./docs/zh-CN/file-management.md) — managed / merged / ejected 更新策略
|
package/dist/bin/internal-cli.js
CHANGED
|
@@ -13,7 +13,6 @@ const taskWorkflowCommand = command === 'task-artifact'
|
|
|
13
13
|
|| command === 'task-review'
|
|
14
14
|
|| command === 'task-event'
|
|
15
15
|
|| command === 'task-ledger'
|
|
16
|
-
|| command === 'task-invalidation'
|
|
17
16
|
|| command === 'task-warning';
|
|
18
17
|
const taskPlatformRecoveryCommand = command === 'task-platform-recovery';
|
|
19
18
|
const manualValidationWorkflowCommand = command === 'manual-validation';
|
|
@@ -231,11 +230,6 @@ if (!controlRouted && !taskViewGuardFailed && internalRouteRegistered)
|
|
|
231
230
|
await taskEvent(process.argv.slice(3));
|
|
232
231
|
break;
|
|
233
232
|
}
|
|
234
|
-
case 'task-invalidation': {
|
|
235
|
-
const { taskInvalidation } = await import('../lib/internal/task-invalidation.js');
|
|
236
|
-
taskInvalidation(process.argv.slice(3));
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
233
|
case 'task-lifecycle': {
|
|
240
234
|
const { taskLifecycle } = await import('../lib/internal/task-lifecycle.js');
|
|
241
235
|
await taskLifecycle(process.argv.slice(3));
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const ALLOWED_PLACEHOLDERS = Object.freeze(['skillName', 'projectName']);
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function isNonEmptySingleLine(value) {
|
|
6
|
+
return typeof value === 'string'
|
|
7
|
+
&& value.trim() !== ''
|
|
8
|
+
&& !/[\r\n]/.test(value);
|
|
9
|
+
}
|
|
10
|
+
function hasValidPlaceholders(invocation) {
|
|
11
|
+
const placeholders = [
|
|
12
|
+
...invocation.matchAll(/\$\{([^}]+)\}/g)
|
|
13
|
+
].map((match) => match[1]);
|
|
14
|
+
return placeholders.includes('skillName')
|
|
15
|
+
&& placeholders.every((placeholder) => ALLOWED_PLACEHOLDERS.includes(placeholder))
|
|
16
|
+
&& !invocation
|
|
17
|
+
.replaceAll('${skillName}', '')
|
|
18
|
+
.replaceAll('${projectName}', '')
|
|
19
|
+
.includes('${');
|
|
20
|
+
}
|
|
21
|
+
function normalizeCustomToolInvocations(config) {
|
|
22
|
+
const items = [];
|
|
23
|
+
const diagnostics = [];
|
|
24
|
+
const root = isRecord(config) ? config : {};
|
|
25
|
+
const sandbox = isRecord(root.sandbox) ? root.sandbox : {};
|
|
26
|
+
const sandboxTools = isRecord(sandbox.tools) ? sandbox.tools : { ids: ['agent-infra'] };
|
|
27
|
+
const definitions = sandboxTools.definitions ?? {};
|
|
28
|
+
const selectedIds = sandboxTools.ids;
|
|
29
|
+
if (!Array.isArray(selectedIds)) {
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
items: Object.freeze([]),
|
|
32
|
+
diagnostics: Object.freeze([
|
|
33
|
+
Object.freeze({
|
|
34
|
+
code: 'INVALID_CUSTOM_TOOL_CONFIG',
|
|
35
|
+
path: 'sandbox.tools.ids'
|
|
36
|
+
})
|
|
37
|
+
])
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (!isRecord(definitions)) {
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
items: Object.freeze([]),
|
|
43
|
+
diagnostics: Object.freeze([
|
|
44
|
+
Object.freeze({
|
|
45
|
+
code: 'INVALID_CUSTOM_TOOL_CONFIG',
|
|
46
|
+
path: 'sandbox.tools.definitions'
|
|
47
|
+
})
|
|
48
|
+
])
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
for (const [index, id] of selectedIds.entries()) {
|
|
52
|
+
if (typeof id !== 'string') {
|
|
53
|
+
diagnostics.push({
|
|
54
|
+
code: 'INVALID_CUSTOM_TOOL_INVOCATION',
|
|
55
|
+
path: `sandbox.tools.ids[${index}]`
|
|
56
|
+
});
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const candidate = definitions[id];
|
|
60
|
+
if (!isRecord(candidate))
|
|
61
|
+
continue;
|
|
62
|
+
if (candidate.invoke === undefined)
|
|
63
|
+
continue;
|
|
64
|
+
const base = `sandbox.tools.definitions.${id}`;
|
|
65
|
+
const name = candidate.name === undefined ? id : candidate.name;
|
|
66
|
+
if (!isNonEmptySingleLine(name)) {
|
|
67
|
+
diagnostics.push({ code: 'INVALID_CUSTOM_TOOL_INVOCATION', path: `${base}.name` });
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (!isNonEmptySingleLine(candidate.invoke)) {
|
|
71
|
+
diagnostics.push({ code: 'INVALID_CUSTOM_TOOL_INVOCATION', path: `${base}.invoke` });
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (!hasValidPlaceholders(candidate.invoke)) {
|
|
75
|
+
diagnostics.push({
|
|
76
|
+
code: 'INVALID_CUSTOM_TOOL_INVOCATION_PLACEHOLDER',
|
|
77
|
+
path: `${base}.invoke`
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
items.push(Object.freeze({
|
|
82
|
+
name,
|
|
83
|
+
invocation: candidate.invoke
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
return Object.freeze({
|
|
87
|
+
items: Object.freeze(items),
|
|
88
|
+
diagnostics: Object.freeze(diagnostics.map((diagnostic) => Object.freeze(diagnostic)))
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
export { normalizeCustomToolInvocations };
|
|
92
|
+
//# sourceMappingURL=custom-tool-invocations.js.map
|
|
@@ -37,7 +37,7 @@ function renderNextStepCommands(input) {
|
|
|
37
37
|
args
|
|
38
38
|
})
|
|
39
39
|
}));
|
|
40
|
-
const custom = input.
|
|
40
|
+
const custom = input.customToolInvocations.map((tool) => Object.freeze({
|
|
41
41
|
source: 'custom',
|
|
42
42
|
displayName: tool.name,
|
|
43
43
|
command: renderAgentClientInvocation(tool.invocation, {
|
|
@@ -3,7 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import defaults from '../defaults.json' with { type: 'json' };
|
|
5
5
|
import { normalizeAgentClients, serializeAgentClients } from './config.js';
|
|
6
|
-
import {
|
|
6
|
+
import { normalizeCustomToolInvocations } from './custom-tool-invocations.js';
|
|
7
7
|
import { renderNextStepCommands } from './next-steps.js';
|
|
8
8
|
import { planAgentClientProjectAssets } from './project-assets.js';
|
|
9
9
|
import { listAgentClientAdapters, listEnabledAgentClientAdapters } from './registry.js';
|
|
@@ -141,11 +141,11 @@ function planAgentClientReconciliation(input) {
|
|
|
141
141
|
if (Object.keys(baselines).length === 0) {
|
|
142
142
|
delete nextConfig.files.managedBaselines;
|
|
143
143
|
}
|
|
144
|
-
const custom =
|
|
144
|
+
const custom = normalizeCustomToolInvocations(input.config);
|
|
145
145
|
const nextSteps = renderNextStepCommands({
|
|
146
146
|
projectName,
|
|
147
147
|
state: desired,
|
|
148
|
-
|
|
148
|
+
customToolInvocations: custom.items,
|
|
149
149
|
skillName: 'update-agent-infra'
|
|
150
150
|
});
|
|
151
151
|
const actionableSeed = seedOperations.some((operation) => operation.kind === 'write' || operation.kind === 'remove');
|
|
@@ -38,16 +38,24 @@ const AGENT_CLIENTS_SCHEMA = {
|
|
|
38
38
|
}
|
|
39
39
|
}))
|
|
40
40
|
},
|
|
41
|
-
|
|
42
|
-
type: '
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
sandbox: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
tools: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
ids: { type: 'array', items: { type: 'string' } },
|
|
48
|
+
definitions: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
additionalProperties: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
name: { type: 'string' },
|
|
54
|
+
invoke: { type: 'string' }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
51
59
|
}
|
|
52
60
|
}
|
|
53
61
|
}
|
package/dist/lib/git/workflow.js
CHANGED
|
@@ -103,7 +103,7 @@ function commitExplicitPaths(input, runner = defaultRunner) {
|
|
|
103
103
|
if (tree !== input.expectedTree)
|
|
104
104
|
return { status: 'failed', changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [{ name: 'stage', status: 'applied' }], error: { code: 'GIT_TREE_MISMATCH', message: `Expected staged tree ${input.expectedTree}, received ${tree ?? 'unavailable'}` } };
|
|
105
105
|
}
|
|
106
|
-
const commit = run(runner, input.cwd, ['commit', '-m', input.message]);
|
|
106
|
+
const commit = run(runner, input.cwd, ['commit', '--cleanup=verbatim', '-m', input.message]);
|
|
107
107
|
const after = inspectGitWorkflow(input.cwd, runner);
|
|
108
108
|
if (commit.status !== 0)
|
|
109
109
|
return { status: 'failed', changed: false, snapshot: after.snapshot, operations: [{ name: 'commit', status: 'failed', message: commit.stderr.trim() }], error: { code: 'GIT_COMMIT_FAILED', message: commit.stderr.trim() } };
|
package/dist/lib/init.js
CHANGED
|
@@ -228,8 +228,8 @@ async function cmdInit() {
|
|
|
228
228
|
console.log(' npm install -g @fitlab-ai/agent-infra');
|
|
229
229
|
console.log('');
|
|
230
230
|
if (workflowPlan.nextSteps.length === 0) {
|
|
231
|
-
console.log(' No Agent Client
|
|
232
|
-
console.log(` Configure "
|
|
231
|
+
console.log(' No Agent Client or sandbox custom tool integration enabled.');
|
|
232
|
+
console.log(` Configure a custom tool in "sandbox.tools.definitions" and select it in "sandbox.tools.ids" in ${configPath}.`);
|
|
233
233
|
console.log('');
|
|
234
234
|
}
|
|
235
235
|
else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { AgentClientConfigError, normalizeAgentClients } from '../agent-clients/config.js';
|
|
4
|
-
import {
|
|
4
|
+
import { normalizeCustomToolInvocations } from '../agent-clients/custom-tool-invocations.js';
|
|
5
5
|
import { renderNextStepCommands } from '../agent-clients/next-steps.js';
|
|
6
6
|
import { getAgentClientModelSelection } from '../agent-clients/registry.js';
|
|
7
7
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
@@ -139,11 +139,11 @@ function agentClient(args = []) {
|
|
|
139
139
|
}
|
|
140
140
|
try {
|
|
141
141
|
const clients = normalizeAgentClients(config);
|
|
142
|
-
const custom =
|
|
142
|
+
const custom = normalizeCustomToolInvocations(config);
|
|
143
143
|
const commands = renderNextStepCommands({
|
|
144
144
|
projectName: String(config.project ?? ''),
|
|
145
145
|
state: clients.state,
|
|
146
|
-
|
|
146
|
+
customToolInvocations: custom.items,
|
|
147
147
|
skillName: parsed.skillName,
|
|
148
148
|
...(parsed.taskRef ? { taskRef: parsed.taskRef } : {}),
|
|
149
149
|
...(parsed.version ? { version: parsed.version } : {})
|
|
@@ -57,7 +57,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
57
57
|
'task-orchestration': ['status', 'progress'],
|
|
58
58
|
'task-review': ['preflight', 'finalize-summary'],
|
|
59
59
|
'task-event': ['event'],
|
|
60
|
-
'task-invalidation': ['reconcile'],
|
|
61
60
|
'task-lifecycle': ['intent'],
|
|
62
61
|
'task-finalization': ['complete'],
|
|
63
62
|
'task-platform-recovery': ['inspect', 'recover'],
|
|
@@ -105,8 +104,6 @@ export function internalRouteSelector(command, args) {
|
|
|
105
104
|
return args[1] === 'status' ? 'status' : args[1] ? 'progress' : '';
|
|
106
105
|
if (command === 'task-review')
|
|
107
106
|
return args[1] ?? '';
|
|
108
|
-
if (command === 'task-invalidation')
|
|
109
|
-
return args[1] ?? '';
|
|
110
107
|
if (command === 'task-artifact')
|
|
111
108
|
return args[1] ?? '';
|
|
112
109
|
if (command === 'platform-pr-review' && first(args) === 'publish') {
|
|
@@ -4,8 +4,9 @@ import { serveSandboxControl } from '../sandbox/control/server.js';
|
|
|
4
4
|
import { runSandboxControlExecutor } from '../sandbox/control/executor.js';
|
|
5
5
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
6
6
|
import { parseTaskCreateResult, taskCreateExitCode } from '../task/create-service.js';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.js';
|
|
8
|
+
function writeFinalizationEnvelope(status, accepted, error, changed = false, result = null, requestId = null) {
|
|
9
|
+
process.stdout.write(serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error }));
|
|
9
10
|
}
|
|
10
11
|
function finalizationErrorStatus(error) {
|
|
11
12
|
if (error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN')
|
|
@@ -30,6 +31,28 @@ function recoveredExitCode(response) {
|
|
|
30
31
|
}
|
|
31
32
|
return response.exitCode ?? 1;
|
|
32
33
|
}
|
|
34
|
+
function finalizationResponseOutput(response) {
|
|
35
|
+
try {
|
|
36
|
+
const value = JSON.parse(response.stdout);
|
|
37
|
+
if (value.version === 2
|
|
38
|
+
&& value.status
|
|
39
|
+
&& ['completed', 'failed', 'blocked', 'unknown'].includes(value.status)
|
|
40
|
+
&& (typeof value.changed === 'boolean' || value.changed === null)) {
|
|
41
|
+
return serializeTaskFinalizationEnvelope({
|
|
42
|
+
status: value.status,
|
|
43
|
+
changed: value.changed,
|
|
44
|
+
accepted: true,
|
|
45
|
+
requestId: response.id,
|
|
46
|
+
result: value.result ?? null,
|
|
47
|
+
error: value.error ?? null
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Preserve non-envelope output for diagnostics from the executor.
|
|
53
|
+
}
|
|
54
|
+
return response.stdout;
|
|
55
|
+
}
|
|
33
56
|
function sandboxFinalizationClient(args) {
|
|
34
57
|
if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
|
|
35
58
|
const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: 'task ref, complete intent, and --agent are required', retryable: false };
|
|
@@ -52,7 +75,8 @@ function sandboxFinalizationClient(args) {
|
|
|
52
75
|
catch (error) {
|
|
53
76
|
if (!(error instanceof SandboxControlClientError))
|
|
54
77
|
throw error;
|
|
55
|
-
|
|
78
|
+
const status = finalizationErrorStatus(error.detail);
|
|
79
|
+
writeFinalizationEnvelope(status, error.accepted, error.detail, status === 'unknown' ? null : false, null, error.requestId);
|
|
56
80
|
writeClientError(error);
|
|
57
81
|
process.exitCode = error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.detail.retryable ? 2 : 1;
|
|
58
82
|
return;
|
|
@@ -63,12 +87,13 @@ function sandboxFinalizationClient(args) {
|
|
|
63
87
|
message: 'sandbox control rejected the finalization request',
|
|
64
88
|
retryable: false
|
|
65
89
|
};
|
|
66
|
-
|
|
90
|
+
const status = finalizationErrorStatus(error);
|
|
91
|
+
writeFinalizationEnvelope(status, error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN', error, status === 'unknown' ? null : false, null, response.id);
|
|
67
92
|
process.stderr.write(`${error.message}\n`);
|
|
68
93
|
process.exitCode = error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.retryable ? 2 : 1;
|
|
69
94
|
return;
|
|
70
95
|
}
|
|
71
|
-
process.stdout.write(response
|
|
96
|
+
process.stdout.write(finalizationResponseOutput(response));
|
|
72
97
|
process.stderr.write(response.stderr);
|
|
73
98
|
process.exitCode = response.exitCode ?? 1;
|
|
74
99
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { createDirectHostExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../task/control-authority.js';
|
|
2
|
-
import { applyTaskFinalization
|
|
3
|
-
import { publishTaskFinalizationHandoff } from '../task/finalization-handoff.js';
|
|
2
|
+
import { applyTaskFinalization } from '../task/finalization.js';
|
|
4
3
|
import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
|
|
5
|
-
import { verifyTaskEvent } from '../task/verification.js';
|
|
6
4
|
import { resolveSandboxControlTransport } from './task-operation-registry.js';
|
|
7
5
|
import { requestSandboxTaskFinalization, SandboxControlClientError } from '../sandbox/control/client.js';
|
|
6
|
+
import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.js';
|
|
8
7
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
9
8
|
const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
|
|
10
|
-
function envelope(status, changed, accepted, result, error) {
|
|
11
|
-
return
|
|
9
|
+
function envelope(status, changed, accepted, result, error, requestId = null) {
|
|
10
|
+
return serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error });
|
|
12
11
|
}
|
|
13
12
|
function exitCode(status) {
|
|
14
13
|
return status === 'completed' ? 0 : status === 'blocked' ? 2 : 1;
|
|
@@ -65,22 +64,9 @@ async function taskFinalization(args = []) {
|
|
|
65
64
|
};
|
|
66
65
|
const transport = resolveSandboxControlTransport(process.env, { localWorkflow: true });
|
|
67
66
|
if (transport.kind === 'sandbox-local') {
|
|
68
|
-
const prepared = await prepareTaskFinalization(boundOperation.request, {
|
|
69
|
-
repoRoot,
|
|
70
|
-
preflight: (request, options) => verifyTaskEvent({ ...request, event: 'complete-task.hard-preflight' }, options)
|
|
71
|
-
});
|
|
72
|
-
if (prepared.status !== 'prepared') {
|
|
73
|
-
process.stdout.write(envelope(prepared.status, prepared.changed, true, prepared, prepared.error));
|
|
74
|
-
process.exitCode = exitCode(prepared.status);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
67
|
try {
|
|
78
68
|
const response = requestSandboxTaskFinalization({
|
|
79
|
-
agent: boundOperation.request.agent
|
|
80
|
-
prepareHandoff: (binding) => {
|
|
81
|
-
const receipt = bindTaskFinalizationReceipt(repoRoot, boundOperation.request.taskRef, binding);
|
|
82
|
-
return publishTaskFinalizationHandoff('/share/branch', receipt, binding);
|
|
83
|
-
}
|
|
69
|
+
agent: boundOperation.request.agent
|
|
84
70
|
});
|
|
85
71
|
process.stdout.write(response.stdout);
|
|
86
72
|
process.stderr.write(response.stderr);
|
|
@@ -88,11 +74,16 @@ async function taskFinalization(args = []) {
|
|
|
88
74
|
return;
|
|
89
75
|
}
|
|
90
76
|
catch (error) {
|
|
91
|
-
const
|
|
92
|
-
|
|
77
|
+
const clientError = error instanceof SandboxControlClientError ? error : null;
|
|
78
|
+
const detail = clientError
|
|
79
|
+
? clientError.detail
|
|
93
80
|
: { code: 'SANDBOX_CONTROL_CLIENT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
const status = detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN'
|
|
82
|
+
? 'unknown'
|
|
83
|
+
: detail.retryable ? 'blocked' : 'failed';
|
|
84
|
+
const changed = status === 'unknown' ? null : false;
|
|
85
|
+
process.stdout.write(envelope(status, changed, clientError?.accepted ?? false, null, detail, clientError?.requestId ?? null));
|
|
86
|
+
process.exitCode = exitCode(status);
|
|
96
87
|
return;
|
|
97
88
|
}
|
|
98
89
|
}
|
|
@@ -102,9 +102,8 @@ function internalTaskRoutes() {
|
|
|
102
102
|
descriptor('internal', 'task-review', 'preflight', 'task-bound', 'artifact-write'),
|
|
103
103
|
descriptor('internal', 'task-review', 'finalize-summary', 'task-bound', 'progress'),
|
|
104
104
|
descriptor('internal', 'task-event', 'event', 'task-bound', 'progress'),
|
|
105
|
-
descriptor('internal', 'task-invalidation', 'reconcile', 'task-bound', 'progress'),
|
|
106
105
|
descriptor('internal', 'task-lifecycle', 'intent', 'task-bound', 'progress'),
|
|
107
|
-
descriptor('internal', 'task-finalization', 'complete', 'task-bound', '
|
|
106
|
+
descriptor('internal', 'task-finalization', 'complete', 'task-bound', 'recovery'),
|
|
108
107
|
descriptor('internal', 'task-platform-recovery', 'inspect', 'task-bound', 'diagnostic'),
|
|
109
108
|
descriptor('internal', 'task-platform-recovery', 'recover', 'task-bound', 'recovery'),
|
|
110
109
|
descriptor('internal', 'task-short-id', 'list', 'conditional', 'diagnostic'),
|
|
@@ -179,7 +178,7 @@ export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
|
|
|
179
178
|
'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
|
|
180
179
|
'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
|
|
181
180
|
'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
|
|
182
|
-
'task-orchestration', 'task-review', 'task-event', 'task-
|
|
181
|
+
'task-orchestration', 'task-review', 'task-event', 'task-lifecycle',
|
|
183
182
|
'task-finalization', 'task-platform-recovery', 'task-short-id', 'task-snapshot', 'task-verify', 'task-validate', 'manual-validation'
|
|
184
183
|
]);
|
|
185
184
|
export const PUBLIC_DISPATCHER_ROUTES = Object.freeze([
|
|
@@ -2,7 +2,7 @@ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
|
|
|
2
2
|
import { readPlatformOperationJournal } from '../task/platform-operation-journal.js';
|
|
3
3
|
import { recoverPlatformOperations } from '../task/platform-operation-recovery.js';
|
|
4
4
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
5
|
-
const USAGE = 'Usage: agent-infra-internal task-platform-recovery <N | TASK-id> <inspect | recover> [--agent <agent>] [--selection required|deferred|all]\n';
|
|
5
|
+
const USAGE = 'Usage: agent-infra-internal task-platform-recovery <N | TASK-id> <inspect | recover> [--agent <agent>] [--selection required|deferred|all] [--attempts <positive-integer>]\n';
|
|
6
6
|
function option(args, name) {
|
|
7
7
|
const index = args.indexOf(name);
|
|
8
8
|
return index >= 0 ? args[index + 1] : undefined;
|
|
@@ -41,14 +41,19 @@ async function taskPlatformRecovery(args = []) {
|
|
|
41
41
|
}
|
|
42
42
|
const agent = option(args, '--agent');
|
|
43
43
|
const selection = option(args, '--selection') ?? 'all';
|
|
44
|
+
const attemptsInput = option(args, '--attempts');
|
|
45
|
+
const attempts = attemptsInput === undefined ? undefined : Number(attemptsInput);
|
|
46
|
+
const validAttempts = attemptsInput === undefined
|
|
47
|
+
|| (attempts !== undefined && Number.isSafeInteger(attempts) && attempts > 0);
|
|
44
48
|
if (!agent || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u.test(agent)
|
|
45
|
-
|| !['required', 'deferred', 'all'].includes(selection)
|
|
46
|
-
|
|
49
|
+
|| !['required', 'deferred', 'all'].includes(selection)
|
|
50
|
+
|| !validAttempts) {
|
|
51
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'PLATFORM_OPERATION_RECOVERY_PAYLOAD_INVALID', message: 'recover requires a valid --agent, --selection, and positive --attempts when supplied' } })}\n`);
|
|
47
52
|
process.stderr.write(USAGE);
|
|
48
53
|
process.exitCode = 1;
|
|
49
54
|
return;
|
|
50
55
|
}
|
|
51
|
-
const result = await recoverPlatformOperations(task.taskId, selection, { agent, cwd: repoRoot });
|
|
56
|
+
const result = await recoverPlatformOperations(task.taskId, selection, { agent, cwd: repoRoot, ...(attempts === undefined ? {} : { attempts }) });
|
|
52
57
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
53
58
|
process.exitCode = result.status === 'applied' || result.status === 'no-op' ? 0 : result.status === 'blocked' ? 2 : 1;
|
|
54
59
|
}
|
|
@@ -8,6 +8,7 @@ import { getOpenWorkflowWarnings } from '../task/workflow-warnings.js';
|
|
|
8
8
|
import { applyWorkflowWarningIntent } from '../task/workflow-warning-intents.js';
|
|
9
9
|
import { captureTaskWriteMetadata } from '../task/write.js';
|
|
10
10
|
import { syncPlatformComment } from './issue-comments.js';
|
|
11
|
+
import { providerErrorRetryable } from './provider-validation.js';
|
|
11
12
|
import { platformResult } from './types.js';
|
|
12
13
|
import { taskIssueIdentity } from './task-identities.js';
|
|
13
14
|
function result(base, artifacts, warnings) {
|
|
@@ -124,7 +125,7 @@ async function backfillCompletionComments(taskRef, options) {
|
|
|
124
125
|
if (latest.status === 'failed' || latest.status === 'blocked')
|
|
125
126
|
return result({ ...latest, operations }, artifacts, []);
|
|
126
127
|
if (latest.error)
|
|
127
|
-
return result(platformResult(latest.error.retryable ? 'blocked' : 'failed', {
|
|
128
|
+
return result(platformResult(latest.error.retryable || providerErrorRetryable(latest.error.code) ? 'blocked' : 'failed', {
|
|
128
129
|
platform: latest.platform,
|
|
129
130
|
resource: latest.resource,
|
|
130
131
|
capabilities: latest.capabilities,
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { loadPlatformProvider } from './provider-loader.js';
|
|
3
3
|
import { defaultGitRemote, parseGitHubRemote } from './github-provider.js';
|
|
4
|
+
import { providerErrorRetryable } from './provider-validation.js';
|
|
4
5
|
import { platformResult } from './types.js';
|
|
5
6
|
function errorStatus(error) {
|
|
6
|
-
if (error.retryable || error.code === 'AUTH_REQUIRED' || error.code === 'PLATFORM_DEPENDENCY_MISSING')
|
|
7
|
+
if (error.retryable || providerErrorRetryable(error.code) || error.code === 'AUTH_REQUIRED' || error.code === 'PLATFORM_DEPENDENCY_MISSING')
|
|
7
8
|
return 'blocked';
|
|
8
9
|
if (error.code === 'REMOTE_MISSING' || error.code === 'PLATFORM_UNSUPPORTED')
|
|
9
10
|
return 'no-op';
|
|
10
11
|
return 'failed';
|
|
11
12
|
}
|
|
12
13
|
function contextError(providerType, error, repository = null) {
|
|
14
|
+
const retryable = error.retryable ?? providerErrorRetryable(error.code);
|
|
13
15
|
return platformResult(errorStatus(error), {
|
|
14
16
|
platform: { type: providerType || null, repository, currentUser: null },
|
|
15
|
-
error: { code: error.code, message: error.message, retryable
|
|
17
|
+
error: { code: error.code, message: error.message, retryable }
|
|
16
18
|
});
|
|
17
19
|
}
|
|
18
20
|
function contextFromSnapshot(providerType, snapshot) {
|
|
@@ -59,30 +59,33 @@ function classifyGitHubFailure(result) {
|
|
|
59
59
|
return {
|
|
60
60
|
code: 'PLATFORM_OUTPUT_TOO_LARGE',
|
|
61
61
|
message: 'GitHub CLI output exceeded the configured limit',
|
|
62
|
-
|
|
62
|
+
requestRetryable: false
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
const detail = boundedFailureDetail(result);
|
|
66
66
|
const lower = detail.toLowerCase();
|
|
67
67
|
if (/\b401\b|bad credentials|authentication required|not logged into/.test(lower)) {
|
|
68
|
-
return { code: 'AUTH_REQUIRED', message: detail || 'GitHub authentication is required',
|
|
68
|
+
return { code: 'AUTH_REQUIRED', message: detail || 'GitHub authentication is required', requestRetryable: false };
|
|
69
69
|
}
|
|
70
70
|
if (/\b429\b|rate limit|secondary rate|\b5\d\d\b|timeout|timed out|econnreset|enotfound|dns|tls|socket|network/.test(lower)) {
|
|
71
|
-
return { code: 'NETWORK_TRANSIENT', message: detail || 'GitHub request failed temporarily',
|
|
71
|
+
return { code: 'NETWORK_TRANSIENT', message: detail || 'GitHub request failed temporarily', requestRetryable: true };
|
|
72
72
|
}
|
|
73
73
|
if (/\b403\b|resource not accessible|permission denied/.test(lower)) {
|
|
74
|
-
return { code: 'PERMISSION_DENIED', message: detail || 'GitHub permission denied',
|
|
74
|
+
return { code: 'PERMISSION_DENIED', message: detail || 'GitHub permission denied', requestRetryable: false };
|
|
75
75
|
}
|
|
76
76
|
if (/\b404\b/.test(lower)) {
|
|
77
|
-
return { code: 'RESOURCE_NOT_FOUND', message: detail || 'GitHub resource not found',
|
|
77
|
+
return { code: 'RESOURCE_NOT_FOUND', message: detail || 'GitHub resource not found', requestRetryable: false };
|
|
78
78
|
}
|
|
79
79
|
if (/\b422\b|validation failed/.test(lower)) {
|
|
80
|
-
return { code: 'PLATFORM_REQUEST_INVALID', message: detail || 'GitHub rejected the request',
|
|
80
|
+
return { code: 'PLATFORM_REQUEST_INVALID', message: detail || 'GitHub rejected the request', requestRetryable: false };
|
|
81
81
|
}
|
|
82
82
|
if (errorCode === 'ENOENT') {
|
|
83
|
-
return { code: 'PLATFORM_DEPENDENCY_MISSING', message: detail || 'GitHub CLI is unavailable',
|
|
83
|
+
return { code: 'PLATFORM_DEPENDENCY_MISSING', message: detail || 'GitHub CLI is unavailable', requestRetryable: false };
|
|
84
84
|
}
|
|
85
|
-
return { code: 'PLATFORM_REQUEST_FAILED', message: detail || 'GitHub request failed',
|
|
85
|
+
return { code: 'PLATFORM_REQUEST_FAILED', message: detail || 'GitHub request failed', requestRetryable: false };
|
|
86
|
+
}
|
|
87
|
+
function clientError(error) {
|
|
88
|
+
return { code: error.code, message: error.message };
|
|
86
89
|
}
|
|
87
90
|
function responseUrl(args) {
|
|
88
91
|
const endpoint = args.find((arg, index) => index > 0 && !arg.startsWith('-')) || '';
|
|
@@ -132,14 +135,14 @@ function createGitHubClient(options = {}) {
|
|
|
132
135
|
if (result.status === 0)
|
|
133
136
|
return { ok: true, value: result.stdout };
|
|
134
137
|
const error = classifyGitHubFailure(result);
|
|
135
|
-
if (!retryableMethod || !error.
|
|
136
|
-
return { ok: false, error };
|
|
138
|
+
if (!retryableMethod || !error.requestRetryable)
|
|
139
|
+
return { ok: false, error: clientError(error) };
|
|
137
140
|
if (attempt >= delays.length) {
|
|
138
141
|
return {
|
|
139
142
|
ok: false,
|
|
140
143
|
error: error.code === 'NETWORK_TRANSIENT'
|
|
141
|
-
? { ...error, code: 'NETWORK_RETRY_EXHAUSTED', message: `${error.message} (retry exhausted)` }
|
|
142
|
-
: error
|
|
144
|
+
? { ...clientError(error), code: 'NETWORK_RETRY_EXHAUSTED', message: `${error.message} (retry exhausted)` }
|
|
145
|
+
: clientError(error)
|
|
143
146
|
};
|
|
144
147
|
}
|
|
145
148
|
sleep(delays[attempt]);
|
|
@@ -150,12 +153,12 @@ function createGitHubClient(options = {}) {
|
|
|
150
153
|
version(request = {}) {
|
|
151
154
|
const result = runner(['--version'], request);
|
|
152
155
|
if (result.status !== 0)
|
|
153
|
-
return { ok: false, error: classifyGitHubFailure(result) };
|
|
156
|
+
return { ok: false, error: clientError(classifyGitHubFailure(result)) };
|
|
154
157
|
const version = result.stdout.match(/^gh version (\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)(?:\s|$)/m)?.[1];
|
|
155
158
|
if (!version || !semver.valid(version)) {
|
|
156
159
|
return {
|
|
157
160
|
ok: false,
|
|
158
|
-
error: { code: 'GH_CLI_VERSION_INVALID', message: 'GitHub CLI returned an invalid version'
|
|
161
|
+
error: { code: 'GH_CLI_VERSION_INVALID', message: 'GitHub CLI returned an invalid version' }
|
|
159
162
|
};
|
|
160
163
|
}
|
|
161
164
|
return { ok: true, value: version };
|
|
@@ -174,7 +177,7 @@ function createGitHubClient(options = {}) {
|
|
|
174
177
|
catch {
|
|
175
178
|
return {
|
|
176
179
|
ok: false,
|
|
177
|
-
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub returned invalid JSON'
|
|
180
|
+
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub returned invalid JSON' }
|
|
178
181
|
};
|
|
179
182
|
}
|
|
180
183
|
},
|
|
@@ -189,12 +192,12 @@ function createGitHubClient(options = {}) {
|
|
|
189
192
|
if (!parsed) {
|
|
190
193
|
return {
|
|
191
194
|
ok: false,
|
|
192
|
-
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub response metadata or JSON is invalid'
|
|
195
|
+
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub response metadata or JSON is invalid' }
|
|
193
196
|
};
|
|
194
197
|
}
|
|
195
198
|
return { ok: true, value: parsed };
|
|
196
199
|
}
|
|
197
200
|
};
|
|
198
201
|
}
|
|
199
|
-
export {
|
|
202
|
+
export { createGitHubClient, MINIMUM_GITHUB_CLI_VERSION, parseIncludedResponse };
|
|
200
203
|
//# sourceMappingURL=github-client.js.map
|