@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
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { getProviderAdapter } from './adapters';
|
|
2
|
+
import { getString, isRecord, parseJson } from './json';
|
|
3
|
+
import { mergeRedactions } from './redaction';
|
|
4
|
+
import { requestOptions } from './contract-options';
|
|
5
|
+
import { prepareSingleAgentProviderCommand } from './single-agent-runtime';
|
|
6
|
+
import type { BuildProviderCommandOptions, CommandSpec, ProviderAdapter } from './types';
|
|
7
|
+
export {
|
|
8
|
+
contractError,
|
|
9
|
+
ContractRequestError,
|
|
10
|
+
optionalNumber,
|
|
11
|
+
optionalString,
|
|
12
|
+
requiredString,
|
|
13
|
+
} from './contract-errors';
|
|
14
|
+
export {
|
|
15
|
+
collectCommandSpecEnv,
|
|
16
|
+
commandRedactions,
|
|
17
|
+
envRedactions,
|
|
18
|
+
mergeEnvForRedaction,
|
|
19
|
+
providerCredentialEnv,
|
|
20
|
+
stringRecord,
|
|
21
|
+
} from './contract-env';
|
|
22
|
+
import { contractError, optionalString, requiredString } from './contract-errors';
|
|
23
|
+
import { envRedactions, stringRecord } from './contract-env';
|
|
24
|
+
|
|
25
|
+
export interface RequestData {
|
|
26
|
+
readonly raw: Record<string, unknown>;
|
|
27
|
+
readonly command: string | null;
|
|
28
|
+
readonly provider: string | null;
|
|
29
|
+
readonly env: Readonly<Record<string, string>>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function adapterForProvider(provider: string | null): ProviderAdapter {
|
|
33
|
+
if (provider === null || provider.length === 0) {
|
|
34
|
+
throw contractError({
|
|
35
|
+
code: 'missing-field',
|
|
36
|
+
message: 'provider is required.',
|
|
37
|
+
exitCode: 2,
|
|
38
|
+
field: 'provider',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return getProviderAdapter(provider);
|
|
43
|
+
} catch {
|
|
44
|
+
throw contractError({
|
|
45
|
+
code: 'unknown-provider',
|
|
46
|
+
message: `Unknown provider: ${provider}.`,
|
|
47
|
+
exitCode: 4,
|
|
48
|
+
field: 'provider',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function mergeCommandSpec(
|
|
54
|
+
commandSpec: CommandSpec,
|
|
55
|
+
env: Readonly<Record<string, string>>
|
|
56
|
+
): CommandSpec {
|
|
57
|
+
const mergedEnv = { ...commandSpec.env, ...env };
|
|
58
|
+
return {
|
|
59
|
+
...commandSpec,
|
|
60
|
+
env: mergedEnv,
|
|
61
|
+
redactions: mergeRedactions(commandSpec.redactions, envRedactions(mergedEnv)),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildOptions(request: RequestData): BuildProviderCommandOptions {
|
|
66
|
+
const options = requestOptions(request.raw.options);
|
|
67
|
+
const cwd = optionalString(request.raw, 'cwd');
|
|
68
|
+
if (cwd === undefined || options.cwd !== undefined) return options;
|
|
69
|
+
return { ...options, cwd };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function buildCommandSpec(request: RequestData): {
|
|
73
|
+
readonly adapter: ProviderAdapter;
|
|
74
|
+
readonly commandSpec: CommandSpec;
|
|
75
|
+
readonly options: BuildProviderCommandOptions;
|
|
76
|
+
} {
|
|
77
|
+
const adapter = adapterForProvider(request.provider);
|
|
78
|
+
const context = requiredString(request.raw, 'context');
|
|
79
|
+
const options = buildOptions(request);
|
|
80
|
+
const prepared = prepareSingleAgentProviderCommand({
|
|
81
|
+
provider: adapter.id,
|
|
82
|
+
context,
|
|
83
|
+
options,
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
adapter: prepared.adapter,
|
|
87
|
+
options: prepared.options,
|
|
88
|
+
commandSpec: mergeCommandSpec(prepared.commandSpec, request.env),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function schemaMode(options: BuildProviderCommandOptions): string {
|
|
93
|
+
if (!options.jsonSchema) return 'none';
|
|
94
|
+
return options.strictSchema === false ? 'prompt' : 'strict';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function validateRequest(input: string, schemaVersion: 1): RequestData {
|
|
98
|
+
const parsed = parseRequestObject(input);
|
|
99
|
+
assertSchemaVersion(parsed, schemaVersion);
|
|
100
|
+
const command = requiredCommand(parsed);
|
|
101
|
+
return {
|
|
102
|
+
raw: parsed,
|
|
103
|
+
command,
|
|
104
|
+
provider: getString(parsed, 'provider'),
|
|
105
|
+
env: requestEnv(parsed),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const KNOWN_COMMANDS: readonly string[] = [
|
|
110
|
+
'probe',
|
|
111
|
+
'build-command',
|
|
112
|
+
'parse-output',
|
|
113
|
+
'classify-error',
|
|
114
|
+
'invoke',
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
function parseRequestObject(input: string): Record<string, unknown> {
|
|
118
|
+
let parsed: unknown;
|
|
119
|
+
try {
|
|
120
|
+
parsed = parseJson(input);
|
|
121
|
+
} catch {
|
|
122
|
+
throw contractError({
|
|
123
|
+
code: 'malformed-json',
|
|
124
|
+
message: 'Request body must be valid JSON.',
|
|
125
|
+
exitCode: 2,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (isRecord(parsed)) return parsed;
|
|
129
|
+
throw contractError({
|
|
130
|
+
code: 'invalid-request',
|
|
131
|
+
message: 'Request body must be a JSON object.',
|
|
132
|
+
exitCode: 2,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function assertSchemaVersion(parsed: Record<string, unknown>, schemaVersion: 1): void {
|
|
137
|
+
if (parsed.schemaVersion === schemaVersion) return;
|
|
138
|
+
throw contractError({
|
|
139
|
+
code: 'unsupported-schema-version',
|
|
140
|
+
message: 'schemaVersion must be 1.',
|
|
141
|
+
exitCode: 2,
|
|
142
|
+
field: 'schemaVersion',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function requiredCommand(parsed: Record<string, unknown>): string {
|
|
147
|
+
const command = getString(parsed, 'command');
|
|
148
|
+
if (command === null) {
|
|
149
|
+
throw contractError({
|
|
150
|
+
code: 'missing-field',
|
|
151
|
+
message: 'command is required.',
|
|
152
|
+
exitCode: 2,
|
|
153
|
+
field: 'command',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (KNOWN_COMMANDS.includes(command)) return command;
|
|
157
|
+
throw contractError({
|
|
158
|
+
code: 'unknown-command',
|
|
159
|
+
message: `Unknown command: ${command}.`,
|
|
160
|
+
exitCode: 3,
|
|
161
|
+
field: 'command',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function requestEnv(parsed: Record<string, unknown>): Readonly<Record<string, string>> {
|
|
166
|
+
return stringRecord(parsed.env, 'env');
|
|
167
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { dispatchRequest } from './contract-actions';
|
|
2
|
+
import {
|
|
3
|
+
fallbackErrorEnvelope,
|
|
4
|
+
requestEnvelopeData,
|
|
5
|
+
requestErrorFromUnknown,
|
|
6
|
+
} from './contract-fallback';
|
|
7
|
+
import { finalizeEnvelope, providerExecutableSchemaVersion } from './contract-envelope';
|
|
8
|
+
import { validateRequest } from './contract-support';
|
|
9
|
+
import { spawnProcessRunner, type ProcessRunner } from './process-runner';
|
|
10
|
+
import type { ContractEnvelope } from './contract-envelope';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
providerExecutableSchemaVersion,
|
|
14
|
+
type ContractEnvelope,
|
|
15
|
+
type ContractErrorEnvelope,
|
|
16
|
+
type ContractErrorObject,
|
|
17
|
+
type ContractEvidence,
|
|
18
|
+
type ContractSuccessEnvelope,
|
|
19
|
+
} from './contract-envelope';
|
|
20
|
+
|
|
21
|
+
export type ProviderExecutableCommand =
|
|
22
|
+
| 'probe'
|
|
23
|
+
| 'build-command'
|
|
24
|
+
| 'parse-output'
|
|
25
|
+
| 'classify-error'
|
|
26
|
+
| 'invoke';
|
|
27
|
+
|
|
28
|
+
export interface ProviderExecutableResponse {
|
|
29
|
+
readonly envelope: ContractEnvelope;
|
|
30
|
+
readonly exitCode: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ProviderExecutableOptions {
|
|
34
|
+
readonly runner?: ProcessRunner;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function runProviderExecutable(
|
|
38
|
+
input: string,
|
|
39
|
+
options: ProviderExecutableOptions = {}
|
|
40
|
+
): Promise<ProviderExecutableResponse> {
|
|
41
|
+
const fallback = requestEnvelopeData(input);
|
|
42
|
+
try {
|
|
43
|
+
const request = validateRequest(input, providerExecutableSchemaVersion);
|
|
44
|
+
const envelope = await dispatchRequest(request, options.runner ?? spawnProcessRunner());
|
|
45
|
+
return {
|
|
46
|
+
envelope: finalizeEnvelope(envelope, request.env),
|
|
47
|
+
exitCode: 0,
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
const requestError = requestErrorFromUnknown(error);
|
|
51
|
+
return {
|
|
52
|
+
envelope: finalizeEnvelope(fallbackErrorEnvelope(fallback, requestError), fallback.env),
|
|
53
|
+
exitCode: requestError.exitCode,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const EXECUTABLE_RESOLUTION_ENV_KEYS = new Set(['path', 'pathext']);
|
|
2
|
+
const PROCESS_CONTROL_ENV_KEYS = new Set([
|
|
3
|
+
'_java_options',
|
|
4
|
+
'bash_env',
|
|
5
|
+
'bun_options',
|
|
6
|
+
'bun_preload',
|
|
7
|
+
'classpath',
|
|
8
|
+
'env',
|
|
9
|
+
'java_tool_options',
|
|
10
|
+
'jdk_java_options',
|
|
11
|
+
'ld_library_path',
|
|
12
|
+
'ld_preload',
|
|
13
|
+
'lua_cpath',
|
|
14
|
+
'lua_init',
|
|
15
|
+
'lua_path',
|
|
16
|
+
'node_options',
|
|
17
|
+
'node_path',
|
|
18
|
+
'npm_config_node_options',
|
|
19
|
+
'perl5lib',
|
|
20
|
+
'perl5opt',
|
|
21
|
+
'pythonhome',
|
|
22
|
+
'pythonpath',
|
|
23
|
+
'pythonstartup',
|
|
24
|
+
'rubylib',
|
|
25
|
+
'rubyopt',
|
|
26
|
+
'zshenv',
|
|
27
|
+
]);
|
|
28
|
+
const PROCESS_CONTROL_ENV_PREFIXES = ['dyld_', 'ld_', 'node_'];
|
|
29
|
+
|
|
30
|
+
type EnvRecord = Readonly<Record<string, string | undefined>>;
|
|
31
|
+
|
|
32
|
+
export function isExecutableResolutionEnvKey(key: string): boolean {
|
|
33
|
+
return EXECUTABLE_RESOLUTION_ENV_KEYS.has(key.toLowerCase());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function isProcessControlEnvKey(key: string): boolean {
|
|
37
|
+
const normalized = key.toLowerCase();
|
|
38
|
+
if (PROCESS_CONTROL_ENV_KEYS.has(normalized)) return true;
|
|
39
|
+
return PROCESS_CONTROL_ENV_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isUnsafeProviderEnvKey(key: string): boolean {
|
|
43
|
+
return isExecutableResolutionEnvKey(key) || isProcessControlEnvKey(key);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function findExecutableResolutionEnvKey(env: EnvRecord): string | null {
|
|
47
|
+
for (const key of Object.keys(env)) {
|
|
48
|
+
if (isExecutableResolutionEnvKey(key)) return key;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function findUnsafeProviderEnvKey(env: EnvRecord): string | null {
|
|
54
|
+
for (const key of Object.keys(env)) {
|
|
55
|
+
if (isUnsafeProviderEnvKey(key)) return key;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function omitEnvKeys(
|
|
61
|
+
env: EnvRecord,
|
|
62
|
+
isUnsafe: (key: string) => boolean
|
|
63
|
+
): Readonly<Record<string, string>> {
|
|
64
|
+
const result: Record<string, string> = {};
|
|
65
|
+
for (const [key, value] of Object.entries(env)) {
|
|
66
|
+
if (value === undefined || isUnsafe(key)) continue;
|
|
67
|
+
result[key] = value;
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function omitExecutableResolutionEnv(env: EnvRecord): Readonly<Record<string, string>> {
|
|
73
|
+
return omitEnvKeys(env, isExecutableResolutionEnvKey);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function omitProcessControlEnv(env: EnvRecord): Readonly<Record<string, string>> {
|
|
77
|
+
return omitEnvKeys(env, isProcessControlEnvKey);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function omitUnsafeProviderEnv(env: EnvRecord): Readonly<Record<string, string>> {
|
|
81
|
+
return omitEnvKeys(env, isUnsafeProviderEnvKey);
|
|
82
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { getNumber, getRecord, getString, isRecord, unknownToMessage } from './json';
|
|
2
|
+
import type { ErrorClassification } from './types';
|
|
3
|
+
|
|
4
|
+
const BASE_RETRYABLE_PATTERNS: readonly RegExp[] = [
|
|
5
|
+
/rate.?limit/i,
|
|
6
|
+
/\b429\b/i,
|
|
7
|
+
/too many requests/i,
|
|
8
|
+
/overloaded/i,
|
|
9
|
+
/temporar(?:y|ily)/i,
|
|
10
|
+
/unavailable/i,
|
|
11
|
+
/try again/i,
|
|
12
|
+
/timeout/i,
|
|
13
|
+
/timed out/i,
|
|
14
|
+
/deadline exceeded/i,
|
|
15
|
+
/connection (?:reset|refused)/i,
|
|
16
|
+
/\b(econnreset|econnrefused|etimedout|eai_again)\b/i,
|
|
17
|
+
/network/i,
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const BASE_PERMANENT_PATTERNS: readonly RegExp[] = [
|
|
21
|
+
/invalid[_ -]?api[_ -]?key/i,
|
|
22
|
+
/api[_ -]?key.*invalid/i,
|
|
23
|
+
/unauthorized/i,
|
|
24
|
+
/forbidden/i,
|
|
25
|
+
/authentication/i,
|
|
26
|
+
/permission denied/i,
|
|
27
|
+
/invalid argument/i,
|
|
28
|
+
/unknown option/i,
|
|
29
|
+
/\busage:\b/i,
|
|
30
|
+
/command not found/i,
|
|
31
|
+
/not recognized as an internal or external command/i,
|
|
32
|
+
/model not found/i,
|
|
33
|
+
/context length exceeded/i,
|
|
34
|
+
/insufficient quota/i,
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function getStatus(error: unknown): number | null {
|
|
38
|
+
if (!isRecord(error)) return null;
|
|
39
|
+
const directStatus = getNumber(error, 'status') ?? getNumber(error, 'statusCode');
|
|
40
|
+
if (directStatus !== null) return directStatus;
|
|
41
|
+
|
|
42
|
+
const response = getRecord(error, 'response');
|
|
43
|
+
if (response === null) return null;
|
|
44
|
+
return getNumber(response, 'status') ?? getNumber(response, 'statusCode');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getCode(error: unknown): string | null {
|
|
48
|
+
if (!isRecord(error)) return null;
|
|
49
|
+
return getString(error, 'code');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function firstMatchedPattern(patterns: readonly RegExp[], message: string): RegExp | null {
|
|
53
|
+
for (const pattern of patterns) {
|
|
54
|
+
if (pattern.test(message)) return pattern;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function baseRetryableErrorPatterns(): readonly RegExp[] {
|
|
60
|
+
return BASE_RETRYABLE_PATTERNS;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function basePermanentErrorPatterns(): readonly RegExp[] {
|
|
64
|
+
return BASE_PERMANENT_PATTERNS;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function classifyErrorWithPatterns(
|
|
68
|
+
error: unknown,
|
|
69
|
+
retryablePatterns: readonly RegExp[],
|
|
70
|
+
permanentPatterns: readonly RegExp[]
|
|
71
|
+
): ErrorClassification {
|
|
72
|
+
const statusClassification = classifyStatus(getStatus(error));
|
|
73
|
+
if (statusClassification !== null) return statusClassification;
|
|
74
|
+
|
|
75
|
+
const codeClassification = classifyCode(getCode(error));
|
|
76
|
+
if (codeClassification !== null) return codeClassification;
|
|
77
|
+
|
|
78
|
+
const message = unknownToMessage(error).trim();
|
|
79
|
+
if (!message) return { retryable: true, kind: 'unknown-retryable' };
|
|
80
|
+
return classifyMessage(message, retryablePatterns, permanentPatterns);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function classifyStatus(status: number | null): ErrorClassification | null {
|
|
84
|
+
if (typeof status !== 'number') return null;
|
|
85
|
+
if (status === 429 || status >= 500) return { retryable: true, kind: 'status-retryable' };
|
|
86
|
+
if (status >= 400 && status < 500) return { retryable: false, kind: 'status-permanent' };
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function classifyCode(code: string | null): ErrorClassification | null {
|
|
91
|
+
if (typeof code !== 'string') return null;
|
|
92
|
+
if (/\b(econnreset|econnrefused|etimedout|eai_again)\b/i.test(code)) {
|
|
93
|
+
return { retryable: true, kind: 'code-retryable', matchedPattern: 'network-code' };
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function classifyMessage(
|
|
99
|
+
message: string,
|
|
100
|
+
retryablePatterns: readonly RegExp[],
|
|
101
|
+
permanentPatterns: readonly RegExp[]
|
|
102
|
+
): ErrorClassification {
|
|
103
|
+
const permanent = firstMatchedPattern(permanentPatterns, message);
|
|
104
|
+
if (permanent !== null) {
|
|
105
|
+
return {
|
|
106
|
+
retryable: false,
|
|
107
|
+
kind: 'permanent-pattern',
|
|
108
|
+
matchedPattern: permanent.source,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const retryable = firstMatchedPattern(retryablePatterns, message);
|
|
113
|
+
if (retryable !== null) {
|
|
114
|
+
return {
|
|
115
|
+
retryable: true,
|
|
116
|
+
kind: 'retryable-pattern',
|
|
117
|
+
matchedPattern: retryable.source,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return { retryable: true, kind: 'unknown-retryable' };
|
|
122
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runProviderExecutable } from './contract';
|
|
3
|
+
import { spawnProcessRunner } from './process-runner';
|
|
4
|
+
import { stringifyJson, unknownToMessage } from './json';
|
|
5
|
+
|
|
6
|
+
async function readStdin(): Promise<string> {
|
|
7
|
+
const chunks: Buffer[] = [];
|
|
8
|
+
for await (const chunk of process.stdin) {
|
|
9
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
10
|
+
}
|
|
11
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function main(): Promise<void> {
|
|
15
|
+
const input = await readStdin();
|
|
16
|
+
const response = await runProviderExecutable(input, { runner: spawnProcessRunner() });
|
|
17
|
+
process.stdout.write(`${stringifyJson(response.envelope)}\n`);
|
|
18
|
+
process.exitCode = response.exitCode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
main().catch((error: unknown) => {
|
|
22
|
+
process.stderr.write(`${unknownToMessage(error)}\n`);
|
|
23
|
+
process.exitCode = 5;
|
|
24
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export {
|
|
2
|
+
buildProviderCommand,
|
|
3
|
+
classifyProviderError,
|
|
4
|
+
detectProviderFatalError,
|
|
5
|
+
detectProviderStreamingModeError,
|
|
6
|
+
getProviderAdapter,
|
|
7
|
+
listProviderAdapters,
|
|
8
|
+
parseProviderChunk,
|
|
9
|
+
recoverProviderStructuredOutput,
|
|
10
|
+
resolveModelSpec,
|
|
11
|
+
supportsProviderStructuredOutputRecovery,
|
|
12
|
+
NO_MESSAGES_RETURNED,
|
|
13
|
+
STREAMING_MODE_ERROR,
|
|
14
|
+
type StreamingModeError,
|
|
15
|
+
type StructuredOutputRecovery,
|
|
16
|
+
} from './adapters';
|
|
17
|
+
export {
|
|
18
|
+
providerExecutableSchemaVersion,
|
|
19
|
+
runProviderExecutable,
|
|
20
|
+
type ContractEnvelope,
|
|
21
|
+
type ContractErrorEnvelope,
|
|
22
|
+
type ContractErrorObject,
|
|
23
|
+
type ContractEvidence,
|
|
24
|
+
type ContractSuccessEnvelope,
|
|
25
|
+
type ProviderExecutableCommand,
|
|
26
|
+
type ProviderExecutableOptions,
|
|
27
|
+
type ProviderExecutableResponse,
|
|
28
|
+
} from './contract';
|
|
29
|
+
export {
|
|
30
|
+
spawnProcessRunner,
|
|
31
|
+
type ProcessResult,
|
|
32
|
+
type ProcessRunner,
|
|
33
|
+
type ProcessRunnerOptions,
|
|
34
|
+
} from './process-runner';
|
|
35
|
+
export {
|
|
36
|
+
detectRuntimeProviderCliFeatures,
|
|
37
|
+
prepareSingleAgentProviderCommand,
|
|
38
|
+
type PreparedSingleAgentProviderCommand,
|
|
39
|
+
type SingleAgentProviderCommandInput,
|
|
40
|
+
} from './single-agent-runtime';
|
|
41
|
+
|
|
42
|
+
export type {
|
|
43
|
+
AgentCliProviderHelperMetadata,
|
|
44
|
+
BuildProviderCommandOptions,
|
|
45
|
+
CleanupMetadata,
|
|
46
|
+
CliFeatureOverrides,
|
|
47
|
+
ClaudeCliFeatures,
|
|
48
|
+
CodexCliFeatures,
|
|
49
|
+
CommandSpec,
|
|
50
|
+
ErrorClassification,
|
|
51
|
+
ErrorClassificationKind,
|
|
52
|
+
GeminiCliFeatures,
|
|
53
|
+
KnownProviderName,
|
|
54
|
+
LevelModelSpec,
|
|
55
|
+
LevelOverrides,
|
|
56
|
+
ModelCatalogEntry,
|
|
57
|
+
ModelLevel,
|
|
58
|
+
ModelSpec,
|
|
59
|
+
OpencodeCliFeatures,
|
|
60
|
+
OutputEvent,
|
|
61
|
+
OutputFormat,
|
|
62
|
+
ProviderAdapter,
|
|
63
|
+
ProviderAlias,
|
|
64
|
+
ProviderCliFeatures,
|
|
65
|
+
ProviderId,
|
|
66
|
+
RedactionMetadata,
|
|
67
|
+
ResolvedModelSpec,
|
|
68
|
+
ResultEvent,
|
|
69
|
+
TextEvent,
|
|
70
|
+
ThinkingEvent,
|
|
71
|
+
ToolCallEvent,
|
|
72
|
+
ToolResultEvent,
|
|
73
|
+
WarningMetadata,
|
|
74
|
+
} from './types';
|
|
75
|
+
|
|
76
|
+
import type { AgentCliProviderHelperMetadata } from './types';
|
|
77
|
+
|
|
78
|
+
export const agentCliProviderHelperMetadata: Readonly<AgentCliProviderHelperMetadata> = {
|
|
79
|
+
packageName: '@the-open-engine/zeroshot',
|
|
80
|
+
buildOutputDir: 'lib/agent-cli-provider',
|
|
81
|
+
contractVersion: 1,
|
|
82
|
+
adapterVersion: '1',
|
|
83
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { classifyProviderError } from './adapters';
|
|
2
|
+
import type { ProcessResult } from './process-runner';
|
|
3
|
+
import type { ErrorClassification, ProviderAdapter } from './types';
|
|
4
|
+
|
|
5
|
+
export function providerFailureClassification(
|
|
6
|
+
adapter: ProviderAdapter,
|
|
7
|
+
result: ProcessResult
|
|
8
|
+
): ErrorClassification | null {
|
|
9
|
+
if (result.timedOut) {
|
|
10
|
+
return classifyProviderError(adapter.id, {
|
|
11
|
+
message: `Provider timed out after ${result.timeoutMs ?? 'unknown'}ms`,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
if (result.exitCode === 0 && result.signal === null) return null;
|
|
15
|
+
return classifyProviderError(adapter.id, {
|
|
16
|
+
message: result.stderr || result.stdout || `Provider exited with code ${result.exitCode ?? ''}`,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
2
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function parseJson(value: string): unknown {
|
|
6
|
+
const parsed: unknown = JSON.parse(value);
|
|
7
|
+
return parsed;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function tryParseJson(value: string): unknown | null {
|
|
11
|
+
try {
|
|
12
|
+
return parseJson(value);
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getRecord(
|
|
19
|
+
record: Record<string, unknown>,
|
|
20
|
+
key: string
|
|
21
|
+
): Record<string, unknown> | null {
|
|
22
|
+
const value = record[key];
|
|
23
|
+
return isRecord(value) ? value : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getArray(record: Record<string, unknown>, key: string): readonly unknown[] {
|
|
27
|
+
const value = record[key];
|
|
28
|
+
return Array.isArray(value) ? value : [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getString(record: Record<string, unknown>, key: string): string | null {
|
|
32
|
+
const value = record[key];
|
|
33
|
+
return typeof value === 'string' ? value : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getOptionalString(
|
|
37
|
+
record: Record<string, unknown>,
|
|
38
|
+
key: string
|
|
39
|
+
): string | null | undefined {
|
|
40
|
+
const value = record[key];
|
|
41
|
+
if (typeof value === 'string') return value;
|
|
42
|
+
if (value === null) return null;
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getStringFromKeys(
|
|
47
|
+
record: Record<string, unknown>,
|
|
48
|
+
keys: readonly string[]
|
|
49
|
+
): string | null {
|
|
50
|
+
for (const key of keys) {
|
|
51
|
+
const value = getString(record, key);
|
|
52
|
+
if (value !== null) return value;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getOrStringFromKeys(
|
|
58
|
+
record: Record<string, unknown>,
|
|
59
|
+
keys: readonly string[]
|
|
60
|
+
): string | null | undefined {
|
|
61
|
+
for (const key of keys) {
|
|
62
|
+
const value = getOptionalString(record, key);
|
|
63
|
+
if (value) return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const lastKey = keys[keys.length - 1];
|
|
67
|
+
return lastKey === undefined ? undefined : getOptionalString(record, lastKey);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function getOrStringFromKeysWithFallback(
|
|
71
|
+
record: Record<string, unknown>,
|
|
72
|
+
keys: readonly string[],
|
|
73
|
+
fallback: string | null | undefined
|
|
74
|
+
): string | null | undefined {
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
const value = getOptionalString(record, key);
|
|
77
|
+
if (value) return value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return fallback;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getNumber(record: Record<string, unknown>, key: string): number | null {
|
|
84
|
+
const value = record[key];
|
|
85
|
+
return typeof value === 'number' ? value : null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function getBoolean(record: Record<string, unknown>, key: string): boolean | null {
|
|
89
|
+
const value = record[key];
|
|
90
|
+
return typeof value === 'boolean' ? value : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function stringifyJson(value: unknown, space?: number): string {
|
|
94
|
+
const serialized = JSON.stringify(value, null, space);
|
|
95
|
+
if (typeof serialized !== 'string') {
|
|
96
|
+
throw new Error('JSON schema must be serializable.');
|
|
97
|
+
}
|
|
98
|
+
return serialized;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function stringifyContent(value: unknown): string {
|
|
102
|
+
if (typeof value === 'string') return value;
|
|
103
|
+
return stringifyJson(value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function unknownToMessage(error: unknown): string {
|
|
107
|
+
if (error instanceof Error) return error.message;
|
|
108
|
+
if (typeof error === 'string') return error;
|
|
109
|
+
if (isRecord(error)) {
|
|
110
|
+
const message = error.message;
|
|
111
|
+
if (typeof message === 'string' && message) return message;
|
|
112
|
+
}
|
|
113
|
+
return String(error);
|
|
114
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function stripTimestampPrefix(line: string): string {
|
|
2
|
+
let trimmed = line.trim().replace(/\r$/, '');
|
|
3
|
+
if (!trimmed) return '';
|
|
4
|
+
|
|
5
|
+
const timestampMatch = /^\[(\d{13})\](.*)$/.exec(trimmed);
|
|
6
|
+
const timestampRemainder = timestampMatch?.[2];
|
|
7
|
+
if (typeof timestampRemainder === 'string') {
|
|
8
|
+
trimmed = timestampRemainder.trimStart();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
|
|
12
|
+
const pipeMatch = /^[^|]{1,40}\|\s*(.*)$/.exec(trimmed);
|
|
13
|
+
const afterPipe = pipeMatch?.[1]?.trimStart();
|
|
14
|
+
if (typeof afterPipe === 'string' && (afterPipe.startsWith('{') || afterPipe.startsWith('['))) {
|
|
15
|
+
return afterPipe;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return trimmed;
|
|
20
|
+
}
|