@fitlab-ai/agent-infra 0.8.2 → 0.8.4
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.zh-CN.md +10 -0
- package/bin/cli.ts +1 -1
- package/dist/bin/cli.js +2 -2
- package/dist/lib/cp.js +57 -16
- package/dist/lib/decide.js +2 -2
- package/dist/lib/defaults.json +2 -0
- package/dist/lib/init.js +7 -7
- package/dist/lib/merge.js +1 -1
- package/dist/lib/prompt.js +1 -1
- package/dist/lib/run/index.js +7 -7
- package/dist/lib/run/prompt.js +1 -1
- package/dist/lib/sandbox/capture.js +10 -8
- package/dist/lib/sandbox/clipboard/bridge.js +59 -21
- package/dist/lib/sandbox/clipboard/inbox.js +82 -0
- package/dist/lib/sandbox/clipboard/index.js +5 -6
- package/dist/lib/sandbox/clipboard/linux.js +91 -0
- package/dist/lib/sandbox/clipboard/paths.js +1 -1
- package/dist/lib/sandbox/clipboard/win32.js +144 -0
- package/dist/lib/sandbox/commands/create.js +106 -88
- package/dist/lib/sandbox/commands/enter.js +9 -9
- package/dist/lib/sandbox/commands/list-running.js +2 -2
- package/dist/lib/sandbox/commands/ls.js +7 -7
- package/dist/lib/sandbox/commands/prune.js +8 -8
- package/dist/lib/sandbox/commands/rebuild.js +38 -50
- package/dist/lib/sandbox/commands/refresh.js +2 -2
- package/dist/lib/sandbox/commands/rm.js +11 -11
- package/dist/lib/sandbox/commands/show.js +4 -4
- package/dist/lib/sandbox/commands/start.js +4 -4
- package/dist/lib/sandbox/commands/vm.js +4 -4
- package/dist/lib/sandbox/config.js +10 -4
- package/dist/lib/sandbox/constants.js +4 -1
- package/dist/lib/sandbox/credentials.js +1 -1
- package/dist/lib/sandbox/dotfiles.js +1 -1
- package/dist/lib/sandbox/engine.js +3 -3
- package/dist/lib/sandbox/engines/index.js +5 -5
- package/dist/lib/sandbox/engines/wsl2-paths.js +1 -1
- package/dist/lib/sandbox/image-build.js +80 -0
- package/dist/lib/sandbox/image-prune.js +2 -2
- package/dist/lib/sandbox/index.js +10 -10
- package/dist/lib/sandbox/managed-fs.js +1 -1
- package/dist/lib/sandbox/readme-scaffold.js +1 -1
- package/dist/lib/sandbox/runtimes/base.dockerfile +3 -0
- package/dist/lib/sandbox/shell.js +9 -1
- package/dist/lib/sandbox/tools.js +4 -4
- package/dist/lib/server/adapters/feishu/index.js +10 -3
- package/dist/lib/server/adapters/feishu/renderer.js +88 -0
- package/dist/lib/server/adapters/feishu/transport.js +2 -13
- package/dist/lib/server/daemon.js +64 -43
- package/dist/lib/server/display.js +83 -0
- package/dist/lib/server/index.js +2 -2
- package/dist/lib/server/process-control.js +2 -2
- package/dist/lib/server/protocol.js +2 -2
- package/dist/lib/server/streamer.js +5 -4
- package/dist/lib/task/commands/cat.js +2 -2
- package/dist/lib/task/commands/decisions.js +4 -4
- package/dist/lib/task/commands/files.js +3 -3
- package/dist/lib/task/commands/grep.js +3 -3
- package/dist/lib/task/commands/issue-body.js +4 -4
- package/dist/lib/task/commands/log.js +5 -5
- package/dist/lib/task/commands/ls.js +3 -3
- package/dist/lib/task/commands/show.js +1 -1
- package/dist/lib/task/commands/status.js +71 -25
- package/dist/lib/task/index.js +9 -9
- package/dist/lib/task/resolve-ref.js +1 -1
- package/dist/lib/task/workflow-warnings.js +94 -0
- package/dist/lib/update.js +4 -4
- package/lib/cp.ts +56 -15
- package/lib/defaults.json +2 -0
- package/lib/sandbox/capture.ts +5 -3
- package/lib/sandbox/clipboard/bridge.ts +62 -18
- package/lib/sandbox/clipboard/inbox.ts +83 -0
- package/lib/sandbox/clipboard/index.ts +5 -6
- package/lib/sandbox/clipboard/linux.ts +124 -0
- package/lib/sandbox/clipboard/win32.ts +173 -0
- package/lib/sandbox/commands/create.ts +132 -88
- package/lib/sandbox/commands/rebuild.ts +42 -54
- package/lib/sandbox/config.ts +13 -1
- package/lib/sandbox/constants.ts +4 -0
- package/lib/sandbox/image-build.ts +134 -0
- package/lib/sandbox/runtimes/base.dockerfile +3 -0
- package/lib/sandbox/shell.ts +11 -2
- package/lib/sandbox/tools.ts +5 -5
- package/lib/server/adapters/_contract.ts +3 -0
- package/lib/server/adapters/feishu/index.ts +11 -3
- package/lib/server/adapters/feishu/renderer.ts +99 -0
- package/lib/server/adapters/feishu/transport.ts +5 -18
- package/lib/server/daemon.ts +74 -38
- package/lib/server/display.ts +136 -0
- package/lib/server/streamer.ts +5 -4
- package/lib/task/commands/log.ts +3 -3
- package/lib/task/commands/status.ts +102 -21
- package/lib/task/workflow-warnings.ts +121 -0
- package/package.json +2 -2
- package/templates/.agents/README.en.md +3 -0
- package/templates/.agents/README.zh-CN.md +3 -0
- package/templates/.agents/rules/create-issue.github.en.md +4 -4
- package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -4
- package/templates/.agents/rules/issue-pr-commands.github.en.md +7 -2
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +7 -2
- package/templates/.agents/rules/issue-sync.github.en.md +13 -0
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +13 -0
- package/templates/.agents/rules/next-step-output.en.md +15 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +15 -1
- package/templates/.agents/rules/pr-sync.github.en.md +17 -2
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +17 -2
- package/templates/.agents/rules/review-handshake.en.md +2 -2
- package/templates/.agents/rules/review-handshake.zh-CN.md +2 -2
- package/templates/.agents/rules/task-management.en.md +1 -1
- package/templates/.agents/rules/task-management.zh-CN.md +1 -1
- package/templates/.agents/rules/testing-discipline.en.md +3 -41
- package/templates/.agents/rules/testing-discipline.zh-CN.md +3 -41
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +57 -0
- package/templates/.agents/scripts/validate-artifact.js +213 -0
- package/templates/.agents/scripts/workflow-warnings.js +290 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +9 -9
- package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/branch-management.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/output-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/commit/reference/task-status-update.en.md +6 -6
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +6 -6
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +142 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +142 -0
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +49 -0
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +49 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +48 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +48 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +60 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +87 -0
- package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- 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/create-task/SKILL.en.md +6 -3
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/import-issue/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -2
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +165 -41
- package/templates/.agents/skills/plan-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-code/SKILL.en.md +8 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-code/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/test/SKILL.zh-CN.md +26 -8
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +36 -6
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +8 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +8 -1
- package/templates/.agents/templates/task.en.md +7 -0
- package/templates/.agents/templates/task.zh-CN.md +7 -0
- package/templates/.claude/commands/complete-manual-validation.en.md +9 -0
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +9 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +8 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +8 -0
- package/templates/.github/workflows/metadata-sync.yml +5 -0
- package/templates/.opencode/commands/complete-manual-validation.en.md +11 -0
- package/templates/.opencode/commands/complete-manual-validation.zh-CN.md +11 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
2
|
+
import { normalizeOutbound, outboundToText } from '../../display.js';
|
|
3
|
+
const FEISHU_LARK_MD_MAX_LENGTH = 20_000;
|
|
4
|
+
function templateForTone(tone) {
|
|
5
|
+
if (tone === 'success')
|
|
6
|
+
return 'green';
|
|
7
|
+
if (tone === 'warning')
|
|
8
|
+
return 'yellow';
|
|
9
|
+
if (tone === 'danger')
|
|
10
|
+
return 'red';
|
|
11
|
+
return 'blue';
|
|
12
|
+
}
|
|
13
|
+
function templateForResult(exitCode) {
|
|
14
|
+
return exitCode === 0 ? 'green' : 'red';
|
|
15
|
+
}
|
|
16
|
+
export function cleanFeishuText(text) {
|
|
17
|
+
return stripVTControlCharacters(text).replace(/\r\n/g, '\n');
|
|
18
|
+
}
|
|
19
|
+
function clampLarkMarkdown(text) {
|
|
20
|
+
const clean = cleanFeishuText(text);
|
|
21
|
+
if (clean.length <= FEISHU_LARK_MD_MAX_LENGTH)
|
|
22
|
+
return clean;
|
|
23
|
+
return `${clean.slice(0, FEISHU_LARK_MD_MAX_LENGTH - 18)}\n\n...(truncated)`;
|
|
24
|
+
}
|
|
25
|
+
function div(content) {
|
|
26
|
+
return { tag: 'div', text: { tag: 'lark_md', content: clampLarkMarkdown(content) } };
|
|
27
|
+
}
|
|
28
|
+
function fields(rows) {
|
|
29
|
+
return {
|
|
30
|
+
tag: 'div',
|
|
31
|
+
fields: rows.map(([label, value]) => ({
|
|
32
|
+
is_short: true,
|
|
33
|
+
text: { tag: 'lark_md', content: clampLarkMarkdown(`**${label}**\n${value}`) }
|
|
34
|
+
}))
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function card(title, template, elements) {
|
|
38
|
+
return {
|
|
39
|
+
msg_type: 'interactive',
|
|
40
|
+
content: JSON.stringify({
|
|
41
|
+
config: { wide_screen_mode: true },
|
|
42
|
+
header: { title: { tag: 'plain_text', content: cleanFeishuText(title) }, template },
|
|
43
|
+
elements
|
|
44
|
+
})
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function tableMarkdown(message) {
|
|
48
|
+
return outboundToText(message);
|
|
49
|
+
}
|
|
50
|
+
function renderStatus(message) {
|
|
51
|
+
const elements = [];
|
|
52
|
+
if (message.fields && message.fields.length > 0)
|
|
53
|
+
elements.push(fields(message.fields));
|
|
54
|
+
if (message.body)
|
|
55
|
+
elements.push(div(message.body));
|
|
56
|
+
if (elements.length === 0)
|
|
57
|
+
elements.push(div(message.title));
|
|
58
|
+
return card(message.title, templateForTone(message.tone), elements);
|
|
59
|
+
}
|
|
60
|
+
function renderStream(message) {
|
|
61
|
+
const template = message.phase === 'finished' ? templateForResult(message.exitCode ?? null) : 'blue';
|
|
62
|
+
return card(message.title, template, [div(outboundToText(message))]);
|
|
63
|
+
}
|
|
64
|
+
function renderCommandResult(message) {
|
|
65
|
+
return card(message.title, templateForResult(message.exitCode), [div(outboundToText(message))]);
|
|
66
|
+
}
|
|
67
|
+
function renderUnknownDisplayKind(message) {
|
|
68
|
+
return card('agent-infra', 'blue', [div(`[unknown display kind: ${String(message.kind)}]`)]);
|
|
69
|
+
}
|
|
70
|
+
export function renderFeishuMessage(message) {
|
|
71
|
+
const normalized = normalizeOutbound(message);
|
|
72
|
+
switch (normalized.kind) {
|
|
73
|
+
case 'text':
|
|
74
|
+
return card('agent-infra', 'blue', [div(normalized.text)]);
|
|
75
|
+
case 'markdown':
|
|
76
|
+
return card('agent-infra', 'blue', [div(normalized.markdown)]);
|
|
77
|
+
case 'table':
|
|
78
|
+
return card(normalized.title ?? 'agent-infra', 'blue', [div(tableMarkdown(normalized))]);
|
|
79
|
+
case 'status-card':
|
|
80
|
+
return renderStatus(normalized);
|
|
81
|
+
case 'stream-event':
|
|
82
|
+
return renderStream(normalized);
|
|
83
|
+
case 'command-result':
|
|
84
|
+
return renderCommandResult(normalized);
|
|
85
|
+
}
|
|
86
|
+
return renderUnknownDisplayKind(normalized);
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as lark from '@larksuiteoapi/node-sdk';
|
|
2
|
-
import { stripVTControlCharacters } from 'node:util';
|
|
3
2
|
function asRecord(value) {
|
|
4
3
|
return typeof value === 'object' && value !== null ? value : {};
|
|
5
4
|
}
|
|
@@ -41,21 +40,11 @@ export function normalizeMessage(event) {
|
|
|
41
40
|
function resolveDomain(value) {
|
|
42
41
|
return value === 'lark' || value === 'Lark' ? lark.Domain.Lark : lark.Domain.Feishu;
|
|
43
42
|
}
|
|
44
|
-
export function cleanFeishuText(text) {
|
|
45
|
-
return stripVTControlCharacters(text).replace(/\r\n/g, '\n');
|
|
46
|
-
}
|
|
47
|
-
export function cardMessage(text) {
|
|
48
|
-
return { kind: 'interactive', title: 'agent-infra', text: cleanFeishuText(text) };
|
|
49
|
-
}
|
|
50
43
|
export function toFeishuCreateData(chatId, message) {
|
|
51
44
|
return {
|
|
52
45
|
receive_id: chatId,
|
|
53
|
-
msg_type:
|
|
54
|
-
content:
|
|
55
|
-
config: { wide_screen_mode: true },
|
|
56
|
-
header: { title: { tag: 'plain_text', content: message.title }, template: 'blue' },
|
|
57
|
-
elements: [{ tag: 'div', text: { tag: 'lark_md', content: message.text } }]
|
|
58
|
-
})
|
|
46
|
+
msg_type: message.msg_type,
|
|
47
|
+
content: message.content
|
|
59
48
|
};
|
|
60
49
|
}
|
|
61
50
|
// Build the real SDK-backed transport from adapter config. appId/appSecret come
|
|
@@ -1,14 +1,69 @@
|
|
|
1
|
-
import { VERSION } from
|
|
2
|
-
import { loadServerConfig } from
|
|
3
|
-
import { createLogger } from
|
|
4
|
-
import { loadAdapters, unloadAdapters } from
|
|
5
|
-
import { authorize } from
|
|
6
|
-
import { commandHelp, parseCommand } from
|
|
7
|
-
import { runAi } from
|
|
8
|
-
import { streamCommand } from
|
|
1
|
+
import { VERSION } from '../version.js';
|
|
2
|
+
import { loadServerConfig } from './config.js';
|
|
3
|
+
import { createLogger } from './logger.js';
|
|
4
|
+
import { loadAdapters, unloadAdapters } from './plugin-loader.js';
|
|
5
|
+
import { authorize } from './auth.js';
|
|
6
|
+
import { commandHelp, parseCommand } from './protocol.js';
|
|
7
|
+
import { runAi } from './runner.js';
|
|
8
|
+
import { streamCommand } from './streamer.js';
|
|
9
|
+
import { markdownMessage, replyOutbound, textMessage } from './display.js';
|
|
10
|
+
import { buildStatusModel, statusModelToDisplay } from '../task/commands/status.js';
|
|
9
11
|
function errorMessage(error) {
|
|
10
12
|
return error instanceof Error ? error.message : String(error);
|
|
11
13
|
}
|
|
14
|
+
function isTaskStatus(argv) {
|
|
15
|
+
return argv[0] === 'task' && argv[1] === 'status' && typeof argv[2] === 'string';
|
|
16
|
+
}
|
|
17
|
+
export function createMessageDispatcher(options) {
|
|
18
|
+
const runAiImpl = options.runAi ?? runAi;
|
|
19
|
+
const buildStatusModelImpl = options.buildStatusModel ?? buildStatusModel;
|
|
20
|
+
const statusModelToDisplayImpl = options.statusModelToDisplay ?? statusModelToDisplay;
|
|
21
|
+
return async (message) => {
|
|
22
|
+
const plan = parseCommand(message.text);
|
|
23
|
+
if (plan.kind === 'ignore')
|
|
24
|
+
return;
|
|
25
|
+
if (plan.kind === 'error') {
|
|
26
|
+
await replyOutbound(message, textMessage(plan.message));
|
|
27
|
+
options.logger.info(`command rejected from ${message.adapter}:${message.userId}: ${plan.message}`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (plan.kind === 'builtin' && plan.name === 'ping') {
|
|
31
|
+
await replyOutbound(message, textMessage(`pong ${VERSION}`));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (plan.kind === 'builtin' && plan.name === 'help') {
|
|
35
|
+
await replyOutbound(message, markdownMessage(commandHelp()));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (plan.kind === 'builtin' && plan.name === 'version') {
|
|
39
|
+
await replyOutbound(message, textMessage(`agent-infra ${VERSION}`));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (plan.kind === 'ai') {
|
|
43
|
+
const allowed = authorize({ adapter: message.adapter, userId: message.userId }, plan.role, options.config.auth);
|
|
44
|
+
if (!allowed.ok) {
|
|
45
|
+
await replyOutbound(message, textMessage(allowed.message));
|
|
46
|
+
options.logger.info(`unauthorized command from ${message.adapter}:${message.userId}: ${allowed.message}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (isTaskStatus(plan.argv)) {
|
|
50
|
+
try {
|
|
51
|
+
await replyOutbound(message, statusModelToDisplayImpl(buildStatusModelImpl(plan.argv[2])));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Fall back to the existing CLI streaming path. This preserves the
|
|
56
|
+
// old behavior for invalid refs and any status-model collection error.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
await streamCommand({
|
|
60
|
+
title: `ai ${plan.argv.join(' ')}`,
|
|
61
|
+
chunkChars: typeof options.config.stream?.chunkChars === 'number' ? options.config.stream.chunkChars : 4000,
|
|
62
|
+
throttleMs: typeof options.config.stream?.throttleMs === 'number' ? options.config.stream.throttleMs : 1500
|
|
63
|
+
}, (emit) => runAiImpl(plan.argv, { onChunk: emit }), (outbound) => replyOutbound(message, outbound));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
12
67
|
// The daemon main loop. Runs in the detached child spawned by
|
|
13
68
|
// process-control.start(), or in the foreground for debugging.
|
|
14
69
|
//
|
|
@@ -35,41 +90,7 @@ export async function runDaemon() {
|
|
|
35
90
|
const shutdown = new Promise((resolve) => {
|
|
36
91
|
resolveShutdown = resolve;
|
|
37
92
|
});
|
|
38
|
-
const dispatch =
|
|
39
|
-
const plan = parseCommand(message.text);
|
|
40
|
-
if (plan.kind === 'ignore')
|
|
41
|
-
return;
|
|
42
|
-
if (plan.kind === 'error') {
|
|
43
|
-
await message.reply(plan.message);
|
|
44
|
-
logger.info(`command rejected from ${message.adapter}:${message.userId}: ${plan.message}`);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
if (plan.kind === 'builtin' && plan.name === 'ping') {
|
|
48
|
-
await message.reply(`pong ${VERSION}`);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (plan.kind === 'builtin' && plan.name === 'help') {
|
|
52
|
-
await message.reply(commandHelp());
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (plan.kind === 'builtin' && plan.name === 'version') {
|
|
56
|
-
await message.reply(`agent-infra ${VERSION}`);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (plan.kind === 'ai') {
|
|
60
|
-
const allowed = authorize({ adapter: message.adapter, userId: message.userId }, plan.role, config.auth);
|
|
61
|
-
if (!allowed.ok) {
|
|
62
|
-
await message.reply(allowed.message);
|
|
63
|
-
logger.info(`unauthorized command from ${message.adapter}:${message.userId}: ${allowed.message}`);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
await streamCommand({
|
|
67
|
-
title: `ai ${plan.argv.join(' ')}`,
|
|
68
|
-
chunkChars: typeof config.stream?.chunkChars === 'number' ? config.stream.chunkChars : 4000,
|
|
69
|
-
throttleMs: typeof config.stream?.throttleMs === 'number' ? config.stream.throttleMs : 1500
|
|
70
|
-
}, (emit) => runAi(plan.argv, { onChunk: emit }), (text) => message.reply(text));
|
|
71
|
-
}
|
|
72
|
-
};
|
|
93
|
+
const dispatch = createMessageDispatcher({ config, logger });
|
|
73
94
|
const ctx = { config, logger, dispatch, signal: abortController.signal };
|
|
74
95
|
const adapters = await loadAdapters(config, ctx);
|
|
75
96
|
logger.ok(`loaded ${adapters.length} adapter(s)`);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export function textMessage(text) {
|
|
2
|
+
return { kind: 'text', text };
|
|
3
|
+
}
|
|
4
|
+
export function markdownMessage(markdown) {
|
|
5
|
+
return { kind: 'markdown', markdown };
|
|
6
|
+
}
|
|
7
|
+
export function tableMessage(columns, rows, title) {
|
|
8
|
+
return { kind: 'table', title, columns, rows };
|
|
9
|
+
}
|
|
10
|
+
export function statusCard(title, tone, fields, body) {
|
|
11
|
+
return { kind: 'status-card', title, tone, fields, body };
|
|
12
|
+
}
|
|
13
|
+
export function streamEvent(title, phase, text, exitCode, signal) {
|
|
14
|
+
const message = { kind: 'stream-event', title, phase };
|
|
15
|
+
if (text !== undefined)
|
|
16
|
+
message.text = text;
|
|
17
|
+
if (exitCode !== undefined)
|
|
18
|
+
message.exitCode = exitCode;
|
|
19
|
+
if (signal !== undefined)
|
|
20
|
+
message.signal = signal;
|
|
21
|
+
return message;
|
|
22
|
+
}
|
|
23
|
+
export function commandResult(title, exitCode, signal, stdout, stderr) {
|
|
24
|
+
return { kind: 'command-result', title, exitCode, signal, stdout, stderr };
|
|
25
|
+
}
|
|
26
|
+
export function normalizeOutbound(message) {
|
|
27
|
+
return typeof message === 'string' ? textMessage(message) : message;
|
|
28
|
+
}
|
|
29
|
+
function tableToText(message) {
|
|
30
|
+
const lines = [];
|
|
31
|
+
if (message.title)
|
|
32
|
+
lines.push(message.title);
|
|
33
|
+
lines.push(message.columns.join(' | '));
|
|
34
|
+
lines.push(message.columns.map(() => '---').join(' | '));
|
|
35
|
+
for (const row of message.rows) {
|
|
36
|
+
lines.push(row.join(' | '));
|
|
37
|
+
}
|
|
38
|
+
return lines.join('\n');
|
|
39
|
+
}
|
|
40
|
+
function compact(lines) {
|
|
41
|
+
return lines.filter((line) => typeof line === 'string' && line !== '').join('\n');
|
|
42
|
+
}
|
|
43
|
+
function unknownDisplayKind(message) {
|
|
44
|
+
return `[unknown: ${String(message.kind)}]`;
|
|
45
|
+
}
|
|
46
|
+
export function outboundToText(message) {
|
|
47
|
+
const normalized = normalizeOutbound(message);
|
|
48
|
+
switch (normalized.kind) {
|
|
49
|
+
case 'text':
|
|
50
|
+
return normalized.text;
|
|
51
|
+
case 'markdown':
|
|
52
|
+
return normalized.markdown;
|
|
53
|
+
case 'table':
|
|
54
|
+
return tableToText(normalized);
|
|
55
|
+
case 'status-card':
|
|
56
|
+
return compact([
|
|
57
|
+
normalized.title,
|
|
58
|
+
...(normalized.fields ?? []).map(([label, value]) => `${label}: ${value}`),
|
|
59
|
+
normalized.body
|
|
60
|
+
]);
|
|
61
|
+
case 'stream-event':
|
|
62
|
+
if (normalized.phase === 'started')
|
|
63
|
+
return `started ${normalized.title}`;
|
|
64
|
+
if (normalized.phase === 'chunk')
|
|
65
|
+
return normalized.text ?? '';
|
|
66
|
+
return `finished ${normalized.title} exitCode=${normalized.exitCode ?? 'null'} signal=${normalized.signal ?? 'null'}`;
|
|
67
|
+
case 'command-result':
|
|
68
|
+
return compact([
|
|
69
|
+
`finished ${normalized.title} exitCode=${normalized.exitCode ?? 'null'} signal=${normalized.signal ?? 'null'}`,
|
|
70
|
+
normalized.stdout,
|
|
71
|
+
normalized.stderr
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
return unknownDisplayKind(normalized);
|
|
75
|
+
}
|
|
76
|
+
export async function replyOutbound(inbound, message) {
|
|
77
|
+
if (inbound.replyDisplay) {
|
|
78
|
+
await inbound.replyDisplay(message);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await inbound.reply(outboundToText(message));
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=display.js.map
|
package/dist/lib/server/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { runDaemon } from
|
|
2
|
-
import { start, stop, status, logs } from
|
|
1
|
+
import { runDaemon } from './daemon.js';
|
|
2
|
+
import { start, stop, status, logs } from './process-control.js';
|
|
3
3
|
const USAGE = `Usage: ai server <command> [options]
|
|
4
4
|
|
|
5
5
|
Commands:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { spawn, execFileSync } from 'node:child_process';
|
|
4
|
-
import { loadServerConfig } from
|
|
5
|
-
import { runDaemon } from
|
|
4
|
+
import { loadServerConfig } from './config.js';
|
|
5
|
+
import { runDaemon } from './daemon.js';
|
|
6
6
|
export function buildStopCommand(pid, platform) {
|
|
7
7
|
if (platform === 'win32') {
|
|
8
8
|
return { kind: 'exec', command: 'taskkill', args: ['/PID', String(pid), '/T', '/F'] };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { VERSION } from
|
|
2
|
-
import { getSkillRunSpec } from
|
|
1
|
+
import { VERSION } from '../version.js';
|
|
2
|
+
import { getSkillRunSpec } from '../run/skills.js';
|
|
3
3
|
const TASK_READ = new Set(['ls', 'list', 'status', 'show', 'log', 'decisions']);
|
|
4
4
|
const SANDBOX_READ = new Set(['ls', 'list', 'show']);
|
|
5
5
|
const SANDBOX_WRITE = new Set(['create', 'start']);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { streamEvent } from './display.js';
|
|
2
|
+
import { redactSecrets } from './redact.js';
|
|
2
3
|
function chunks(text, size) {
|
|
3
4
|
if (text.length === 0)
|
|
4
5
|
return [];
|
|
@@ -9,7 +10,7 @@ function chunks(text, size) {
|
|
|
9
10
|
return out;
|
|
10
11
|
}
|
|
11
12
|
export async function streamCommand(options, run, send) {
|
|
12
|
-
await send(
|
|
13
|
+
await send(streamEvent(options.title, 'started'));
|
|
13
14
|
const size = options.chunkChars ?? 4000;
|
|
14
15
|
const throttleMs = options.throttleMs ?? 0;
|
|
15
16
|
let streamed = false;
|
|
@@ -22,7 +23,7 @@ export async function streamCommand(options, run, send) {
|
|
|
22
23
|
buffer = '';
|
|
23
24
|
lastFlush = Date.now();
|
|
24
25
|
for (const chunk of chunks(text, size)) {
|
|
25
|
-
await send(chunk);
|
|
26
|
+
await send(streamEvent(options.title, 'chunk', chunk));
|
|
26
27
|
}
|
|
27
28
|
};
|
|
28
29
|
const emit = async (chunk) => {
|
|
@@ -38,7 +39,7 @@ export async function streamCommand(options, run, send) {
|
|
|
38
39
|
buffer += [result.stdout, result.stderr].filter(Boolean).join('\n');
|
|
39
40
|
}
|
|
40
41
|
await flush();
|
|
41
|
-
await send(
|
|
42
|
+
await send(streamEvent(options.title, 'finished', undefined, result.exitCode, result.signal));
|
|
42
43
|
return result;
|
|
43
44
|
}
|
|
44
45
|
//# sourceMappingURL=streamer.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { resolveTaskRef } from
|
|
3
|
-
import { resolveArtifact } from
|
|
2
|
+
import { resolveTaskRef } from '../resolve-ref.js';
|
|
3
|
+
import { resolveArtifact } from '../artifacts.js';
|
|
4
4
|
const USAGE = `Usage: ai task cat <N | #N | TASK-id> <artifact | N>
|
|
5
5
|
|
|
6
6
|
Prints a task artifact's raw content to stdout.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { formatTable } from
|
|
4
|
-
import { resolveTaskRef } from
|
|
5
|
-
import { parseLedger, HUMAN_DECISION_STATUSES } from
|
|
6
|
-
import { extractSubSection } from
|
|
3
|
+
import { formatTable } from '../../table.js';
|
|
4
|
+
import { resolveTaskRef } from '../resolve-ref.js';
|
|
5
|
+
import { parseLedger, HUMAN_DECISION_STATUSES } from '../ledger.js';
|
|
6
|
+
import { extractSubSection } from '../sections.js';
|
|
7
7
|
const USAGE = `Usage: ai task decisions <N | #N | TASK-id> [selector] [options]
|
|
8
8
|
|
|
9
9
|
Lists the human-decision (HD-) items recorded in a task's review disagreement
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { formatTable } from
|
|
2
|
-
import { resolveTaskRef } from
|
|
3
|
-
import { enumerateArtifacts } from
|
|
1
|
+
import { formatTable } from '../../table.js';
|
|
2
|
+
import { resolveTaskRef } from '../resolve-ref.js';
|
|
3
|
+
import { enumerateArtifacts } from '../artifacts.js';
|
|
4
4
|
const USAGE = `Usage: ai task files <N | #N | TASK-id>
|
|
5
5
|
|
|
6
6
|
Lists the artifacts in a task directory with stable numbers.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { resolveTaskRef, detectRepoRoot, enumerateTaskDirs } from
|
|
4
|
-
import { enumerateArtifacts, resolveArtifact } from
|
|
5
|
-
import { loadShortIdByTaskId } from
|
|
3
|
+
import { resolveTaskRef, detectRepoRoot, enumerateTaskDirs } from '../resolve-ref.js';
|
|
4
|
+
import { enumerateArtifacts, resolveArtifact } from '../artifacts.js';
|
|
5
|
+
import { loadShortIdByTaskId } from '../short-id.js';
|
|
6
6
|
const USAGE = `Usage: ai task grep <pattern> [ref] [artifact | N]
|
|
7
7
|
|
|
8
8
|
Literal (non-regex) line search across task artifacts.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { resolveTaskRef } from
|
|
3
|
-
import { extractTitle } from
|
|
4
|
-
import { extractSection, findSectionHeading } from
|
|
5
|
-
import { renderTemplateBody, PLACEHOLDER } from
|
|
2
|
+
import { resolveTaskRef } from '../resolve-ref.js';
|
|
3
|
+
import { extractTitle } from '../frontmatter.js';
|
|
4
|
+
import { extractSection, findSectionHeading } from '../sections.js';
|
|
5
|
+
import { renderTemplateBody, PLACEHOLDER } from '../issue-form.js';
|
|
6
6
|
const USAGE = `Usage: ai task issue-body <N | #N | TASK-id> [--template <path>]
|
|
7
7
|
|
|
8
8
|
Print a deterministic Issue body extracted from a task's task.md.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { formatTable } from
|
|
3
|
-
import { resolveTaskRef } from
|
|
4
|
-
import { parseLedger
|
|
2
|
+
import { formatTable } from '../../table.js';
|
|
3
|
+
import { resolveTaskRef } from '../resolve-ref.js';
|
|
4
|
+
import { parseLedger } from '../ledger.js';
|
|
5
5
|
const USAGE = `Usage: ai task log <N | #N | TASK-id>
|
|
6
6
|
|
|
7
7
|
Renders a task's activity log as a per-step status table. A step's start and
|
|
@@ -13,7 +13,7 @@ Columns: # (row) / STEP / AGENT / STARTED / DONE / NOTE
|
|
|
13
13
|
A human-executed step shows AGENT as 'human' and, when it has no start marker,
|
|
14
14
|
a '-' STARTED placeholder. Review-step NOTE also carries two human counts in
|
|
15
15
|
the verdict list, right after blockers/major/minor: manual-validation
|
|
16
|
-
and human-decision (current ledger stage total).
|
|
16
|
+
and human-decision (current pending ledger stage total).
|
|
17
17
|
`;
|
|
18
18
|
const TABLE_HEADERS = ['#', 'STEP', 'AGENT', 'STARTED', 'DONE', 'NOTE'];
|
|
19
19
|
// The activity-log H2 heading is language-dependent (zh template / en template).
|
|
@@ -100,7 +100,7 @@ function pairEntries(entries) {
|
|
|
100
100
|
function countHumanDecisionsByStage(rows) {
|
|
101
101
|
const counts = new Map();
|
|
102
102
|
for (const row of rows) {
|
|
103
|
-
if (!isReviewStage(row.stage) ||
|
|
103
|
+
if (!isReviewStage(row.stage) || row.status !== 'needs-human-decision')
|
|
104
104
|
continue;
|
|
105
105
|
counts.set(row.stage, (counts.get(row.stage) ?? 0) + 1);
|
|
106
106
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
|
-
import { formatTable } from
|
|
5
|
-
import { parseTaskFrontmatter, extractTitle } from
|
|
6
|
-
import { loadShortIdByTaskId } from
|
|
4
|
+
import { formatTable } from '../../table.js';
|
|
5
|
+
import { parseTaskFrontmatter, extractTitle } from '../frontmatter.js';
|
|
6
|
+
import { loadShortIdByTaskId } from '../short-id.js';
|
|
7
7
|
const USAGE = `Usage: ai task ls [--all | --blocked | --completed]
|
|
8
8
|
|
|
9
9
|
Lists tasks under .agents/workspace/. Defaults to active tasks only.
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
|
-
import { commandForEngine } from
|
|
5
|
-
import { resolveTaskRef } from
|
|
6
|
-
import { enumerateArtifacts } from
|
|
7
|
-
import { parseTaskFrontmatter, extractTitle } from
|
|
8
|
-
import { loadShortIdByTaskId } from
|
|
9
|
-
import {
|
|
4
|
+
import { commandForEngine } from '../../sandbox/shell.js';
|
|
5
|
+
import { resolveTaskRef } from '../resolve-ref.js';
|
|
6
|
+
import { enumerateArtifacts } from '../artifacts.js';
|
|
7
|
+
import { parseTaskFrontmatter, extractTitle } from '../frontmatter.js';
|
|
8
|
+
import { loadShortIdByTaskId } from '../short-id.js';
|
|
9
|
+
import { getOpenWorkflowWarnings, formatWorkflowWarningSummary } from '../workflow-warnings.js';
|
|
10
|
+
import { parseActivityLog, pairEntries } from './log.js';
|
|
11
|
+
import { statusCard } from '../../server/display.js';
|
|
10
12
|
const USAGE = `Usage: ai task status <N | #N | TASK-id>
|
|
11
13
|
|
|
12
14
|
Prints an aggregated "health check" view for a task: header, metadata,
|
|
@@ -267,6 +269,9 @@ function renderStatus(model) {
|
|
|
267
269
|
if (model.title)
|
|
268
270
|
lines.push(model.title);
|
|
269
271
|
lines.push('', 'Metadata', ...renderPairs(model.metadata));
|
|
272
|
+
if (model.workflowWarnings.length > 0) {
|
|
273
|
+
lines.push('', `Workflow Warnings (${model.workflowWarnings.length} open)`, ...formatWorkflowWarningSummary(model.workflowWarnings).map((line) => ` ${line}`));
|
|
274
|
+
}
|
|
270
275
|
lines.push('', `Artifacts (${model.artifacts.count})`);
|
|
271
276
|
if (model.artifacts.groups.length === 0) {
|
|
272
277
|
lines.push(' (none)');
|
|
@@ -302,6 +307,59 @@ function renderStatus(model) {
|
|
|
302
307
|
]));
|
|
303
308
|
return lines;
|
|
304
309
|
}
|
|
310
|
+
function modelTone(model) {
|
|
311
|
+
if (model.workflow.state === 'in-progress')
|
|
312
|
+
return model.workflow.stale === 'yes' ? 'warning' : 'running';
|
|
313
|
+
if (model.metadata.some(([key, value]) => key === 'status' && value === 'completed'))
|
|
314
|
+
return 'success';
|
|
315
|
+
if (model.metadata.some(([key, value]) => key === 'status' && (value === 'blocked' || value === 'cancelled'))) {
|
|
316
|
+
return 'danger';
|
|
317
|
+
}
|
|
318
|
+
return 'info';
|
|
319
|
+
}
|
|
320
|
+
function buildFromResolved(input) {
|
|
321
|
+
const content = fs.readFileSync(input.taskMdPath, 'utf8');
|
|
322
|
+
const fm = parseTaskFrontmatter(content);
|
|
323
|
+
const run = input.run ?? makeRunner(input.repoRoot);
|
|
324
|
+
const artifacts = enumerateArtifacts(input.taskDir);
|
|
325
|
+
const workflow = collectWorkflow(content, input.now);
|
|
326
|
+
return {
|
|
327
|
+
taskId: input.taskId,
|
|
328
|
+
shortId: input.shortId ?? loadShortIdByTaskId(input.repoRoot).get(input.taskId) ?? DASH,
|
|
329
|
+
title: extractTitle(content),
|
|
330
|
+
metadata: collectMetadata(fm),
|
|
331
|
+
workflowWarnings: getOpenWorkflowWarnings(content),
|
|
332
|
+
artifacts: { count: artifacts.length, groups: groupArtifacts(artifacts) },
|
|
333
|
+
workflow,
|
|
334
|
+
runtime: collectRuntime(input.taskDir, workflow, run),
|
|
335
|
+
git: collectGit(fm.branch ?? '', run)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function buildStatusModel(refOrInput, options = {}) {
|
|
339
|
+
if (typeof refOrInput !== 'string') {
|
|
340
|
+
return buildFromResolved(refOrInput);
|
|
341
|
+
}
|
|
342
|
+
const resolved = resolveTaskRef(refOrInput);
|
|
343
|
+
if (!resolved.ok) {
|
|
344
|
+
throw new Error(resolved.message);
|
|
345
|
+
}
|
|
346
|
+
return buildFromResolved({
|
|
347
|
+
taskId: resolved.taskId,
|
|
348
|
+
taskDir: resolved.taskDir,
|
|
349
|
+
taskMdPath: resolved.taskMdPath,
|
|
350
|
+
repoRoot: resolved.repoRoot,
|
|
351
|
+
run: options.run,
|
|
352
|
+
now: options.now
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
function statusModelToDisplay(model) {
|
|
356
|
+
return statusCard(`Task ${model.taskId} (${model.shortId})`, modelTone(model), [
|
|
357
|
+
['workflow', model.workflow.state],
|
|
358
|
+
['step', model.workflow.step],
|
|
359
|
+
['runtime', model.runtime.status],
|
|
360
|
+
['git', model.git.uncommitted]
|
|
361
|
+
], renderStatus(model).join('\n'));
|
|
362
|
+
}
|
|
305
363
|
function status(args = []) {
|
|
306
364
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
307
365
|
process.stdout.write(USAGE);
|
|
@@ -309,30 +367,18 @@ function status(args = []) {
|
|
|
309
367
|
process.exitCode = 1;
|
|
310
368
|
return;
|
|
311
369
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
370
|
+
let model;
|
|
371
|
+
try {
|
|
372
|
+
model = buildStatusModel(args[0]);
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
process.stderr.write(`ai task status: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
315
376
|
process.exitCode = 1;
|
|
316
377
|
return;
|
|
317
378
|
}
|
|
318
|
-
const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
319
|
-
const fm = parseTaskFrontmatter(content);
|
|
320
|
-
const run = makeRunner(resolved.repoRoot);
|
|
321
|
-
const artifacts = enumerateArtifacts(resolved.taskDir);
|
|
322
|
-
const workflow = collectWorkflow(content);
|
|
323
|
-
const model = {
|
|
324
|
-
taskId: resolved.taskId,
|
|
325
|
-
shortId: loadShortIdByTaskId(resolved.repoRoot).get(resolved.taskId) ?? DASH,
|
|
326
|
-
title: extractTitle(content),
|
|
327
|
-
metadata: collectMetadata(fm),
|
|
328
|
-
artifacts: { count: artifacts.length, groups: groupArtifacts(artifacts) },
|
|
329
|
-
workflow,
|
|
330
|
-
runtime: collectRuntime(resolved.taskDir, workflow, run),
|
|
331
|
-
git: collectGit(fm.branch ?? '', run)
|
|
332
|
-
};
|
|
333
379
|
for (const line of renderStatus(model)) {
|
|
334
380
|
process.stdout.write(`${line}\n`);
|
|
335
381
|
}
|
|
336
382
|
}
|
|
337
|
-
export { status, makeRunner, collectMetadata, groupArtifacts, collectGit, collectWorkflow, collectRuntime, renderStatus, METADATA_KEYS };
|
|
383
|
+
export { status, makeRunner, buildStatusModel, collectMetadata, groupArtifacts, collectGit, collectWorkflow, collectRuntime, renderStatus, statusModelToDisplay, METADATA_KEYS };
|
|
338
384
|
//# sourceMappingURL=status.js.map
|