@sapiom/orchestration-core 0.1.1
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 +9 -0
- package/LICENSE +21 -0
- package/dist/cjs/check.d.ts +10 -0
- package/dist/cjs/check.js +154 -0
- package/dist/cjs/client.d.ts +17 -0
- package/dist/cjs/client.js +70 -0
- package/dist/cjs/config.d.ts +9 -0
- package/dist/cjs/config.js +38 -0
- package/dist/cjs/deploy.d.ts +12 -0
- package/dist/cjs/deploy.js +51 -0
- package/dist/cjs/errors.d.ts +15 -0
- package/dist/cjs/errors.js +23 -0
- package/dist/cjs/git.d.ts +2 -0
- package/dist/cjs/git.js +44 -0
- package/dist/cjs/index.d.ts +29 -0
- package/dist/cjs/index.js +50 -0
- package/dist/cjs/inspect.d.ts +41 -0
- package/dist/cjs/inspect.js +17 -0
- package/dist/cjs/link.d.ts +15 -0
- package/dist/cjs/link.js +19 -0
- package/dist/cjs/local/dispatcher.d.ts +35 -0
- package/dist/cjs/local/dispatcher.js +115 -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 +26 -0
- package/dist/cjs/local/run-local.js +73 -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 +22 -0
- package/dist/cjs/scaffold.js +145 -0
- package/dist/cjs/signal.d.ts +12 -0
- package/dist/cjs/signal.js +24 -0
- package/dist/esm/check.d.ts +10 -0
- package/dist/esm/check.js +115 -0
- package/dist/esm/client.d.ts +17 -0
- package/dist/esm/client.js +65 -0
- package/dist/esm/config.d.ts +9 -0
- package/dist/esm/config.js +29 -0
- package/dist/esm/deploy.d.ts +12 -0
- package/dist/esm/deploy.js +48 -0
- package/dist/esm/errors.d.ts +15 -0
- package/dist/esm/errors.js +19 -0
- package/dist/esm/git.d.ts +2 -0
- package/dist/esm/git.js +40 -0
- package/dist/esm/index.d.ts +29 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/inspect.d.ts +41 -0
- package/dist/esm/inspect.js +12 -0
- package/dist/esm/link.d.ts +15 -0
- package/dist/esm/link.js +16 -0
- package/dist/esm/local/dispatcher.d.ts +35 -0
- package/dist/esm/local/dispatcher.js +111 -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 +26 -0
- package/dist/esm/local/run-local.js +65 -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 +22 -0
- package/dist/esm/scaffold.js +135 -0
- package/dist/esm/signal.d.ts +12 -0
- package/dist/esm/signal.js +20 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +73 -0
- package/templates/default/AGENTS.md +29 -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,115 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { assertValidGraph, buildManifest, isOrchestrationDefinition, workflowManifestSchema } from '@sapiom/orchestration';
|
|
7
|
+
import * as esbuild from 'esbuild';
|
|
8
|
+
import { OrchestrationError } from './errors.js';
|
|
9
|
+
function runTypecheck(sourceDir) {
|
|
10
|
+
const tscBin = path.join(sourceDir, 'node_modules', '.bin', 'tsc');
|
|
11
|
+
if (!existsSync(tscBin)) {
|
|
12
|
+
return 'typecheck skipped — TypeScript is not installed (run npm install first)';
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
execFileSync(tscBin, ['--noEmit'], { cwd: sourceDir, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const e = err;
|
|
20
|
+
const output = (e.stdout?.toString() ?? '').trim() || (e.stderr?.toString() ?? '').trim();
|
|
21
|
+
throw new OrchestrationError({
|
|
22
|
+
code: 'TYPECHECK_FAILED',
|
|
23
|
+
message: 'The orchestration has type errors.',
|
|
24
|
+
hint: output || 'Run `tsc --noEmit` for details.',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const LOCAL_SDK_VERSION = '0.0.0-local';
|
|
29
|
+
export async function check(opts) {
|
|
30
|
+
const { sourceDir } = opts;
|
|
31
|
+
const entryFile = path.join(sourceDir, 'index.ts');
|
|
32
|
+
if (!existsSync(entryFile)) {
|
|
33
|
+
throw new OrchestrationError({
|
|
34
|
+
code: 'NO_ENTRY',
|
|
35
|
+
message: `No index.ts found in ${sourceDir}.`,
|
|
36
|
+
hint: 'Run this from an orchestration project, or pass its directory.',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const warnings = [];
|
|
40
|
+
const typecheckSkip = runTypecheck(sourceDir);
|
|
41
|
+
if (typecheckSkip)
|
|
42
|
+
warnings.push(typecheckSkip);
|
|
43
|
+
const tmp = mkdtempSync(path.join(tmpdir(), 'sapiom-check-'));
|
|
44
|
+
const bundlePath = path.join(tmp, 'definition.mjs');
|
|
45
|
+
try {
|
|
46
|
+
try {
|
|
47
|
+
await esbuild.build({
|
|
48
|
+
entryPoints: [entryFile],
|
|
49
|
+
outfile: bundlePath,
|
|
50
|
+
bundle: true,
|
|
51
|
+
platform: 'node',
|
|
52
|
+
target: 'node20',
|
|
53
|
+
format: 'esm',
|
|
54
|
+
logLevel: 'silent',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new OrchestrationError({
|
|
59
|
+
code: 'BUNDLE_FAILED',
|
|
60
|
+
message: 'Failed to bundle the orchestration.',
|
|
61
|
+
hint: err instanceof Error ? err.message : String(err),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const mod = await import(`file://${bundlePath}?t=${Date.now()}`);
|
|
65
|
+
const defs = [];
|
|
66
|
+
for (const value of Object.values(mod)) {
|
|
67
|
+
if (isOrchestrationDefinition(value) && !defs.includes(value))
|
|
68
|
+
defs.push(value);
|
|
69
|
+
}
|
|
70
|
+
if (defs.length === 0) {
|
|
71
|
+
throw new OrchestrationError({
|
|
72
|
+
code: 'NO_DEFINITION',
|
|
73
|
+
message: 'No orchestration was exported from index.ts.',
|
|
74
|
+
hint: 'Export the result of defineOrchestration({ … }).',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (defs.length > 1) {
|
|
78
|
+
throw new OrchestrationError({
|
|
79
|
+
code: 'MULTIPLE_DEFINITIONS',
|
|
80
|
+
message: 'index.ts exports more than one orchestration.',
|
|
81
|
+
hint: 'Export exactly one defineOrchestration({ … }) result.',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const def = defs[0];
|
|
85
|
+
const sha256 = createHash('sha256').update(readFileSync(bundlePath)).digest('hex');
|
|
86
|
+
let manifest;
|
|
87
|
+
try {
|
|
88
|
+
manifest = workflowManifestSchema.parse(buildManifest(def, { sdkVersion: LOCAL_SDK_VERSION, artifact: { sha256, entryFile: 'definition.mjs' } }));
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
throw new OrchestrationError({
|
|
92
|
+
code: 'MANIFEST_INVALID',
|
|
93
|
+
message: 'The orchestration produced an invalid manifest.',
|
|
94
|
+
hint: err instanceof Error ? err.message : String(err),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
warnings.push(...assertValidGraph(manifest));
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
throw new OrchestrationError({
|
|
102
|
+
code: 'GRAPH_INVALID',
|
|
103
|
+
message: 'The orchestration graph is invalid.',
|
|
104
|
+
hint: err instanceof Error ? err.message : String(err),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const steps = manifest.steps;
|
|
108
|
+
const stepCount = Array.isArray(steps) ? steps.length : Object.keys(steps ?? {}).length;
|
|
109
|
+
const name = manifest.name ?? 'orchestration';
|
|
110
|
+
return { name, stepCount, warnings, manifest };
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const DEFAULT_WORKFLOWS_HOST = "https://api.sapiom.ai";
|
|
2
|
+
export interface ClientOptions {
|
|
3
|
+
host?: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
}
|
|
6
|
+
export interface GatewayErrorBody {
|
|
7
|
+
message?: string | string[];
|
|
8
|
+
}
|
|
9
|
+
export declare class GatewayClient {
|
|
10
|
+
private readonly base;
|
|
11
|
+
private readonly apiKey;
|
|
12
|
+
constructor(opts: ClientOptions);
|
|
13
|
+
request<T = unknown>(method: string, path: string, body?: unknown): Promise<T>;
|
|
14
|
+
get<T = unknown>(path: string): Promise<T>;
|
|
15
|
+
post<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
16
|
+
}
|
|
17
|
+
export declare function createClient(opts: ClientOptions): GatewayClient;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { OrchestrationError } from './errors.js';
|
|
2
|
+
export const DEFAULT_WORKFLOWS_HOST = 'https://api.sapiom.ai';
|
|
3
|
+
export class GatewayClient {
|
|
4
|
+
constructor(opts) {
|
|
5
|
+
const host = (opts.host ?? DEFAULT_WORKFLOWS_HOST).replace(/\/$/, '');
|
|
6
|
+
this.base = `${host}/v1/workflows`;
|
|
7
|
+
this.apiKey = opts.apiKey;
|
|
8
|
+
}
|
|
9
|
+
async request(method, path, body) {
|
|
10
|
+
let res;
|
|
11
|
+
try {
|
|
12
|
+
res = await fetch(`${this.base}${path}`, {
|
|
13
|
+
method,
|
|
14
|
+
headers: { 'x-api-key': this.apiKey, 'content-type': 'application/json' },
|
|
15
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
throw new OrchestrationError({
|
|
20
|
+
code: 'NETWORK',
|
|
21
|
+
message: `Could not reach ${this.base}.`,
|
|
22
|
+
hint: err instanceof Error ? err.message : String(err),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const text = await res.text();
|
|
26
|
+
const data = text ? safeParse(text) : undefined;
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
throw new OrchestrationError({
|
|
29
|
+
code: `HTTP_${res.status}`,
|
|
30
|
+
message: messageFrom(data) ?? `Request failed (${res.status} ${res.statusText}).`,
|
|
31
|
+
hint: res.status === 401 || res.status === 403
|
|
32
|
+
? 'Check your API key (`sapiom login` or SAPIOM_API_KEY) and that it has access to this orchestration.'
|
|
33
|
+
: undefined,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
get(path) {
|
|
39
|
+
return this.request('GET', path);
|
|
40
|
+
}
|
|
41
|
+
post(path, body) {
|
|
42
|
+
return this.request('POST', path, body);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function createClient(opts) {
|
|
46
|
+
return new GatewayClient(opts);
|
|
47
|
+
}
|
|
48
|
+
function safeParse(text) {
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(text);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return text;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function messageFrom(data) {
|
|
57
|
+
if (data && typeof data === 'object' && 'message' in data) {
|
|
58
|
+
const m = data.message;
|
|
59
|
+
if (Array.isArray(m))
|
|
60
|
+
return m.join('; ');
|
|
61
|
+
if (typeof m === 'string')
|
|
62
|
+
return m;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const CONFIG_FILE = "sapiom.json";
|
|
2
|
+
export interface SapiomConfig {
|
|
3
|
+
definitionId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
host?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function readConfig(dir: string): SapiomConfig | null;
|
|
8
|
+
export declare function requireConfig(dir: string): SapiomConfig;
|
|
9
|
+
export declare function writeConfig(dir: string, cfg: SapiomConfig): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { OrchestrationError } from './errors.js';
|
|
4
|
+
export const CONFIG_FILE = 'sapiom.json';
|
|
5
|
+
export function readConfig(dir) {
|
|
6
|
+
const file = path.join(dir, CONFIG_FILE);
|
|
7
|
+
if (!existsSync(file))
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new OrchestrationError({ code: 'BAD_CONFIG', message: `${CONFIG_FILE} is not valid JSON.` });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function requireConfig(dir) {
|
|
17
|
+
const cfg = readConfig(dir);
|
|
18
|
+
if (!cfg?.definitionId) {
|
|
19
|
+
throw new OrchestrationError({
|
|
20
|
+
code: 'NOT_LINKED',
|
|
21
|
+
message: 'This project is not linked to a Sapiom orchestration.',
|
|
22
|
+
hint: 'Run: sapiom orchestrations link <name>',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return cfg;
|
|
26
|
+
}
|
|
27
|
+
export function writeConfig(dir, cfg) {
|
|
28
|
+
writeFileSync(path.join(dir, CONFIG_FILE), JSON.stringify(cfg, null, 2) + '\n');
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GatewayClient } from './client.js';
|
|
2
|
+
export interface DeployOptions {
|
|
3
|
+
projectDir: string;
|
|
4
|
+
definitionId: string;
|
|
5
|
+
branch?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DeployResult {
|
|
8
|
+
definitionId: string;
|
|
9
|
+
buildRunId: string;
|
|
10
|
+
status: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function deploy(opts: DeployOptions, client: GatewayClient): Promise<DeployResult>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { OrchestrationError } from './errors.js';
|
|
2
|
+
import { assertDeployable, pushHead } from './git.js';
|
|
3
|
+
const TERMINAL = new Set(['ready', 'failed', 'cancelled', 'superseded']);
|
|
4
|
+
const POLL_DELAYS_MS = [1000, 2000, 3000, 5000, 5000, 8000, 10000];
|
|
5
|
+
const POLL_BUDGET_MS = 300000;
|
|
6
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
|
+
export async function deploy(opts, client) {
|
|
8
|
+
const { projectDir, definitionId, branch = 'main' } = opts;
|
|
9
|
+
assertDeployable(projectDir);
|
|
10
|
+
const { pushUrl } = await client.post(`/definitions/${definitionId}/push-credentials`, {});
|
|
11
|
+
pushHead(projectDir, pushUrl, branch);
|
|
12
|
+
const triggered = await client.post(`/definitions/${definitionId}/builds`, {});
|
|
13
|
+
const buildRunId = triggered.buildRunId ?? triggered.id;
|
|
14
|
+
if (!buildRunId) {
|
|
15
|
+
throw new OrchestrationError({
|
|
16
|
+
code: 'BUILD_NO_ID',
|
|
17
|
+
message: 'The build was triggered but no build id was returned.',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const final = await pollBuild(client, definitionId, buildRunId);
|
|
21
|
+
if (final.status !== 'ready') {
|
|
22
|
+
throw new OrchestrationError({
|
|
23
|
+
code: 'BUILD_FAILED',
|
|
24
|
+
message: `Build ${final.status}.`,
|
|
25
|
+
step: 'build',
|
|
26
|
+
hint: final.error?.stack || final.error?.message,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return { definitionId, buildRunId, status: final.status };
|
|
30
|
+
}
|
|
31
|
+
async function pollBuild(client, definitionId, buildRunId) {
|
|
32
|
+
let elapsed = 0;
|
|
33
|
+
let i = 0;
|
|
34
|
+
while (elapsed < POLL_BUDGET_MS) {
|
|
35
|
+
const build = await client.get(`/definitions/${definitionId}/builds/${buildRunId}`);
|
|
36
|
+
if (TERMINAL.has(build.status))
|
|
37
|
+
return build;
|
|
38
|
+
const delay = POLL_DELAYS_MS[Math.min(i++, POLL_DELAYS_MS.length - 1)];
|
|
39
|
+
await sleep(delay);
|
|
40
|
+
elapsed += delay;
|
|
41
|
+
}
|
|
42
|
+
throw new OrchestrationError({
|
|
43
|
+
code: 'BUILD_TIMEOUT',
|
|
44
|
+
message: 'Build did not finish in time.',
|
|
45
|
+
step: 'build',
|
|
46
|
+
hint: `Check it later via the logs API for build ${buildRunId}`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface StructuredError {
|
|
2
|
+
code: string;
|
|
3
|
+
message: string;
|
|
4
|
+
step?: string;
|
|
5
|
+
hint?: string;
|
|
6
|
+
docsUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class OrchestrationError extends Error {
|
|
9
|
+
readonly code: string;
|
|
10
|
+
readonly step?: string;
|
|
11
|
+
readonly hint?: string;
|
|
12
|
+
readonly docsUrl?: string;
|
|
13
|
+
constructor(err: StructuredError);
|
|
14
|
+
toStructured(): StructuredError;
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class OrchestrationError extends Error {
|
|
2
|
+
constructor(err) {
|
|
3
|
+
super(err.message);
|
|
4
|
+
this.name = 'OrchestrationError';
|
|
5
|
+
this.code = err.code;
|
|
6
|
+
this.step = err.step;
|
|
7
|
+
this.hint = err.hint;
|
|
8
|
+
this.docsUrl = err.docsUrl;
|
|
9
|
+
}
|
|
10
|
+
toStructured() {
|
|
11
|
+
return {
|
|
12
|
+
code: this.code,
|
|
13
|
+
message: this.message,
|
|
14
|
+
...(this.step ? { step: this.step } : {}),
|
|
15
|
+
...(this.hint ? { hint: this.hint } : {}),
|
|
16
|
+
...(this.docsUrl ? { docsUrl: this.docsUrl } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
package/dist/esm/git.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { OrchestrationError } from './errors.js';
|
|
3
|
+
function git(args, cwd) {
|
|
4
|
+
try {
|
|
5
|
+
return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
6
|
+
}
|
|
7
|
+
catch (err) {
|
|
8
|
+
const stderr = err.stderr?.toString().trim();
|
|
9
|
+
throw new OrchestrationError({
|
|
10
|
+
code: 'GIT',
|
|
11
|
+
message: `git ${args[0]} failed.`,
|
|
12
|
+
hint: stderr || (err instanceof Error ? err.message : String(err)),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function assertDeployable(dir) {
|
|
17
|
+
try {
|
|
18
|
+
execFileSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: dir, stdio: 'ignore' });
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new OrchestrationError({
|
|
22
|
+
code: 'NOT_GIT',
|
|
23
|
+
message: 'Not a git repository.',
|
|
24
|
+
hint: 'Initialize one: git init && git add -A && git commit -m "init"',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
execFileSync('git', ['rev-parse', 'HEAD'], { cwd: dir, stdio: 'ignore' });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new OrchestrationError({
|
|
32
|
+
code: 'NO_COMMITS',
|
|
33
|
+
message: 'This repository has no commits yet.',
|
|
34
|
+
hint: 'Commit your work: git add -A && git commit -m "…"',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function pushHead(dir, pushUrl, branch) {
|
|
39
|
+
git(['push', '--force', pushUrl, `HEAD:${branch}`], dir);
|
|
40
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export { OrchestrationError } from './errors.js';
|
|
2
|
+
export type { StructuredError } from './errors.js';
|
|
3
|
+
export { GatewayClient, createClient, DEFAULT_WORKFLOWS_HOST } from './client.js';
|
|
4
|
+
export type { ClientOptions, GatewayErrorBody } from './client.js';
|
|
5
|
+
export { readConfig, requireConfig, writeConfig, CONFIG_FILE } from './config.js';
|
|
6
|
+
export type { SapiomConfig } from './config.js';
|
|
7
|
+
export { scaffold, resolveVersions, resolveTemplate, listTemplates, DEFAULT_TEMPLATE } from './scaffold.js';
|
|
8
|
+
export type { ScaffoldOptions, ScaffoldResult, ResolvedVersions } from './scaffold.js';
|
|
9
|
+
export { check } from './check.js';
|
|
10
|
+
export type { CheckOptions, CheckResult } from './check.js';
|
|
11
|
+
export { link } from './link.js';
|
|
12
|
+
export type { LinkOptions, LinkResult, DefinitionSummary } from './link.js';
|
|
13
|
+
export { deploy } from './deploy.js';
|
|
14
|
+
export type { DeployOptions, DeployResult } from './deploy.js';
|
|
15
|
+
export { run, parseJsonInput } from './run.js';
|
|
16
|
+
export type { RunOptions, RunResult } from './run.js';
|
|
17
|
+
export { inspect, listExecutions, inspectBuild } from './inspect.js';
|
|
18
|
+
export type { InspectOptions, InspectResult, ListExecutionsResult, InspectBuildOptions, InspectBuildResult, ExecutionDetail, StepRecord, BuildDetail, } from './inspect.js';
|
|
19
|
+
export { signal, parseSignalPayload } from './signal.js';
|
|
20
|
+
export type { SignalOptions, SignalResult } from './signal.js';
|
|
21
|
+
export { assertDeployable, pushHead } from './git.js';
|
|
22
|
+
export { parseStubFile, STUB_FILE_VERSION } from './local/stubs.js';
|
|
23
|
+
export type { StubFile, StepStubs, StubResponse } from './local/stubs.js';
|
|
24
|
+
export { runLocal, runLocalFromDir, STUBS_FILE } from './local/run-local.js';
|
|
25
|
+
export type { RunLocalOptions, LocalRunResult, LocalRunOutcome } from './local/run-local.js';
|
|
26
|
+
export { loadDefinition } from './local/load.js';
|
|
27
|
+
export type { LoadedDefinition } from './local/load.js';
|
|
28
|
+
export { LocalStubDispatcher } from './local/dispatcher.js';
|
|
29
|
+
export type { LocalStepTrace, LogEntry } from './local/dispatcher.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { OrchestrationError } from './errors.js';
|
|
2
|
+
export { GatewayClient, createClient, DEFAULT_WORKFLOWS_HOST } from './client.js';
|
|
3
|
+
export { readConfig, requireConfig, writeConfig, CONFIG_FILE } from './config.js';
|
|
4
|
+
export { scaffold, resolveVersions, resolveTemplate, listTemplates, DEFAULT_TEMPLATE } from './scaffold.js';
|
|
5
|
+
export { check } from './check.js';
|
|
6
|
+
export { link } from './link.js';
|
|
7
|
+
export { deploy } from './deploy.js';
|
|
8
|
+
export { run, parseJsonInput } from './run.js';
|
|
9
|
+
export { inspect, listExecutions, inspectBuild } from './inspect.js';
|
|
10
|
+
export { signal, parseSignalPayload } from './signal.js';
|
|
11
|
+
export { assertDeployable, pushHead } from './git.js';
|
|
12
|
+
export { parseStubFile, STUB_FILE_VERSION } from './local/stubs.js';
|
|
13
|
+
export { runLocal, runLocalFromDir, STUBS_FILE } from './local/run-local.js';
|
|
14
|
+
export { loadDefinition } from './local/load.js';
|
|
15
|
+
export { LocalStubDispatcher } from './local/dispatcher.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { GatewayClient } from './client.js';
|
|
2
|
+
export interface StepRecord {
|
|
3
|
+
stepName: string;
|
|
4
|
+
attempt: number;
|
|
5
|
+
status: string;
|
|
6
|
+
error?: {
|
|
7
|
+
message?: string;
|
|
8
|
+
stack?: string;
|
|
9
|
+
} | null;
|
|
10
|
+
}
|
|
11
|
+
export interface ExecutionDetail {
|
|
12
|
+
id: string;
|
|
13
|
+
status: string;
|
|
14
|
+
currentStep?: string | null;
|
|
15
|
+
error?: unknown;
|
|
16
|
+
steps?: StepRecord[];
|
|
17
|
+
}
|
|
18
|
+
export interface BuildDetail {
|
|
19
|
+
id?: string;
|
|
20
|
+
status: string;
|
|
21
|
+
error?: unknown;
|
|
22
|
+
}
|
|
23
|
+
export interface InspectOptions {
|
|
24
|
+
executionId: string;
|
|
25
|
+
}
|
|
26
|
+
export interface InspectResult {
|
|
27
|
+
execution: ExecutionDetail;
|
|
28
|
+
}
|
|
29
|
+
export declare function inspect(opts: InspectOptions, client: GatewayClient): Promise<InspectResult>;
|
|
30
|
+
export interface ListExecutionsResult {
|
|
31
|
+
executions: ExecutionDetail[];
|
|
32
|
+
}
|
|
33
|
+
export declare function listExecutions(client: GatewayClient): Promise<ListExecutionsResult>;
|
|
34
|
+
export interface InspectBuildOptions {
|
|
35
|
+
definitionId: string;
|
|
36
|
+
buildRunId: string;
|
|
37
|
+
}
|
|
38
|
+
export interface InspectBuildResult {
|
|
39
|
+
build: BuildDetail;
|
|
40
|
+
}
|
|
41
|
+
export declare function inspectBuild(opts: InspectBuildOptions, client: GatewayClient): Promise<InspectBuildResult>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export async function inspect(opts, client) {
|
|
2
|
+
const execution = await client.get(`/executions/${opts.executionId}`);
|
|
3
|
+
return { execution };
|
|
4
|
+
}
|
|
5
|
+
export async function listExecutions(client) {
|
|
6
|
+
const executions = await client.get('/executions');
|
|
7
|
+
return { executions };
|
|
8
|
+
}
|
|
9
|
+
export async function inspectBuild(opts, client) {
|
|
10
|
+
const build = await client.get(`/definitions/${opts.definitionId}/builds/${opts.buildRunId}`);
|
|
11
|
+
return { build };
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { GatewayClient } from './client.js';
|
|
2
|
+
export interface DefinitionSummary {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
slug?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LinkOptions {
|
|
8
|
+
name: string;
|
|
9
|
+
create?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface LinkResult {
|
|
12
|
+
definitionId: string;
|
|
13
|
+
name: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function link(opts: LinkOptions, client: GatewayClient): Promise<LinkResult>;
|
package/dist/esm/link.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { OrchestrationError } from './errors.js';
|
|
2
|
+
export async function link(opts, client) {
|
|
3
|
+
const list = await client.get('/definitions');
|
|
4
|
+
let def = list.find((d) => d.name === opts.name || d.slug === opts.name);
|
|
5
|
+
if (!def) {
|
|
6
|
+
if (!opts.create) {
|
|
7
|
+
throw new OrchestrationError({
|
|
8
|
+
code: 'NOT_FOUND',
|
|
9
|
+
message: `No orchestration named '${opts.name}'.`,
|
|
10
|
+
hint: 'Create it with { create: true }, or pass the name of an existing one.',
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
def = await client.post('/definitions', { name: opts.name });
|
|
14
|
+
}
|
|
15
|
+
return { definitionId: def.id, name: def.name };
|
|
16
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type NextStepDirective, type OrchestrationDefinition } from '@sapiom/orchestration';
|
|
2
|
+
import { type StepDispatcher, type StepDispatchRequest, type WorkflowRunnerCore } from '@sapiom/orchestration-runtime';
|
|
3
|
+
import type { StubFile } from './stubs.js';
|
|
4
|
+
export interface LogEntry {
|
|
5
|
+
level: 'info' | 'warn' | 'error' | 'debug';
|
|
6
|
+
msg: string;
|
|
7
|
+
meta?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface LocalStepTrace {
|
|
10
|
+
step: string;
|
|
11
|
+
attempt: number;
|
|
12
|
+
input: unknown;
|
|
13
|
+
status: 'succeeded' | 'threw';
|
|
14
|
+
output?: unknown;
|
|
15
|
+
directive?: NextStepDirective;
|
|
16
|
+
error?: {
|
|
17
|
+
name: string;
|
|
18
|
+
message: string;
|
|
19
|
+
stack?: string;
|
|
20
|
+
};
|
|
21
|
+
logs: LogEntry[];
|
|
22
|
+
}
|
|
23
|
+
export declare class LocalStubDispatcher implements StepDispatcher {
|
|
24
|
+
private readonly definition;
|
|
25
|
+
private readonly stubs;
|
|
26
|
+
private core;
|
|
27
|
+
private maxAttemptsPerStep;
|
|
28
|
+
private signals;
|
|
29
|
+
readonly trace: LocalStepTrace[];
|
|
30
|
+
constructor(definition: OrchestrationDefinition, stubs: StubFile);
|
|
31
|
+
setCore(core: WorkflowRunnerCore): void;
|
|
32
|
+
setMaxAttempts(max: number): void;
|
|
33
|
+
setSignals(signals: Map<string, unknown>): void;
|
|
34
|
+
dispatch(request: StepDispatchRequest): Promise<void>;
|
|
35
|
+
}
|