@vizamodo/modo-dispatcher 1.1.77
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/README.md +43 -0
- package/dist/artifacts/builders.d.ts +32 -0
- package/dist/artifacts/builders.js +72 -0
- package/dist/artifacts/downloader.d.ts +28 -0
- package/dist/artifacts/downloader.js +102 -0
- package/dist/artifacts/normalizer.d.ts +22 -0
- package/dist/artifacts/normalizer.js +65 -0
- package/dist/artifacts/types.d.ts +94 -0
- package/dist/artifacts/types.js +1 -0
- package/dist/auth/identity/extract-identity.d.ts +8 -0
- package/dist/auth/identity/extract-identity.js +15 -0
- package/dist/auth/identity/normalize-teams.d.ts +1 -0
- package/dist/auth/identity/normalize-teams.js +5 -0
- package/dist/auth/identity/validate-claims.d.ts +2 -0
- package/dist/auth/identity/validate-claims.js +5 -0
- package/dist/auth/jwt/parse.d.ts +2 -0
- package/dist/auth/jwt/parse.js +16 -0
- package/dist/auth/jwt/verify.d.ts +1 -0
- package/dist/auth/jwt/verify.js +9 -0
- package/dist/auth/oauth/auth-error.d.ts +12 -0
- package/dist/auth/oauth/auth-error.js +47 -0
- package/dist/auth/oauth/auth-session-store.d.ts +30 -0
- package/dist/auth/oauth/auth-session-store.js +46 -0
- package/dist/auth/oauth/callback-server.d.ts +21 -0
- package/dist/auth/oauth/callback-server.js +319 -0
- package/dist/auth/oauth/github.d.ts +14 -0
- package/dist/auth/oauth/github.js +100 -0
- package/dist/auth/oauth/sse.d.ts +11 -0
- package/dist/auth/oauth/sse.js +67 -0
- package/dist/auth/oauth/templates/failed.html +629 -0
- package/dist/auth/oauth/templates/pending.html +620 -0
- package/dist/auth/oauth/templates/success.html +577 -0
- package/dist/auth/session/access-token.d.ts +15 -0
- package/dist/auth/session/access-token.js +64 -0
- package/dist/auth/session/login.d.ts +18 -0
- package/dist/auth/session/login.js +144 -0
- package/dist/auth/session/logout.d.ts +3 -0
- package/dist/auth/session/logout.js +21 -0
- package/dist/auth/session/refresh-token.d.ts +8 -0
- package/dist/auth/session/refresh-token.js +16 -0
- package/dist/auth/session/refresh.d.ts +2 -0
- package/dist/auth/session/refresh.js +61 -0
- package/dist/auth/session/rotate.d.ts +4 -0
- package/dist/auth/session/rotate.js +4 -0
- package/dist/auth/session/session-manager.d.ts +16 -0
- package/dist/auth/session/session-manager.js +54 -0
- package/dist/auth/session/token-state.d.ts +20 -0
- package/dist/auth/session/token-state.js +55 -0
- package/dist/auth/session/types.d.ts +35 -0
- package/dist/auth/session/types.js +1 -0
- package/dist/auth/storage/keychain.d.ts +16 -0
- package/dist/auth/storage/keychain.js +107 -0
- package/dist/auth/storage/memory.d.ts +10 -0
- package/dist/auth/storage/memory.js +15 -0
- package/dist/auth/storage/types.d.ts +5 -0
- package/dist/auth/storage/types.js +1 -0
- package/dist/config/defaults.d.ts +94 -0
- package/dist/config/defaults.js +116 -0
- package/dist/core/auth-bootstrap.d.ts +15 -0
- package/dist/core/auth-bootstrap.js +33 -0
- package/dist/core/bootstrap.d.ts +73 -0
- package/dist/core/bootstrap.js +248 -0
- package/dist/core/dispatcher.d.ts +2 -0
- package/dist/core/dispatcher.js +28 -0
- package/dist/core/ensure-bootstrap.d.ts +3 -0
- package/dist/core/ensure-bootstrap.js +25 -0
- package/dist/core/errors.d.ts +69 -0
- package/dist/core/errors.js +121 -0
- package/dist/core/runtime-orchestrator.d.ts +17 -0
- package/dist/core/runtime-orchestrator.js +47 -0
- package/dist/core/runtime.d.ts +16 -0
- package/dist/core/runtime.js +52 -0
- package/dist/core/validation.d.ts +2 -0
- package/dist/core/validation.js +21 -0
- package/dist/crypto/encrypt.d.ts +12 -0
- package/dist/crypto/encrypt.js +80 -0
- package/dist/flows/email-otp-flow.d.ts +7 -0
- package/dist/flows/email-otp-flow.js +249 -0
- package/dist/gateway/auth-header.d.ts +2 -0
- package/dist/gateway/auth-header.js +21 -0
- package/dist/gateway/client.d.ts +7 -0
- package/dist/gateway/client.js +24 -0
- package/dist/gateway/dispatch-with-bootstrap-retry.d.ts +25 -0
- package/dist/gateway/dispatch-with-bootstrap-retry.js +157 -0
- package/dist/gateway/dispatch.d.ts +23 -0
- package/dist/gateway/dispatch.js +110 -0
- package/dist/gateway/session-waiter.d.ts +11 -0
- package/dist/gateway/session-waiter.js +126 -0
- package/dist/gateway/session.d.ts +21 -0
- package/dist/gateway/session.js +74 -0
- package/dist/gateway/types.d.ts +113 -0
- package/dist/gateway/types.js +19 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +21 -0
- package/dist/runtime/runtime-context.d.ts +6 -0
- package/dist/runtime/runtime-context.js +1 -0
- package/dist/types/dispatcher.d.ts +275 -0
- package/dist/types/dispatcher.js +1 -0
- package/dist/types/payload.d.ts +5 -0
- package/dist/types/payload.js +2 -0
- package/dist/types/runtime-env.d.ts +2 -0
- package/dist/types/runtime-env.js +5 -0
- package/dist/ui/banner.d.ts +4 -0
- package/dist/ui/banner.js +20 -0
- package/dist/utils/request-dedup.d.ts +75 -0
- package/dist/utils/request-dedup.js +102 -0
- package/dist/utils/type-guards.d.ts +173 -0
- package/dist/utils/type-guards.js +232 -0
- package/package.json +43 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { DispatchRejectedError, GatewayError, SessionTimeoutError, } from "../core/errors.js";
|
|
2
|
+
import { waitForSessionDone } from "./session.js";
|
|
3
|
+
import { finalizeResult } from "../artifacts/builders.js";
|
|
4
|
+
import { extractSessionId, extractString, isObject, hasStringProperty, } from "../utils/type-guards.js";
|
|
5
|
+
import { readArtifactString, normalizeAcceptedArtifacts } from "../artifacts/normalizer.js";
|
|
6
|
+
/**
|
|
7
|
+
* Wait for the result of an accepted dispatch session.
|
|
8
|
+
*
|
|
9
|
+
* Routes through the GatewayDispatchAccepted action:
|
|
10
|
+
* - "reject" / "idle" → throws or returns early
|
|
11
|
+
* - "cancel" / "done" → returns immediately
|
|
12
|
+
* - "dispatch" / "reuse" → connects WebSocket, waits, finalizes
|
|
13
|
+
*/
|
|
14
|
+
export async function waitResultFromAccepted(accepted, input) {
|
|
15
|
+
switch (accepted.action) {
|
|
16
|
+
case "reject": {
|
|
17
|
+
// Distinguish between runtime/business rejects (should be
|
|
18
|
+
// normalized into a runtime DispatchResult) and gateway-level
|
|
19
|
+
// rejects (transport/concurrency errors) which should continue
|
|
20
|
+
// to throw. Use the `type` field to decide.
|
|
21
|
+
if (accepted.type === "runtime") {
|
|
22
|
+
const artifacts = accepted.artifacts ?? {};
|
|
23
|
+
const sessionId = extractSessionId(artifacts) ?? "runtime";
|
|
24
|
+
// IMPORTANT:
|
|
25
|
+
// Runtime/gateway admission rejects are NOT github execution
|
|
26
|
+
// results, even when the original requested runType was
|
|
27
|
+
// "infra". No workflow execution happened.
|
|
28
|
+
//
|
|
29
|
+
// Always normalize runtime rejects as runtime results so
|
|
30
|
+
// semantic error payloads (reason/error.message) survive.
|
|
31
|
+
const conclusion = accepted.status;
|
|
32
|
+
return finalizeResult({
|
|
33
|
+
sessionId,
|
|
34
|
+
status: accepted.status,
|
|
35
|
+
conclusion,
|
|
36
|
+
runType: "runtime",
|
|
37
|
+
artifacts: normalizeAcceptedArtifacts(accepted.artifacts, "runtime"),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
throw new DispatchRejectedError("Dispatch rejected: another command is running");
|
|
41
|
+
}
|
|
42
|
+
case "idle": throw new DispatchRejectedError("No active session");
|
|
43
|
+
case "cancel":
|
|
44
|
+
{
|
|
45
|
+
const artifacts = accepted.artifacts ?? {};
|
|
46
|
+
const sessionId = readArtifactString(artifacts, "sessionId") ??
|
|
47
|
+
readArtifactString(artifacts, "executionKey") ??
|
|
48
|
+
"runtime";
|
|
49
|
+
const cancelStatus = accepted.status === "already_done" ? "already_done" : "cancelled";
|
|
50
|
+
return {
|
|
51
|
+
kind: "cancel",
|
|
52
|
+
status: cancelStatus,
|
|
53
|
+
sessionId,
|
|
54
|
+
...(accepted.conclusion !== undefined ? { conclusion: accepted.conclusion } : {}),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
case "done":
|
|
58
|
+
{
|
|
59
|
+
const artifacts = accepted.artifacts ?? {};
|
|
60
|
+
const sessionId = extractSessionId(artifacts) ?? "runtime";
|
|
61
|
+
// Special semantic:
|
|
62
|
+
// cancel request reached an execution that already completed.
|
|
63
|
+
// This is NOT a github artifact result.
|
|
64
|
+
// Preserve the informational payload so renderer can show it.
|
|
65
|
+
if (input.runType === "infra" &&
|
|
66
|
+
accepted.status === "already_done") {
|
|
67
|
+
return {
|
|
68
|
+
kind: "cancel",
|
|
69
|
+
status: "already_done",
|
|
70
|
+
sessionId,
|
|
71
|
+
...(accepted.conclusion !== undefined
|
|
72
|
+
? { conclusion: accepted.conclusion }
|
|
73
|
+
: {}),
|
|
74
|
+
...(isObject(artifacts) ? { data: artifacts } : {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return finalizeResult({
|
|
78
|
+
sessionId,
|
|
79
|
+
status: accepted.status,
|
|
80
|
+
conclusion: accepted.conclusion ?? accepted.status,
|
|
81
|
+
runType: input.runType,
|
|
82
|
+
artifacts: normalizeAcceptedArtifacts(accepted.artifacts, input.runType),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
case "dispatch":
|
|
86
|
+
case "reuse": {
|
|
87
|
+
const artifacts = accepted.artifacts ?? {};
|
|
88
|
+
const wsToken = extractString(artifacts, "wsToken");
|
|
89
|
+
const wsUrl = extractString(artifacts, "wsUrl");
|
|
90
|
+
const acceptedSessionId = extractSessionId(artifacts) ?? "runtime";
|
|
91
|
+
if (!wsToken) {
|
|
92
|
+
throw new GatewayError("Missing wsToken for WebSocket Authorization", {
|
|
93
|
+
details: { sessionId: acceptedSessionId, action: accepted.action }
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (!wsUrl) {
|
|
97
|
+
throw new GatewayError("Missing wsUrl for WebSocket session", {
|
|
98
|
+
details: { sessionId: acceptedSessionId, action: accepted.action }
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
let rawDone;
|
|
102
|
+
try {
|
|
103
|
+
rawDone = await waitForSessionDone(wsUrl, { wsToken });
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const errorName = isObject(err) && hasStringProperty(err, "name") ? err.name : "";
|
|
107
|
+
if (errorName === "SessionTimeoutError")
|
|
108
|
+
throw new SessionTimeoutError();
|
|
109
|
+
throw new GatewayError("Gateway session error", { cause: err });
|
|
110
|
+
}
|
|
111
|
+
const rawDoneSessionId = extractSessionId(rawDone.artifacts) ?? acceptedSessionId;
|
|
112
|
+
if (input.runType === "infra" && rawDoneSessionId !== acceptedSessionId) {
|
|
113
|
+
throw new GatewayError(`Gateway returned mismatched sessionId (expected ${acceptedSessionId}, got ${rawDoneSessionId})`);
|
|
114
|
+
}
|
|
115
|
+
return finalizeResult({
|
|
116
|
+
sessionId: rawDoneSessionId,
|
|
117
|
+
status: rawDone.status,
|
|
118
|
+
conclusion: rawDone.conclusion,
|
|
119
|
+
runType: input.runType,
|
|
120
|
+
artifacts: normalizeAcceptedArtifacts(rawDone.artifacts, input.runType),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
default:
|
|
124
|
+
throw new DispatchRejectedError("Unsupported gateway action");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GatewaySessionDoneEvent } from "./types.js";
|
|
2
|
+
export interface WaitForSessionOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Maximum time to wait for session completion (ms)
|
|
5
|
+
* Default: 10 minutes
|
|
6
|
+
*/
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Optional WebSocket bearer token. If provided, it will be sent as
|
|
10
|
+
* `Authorization: Bearer <token>` during the WebSocket handshake.
|
|
11
|
+
*/
|
|
12
|
+
wsToken?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Wait for session completion via WebSocket.
|
|
16
|
+
*
|
|
17
|
+
* Contract:
|
|
18
|
+
* - Gateway sends exactly ONE message: { type: "done", ... }
|
|
19
|
+
* - Connection is closed after that
|
|
20
|
+
*/
|
|
21
|
+
export declare function waitForSessionDone(wsUrl: string, options?: WaitForSessionOptions): Promise<GatewaySessionDoneEvent>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import { hasType } from "../utils/type-guards.js";
|
|
3
|
+
import { DEFAULTS } from "../config/defaults.js";
|
|
4
|
+
/**
|
|
5
|
+
* Wait for session completion via WebSocket.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - Gateway sends exactly ONE message: { type: "done", ... }
|
|
9
|
+
* - Connection is closed after that
|
|
10
|
+
*/
|
|
11
|
+
export function waitForSessionDone(wsUrl, options = {}) {
|
|
12
|
+
const { timeoutMs = DEFAULTS.WEBSOCKET_TIMEOUT_MS, wsToken } = options;
|
|
13
|
+
if (!wsUrl.startsWith("wss://")) {
|
|
14
|
+
return Promise.reject(new Error(`Invalid WebSocket URL: ${wsUrl}`));
|
|
15
|
+
}
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
let settled = false;
|
|
18
|
+
const headers = {};
|
|
19
|
+
if (wsToken) {
|
|
20
|
+
headers["Authorization"] = `Bearer ${wsToken}`;
|
|
21
|
+
}
|
|
22
|
+
const ws = Object.keys(headers).length > 0
|
|
23
|
+
? new WebSocket(wsUrl, { headers })
|
|
24
|
+
: new WebSocket(wsUrl);
|
|
25
|
+
const timeout = setTimeout(() => {
|
|
26
|
+
if (settled)
|
|
27
|
+
return;
|
|
28
|
+
settled = true;
|
|
29
|
+
ws.close();
|
|
30
|
+
reject(new Error("Session wait timed out"));
|
|
31
|
+
}, timeoutMs);
|
|
32
|
+
ws.on("message", (data) => {
|
|
33
|
+
if (settled)
|
|
34
|
+
return;
|
|
35
|
+
let dataParsed;
|
|
36
|
+
try {
|
|
37
|
+
dataParsed = JSON.parse(data.toString());
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
settled = true;
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
ws.close();
|
|
43
|
+
reject(new Error("Invalid JSON received from gateway"));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!hasType(dataParsed, "done")) {
|
|
47
|
+
settled = true;
|
|
48
|
+
clearTimeout(timeout);
|
|
49
|
+
ws.close();
|
|
50
|
+
reject(new Error("Unexpected WebSocket message from gateway"));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
settled = true;
|
|
54
|
+
clearTimeout(timeout);
|
|
55
|
+
ws.close();
|
|
56
|
+
resolve(dataParsed);
|
|
57
|
+
});
|
|
58
|
+
ws.on("error", (_err) => {
|
|
59
|
+
if (settled)
|
|
60
|
+
return;
|
|
61
|
+
settled = true;
|
|
62
|
+
clearTimeout(timeout);
|
|
63
|
+
ws.close();
|
|
64
|
+
reject(new Error("WebSocket error while waiting for session"));
|
|
65
|
+
});
|
|
66
|
+
ws.on("close", (code, reason) => {
|
|
67
|
+
if (settled)
|
|
68
|
+
return;
|
|
69
|
+
settled = true;
|
|
70
|
+
clearTimeout(timeout);
|
|
71
|
+
reject(new Error(`WebSocket closed before session completion (code=${code}, reason=${reason.toString()})`));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Artifacts, LogArtifact, ResultArtifact, ArtifactRef, GatewayArtifactRef } from '../artifacts/types.js';
|
|
2
|
+
export type { Artifacts, LogArtifact, ResultArtifact, ArtifactRef, GatewayArtifactRef };
|
|
3
|
+
export interface EncryptedPayload {
|
|
4
|
+
alg: "x25519-hkdf-aes256gcm";
|
|
5
|
+
clientPub: string;
|
|
6
|
+
iv: string;
|
|
7
|
+
tag: string;
|
|
8
|
+
data: string;
|
|
9
|
+
created: number;
|
|
10
|
+
jti: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GatewayDispatchRequest {
|
|
13
|
+
/**
|
|
14
|
+
* Intent identifier
|
|
15
|
+
* e.g. "deploy-base-worker", "publish-billing", "query-repo"
|
|
16
|
+
*/
|
|
17
|
+
intent: string;
|
|
18
|
+
/**
|
|
19
|
+
* Execution routing hint from client.
|
|
20
|
+
* - "infra": route to GitHub Actions
|
|
21
|
+
* - "runtime": route to hub worker
|
|
22
|
+
*/
|
|
23
|
+
runType?: "infra" | "runtime";
|
|
24
|
+
infraKey: string;
|
|
25
|
+
/**
|
|
26
|
+
* Maps to GitHub `repository_dispatch.event_type`
|
|
27
|
+
*/
|
|
28
|
+
commandType: string;
|
|
29
|
+
/**
|
|
30
|
+
* Canonical allowlist of GitHub teams permitted to execute this command.
|
|
31
|
+
*
|
|
32
|
+
* SECURITY-CRITICAL:
|
|
33
|
+
* Gateway/server authorization MUST verify that the authenticated
|
|
34
|
+
* identity extracted from JWT/session intersects with this allowlist.
|
|
35
|
+
*
|
|
36
|
+
* This field MUST be forwarded unchanged end-to-end.
|
|
37
|
+
*/
|
|
38
|
+
allowedTeams: string[];
|
|
39
|
+
/**
|
|
40
|
+
* Optional runner label hint for execution environment.
|
|
41
|
+
* Gateway treats this as an opaque hint and forwards it downstream.
|
|
42
|
+
* If omitted, execution layer may apply a default (e.g. "selfhosted").
|
|
43
|
+
*/
|
|
44
|
+
runnerLabel?: string;
|
|
45
|
+
/**
|
|
46
|
+
* If true, do NOT delete source workflow logs/runs.
|
|
47
|
+
* Useful for debugging and audits.
|
|
48
|
+
* Default behavior is to allow the control-plane to delete logs for hygiene.
|
|
49
|
+
*/
|
|
50
|
+
keepLog?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Flow gates controlling which workflow setup steps are enabled.
|
|
53
|
+
* Best-effort, client-decided. May be wrong.
|
|
54
|
+
* Gateway MUST treat this as opaque data and forward it unchanged.
|
|
55
|
+
*/
|
|
56
|
+
flowGates?: {
|
|
57
|
+
secrets?: boolean;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Payload is ALWAYS encrypted.
|
|
61
|
+
* Gateway must decrypt before forwarding.
|
|
62
|
+
*/
|
|
63
|
+
payload: EncryptedPayload | null;
|
|
64
|
+
/**
|
|
65
|
+
* Optional resource identity for concurrency control.
|
|
66
|
+
* This is an infra-level field and MUST NOT be derived from encrypted payload.
|
|
67
|
+
* Gateway uses this as a hint and may validate or override it.
|
|
68
|
+
*/
|
|
69
|
+
resourceId: string;
|
|
70
|
+
}
|
|
71
|
+
export type GatewayArtifacts = {
|
|
72
|
+
log?: {
|
|
73
|
+
path: string;
|
|
74
|
+
downloadUrl?: string;
|
|
75
|
+
};
|
|
76
|
+
result?: {
|
|
77
|
+
path: string;
|
|
78
|
+
downloadUrl?: string;
|
|
79
|
+
contentType?: string;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
export type GatewayDispatchAccepted = {
|
|
83
|
+
action: "dispatch" | "reuse" | "done" | "reject" | "cancel" | "idle";
|
|
84
|
+
type: "runtime" | "infra";
|
|
85
|
+
status: string;
|
|
86
|
+
conclusion?: string;
|
|
87
|
+
artifacts: Record<string, unknown>;
|
|
88
|
+
};
|
|
89
|
+
export interface GatewayDispatchRejected {
|
|
90
|
+
ok: false;
|
|
91
|
+
/**
|
|
92
|
+
* Stable error code for client handling
|
|
93
|
+
*/
|
|
94
|
+
error: string;
|
|
95
|
+
/**
|
|
96
|
+
* Human-readable explanation (for CLI display)
|
|
97
|
+
*/
|
|
98
|
+
message?: string;
|
|
99
|
+
}
|
|
100
|
+
export type GatewayDispatchResponse = GatewayDispatchAccepted | GatewayDispatchRejected;
|
|
101
|
+
export interface GatewaySessionDoneEvent {
|
|
102
|
+
type: "done";
|
|
103
|
+
/**
|
|
104
|
+
* Final execution status
|
|
105
|
+
*/
|
|
106
|
+
status: string;
|
|
107
|
+
conclusion: string;
|
|
108
|
+
/**
|
|
109
|
+
* Artifacts generated by the workflow
|
|
110
|
+
* These are presigned URLs (read-only)
|
|
111
|
+
*/
|
|
112
|
+
artifacts: Record<string, unknown>;
|
|
113
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
* Gateway ⇄ Dispatcher Contract (Encryption-Only)
|
|
3
|
+
* ============================================================
|
|
4
|
+
* Notes:
|
|
5
|
+
* - Gateway MUST NOT stream logs
|
|
6
|
+
* - Logs are fetched later via presigned artifact URLs
|
|
7
|
+
* - WebSocket is used ONLY for final status notification
|
|
8
|
+
* ============================================================
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
/* ---------------------------
|
|
12
|
+
* Artifact Reference
|
|
13
|
+
* ---------------------------
|
|
14
|
+
*
|
|
15
|
+
* NOTE: ArtifactRef is now defined in artifacts/types.ts (SSOT)
|
|
16
|
+
* and re-exported from this file for backward compatibility.
|
|
17
|
+
*
|
|
18
|
+
* See: ArtifactRef in '../artifacts/types.js'
|
|
19
|
+
*/
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { dispatcherDispatch } from "./core/dispatcher.js";
|
|
2
|
+
export { executeDispatchLifecycle } from "./core/runtime-orchestrator.js";
|
|
3
|
+
export { normalizeDispatcherError } from "./core/errors.js";
|
|
4
|
+
export { DispatcherError, DispatchRejectedError, SessionTimeoutError, } from "./core/errors.js";
|
|
5
|
+
export { initAuthRuntime, getAuthRuntime } from "./core/runtime.js";
|
|
6
|
+
export { GatewayBootstrapConfig, GatewayBootstrapMap, readLocalGatewayConfig, writeLocalGatewayConfig, persistBootstrapMap, refreshBootstrapFromGateway, loadGatewayBootstrapConfig, bootstrapGatewayConfig, } from "./core/bootstrap.js";
|
|
7
|
+
export type { LoadGatewayBootstrapConfigOptions } from "./core/bootstrap.js";
|
|
8
|
+
export { ensureBootstrapped } from "./core/ensure-bootstrap.js";
|
|
9
|
+
export { exchangeGithubCodeForTokens } from "./auth/oauth/github.js";
|
|
10
|
+
export type { ExchangeResult } from "./auth/oauth/github.js";
|
|
11
|
+
export { performInteractiveLogin } from "./auth/session/login.js";
|
|
12
|
+
export type { LoginResult } from "./auth/session/login.js";
|
|
13
|
+
export { logoutSession } from "./auth/session/logout.js";
|
|
14
|
+
export { rotateTokens as persistTokenPair } from "./auth/session/rotate.js";
|
|
15
|
+
export type { DispatchInput, DispatchOptions, DispatchResult, DispatchHandle, DispatchMode, InteractivePromptHooks, } from "./types/dispatcher.js";
|
|
16
|
+
export type { RuntimeEnv } from "./types/runtime-env.js";
|
|
17
|
+
export type { TokenPair, TokenClaims, SessionAuthOptions, } from "./auth/session/types.js";
|
|
18
|
+
export type { LoginOptions, LoginDeps, } from "./auth/session/login.js";
|
|
19
|
+
export type { Artifacts, LogArtifact, ResultArtifact, } from "./artifacts/types.js";
|
|
20
|
+
export type { GatewayDispatchRequest } from "./gateway/types.js";
|
|
21
|
+
export { getConfig, createConfig, resetConfig, DEFAULTS } from "./config/defaults.js";
|
|
22
|
+
export type { DispatcherConfig } from "./config/defaults.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
2
|
+
// Only symbols intended for external consumers (CLI, tooling).
|
|
3
|
+
// Internal utilities, auth internals, and type guards are not re‑exported.
|
|
4
|
+
export { dispatcherDispatch } from "./core/dispatcher.js";
|
|
5
|
+
export { executeDispatchLifecycle } from "./core/runtime-orchestrator.js";
|
|
6
|
+
export { normalizeDispatcherError } from "./core/errors.js";
|
|
7
|
+
// Errors (for exit‑code mapping & error handling)
|
|
8
|
+
export { DispatcherError, DispatchRejectedError, SessionTimeoutError, } from "./core/errors.js";
|
|
9
|
+
// Auth runtime (init / get)
|
|
10
|
+
export { initAuthRuntime, getAuthRuntime } from "./core/runtime.js";
|
|
11
|
+
// Bootstrap
|
|
12
|
+
export { readLocalGatewayConfig, writeLocalGatewayConfig, persistBootstrapMap, refreshBootstrapFromGateway, loadGatewayBootstrapConfig, bootstrapGatewayConfig, } from "./core/bootstrap.js";
|
|
13
|
+
export { ensureBootstrapped } from "./core/ensure-bootstrap.js";
|
|
14
|
+
// OAuth (GitHub)
|
|
15
|
+
export { exchangeGithubCodeForTokens } from "./auth/oauth/github.js";
|
|
16
|
+
// Session management
|
|
17
|
+
export { performInteractiveLogin } from "./auth/session/login.js";
|
|
18
|
+
export { logoutSession } from "./auth/session/logout.js";
|
|
19
|
+
export { rotateTokens as persistTokenPair } from "./auth/session/rotate.js";
|
|
20
|
+
// Configuration
|
|
21
|
+
export { getConfig, createConfig, resetConfig, DEFAULTS } from "./config/defaults.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { LogArtifact } from "../artifacts/types.js";
|
|
2
|
+
import { SessionAuthOptions } from "../auth/session/types.js";
|
|
3
|
+
export interface EncryptOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Server public key as RAW X25519 bytes encoded in base64.
|
|
6
|
+
*
|
|
7
|
+
* - Must decode to exactly 32 bytes.
|
|
8
|
+
* - Used to derive the shared secret via X25519.
|
|
9
|
+
*
|
|
10
|
+
* This is the canonical KISS format for viza gateway bootstrap.
|
|
11
|
+
*/
|
|
12
|
+
serverPublicKeyRawB64: string;
|
|
13
|
+
}
|
|
14
|
+
/****
|
|
15
|
+
* Public types for cli-dispatcher.
|
|
16
|
+
* These types form the stable contract between viza-cli and cli-dispatcher.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Input describing what to dispatch.
|
|
20
|
+
*/
|
|
21
|
+
export interface DispatchInput {
|
|
22
|
+
/**
|
|
23
|
+
* Intent name (e.g. "deploy-worker", "publish-app").
|
|
24
|
+
* Interpreted by the gateway.
|
|
25
|
+
*/
|
|
26
|
+
intent: string;
|
|
27
|
+
/**
|
|
28
|
+
* Target infra allowlist key (e.g. "aws", "cloudflare", "github", "app", "core").
|
|
29
|
+
* Used by deploy hub to checkout the correct infra repo.
|
|
30
|
+
*/
|
|
31
|
+
infraKey: string;
|
|
32
|
+
/**
|
|
33
|
+
* Execution type (infra | runtime).
|
|
34
|
+
* Determines routing behavior at the gateway.
|
|
35
|
+
* This field is REQUIRED to avoid implicit inference.
|
|
36
|
+
*/
|
|
37
|
+
runType: "infra" | "runtime";
|
|
38
|
+
/**
|
|
39
|
+
* Canonical allowlist of GitHub teams permitted to execute this command.
|
|
40
|
+
*
|
|
41
|
+
* This field is SECURITY-CRITICAL.
|
|
42
|
+
* Dispatcher/gateway authorization MUST preserve this contract end-to-end.
|
|
43
|
+
*
|
|
44
|
+
* Example:
|
|
45
|
+
* ["viza-super", "viza-admin"]
|
|
46
|
+
*/
|
|
47
|
+
allowedTeams: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Plain business payload.
|
|
50
|
+
* Payload is ALWAYS encrypted before being sent to gateway.
|
|
51
|
+
* Plain payload never leaves dispatcher.
|
|
52
|
+
*/
|
|
53
|
+
payload: unknown;
|
|
54
|
+
/**
|
|
55
|
+
* Optional resource identity for concurrency control.
|
|
56
|
+
* This is an infra-level hint and MUST NOT be part of business payload.
|
|
57
|
+
* Gateway may validate/derive and enforce concurrency using this value.
|
|
58
|
+
*/
|
|
59
|
+
resourceId: string;
|
|
60
|
+
/**
|
|
61
|
+
* Maps to GitHub repository_dispatch.event_type.
|
|
62
|
+
*/
|
|
63
|
+
commandType: string;
|
|
64
|
+
/**
|
|
65
|
+
* Optional runner label hint for execution environment.
|
|
66
|
+
* Gateway treats this as an opaque hint and forwards it downstream.
|
|
67
|
+
*/
|
|
68
|
+
runnerLabel?: string;
|
|
69
|
+
/**
|
|
70
|
+
* If true, do NOT delete source workflow logs/runs.
|
|
71
|
+
* Useful for debugging and audits.
|
|
72
|
+
* Default behavior is to allow the control-plane to delete logs for hygiene.
|
|
73
|
+
*/
|
|
74
|
+
keepLog?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Flow gates controlling which workflow setup steps are enabled.
|
|
77
|
+
* Best-effort, client-decided. May be wrong.
|
|
78
|
+
* Dispatcher and gateway MUST treat this as opaque data.
|
|
79
|
+
*/
|
|
80
|
+
flowGates?: {
|
|
81
|
+
secrets?: boolean;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Gateway-related options.
|
|
86
|
+
*/
|
|
87
|
+
export interface DispatchGatewayOptions {
|
|
88
|
+
/**
|
|
89
|
+
* Base HTTPS endpoint of the dispatch gateway.
|
|
90
|
+
* Example: https://dispatch.viza.io
|
|
91
|
+
*/
|
|
92
|
+
endpoint: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* UI-related toggles.
|
|
96
|
+
* NOTE: Dispatcher itself does NOT render UI.
|
|
97
|
+
* These flags are only passed through for caller coordination.
|
|
98
|
+
*/
|
|
99
|
+
export interface DispatchUIOptions {
|
|
100
|
+
/**
|
|
101
|
+
* Whether banner/spinner/logs are enabled.
|
|
102
|
+
* Dispatcher does not interpret these flags directly.
|
|
103
|
+
*/
|
|
104
|
+
banner?: boolean;
|
|
105
|
+
spinner?: boolean;
|
|
106
|
+
showLog?: boolean;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Lifecycle hooks invoked by interactive dispatch flows when the dispatcher
|
|
110
|
+
* needs exclusive ownership of stdin/stdout (e.g. OTP entry).
|
|
111
|
+
*
|
|
112
|
+
* The CLI MUST suspend any active spinner / runtime renderer / async
|
|
113
|
+
* stdout writer while these hooks are active. The dispatcher guarantees:
|
|
114
|
+
* - `onBeforePrompt` is called BEFORE any readline prompt is opened
|
|
115
|
+
* and BEFORE any `console.log` is emitted within the interactive
|
|
116
|
+
* section.
|
|
117
|
+
* - `onAfterPrompt` is called AFTER the readline prompt has been closed
|
|
118
|
+
* and after the interactive section has fully written its output.
|
|
119
|
+
*
|
|
120
|
+
* Hooks may be invoked multiple times (e.g. resend / retry loops). They
|
|
121
|
+
* MUST be idempotent.
|
|
122
|
+
*/
|
|
123
|
+
export interface InteractivePromptHooks {
|
|
124
|
+
/**
|
|
125
|
+
* Stop spinners / runtime renderers / async stdout writers.
|
|
126
|
+
* stdout MUST be quiet after this resolves.
|
|
127
|
+
*/
|
|
128
|
+
onBeforePrompt?: () => void | Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Resume spinners / runtime renderers.
|
|
131
|
+
* Only called after the prompt section has fully released stdout.
|
|
132
|
+
*/
|
|
133
|
+
onAfterPrompt?: () => void | Promise<void>;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Options controlling dispatch behavior.
|
|
137
|
+
*/
|
|
138
|
+
export interface DispatchOptions {
|
|
139
|
+
auth: SessionAuthOptions;
|
|
140
|
+
/**
|
|
141
|
+
* Full CLI command string used to trigger this dispatch.
|
|
142
|
+
* Passed from CLI for UX purposes (e.g., suggesting --status).
|
|
143
|
+
* Not used for routing or execution logic.
|
|
144
|
+
*/
|
|
145
|
+
fullCommand?: string;
|
|
146
|
+
interactiveAuthFlow?: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Hooks invoked by interactive flows (e.g. email OTP) so the CLI can
|
|
149
|
+
* fully suspend its runtime renderer / spinner while readline owns
|
|
150
|
+
* stdout. Without these hooks the CLI's spinner WILL repaint on top
|
|
151
|
+
* of the OTP prompt.
|
|
152
|
+
*/
|
|
153
|
+
interactivePromptHooks?: InteractivePromptHooks;
|
|
154
|
+
/**
|
|
155
|
+
* Whether to print the dispatch banner (e.g. "[user] is dispatching as team …").
|
|
156
|
+
* Default: true. Set to false for inner / recurring calls where the banner
|
|
157
|
+
* would clutter the terminal (e.g. OTP verify retry loops).
|
|
158
|
+
*/
|
|
159
|
+
banner?: boolean;
|
|
160
|
+
}
|
|
161
|
+
export type DispatchMode = "dispatch" | "status" | "cancel";
|
|
162
|
+
/**
|
|
163
|
+
* Structured result returned by hub-worker runtime commands.
|
|
164
|
+
* This is the canonical runtime execution contract.
|
|
165
|
+
*/
|
|
166
|
+
export interface RuntimeCommandResult<T = unknown> {
|
|
167
|
+
ok: boolean;
|
|
168
|
+
/**
|
|
169
|
+
* Correlation identifier of the runtime command.
|
|
170
|
+
* Usually equals the command name (e.g. "aws.login").
|
|
171
|
+
*/
|
|
172
|
+
correlationId: string;
|
|
173
|
+
/**
|
|
174
|
+
* Structured step logs produced by the runtime logger.
|
|
175
|
+
*/
|
|
176
|
+
log: unknown[];
|
|
177
|
+
/**
|
|
178
|
+
* Optional command-specific result payload.
|
|
179
|
+
* Each command may return a different shape.
|
|
180
|
+
*/
|
|
181
|
+
result?: T;
|
|
182
|
+
/**
|
|
183
|
+
* Optional structured error returned when ok=false.
|
|
184
|
+
*/
|
|
185
|
+
error?: {
|
|
186
|
+
message: string;
|
|
187
|
+
stack?: string;
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
type ExecutionResult = {
|
|
191
|
+
/**
|
|
192
|
+
* Business status returned by handler.
|
|
193
|
+
* Example: "sent" | "pending" | "verified" | "provisioned"
|
|
194
|
+
*/
|
|
195
|
+
status: string;
|
|
196
|
+
/**
|
|
197
|
+
* Optional structured business data.
|
|
198
|
+
*/
|
|
199
|
+
data?: {
|
|
200
|
+
email?: string;
|
|
201
|
+
alias?: string;
|
|
202
|
+
/**
|
|
203
|
+
* Indicates whether a new OTP was generated or an existing one is reused.
|
|
204
|
+
*/
|
|
205
|
+
isNew?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Number of attempts already used (from SOT).
|
|
208
|
+
*/
|
|
209
|
+
attempts?: number;
|
|
210
|
+
/**
|
|
211
|
+
* Number of attempts remaining (from SOT).
|
|
212
|
+
*/
|
|
213
|
+
retryAfter?: number;
|
|
214
|
+
attemptsLeft?: number;
|
|
215
|
+
maxAttempts?: number;
|
|
216
|
+
[key: string]: unknown;
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
export type DispatchResult = {
|
|
220
|
+
/**
|
|
221
|
+
* Execution via GitHub Actions control-plane.
|
|
222
|
+
*/
|
|
223
|
+
kind: "github";
|
|
224
|
+
status: string;
|
|
225
|
+
conclusion: string;
|
|
226
|
+
/**
|
|
227
|
+
* Log artifact metadata (SSOT)
|
|
228
|
+
*/
|
|
229
|
+
log?: LogArtifact;
|
|
230
|
+
/**
|
|
231
|
+
* Raw log archive buffer (zip), if downloaded.
|
|
232
|
+
*/
|
|
233
|
+
logBuffer?: Buffer;
|
|
234
|
+
/**
|
|
235
|
+
* Raw workflow artifact buffer (optional), if downloaded.
|
|
236
|
+
*/
|
|
237
|
+
resultBuffer?: Buffer;
|
|
238
|
+
} | {
|
|
239
|
+
/**
|
|
240
|
+
* Immediate execution via hub-worker runtime.
|
|
241
|
+
*
|
|
242
|
+
* Runtime commands use the canonical RuntimeCommandResult contract.
|
|
243
|
+
*/
|
|
244
|
+
kind: "runtime";
|
|
245
|
+
/**
|
|
246
|
+
* Transport/runtime execution status.
|
|
247
|
+
*/
|
|
248
|
+
status: string;
|
|
249
|
+
/**
|
|
250
|
+
* Canonical orchestration conclusion.
|
|
251
|
+
*/
|
|
252
|
+
conclusion: string;
|
|
253
|
+
/**
|
|
254
|
+
* Canonical runtime command payload.
|
|
255
|
+
*/
|
|
256
|
+
data: RuntimeCommandResult;
|
|
257
|
+
} | ({
|
|
258
|
+
/**
|
|
259
|
+
* Immediate execution via gateway (infra commands).
|
|
260
|
+
*/
|
|
261
|
+
kind: "infra";
|
|
262
|
+
} & ExecutionResult) | {
|
|
263
|
+
kind: "cancel";
|
|
264
|
+
status: "cancelled" | "already_done";
|
|
265
|
+
conclusion?: string;
|
|
266
|
+
sessionId: string;
|
|
267
|
+
};
|
|
268
|
+
export interface DispatchHandle {
|
|
269
|
+
sessionId: string;
|
|
270
|
+
wsUrl: string;
|
|
271
|
+
executionKey?: string;
|
|
272
|
+
currentUser: string;
|
|
273
|
+
wait(): Promise<DispatchResult>;
|
|
274
|
+
}
|
|
275
|
+
export {};
|