@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
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Sapiom
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.check = check;
|
|
40
|
+
const node_child_process_1 = require("node:child_process");
|
|
41
|
+
const node_crypto_1 = require("node:crypto");
|
|
42
|
+
const node_fs_1 = require("node:fs");
|
|
43
|
+
const node_os_1 = require("node:os");
|
|
44
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
45
|
+
const orchestration_1 = require("@sapiom/orchestration");
|
|
46
|
+
const esbuild = __importStar(require("esbuild"));
|
|
47
|
+
const errors_js_1 = require("./errors.js");
|
|
48
|
+
function runTypecheck(sourceDir) {
|
|
49
|
+
const tscBin = node_path_1.default.join(sourceDir, 'node_modules', '.bin', 'tsc');
|
|
50
|
+
if (!(0, node_fs_1.existsSync)(tscBin)) {
|
|
51
|
+
return 'typecheck skipped — TypeScript is not installed (run npm install first)';
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
(0, node_child_process_1.execFileSync)(tscBin, ['--noEmit'], { cwd: sourceDir, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
const e = err;
|
|
59
|
+
const output = (e.stdout?.toString() ?? '').trim() || (e.stderr?.toString() ?? '').trim();
|
|
60
|
+
throw new errors_js_1.OrchestrationError({
|
|
61
|
+
code: 'TYPECHECK_FAILED',
|
|
62
|
+
message: 'The orchestration has type errors.',
|
|
63
|
+
hint: output || 'Run `tsc --noEmit` for details.',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const LOCAL_SDK_VERSION = '0.0.0-local';
|
|
68
|
+
async function check(opts) {
|
|
69
|
+
const { sourceDir } = opts;
|
|
70
|
+
const entryFile = node_path_1.default.join(sourceDir, 'index.ts');
|
|
71
|
+
if (!(0, node_fs_1.existsSync)(entryFile)) {
|
|
72
|
+
throw new errors_js_1.OrchestrationError({
|
|
73
|
+
code: 'NO_ENTRY',
|
|
74
|
+
message: `No index.ts found in ${sourceDir}.`,
|
|
75
|
+
hint: 'Run this from an orchestration project, or pass its directory.',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const warnings = [];
|
|
79
|
+
const typecheckSkip = runTypecheck(sourceDir);
|
|
80
|
+
if (typecheckSkip)
|
|
81
|
+
warnings.push(typecheckSkip);
|
|
82
|
+
const tmp = (0, node_fs_1.mkdtempSync)(node_path_1.default.join((0, node_os_1.tmpdir)(), 'sapiom-check-'));
|
|
83
|
+
const bundlePath = node_path_1.default.join(tmp, 'definition.mjs');
|
|
84
|
+
try {
|
|
85
|
+
try {
|
|
86
|
+
await esbuild.build({
|
|
87
|
+
entryPoints: [entryFile],
|
|
88
|
+
outfile: bundlePath,
|
|
89
|
+
bundle: true,
|
|
90
|
+
platform: 'node',
|
|
91
|
+
target: 'node20',
|
|
92
|
+
format: 'esm',
|
|
93
|
+
logLevel: 'silent',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
throw new errors_js_1.OrchestrationError({
|
|
98
|
+
code: 'BUNDLE_FAILED',
|
|
99
|
+
message: 'Failed to bundle the orchestration.',
|
|
100
|
+
hint: err instanceof Error ? err.message : String(err),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const mod = await Promise.resolve(`${`file://${bundlePath}?t=${Date.now()}`}`).then(s => __importStar(require(s)));
|
|
104
|
+
const defs = [];
|
|
105
|
+
for (const value of Object.values(mod)) {
|
|
106
|
+
if ((0, orchestration_1.isOrchestrationDefinition)(value) && !defs.includes(value))
|
|
107
|
+
defs.push(value);
|
|
108
|
+
}
|
|
109
|
+
if (defs.length === 0) {
|
|
110
|
+
throw new errors_js_1.OrchestrationError({
|
|
111
|
+
code: 'NO_DEFINITION',
|
|
112
|
+
message: 'No orchestration was exported from index.ts.',
|
|
113
|
+
hint: 'Export the result of defineOrchestration({ … }).',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (defs.length > 1) {
|
|
117
|
+
throw new errors_js_1.OrchestrationError({
|
|
118
|
+
code: 'MULTIPLE_DEFINITIONS',
|
|
119
|
+
message: 'index.ts exports more than one orchestration.',
|
|
120
|
+
hint: 'Export exactly one defineOrchestration({ … }) result.',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const def = defs[0];
|
|
124
|
+
const sha256 = (0, node_crypto_1.createHash)('sha256').update((0, node_fs_1.readFileSync)(bundlePath)).digest('hex');
|
|
125
|
+
let manifest;
|
|
126
|
+
try {
|
|
127
|
+
manifest = orchestration_1.workflowManifestSchema.parse((0, orchestration_1.buildManifest)(def, { sdkVersion: LOCAL_SDK_VERSION, artifact: { sha256, entryFile: 'definition.mjs' } }));
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
throw new errors_js_1.OrchestrationError({
|
|
131
|
+
code: 'MANIFEST_INVALID',
|
|
132
|
+
message: 'The orchestration produced an invalid manifest.',
|
|
133
|
+
hint: err instanceof Error ? err.message : String(err),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
warnings.push(...(0, orchestration_1.assertValidGraph)(manifest));
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
throw new errors_js_1.OrchestrationError({
|
|
141
|
+
code: 'GRAPH_INVALID',
|
|
142
|
+
message: 'The orchestration graph is invalid.',
|
|
143
|
+
hint: err instanceof Error ? err.message : String(err),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const steps = manifest.steps;
|
|
147
|
+
const stepCount = Array.isArray(steps) ? steps.length : Object.keys(steps ?? {}).length;
|
|
148
|
+
const name = manifest.name ?? 'orchestration';
|
|
149
|
+
return { name, stepCount, warnings, manifest };
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
(0, node_fs_1.rmSync)(tmp, { recursive: true, force: true });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -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,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GatewayClient = exports.DEFAULT_WORKFLOWS_HOST = void 0;
|
|
4
|
+
exports.createClient = createClient;
|
|
5
|
+
const errors_js_1 = require("./errors.js");
|
|
6
|
+
exports.DEFAULT_WORKFLOWS_HOST = 'https://api.sapiom.ai';
|
|
7
|
+
class GatewayClient {
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
const host = (opts.host ?? exports.DEFAULT_WORKFLOWS_HOST).replace(/\/$/, '');
|
|
10
|
+
this.base = `${host}/v1/workflows`;
|
|
11
|
+
this.apiKey = opts.apiKey;
|
|
12
|
+
}
|
|
13
|
+
async request(method, path, body) {
|
|
14
|
+
let res;
|
|
15
|
+
try {
|
|
16
|
+
res = await fetch(`${this.base}${path}`, {
|
|
17
|
+
method,
|
|
18
|
+
headers: { 'x-api-key': this.apiKey, 'content-type': 'application/json' },
|
|
19
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
throw new errors_js_1.OrchestrationError({
|
|
24
|
+
code: 'NETWORK',
|
|
25
|
+
message: `Could not reach ${this.base}.`,
|
|
26
|
+
hint: err instanceof Error ? err.message : String(err),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const text = await res.text();
|
|
30
|
+
const data = text ? safeParse(text) : undefined;
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
throw new errors_js_1.OrchestrationError({
|
|
33
|
+
code: `HTTP_${res.status}`,
|
|
34
|
+
message: messageFrom(data) ?? `Request failed (${res.status} ${res.statusText}).`,
|
|
35
|
+
hint: res.status === 401 || res.status === 403
|
|
36
|
+
? 'Check your API key (`sapiom login` or SAPIOM_API_KEY) and that it has access to this orchestration.'
|
|
37
|
+
: undefined,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
get(path) {
|
|
43
|
+
return this.request('GET', path);
|
|
44
|
+
}
|
|
45
|
+
post(path, body) {
|
|
46
|
+
return this.request('POST', path, body);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.GatewayClient = GatewayClient;
|
|
50
|
+
function createClient(opts) {
|
|
51
|
+
return new GatewayClient(opts);
|
|
52
|
+
}
|
|
53
|
+
function safeParse(text) {
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(text);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return text;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function messageFrom(data) {
|
|
62
|
+
if (data && typeof data === 'object' && 'message' in data) {
|
|
63
|
+
const m = data.message;
|
|
64
|
+
if (Array.isArray(m))
|
|
65
|
+
return m.join('; ');
|
|
66
|
+
if (typeof m === 'string')
|
|
67
|
+
return m;
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
@@ -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,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CONFIG_FILE = void 0;
|
|
7
|
+
exports.readConfig = readConfig;
|
|
8
|
+
exports.requireConfig = requireConfig;
|
|
9
|
+
exports.writeConfig = writeConfig;
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const errors_js_1 = require("./errors.js");
|
|
13
|
+
exports.CONFIG_FILE = 'sapiom.json';
|
|
14
|
+
function readConfig(dir) {
|
|
15
|
+
const file = node_path_1.default.join(dir, exports.CONFIG_FILE);
|
|
16
|
+
if (!(0, node_fs_1.existsSync)(file))
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new errors_js_1.OrchestrationError({ code: 'BAD_CONFIG', message: `${exports.CONFIG_FILE} is not valid JSON.` });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function requireConfig(dir) {
|
|
26
|
+
const cfg = readConfig(dir);
|
|
27
|
+
if (!cfg?.definitionId) {
|
|
28
|
+
throw new errors_js_1.OrchestrationError({
|
|
29
|
+
code: 'NOT_LINKED',
|
|
30
|
+
message: 'This project is not linked to a Sapiom orchestration.',
|
|
31
|
+
hint: 'Run: sapiom orchestrations link <name>',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return cfg;
|
|
35
|
+
}
|
|
36
|
+
function writeConfig(dir, cfg) {
|
|
37
|
+
(0, node_fs_1.writeFileSync)(node_path_1.default.join(dir, exports.CONFIG_FILE), JSON.stringify(cfg, null, 2) + '\n');
|
|
38
|
+
}
|
|
@@ -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,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deploy = deploy;
|
|
4
|
+
const errors_js_1 = require("./errors.js");
|
|
5
|
+
const git_js_1 = require("./git.js");
|
|
6
|
+
const TERMINAL = new Set(['ready', 'failed', 'cancelled', 'superseded']);
|
|
7
|
+
const POLL_DELAYS_MS = [1000, 2000, 3000, 5000, 5000, 8000, 10000];
|
|
8
|
+
const POLL_BUDGET_MS = 300000;
|
|
9
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
async function deploy(opts, client) {
|
|
11
|
+
const { projectDir, definitionId, branch = 'main' } = opts;
|
|
12
|
+
(0, git_js_1.assertDeployable)(projectDir);
|
|
13
|
+
const { pushUrl } = await client.post(`/definitions/${definitionId}/push-credentials`, {});
|
|
14
|
+
(0, git_js_1.pushHead)(projectDir, pushUrl, branch);
|
|
15
|
+
const triggered = await client.post(`/definitions/${definitionId}/builds`, {});
|
|
16
|
+
const buildRunId = triggered.buildRunId ?? triggered.id;
|
|
17
|
+
if (!buildRunId) {
|
|
18
|
+
throw new errors_js_1.OrchestrationError({
|
|
19
|
+
code: 'BUILD_NO_ID',
|
|
20
|
+
message: 'The build was triggered but no build id was returned.',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const final = await pollBuild(client, definitionId, buildRunId);
|
|
24
|
+
if (final.status !== 'ready') {
|
|
25
|
+
throw new errors_js_1.OrchestrationError({
|
|
26
|
+
code: 'BUILD_FAILED',
|
|
27
|
+
message: `Build ${final.status}.`,
|
|
28
|
+
step: 'build',
|
|
29
|
+
hint: final.error?.stack || final.error?.message,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return { definitionId, buildRunId, status: final.status };
|
|
33
|
+
}
|
|
34
|
+
async function pollBuild(client, definitionId, buildRunId) {
|
|
35
|
+
let elapsed = 0;
|
|
36
|
+
let i = 0;
|
|
37
|
+
while (elapsed < POLL_BUDGET_MS) {
|
|
38
|
+
const build = await client.get(`/definitions/${definitionId}/builds/${buildRunId}`);
|
|
39
|
+
if (TERMINAL.has(build.status))
|
|
40
|
+
return build;
|
|
41
|
+
const delay = POLL_DELAYS_MS[Math.min(i++, POLL_DELAYS_MS.length - 1)];
|
|
42
|
+
await sleep(delay);
|
|
43
|
+
elapsed += delay;
|
|
44
|
+
}
|
|
45
|
+
throw new errors_js_1.OrchestrationError({
|
|
46
|
+
code: 'BUILD_TIMEOUT',
|
|
47
|
+
message: 'Build did not finish in time.',
|
|
48
|
+
step: 'build',
|
|
49
|
+
hint: `Check it later via the logs API for build ${buildRunId}`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -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,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OrchestrationError = void 0;
|
|
4
|
+
class OrchestrationError extends Error {
|
|
5
|
+
constructor(err) {
|
|
6
|
+
super(err.message);
|
|
7
|
+
this.name = 'OrchestrationError';
|
|
8
|
+
this.code = err.code;
|
|
9
|
+
this.step = err.step;
|
|
10
|
+
this.hint = err.hint;
|
|
11
|
+
this.docsUrl = err.docsUrl;
|
|
12
|
+
}
|
|
13
|
+
toStructured() {
|
|
14
|
+
return {
|
|
15
|
+
code: this.code,
|
|
16
|
+
message: this.message,
|
|
17
|
+
...(this.step ? { step: this.step } : {}),
|
|
18
|
+
...(this.hint ? { hint: this.hint } : {}),
|
|
19
|
+
...(this.docsUrl ? { docsUrl: this.docsUrl } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.OrchestrationError = OrchestrationError;
|
package/dist/cjs/git.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertDeployable = assertDeployable;
|
|
4
|
+
exports.pushHead = pushHead;
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const errors_js_1 = require("./errors.js");
|
|
7
|
+
function git(args, cwd) {
|
|
8
|
+
try {
|
|
9
|
+
return (0, node_child_process_1.execFileSync)('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
const stderr = err.stderr?.toString().trim();
|
|
13
|
+
throw new errors_js_1.OrchestrationError({
|
|
14
|
+
code: 'GIT',
|
|
15
|
+
message: `git ${args[0]} failed.`,
|
|
16
|
+
hint: stderr || (err instanceof Error ? err.message : String(err)),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function assertDeployable(dir) {
|
|
21
|
+
try {
|
|
22
|
+
(0, node_child_process_1.execFileSync)('git', ['rev-parse', '--is-inside-work-tree'], { cwd: dir, stdio: 'ignore' });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new errors_js_1.OrchestrationError({
|
|
26
|
+
code: 'NOT_GIT',
|
|
27
|
+
message: 'Not a git repository.',
|
|
28
|
+
hint: 'Initialize one: git init && git add -A && git commit -m "init"',
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
(0, node_child_process_1.execFileSync)('git', ['rev-parse', 'HEAD'], { cwd: dir, stdio: 'ignore' });
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new errors_js_1.OrchestrationError({
|
|
36
|
+
code: 'NO_COMMITS',
|
|
37
|
+
message: 'This repository has no commits yet.',
|
|
38
|
+
hint: 'Commit your work: git add -A && git commit -m "…"',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function pushHead(dir, pushUrl, branch) {
|
|
43
|
+
git(['push', '--force', pushUrl, `HEAD:${branch}`], dir);
|
|
44
|
+
}
|
|
@@ -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,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalStubDispatcher = exports.loadDefinition = exports.STUBS_FILE = exports.runLocalFromDir = exports.runLocal = exports.STUB_FILE_VERSION = exports.parseStubFile = exports.pushHead = exports.assertDeployable = exports.parseSignalPayload = exports.signal = exports.inspectBuild = exports.listExecutions = exports.inspect = exports.parseJsonInput = exports.run = exports.deploy = exports.link = exports.check = exports.DEFAULT_TEMPLATE = exports.listTemplates = exports.resolveTemplate = exports.resolveVersions = exports.scaffold = exports.CONFIG_FILE = exports.writeConfig = exports.requireConfig = exports.readConfig = exports.DEFAULT_WORKFLOWS_HOST = exports.createClient = exports.GatewayClient = exports.OrchestrationError = void 0;
|
|
4
|
+
var errors_js_1 = require("./errors.js");
|
|
5
|
+
Object.defineProperty(exports, "OrchestrationError", { enumerable: true, get: function () { return errors_js_1.OrchestrationError; } });
|
|
6
|
+
var client_js_1 = require("./client.js");
|
|
7
|
+
Object.defineProperty(exports, "GatewayClient", { enumerable: true, get: function () { return client_js_1.GatewayClient; } });
|
|
8
|
+
Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return client_js_1.createClient; } });
|
|
9
|
+
Object.defineProperty(exports, "DEFAULT_WORKFLOWS_HOST", { enumerable: true, get: function () { return client_js_1.DEFAULT_WORKFLOWS_HOST; } });
|
|
10
|
+
var config_js_1 = require("./config.js");
|
|
11
|
+
Object.defineProperty(exports, "readConfig", { enumerable: true, get: function () { return config_js_1.readConfig; } });
|
|
12
|
+
Object.defineProperty(exports, "requireConfig", { enumerable: true, get: function () { return config_js_1.requireConfig; } });
|
|
13
|
+
Object.defineProperty(exports, "writeConfig", { enumerable: true, get: function () { return config_js_1.writeConfig; } });
|
|
14
|
+
Object.defineProperty(exports, "CONFIG_FILE", { enumerable: true, get: function () { return config_js_1.CONFIG_FILE; } });
|
|
15
|
+
var scaffold_js_1 = require("./scaffold.js");
|
|
16
|
+
Object.defineProperty(exports, "scaffold", { enumerable: true, get: function () { return scaffold_js_1.scaffold; } });
|
|
17
|
+
Object.defineProperty(exports, "resolveVersions", { enumerable: true, get: function () { return scaffold_js_1.resolveVersions; } });
|
|
18
|
+
Object.defineProperty(exports, "resolveTemplate", { enumerable: true, get: function () { return scaffold_js_1.resolveTemplate; } });
|
|
19
|
+
Object.defineProperty(exports, "listTemplates", { enumerable: true, get: function () { return scaffold_js_1.listTemplates; } });
|
|
20
|
+
Object.defineProperty(exports, "DEFAULT_TEMPLATE", { enumerable: true, get: function () { return scaffold_js_1.DEFAULT_TEMPLATE; } });
|
|
21
|
+
var check_js_1 = require("./check.js");
|
|
22
|
+
Object.defineProperty(exports, "check", { enumerable: true, get: function () { return check_js_1.check; } });
|
|
23
|
+
var link_js_1 = require("./link.js");
|
|
24
|
+
Object.defineProperty(exports, "link", { enumerable: true, get: function () { return link_js_1.link; } });
|
|
25
|
+
var deploy_js_1 = require("./deploy.js");
|
|
26
|
+
Object.defineProperty(exports, "deploy", { enumerable: true, get: function () { return deploy_js_1.deploy; } });
|
|
27
|
+
var run_js_1 = require("./run.js");
|
|
28
|
+
Object.defineProperty(exports, "run", { enumerable: true, get: function () { return run_js_1.run; } });
|
|
29
|
+
Object.defineProperty(exports, "parseJsonInput", { enumerable: true, get: function () { return run_js_1.parseJsonInput; } });
|
|
30
|
+
var inspect_js_1 = require("./inspect.js");
|
|
31
|
+
Object.defineProperty(exports, "inspect", { enumerable: true, get: function () { return inspect_js_1.inspect; } });
|
|
32
|
+
Object.defineProperty(exports, "listExecutions", { enumerable: true, get: function () { return inspect_js_1.listExecutions; } });
|
|
33
|
+
Object.defineProperty(exports, "inspectBuild", { enumerable: true, get: function () { return inspect_js_1.inspectBuild; } });
|
|
34
|
+
var signal_js_1 = require("./signal.js");
|
|
35
|
+
Object.defineProperty(exports, "signal", { enumerable: true, get: function () { return signal_js_1.signal; } });
|
|
36
|
+
Object.defineProperty(exports, "parseSignalPayload", { enumerable: true, get: function () { return signal_js_1.parseSignalPayload; } });
|
|
37
|
+
var git_js_1 = require("./git.js");
|
|
38
|
+
Object.defineProperty(exports, "assertDeployable", { enumerable: true, get: function () { return git_js_1.assertDeployable; } });
|
|
39
|
+
Object.defineProperty(exports, "pushHead", { enumerable: true, get: function () { return git_js_1.pushHead; } });
|
|
40
|
+
var stubs_js_1 = require("./local/stubs.js");
|
|
41
|
+
Object.defineProperty(exports, "parseStubFile", { enumerable: true, get: function () { return stubs_js_1.parseStubFile; } });
|
|
42
|
+
Object.defineProperty(exports, "STUB_FILE_VERSION", { enumerable: true, get: function () { return stubs_js_1.STUB_FILE_VERSION; } });
|
|
43
|
+
var run_local_js_1 = require("./local/run-local.js");
|
|
44
|
+
Object.defineProperty(exports, "runLocal", { enumerable: true, get: function () { return run_local_js_1.runLocal; } });
|
|
45
|
+
Object.defineProperty(exports, "runLocalFromDir", { enumerable: true, get: function () { return run_local_js_1.runLocalFromDir; } });
|
|
46
|
+
Object.defineProperty(exports, "STUBS_FILE", { enumerable: true, get: function () { return run_local_js_1.STUBS_FILE; } });
|
|
47
|
+
var load_js_1 = require("./local/load.js");
|
|
48
|
+
Object.defineProperty(exports, "loadDefinition", { enumerable: true, get: function () { return load_js_1.loadDefinition; } });
|
|
49
|
+
var dispatcher_js_1 = require("./local/dispatcher.js");
|
|
50
|
+
Object.defineProperty(exports, "LocalStubDispatcher", { enumerable: true, get: function () { return dispatcher_js_1.LocalStubDispatcher; } });
|
|
@@ -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,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inspect = inspect;
|
|
4
|
+
exports.listExecutions = listExecutions;
|
|
5
|
+
exports.inspectBuild = inspectBuild;
|
|
6
|
+
async function inspect(opts, client) {
|
|
7
|
+
const execution = await client.get(`/executions/${opts.executionId}`);
|
|
8
|
+
return { execution };
|
|
9
|
+
}
|
|
10
|
+
async function listExecutions(client) {
|
|
11
|
+
const executions = await client.get('/executions');
|
|
12
|
+
return { executions };
|
|
13
|
+
}
|
|
14
|
+
async function inspectBuild(opts, client) {
|
|
15
|
+
const build = await client.get(`/definitions/${opts.definitionId}/builds/${opts.buildRunId}`);
|
|
16
|
+
return { build };
|
|
17
|
+
}
|
|
@@ -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>;
|