@sapiom/agent-core 0.6.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 +246 -0
- package/LICENSE +21 -0
- package/dist/cjs/__tests__/execution-detail.wire.fixture.json +101 -0
- package/dist/cjs/__tests__/execution-projection.preseam.fixture.json +37 -0
- package/dist/cjs/__tests__/execution-projection.with-cost.fixture.json +60 -0
- package/dist/cjs/bundle.d.ts +5 -0
- package/dist/cjs/bundle.js +134 -0
- package/dist/cjs/check.d.ts +10 -0
- package/dist/cjs/check.js +154 -0
- package/dist/cjs/client.d.ts +21 -0
- package/dist/cjs/client.js +108 -0
- package/dist/cjs/clone.d.ts +17 -0
- package/dist/cjs/clone.js +71 -0
- package/dist/cjs/config.d.ts +16 -0
- package/dist/cjs/config.js +40 -0
- package/dist/cjs/decode.d.ts +4 -0
- package/dist/cjs/decode.js +172 -0
- package/dist/cjs/deploy.d.ts +12 -0
- package/dist/cjs/deploy.js +67 -0
- package/dist/cjs/errors.d.ts +15 -0
- package/dist/cjs/errors.js +23 -0
- package/dist/cjs/git.d.ts +12 -0
- package/dist/cjs/git.js +76 -0
- package/dist/cjs/index.d.ts +41 -0
- package/dist/cjs/index.js +72 -0
- package/dist/cjs/inspect.d.ts +40 -0
- package/dist/cjs/inspect.js +99 -0
- package/dist/cjs/link.d.ts +15 -0
- package/dist/cjs/link.js +19 -0
- package/dist/cjs/local/dispatcher.d.ts +37 -0
- package/dist/cjs/local/dispatcher.js +149 -0
- package/dist/cjs/local/load.d.ts +6 -0
- package/dist/cjs/local/load.js +102 -0
- package/dist/cjs/local/run-local.d.ts +32 -0
- package/dist/cjs/local/run-local.js +95 -0
- package/dist/cjs/local/stubs.d.ts +8 -0
- package/dist/cjs/local/stubs.js +35 -0
- package/dist/cjs/run.d.ts +11 -0
- package/dist/cjs/run.js +29 -0
- package/dist/cjs/scaffold.d.ts +24 -0
- package/dist/cjs/scaffold.js +190 -0
- package/dist/cjs/schedule.d.ts +67 -0
- package/dist/cjs/schedule.js +33 -0
- package/dist/cjs/signal.d.ts +12 -0
- package/dist/cjs/signal.js +24 -0
- package/dist/cjs/types.d.ts +99 -0
- package/dist/cjs/types.js +11 -0
- package/dist/cjs/watch.d.ts +10 -0
- package/dist/cjs/watch.js +101 -0
- package/dist/esm/__tests__/execution-detail.wire.fixture.json +101 -0
- package/dist/esm/__tests__/execution-projection.preseam.fixture.json +37 -0
- package/dist/esm/__tests__/execution-projection.with-cost.fixture.json +60 -0
- package/dist/esm/bundle.d.ts +5 -0
- package/dist/esm/bundle.js +95 -0
- package/dist/esm/check.d.ts +10 -0
- package/dist/esm/check.js +115 -0
- package/dist/esm/client.d.ts +21 -0
- package/dist/esm/client.js +103 -0
- package/dist/esm/clone.d.ts +17 -0
- package/dist/esm/clone.js +65 -0
- package/dist/esm/config.d.ts +16 -0
- package/dist/esm/config.js +31 -0
- package/dist/esm/decode.d.ts +4 -0
- package/dist/esm/decode.js +167 -0
- package/dist/esm/deploy.d.ts +12 -0
- package/dist/esm/deploy.js +61 -0
- package/dist/esm/errors.d.ts +15 -0
- package/dist/esm/errors.js +19 -0
- package/dist/esm/git.d.ts +12 -0
- package/dist/esm/git.js +69 -0
- package/dist/esm/index.d.ts +41 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/inspect.d.ts +40 -0
- package/dist/esm/inspect.js +92 -0
- package/dist/esm/link.d.ts +15 -0
- package/dist/esm/link.js +16 -0
- package/dist/esm/local/dispatcher.d.ts +37 -0
- package/dist/esm/local/dispatcher.js +145 -0
- package/dist/esm/local/load.d.ts +6 -0
- package/dist/esm/local/load.js +63 -0
- package/dist/esm/local/run-local.d.ts +32 -0
- package/dist/esm/local/run-local.js +87 -0
- package/dist/esm/local/stubs.d.ts +8 -0
- package/dist/esm/local/stubs.js +31 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/run.d.ts +11 -0
- package/dist/esm/run.js +25 -0
- package/dist/esm/scaffold.d.ts +24 -0
- package/dist/esm/scaffold.js +179 -0
- package/dist/esm/schedule.d.ts +67 -0
- package/dist/esm/schedule.js +26 -0
- package/dist/esm/signal.d.ts +12 -0
- package/dist/esm/signal.js +20 -0
- package/dist/esm/types.d.ts +99 -0
- package/dist/esm/types.js +8 -0
- package/dist/esm/watch.d.ts +10 -0
- package/dist/esm/watch.js +96 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +73 -0
- package/templates/coding-pause/AGENTS.md +48 -0
- package/templates/coding-pause/CLAUDE.md +7 -0
- package/templates/coding-pause/README.md +53 -0
- package/templates/coding-pause/_gitignore +3 -0
- package/templates/coding-pause/index.ts +109 -0
- package/templates/coding-pause/package.json +21 -0
- package/templates/coding-pause/tsconfig.json +12 -0
- package/templates/default/AGENTS.md +48 -0
- package/templates/default/CLAUDE.md +7 -0
- package/templates/default/README.md +30 -0
- package/templates/default/_gitignore +3 -0
- package/templates/default/index.ts +26 -0
- package/templates/default/package.json +21 -0
- package/templates/default/tsconfig.json +12 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const STUB_FILE_VERSION = 1;
|
|
2
|
+
export type StubResponse = unknown;
|
|
3
|
+
export type StepStubs = Record<string, StubResponse | StubResponse[]>;
|
|
4
|
+
export interface StubFile {
|
|
5
|
+
version: number;
|
|
6
|
+
steps: Record<string, StepStubs>;
|
|
7
|
+
}
|
|
8
|
+
export declare function parseStubFile(raw: unknown): StubFile;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AgentOperationError } from '../errors.js';
|
|
2
|
+
export const STUB_FILE_VERSION = 1;
|
|
3
|
+
export function parseStubFile(raw) {
|
|
4
|
+
if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
5
|
+
throw invalid('the stub file must be a JSON object.');
|
|
6
|
+
}
|
|
7
|
+
const obj = raw;
|
|
8
|
+
const version = obj.version ?? STUB_FILE_VERSION;
|
|
9
|
+
if (typeof version !== 'number') {
|
|
10
|
+
throw invalid('`version` must be a number.');
|
|
11
|
+
}
|
|
12
|
+
const stepsRaw = obj.steps ?? {};
|
|
13
|
+
if (stepsRaw == null || typeof stepsRaw !== 'object' || Array.isArray(stepsRaw)) {
|
|
14
|
+
throw invalid('`steps` must be an object keyed by step name.');
|
|
15
|
+
}
|
|
16
|
+
const steps = {};
|
|
17
|
+
for (const [stepName, stepStubs] of Object.entries(stepsRaw)) {
|
|
18
|
+
if (stepStubs == null || typeof stepStubs !== 'object' || Array.isArray(stepStubs)) {
|
|
19
|
+
throw invalid(`steps.${stepName} must be an object keyed by capability path.`);
|
|
20
|
+
}
|
|
21
|
+
steps[stepName] = stepStubs;
|
|
22
|
+
}
|
|
23
|
+
return { version, steps };
|
|
24
|
+
}
|
|
25
|
+
function invalid(detail) {
|
|
26
|
+
return new AgentOperationError({
|
|
27
|
+
code: 'STUBS_INVALID',
|
|
28
|
+
message: `Invalid stub file: ${detail}`,
|
|
29
|
+
hint: 'Expected { "version": 1, "steps": { "<step>": { "<capability.path>": <response> | [<response>] } } }.',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type": "module"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GatewayClient } from './client.js';
|
|
2
|
+
export interface RunOptions {
|
|
3
|
+
definitionId: string;
|
|
4
|
+
input?: unknown;
|
|
5
|
+
}
|
|
6
|
+
export interface RunResult {
|
|
7
|
+
executionId: string;
|
|
8
|
+
raw: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export declare function run(opts: RunOptions, client: GatewayClient): Promise<RunResult>;
|
|
11
|
+
export declare function parseJsonInput(raw: string): unknown;
|
package/dist/esm/run.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AgentOperationError } from './errors.js';
|
|
2
|
+
export async function run(opts, client) {
|
|
3
|
+
const { definitionId, input = {} } = opts;
|
|
4
|
+
const res = await client.post('/executions', { definitionId, input });
|
|
5
|
+
const executionId = res.executionId ?? res.id;
|
|
6
|
+
if (!executionId) {
|
|
7
|
+
throw new AgentOperationError({
|
|
8
|
+
code: 'RUN_NO_ID',
|
|
9
|
+
message: 'The execution was started but no execution id was returned.',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return { executionId, raw: res };
|
|
13
|
+
}
|
|
14
|
+
export function parseJsonInput(raw) {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(raw);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new AgentOperationError({
|
|
20
|
+
code: 'BAD_INPUT',
|
|
21
|
+
message: 'Input is not valid JSON.',
|
|
22
|
+
hint: 'Pass a valid JSON string, e.g. \'{"key":"value"}\'',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const DEFAULT_TEMPLATE = "default";
|
|
2
|
+
export interface ResolvedVersions {
|
|
3
|
+
agent: string;
|
|
4
|
+
tools: string;
|
|
5
|
+
zod: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function registryFor(pkg: string): string;
|
|
8
|
+
export declare function resolveVersions(): Promise<ResolvedVersions>;
|
|
9
|
+
export declare function listTemplates(templatesDir?: string): string[];
|
|
10
|
+
export declare function resolveTemplate(name: string, templatesDir?: string): string;
|
|
11
|
+
export interface ScaffoldOptions {
|
|
12
|
+
targetDir: string;
|
|
13
|
+
template?: string;
|
|
14
|
+
projectName?: string;
|
|
15
|
+
templatesDir?: string;
|
|
16
|
+
versions?: ResolvedVersions;
|
|
17
|
+
}
|
|
18
|
+
export interface ScaffoldResult {
|
|
19
|
+
targetDir: string;
|
|
20
|
+
template: string;
|
|
21
|
+
projectName: string;
|
|
22
|
+
gitInitialized: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function scaffold(opts: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { AgentOperationError } from "./errors.js";
|
|
6
|
+
function resolveModuleDir() {
|
|
7
|
+
if (typeof __dirname !== "undefined")
|
|
8
|
+
return __dirname;
|
|
9
|
+
try {
|
|
10
|
+
const metaUrl = eval("import.meta.url");
|
|
11
|
+
if (typeof metaUrl === "string")
|
|
12
|
+
return path.dirname(fileURLToPath(metaUrl));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
}
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
const moduleDir = resolveModuleDir();
|
|
19
|
+
function getTemplatesDir(override) {
|
|
20
|
+
return (override ??
|
|
21
|
+
process.env.SAPIOM_TEMPLATES_DIR ??
|
|
22
|
+
path.resolve(moduleDir, "..", "..", "templates"));
|
|
23
|
+
}
|
|
24
|
+
export const DEFAULT_TEMPLATE = "default";
|
|
25
|
+
const DOTFILE_NAMES = new Set(["_gitignore", "_npmrc"]);
|
|
26
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
27
|
+
const VERSION_FALLBACK = {
|
|
28
|
+
agent: "0.1.1",
|
|
29
|
+
tools: "0.1.1",
|
|
30
|
+
};
|
|
31
|
+
const ZOD_VERSION = "4.1.12";
|
|
32
|
+
export function registryFor(pkg) {
|
|
33
|
+
const scope = pkg.startsWith("@") ? pkg.slice(0, pkg.indexOf("/")) : null;
|
|
34
|
+
const scoped = scope
|
|
35
|
+
? process.env[`npm_config_${scope}:registry`]
|
|
36
|
+
: undefined;
|
|
37
|
+
const registry = scoped || process.env.npm_config_registry || DEFAULT_REGISTRY;
|
|
38
|
+
return registry.replace(/\/+$/, "");
|
|
39
|
+
}
|
|
40
|
+
async function latestNpmVersion(pkg) {
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(`${registryFor(pkg)}/${encodeURIComponent(pkg)}/latest`, {
|
|
43
|
+
signal: AbortSignal.timeout(5000),
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok)
|
|
46
|
+
return null;
|
|
47
|
+
const json = (await res.json());
|
|
48
|
+
return typeof json.version === "string" ? json.version : null;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function resolveVersions() {
|
|
55
|
+
const [agent, tools] = await Promise.all([
|
|
56
|
+
latestNpmVersion("@sapiom/agent"),
|
|
57
|
+
latestNpmVersion("@sapiom/tools"),
|
|
58
|
+
]);
|
|
59
|
+
return {
|
|
60
|
+
agent: agent ?? VERSION_FALLBACK.agent,
|
|
61
|
+
tools: tools ?? VERSION_FALLBACK.tools,
|
|
62
|
+
zod: ZOD_VERSION,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function listTemplates(templatesDir) {
|
|
66
|
+
const root = getTemplatesDir(templatesDir);
|
|
67
|
+
if (!existsSync(root))
|
|
68
|
+
return [];
|
|
69
|
+
return readdirSync(root).filter((name) => statSync(path.join(root, name)).isDirectory());
|
|
70
|
+
}
|
|
71
|
+
export function resolveTemplate(name, templatesDir) {
|
|
72
|
+
const dir = path.join(getTemplatesDir(templatesDir), name);
|
|
73
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) {
|
|
74
|
+
const available = listTemplates(templatesDir);
|
|
75
|
+
throw new AgentOperationError({
|
|
76
|
+
code: "UNKNOWN_TEMPLATE",
|
|
77
|
+
message: `Unknown template '${name}'.` +
|
|
78
|
+
(available.length
|
|
79
|
+
? ` Available: ${available.join(", ")}.`
|
|
80
|
+
: " No templates are bundled."),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return dir;
|
|
84
|
+
}
|
|
85
|
+
function applyReplacements(file, replacements) {
|
|
86
|
+
let content;
|
|
87
|
+
try {
|
|
88
|
+
content = readFileSync(file, "utf8");
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let changed = false;
|
|
94
|
+
for (const [token, value] of Object.entries(replacements)) {
|
|
95
|
+
if (content.includes(token)) {
|
|
96
|
+
content = content.split(token).join(value);
|
|
97
|
+
changed = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (changed)
|
|
101
|
+
writeFileSync(file, content);
|
|
102
|
+
}
|
|
103
|
+
function walk(dir, onFile) {
|
|
104
|
+
for (const entry of readdirSync(dir)) {
|
|
105
|
+
const full = path.join(dir, entry);
|
|
106
|
+
if (statSync(full).isDirectory())
|
|
107
|
+
walk(full, onFile);
|
|
108
|
+
else
|
|
109
|
+
onFile(full);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function copyTemplate(templateDir, targetDir, replacements) {
|
|
113
|
+
cpSync(templateDir, targetDir, { recursive: true });
|
|
114
|
+
walk(targetDir, (file) => {
|
|
115
|
+
const base = path.basename(file);
|
|
116
|
+
if (DOTFILE_NAMES.has(base)) {
|
|
117
|
+
const dotted = path.join(path.dirname(file), "." + base.slice(1));
|
|
118
|
+
renameSync(file, dotted);
|
|
119
|
+
applyReplacements(dotted, replacements);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
applyReplacements(file, replacements);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function initGitRepo(dir) {
|
|
126
|
+
const tryGit = (args) => {
|
|
127
|
+
try {
|
|
128
|
+
execFileSync("git", args, { cwd: dir, stdio: "ignore" });
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
if (!tryGit(["init"]))
|
|
136
|
+
return false;
|
|
137
|
+
tryGit(["add", "-A"]);
|
|
138
|
+
return (tryGit(["commit", "-m", "Initial commit"]) ||
|
|
139
|
+
tryGit([
|
|
140
|
+
"-c",
|
|
141
|
+
"user.name=Sapiom",
|
|
142
|
+
"-c",
|
|
143
|
+
"user.email=noreply@sapiom.ai",
|
|
144
|
+
"commit",
|
|
145
|
+
"-m",
|
|
146
|
+
"Initial commit",
|
|
147
|
+
]));
|
|
148
|
+
}
|
|
149
|
+
export async function scaffold(opts) {
|
|
150
|
+
const { targetDir } = opts;
|
|
151
|
+
const template = opts.template ?? DEFAULT_TEMPLATE;
|
|
152
|
+
const projectName = opts.projectName ?? path.basename(targetDir);
|
|
153
|
+
if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {
|
|
154
|
+
throw new AgentOperationError({
|
|
155
|
+
code: "DIR_NOT_EMPTY",
|
|
156
|
+
message: `Target directory '${targetDir}' already exists and is not empty.`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const templateDir = resolveTemplate(template, opts.templatesDir);
|
|
160
|
+
const versions = opts.versions ?? (await resolveVersions());
|
|
161
|
+
mkdirSync(targetDir, { recursive: true });
|
|
162
|
+
copyTemplate(templateDir, targetDir, {
|
|
163
|
+
__PROJECT_NAME__: projectName,
|
|
164
|
+
__AGENT_VERSION__: versions.agent,
|
|
165
|
+
__TOOLS_VERSION__: versions.tools,
|
|
166
|
+
__ZOD_VERSION__: versions.zod,
|
|
167
|
+
});
|
|
168
|
+
if (!opts.versions) {
|
|
169
|
+
const sapiomRegistry = registryFor("@sapiom/tools");
|
|
170
|
+
if (sapiomRegistry !== DEFAULT_REGISTRY) {
|
|
171
|
+
writeFileSync(path.join(targetDir, ".npmrc"), `@sapiom:registry=${sapiomRegistry}\n`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const devDir = path.join(targetDir, ".sapiom-dev");
|
|
175
|
+
mkdirSync(devDir, { recursive: true });
|
|
176
|
+
writeFileSync(path.join(devDir, "stubs.json"), JSON.stringify({ version: 1, steps: {} }, null, 2) + "\n");
|
|
177
|
+
const gitInitialized = initGitRepo(targetDir);
|
|
178
|
+
return { targetDir, template, projectName, gitInitialized };
|
|
179
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { GatewayClient } from './client.js';
|
|
2
|
+
export type ScheduleKind = 'schedule_cron' | 'schedule_once';
|
|
3
|
+
export type ScheduleStatus = 'active' | 'paused' | 'completed' | 'disabled';
|
|
4
|
+
export interface SchedulePolicy {
|
|
5
|
+
catchupPolicy?: 'skip' | 'all';
|
|
6
|
+
overlapPolicy?: 'allow' | 'skip';
|
|
7
|
+
jitterMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface CreateScheduleOptions {
|
|
10
|
+
definition: string;
|
|
11
|
+
kind: ScheduleKind;
|
|
12
|
+
input?: unknown;
|
|
13
|
+
cron?: string;
|
|
14
|
+
timezone?: string;
|
|
15
|
+
startAt?: string;
|
|
16
|
+
endAt?: string;
|
|
17
|
+
policy?: SchedulePolicy;
|
|
18
|
+
at?: string | Date;
|
|
19
|
+
}
|
|
20
|
+
export interface ScheduleFireRecord {
|
|
21
|
+
scheduledFor: string;
|
|
22
|
+
state: string;
|
|
23
|
+
firedAt: string | null;
|
|
24
|
+
executionId: string | null;
|
|
25
|
+
error?: unknown;
|
|
26
|
+
}
|
|
27
|
+
export interface ScheduleSummary {
|
|
28
|
+
id: string;
|
|
29
|
+
kind: ScheduleKind;
|
|
30
|
+
status: ScheduleStatus;
|
|
31
|
+
definitionSlug: string;
|
|
32
|
+
cron: string | null;
|
|
33
|
+
timezone: string | null;
|
|
34
|
+
nextFireAt: string | null;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ScheduleDetail extends ScheduleSummary {
|
|
38
|
+
input: unknown;
|
|
39
|
+
startAt: string | null;
|
|
40
|
+
endAt: string | null;
|
|
41
|
+
policy: unknown;
|
|
42
|
+
recentFires: ScheduleFireRecord[];
|
|
43
|
+
}
|
|
44
|
+
export interface ListSchedulesOptions {
|
|
45
|
+
definition: string;
|
|
46
|
+
status?: ScheduleStatus;
|
|
47
|
+
limit?: number;
|
|
48
|
+
offset?: number;
|
|
49
|
+
}
|
|
50
|
+
export interface CronPreviewOptions {
|
|
51
|
+
cron: string;
|
|
52
|
+
timezone?: string;
|
|
53
|
+
count?: number;
|
|
54
|
+
}
|
|
55
|
+
export interface CronPreview {
|
|
56
|
+
cron: string;
|
|
57
|
+
timezone: string;
|
|
58
|
+
occurrences: string[];
|
|
59
|
+
}
|
|
60
|
+
export declare function createSchedule(opts: CreateScheduleOptions, client: GatewayClient): Promise<ScheduleDetail>;
|
|
61
|
+
export declare function listSchedules(opts: ListSchedulesOptions, client: GatewayClient): Promise<ScheduleSummary[]>;
|
|
62
|
+
export declare function getSchedule(id: string, client: GatewayClient): Promise<ScheduleDetail>;
|
|
63
|
+
export declare function cancelSchedule(id: string, client: GatewayClient): Promise<{
|
|
64
|
+
id: string;
|
|
65
|
+
status: ScheduleStatus;
|
|
66
|
+
}>;
|
|
67
|
+
export declare function previewCron(opts: CronPreviewOptions, client: GatewayClient): Promise<CronPreview>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export async function createSchedule(opts, client) {
|
|
2
|
+
const { definition, ...body } = opts;
|
|
3
|
+
return client.post(`/${encodeURIComponent(definition)}/triggers`, body);
|
|
4
|
+
}
|
|
5
|
+
export async function listSchedules(opts, client) {
|
|
6
|
+
const { definition, ...filters } = opts;
|
|
7
|
+
return client.get(`/${encodeURIComponent(definition)}/triggers${toQuery(filters)}`);
|
|
8
|
+
}
|
|
9
|
+
export async function getSchedule(id, client) {
|
|
10
|
+
return client.get(`/triggers/${encodeURIComponent(id)}`);
|
|
11
|
+
}
|
|
12
|
+
export async function cancelSchedule(id, client) {
|
|
13
|
+
return client.request('DELETE', `/triggers/${encodeURIComponent(id)}`);
|
|
14
|
+
}
|
|
15
|
+
export async function previewCron(opts, client) {
|
|
16
|
+
return client.post('/triggers/preview-cron', opts);
|
|
17
|
+
}
|
|
18
|
+
function toQuery(filters) {
|
|
19
|
+
const params = new URLSearchParams();
|
|
20
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
21
|
+
if (value !== undefined && value !== null)
|
|
22
|
+
params.set(key, String(value));
|
|
23
|
+
}
|
|
24
|
+
const qs = params.toString();
|
|
25
|
+
return qs ? `?${qs}` : '';
|
|
26
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GatewayClient } from './client.js';
|
|
2
|
+
export interface SignalOptions {
|
|
3
|
+
executionId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
correlationId: string;
|
|
6
|
+
payload?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface SignalResult {
|
|
9
|
+
matched: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function signal(opts: SignalOptions, client: GatewayClient): Promise<SignalResult>;
|
|
12
|
+
export declare function parseSignalPayload(raw: string): unknown;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AgentOperationError } from './errors.js';
|
|
2
|
+
export async function signal(opts, client) {
|
|
3
|
+
const res = await client.post(`/executions/${opts.executionId}/signals`, {
|
|
4
|
+
name: opts.name,
|
|
5
|
+
correlationId: opts.correlationId,
|
|
6
|
+
payload: opts.payload,
|
|
7
|
+
});
|
|
8
|
+
return { matched: res.matched ?? 0 };
|
|
9
|
+
}
|
|
10
|
+
export function parseSignalPayload(raw) {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(raw);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new AgentOperationError({
|
|
16
|
+
code: 'BAD_PAYLOAD',
|
|
17
|
+
message: 'Signal payload is not valid JSON.',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export type SettleState = "pending" | "settling" | "final";
|
|
2
|
+
export interface CostNode {
|
|
3
|
+
authorizedUsd: string;
|
|
4
|
+
capturedUsd: string;
|
|
5
|
+
settleState: SettleState;
|
|
6
|
+
}
|
|
7
|
+
export interface StepEvent {
|
|
8
|
+
sourceId: string;
|
|
9
|
+
sequence: number;
|
|
10
|
+
kind: string;
|
|
11
|
+
payload: Record<string, unknown>;
|
|
12
|
+
eventTs: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface StepErrorFrame {
|
|
15
|
+
function?: string;
|
|
16
|
+
file?: string;
|
|
17
|
+
line?: number;
|
|
18
|
+
column?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface StepErrorTrace {
|
|
21
|
+
frames: StepErrorFrame[];
|
|
22
|
+
sourceMapped: boolean;
|
|
23
|
+
raw?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface StepError {
|
|
26
|
+
message: string;
|
|
27
|
+
trace: StepErrorTrace | null;
|
|
28
|
+
traceUnavailableReason: string | null;
|
|
29
|
+
}
|
|
30
|
+
export declare const SSE_EVENT_TYPES: readonly ["step.started", "step.captured", "cost.updated", "run.paused", "run.resumed", "run.terminal"];
|
|
31
|
+
export type SseEventType = (typeof SSE_EVENT_TYPES)[number];
|
|
32
|
+
export interface SseEvent {
|
|
33
|
+
type: SseEventType;
|
|
34
|
+
executionId: string;
|
|
35
|
+
traceRoot: string | null;
|
|
36
|
+
nodeId: string | null;
|
|
37
|
+
}
|
|
38
|
+
export interface ExecutionRef {
|
|
39
|
+
executionId: string;
|
|
40
|
+
traceRoot: string;
|
|
41
|
+
name: string;
|
|
42
|
+
status: string;
|
|
43
|
+
}
|
|
44
|
+
export interface DispatchRef {
|
|
45
|
+
childExecutionId: string;
|
|
46
|
+
targetType: string;
|
|
47
|
+
correlationId: string;
|
|
48
|
+
status: string;
|
|
49
|
+
}
|
|
50
|
+
export interface StepProjection {
|
|
51
|
+
stepName: string;
|
|
52
|
+
stepOrder: number;
|
|
53
|
+
attempt: number;
|
|
54
|
+
status: string;
|
|
55
|
+
spanId: string | null;
|
|
56
|
+
startedAt: string | null;
|
|
57
|
+
finishedAt: string | null;
|
|
58
|
+
input: unknown;
|
|
59
|
+
output: unknown;
|
|
60
|
+
sharedStateAfter: Record<string, unknown> | null;
|
|
61
|
+
nextDirective: unknown;
|
|
62
|
+
cost: CostNode | null;
|
|
63
|
+
logs: unknown;
|
|
64
|
+
events: StepEvent[];
|
|
65
|
+
error: StepError | null;
|
|
66
|
+
dispatch: DispatchRef | null;
|
|
67
|
+
}
|
|
68
|
+
export interface ExecutionProjection {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
organizationId: string | null;
|
|
72
|
+
tenantId: string | null;
|
|
73
|
+
status: string;
|
|
74
|
+
currentStep: string | null;
|
|
75
|
+
currentStepAttempt: number;
|
|
76
|
+
version: number;
|
|
77
|
+
definitionId: string | null;
|
|
78
|
+
buildRunId: string | null;
|
|
79
|
+
idempotencyKey: string | null;
|
|
80
|
+
pausedSignalName: string | null;
|
|
81
|
+
pausedSignalCorrelationId: string | null;
|
|
82
|
+
pausedUntil: string | null;
|
|
83
|
+
startedAt: string;
|
|
84
|
+
finishedAt: string | null;
|
|
85
|
+
input: unknown;
|
|
86
|
+
sharedState: Record<string, unknown>;
|
|
87
|
+
output: unknown;
|
|
88
|
+
error: unknown;
|
|
89
|
+
pausedStepInputSchema: Record<string, unknown> | null;
|
|
90
|
+
pausedStepInputExample: unknown;
|
|
91
|
+
traceRoot: string;
|
|
92
|
+
rootExecutionId: string;
|
|
93
|
+
traceParent: string | null;
|
|
94
|
+
parentExecutionId: string | null;
|
|
95
|
+
traceId: string | null;
|
|
96
|
+
children: ExecutionRef[];
|
|
97
|
+
cost: CostNode | null;
|
|
98
|
+
steps: StepProjection[];
|
|
99
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GatewayClient } from "./client.js";
|
|
2
|
+
import { type SseEvent } from "./types.js";
|
|
3
|
+
export interface WatchExecutionOptions {
|
|
4
|
+
executionId: string;
|
|
5
|
+
lastEventId?: string;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
}
|
|
8
|
+
export declare function watchExecution(opts: WatchExecutionOptions, client: GatewayClient): AsyncGenerator<SseEvent>;
|
|
9
|
+
export declare function parseSseFrame(raw: string): SseEvent | null;
|
|
10
|
+
export declare function parseSseEvent(raw: string): SseEvent | null;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { SSE_EVENT_TYPES } from "./types.js";
|
|
2
|
+
export async function* watchExecution(opts, client) {
|
|
3
|
+
const controller = new AbortController();
|
|
4
|
+
const onExternalAbort = () => controller.abort();
|
|
5
|
+
if (opts.signal) {
|
|
6
|
+
if (opts.signal.aborted)
|
|
7
|
+
controller.abort();
|
|
8
|
+
else
|
|
9
|
+
opts.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
10
|
+
}
|
|
11
|
+
const path = `/executions/${encodeURIComponent(opts.executionId)}/stream`;
|
|
12
|
+
try {
|
|
13
|
+
const res = await client.openStream(path, {
|
|
14
|
+
signal: controller.signal,
|
|
15
|
+
lastEventId: opts.lastEventId,
|
|
16
|
+
});
|
|
17
|
+
const reader = res.body.getReader();
|
|
18
|
+
const decoder = new TextDecoder();
|
|
19
|
+
let buffer = "";
|
|
20
|
+
try {
|
|
21
|
+
for (;;) {
|
|
22
|
+
const { done, value } = await reader.read();
|
|
23
|
+
if (done)
|
|
24
|
+
break;
|
|
25
|
+
buffer += decoder.decode(value, { stream: true });
|
|
26
|
+
for (;;) {
|
|
27
|
+
const boundary = indexOfFrameBoundary(buffer);
|
|
28
|
+
if (boundary === -1)
|
|
29
|
+
break;
|
|
30
|
+
const raw = buffer.slice(0, boundary.at);
|
|
31
|
+
buffer = buffer.slice(boundary.after);
|
|
32
|
+
const event = parseSseFrame(raw);
|
|
33
|
+
if (event)
|
|
34
|
+
yield event;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
reader.releaseLock();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
controller.abort();
|
|
44
|
+
if (opts.signal)
|
|
45
|
+
opts.signal.removeEventListener("abort", onExternalAbort);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function indexOfFrameBoundary(buffer) {
|
|
49
|
+
const lf = buffer.indexOf("\n\n");
|
|
50
|
+
const crlf = buffer.indexOf("\r\n\r\n");
|
|
51
|
+
if (lf === -1 && crlf === -1)
|
|
52
|
+
return -1;
|
|
53
|
+
if (crlf !== -1 && (lf === -1 || crlf < lf))
|
|
54
|
+
return { at: crlf, after: crlf + 4 };
|
|
55
|
+
return { at: lf, after: lf + 2 };
|
|
56
|
+
}
|
|
57
|
+
export function parseSseFrame(raw) {
|
|
58
|
+
const dataLines = [];
|
|
59
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
60
|
+
if (!line || line.startsWith(":"))
|
|
61
|
+
continue;
|
|
62
|
+
const colon = line.indexOf(":");
|
|
63
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
64
|
+
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
65
|
+
if (value.startsWith(" "))
|
|
66
|
+
value = value.slice(1);
|
|
67
|
+
if (field === "data")
|
|
68
|
+
dataLines.push(value);
|
|
69
|
+
}
|
|
70
|
+
if (dataLines.length === 0)
|
|
71
|
+
return null;
|
|
72
|
+
return parseSseEvent(dataLines.join("\n"));
|
|
73
|
+
}
|
|
74
|
+
export function parseSseEvent(raw) {
|
|
75
|
+
let parsed;
|
|
76
|
+
try {
|
|
77
|
+
parsed = JSON.parse(raw);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if (!parsed || typeof parsed !== "object")
|
|
83
|
+
return null;
|
|
84
|
+
const m = parsed;
|
|
85
|
+
if (typeof m.type !== "string" || !SSE_EVENT_TYPES.includes(m.type)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
if (typeof m.executionId !== "string" || m.executionId.length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
return {
|
|
91
|
+
type: m.type,
|
|
92
|
+
executionId: m.executionId,
|
|
93
|
+
traceRoot: typeof m.traceRoot === "string" ? m.traceRoot : null,
|
|
94
|
+
nodeId: typeof m.nodeId === "string" ? m.nodeId : null,
|
|
95
|
+
};
|
|
96
|
+
}
|