@the-open-engine/zeroshot 5.4.0
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 +539 -0
- package/LICENSE +21 -0
- package/README.md +508 -0
- package/cli/commands/inspect-render.js +135 -0
- package/cli/commands/inspect.js +294 -0
- package/cli/commands/providers.js +149 -0
- package/cli/index.js +5431 -0
- package/cli/lib/first-run.js +211 -0
- package/cli/lib/update-checker.js +281 -0
- package/cli/message-formatter-utils.js +75 -0
- package/cli/message-formatters-normal.js +275 -0
- package/cli/message-formatters-watch.js +185 -0
- package/cluster-templates/base-templates/debug-workflow.json +422 -0
- package/cluster-templates/base-templates/full-workflow.json +727 -0
- package/cluster-templates/base-templates/heavy-validation.json +272 -0
- package/cluster-templates/base-templates/quick-validation.json +285 -0
- package/cluster-templates/base-templates/single-worker.json +71 -0
- package/cluster-templates/base-templates/worker-validator.json +230 -0
- package/cluster-templates/conductor-bootstrap.json +130 -0
- package/docker/zeroshot-cluster/Dockerfile +153 -0
- package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.js +181 -0
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.js +151 -0
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
- package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
- package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/common.js +71 -0
- package/lib/agent-cli-provider/adapters/common.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.js +176 -0
- package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
- package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/index.js +95 -0
- package/lib/agent-cli-provider/adapters/index.js.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.js +228 -0
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
- package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-actions.js +140 -0
- package/lib/agent-cli-provider/contract-actions.js.map +1 -0
- package/lib/agent-cli-provider/contract-env.d.ts +11 -0
- package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-env.js +113 -0
- package/lib/agent-cli-provider/contract-env.js.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.js +52 -0
- package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
- package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
- package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-errors.js +71 -0
- package/lib/agent-cli-provider/contract-errors.js.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.js +102 -0
- package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.js +89 -0
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
- package/lib/agent-cli-provider/contract-options.d.ts +3 -0
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-options.js +140 -0
- package/lib/agent-cli-provider/contract-options.js.map +1 -0
- package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
- package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-parse.js +81 -0
- package/lib/agent-cli-provider/contract-parse.js.map +1 -0
- package/lib/agent-cli-provider/contract-support.d.ts +18 -0
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-support.js +154 -0
- package/lib/agent-cli-provider/contract-support.js.map +1 -0
- package/lib/agent-cli-provider/contract.d.ts +13 -0
- package/lib/agent-cli-provider/contract.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract.js +30 -0
- package/lib/agent-cli-provider/contract.js.map +1 -0
- package/lib/agent-cli-provider/env-safety.d.ts +11 -0
- package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
- package/lib/agent-cli-provider/env-safety.js +83 -0
- package/lib/agent-cli-provider/env-safety.js.map +1 -0
- package/lib/agent-cli-provider/errors.d.ts +5 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/errors.js +115 -0
- package/lib/agent-cli-provider/errors.js.map +1 -0
- package/lib/agent-cli-provider/executable.d.ts +3 -0
- package/lib/agent-cli-provider/executable.d.ts.map +1 -0
- package/lib/agent-cli-provider/executable.js +24 -0
- package/lib/agent-cli-provider/executable.js.map +1 -0
- package/lib/agent-cli-provider/index.d.ts +8 -0
- package/lib/agent-cli-provider/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/index.js +31 -0
- package/lib/agent-cli-provider/index.js.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.js +17 -0
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
- package/lib/agent-cli-provider/json.d.ts +16 -0
- package/lib/agent-cli-provider/json.d.ts.map +1 -0
- package/lib/agent-cli-provider/json.js +110 -0
- package/lib/agent-cli-provider/json.js.map +1 -0
- package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
- package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
- package/lib/agent-cli-provider/log-prefix.js +22 -0
- package/lib/agent-cli-provider/log-prefix.js.map +1 -0
- package/lib/agent-cli-provider/process-runner.d.ts +17 -0
- package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/process-runner.js +89 -0
- package/lib/agent-cli-provider/process-runner.js.map +1 -0
- package/lib/agent-cli-provider/redaction.d.ts +9 -0
- package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
- package/lib/agent-cli-provider/redaction.js +227 -0
- package/lib/agent-cli-provider/redaction.js.map +1 -0
- package/lib/agent-cli-provider/schema.d.ts +5 -0
- package/lib/agent-cli-provider/schema.d.ts.map +1 -0
- package/lib/agent-cli-provider/schema.js +136 -0
- package/lib/agent-cli-provider/schema.js.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
- package/lib/agent-cli-provider/types.d.ts +194 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -0
- package/lib/agent-cli-provider/types.js +12 -0
- package/lib/agent-cli-provider/types.js.map +1 -0
- package/lib/completion.js +174 -0
- package/lib/detached-startup.js +220 -0
- package/lib/docker-config.js +220 -0
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +55 -0
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-detection.js +59 -0
- package/lib/provider-names.js +57 -0
- package/lib/repo-settings.js +69 -0
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +542 -0
- package/lib/start-cluster.js +321 -0
- package/lib/stream-json-parser.js +67 -0
- package/package.json +162 -0
- package/scripts/fix-node-pty-permissions.js +75 -0
- package/scripts/record-demo.sh +279 -0
- package/scripts/setup-merge-queue.sh +170 -0
- package/scripts/test-install.sh +40 -0
- package/scripts/validate-templates.js +107 -0
- package/src/agent/agent-config.js +266 -0
- package/src/agent/agent-context-builder.js +189 -0
- package/src/agent/agent-context-sections.js +338 -0
- package/src/agent/agent-context-sources.js +147 -0
- package/src/agent/agent-hook-executor.js +721 -0
- package/src/agent/agent-input-injector.js +141 -0
- package/src/agent/agent-lifecycle.js +982 -0
- package/src/agent/agent-quality-gate-schema.js +93 -0
- package/src/agent/agent-quality-gates-context.js +51 -0
- package/src/agent/agent-stuck-detector.js +256 -0
- package/src/agent/agent-task-executor.js +2028 -0
- package/src/agent/agent-trigger-evaluator.js +67 -0
- package/src/agent/context-metrics.js +160 -0
- package/src/agent/context-pack-builder.js +367 -0
- package/src/agent/context-replay-policy.js +51 -0
- package/src/agent/guidance-queue.js +77 -0
- package/src/agent/output-extraction.js +367 -0
- package/src/agent/output-reformatter.js +175 -0
- package/src/agent/pr-verification.js +653 -0
- package/src/agent/rate-limit-backoff.js +82 -0
- package/src/agent/schema-utils.js +146 -0
- package/src/agent/validation-platform.js +35 -0
- package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
- package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
- package/src/agent-cli-provider/adapters/claude.ts +247 -0
- package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
- package/src/agent-cli-provider/adapters/codex.ts +217 -0
- package/src/agent-cli-provider/adapters/common.ts +124 -0
- package/src/agent-cli-provider/adapters/gemini.ts +243 -0
- package/src/agent-cli-provider/adapters/index.ts +126 -0
- package/src/agent-cli-provider/adapters/opencode.ts +286 -0
- package/src/agent-cli-provider/contract-actions.ts +150 -0
- package/src/agent-cli-provider/contract-env.ts +111 -0
- package/src/agent-cli-provider/contract-envelope.ts +110 -0
- package/src/agent-cli-provider/contract-errors.ts +66 -0
- package/src/agent-cli-provider/contract-fallback.ts +121 -0
- package/src/agent-cli-provider/contract-invoke.ts +104 -0
- package/src/agent-cli-provider/contract-options.ts +173 -0
- package/src/agent-cli-provider/contract-parse.ts +94 -0
- package/src/agent-cli-provider/contract-support.ts +167 -0
- package/src/agent-cli-provider/contract.ts +56 -0
- package/src/agent-cli-provider/env-safety.ts +82 -0
- package/src/agent-cli-provider/errors.ts +122 -0
- package/src/agent-cli-provider/executable.ts +24 -0
- package/src/agent-cli-provider/index.ts +83 -0
- package/src/agent-cli-provider/invoke-evidence.ts +18 -0
- package/src/agent-cli-provider/json.ts +114 -0
- package/src/agent-cli-provider/log-prefix.ts +20 -0
- package/src/agent-cli-provider/process-runner.ts +145 -0
- package/src/agent-cli-provider/redaction.ts +282 -0
- package/src/agent-cli-provider/schema.ts +115 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
- package/src/agent-cli-provider/types.ts +237 -0
- package/src/agent-wrapper.js +615 -0
- package/src/agents/git-pusher-template.js +705 -0
- package/src/attach/attach-client.js +438 -0
- package/src/attach/attach-server.js +543 -0
- package/src/attach/index.js +37 -0
- package/src/attach/protocol.js +220 -0
- package/src/attach/ring-buffer.js +121 -0
- package/src/attach/send-input.js +88 -0
- package/src/attach/socket-discovery.js +267 -0
- package/src/claude-task-runner.js +661 -0
- package/src/config-router.js +89 -0
- package/src/config-validator.js +2202 -0
- package/src/copy-worker.js +43 -0
- package/src/guidance-topics.js +10 -0
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +1734 -0
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +307 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +210 -0
- package/src/issue-providers/gitlab-provider.js +262 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +260 -0
- package/src/ledger.js +692 -0
- package/src/lib/gc.js +232 -0
- package/src/lib/safe-exec.js +88 -0
- package/src/logic-engine.js +201 -0
- package/src/message-buffer.js +81 -0
- package/src/message-bus-bridge.js +144 -0
- package/src/message-bus.js +256 -0
- package/src/name-generator.js +232 -0
- package/src/orchestrator.js +3924 -0
- package/src/preflight.js +712 -0
- package/src/process-metrics.js +608 -0
- package/src/providers/anthropic/index.js +3 -0
- package/src/providers/base-provider.js +355 -0
- package/src/providers/capabilities.js +60 -0
- package/src/providers/google/index.js +3 -0
- package/src/providers/index.js +293 -0
- package/src/providers/openai/index.js +3 -0
- package/src/providers/opencode/index.js +3 -0
- package/src/quality-gates.js +143 -0
- package/src/schemas/sub-cluster.js +208 -0
- package/src/state-snapshot.js +398 -0
- package/src/state-snapshotter.js +142 -0
- package/src/status-footer.js +1026 -0
- package/src/sub-cluster-wrapper.js +693 -0
- package/src/task-runner.js +30 -0
- package/src/template-resolver.js +425 -0
- package/src/template-validation/index.js +338 -0
- package/src/template-validation/simulate-consensus-gates.js +324 -0
- package/src/template-validation/simulate-random-topology.js +541 -0
- package/src/template-validation/simulate-two-stage-validation.js +270 -0
- package/src/worktree-claude-config.js +135 -0
- package/src/worktree-tooling-env.js +150 -0
- package/task-lib/attachable-watcher.js +381 -0
- package/task-lib/commands/clean.js +50 -0
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/get-log-path.js +23 -0
- package/task-lib/commands/kill.js +32 -0
- package/task-lib/commands/list.js +105 -0
- package/task-lib/commands/logs.js +439 -0
- package/task-lib/commands/resume.js +42 -0
- package/task-lib/commands/run.js +57 -0
- package/task-lib/commands/schedule.js +105 -0
- package/task-lib/commands/scheduler-cmd.js +96 -0
- package/task-lib/commands/schedules.js +148 -0
- package/task-lib/commands/status.js +44 -0
- package/task-lib/commands/unschedule.js +16 -0
- package/task-lib/completion.js +9 -0
- package/task-lib/config.js +11 -0
- package/task-lib/name-generator.js +230 -0
- package/task-lib/package.json +3 -0
- package/task-lib/provider-helper-runtime.js +29 -0
- package/task-lib/runner.js +190 -0
- package/task-lib/scheduler.js +252 -0
- package/task-lib/store.js +529 -0
- package/task-lib/watcher.js +305 -0
package/src/lib/gc.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Garbage collection for orphaned zeroshot worktrees and database files.
|
|
3
|
+
*
|
|
4
|
+
* Standalone module with ZERO dependencies on Orchestrator or IsolationManager.
|
|
5
|
+
* All operations are synchronous so it can be called from any context
|
|
6
|
+
* (CLI, createWorktree pre-flight, etc.) without async concerns.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
const DEFAULT_STORAGE_DIR = path.join(os.homedir(), '.zeroshot');
|
|
14
|
+
|
|
15
|
+
/** Cluster ID pattern: adjective-noun-number (e.g., "flying-jungle-51") */
|
|
16
|
+
const CLUSTER_ID_PATTERN = /^[a-z]+-[a-z]+-\d+$/;
|
|
17
|
+
|
|
18
|
+
function isClusterDir(entry) {
|
|
19
|
+
return entry.isDirectory() && CLUSTER_ID_PATTERN.test(entry.name);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolveActiveClusterIdFromEnv() {
|
|
23
|
+
const clusterId = process.env.ZEROSHOT_CLUSTER_ID;
|
|
24
|
+
if (typeof clusterId !== 'string' || clusterId.trim().length === 0) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const normalized = clusterId.trim();
|
|
28
|
+
return CLUSTER_ID_PATTERN.test(normalized) ? normalized : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read known cluster IDs from clusters.json (synchronous, no locking).
|
|
33
|
+
* @param {string} storageDir
|
|
34
|
+
* @returns {Set<string>}
|
|
35
|
+
*/
|
|
36
|
+
function readKnownClusterIds(storageDir) {
|
|
37
|
+
const ids = new Set();
|
|
38
|
+
const clustersFile = path.join(storageDir, 'clusters.json');
|
|
39
|
+
try {
|
|
40
|
+
if (!fs.existsSync(clustersFile)) return ids;
|
|
41
|
+
const raw = JSON.parse(fs.readFileSync(clustersFile, 'utf8'));
|
|
42
|
+
for (const id of Object.keys(raw)) ids.add(id);
|
|
43
|
+
} catch {
|
|
44
|
+
// Corrupt/missing — treat as empty (safe: nothing deleted incorrectly)
|
|
45
|
+
}
|
|
46
|
+
return ids;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveStorageAndKnownIds(storageDirOrOptions = DEFAULT_STORAGE_DIR) {
|
|
50
|
+
const options =
|
|
51
|
+
typeof storageDirOrOptions === 'string'
|
|
52
|
+
? { storageDir: storageDirOrOptions }
|
|
53
|
+
: storageDirOrOptions || {};
|
|
54
|
+
const storageDir = options.storageDir || DEFAULT_STORAGE_DIR;
|
|
55
|
+
const knownIds = readKnownClusterIds(storageDir);
|
|
56
|
+
const activeClusterId = resolveActiveClusterIdFromEnv();
|
|
57
|
+
if (activeClusterId) {
|
|
58
|
+
knownIds.add(activeClusterId);
|
|
59
|
+
}
|
|
60
|
+
if (options.extraKnownIds) {
|
|
61
|
+
for (const id of options.extraKnownIds) knownIds.add(id);
|
|
62
|
+
}
|
|
63
|
+
return { storageDir, knownIds };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Count orphaned worktree directories (for error messages).
|
|
68
|
+
* @param {string|{storageDir?: string, extraKnownIds?: Set<string>}} [storageDirOrOptions]
|
|
69
|
+
*/
|
|
70
|
+
function countOrphanedWorktrees(storageDirOrOptions = DEFAULT_STORAGE_DIR) {
|
|
71
|
+
const { storageDir, knownIds } = resolveStorageAndKnownIds(storageDirOrOptions);
|
|
72
|
+
const worktreeDir = path.join(storageDir, 'worktrees');
|
|
73
|
+
if (!fs.existsSync(worktreeDir)) return 0;
|
|
74
|
+
try {
|
|
75
|
+
return fs
|
|
76
|
+
.readdirSync(worktreeDir, { withFileTypes: true })
|
|
77
|
+
.filter((e) => isClusterDir(e) && !knownIds.has(e.name)).length;
|
|
78
|
+
} catch {
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Try to remove a single file. Returns error string or null. */
|
|
84
|
+
function tryUnlink(filePath) {
|
|
85
|
+
try {
|
|
86
|
+
fs.unlinkSync(filePath);
|
|
87
|
+
return null;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
return err.message;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Try to remove a directory tree. Returns error string or null. */
|
|
94
|
+
function tryRmdir(dirPath) {
|
|
95
|
+
try {
|
|
96
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
97
|
+
return null;
|
|
98
|
+
} catch (err) {
|
|
99
|
+
return err.message;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Find repo root from a worktree's .git file (gitdir pointer). */
|
|
104
|
+
function findRepoRootFromWorktree(worktreeDir) {
|
|
105
|
+
let entries;
|
|
106
|
+
try {
|
|
107
|
+
entries = fs.readdirSync(worktreeDir, { withFileTypes: true });
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
if (!entry.isDirectory()) continue;
|
|
113
|
+
const dotGit = path.join(worktreeDir, entry.name, '.git');
|
|
114
|
+
try {
|
|
115
|
+
const content = fs.readFileSync(dotGit, 'utf8').trim();
|
|
116
|
+
const match = content.match(/^gitdir:\s*(.+)/);
|
|
117
|
+
if (!match) continue;
|
|
118
|
+
// gitdir: /repo/.git/worktrees/<name> → resolve to /repo
|
|
119
|
+
const repoRoot = path.resolve(match[1].trim(), '..', '..', '..');
|
|
120
|
+
if (fs.existsSync(path.join(repoRoot, '.git'))) return repoRoot;
|
|
121
|
+
} catch {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Best-effort git worktree prune. */
|
|
129
|
+
function pruneGitWorktrees(worktreeDir) {
|
|
130
|
+
const repoRoot = findRepoRootFromWorktree(worktreeDir);
|
|
131
|
+
if (!repoRoot) return;
|
|
132
|
+
try {
|
|
133
|
+
require('child_process').execSync('git worktree prune', {
|
|
134
|
+
cwd: repoRoot,
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
stdio: 'pipe',
|
|
137
|
+
timeout: 10000,
|
|
138
|
+
});
|
|
139
|
+
} catch {
|
|
140
|
+
// Best effort
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Garbage-collect orphaned worktree directories and database files.
|
|
146
|
+
*
|
|
147
|
+
* @param {object} [options]
|
|
148
|
+
* @param {string} [options.storageDir]
|
|
149
|
+
* @param {Set<string>} [options.extraKnownIds]
|
|
150
|
+
* @param {boolean} [options.dryRun=false]
|
|
151
|
+
* @param {boolean} [options.removeDbFiles] - Defaults to false when ZEROSHOT_CLUSTER_ID is set, else true
|
|
152
|
+
* @returns {{ orphanedWorktrees: string[], orphanedDbs: string[], errors: string[] }}
|
|
153
|
+
*/
|
|
154
|
+
function gcOrphanedWorktrees(options = {}) {
|
|
155
|
+
const storageDir = options.storageDir || DEFAULT_STORAGE_DIR;
|
|
156
|
+
const dryRun = options.dryRun || false;
|
|
157
|
+
const activeClusterId = resolveActiveClusterIdFromEnv();
|
|
158
|
+
const removeDbFiles =
|
|
159
|
+
typeof options.removeDbFiles === 'boolean' ? options.removeDbFiles : activeClusterId === null;
|
|
160
|
+
const worktreeDir = path.join(storageDir, 'worktrees');
|
|
161
|
+
const result = { orphanedWorktrees: [], orphanedDbs: [], errors: [] };
|
|
162
|
+
|
|
163
|
+
const { knownIds } = resolveStorageAndKnownIds({
|
|
164
|
+
storageDir,
|
|
165
|
+
extraKnownIds: options.extraKnownIds,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
collectOrphanedWorktrees(worktreeDir, knownIds, dryRun, result);
|
|
169
|
+
if (removeDbFiles) {
|
|
170
|
+
collectOrphanedDbFiles(storageDir, knownIds, dryRun, result);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!dryRun && result.orphanedWorktrees.length > 0) {
|
|
174
|
+
pruneGitWorktrees(worktreeDir);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function collectOrphanedWorktrees(worktreeDir, knownIds, dryRun, result) {
|
|
181
|
+
if (!fs.existsSync(worktreeDir)) return;
|
|
182
|
+
let entries;
|
|
183
|
+
try {
|
|
184
|
+
entries = fs.readdirSync(worktreeDir, { withFileTypes: true });
|
|
185
|
+
} catch (err) {
|
|
186
|
+
result.errors.push(`Failed to read worktree dir: ${err.message}`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
if (!isClusterDir(entry) || knownIds.has(entry.name)) continue;
|
|
191
|
+
result.orphanedWorktrees.push(entry.name);
|
|
192
|
+
if (dryRun) continue;
|
|
193
|
+
const err = tryRmdir(path.join(worktreeDir, entry.name));
|
|
194
|
+
if (err) result.errors.push(`Failed to remove worktree ${entry.name}: ${err}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function collectOrphanedDbFiles(storageDir, knownIds, dryRun, result) {
|
|
199
|
+
let entries;
|
|
200
|
+
try {
|
|
201
|
+
entries = fs.readdirSync(storageDir);
|
|
202
|
+
} catch {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
for (const entry of entries) {
|
|
206
|
+
const match = entry.match(/^(.+)\.(db|db-wal|db-shm)$/);
|
|
207
|
+
if (!match || knownIds.has(match[1])) continue;
|
|
208
|
+
result.orphanedDbs.push(entry);
|
|
209
|
+
if (dryRun) continue;
|
|
210
|
+
const err = tryUnlink(path.join(storageDir, entry));
|
|
211
|
+
if (err) result.errors.push(`Failed to remove db file ${entry}: ${err}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get disk space info for a path.
|
|
217
|
+
* @param {string} dirPath
|
|
218
|
+
* @returns {{ available: number, total: number, usagePercent: number } | null}
|
|
219
|
+
*/
|
|
220
|
+
function getDiskSpace(dirPath) {
|
|
221
|
+
try {
|
|
222
|
+
const stats = fs.statfsSync(dirPath);
|
|
223
|
+
const available = stats.bavail * stats.bsize;
|
|
224
|
+
const total = stats.blocks * stats.bsize;
|
|
225
|
+
const usagePercent = total > 0 ? ((total - available) / total) * 100 : 0;
|
|
226
|
+
return { available, total, usagePercent };
|
|
227
|
+
} catch {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = { gcOrphanedWorktrees, countOrphanedWorktrees, getDiskSpace, CLUSTER_ID_PATTERN };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe subprocess execution with mandatory timeouts.
|
|
3
|
+
*
|
|
4
|
+
* NEVER use child_process.exec() or execSync() directly.
|
|
5
|
+
* These wrappers enforce timeouts to prevent infinite hangs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { exec: nodeExec, execSync: nodeExecSync } = require('child_process');
|
|
9
|
+
|
|
10
|
+
/** Default timeout: 30 seconds */
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Execute command with mandatory timeout.
|
|
15
|
+
* Supports both Promise and callback styles for gradual migration.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} command - Command to execute
|
|
18
|
+
* @param {object} [options] - Options (timeout uses default if not specified)
|
|
19
|
+
* @param {function} [callback] - Optional callback(error, stdout, stderr)
|
|
20
|
+
* @returns {Promise<{stdout: string, stderr: string}>|void} Promise if no callback, void if callback
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Promise style (preferred)
|
|
24
|
+
* const { stdout } = await exec('ls -la', { timeout: 5000 });
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Callback style (for legacy code migration)
|
|
28
|
+
* exec('ls -la', { timeout: 5000 }, (error, stdout) => { ... });
|
|
29
|
+
*/
|
|
30
|
+
function exec(command, optionsOrCallback = {}, callbackArg = null) {
|
|
31
|
+
// Handle overloaded signature: exec(cmd, callback)
|
|
32
|
+
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : callbackArg;
|
|
33
|
+
const options = typeof optionsOrCallback === 'function' ? {} : optionsOrCallback;
|
|
34
|
+
|
|
35
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
36
|
+
|
|
37
|
+
if (timeout <= 0) {
|
|
38
|
+
const err = new Error('exec() timeout must be > 0. Infinite waits are forbidden.');
|
|
39
|
+
if (callback) {
|
|
40
|
+
callback(err);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
return Promise.reject(err);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Callback style
|
|
47
|
+
if (callback) {
|
|
48
|
+
nodeExec(command, { ...options, timeout }, (error, stdout, stderr) => {
|
|
49
|
+
if (error && error.killed && error.signal === 'SIGTERM') {
|
|
50
|
+
error.message = `Command timed out after ${timeout}ms: ${command}`;
|
|
51
|
+
}
|
|
52
|
+
callback(error, stdout, stderr);
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Promise style
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
nodeExec(command, { ...options, timeout }, (error, stdout, stderr) => {
|
|
60
|
+
if (error) {
|
|
61
|
+
if (error.killed && error.signal === 'SIGTERM') {
|
|
62
|
+
error.message = `Command timed out after ${timeout}ms: ${command}`;
|
|
63
|
+
}
|
|
64
|
+
reject(error);
|
|
65
|
+
} else {
|
|
66
|
+
resolve({ stdout, stderr });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Execute command with mandatory timeout (sync)
|
|
74
|
+
* @param {string} command - Command to execute
|
|
75
|
+
* @param {object} [options] - Options (timeout required or uses default)
|
|
76
|
+
* @returns {string} stdout
|
|
77
|
+
*/
|
|
78
|
+
function execSync(command, options = {}) {
|
|
79
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
80
|
+
|
|
81
|
+
if (timeout <= 0) {
|
|
82
|
+
throw new Error('execSync() timeout must be > 0. Infinite waits are forbidden.');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return nodeExecSync(command, { ...options, timeout });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { exec, execSync, DEFAULT_TIMEOUT_MS };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LogicEngine - JavaScript sandbox for agent decision logic
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Isolated VM for executing agent trigger logic
|
|
6
|
+
* - Timeout enforcement (1 second)
|
|
7
|
+
* - Ledger API access for queries
|
|
8
|
+
* - Helper functions for common patterns
|
|
9
|
+
* - Sandbox security (no fs, network, child_process)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const vm = require('vm');
|
|
13
|
+
|
|
14
|
+
function buildLedgerAPI(messageBus, clusterId) {
|
|
15
|
+
return {
|
|
16
|
+
query: (criteria) => {
|
|
17
|
+
return messageBus.query({ ...criteria, cluster_id: clusterId });
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
findLast: (criteria) => {
|
|
21
|
+
return messageBus.findLast({ ...criteria, cluster_id: clusterId });
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
count: (criteria) => {
|
|
25
|
+
return messageBus.count({ ...criteria, cluster_id: clusterId });
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
since: (timestamp) => {
|
|
29
|
+
return messageBus.since({ cluster_id: clusterId, timestamp });
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildHelpers(ledgerAPI) {
|
|
35
|
+
return {
|
|
36
|
+
allResponded: (agents, topic, since) => {
|
|
37
|
+
const responses = ledgerAPI.query({ topic, since });
|
|
38
|
+
const responders = new Set(responses.map((r) => r.sender));
|
|
39
|
+
return agents.every((a) => responders.has(a.id || a));
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
hasConsensus: (topic, since) => {
|
|
43
|
+
const responses = ledgerAPI.query({ topic, since });
|
|
44
|
+
if (responses.length === 0) return false;
|
|
45
|
+
return responses.every((r) => r.content?.data?.approved === true);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
timeSinceLastMessage: (topic) => {
|
|
49
|
+
const last = ledgerAPI.findLast({ topic });
|
|
50
|
+
if (!last) return Infinity;
|
|
51
|
+
return Date.now() - last.timestamp;
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
hasMessagesSince: (topic, since) => {
|
|
55
|
+
const count = ledgerAPI.count({ topic, since });
|
|
56
|
+
return count > 0;
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
getConfig: require('./config-router').getConfig,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function buildClusterAPI(cluster, clusterId) {
|
|
64
|
+
const getAgents = () => (cluster ? cluster.agents || [] : []);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
id: clusterId,
|
|
68
|
+
getAgents,
|
|
69
|
+
getAgentsByRole: (role) => getAgents().filter((a) => a.role === role),
|
|
70
|
+
getAgent: (id) => getAgents().find((a) => a.id === id) || null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function buildAgentContext(agent) {
|
|
75
|
+
return {
|
|
76
|
+
id: agent.id,
|
|
77
|
+
role: agent.role,
|
|
78
|
+
iteration: agent.iteration || 0,
|
|
79
|
+
requiredQualityGates: Array.isArray(agent.requiredQualityGates)
|
|
80
|
+
? agent.requiredQualityGates
|
|
81
|
+
: [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getSafeBuiltins() {
|
|
86
|
+
return { Set, Map, Array, Object, String, Number, Boolean, Math, Date, JSON };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getQuietConsole() {
|
|
90
|
+
return {
|
|
91
|
+
log: () => {},
|
|
92
|
+
error: () => {},
|
|
93
|
+
warn: () => {},
|
|
94
|
+
info: () => {},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class LogicEngine {
|
|
99
|
+
constructor(messageBus, cluster) {
|
|
100
|
+
this.messageBus = messageBus;
|
|
101
|
+
this.cluster = cluster;
|
|
102
|
+
this.timeout = 1000; // 1 second
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Evaluate a trigger logic script
|
|
107
|
+
* @param {String} script - JavaScript code to evaluate
|
|
108
|
+
* @param {Object} agent - Agent context
|
|
109
|
+
* @param {Object} message - Triggering message
|
|
110
|
+
* @returns {Boolean} Whether agent should wake up
|
|
111
|
+
*/
|
|
112
|
+
evaluate(script, agent, message) {
|
|
113
|
+
try {
|
|
114
|
+
// Build sandbox context
|
|
115
|
+
const context = this._buildContext(agent, message);
|
|
116
|
+
|
|
117
|
+
// Create isolated context with frozen prototypes
|
|
118
|
+
// This prevents prototype pollution attacks
|
|
119
|
+
const isolatedContext = {};
|
|
120
|
+
|
|
121
|
+
// Freeze Object, Array, Function prototypes in the sandbox
|
|
122
|
+
isolatedContext.Object = Object.freeze({ ...Object });
|
|
123
|
+
isolatedContext.Array = Array;
|
|
124
|
+
isolatedContext.Function = Function;
|
|
125
|
+
|
|
126
|
+
// Copy safe context properties
|
|
127
|
+
Object.assign(isolatedContext, context);
|
|
128
|
+
|
|
129
|
+
// Wrap script to prevent prototype access
|
|
130
|
+
const wrappedScript = `(function() {
|
|
131
|
+
'use strict';
|
|
132
|
+
// Prevent prototype pollution
|
|
133
|
+
const frozenObject = Object;
|
|
134
|
+
const frozenArray = Array;
|
|
135
|
+
Object.freeze(frozenObject.prototype);
|
|
136
|
+
Object.freeze(frozenArray.prototype);
|
|
137
|
+
|
|
138
|
+
${script}
|
|
139
|
+
})()`;
|
|
140
|
+
|
|
141
|
+
// Create and run in context
|
|
142
|
+
vm.createContext(isolatedContext);
|
|
143
|
+
const result = vm.runInContext(wrappedScript, isolatedContext, {
|
|
144
|
+
timeout: this.timeout,
|
|
145
|
+
displayErrors: true,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Coerce to boolean
|
|
149
|
+
return Boolean(result);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error(`Logic evaluation error for agent ${agent.id}:`, error.message);
|
|
152
|
+
return false; // Default to false on error
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Build sandbox context with APIs and helpers
|
|
158
|
+
* @private
|
|
159
|
+
*/
|
|
160
|
+
_buildContext(agent, message) {
|
|
161
|
+
const clusterId = agent.cluster_id;
|
|
162
|
+
const ledgerAPI = buildLedgerAPI(this.messageBus, clusterId);
|
|
163
|
+
|
|
164
|
+
// Build context
|
|
165
|
+
return {
|
|
166
|
+
agent: buildAgentContext(agent),
|
|
167
|
+
message: message || null,
|
|
168
|
+
ledger: ledgerAPI,
|
|
169
|
+
cluster: buildClusterAPI(this.cluster, clusterId),
|
|
170
|
+
helpers: buildHelpers(ledgerAPI),
|
|
171
|
+
...getSafeBuiltins(),
|
|
172
|
+
console: getQuietConsole(),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Validate script syntax without executing
|
|
178
|
+
* @param {String} script - JavaScript code
|
|
179
|
+
* @returns {Object} { valid: Boolean, error: String }
|
|
180
|
+
*/
|
|
181
|
+
validateScript(script) {
|
|
182
|
+
try {
|
|
183
|
+
// Wrap in function like evaluate() does
|
|
184
|
+
const wrappedScript = `(function() { ${script} })()`;
|
|
185
|
+
new vm.Script(wrappedScript);
|
|
186
|
+
return { valid: true, error: null };
|
|
187
|
+
} catch (error) {
|
|
188
|
+
return { valid: false, error: error.message };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Set timeout for script execution
|
|
194
|
+
* @param {Number} ms - Timeout in milliseconds
|
|
195
|
+
*/
|
|
196
|
+
setTimeout(ms) {
|
|
197
|
+
this.timeout = ms;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = LogicEngine;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message buffering helper
|
|
3
|
+
*
|
|
4
|
+
* Ensures trigger-matching messages are never dropped just because an agent/subcluster is busy.
|
|
5
|
+
* Dropped workflow signals (e.g. VALIDATION_RESULT) can wedge clusters in "running" state.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function bufferMessage(target, message, options = {}) {
|
|
9
|
+
const maxBuffered = options.maxBuffered ?? 200;
|
|
10
|
+
|
|
11
|
+
if (!target._bufferedMessages) {
|
|
12
|
+
target._bufferedMessages = [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (target._bufferedMessages.length >= maxBuffered) {
|
|
16
|
+
target._bufferedMessages.shift();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
target._bufferedMessages.push(message);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function scheduleDrain(target, drainFn, options = {}) {
|
|
23
|
+
if (target._bufferDrainScheduled) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
target._bufferDrainScheduled = true;
|
|
28
|
+
|
|
29
|
+
const label = options.label || 'MessageBuffer';
|
|
30
|
+
const id = target.id || 'unknown';
|
|
31
|
+
|
|
32
|
+
const run = () => {
|
|
33
|
+
target._bufferDrainScheduled = false;
|
|
34
|
+
drainFn().catch((error) => {
|
|
35
|
+
console.error(`\n${'='.repeat(80)}`);
|
|
36
|
+
console.error(`🔴 FATAL: ${label} drain crashed (${id})`);
|
|
37
|
+
console.error(`${'='.repeat(80)}`);
|
|
38
|
+
console.error(`Error: ${error.message}`);
|
|
39
|
+
console.error(`Stack: ${error.stack}`);
|
|
40
|
+
console.error(`${'='.repeat(80)}\n`);
|
|
41
|
+
setImmediate(() => {
|
|
42
|
+
throw error;
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const current = target._currentExecution;
|
|
48
|
+
if (current && typeof current.finally === 'function') {
|
|
49
|
+
current.finally(() => setImmediate(run));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setImmediate(run);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function drainBufferedMessages(target, handleFn, options = {}) {
|
|
57
|
+
if (!target.running) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const buffer = target._bufferedMessages;
|
|
62
|
+
if (!buffer || buffer.length === 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (target.state !== 'idle') {
|
|
67
|
+
scheduleDrain(target, () => drainBufferedMessages(target, handleFn, options), options);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
while (target.running && target.state === 'idle' && buffer.length > 0) {
|
|
72
|
+
const next = buffer.shift();
|
|
73
|
+
await handleFn(next);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
bufferMessage,
|
|
79
|
+
scheduleDrain,
|
|
80
|
+
drainBufferedMessages,
|
|
81
|
+
};
|