evolcore 0.0.1 → 0.0.2
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/CHANGELOG.md +813 -0
- package/LICENSE +21 -0
- package/MIGRATION-0.5.0.md +378 -0
- package/README.md +318 -14
- package/ROLE_ACCESS_CONTROL.md +174 -0
- package/assets/.env.template +4 -0
- package/assets/brand/evolcore/README.md +19 -0
- package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
- package/assets/brand/evolcore/evolcore-app-icon.svg +13 -0
- package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
- package/assets/brand/evolcore/evolcore-brand-board.svg +126 -0
- package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.svg +14 -0
- package/assets/brand/evolcore/evolcore-logo.png +0 -0
- package/assets/brand/evolcore/evolcore-logo.svg +14 -0
- package/assets/brand/evolcore/evolcore-mark.png +0 -0
- package/assets/brand/evolcore/evolcore-mark.svg +10 -0
- package/bin/ec-safe-output.js +161 -0
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent.js +163 -0
- package/dist/agents/claude-runner.js +2385 -0
- package/dist/agents/codex-app-server-client.js +448 -0
- package/dist/agents/codex-runner.js +2639 -0
- package/dist/agents/gemini-runner.js +666 -0
- package/dist/agents/runner-types.js +75 -0
- package/dist/aun/aid/agentmd.js +216 -0
- package/dist/aun/aid/client.js +132 -0
- package/dist/aun/aid/control-aid.js +91 -0
- package/dist/aun/aid/identity.js +518 -0
- package/dist/aun/aid/index.js +4 -0
- package/dist/aun/aid/store.js +74 -0
- package/dist/aun/aid/types.js +1 -0
- package/dist/aun/aid/validation.js +21 -0
- package/dist/aun/group-identity.js +10 -0
- package/dist/aun/msg/group-index.js +6 -0
- package/dist/aun/msg/group.js +1231 -0
- package/dist/aun/msg/history.js +123 -0
- package/dist/aun/msg/index.js +5 -0
- package/dist/aun/msg/p2p.js +393 -0
- package/dist/aun/msg/payload-type.js +27 -0
- package/dist/aun/msg/upload.js +137 -0
- package/dist/aun/outbox.js +160 -0
- package/dist/aun/rpc/caller.js +42 -0
- package/dist/aun/rpc/connection.js +25 -0
- package/dist/aun/rpc/index.js +2 -0
- package/dist/aun/service-proxy.js +225 -0
- package/dist/aun/storage/download.js +29 -0
- package/dist/aun/storage/index.js +3 -0
- package/dist/aun/storage/manage.js +10 -0
- package/dist/aun/storage/upload.js +68 -0
- package/dist/channels/aun.js +4147 -0
- package/dist/channels/daemon.js +422 -0
- package/dist/channels/dingtalk.js +649 -0
- package/dist/channels/feishu.js +1789 -0
- package/dist/channels/qqbot.js +409 -0
- package/dist/channels/wechat.js +817 -0
- package/dist/channels/wecom.js +565 -0
- package/dist/cli/agent-command.js +641 -0
- package/dist/cli/agent.js +1059 -0
- package/dist/cli/aun-commands.js +1948 -0
- package/dist/cli/bench.js +1228 -0
- package/dist/cli/cli-argv.js +66 -0
- package/dist/cli/code-stats.js +329 -0
- package/dist/cli/command-log.js +82 -0
- package/dist/cli/config-selector.js +69 -0
- package/dist/cli/config.js +261 -0
- package/dist/cli/ctl-command.js +62 -0
- package/dist/cli/daemon-commands.js +2887 -0
- package/dist/cli/fs-command.js +1447 -0
- package/dist/cli/handoff-command.js +302 -0
- package/dist/cli/help.js +31 -0
- package/dist/cli/index.js +358 -0
- package/dist/cli/init-channel.js +1377 -0
- package/dist/cli/init.js +553 -0
- package/dist/cli/link-rules.js +240 -0
- package/dist/cli/model.js +590 -0
- package/dist/cli/net-check.js +723 -0
- package/dist/cli/queue-command.js +126 -0
- package/dist/cli/response.js +345 -0
- package/dist/cli/restart-monitor.js +456 -0
- package/dist/cli/stats.js +607 -0
- package/dist/cli/task-context.js +80 -0
- package/dist/cli/trigger-command.js +505 -0
- package/dist/cli/version.js +88 -0
- package/dist/cli/watch-logs.js +33 -0
- package/dist/cli/watch-msg.js +673 -0
- package/dist/config/boot-log.js +266 -0
- package/dist/config/builtin-role-templates.js +30 -0
- package/dist/config/builtin-roles.js +85 -0
- package/dist/config/config-batch-get.js +11 -0
- package/dist/config/config-field-policy.js +261 -0
- package/dist/config/config-manager.js +916 -0
- package/dist/config/config-operation-service.js +384 -0
- package/dist/config/contact-book.js +371 -0
- package/dist/config/gateway-config.js +858 -0
- package/dist/config/lifecycle.js +17 -0
- package/dist/config/mention-mode.js +27 -0
- package/dist/config/merge.js +161 -0
- package/dist/config/owner-policy.js +4 -0
- package/dist/config/peer-role-resolver.js +139 -0
- package/dist/config/resolved-config-op.js +483 -0
- package/dist/config/role-config-v4-startup.js +32 -0
- package/dist/config/role-config-v5-startup.js +27 -0
- package/dist/config/role-schema.js +105 -0
- package/dist/config/role-service.js +159 -0
- package/dist/config/role-store.js +204 -0
- package/dist/config/roles.js +55 -0
- package/dist/config/schema-registry.js +154 -0
- package/dist/config/snapshot.js +598 -0
- package/dist/config-store.js +503 -0
- package/dist/core/auth/agent-delegation.js +111 -0
- package/dist/core/auth/auth-gateway.js +166 -0
- package/dist/core/auth/authenticated-actor.js +6 -0
- package/dist/core/auth/authorization-audit.js +110 -0
- package/dist/core/auth/operation-authorizer.js +718 -0
- package/dist/core/auth/operation-catalog.js +675 -0
- package/dist/core/baseagent-loader.js +54 -0
- package/dist/core/bootstrap-service.js +175 -0
- package/dist/core/capability/capability-manager.js +316 -0
- package/dist/core/capability/providers/claude-capability-provider.js +176 -0
- package/dist/core/capability/providers/codex-capability-provider.js +148 -0
- package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
- package/dist/core/capability/types.js +27 -0
- package/dist/core/causation/audit.js +103 -0
- package/dist/core/causation/aun-association.js +111 -0
- package/dist/core/causation/context.js +93 -0
- package/dist/core/causation/index.js +4 -0
- package/dist/core/causation/types.js +2 -0
- package/dist/core/channel-loader.js +277 -0
- package/dist/core/command/agent-control.js +616 -0
- package/dist/core/command/cli-intent-parser.js +225 -0
- package/dist/core/command/command-handler.js +1638 -0
- package/dist/core/command/menu-handler.js +3354 -0
- package/dist/core/command/menu-protocol.js +247 -0
- package/dist/core/command/role-menu.js +1524 -0
- package/dist/core/command/slash-gate.js +148 -0
- package/dist/core/command/slash-handler.js +3056 -0
- package/dist/core/daemon-file-cache.js +216 -0
- package/dist/core/event-bus.js +32 -0
- package/dist/core/event-catalog.js +740 -0
- package/dist/core/evolagent-registry.js +546 -0
- package/dist/core/evolagent.js +331 -0
- package/dist/core/handoff/dispatcher.js +229 -0
- package/dist/core/handoff/mutex.js +46 -0
- package/dist/core/handoff/runtime.js +324 -0
- package/dist/core/handoff/store.js +537 -0
- package/dist/core/handoff/types.js +12 -0
- package/dist/core/inference/text-inference.js +173 -0
- package/dist/core/interaction-registration.js +10 -0
- package/dist/core/interaction-router.js +278 -0
- package/dist/core/message/create-status.js +67 -0
- package/dist/core/message/im-renderer.js +657 -0
- package/dist/core/message/items-formatter.js +76 -0
- package/dist/core/message/logical-queue-bridge.js +123 -0
- package/dist/core/message/message-bridge.js +803 -0
- package/dist/core/message/message-cache.js +56 -0
- package/dist/core/message/message-log.js +339 -0
- package/dist/core/message/message-processor.js +4 -0
- package/dist/core/message/message-queue.js +1436 -0
- package/dist/core/message/message-utils.js +70 -0
- package/dist/core/message/peer-mode.js +105 -0
- package/dist/core/message/pending-hints.js +232 -0
- package/dist/core/message/response-depth.js +33 -0
- package/dist/core/message/response-engine.js +3776 -0
- package/dist/core/message/response-snapshot.js +83 -0
- package/dist/core/message/stream-debouncer.js +130 -0
- package/dist/core/message/stream-idle-monitor.js +124 -0
- package/dist/core/model/config-scope.js +162 -0
- package/dist/core/model/field-scope.js +78 -0
- package/dist/core/model/model-catalog.js +227 -0
- package/dist/core/model/model-diagnostics.js +182 -0
- package/dist/core/model/model-permission.js +90 -0
- package/dist/core/permission/approval-gateway.js +1017 -0
- package/dist/core/permission/ec-command-parser.js +347 -0
- package/dist/core/permission/execution-sandbox.js +16 -0
- package/dist/core/permission/index.js +6 -0
- package/dist/core/permission/mode.js +24 -0
- package/dist/core/permission/sandbox-runtime.js +265 -0
- package/dist/core/permission/tool-policy.js +987 -0
- package/dist/core/permission/unix-socket-policy.js +99 -0
- package/dist/core/protected-paths.js +330 -0
- package/dist/core/relation/peer-identity.js +222 -0
- package/dist/core/relation/peer-key.js +1 -0
- package/dist/core/role/runtime-policy.js +141 -0
- package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
- package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
- package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
- package/dist/core/session/session-file-adapter.js +7 -0
- package/dist/core/session/session-file-health.js +45 -0
- package/dist/core/session/session-fs-store.js +232 -0
- package/dist/core/session/session-key.js +24 -0
- package/dist/core/session/session-manager.js +1587 -0
- package/dist/core/session/session-mapper.js +100 -0
- package/dist/core/session/session-renew.js +314 -0
- package/dist/core/session/session-title.js +128 -0
- package/dist/core/session/session-turn-coordinator.js +205 -0
- package/dist/core/session/session-turns.js +67 -0
- package/dist/core/system-channels.js +29 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/group-rules-sync.js +345 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/kit-renderer.js +359 -0
- package/dist/eck/manifest-engine.js +446 -0
- package/dist/eck/message-renderer.js +199 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +2870 -4
- package/dist/ipc.js +748 -0
- package/dist/paths.js +262 -0
- package/dist/product.js +18 -0
- package/dist/response-system/context-builder.js +71 -0
- package/dist/response-system/coordinator.js +117 -0
- package/dist/response-system/decision-executor.js +86 -0
- package/dist/response-system/engines/v1/index.js +21 -0
- package/dist/response-system/engines/v1/interactive-flow.js +27 -0
- package/dist/response-system/engines/v1/proactive-flow.js +137 -0
- package/dist/response-system/engines/v1/types.js +1 -0
- package/dist/response-system/extensions.js +41 -0
- package/dist/response-system/index.js +6 -0
- package/dist/response-system/modes/index.js +7 -0
- package/dist/response-system/modes/single-session/index.js +72 -0
- package/dist/response-system/queues/fifo-queue.js +44 -0
- package/dist/response-system/queues/index.js +6 -0
- package/dist/response-system/queues/lifo-queue.js +42 -0
- package/dist/response-system/queues/priority-queue.js +63 -0
- package/dist/response-system/registry.js +97 -0
- package/dist/response-system/resolver.js +37 -0
- package/dist/response-system/selector.js +23 -0
- package/dist/response-system/types.js +7 -0
- package/dist/stats/billing.js +151 -0
- package/dist/stats/budget.js +93 -0
- package/dist/stats/db.js +403 -0
- package/dist/stats/eck-vars.js +89 -0
- package/dist/stats/index.js +11 -0
- package/dist/stats/normalizer.js +80 -0
- package/dist/stats/price-resolver.js +138 -0
- package/dist/stats/query.js +763 -0
- package/dist/stats/role-budget.js +168 -0
- package/dist/stats/writer.js +151 -0
- package/dist/trigger/anomaly-store.js +258 -0
- package/dist/trigger/audit.js +152 -0
- package/dist/trigger/event-source.js +119 -0
- package/dist/trigger/feedback.js +685 -0
- package/dist/trigger/history.js +290 -0
- package/dist/trigger/manager.js +291 -0
- package/dist/trigger/parser.js +520 -0
- package/dist/trigger/patch.js +148 -0
- package/dist/trigger/scheduler.js +1600 -0
- package/dist/trigger/script-executor.js +155 -0
- package/dist/trigger/state.js +145 -0
- package/dist/trigger/types.js +1 -0
- package/dist/trigger/validation.js +621 -0
- package/dist/types.js +12 -0
- package/dist/utils/aid-bind.js +299 -0
- package/dist/utils/atomic-write.js +95 -0
- package/dist/utils/avatar-upload.js +123 -0
- package/dist/utils/cross-platform.js +297 -0
- package/dist/utils/ecweb-utils.js +73 -0
- package/dist/utils/error-dict.json +153 -0
- package/dist/utils/error-utils.js +349 -0
- package/dist/utils/instance-registry.js +437 -0
- package/dist/utils/locale.js +21 -0
- package/dist/utils/log-writer.js +224 -0
- package/dist/utils/logger.js +89 -0
- package/dist/utils/markdown-to-plain-text.js +20 -0
- package/dist/utils/media-cache.js +271 -0
- package/dist/utils/model-prices.jsonl +17 -0
- package/dist/utils/npm-ops.js +210 -0
- package/dist/utils/process-introspect.js +133 -0
- package/dist/utils/process-tree-stats.js +271 -0
- package/dist/utils/project-path.js +74 -0
- package/dist/utils/stats.js +410 -0
- package/dist/utils/tool-summary.js +284 -0
- package/dist/utils/welcome.js +268 -0
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +65 -0
- package/kits/docs/aun/CHEATSHEET.md +19 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/aun.md +65 -0
- package/kits/docs/channels/feishu.md +56 -0
- package/kits/docs/context-assembly.md +366 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolcore/INDEX.md +66 -0
- package/kits/docs/evolcore/agent.md +69 -0
- package/kits/docs/evolcore/aid.md +49 -0
- package/kits/docs/evolcore/config.md +149 -0
- package/kits/docs/evolcore/ctl.md +46 -0
- package/kits/docs/evolcore/event.md +216 -0
- package/kits/docs/evolcore/fs-architecture.md +1215 -0
- package/kits/docs/evolcore/fs.md +101 -0
- package/kits/docs/evolcore/group-fs.md +17 -0
- package/kits/docs/evolcore/group-rules.md +226 -0
- package/kits/docs/evolcore/group.md +141 -0
- package/kits/docs/evolcore/model.md +47 -0
- package/kits/docs/evolcore/msg.md +130 -0
- package/kits/docs/evolcore/response.md +80 -0
- package/kits/docs/evolcore/rpc.md +35 -0
- package/kits/docs/evolcore/self-summary.md +29 -0
- package/kits/docs/evolcore/stats.md +70 -0
- package/kits/docs/evolcore/storage.md +49 -0
- package/kits/docs/evolcore/trigger.md +524 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +23 -0
- package/kits/docs/identity/identity-tools.md +26 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/docs/prompt-loading-architecture.md +266 -0
- package/kits/docs/venues/aun-group.md +45 -0
- package/kits/docs/venues/aun-private.md +10 -0
- package/kits/docs/venues/client-desktop.md +10 -0
- package/kits/docs/venues/client-mobile.md +10 -0
- package/kits/docs/venues/feishu-group.md +13 -0
- package/kits/docs/venues/feishu-private.md +9 -0
- package/kits/docs/venues/group.md +25 -0
- package/kits/docs/venues/private.md +10 -0
- package/kits/eck_manifest.auxiliary.json +43 -0
- package/kits/eck_manifest.json +191 -0
- package/kits/eck_message_manifest.json +63 -0
- package/kits/migrations/README-role-config-v4.md +32 -0
- package/kits/migrations/migrate-role-config-v4.mjs +623 -0
- package/kits/migrations/migrate-role-config-v5.mjs +346 -0
- package/kits/migrations/rename-config-file.mjs +99 -0
- package/kits/rules/01-overview.md +142 -0
- package/kits/rules/02-navigation.md +76 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +59 -0
- package/kits/rules/05-venue.md +44 -0
- package/kits/rules/06-channel.md +59 -0
- package/kits/schemas/_meta.json +29 -0
- package/kits/schemas/agent-config.schema.1.json +177 -0
- package/kits/schemas/agent-config.schema.2.json +239 -0
- package/kits/schemas/agent-config.schema.3.json +119 -0
- package/kits/schemas/agent-config.schema.4.json +208 -0
- package/kits/schemas/agent-config.schema.5.json +326 -0
- package/kits/schemas/contact-book.schema.1.json +36 -0
- package/kits/schemas/daemon.schema.1.json +90 -0
- package/kits/schemas/defaults.schema.1.json +81 -0
- package/kits/schemas/menu-exec-schema-commands.md +208 -0
- package/kits/schemas/migrations/README.md +28 -0
- package/kits/schemas/relation-config.schema.1.json +158 -0
- package/kits/schemas/relation-config.schema.2.json +73 -0
- package/kits/schemas/relation-config.schema.3.json +50 -0
- package/kits/schemas/relation-config.schema.4.json +47 -0
- package/kits/schemas/role-config.schema.1.json +200 -0
- package/kits/schemas/role-registry.schema.1.json +35 -0
- package/kits/schemas/single-session.schema.1.json +31 -0
- package/kits/templates/bootstrap-welcome.md +15 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
- package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
- package/kits/templates/message-fragments/inject-default.md +2 -0
- package/kits/templates/message-fragments/item.md +2 -0
- package/kits/templates/roles/admin.json +8 -0
- package/kits/templates/roles/member.json +40 -0
- package/kits/templates/roles/owner.json +8 -0
- package/kits/templates/roles/visitor.json +39 -0
- package/kits/templates/system-fragments/baseagent.md +14 -0
- package/kits/templates/system-fragments/bootstrap.md +16 -0
- package/kits/templates/system-fragments/channel.md +48 -0
- package/kits/templates/system-fragments/commands.md +26 -0
- package/kits/templates/system-fragments/identity.md +11 -0
- package/kits/templates/system-fragments/relation.md +19 -0
- package/kits/templates/system-fragments/session.md +53 -0
- package/kits/templates/system-fragments/venue.md +31 -0
- package/package.json +50 -15
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { resolveAnthropicConfig, resolveOpenaiConfig, } from '../../agents/baseagent.js';
|
|
3
|
+
const MODEL_LIST_TTL_MS = 5 * 60 * 1000;
|
|
4
|
+
const MODEL_LIST_MAX_PAGES = 20;
|
|
5
|
+
const anthropicModelListCache = new Map();
|
|
6
|
+
function apiEndpoint(baseUrl, defaultBaseUrl, resource) {
|
|
7
|
+
const base = (baseUrl || defaultBaseUrl).replace(/\/+$/, '');
|
|
8
|
+
return base.endsWith('/v1') ? `${base}/${resource}` : `${base}/v1/${resource}`;
|
|
9
|
+
}
|
|
10
|
+
async function parseError(response) {
|
|
11
|
+
let raw = '';
|
|
12
|
+
try {
|
|
13
|
+
raw = await response.text();
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
let detail = raw;
|
|
17
|
+
try {
|
|
18
|
+
const body = JSON.parse(raw);
|
|
19
|
+
detail = body?.error?.message || body?.message || body?.detail || raw;
|
|
20
|
+
}
|
|
21
|
+
catch { }
|
|
22
|
+
const suffix = detail ? `: ${String(detail).slice(0, 500)}` : '';
|
|
23
|
+
return new Error(`Text inference request failed with HTTP ${response.status}${suffix}`);
|
|
24
|
+
}
|
|
25
|
+
export class AnthropicTextInferenceProvider {
|
|
26
|
+
config;
|
|
27
|
+
baseagent = 'claude';
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
}
|
|
31
|
+
async listModels(signal) {
|
|
32
|
+
const cacheKey = this.modelListCacheKey();
|
|
33
|
+
const cached = anthropicModelListCache.get(cacheKey);
|
|
34
|
+
if (cached && Date.now() - cached.fetchedAt < MODEL_LIST_TTL_MS)
|
|
35
|
+
return [...cached.models];
|
|
36
|
+
const models = [];
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
let afterId;
|
|
39
|
+
for (let page = 0; page < MODEL_LIST_MAX_PAGES; page++) {
|
|
40
|
+
const endpoint = new URL(apiEndpoint(this.config.baseUrl, 'https://api.anthropic.com', 'models'));
|
|
41
|
+
endpoint.searchParams.set('limit', '100');
|
|
42
|
+
if (afterId)
|
|
43
|
+
endpoint.searchParams.set('after_id', afterId);
|
|
44
|
+
const response = await fetch(endpoint, {
|
|
45
|
+
method: 'GET',
|
|
46
|
+
signal,
|
|
47
|
+
headers: this.headers(),
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok)
|
|
50
|
+
throw await parseError(response);
|
|
51
|
+
const body = await response.json();
|
|
52
|
+
const entries = Array.isArray(body.data)
|
|
53
|
+
? body.data
|
|
54
|
+
: (Array.isArray(body.models) ? body.models : []);
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
const id = typeof entry === 'string'
|
|
57
|
+
? entry
|
|
58
|
+
: entry?.id || entry?.name || entry?.model;
|
|
59
|
+
if (typeof id !== 'string' || !id.trim() || seen.has(id))
|
|
60
|
+
continue;
|
|
61
|
+
seen.add(id);
|
|
62
|
+
models.push(id);
|
|
63
|
+
}
|
|
64
|
+
if (body.has_more !== true)
|
|
65
|
+
break;
|
|
66
|
+
const next = typeof body.last_id === 'string' && body.last_id ? body.last_id : undefined;
|
|
67
|
+
if (!next || next === afterId)
|
|
68
|
+
throw new Error('Claude model list pagination returned no next cursor');
|
|
69
|
+
afterId = next;
|
|
70
|
+
}
|
|
71
|
+
if (models.length === 0)
|
|
72
|
+
throw new Error('Claude model list API returned no models');
|
|
73
|
+
anthropicModelListCache.set(cacheKey, { models, fetchedAt: Date.now() });
|
|
74
|
+
return [...models];
|
|
75
|
+
}
|
|
76
|
+
async completeText(request) {
|
|
77
|
+
const response = await fetch(apiEndpoint(this.config.baseUrl, 'https://api.anthropic.com', 'messages'), {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
signal: request.signal,
|
|
80
|
+
headers: this.headers(),
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
model: request.model,
|
|
83
|
+
max_tokens: 256,
|
|
84
|
+
system: request.system,
|
|
85
|
+
messages: [{ role: 'user', content: request.input }],
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok)
|
|
89
|
+
throw await parseError(response);
|
|
90
|
+
const body = await response.json();
|
|
91
|
+
const text = Array.isArray(body.content)
|
|
92
|
+
? body.content
|
|
93
|
+
.filter((item) => item?.type === 'text' && typeof item.text === 'string')
|
|
94
|
+
.map((item) => item.text)
|
|
95
|
+
.join('')
|
|
96
|
+
: '';
|
|
97
|
+
if (!text)
|
|
98
|
+
throw new Error('Text inference response contained no text');
|
|
99
|
+
return text;
|
|
100
|
+
}
|
|
101
|
+
headers() {
|
|
102
|
+
return {
|
|
103
|
+
'Content-Type': 'application/json',
|
|
104
|
+
'anthropic-version': '2023-06-01',
|
|
105
|
+
'x-api-key': this.config.apiKey,
|
|
106
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
modelListCacheKey() {
|
|
110
|
+
const endpoint = apiEndpoint(this.config.baseUrl, 'https://api.anthropic.com', 'models');
|
|
111
|
+
const credential = createHash('sha256').update(this.config.apiKey).digest('hex');
|
|
112
|
+
return `${endpoint}\0${credential}`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export function _resetTextInferenceModelCacheForTests() {
|
|
116
|
+
anthropicModelListCache.clear();
|
|
117
|
+
}
|
|
118
|
+
export class OpenAITextInferenceProvider {
|
|
119
|
+
config;
|
|
120
|
+
baseagent = 'codex';
|
|
121
|
+
constructor(config) {
|
|
122
|
+
this.config = config;
|
|
123
|
+
}
|
|
124
|
+
async completeText(request) {
|
|
125
|
+
const response = await fetch(apiEndpoint(this.config.baseUrl, 'https://api.openai.com', 'responses'), {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
signal: request.signal,
|
|
128
|
+
headers: {
|
|
129
|
+
'Content-Type': 'application/json',
|
|
130
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
131
|
+
},
|
|
132
|
+
body: JSON.stringify({
|
|
133
|
+
model: request.model,
|
|
134
|
+
instructions: request.system,
|
|
135
|
+
input: request.input,
|
|
136
|
+
store: false,
|
|
137
|
+
tool_choice: 'none',
|
|
138
|
+
max_output_tokens: 256,
|
|
139
|
+
...(request.effort ? { reasoning: { effort: request.effort } } : {}),
|
|
140
|
+
}),
|
|
141
|
+
});
|
|
142
|
+
if (!response.ok)
|
|
143
|
+
throw await parseError(response);
|
|
144
|
+
const body = await response.json();
|
|
145
|
+
const direct = typeof body.output_text === 'string' ? body.output_text : '';
|
|
146
|
+
const text = direct || (Array.isArray(body.output)
|
|
147
|
+
? body.output.flatMap((item) => Array.isArray(item?.content) ? item.content : [])
|
|
148
|
+
.filter((item) => item?.type === 'output_text' && typeof item.text === 'string')
|
|
149
|
+
.map((item) => item.text)
|
|
150
|
+
.join('')
|
|
151
|
+
: '');
|
|
152
|
+
if (!text)
|
|
153
|
+
throw new Error('Text inference response contained no text');
|
|
154
|
+
return text;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export function createTextInferenceProvider(baseagent, config) {
|
|
158
|
+
if (baseagent === 'claude') {
|
|
159
|
+
const override = config.baseagents?.claude;
|
|
160
|
+
if (!override)
|
|
161
|
+
return undefined;
|
|
162
|
+
const synthetic = { agents: { claude: override } };
|
|
163
|
+
return new AnthropicTextInferenceProvider(resolveAnthropicConfig(synthetic, override));
|
|
164
|
+
}
|
|
165
|
+
if (baseagent === 'codex') {
|
|
166
|
+
const override = config.baseagents?.codex;
|
|
167
|
+
if (!override)
|
|
168
|
+
return undefined;
|
|
169
|
+
const synthetic = { agents: { codex: override } };
|
|
170
|
+
return new OpenAITextInferenceProvider(resolveOpenaiConfig(synthetic, override));
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Bind an adapter response to its runtime-owned channel identity domain. */
|
|
2
|
+
export function registerAdapterInteractions(adapter, router) {
|
|
3
|
+
const channelKey = adapter.channelKey;
|
|
4
|
+
adapter.onInteraction?.(async (response) => {
|
|
5
|
+
return router.handleAsync({ ...response, channelKey });
|
|
6
|
+
});
|
|
7
|
+
adapter.onInteractionInvalidated?.((interactionId, reason) => {
|
|
8
|
+
return router.cancelFromChannel(interactionId, reason);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { logger } from '../utils/logger.js';
|
|
2
|
+
export class InteractionRouter {
|
|
3
|
+
handlers = new Map();
|
|
4
|
+
/** sessionId → 该会话当前待应答的交互数量,用于触发 wait 生命周期钩子 */
|
|
5
|
+
pendingBySession = new Map();
|
|
6
|
+
waitHooks;
|
|
7
|
+
setWaitHooks(hooks) {
|
|
8
|
+
this.waitHooks = hooks;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 在 register() 之前提前标记 session 为等待状态(适用于发卡片有异步延迟的场景)。
|
|
12
|
+
* 必须与 unmarkWaiting() 配对使用,或后续 register() 会接管计数。
|
|
13
|
+
*/
|
|
14
|
+
markWaiting(sessionId) {
|
|
15
|
+
this.incPending(sessionId);
|
|
16
|
+
}
|
|
17
|
+
/** 取消 markWaiting() 的占位(后续若有 register() 接管则不需调用此方法) */
|
|
18
|
+
unmarkWaiting(sessionId) {
|
|
19
|
+
this.decPending(sessionId);
|
|
20
|
+
}
|
|
21
|
+
/** 登记一个待应答交互;session 计数 0→1 时触发 onWaitStart */
|
|
22
|
+
incPending(sessionId) {
|
|
23
|
+
const next = (this.pendingBySession.get(sessionId) ?? 0) + 1;
|
|
24
|
+
this.pendingBySession.set(sessionId, next);
|
|
25
|
+
if (next === 1)
|
|
26
|
+
this.waitHooks?.onWaitStart(sessionId);
|
|
27
|
+
}
|
|
28
|
+
/** 注销一个待应答交互;session 计数 1→0 时触发 onWaitEnd */
|
|
29
|
+
decPending(sessionId) {
|
|
30
|
+
const cur = this.pendingBySession.get(sessionId) ?? 0;
|
|
31
|
+
if (cur <= 0)
|
|
32
|
+
return;
|
|
33
|
+
const next = cur - 1;
|
|
34
|
+
if (next === 0) {
|
|
35
|
+
this.pendingBySession.delete(sessionId);
|
|
36
|
+
this.waitHooks?.onWaitEnd(sessionId);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.pendingBySession.set(sessionId, next);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
register(id, sessionId, callback, opts) {
|
|
43
|
+
// 同 id 替换:槽位本就占用,计数不变,不触发 wait 钩子
|
|
44
|
+
const existing = this.handlers.get(id);
|
|
45
|
+
if (existing?.timer)
|
|
46
|
+
clearTimeout(existing.timer);
|
|
47
|
+
const isReplacement = !!existing;
|
|
48
|
+
const pending = {
|
|
49
|
+
callback,
|
|
50
|
+
sessionId,
|
|
51
|
+
initiatorId: opts?.initiatorId,
|
|
52
|
+
initiatorChannelKey: opts?.initiatorChannelKey,
|
|
53
|
+
fallbackCommand: opts?.fallbackCommand,
|
|
54
|
+
onCancel: opts?.onCancel,
|
|
55
|
+
onChannelCancel: opts?.onChannelCancel,
|
|
56
|
+
};
|
|
57
|
+
if (opts?.timeoutMs && opts.timeoutMs > 0) {
|
|
58
|
+
pending.timer = setTimeout(() => {
|
|
59
|
+
// A replacement may have reused the same id while this timer was
|
|
60
|
+
// waiting. Only expire the exact registration that created it.
|
|
61
|
+
if (this.handlers.get(id) !== pending)
|
|
62
|
+
return;
|
|
63
|
+
this.handlers.delete(id);
|
|
64
|
+
this.decPending(pending.sessionId);
|
|
65
|
+
const cancelReason = opts.timeoutCancelReason ?? 'timeout';
|
|
66
|
+
logger.debug(`[InteractionRouter] Timeout for interaction: ${id}`);
|
|
67
|
+
opts.onTimeout?.();
|
|
68
|
+
if (opts.onCancel) {
|
|
69
|
+
Promise.resolve(opts.onCancel(cancelReason)).catch(err => {
|
|
70
|
+
logger.error(`[InteractionRouter] Timeout cancellation error for ${id}:`, err);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}, opts.timeoutMs);
|
|
74
|
+
}
|
|
75
|
+
this.handlers.set(id, pending);
|
|
76
|
+
if (!isReplacement)
|
|
77
|
+
this.incPending(sessionId);
|
|
78
|
+
}
|
|
79
|
+
dispatch(response) {
|
|
80
|
+
const handler = this.handlers.get(response.id);
|
|
81
|
+
if (!handler)
|
|
82
|
+
return false;
|
|
83
|
+
// Initiator 校验(集中式 backstop):非发起者的操作直接丢弃,不消费 handler、不解除等待,
|
|
84
|
+
// 让真正的发起者仍可继续操作。身份只信渠道传入的已认证 operatorId(来自消息信封,非 payload 自报)。
|
|
85
|
+
// 渠道层若已自行校验(如飞书的 reject toast),此处不会重复命中(operatorId 已匹配)。
|
|
86
|
+
if (handler.initiatorId && response.operatorId !== handler.initiatorId) {
|
|
87
|
+
logger.info(`[InteractionRouter] rejected unauthenticated or non-initiator response: operator=${response.operatorId ?? '<missing>'} initiator=${handler.initiatorId} id=${response.id}`);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (handler.initiatorChannelKey && response.channelKey !== handler.initiatorChannelKey) {
|
|
91
|
+
logger.info(`[InteractionRouter] rejected response from wrong channel: channel=${response.channelKey ?? '<missing>'} expected=${handler.initiatorChannelKey} operator=${response.operatorId ?? '<missing>'} id=${response.id}`);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
// The handler must remain registered while the callback runs. Permission
|
|
95
|
+
// callbacks can discover that authorization was revoked and call
|
|
96
|
+
// router.cancel(reason), whose onCancel hook invalidates the rendered card.
|
|
97
|
+
// Removing the slot before callback execution loses that invalidator.
|
|
98
|
+
if (handler.inFlight) {
|
|
99
|
+
logger.info(`[InteractionRouter] ignored duplicate in-flight response: id=${response.id}`);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
handler.inFlight = true;
|
|
103
|
+
const settle = (accepted, rejectionReason = 'backend_rejected') => {
|
|
104
|
+
// The callback may already have resolved/cancelled this exact handler.
|
|
105
|
+
if (this.handlers.get(response.id) !== handler)
|
|
106
|
+
return accepted;
|
|
107
|
+
if (handler.timer)
|
|
108
|
+
clearTimeout(handler.timer);
|
|
109
|
+
this.handlers.delete(response.id);
|
|
110
|
+
this.decPending(handler.sessionId);
|
|
111
|
+
if (!accepted && handler.onCancel) {
|
|
112
|
+
Promise.resolve(handler.onCancel(rejectionReason)).catch(err => {
|
|
113
|
+
logger.error(`[InteractionRouter] Rejection cancellation error for ${response.id}:`, err);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return accepted;
|
|
117
|
+
};
|
|
118
|
+
const fail = (err) => {
|
|
119
|
+
logger.error(`[InteractionRouter] Callback error for ${response.id}:`, err);
|
|
120
|
+
return settle(false, 'callback_error');
|
|
121
|
+
};
|
|
122
|
+
try {
|
|
123
|
+
const result = response.channelKey === undefined
|
|
124
|
+
? handler.callback(response.action, response.values, response.operatorId)
|
|
125
|
+
: handler.callback(response.action, response.values, response.operatorId, response.channelKey);
|
|
126
|
+
if (result && typeof result.catch === 'function') {
|
|
127
|
+
return Promise.resolve(result).then(accepted => settle(accepted !== false), fail);
|
|
128
|
+
}
|
|
129
|
+
return settle(result !== false);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
return fail(err);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Synchronous compatibility entrypoint used by text-command fallbacks.
|
|
137
|
+
* Async callbacks are started once and report true here; channel adapters
|
|
138
|
+
* must use handleAsync() so their UI waits for the backend result.
|
|
139
|
+
*/
|
|
140
|
+
handle(response) {
|
|
141
|
+
const result = this.dispatch(response);
|
|
142
|
+
if (result && typeof result.then === 'function') {
|
|
143
|
+
void result;
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
/** Await the authoritative backend result before a channel resolves a card. */
|
|
149
|
+
async handleAsync(response) {
|
|
150
|
+
return await this.dispatch(response);
|
|
151
|
+
}
|
|
152
|
+
async cancelAll(sessionId, reason) {
|
|
153
|
+
const pending = [];
|
|
154
|
+
for (const [id, handler] of this.handlers.entries()) {
|
|
155
|
+
if (handler.sessionId === sessionId) {
|
|
156
|
+
if (handler.timer)
|
|
157
|
+
clearTimeout(handler.timer);
|
|
158
|
+
this.handlers.delete(id);
|
|
159
|
+
this.decPending(handler.sessionId);
|
|
160
|
+
if (reason && handler.onCancel) {
|
|
161
|
+
pending.push(Promise.resolve(handler.onCancel(reason)).then(() => undefined));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
await Promise.allSettled(pending);
|
|
166
|
+
}
|
|
167
|
+
async cancel(id, reason) {
|
|
168
|
+
const handler = this.handlers.get(id);
|
|
169
|
+
if (handler) {
|
|
170
|
+
if (handler.timer)
|
|
171
|
+
clearTimeout(handler.timer);
|
|
172
|
+
this.handlers.delete(id);
|
|
173
|
+
this.decPending(handler.sessionId);
|
|
174
|
+
if (reason && handler.onCancel)
|
|
175
|
+
await handler.onCancel(reason);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Cancel an interaction because the channel superseded its rendered card.
|
|
182
|
+
* Permission flows use onChannelCancel to settle their business promise;
|
|
183
|
+
* ordinary interactions fall back to their existing onCancel hook.
|
|
184
|
+
*/
|
|
185
|
+
async cancelFromChannel(id, reason) {
|
|
186
|
+
const handler = this.handlers.get(id);
|
|
187
|
+
if (!handler)
|
|
188
|
+
return false;
|
|
189
|
+
if (!handler.onChannelCancel)
|
|
190
|
+
return this.cancel(id, reason);
|
|
191
|
+
if (handler.inFlight)
|
|
192
|
+
return false;
|
|
193
|
+
handler.inFlight = true;
|
|
194
|
+
try {
|
|
195
|
+
const result = await handler.onChannelCancel(reason);
|
|
196
|
+
const accepted = result !== false;
|
|
197
|
+
if (this.handlers.get(id) === handler) {
|
|
198
|
+
handler.inFlight = false;
|
|
199
|
+
if (accepted)
|
|
200
|
+
await this.cancel(id, reason);
|
|
201
|
+
}
|
|
202
|
+
return accepted;
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
if (this.handlers.get(id) === handler)
|
|
206
|
+
handler.inFlight = false;
|
|
207
|
+
logger.error(`[InteractionRouter] Channel cancellation error for ${id}:`, err);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
getPending(sessionId) {
|
|
212
|
+
const ids = [];
|
|
213
|
+
for (const [id, handler] of this.handlers.entries()) {
|
|
214
|
+
if (handler.sessionId === sessionId)
|
|
215
|
+
ids.push(id);
|
|
216
|
+
}
|
|
217
|
+
return ids;
|
|
218
|
+
}
|
|
219
|
+
getInitiator(id) {
|
|
220
|
+
return this.handlers.get(id)?.initiatorId;
|
|
221
|
+
}
|
|
222
|
+
findPendingByCommand(sessionId, command) {
|
|
223
|
+
for (const [id, handler] of this.handlers.entries()) {
|
|
224
|
+
if (handler.sessionId === sessionId && handler.fallbackCommand === command) {
|
|
225
|
+
return id;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/** 把 CommandCard 渲染为文本提示,channel 不支持卡片时使用 */
|
|
232
|
+
export function renderCommandCardAsText(card) {
|
|
233
|
+
const lines = [card.title];
|
|
234
|
+
if (card.body)
|
|
235
|
+
lines.push(card.body);
|
|
236
|
+
lines.push('', '可用命令:');
|
|
237
|
+
for (const btn of card.buttons) {
|
|
238
|
+
const marker = btn.disabled ? '✓' : ' ';
|
|
239
|
+
lines.push(` ${marker} ${btn.command} ← ${btn.label}`);
|
|
240
|
+
}
|
|
241
|
+
return lines.join('\n');
|
|
242
|
+
}
|
|
243
|
+
/** 把 ActionInteraction 渲染为文本提示,channel 不支持卡片或卡片发送失败时使用 */
|
|
244
|
+
export function renderActionAsText(req) {
|
|
245
|
+
if (req.kind.kind !== 'action') {
|
|
246
|
+
throw new Error('[renderActionAsText] expected ActionInteraction, got ' + req.kind.kind);
|
|
247
|
+
}
|
|
248
|
+
const action = req.kind;
|
|
249
|
+
const fb = req.fallback;
|
|
250
|
+
const lines = [action.title];
|
|
251
|
+
if (action.body)
|
|
252
|
+
lines.push(action.body);
|
|
253
|
+
// checkers 多选:渲染选项列表
|
|
254
|
+
if (action.checkers?.length) {
|
|
255
|
+
lines.push('');
|
|
256
|
+
action.checkers.forEach((chk, idx) => {
|
|
257
|
+
const desc = chk.description ? ` — ${chk.description}` : '';
|
|
258
|
+
lines.push(` ${idx + 1}. ${chk.label}${desc}`);
|
|
259
|
+
});
|
|
260
|
+
lines.push('', '回复选项编号(多选用逗号分隔),或输入自定义内容');
|
|
261
|
+
return lines.join('\n');
|
|
262
|
+
}
|
|
263
|
+
if (!fb) {
|
|
264
|
+
return lines.join('\n');
|
|
265
|
+
}
|
|
266
|
+
lines.push('', '回复:');
|
|
267
|
+
for (const btn of action.buttons) {
|
|
268
|
+
const arg = fb.buttonArgMap?.[btn.key] ?? btn.key;
|
|
269
|
+
lines.push(` /${fb.command} ${arg} ← ${btn.label}`);
|
|
270
|
+
}
|
|
271
|
+
if (fb.acceptFreeText && fb.freeTextHint) {
|
|
272
|
+
lines.push(` ${fb.freeTextHint}`);
|
|
273
|
+
}
|
|
274
|
+
if (action.allowCustomInput) {
|
|
275
|
+
lines.push(` 或直接输入自定义内容`);
|
|
276
|
+
}
|
|
277
|
+
return lines.join('\n');
|
|
278
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
const FILE = 'create-status.json';
|
|
4
|
+
export function readCreateStatus(agentDir) {
|
|
5
|
+
try {
|
|
6
|
+
const raw = fs.readFileSync(path.join(agentDir, FILE), 'utf-8');
|
|
7
|
+
return JSON.parse(raw);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** 删除构建进度文件(agent 删除时清理)。非 purge 删除只移除 config.json,
|
|
14
|
+
* 故需显式清理本文件,避免目录残留陈旧进度。 */
|
|
15
|
+
export function removeCreateStatus(agentDir) {
|
|
16
|
+
try {
|
|
17
|
+
fs.rmSync(path.join(agentDir, FILE), { force: true });
|
|
18
|
+
}
|
|
19
|
+
catch { /* ignore */ }
|
|
20
|
+
}
|
|
21
|
+
/** 构建进度写入器。每次状态变更原子落盘(写临时文件 + rename)。 */
|
|
22
|
+
export class CreateStatusWriter {
|
|
23
|
+
agentDir;
|
|
24
|
+
status;
|
|
25
|
+
constructor(agentDir, aid) {
|
|
26
|
+
this.agentDir = agentDir;
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
this.status = { aid, status: 'in_progress', currentPhase: null, steps: [], error: null, startedAt: now, updatedAt: now };
|
|
29
|
+
fs.mkdirSync(agentDir, { recursive: true });
|
|
30
|
+
this.flush();
|
|
31
|
+
}
|
|
32
|
+
begin(phase) {
|
|
33
|
+
this.status.currentPhase = phase;
|
|
34
|
+
this.status.steps.push({ phase, state: 'in_progress', ts: Date.now() });
|
|
35
|
+
this.flush();
|
|
36
|
+
}
|
|
37
|
+
done(phase, detail) { this.mark(phase, 'done', detail); }
|
|
38
|
+
warn(phase, detail) { this.mark(phase, 'warn', detail); }
|
|
39
|
+
finishReady() { this.status.status = 'ready'; this.status.currentPhase = null; this.flush(); }
|
|
40
|
+
finishFailed(phase, error) {
|
|
41
|
+
this.mark(phase, 'failed', error);
|
|
42
|
+
this.status.status = 'failed';
|
|
43
|
+
this.status.error = error;
|
|
44
|
+
this.status.currentPhase = null;
|
|
45
|
+
this.flush();
|
|
46
|
+
}
|
|
47
|
+
mark(phase, state, detail) {
|
|
48
|
+
const step = [...this.status.steps].reverse().find(s => s.phase === phase);
|
|
49
|
+
if (step) {
|
|
50
|
+
step.state = state;
|
|
51
|
+
if (detail)
|
|
52
|
+
step.detail = detail;
|
|
53
|
+
step.ts = Date.now();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.status.steps.push({ phase, state, detail, ts: Date.now() });
|
|
57
|
+
}
|
|
58
|
+
this.flush();
|
|
59
|
+
}
|
|
60
|
+
flush() {
|
|
61
|
+
this.status.updatedAt = Date.now();
|
|
62
|
+
const file = path.join(this.agentDir, FILE);
|
|
63
|
+
const tmp = `${file}.tmp`;
|
|
64
|
+
fs.writeFileSync(tmp, JSON.stringify(this.status, null, 2));
|
|
65
|
+
fs.renameSync(tmp, file);
|
|
66
|
+
}
|
|
67
|
+
}
|