feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,156 @@
import { timingSafeEqual } from 'node:crypto';
import type { ClusterTaskExecutionRevision } from '../task-definition/clusterExecutionRevision';
import {
CLUSTER_EXECUTOR_TYPE,
normalizeClusterTaskExecutionRevision,
} from '../task-definition/clusterExecutionRevision';
import type { RunDispatchLeaseRecord } from '../run/runDispatchLease';
import {
assertRunDispatchId,
assertRunDispatchLeaseRecord,
digestRunDispatchLeaseToken,
} from '../run/runDispatchLease';
import {
assertWorkerId,
assertWorkerSessionId,
} from '../worker/workerSession';
export * from './remoteWorkerPlacement';
export const MAX_REMOTE_DISPATCH_PAGE_SIZE = 64;
const SHA256 = /^[a-f0-9]{64}$/;
export interface ClusterDispatchCandidateCursor {
readonly priority: number;
readonly queuedAtMs: number;
readonly attemptCreatedAtMs: number;
readonly attemptId: string;
}
export interface ClusterDispatchCandidate extends ClusterDispatchCandidateCursor {
readonly runId: string;
readonly projectId: string;
readonly taskId: string;
readonly taskRevision: string;
readonly attemptNumber: number;
readonly executorType: typeof CLUSTER_EXECUTOR_TYPE;
}
export interface ClusterDispatchCandidatePage {
readonly observedAtMs: number;
readonly candidates: readonly ClusterDispatchCandidate[];
readonly truncated: boolean;
readonly next?: ClusterDispatchCandidateCursor;
}
export interface ClusterDispatchRecovery {
readonly observedAtMs: number;
readonly candidate: ClusterDispatchCandidate;
readonly lease: RunDispatchLeaseRecord;
readonly workerCurrent: boolean;
}
export interface ClusterDispatchSource {
listClusterDispatchCandidates(options: Readonly<{
limit: number;
after?: ClusterDispatchCandidateCursor;
}>): Promise<ClusterDispatchCandidatePage>;
findClusterDispatchRecovery(offerId: string): Promise<ClusterDispatchRecovery | null>;
}
export interface ClusterRemoteExecutionOffer {
readonly offerId: string;
readonly deliveryKind: 'new_claim' | 'lease_recovery';
readonly executionDigest: string;
readonly candidate: ClusterDispatchCandidate;
readonly worker: Readonly<{
workerId: string;
sessionId: string;
generation: number;
}>;
readonly lease: RunDispatchLeaseRecord;
/** Ephemeral capability supplied by the Worker; never persist or log it. */
readonly leaseToken: string;
readonly executionRevision: ClusterTaskExecutionRevision;
readonly placementScore: number;
}
function invalid(message: string): never {
throw new TypeError(`Remote dispatch value is invalid: ${message}`);
}
function safeInteger(value: unknown, label: string, minimum = 0): number {
if (!Number.isSafeInteger(value) || (value as number) < minimum) invalid(`${label} is invalid`);
return value as number;
}
export function normalizeClusterDispatchCandidate(value: ClusterDispatchCandidate): ClusterDispatchCandidate {
if (!value || typeof value !== 'object' || Array.isArray(value)) invalid('candidate is invalid');
for (const [label, id] of [
['runId', value.runId], ['attemptId', value.attemptId], ['projectId', value.projectId],
['taskId', value.taskId], ['taskRevision', value.taskRevision],
] as const) assertRunDispatchId(label, id);
if (!Number.isSafeInteger(value.priority)) invalid('candidate priority is invalid');
safeInteger(value.queuedAtMs, 'candidate queuedAtMs');
safeInteger(value.attemptCreatedAtMs, 'candidate attemptCreatedAtMs');
safeInteger(value.attemptNumber, 'candidate attemptNumber', 1);
if (value.executorType !== CLUSTER_EXECUTOR_TYPE) invalid('candidate executorType is invalid');
return Object.freeze({ ...value });
}
export function normalizeClusterDispatchCursor(value: ClusterDispatchCandidateCursor): ClusterDispatchCandidateCursor {
if (!value || typeof value !== 'object' || Array.isArray(value)) invalid('cursor is invalid');
if (!Number.isSafeInteger(value.priority)) invalid('cursor priority is invalid');
safeInteger(value.queuedAtMs, 'cursor queuedAtMs');
safeInteger(value.attemptCreatedAtMs, 'cursor attemptCreatedAtMs');
assertRunDispatchId('attemptId', value.attemptId);
return Object.freeze({ ...value });
}
export function assertRemoteDispatchPageSize(limit: number): void {
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_REMOTE_DISPATCH_PAGE_SIZE) {
throw new RangeError(`Remote dispatch page size must be between 1 and ${MAX_REMOTE_DISPATCH_PAGE_SIZE}`);
}
}
export function createClusterRemoteExecutionOffer(value: ClusterRemoteExecutionOffer): ClusterRemoteExecutionOffer {
assertRunDispatchId('offerId', value.offerId);
const candidate = normalizeClusterDispatchCandidate(value.candidate);
assertWorkerId(value.worker.workerId);
assertWorkerSessionId(value.worker.sessionId);
safeInteger(value.worker.generation, 'offer worker generation', 1);
assertRunDispatchLeaseRecord(value.lease);
if (
value.lease.status !== 'leased' ||
value.lease.attemptId !== candidate.attemptId || value.lease.runId !== candidate.runId ||
value.lease.workerId !== value.worker.workerId ||
value.lease.workerSessionId !== value.worker.sessionId ||
value.lease.workerGeneration !== value.worker.generation ||
digestRunDispatchLeaseToken(value.leaseToken) !== value.lease.leaseTokenDigest
) invalid('offer authority does not match');
const executionRevision = normalizeClusterTaskExecutionRevision(value.executionRevision);
if (
executionRevision.projectId !== candidate.projectId ||
executionRevision.taskId !== candidate.taskId ||
executionRevision.taskRevision !== candidate.taskRevision
) invalid('offer execution revision does not match candidate');
if (!SHA256.test(value.executionDigest) || value.executionDigest !== executionRevision.contentDigest) invalid('offer execution digest does not match');
if (!Number.isSafeInteger(value.placementScore) || value.placementScore < 0) invalid('offer placement score is invalid');
return Object.freeze({
offerId: value.offerId,
deliveryKind: value.deliveryKind,
executionDigest: value.executionDigest,
candidate,
worker: Object.freeze({ ...value.worker }),
lease: Object.freeze({ ...value.lease }),
leaseToken: value.leaseToken,
executionRevision,
placementScore: value.placementScore,
});
}
export function leaseTokenMatchesDigest(token: string, digest: string): boolean {
const actual = Buffer.from(digestRunDispatchLeaseToken(token), 'hex');
const expected = SHA256.test(digest) ? Buffer.from(digest, 'hex') : Buffer.alloc(0);
return actual.length === expected.length && timingSafeEqual(actual, expected);
}
@@ -0,0 +1,381 @@
import type { ClusterRemoteExecutionOffer } from './remoteDispatch';
import {
createClusterRemoteExecutionOffer,
normalizeClusterDispatchCandidate,
} from './remoteDispatch';
import {
assertRunDispatchId,
assertRunDispatchLeaseToken,
digestRunDispatchLeaseToken,
} from '../run/runDispatchLease';
import {
assertWorkerId,
assertWorkerSessionId,
} from '../worker/workerSession';
export const REMOTE_EXECUTION_OFFER_DELIVERY_SCHEMA =
'qinglong/remote-execution-offer@v1';
export const MAX_REMOTE_EXECUTION_OFFER_RESPONSE_BYTES = 128 * 1024;
const IDLE_REASONS = [
'worker_unavailable',
'no_candidates',
'no_match',
'plans_unavailable',
'claim_raced',
'claim_budget_exhausted',
'scan_budget_exhausted',
] as const;
export type RemoteExecutionOfferIdleReason = (typeof IDLE_REASONS)[number];
export interface RemoteExecutionOfferClaimAuthority {
readonly workerId: string;
readonly workerSessionId: string;
readonly workerGeneration: number;
readonly offerId: string;
readonly leaseToken: string;
}
export interface RemoteExecutionOfferDeliveryStats {
readonly pages: number;
readonly candidates: number;
readonly plansUnavailable: number;
readonly placementMismatches: number;
readonly claimAttempts: number;
readonly claimRaces: number;
}
export type RemoteExecutionOfferPullResult =
| Readonly<{
status: 'offered';
offer: ClusterRemoteExecutionOffer;
stats: RemoteExecutionOfferDeliveryStats;
truncated: boolean;
}>
| Readonly<{
status: 'idle';
reason: RemoteExecutionOfferIdleReason;
stats: RemoteExecutionOfferDeliveryStats;
truncated: boolean;
}>;
export type RemoteExecutionOfferPullBody =
| Readonly<{
schema: typeof REMOTE_EXECUTION_OFFER_DELIVERY_SCHEMA;
status: 'offered';
offer: Readonly<{
offerId: string;
deliveryKind: ClusterRemoteExecutionOffer['deliveryKind'];
executionDigest: string;
candidate: ClusterRemoteExecutionOffer['candidate'];
worker: ClusterRemoteExecutionOffer['worker'];
lease: Readonly<{
version: number;
leaseGeneration: number;
acquiredAtMs: number;
renewedAtMs: number;
expiresAtMs: number;
updatedAtMs: number;
}>;
executionRevision: ClusterRemoteExecutionOffer['executionRevision'];
placementScore: number;
}>;
stats: RemoteExecutionOfferDeliveryStats;
truncated: boolean;
}>
| Readonly<{
schema: typeof REMOTE_EXECUTION_OFFER_DELIVERY_SCHEMA;
status: 'idle';
reason: RemoteExecutionOfferIdleReason;
stats: RemoteExecutionOfferDeliveryStats;
truncated: boolean;
}>;
export class InvalidRemoteExecutionOfferDeliveryError extends TypeError {
readonly code = 'REMOTE_EXECUTION_OFFER_DELIVERY_INVALID';
constructor(message: string) {
super(`Remote execution offer delivery is invalid: ${message}`);
this.name = 'InvalidRemoteExecutionOfferDeliveryError';
}
}
function invalid(message: string): never {
throw new InvalidRemoteExecutionOfferDeliveryError(message);
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return invalid(`${label} is not an object`);
}
return value as Record<string, unknown>;
}
function exactKeys(
value: Record<string, unknown>,
expected: readonly string[],
label: string,
): void {
const actual = Object.keys(value).sort();
const sorted = [...expected].sort();
if (
actual.length !== sorted.length ||
actual.some((key, index) => key !== sorted[index])
) {
invalid(`${label} shape is invalid`);
}
}
function safeInteger(value: unknown, label: string, minimum = 0): number {
if (!Number.isSafeInteger(value) || (value as number) < minimum) {
return invalid(`${label} is invalid`);
}
return value as number;
}
function boolean(value: unknown, label: string): boolean {
if (typeof value !== 'boolean') return invalid(`${label} is invalid`);
return value;
}
function normalizeOffer(
value: ClusterRemoteExecutionOffer,
): ClusterRemoteExecutionOffer {
try {
return createClusterRemoteExecutionOffer(value);
} catch (error) {
if (error instanceof InvalidRemoteExecutionOfferDeliveryError) throw error;
return invalid('offered payload is invalid');
}
}
function normalizeStats(value: unknown): RemoteExecutionOfferDeliveryStats {
const candidate = object(value, 'stats');
const keys = [
'pages',
'candidates',
'plansUnavailable',
'placementMismatches',
'claimAttempts',
'claimRaces',
] as const;
exactKeys(candidate, keys, 'stats');
const normalized = Object.freeze({
pages: safeInteger(candidate.pages, 'stats.pages'),
candidates: safeInteger(candidate.candidates, 'stats.candidates'),
plansUnavailable: safeInteger(
candidate.plansUnavailable,
'stats.plansUnavailable',
),
placementMismatches: safeInteger(
candidate.placementMismatches,
'stats.placementMismatches',
),
claimAttempts: safeInteger(
candidate.claimAttempts,
'stats.claimAttempts',
),
claimRaces: safeInteger(candidate.claimRaces, 'stats.claimRaces'),
});
if (
normalized.pages > 16 ||
normalized.candidates > 1024 ||
normalized.plansUnavailable > normalized.candidates ||
normalized.placementMismatches > normalized.candidates ||
normalized.claimAttempts > 64 ||
normalized.claimRaces > normalized.claimAttempts
) {
invalid('stats exceed the reviewed budget');
}
return normalized;
}
export function normalizeRemoteExecutionOfferClaimAuthority(
value: RemoteExecutionOfferClaimAuthority,
): RemoteExecutionOfferClaimAuthority {
const candidate = object(value, 'claim authority');
exactKeys(candidate, [
'workerId',
'workerSessionId',
'workerGeneration',
'offerId',
'leaseToken',
], 'claim authority');
assertWorkerId(value.workerId);
assertWorkerSessionId(value.workerSessionId);
safeInteger(value.workerGeneration, 'workerGeneration', 1);
assertRunDispatchId('offerId', value.offerId);
assertRunDispatchLeaseToken(value.leaseToken);
return Object.freeze({ ...value });
}
export function createRemoteExecutionOfferPullBody(
result: RemoteExecutionOfferPullResult,
): RemoteExecutionOfferPullBody {
const stats = normalizeStats(result.stats);
if (result.status === 'idle') {
if (!IDLE_REASONS.includes(result.reason)) invalid('idle reason is invalid');
return Object.freeze({
schema: REMOTE_EXECUTION_OFFER_DELIVERY_SCHEMA,
status: 'idle' as const,
reason: result.reason,
stats,
truncated: boolean(result.truncated, 'truncated'),
});
}
const offer = normalizeOffer(result.offer);
return Object.freeze({
schema: REMOTE_EXECUTION_OFFER_DELIVERY_SCHEMA,
status: 'offered' as const,
offer: Object.freeze({
offerId: offer.offerId,
deliveryKind: offer.deliveryKind,
executionDigest: offer.executionDigest,
candidate: offer.candidate,
worker: offer.worker,
lease: Object.freeze({
version: offer.lease.version,
leaseGeneration: offer.lease.leaseGeneration,
acquiredAtMs: offer.lease.acquiredAtMs,
renewedAtMs: offer.lease.renewedAtMs,
expiresAtMs: offer.lease.expiresAtMs,
updatedAtMs: offer.lease.updatedAtMs,
}),
executionRevision: offer.executionRevision,
placementScore: offer.placementScore,
}),
stats,
truncated: boolean(result.truncated, 'truncated'),
});
}
export function parseRemoteExecutionOfferPullResponse(
serialized: Uint8Array | string,
authorityValue: RemoteExecutionOfferClaimAuthority,
): RemoteExecutionOfferPullResult {
const bytes = typeof serialized === 'string'
? Buffer.from(serialized, 'utf8')
: Buffer.from(serialized);
if (
bytes.byteLength < 2 ||
bytes.byteLength > MAX_REMOTE_EXECUTION_OFFER_RESPONSE_BYTES
) {
invalid('response byte size is outside the allowed range');
}
let parsed: unknown;
try {
parsed = JSON.parse(bytes.toString('utf8')) as unknown;
} catch {
return invalid('response is not valid JSON');
}
const authority = normalizeRemoteExecutionOfferClaimAuthority(authorityValue);
const response = object(parsed, 'response');
if (response.status === 'idle') {
exactKeys(response, [
'schema', 'status', 'reason', 'stats', 'truncated',
], 'idle response');
if (
response.schema !== REMOTE_EXECUTION_OFFER_DELIVERY_SCHEMA ||
typeof response.reason !== 'string' ||
!IDLE_REASONS.includes(response.reason as RemoteExecutionOfferIdleReason)
) {
invalid('idle response fence is invalid');
}
return Object.freeze({
status: 'idle' as const,
reason: response.reason as RemoteExecutionOfferIdleReason,
stats: normalizeStats(response.stats),
truncated: boolean(response.truncated, 'truncated'),
});
}
exactKeys(response, [
'schema', 'status', 'offer', 'stats', 'truncated',
], 'offered response');
if (
response.schema !== REMOTE_EXECUTION_OFFER_DELIVERY_SCHEMA ||
response.status !== 'offered'
) {
invalid('response schema or status is invalid');
}
const wire = object(response.offer, 'offer');
exactKeys(wire, [
'offerId', 'deliveryKind', 'executionDigest', 'candidate', 'worker',
'lease', 'executionRevision', 'placementScore',
], 'offer');
if (wire.offerId !== authority.offerId) invalid('offerId does not match claim');
const worker = object(wire.worker, 'offer.worker');
exactKeys(worker, ['workerId', 'sessionId', 'generation'], 'offer.worker');
if (
worker.workerId !== authority.workerId ||
worker.sessionId !== authority.workerSessionId ||
worker.generation !== authority.workerGeneration
) {
invalid('Worker target does not match claim');
}
let candidate: ClusterRemoteExecutionOffer['candidate'];
try {
candidate = normalizeClusterDispatchCandidate(
wire.candidate as ClusterRemoteExecutionOffer['candidate'],
);
} catch {
return invalid('offer candidate is invalid');
}
const lease = object(wire.lease, 'offer.lease');
exactKeys(lease, [
'version', 'leaseGeneration', 'acquiredAtMs', 'renewedAtMs',
'expiresAtMs', 'updatedAtMs',
], 'offer.lease');
const offer = normalizeOffer({
offerId: authority.offerId,
deliveryKind: wire.deliveryKind as ClusterRemoteExecutionOffer['deliveryKind'],
executionDigest: wire.executionDigest as string,
candidate,
worker: {
workerId: authority.workerId,
sessionId: authority.workerSessionId,
generation: authority.workerGeneration,
},
lease: {
attemptId: candidate.attemptId,
runId: candidate.runId,
status: 'leased',
version: safeInteger(lease.version, 'offer.lease.version'),
leaseGeneration: safeInteger(
lease.leaseGeneration,
'offer.lease.leaseGeneration',
1,
),
workerId: authority.workerId,
workerSessionId: authority.workerSessionId,
workerGeneration: authority.workerGeneration,
leaseTokenDigest: digestRunDispatchLeaseToken(authority.leaseToken),
acquiredAtMs: safeInteger(
lease.acquiredAtMs,
'offer.lease.acquiredAtMs',
),
renewedAtMs: safeInteger(
lease.renewedAtMs,
'offer.lease.renewedAtMs',
),
expiresAtMs: safeInteger(
lease.expiresAtMs,
'offer.lease.expiresAtMs',
),
updatedAtMs: safeInteger(
lease.updatedAtMs,
'offer.lease.updatedAtMs',
),
},
leaseToken: authority.leaseToken,
executionRevision:
wire.executionRevision as ClusterRemoteExecutionOffer['executionRevision'],
placementScore: wire.placementScore as number,
});
return Object.freeze({
status: 'offered' as const,
offer,
stats: normalizeStats(response.stats),
truncated: boolean(response.truncated, 'truncated'),
});
}
@@ -0,0 +1,198 @@
import {
assertRunDispatchId,
assertRunDispatchLeaseFence,
} from '../run/runDispatchLease';
import type { RunAttemptStatus, RunStatus } from '../run/run';
export interface RemoteRunActivationFence {
readonly runId: string;
readonly attemptId: string;
readonly workerId: string;
readonly workerSessionId: string;
readonly workerGeneration: number;
readonly offerId: string;
readonly leaseGeneration: number;
/** Bearer capability. Persistence implementations must store only its digest. */
readonly leaseToken: string;
readonly expectedLeaseVersion: number;
}
export interface AcknowledgeRemoteRunStartingCommand
extends RemoteRunActivationFence {
readonly eventId: string;
}
export interface AcknowledgeRemoteRunRunningCommand
extends RemoteRunActivationFence {
readonly attemptEventId: string;
readonly runEventId: string;
readonly executorHandle: string;
readonly logArtifactId?: string;
/** Sequence bound to the completion callback capability kept by the Worker. */
readonly callbackSequence: number;
/** Lowercase SHA-256 digest. The raw callback capability never crosses storage. */
readonly callbackTokenDigest: string;
}
export interface FailRemoteRunStartCommand extends RemoteRunActivationFence {
readonly attemptEventId: string;
readonly runEventId: string;
}
export type RemoteRunActivationStatus =
| 'applied'
| 'already_starting'
| 'already_running'
| 'already_terminal';
export interface RemoteRunActivationSnapshot {
readonly runId: string;
readonly attemptId: string;
readonly runStatus: RunStatus;
readonly attemptStatus: RunAttemptStatus;
readonly leaseVersion: number;
readonly leaseGeneration: number;
readonly callbackSequence: number;
/** Database-clock deadline durably bound when starting was acknowledged. */
readonly deadlineAtMs?: number;
readonly startedAtMs?: number;
readonly finishedAtMs?: number;
readonly executorHandle?: string;
readonly logArtifactId?: string;
readonly errorCode?: string;
}
export interface RemoteRunActivationResult {
readonly status: RemoteRunActivationStatus;
readonly snapshot: Readonly<RemoteRunActivationSnapshot>;
}
export interface RemoteRunActivationRepository {
acknowledgeStarting(
command: AcknowledgeRemoteRunStartingCommand,
): Promise<Readonly<RemoteRunActivationResult>>;
acknowledgeRunning(
command: AcknowledgeRemoteRunRunningCommand,
): Promise<Readonly<RemoteRunActivationResult>>;
failStart(
command: FailRemoteRunStartCommand,
): Promise<Readonly<RemoteRunActivationResult>>;
}
export type RemoteRunActivationFenceReason =
| 'missing'
| 'run_mismatch'
| 'execution_owner_mismatch'
| 'executor_mismatch'
| 'attempt_state_mismatch'
| 'run_state_mismatch'
| 'worker_mismatch'
| 'worker_session_mismatch'
| 'worker_generation_mismatch'
| 'lease_generation_mismatch'
| 'lease_token_mismatch'
| 'offer_mismatch'
| 'version_mismatch'
| 'lease_expired'
| 'worker_unavailable'
| 'replay_mismatch';
export class RemoteRunActivationFenceRejectedError extends Error {
readonly code = 'REMOTE_RUN_ACTIVATION_FENCED';
constructor(
readonly attemptId: string,
readonly reason: RemoteRunActivationFenceReason,
) {
super(`Remote Run activation for Attempt ${attemptId} was fenced: ${reason}`);
this.name = 'RemoteRunActivationFenceRejectedError';
}
}
export class RemoteRunActivationUnavailableError extends Error {
readonly code = 'REMOTE_RUN_ACTIVATION_UNAVAILABLE';
constructor(options?: ErrorOptions) {
super('Remote Run activation storage is unavailable', options);
this.name = 'RemoteRunActivationUnavailableError';
}
}
function boundedId(name: string, value: string, maximum: number): void {
assertRunDispatchId(name, value);
if (value.length > maximum) {
throw new TypeError(`Remote Run activation ${name} is invalid`);
}
}
export function assertRemoteRunActivationFence(
value: RemoteRunActivationFence,
): void {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new TypeError('Remote Run activation fence is invalid');
}
boundedId('runId', value.runId, 36);
boundedId('attemptId', value.attemptId, 36);
boundedId('offerId', value.offerId, 128);
assertRunDispatchLeaseFence({
workerId: value.workerId,
workerSessionId: value.workerSessionId,
workerGeneration: value.workerGeneration,
leaseGeneration: value.leaseGeneration,
leaseToken: value.leaseToken,
expectedVersion: value.expectedLeaseVersion,
});
for (const number of [
value.workerGeneration,
value.leaseGeneration,
value.expectedLeaseVersion,
]) {
if (number > 2_147_483_647) {
throw new RangeError('Remote Run activation fence version is invalid');
}
}
}
export function assertAcknowledgeRemoteRunStartingCommand(
command: AcknowledgeRemoteRunStartingCommand,
): void {
assertRemoteRunActivationFence(command);
boundedId('eventId', command.eventId, 36);
}
export function assertAcknowledgeRemoteRunRunningCommand(
command: AcknowledgeRemoteRunRunningCommand,
): void {
assertRemoteRunActivationFence(command);
boundedId('attemptEventId', command.attemptEventId, 36);
boundedId('runEventId', command.runEventId, 36);
if (
typeof command.executorHandle !== 'string' ||
command.executorHandle.length < 1 ||
command.executorHandle.length > 512 ||
/[\u0000-\u001f\u007f]/.test(command.executorHandle)
) {
throw new TypeError('Remote Run activation executorHandle is invalid');
}
if (command.logArtifactId !== undefined) {
boundedId('logArtifactId', command.logArtifactId, 36);
}
if (
!Number.isSafeInteger(command.callbackSequence) ||
command.callbackSequence < 1 ||
command.callbackSequence > 2_147_483_647
) {
throw new RangeError('Remote Run activation callbackSequence is invalid');
}
if (!/^[0-9a-f]{64}$/.test(command.callbackTokenDigest)) {
throw new TypeError('Remote Run activation callbackTokenDigest is invalid');
}
}
export function assertFailRemoteRunStartCommand(
command: FailRemoteRunStartCommand,
): void {
assertRemoteRunActivationFence(command);
boundedId('attemptEventId', command.attemptEventId, 36);
boundedId('runEventId', command.runEventId, 36);
}
@@ -0,0 +1,258 @@
import { RUN_ATTEMPT_STATUSES, RUN_STATUSES } from '../run/run';
import type {
RemoteRunActivationResult,
RemoteRunActivationSnapshot,
RemoteRunActivationStatus,
} from './remoteRunActivation';
import { assertRunDispatchId } from '../run/runDispatchLease';
export const REMOTE_RUN_ACTIVATION_DELIVERY_SCHEMA =
'qinglong/remote-run-activation@v1';
export const MAX_REMOTE_RUN_ACTIVATION_RESPONSE_BYTES = 16 * 1024;
export type RemoteRunActivationResponseBody = Readonly<{
schema: typeof REMOTE_RUN_ACTIVATION_DELIVERY_SCHEMA;
status: RemoteRunActivationStatus;
snapshot: Readonly<RemoteRunActivationSnapshot>;
}>;
export class InvalidRemoteRunActivationDeliveryError extends TypeError {
readonly code = 'REMOTE_RUN_ACTIVATION_DELIVERY_INVALID';
constructor(message: string) {
super(`Remote Run activation delivery is invalid: ${message}`);
this.name = 'InvalidRemoteRunActivationDeliveryError';
}
}
const ACTIVATION_STATUSES = new Set<RemoteRunActivationStatus>([
'applied',
'already_starting',
'already_running',
'already_terminal',
]);
const REQUIRED_SNAPSHOT_KEYS = [
'runId',
'attemptId',
'runStatus',
'attemptStatus',
'leaseVersion',
'leaseGeneration',
'callbackSequence',
] as const;
const OPTIONAL_SNAPSHOT_KEYS = [
'deadlineAtMs',
'startedAtMs',
'finishedAtMs',
'executorHandle',
'logArtifactId',
'errorCode',
] as const;
const SNAPSHOT_KEYS = new Set<string>([
...REQUIRED_SNAPSHOT_KEYS,
...OPTIONAL_SNAPSHOT_KEYS,
]);
function invalid(message: string): never {
throw new InvalidRemoteRunActivationDeliveryError(message);
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return invalid(`${label} is not an object`);
}
return value as Record<string, unknown>;
}
function exactKeys(
value: Record<string, unknown>,
expected: readonly string[],
label: string,
): void {
const actual = Object.keys(value).sort();
const sorted = [...expected].sort();
if (
actual.length !== sorted.length ||
actual.some((key, index) => key !== sorted[index])
) {
invalid(`${label} shape is invalid`);
}
}
function boundedId(label: string, value: unknown, maximum: number): string {
if (typeof value !== 'string') return invalid(`${label} is invalid`);
try {
assertRunDispatchId(label, value);
} catch {
return invalid(`${label} is invalid`);
}
if (value.length > maximum) return invalid(`${label} is invalid`);
return value;
}
function boundedInteger(
label: string,
value: unknown,
maximum = 2_147_483_647,
): number {
if (
!Number.isSafeInteger(value) ||
(value as number) < 0 ||
(value as number) > maximum
) {
return invalid(`${label} is invalid`);
}
return value as number;
}
function boundedText(label: string, value: unknown, maximum: number): string {
if (
typeof value !== 'string' ||
value.length < 1 ||
value.length > maximum ||
/[\u0000-\u001f\u007f]/.test(value)
) {
return invalid(`${label} is invalid`);
}
return value;
}
function normalizeSnapshot(value: unknown): Readonly<RemoteRunActivationSnapshot> {
const snapshot = object(value, 'snapshot');
const keys = Object.keys(snapshot);
if (
REQUIRED_SNAPSHOT_KEYS.some((key) => !Object.hasOwn(snapshot, key)) ||
keys.some((key) => !SNAPSHOT_KEYS.has(key))
) {
return invalid('snapshot shape is invalid');
}
if (
typeof snapshot.runStatus !== 'string' ||
!RUN_STATUSES.includes(snapshot.runStatus as never) ||
typeof snapshot.attemptStatus !== 'string' ||
!RUN_ATTEMPT_STATUSES.includes(snapshot.attemptStatus as never)
) {
return invalid('snapshot status is invalid');
}
const normalized: RemoteRunActivationSnapshot = {
runId: boundedId('runId', snapshot.runId, 36),
attemptId: boundedId('attemptId', snapshot.attemptId, 36),
runStatus: snapshot.runStatus as RemoteRunActivationSnapshot['runStatus'],
attemptStatus:
snapshot.attemptStatus as RemoteRunActivationSnapshot['attemptStatus'],
leaseVersion: boundedInteger('leaseVersion', snapshot.leaseVersion),
leaseGeneration: boundedInteger(
'leaseGeneration',
snapshot.leaseGeneration,
),
callbackSequence: boundedInteger(
'callbackSequence',
snapshot.callbackSequence,
),
...(snapshot.deadlineAtMs === undefined
? {}
: { deadlineAtMs: boundedInteger('deadlineAtMs', snapshot.deadlineAtMs, Number.MAX_SAFE_INTEGER) }),
...(snapshot.startedAtMs === undefined
? {}
: { startedAtMs: boundedInteger('startedAtMs', snapshot.startedAtMs, Number.MAX_SAFE_INTEGER) }),
...(snapshot.finishedAtMs === undefined
? {}
: { finishedAtMs: boundedInteger('finishedAtMs', snapshot.finishedAtMs, Number.MAX_SAFE_INTEGER) }),
...(snapshot.executorHandle === undefined
? {}
: { executorHandle: boundedText('executorHandle', snapshot.executorHandle, 512) }),
...(snapshot.logArtifactId === undefined
? {}
: { logArtifactId: boundedId('logArtifactId', snapshot.logArtifactId, 36) }),
...(snapshot.errorCode === undefined
? {}
: { errorCode: boundedText('errorCode', snapshot.errorCode, 128) }),
};
return Object.freeze(normalized);
}
function normalizeResult(value: unknown): Readonly<RemoteRunActivationResult> {
const result = object(value, 'result');
exactKeys(result, ['status', 'snapshot'], 'result');
if (
typeof result.status !== 'string' ||
!ACTIVATION_STATUSES.has(result.status as RemoteRunActivationStatus)
) {
return invalid('status is invalid');
}
const normalized = Object.freeze({
status: result.status as RemoteRunActivationStatus,
snapshot: normalizeSnapshot(result.snapshot),
});
const snapshot = normalized.snapshot;
const starting = snapshot.runStatus === 'dispatching' &&
snapshot.attemptStatus === 'starting' &&
snapshot.executorHandle === undefined &&
snapshot.startedAtMs === undefined &&
snapshot.finishedAtMs === undefined &&
snapshot.errorCode === undefined;
const running = snapshot.runStatus === 'running' &&
snapshot.attemptStatus === 'running' &&
snapshot.executorHandle !== undefined &&
snapshot.startedAtMs !== undefined &&
snapshot.finishedAtMs === undefined &&
snapshot.errorCode === undefined;
const terminalStatus = snapshot.runStatus === snapshot.attemptStatus &&
['failed', 'cancelled', 'timed_out'].includes(snapshot.runStatus);
const terminal = terminalStatus &&
snapshot.executorHandle === undefined &&
snapshot.startedAtMs === undefined &&
snapshot.finishedAtMs !== undefined &&
snapshot.errorCode !== undefined;
if (!starting && !running && !terminal) {
return invalid('snapshot state is invalid');
}
if (
(normalized.status === 'already_starting' && !starting) ||
(normalized.status === 'already_running' && !running) ||
(normalized.status === 'already_terminal' && !terminal)
) {
return invalid('status and snapshot state disagree');
}
return normalized;
}
export function createRemoteRunActivationResponseBody(
result: Readonly<RemoteRunActivationResult>,
): RemoteRunActivationResponseBody {
const normalized = normalizeResult(result);
return Object.freeze({
schema: REMOTE_RUN_ACTIVATION_DELIVERY_SCHEMA,
status: normalized.status,
snapshot: normalized.snapshot,
});
}
export function parseRemoteRunActivationResponse(
serialized: Uint8Array | string,
): Readonly<RemoteRunActivationResult> {
const bytes = typeof serialized === 'string'
? Buffer.from(serialized, 'utf8')
: Buffer.from(serialized);
if (
bytes.byteLength < 2 ||
bytes.byteLength > MAX_REMOTE_RUN_ACTIVATION_RESPONSE_BYTES
) {
return invalid('response byte size is outside the allowed range');
}
let parsed: unknown;
try {
parsed = JSON.parse(bytes.toString('utf8')) as unknown;
} catch {
return invalid('response is not valid JSON');
}
const response = object(parsed, 'response');
exactKeys(response, ['schema', 'status', 'snapshot'], 'response');
if (response.schema !== REMOTE_RUN_ACTIVATION_DELIVERY_SCHEMA) {
return invalid('response schema is invalid');
}
return normalizeResult({
status: response.status,
snapshot: response.snapshot,
});
}
@@ -0,0 +1,393 @@
import { digestRunDispatchLeaseToken, assertRunDispatchId } from '../run/runDispatchLease';
import { parseSecretRef } from '../secret/secretReference';
import { assertWorkerId, assertWorkerSessionId } from '../worker/workerSession';
export const REMOTE_SECRET_DELIVERY_SCHEMA =
'qinglong/remote-secret-delivery@v1';
export const MAX_REMOTE_SECRET_DELIVERY_REFS = 64;
export const MAX_REMOTE_SECRET_DELIVERY_REQUEST_BYTES = 64 * 1024;
export const MAX_REMOTE_SECRET_DELIVERY_RESPONSE_BYTES = 128 * 1024;
export const MAX_REMOTE_SECRET_VALUE_BYTES = 16 * 1024;
export const MAX_REMOTE_SECRET_DELIVERY_TOTAL_VALUE_BYTES = 64 * 1024;
export interface RemoteWorkerSecretDeliveryCommand {
readonly workerId: string;
readonly workerSessionId: string;
readonly workerGeneration: number;
readonly runId: string;
readonly attemptId: string;
readonly projectId: string;
readonly taskId: string;
readonly taskRevision: string;
readonly executionDigest: string;
readonly offerId: string;
readonly leaseGeneration: number;
readonly leaseToken: string;
readonly expectedLeaseVersion: number;
readonly secretRefs: readonly string[];
}
export type RemoteWorkerSecretDeliveryRequestBody = Readonly<
Omit<RemoteWorkerSecretDeliveryCommand, 'workerId' | 'workerSessionId'> & {
readonly schema: typeof REMOTE_SECRET_DELIVERY_SCHEMA;
}
>;
export interface RemoteWorkerSecretDeliveryAuthority {
readonly workerId: string;
readonly workerSessionId: string;
readonly workerGeneration: number;
readonly runId: string;
readonly attemptId: string;
readonly projectId: string;
readonly taskId: string;
readonly taskRevision: string;
readonly executionDigest: string;
readonly offerId: string;
readonly leaseGeneration: number;
readonly leaseVersion: number;
readonly secretRefs: readonly string[];
}
export interface RemoteWorkerSecretValue {
readonly secretRef: string;
readonly value: string;
}
export interface RemoteWorkerSecretResolution {
readonly values: readonly RemoteWorkerSecretValue[];
readonly dispose?: () => Promise<void> | void;
}
export interface RemoteWorkerSecretDeliveryAuthorityRepository {
authorize(
command: RemoteWorkerSecretDeliveryCommand,
): Promise<Readonly<RemoteWorkerSecretDeliveryAuthority>>;
}
export interface RemoteWorkerSecretValueProvider {
resolve(
authority: Readonly<RemoteWorkerSecretDeliveryAuthority>,
): Promise<Readonly<RemoteWorkerSecretResolution> | undefined>;
}
export interface RemoteWorkerSecretDeliveryResult {
readonly runId: string;
readonly attemptId: string;
readonly offerId: string;
readonly executionDigest: string;
readonly values: readonly RemoteWorkerSecretValue[];
readonly dispose?: () => Promise<void> | void;
}
export type RemoteWorkerSecretDeliveryResponseBody = Readonly<
Omit<RemoteWorkerSecretDeliveryResult, 'dispose'> & {
readonly schema: typeof REMOTE_SECRET_DELIVERY_SCHEMA;
}
>;
export class InvalidRemoteWorkerSecretDeliveryError extends TypeError {
readonly code = 'REMOTE_SECRET_DELIVERY_INVALID';
constructor(message: string) {
super(`Remote Worker Secret delivery is invalid: ${message}`);
this.name = 'InvalidRemoteWorkerSecretDeliveryError';
}
}
export class RemoteWorkerSecretDeliveryFenceRejectedError extends Error {
readonly code = 'REMOTE_SECRET_DELIVERY_FENCED';
constructor(readonly reason: 'authority_mismatch' | 'secret_scope_mismatch') {
super(`Remote Worker Secret delivery is fenced: ${reason}`);
this.name = 'RemoteWorkerSecretDeliveryFenceRejectedError';
}
}
export class RemoteWorkerSecretDeliveryUnavailableError extends Error {
readonly code = 'REMOTE_SECRET_DELIVERY_UNAVAILABLE';
constructor() {
super('Remote Worker Secret delivery is unavailable');
this.name = 'RemoteWorkerSecretDeliveryUnavailableError';
}
}
function invalid(message: string): never {
throw new InvalidRemoteWorkerSecretDeliveryError(message);
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return invalid(`${label} is not an object`);
}
return value as Record<string, unknown>;
}
function exactKeys(
value: Record<string, unknown>,
expected: readonly string[],
label: string,
): void {
const actual = Object.keys(value).sort();
const sorted = [...expected].sort();
if (
actual.length !== sorted.length ||
actual.some((key, index) => key !== sorted[index])
) invalid(`${label} shape is invalid`);
}
function identifier(label: string, value: unknown, maximum = 128): string {
if (
typeof value !== 'string' ||
value.length < 1 ||
Buffer.byteLength(value, 'utf8') > maximum ||
/[\u0000-\u001f\u007f]/.test(value)
) return invalid(`${label} is invalid`);
return value;
}
function positiveInteger(label: string, value: unknown, minimum = 1): number {
if (
!Number.isSafeInteger(value) ||
(value as number) < minimum ||
(value as number) > 2_147_483_647
) return invalid(`${label} is invalid`);
return value as number;
}
function normalizeSecretRefs(
value: unknown,
projectId: string,
): readonly string[] {
if (
!Array.isArray(value) ||
value.length < 1 ||
value.length > MAX_REMOTE_SECRET_DELIVERY_REFS
) return invalid('secretRefs are invalid');
const seen = new Set<string>();
const refs = value.map((entry) => {
if (typeof entry !== 'string' || seen.has(entry)) {
return invalid('secretRefs are invalid');
}
try {
if (parseSecretRef(entry).projectId !== projectId) {
return invalid('secretRef project is invalid');
}
} catch (error) {
if (error instanceof InvalidRemoteWorkerSecretDeliveryError) throw error;
return invalid('secretRef is invalid');
}
seen.add(entry);
return entry;
});
return Object.freeze(refs);
}
export function normalizeRemoteWorkerSecretDeliveryCommand(
value: RemoteWorkerSecretDeliveryCommand,
): Readonly<RemoteWorkerSecretDeliveryCommand> {
const command = object(value, 'command');
exactKeys(command, [
'attemptId', 'executionDigest', 'expectedLeaseVersion', 'leaseGeneration',
'leaseToken', 'offerId', 'projectId', 'runId', 'secretRefs', 'taskId',
'taskRevision', 'workerGeneration', 'workerId', 'workerSessionId',
], 'command');
try {
assertWorkerId(command.workerId as string);
assertWorkerSessionId(command.workerSessionId as string);
assertRunDispatchId('runId', command.runId as string);
assertRunDispatchId('attemptId', command.attemptId as string);
assertRunDispatchId('offerId', command.offerId as string);
} catch {
return invalid('authority identifier is invalid');
}
const projectId = identifier('projectId', command.projectId);
const normalized = Object.freeze({
workerId: command.workerId as string,
workerSessionId: command.workerSessionId as string,
workerGeneration: positiveInteger('workerGeneration', command.workerGeneration),
runId: command.runId as string,
attemptId: command.attemptId as string,
projectId,
taskId: identifier('taskId', command.taskId),
taskRevision: identifier('taskRevision', command.taskRevision),
executionDigest: identifier('executionDigest', command.executionDigest, 64),
offerId: command.offerId as string,
leaseGeneration: positiveInteger('leaseGeneration', command.leaseGeneration),
leaseToken: identifier('leaseToken', command.leaseToken, 128),
expectedLeaseVersion: positiveInteger(
'expectedLeaseVersion', command.expectedLeaseVersion, 0,
),
secretRefs: normalizeSecretRefs(command.secretRefs, projectId),
});
if (!/^[0-9a-f]{64}$/.test(normalized.executionDigest)) {
return invalid('executionDigest is invalid');
}
try {
digestRunDispatchLeaseToken(normalized.leaseToken);
} catch {
return invalid('leaseToken is invalid');
}
return normalized;
}
export function normalizeRemoteWorkerSecretDeliveryAuthority(
value: RemoteWorkerSecretDeliveryAuthority,
): Readonly<RemoteWorkerSecretDeliveryAuthority> {
const authority = object(value, 'authority');
exactKeys(authority, [
'attemptId', 'executionDigest', 'leaseGeneration', 'leaseVersion',
'offerId', 'projectId', 'runId', 'secretRefs', 'taskId', 'taskRevision',
'workerGeneration', 'workerId', 'workerSessionId',
], 'authority');
try {
assertWorkerId(authority.workerId as string);
assertWorkerSessionId(authority.workerSessionId as string);
assertRunDispatchId('runId', authority.runId as string);
assertRunDispatchId('attemptId', authority.attemptId as string);
assertRunDispatchId('offerId', authority.offerId as string);
} catch {
return invalid('authority identifier is invalid');
}
const projectId = identifier('projectId', authority.projectId);
const normalized = Object.freeze({
workerId: authority.workerId as string,
workerSessionId: authority.workerSessionId as string,
workerGeneration: positiveInteger(
'workerGeneration', authority.workerGeneration,
),
runId: authority.runId as string,
attemptId: authority.attemptId as string,
projectId,
taskId: identifier('taskId', authority.taskId),
taskRevision: identifier('taskRevision', authority.taskRevision),
executionDigest: identifier(
'executionDigest', authority.executionDigest, 64,
),
offerId: authority.offerId as string,
leaseGeneration: positiveInteger(
'leaseGeneration', authority.leaseGeneration,
),
leaseVersion: positiveInteger('leaseVersion', authority.leaseVersion, 0),
secretRefs: normalizeSecretRefs(authority.secretRefs, projectId),
});
if (!/^[0-9a-f]{64}$/.test(normalized.executionDigest)) {
return invalid('executionDigest is invalid');
}
return normalized;
}
export function createRemoteWorkerSecretDeliveryRequestBody(
command: RemoteWorkerSecretDeliveryCommand,
): RemoteWorkerSecretDeliveryRequestBody {
const normalized = normalizeRemoteWorkerSecretDeliveryCommand(command);
const { workerId: _workerId, workerSessionId: _sessionId, ...request } = normalized;
return Object.freeze({ schema: REMOTE_SECRET_DELIVERY_SCHEMA, ...request });
}
function normalizeValues(
value: unknown,
expectedRefs: readonly string[],
): readonly RemoteWorkerSecretValue[] {
if (!Array.isArray(value) || value.length !== expectedRefs.length) {
return invalid('Secret values are invalid');
}
let totalValueBytes = 0;
return Object.freeze(value.map((entry, index) => {
const item = object(entry, `values[${index}]`);
exactKeys(item, ['secretRef', 'value'], `values[${index}]`);
if (
item.secretRef !== expectedRefs[index] ||
typeof item.value !== 'string' ||
item.value.includes('\0') ||
Buffer.byteLength(item.value, 'utf8') > MAX_REMOTE_SECRET_VALUE_BYTES
) return invalid(`values[${index}] is invalid`);
totalValueBytes += Buffer.byteLength(item.value, 'utf8');
if (totalValueBytes > MAX_REMOTE_SECRET_DELIVERY_TOTAL_VALUE_BYTES) {
return invalid('Secret value byte budget exceeded');
}
return Object.freeze({
secretRef: item.secretRef as string,
value: item.value,
});
}));
}
export function createRemoteWorkerSecretDeliveryResponseBody(
result: Readonly<RemoteWorkerSecretDeliveryResult>,
expectedRefs: readonly string[],
): RemoteWorkerSecretDeliveryResponseBody {
const value = object(result, 'result');
const allowed = ['attemptId', 'dispose', 'executionDigest', 'offerId', 'runId', 'values'];
if (Object.keys(value).some((key) => !allowed.includes(key))) {
return invalid('result shape is invalid');
}
const runId = identifier('runId', value.runId, 36);
const attemptId = identifier('attemptId', value.attemptId, 36);
const offerId = identifier('offerId', value.offerId, 128);
const executionDigest = identifier('executionDigest', value.executionDigest, 64);
if (!/^[0-9a-f]{64}$/.test(executionDigest)) invalid('executionDigest is invalid');
return Object.freeze({
schema: REMOTE_SECRET_DELIVERY_SCHEMA,
runId,
attemptId,
offerId,
executionDigest,
values: normalizeValues(value.values, expectedRefs),
});
}
export function parseRemoteWorkerSecretDeliveryResponse(
serialized: Uint8Array | string,
expected: Readonly<{
runId: string;
attemptId: string;
offerId: string;
executionDigest: string;
secretRefs: readonly string[];
}>,
): Readonly<RemoteWorkerSecretDeliveryResult> {
const bytes = typeof serialized === 'string'
? Buffer.from(serialized, 'utf8')
: Buffer.from(serialized);
if (
bytes.byteLength < 2 ||
bytes.byteLength > MAX_REMOTE_SECRET_DELIVERY_RESPONSE_BYTES
) return invalid('response byte size is outside the allowed range');
let parsed: unknown;
try {
parsed = JSON.parse(bytes.toString('utf8')) as unknown;
} catch {
return invalid('response is not valid JSON');
} finally {
bytes.fill(0);
}
const response = object(parsed, 'response');
exactKeys(response, [
'attemptId', 'executionDigest', 'offerId', 'runId', 'schema', 'values',
], 'response');
if (response.schema !== REMOTE_SECRET_DELIVERY_SCHEMA) {
return invalid('response schema is invalid');
}
const result = createRemoteWorkerSecretDeliveryResponseBody({
runId: response.runId as string,
attemptId: response.attemptId as string,
offerId: response.offerId as string,
executionDigest: response.executionDigest as string,
values: response.values as readonly RemoteWorkerSecretValue[],
}, expected.secretRefs);
if (
result.runId !== expected.runId ||
result.attemptId !== expected.attemptId ||
result.offerId !== expected.offerId ||
result.executionDigest !== expected.executionDigest
) return invalid('response authority does not match request');
return Object.freeze({
runId: result.runId,
attemptId: result.attemptId,
offerId: result.offerId,
executionDigest: result.executionDigest,
values: result.values,
});
}
@@ -0,0 +1,657 @@
import { assertRunDispatchId, digestRunDispatchLeaseToken } from '../run/runDispatchLease';
import { assertWorkerId, assertWorkerSessionId } from '../worker/workerSession';
export const REMOTE_WORKER_ARTIFACT_UPLOAD_SCHEMA =
'qinglong/remote-worker-artifact-upload@v1';
export const REMOTE_WORKER_COMPLETION_SCHEMA =
'qinglong/remote-worker-completion@v1';
export const REMOTE_WORKER_ARTIFACT_CONTENT_TYPE =
'application/vnd.qinglong.worker-artifact';
export const MAX_REMOTE_WORKER_ARTIFACT_BYTES = 64 * 1024 * 1024;
export const MAX_REMOTE_WORKER_ARTIFACT_HEADER_BYTES = 4 * 1024;
export const MAX_REMOTE_WORKER_ARTIFACT_RESPONSE_BYTES = 4 * 1024;
export const MAX_REMOTE_WORKER_COMPLETION_REQUEST_BYTES = 16 * 1024;
export const MAX_REMOTE_WORKER_COMPLETION_RESPONSE_BYTES = 4 * 1024;
const SHA256 = /^[a-f0-9]{64}$/;
const LOG_ARTIFACT_ID = /^wlog-[a-f0-9]{30}$/;
const UPLOAD_STATUSES = new Set(['stored', 'already_stored']);
const COMPLETION_STATUSES = new Set([
'applied',
'already_completed',
'already_terminal',
]);
export interface RemoteWorkerExecutionFence {
readonly workerId: string;
readonly workerSessionId: string;
readonly workerGeneration: number;
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly offerId: string;
readonly leaseGeneration: number;
readonly leaseToken: string;
readonly expectedLeaseVersion: number;
}
export interface RemoteWorkerArtifactUploadCommand
extends RemoteWorkerExecutionFence {
readonly logArtifactId: string;
readonly byteLength: number;
readonly truncated?: boolean;
}
export type RemoteWorkerArtifactUploadRequestHeader = Readonly<
Omit<
RemoteWorkerArtifactUploadCommand,
'workerId' | 'workerSessionId' | 'truncated'
> & {
readonly schema: typeof REMOTE_WORKER_ARTIFACT_UPLOAD_SCHEMA;
readonly truncated: boolean | null;
}
>;
export interface RemoteWorkerArtifactReceipt {
readonly status: 'stored' | 'already_stored';
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly logArtifactId: string;
readonly byteLength: number;
readonly sha256: string;
readonly truncated?: boolean;
}
export type RemoteWorkerArtifactUploadResponseBody = Readonly<
Omit<RemoteWorkerArtifactReceipt, 'truncated'> & {
readonly schema: typeof REMOTE_WORKER_ARTIFACT_UPLOAD_SCHEMA;
readonly truncated: boolean | null;
}
>;
export interface RemoteWorkerCompletionCommand
extends RemoteWorkerExecutionFence {
readonly callbackSequence: number;
readonly callbackTokenDigest: string;
readonly result: Readonly<{
readonly outcome: 'succeeded' | 'failed';
readonly startedAtMs: number;
readonly finishedAtMs: number;
readonly exitCode: number;
}>;
readonly artifact: Readonly<{
readonly logArtifactId: string;
readonly byteLength: number;
readonly sha256: string;
readonly truncated?: boolean;
}>;
}
export type RemoteWorkerCompletionRequestBody = Readonly<
Omit<
RemoteWorkerCompletionCommand,
'workerId' | 'workerSessionId' | 'artifact'
> & {
readonly schema: typeof REMOTE_WORKER_COMPLETION_SCHEMA;
readonly artifact: Readonly<
Omit<RemoteWorkerCompletionCommand['artifact'], 'truncated'> & {
readonly truncated: boolean | null;
}
>;
}
>;
export interface RemoteWorkerCompletionResult {
readonly status: 'applied' | 'already_completed' | 'already_terminal';
readonly runId: string;
readonly attemptId: string;
readonly callbackSequence: number;
}
export type RemoteWorkerCompletionResponseBody = Readonly<
RemoteWorkerCompletionResult & {
readonly schema: typeof REMOTE_WORKER_COMPLETION_SCHEMA;
}
>;
export interface RemoteWorkerArtifactUploadAuthorityRepository {
authorizeArtifactUpload(
command: RemoteWorkerArtifactUploadCommand,
): Promise<void>;
}
export interface RemoteWorkerCompletionRepository {
complete(
command: RemoteWorkerCompletionCommand & Readonly<{
attemptEventId: string;
runEventId: string;
}>,
): Promise<Readonly<RemoteWorkerCompletionResult>>;
}
export class InvalidRemoteWorkerCompletionError extends TypeError {
readonly code = 'REMOTE_WORKER_COMPLETION_INVALID';
constructor(message: string) {
super(`Remote Worker completion is invalid: ${message}`);
this.name = 'InvalidRemoteWorkerCompletionError';
}
}
export class RemoteWorkerCompletionFenceRejectedError extends Error {
readonly code = 'REMOTE_WORKER_COMPLETION_FENCED';
constructor(
readonly attemptId: string,
readonly reason:
| 'missing'
| 'worker_unavailable'
| 'authority_mismatch'
| 'lease_expired'
| 'state_mismatch'
| 'replay_mismatch',
) {
super(`Remote Worker completion is fenced: ${reason}`);
this.name = 'RemoteWorkerCompletionFenceRejectedError';
}
}
export class RemoteWorkerCompletionUnavailableError extends Error {
readonly code = 'REMOTE_WORKER_COMPLETION_UNAVAILABLE';
constructor(options?: ErrorOptions) {
super('Remote Worker completion is unavailable', options);
this.name = 'RemoteWorkerCompletionUnavailableError';
}
}
function invalid(message: string): never {
throw new InvalidRemoteWorkerCompletionError(message);
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return invalid(`${label} is not an object`);
}
return value as Record<string, unknown>;
}
function exactKeys(
value: Record<string, unknown>,
expected: readonly string[],
label: string,
): void {
const actual = Object.keys(value).sort();
const sorted = [...expected].sort();
if (
actual.length !== sorted.length ||
actual.some((key, index) => key !== sorted[index])
) invalid(`${label} shape is invalid`);
}
function identifier(label: string, value: unknown, maximum = 128): string {
if (
typeof value !== 'string' ||
value.length < 1 ||
Buffer.byteLength(value, 'utf8') > maximum ||
/[\u0000-\u001f\u007f]/.test(value)
) return invalid(`${label} is invalid`);
return value;
}
function integer(
label: string,
value: unknown,
minimum = 0,
maximum = Number.MAX_SAFE_INTEGER,
): number {
if (
!Number.isSafeInteger(value) ||
(value as number) < minimum ||
(value as number) > maximum
) return invalid(`${label} is invalid`);
return value as number;
}
function nullableBoolean(
label: string,
value: unknown,
): boolean | undefined {
if (value === null) return undefined;
if (typeof value !== 'boolean') return invalid(`${label} is invalid`);
return value;
}
function fence(
value: Record<string, unknown>,
): Readonly<RemoteWorkerExecutionFence> {
try {
assertWorkerId(value.workerId as string);
assertWorkerSessionId(value.workerSessionId as string);
assertRunDispatchId('runId', value.runId as string);
assertRunDispatchId('attemptId', value.attemptId as string);
assertRunDispatchId('offerId', value.offerId as string);
digestRunDispatchLeaseToken(value.leaseToken as string);
} catch {
return invalid('execution authority is invalid');
}
return Object.freeze({
workerId: value.workerId as string,
workerSessionId: value.workerSessionId as string,
workerGeneration: integer(
'workerGeneration', value.workerGeneration, 1, 2_147_483_647,
),
projectId: identifier('projectId', value.projectId),
runId: value.runId as string,
attemptId: value.attemptId as string,
offerId: value.offerId as string,
leaseGeneration: integer(
'leaseGeneration', value.leaseGeneration, 1, 2_147_483_647,
),
leaseToken: value.leaseToken as string,
expectedLeaseVersion: integer(
'expectedLeaseVersion', value.expectedLeaseVersion, 0, 2_147_483_647,
),
});
}
const FENCE_KEYS = [
'workerId', 'workerSessionId', 'workerGeneration', 'projectId', 'runId',
'attemptId', 'offerId', 'leaseGeneration', 'leaseToken',
'expectedLeaseVersion',
] as const;
export function normalizeRemoteWorkerArtifactUploadCommand(
value: RemoteWorkerArtifactUploadCommand,
): Readonly<RemoteWorkerArtifactUploadCommand> {
const command = object(value, 'Artifact upload command');
const required = [...FENCE_KEYS, 'logArtifactId', 'byteLength'];
const keys = Object.keys(command);
if (
required.some((key) => !Object.hasOwn(command, key)) ||
keys.some((key) => ![...required, 'truncated'].includes(key))
) return invalid('Artifact upload command shape is invalid');
const authority = fence(command);
const logArtifactId = identifier('logArtifactId', command.logArtifactId, 36);
if (!LOG_ARTIFACT_ID.test(logArtifactId)) {
return invalid('logArtifactId is invalid');
}
if (
command.truncated !== undefined &&
typeof command.truncated !== 'boolean'
) return invalid('truncated is invalid');
return Object.freeze({
...authority,
logArtifactId,
byteLength: integer(
'byteLength', command.byteLength, 0, MAX_REMOTE_WORKER_ARTIFACT_BYTES,
),
...(command.truncated === undefined
? {}
: { truncated: command.truncated as boolean }),
});
}
export function createRemoteWorkerArtifactUploadRequestHeader(
command: RemoteWorkerArtifactUploadCommand,
): RemoteWorkerArtifactUploadRequestHeader {
const value = normalizeRemoteWorkerArtifactUploadCommand(command);
const { workerId: _workerId, workerSessionId: _sessionId, ...body } = value;
return Object.freeze({
schema: REMOTE_WORKER_ARTIFACT_UPLOAD_SCHEMA,
...body,
truncated: value.truncated ?? null,
});
}
export function createRemoteWorkerArtifactUploadPreamble(
command: RemoteWorkerArtifactUploadCommand,
): Buffer {
const serialized = Buffer.from(JSON.stringify(
createRemoteWorkerArtifactUploadRequestHeader(command),
), 'utf8');
if (
serialized.byteLength < 2 ||
serialized.byteLength > MAX_REMOTE_WORKER_ARTIFACT_HEADER_BYTES
) {
serialized.fill(0);
return invalid('Artifact upload header exceeds its byte limit');
}
const result = Buffer.allocUnsafe(4 + serialized.byteLength);
result.writeUInt32BE(serialized.byteLength, 0);
serialized.copy(result, 4);
serialized.fill(0);
return result;
}
export function parseRemoteWorkerArtifactUploadHeader(
serialized: Uint8Array | string,
pathAuthority: Readonly<{ workerId: string; workerSessionId: string }>,
): Readonly<RemoteWorkerArtifactUploadCommand> {
const bytes = typeof serialized === 'string'
? Buffer.from(serialized, 'utf8')
: Buffer.from(serialized);
if (
bytes.byteLength < 2 ||
bytes.byteLength > MAX_REMOTE_WORKER_ARTIFACT_HEADER_BYTES
) return invalid('Artifact upload header byte size is invalid');
let parsed: unknown;
try {
parsed = JSON.parse(bytes.toString('utf8')) as unknown;
} catch {
return invalid('Artifact upload header is not valid JSON');
}
const header = object(parsed, 'Artifact upload header');
exactKeys(header, [
'schema', 'workerGeneration', 'projectId', 'runId', 'attemptId',
'offerId', 'leaseGeneration', 'leaseToken', 'expectedLeaseVersion',
'logArtifactId', 'byteLength', 'truncated',
], 'Artifact upload header');
if (header.schema !== REMOTE_WORKER_ARTIFACT_UPLOAD_SCHEMA) {
return invalid('Artifact upload schema is invalid');
}
return normalizeRemoteWorkerArtifactUploadCommand({
workerId: pathAuthority.workerId,
workerSessionId: pathAuthority.workerSessionId,
workerGeneration: header.workerGeneration as number,
projectId: header.projectId as string,
runId: header.runId as string,
attemptId: header.attemptId as string,
offerId: header.offerId as string,
leaseGeneration: header.leaseGeneration as number,
leaseToken: header.leaseToken as string,
expectedLeaseVersion: header.expectedLeaseVersion as number,
logArtifactId: header.logArtifactId as string,
byteLength: header.byteLength as number,
...(nullableBoolean('truncated', header.truncated) === undefined
? {}
: { truncated: header.truncated as boolean }),
});
}
export function normalizeRemoteWorkerArtifactReceipt(
value: RemoteWorkerArtifactReceipt,
): Readonly<RemoteWorkerArtifactReceipt> {
const receipt = object(value, 'Artifact receipt');
const required = [
'status', 'projectId', 'runId', 'attemptId', 'logArtifactId',
'byteLength', 'sha256',
];
const keys = Object.keys(receipt);
if (
required.some((key) => !Object.hasOwn(receipt, key)) ||
keys.some((key) => ![...required, 'truncated'].includes(key)) ||
typeof receipt.status !== 'string' ||
!UPLOAD_STATUSES.has(receipt.status)
) return invalid('Artifact receipt shape is invalid');
try {
assertRunDispatchId('runId', receipt.runId as string);
assertRunDispatchId('attemptId', receipt.attemptId as string);
} catch {
return invalid('Artifact receipt authority is invalid');
}
const logArtifactId = identifier('logArtifactId', receipt.logArtifactId, 36);
if (!LOG_ARTIFACT_ID.test(logArtifactId)) {
return invalid('Artifact receipt identity is invalid');
}
const sha256 = identifier('sha256', receipt.sha256, 64);
if (!SHA256.test(sha256)) return invalid('Artifact receipt digest is invalid');
if (
receipt.truncated !== undefined &&
typeof receipt.truncated !== 'boolean'
) return invalid('Artifact receipt truncation is invalid');
return Object.freeze({
status: receipt.status as RemoteWorkerArtifactReceipt['status'],
projectId: identifier('projectId', receipt.projectId),
runId: receipt.runId as string,
attemptId: receipt.attemptId as string,
logArtifactId,
byteLength: integer(
'byteLength', receipt.byteLength, 0, MAX_REMOTE_WORKER_ARTIFACT_BYTES,
),
sha256,
...(receipt.truncated === undefined
? {}
: { truncated: receipt.truncated as boolean }),
});
}
export function createRemoteWorkerArtifactUploadResponseBody(
receipt: RemoteWorkerArtifactReceipt,
): RemoteWorkerArtifactUploadResponseBody {
const value = normalizeRemoteWorkerArtifactReceipt(receipt);
return Object.freeze({
schema: REMOTE_WORKER_ARTIFACT_UPLOAD_SCHEMA,
...value,
truncated: value.truncated ?? null,
});
}
export function parseRemoteWorkerArtifactUploadResponse(
serialized: Uint8Array | string,
): Readonly<RemoteWorkerArtifactReceipt> {
const bytes = typeof serialized === 'string'
? Buffer.from(serialized, 'utf8')
: Buffer.from(serialized);
if (
bytes.byteLength < 2 ||
bytes.byteLength > MAX_REMOTE_WORKER_ARTIFACT_RESPONSE_BYTES
) return invalid('Artifact response byte size is invalid');
let parsed: unknown;
try {
parsed = JSON.parse(bytes.toString('utf8')) as unknown;
} catch {
return invalid('Artifact response is not valid JSON');
}
const response = object(parsed, 'Artifact response');
exactKeys(response, [
'schema', 'status', 'projectId', 'runId', 'attemptId', 'logArtifactId',
'byteLength', 'sha256', 'truncated',
], 'Artifact response');
if (response.schema !== REMOTE_WORKER_ARTIFACT_UPLOAD_SCHEMA) {
return invalid('Artifact response schema is invalid');
}
const truncated = nullableBoolean('truncated', response.truncated);
return normalizeRemoteWorkerArtifactReceipt({
status: response.status as RemoteWorkerArtifactReceipt['status'],
projectId: response.projectId as string,
runId: response.runId as string,
attemptId: response.attemptId as string,
logArtifactId: response.logArtifactId as string,
byteLength: response.byteLength as number,
sha256: response.sha256 as string,
...(truncated === undefined ? {} : { truncated }),
});
}
export function normalizeRemoteWorkerCompletionCommand(
value: RemoteWorkerCompletionCommand,
): Readonly<RemoteWorkerCompletionCommand> {
const command = object(value, 'completion command');
exactKeys(command, [
...FENCE_KEYS, 'callbackSequence', 'callbackTokenDigest', 'result',
'artifact',
], 'completion command');
const authority = fence(command);
const result = object(command.result, 'completion result');
exactKeys(result, [
'outcome', 'startedAtMs', 'finishedAtMs', 'exitCode',
], 'completion result');
if (
result.outcome !== 'succeeded' &&
result.outcome !== 'failed'
) return invalid('completion outcome is invalid');
const startedAtMs = integer('startedAtMs', result.startedAtMs);
const finishedAtMs = integer('finishedAtMs', result.finishedAtMs);
const exitCode = integer('exitCode', result.exitCode, 0, 255);
if (
finishedAtMs < startedAtMs ||
(result.outcome === 'succeeded') !== (exitCode === 0)
) return invalid('completion result is inconsistent');
const artifact = object(command.artifact, 'completion Artifact');
const requiredArtifact = ['logArtifactId', 'byteLength', 'sha256'];
const artifactKeys = Object.keys(artifact);
if (
requiredArtifact.some((key) => !Object.hasOwn(artifact, key)) ||
artifactKeys.some((key) => ![...requiredArtifact, 'truncated'].includes(key))
) return invalid('completion Artifact shape is invalid');
const logArtifactId = identifier('logArtifactId', artifact.logArtifactId, 36);
const sha256 = identifier('sha256', artifact.sha256, 64);
const callbackTokenDigest = identifier(
'callbackTokenDigest', command.callbackTokenDigest, 64,
);
if (
!LOG_ARTIFACT_ID.test(logArtifactId) ||
!SHA256.test(sha256) ||
!SHA256.test(callbackTokenDigest) ||
(artifact.truncated !== undefined &&
typeof artifact.truncated !== 'boolean')
) return invalid('completion evidence is invalid');
return Object.freeze({
...authority,
callbackSequence: integer(
'callbackSequence', command.callbackSequence, 1, 2_147_483_647,
),
callbackTokenDigest,
result: Object.freeze({
outcome: result.outcome as 'succeeded' | 'failed',
startedAtMs,
finishedAtMs,
exitCode,
}),
artifact: Object.freeze({
logArtifactId,
byteLength: integer(
'byteLength', artifact.byteLength, 0, MAX_REMOTE_WORKER_ARTIFACT_BYTES,
),
sha256,
...(artifact.truncated === undefined
? {}
: { truncated: artifact.truncated as boolean }),
}),
});
}
export function createRemoteWorkerCompletionRequestBody(
command: RemoteWorkerCompletionCommand,
): RemoteWorkerCompletionRequestBody {
const value = normalizeRemoteWorkerCompletionCommand(command);
const { workerId: _workerId, workerSessionId: _sessionId, ...body } = value;
return Object.freeze({
schema: REMOTE_WORKER_COMPLETION_SCHEMA,
...body,
artifact: Object.freeze({
...value.artifact,
truncated: value.artifact.truncated ?? null,
}),
});
}
export function parseRemoteWorkerCompletionRequestBody(
value: unknown,
pathAuthority: Readonly<{ workerId: string; workerSessionId: string }>,
): Readonly<RemoteWorkerCompletionCommand> {
const body = object(value, 'completion request');
exactKeys(body, [
'schema', 'workerGeneration', 'projectId', 'runId', 'attemptId',
'offerId', 'leaseGeneration', 'leaseToken', 'expectedLeaseVersion',
'callbackSequence', 'callbackTokenDigest', 'result', 'artifact',
], 'completion request');
if (body.schema !== REMOTE_WORKER_COMPLETION_SCHEMA) {
return invalid('completion schema is invalid');
}
const artifact = object(body.artifact, 'completion Artifact');
exactKeys(artifact, [
'logArtifactId', 'byteLength', 'sha256', 'truncated',
], 'completion Artifact');
const truncated = nullableBoolean('truncated', artifact.truncated);
return normalizeRemoteWorkerCompletionCommand({
workerId: pathAuthority.workerId,
workerSessionId: pathAuthority.workerSessionId,
workerGeneration: body.workerGeneration as number,
projectId: body.projectId as string,
runId: body.runId as string,
attemptId: body.attemptId as string,
offerId: body.offerId as string,
leaseGeneration: body.leaseGeneration as number,
leaseToken: body.leaseToken as string,
expectedLeaseVersion: body.expectedLeaseVersion as number,
callbackSequence: body.callbackSequence as number,
callbackTokenDigest: body.callbackTokenDigest as string,
result: body.result as RemoteWorkerCompletionCommand['result'],
artifact: {
logArtifactId: artifact.logArtifactId as string,
byteLength: artifact.byteLength as number,
sha256: artifact.sha256 as string,
...(truncated === undefined ? {} : { truncated }),
},
});
}
export function normalizeRemoteWorkerCompletionResult(
value: RemoteWorkerCompletionResult,
): Readonly<RemoteWorkerCompletionResult> {
const result = object(value, 'completion response');
exactKeys(result, [
'status', 'runId', 'attemptId', 'callbackSequence',
], 'completion response');
if (
typeof result.status !== 'string' ||
!COMPLETION_STATUSES.has(result.status)
) return invalid('completion response status is invalid');
try {
assertRunDispatchId('runId', result.runId as string);
assertRunDispatchId('attemptId', result.attemptId as string);
} catch {
return invalid('completion response authority is invalid');
}
return Object.freeze({
status: result.status as RemoteWorkerCompletionResult['status'],
runId: result.runId as string,
attemptId: result.attemptId as string,
callbackSequence: integer(
'callbackSequence', result.callbackSequence, 1, 2_147_483_647,
),
});
}
export function createRemoteWorkerCompletionResponseBody(
value: RemoteWorkerCompletionResult,
): RemoteWorkerCompletionResponseBody {
return Object.freeze({
schema: REMOTE_WORKER_COMPLETION_SCHEMA,
...normalizeRemoteWorkerCompletionResult(value),
});
}
export function parseRemoteWorkerCompletionResponse(
serialized: Uint8Array | string,
): Readonly<RemoteWorkerCompletionResult> {
const bytes = typeof serialized === 'string'
? Buffer.from(serialized, 'utf8')
: Buffer.from(serialized);
if (
bytes.byteLength < 2 ||
bytes.byteLength > MAX_REMOTE_WORKER_COMPLETION_RESPONSE_BYTES
) return invalid('completion response byte size is invalid');
let parsed: unknown;
try {
parsed = JSON.parse(bytes.toString('utf8')) as unknown;
} catch {
return invalid('completion response is not valid JSON');
}
const response = object(parsed, 'completion response envelope');
exactKeys(response, [
'schema', 'status', 'runId', 'attemptId', 'callbackSequence',
], 'completion response envelope');
if (response.schema !== REMOTE_WORKER_COMPLETION_SCHEMA) {
return invalid('completion response schema is invalid');
}
return normalizeRemoteWorkerCompletionResult({
status: response.status as RemoteWorkerCompletionResult['status'],
runId: response.runId as string,
attemptId: response.attemptId as string,
callbackSequence: response.callbackSequence as number,
});
}
@@ -0,0 +1,421 @@
import {
assertRunDispatchId,
assertRunDispatchLeaseDuration,
assertRunDispatchLeaseFence,
type RunDispatchLeaseFenceReason,
} from '../run/runDispatchLease';
import { assertWorkerId, assertWorkerSessionId } from '../worker/workerSession';
export const REMOTE_WORKER_LEASE_CONTROL_SCHEMA =
'qinglong/remote-worker-lease-control@v1';
export const MAX_REMOTE_WORKER_LEASE_CONTROL_REQUEST_BYTES = 8 * 1024;
export const MAX_REMOTE_WORKER_LEASE_CONTROL_RESPONSE_BYTES = 4 * 1024;
export const REMOTE_WORKER_STOP_REASONS = [
'user', 'policy', 'shutdown', 'reconcile', 'timeout',
] as const;
export type RemoteWorkerStopReason =
(typeof REMOTE_WORKER_STOP_REASONS)[number];
export const REMOTE_WORKER_TERMINAL_STATUSES = [
'succeeded', 'failed', 'cancelled', 'timed_out', 'lost',
] as const;
export type RemoteWorkerTerminalStatus =
(typeof REMOTE_WORKER_TERMINAL_STATUSES)[number];
export interface RemoteWorkerLeaseControlCommand {
readonly workerId: string;
readonly workerSessionId: string;
readonly workerGeneration: number;
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly offerId: string;
readonly leaseGeneration: number;
readonly leaseToken: string;
readonly expectedLeaseVersion: number;
}
export type RemoteWorkerLeaseControlRequestBody = Readonly<
Omit<RemoteWorkerLeaseControlCommand, 'workerId' | 'workerSessionId'> & {
readonly schema: typeof REMOTE_WORKER_LEASE_CONTROL_SCHEMA;
}
>;
export type RemoteWorkerLeaseControlResult = Readonly<{
readonly status: 'renewed' | 'stop_requested' | 'terminal';
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly offerId: string;
readonly leaseGeneration: number;
readonly leaseVersion?: number;
readonly renewedAtMs?: number;
readonly expiresAtMs?: number;
readonly stop?: Readonly<{
readonly reason: RemoteWorkerStopReason;
readonly requestedAtMs: number;
}>;
readonly terminalStatus?: RemoteWorkerTerminalStatus;
}>;
export type RemoteWorkerLeaseControlResponseBody = Readonly<{
readonly schema: typeof REMOTE_WORKER_LEASE_CONTROL_SCHEMA;
readonly status: RemoteWorkerLeaseControlResult['status'];
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly offerId: string;
readonly leaseGeneration: number;
readonly leaseVersion: number | null;
readonly renewedAtMs: number | null;
readonly expiresAtMs: number | null;
readonly stop: Readonly<{
readonly reason: RemoteWorkerStopReason;
readonly requestedAtMs: number;
}> | null;
readonly terminalStatus: RemoteWorkerTerminalStatus | null;
}>;
export interface RemoteWorkerLeaseControlRepository {
control(
command: RemoteWorkerLeaseControlCommand & Readonly<{
leaseDurationMs: number;
timeoutEventId: string;
}>,
): Promise<Readonly<RemoteWorkerLeaseControlResult>>;
}
export class InvalidRemoteWorkerLeaseControlError extends TypeError {
readonly code = 'REMOTE_WORKER_LEASE_CONTROL_INVALID';
constructor(message: string) {
super(`Remote Worker lease control is invalid: ${message}`);
this.name = 'InvalidRemoteWorkerLeaseControlError';
}
}
export class RemoteWorkerLeaseControlFenceRejectedError extends Error {
readonly code = 'REMOTE_WORKER_LEASE_CONTROL_FENCED';
constructor(
readonly attemptId: string,
readonly reason:
| RunDispatchLeaseFenceReason
| 'project_mismatch'
| 'execution_owner_mismatch'
| 'executor_mismatch'
| 'offer_mismatch'
| 'state_mismatch',
) {
super(`Remote Worker lease control is fenced: ${reason}`);
this.name = 'RemoteWorkerLeaseControlFenceRejectedError';
}
}
export class RemoteWorkerLeaseControlUnavailableError extends Error {
readonly code = 'REMOTE_WORKER_LEASE_CONTROL_UNAVAILABLE';
constructor(options?: ErrorOptions) {
super('Remote Worker lease control is unavailable', options);
this.name = 'RemoteWorkerLeaseControlUnavailableError';
}
}
function invalid(message: string): never {
throw new InvalidRemoteWorkerLeaseControlError(message);
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return invalid(`${label} is not an object`);
}
return value as Record<string, unknown>;
}
function exactKeys(
value: Record<string, unknown>,
expected: readonly string[],
label: string,
): void {
const actual = Object.keys(value).sort();
const sorted = [...expected].sort();
if (
actual.length !== sorted.length ||
actual.some((key, index) => key !== sorted[index])
) return invalid(`${label} shape is invalid`);
}
function integer(
label: string,
value: unknown,
minimum = 0,
maximum = Number.MAX_SAFE_INTEGER,
): number {
if (
!Number.isSafeInteger(value) ||
(value as number) < minimum ||
(value as number) > maximum
) return invalid(`${label} is invalid`);
return value as number;
}
function identifier(label: string, value: unknown, maximum = 128): string {
if (
typeof value !== 'string' || value.length < 1 ||
Buffer.byteLength(value, 'utf8') > maximum ||
/[\u0000-\u001f\u007f]/.test(value)
) return invalid(`${label} is invalid`);
return value;
}
export function normalizeRemoteWorkerLeaseControlCommand(
value: RemoteWorkerLeaseControlCommand,
): Readonly<RemoteWorkerLeaseControlCommand> {
const command = object(value, 'lease control command');
exactKeys(command, [
'workerId', 'workerSessionId', 'workerGeneration', 'projectId', 'runId',
'attemptId', 'offerId', 'leaseGeneration', 'leaseToken',
'expectedLeaseVersion',
], 'lease control command');
try {
assertWorkerId(command.workerId as string);
assertWorkerSessionId(command.workerSessionId as string);
assertRunDispatchId('runId', command.runId as string);
assertRunDispatchId('attemptId', command.attemptId as string);
assertRunDispatchId('offerId', command.offerId as string);
assertRunDispatchLeaseFence({
workerId: command.workerId as string,
workerSessionId: command.workerSessionId as string,
workerGeneration: command.workerGeneration as number,
leaseGeneration: command.leaseGeneration as number,
leaseToken: command.leaseToken as string,
expectedVersion: command.expectedLeaseVersion as number,
});
} catch {
return invalid('lease control authority is invalid');
}
return Object.freeze({
workerId: command.workerId as string,
workerSessionId: command.workerSessionId as string,
workerGeneration: integer(
'workerGeneration', command.workerGeneration, 1, 2_147_483_647,
),
projectId: identifier('projectId', command.projectId),
runId: command.runId as string,
attemptId: command.attemptId as string,
offerId: command.offerId as string,
leaseGeneration: integer(
'leaseGeneration', command.leaseGeneration, 1, 2_147_483_647,
),
leaseToken: command.leaseToken as string,
expectedLeaseVersion: integer(
'expectedLeaseVersion', command.expectedLeaseVersion,
0, 2_147_483_647,
),
});
}
export function createRemoteWorkerLeaseControlRequestBody(
value: RemoteWorkerLeaseControlCommand,
): RemoteWorkerLeaseControlRequestBody {
const command = normalizeRemoteWorkerLeaseControlCommand(value);
const { workerId: _workerId, workerSessionId: _sessionId, ...body } = command;
return Object.freeze({ schema: REMOTE_WORKER_LEASE_CONTROL_SCHEMA, ...body });
}
export function parseRemoteWorkerLeaseControlRequestBody(
value: unknown,
pathAuthority: Readonly<{ workerId: string; workerSessionId: string }>,
): Readonly<RemoteWorkerLeaseControlCommand> {
const body = object(value, 'lease control request');
exactKeys(body, [
'schema', 'workerGeneration', 'projectId', 'runId', 'attemptId',
'offerId', 'leaseGeneration', 'leaseToken', 'expectedLeaseVersion',
], 'lease control request');
if (body.schema !== REMOTE_WORKER_LEASE_CONTROL_SCHEMA) {
return invalid('lease control schema is invalid');
}
return normalizeRemoteWorkerLeaseControlCommand({
workerId: pathAuthority.workerId,
workerSessionId: pathAuthority.workerSessionId,
workerGeneration: body.workerGeneration as number,
projectId: body.projectId as string,
runId: body.runId as string,
attemptId: body.attemptId as string,
offerId: body.offerId as string,
leaseGeneration: body.leaseGeneration as number,
leaseToken: body.leaseToken as string,
expectedLeaseVersion: body.expectedLeaseVersion as number,
});
}
export function normalizeRemoteWorkerLeaseControlResult(
value: RemoteWorkerLeaseControlResult,
): Readonly<RemoteWorkerLeaseControlResult> {
const result = object(value, 'lease control result');
const allowed = [
'status', 'projectId', 'runId', 'attemptId', 'offerId', 'leaseGeneration',
'leaseVersion', 'renewedAtMs', 'expiresAtMs', 'stop', 'terminalStatus',
];
if (Object.keys(result).some((key) => !allowed.includes(key))) {
return invalid('lease control result shape is invalid');
}
for (const key of [
'status', 'projectId', 'runId', 'attemptId', 'offerId', 'leaseGeneration',
]) {
if (!Object.hasOwn(result, key)) {
return invalid('lease control result is incomplete');
}
}
const status = result.status;
if (!['renewed', 'stop_requested', 'terminal'].includes(String(status))) {
return invalid('lease control result status is invalid');
}
try {
assertRunDispatchId('runId', result.runId as string);
assertRunDispatchId('attemptId', result.attemptId as string);
assertRunDispatchId('offerId', result.offerId as string);
} catch {
return invalid('lease control result authority is invalid');
}
const common = {
projectId: identifier('projectId', result.projectId),
runId: result.runId as string,
attemptId: result.attemptId as string,
offerId: result.offerId as string,
leaseGeneration: integer(
'leaseGeneration', result.leaseGeneration, 1, 2_147_483_647,
),
};
if (status === 'terminal') {
if (
result.leaseVersion !== undefined || result.renewedAtMs !== undefined ||
result.expiresAtMs !== undefined || result.stop !== undefined ||
typeof result.terminalStatus !== 'string' ||
!REMOTE_WORKER_TERMINAL_STATUSES.includes(
result.terminalStatus as RemoteWorkerTerminalStatus,
)
) return invalid('terminal lease control result is invalid');
return Object.freeze({
status: 'terminal' as const,
...common,
terminalStatus: result.terminalStatus as RemoteWorkerTerminalStatus,
});
}
if (
result.terminalStatus !== undefined || result.leaseVersion === undefined ||
result.renewedAtMs === undefined || result.expiresAtMs === undefined
) return invalid('renewed lease control result is invalid');
const leaseVersion = integer(
'leaseVersion', result.leaseVersion, 1, 2_147_483_647,
);
const renewedAtMs = integer('renewedAtMs', result.renewedAtMs);
const expiresAtMs = integer('expiresAtMs', result.expiresAtMs);
if (expiresAtMs <= renewedAtMs) return invalid('lease expiry is invalid');
if (status === 'renewed') {
if (result.stop !== undefined) return invalid('renewed control is invalid');
return Object.freeze({
status: 'renewed' as const,
...common,
leaseVersion,
renewedAtMs,
expiresAtMs,
});
}
const stop = object(result.stop, 'stop control');
exactKeys(stop, ['reason', 'requestedAtMs'], 'stop control');
if (
typeof stop.reason !== 'string' ||
!REMOTE_WORKER_STOP_REASONS.includes(stop.reason as RemoteWorkerStopReason)
) return invalid('stop reason is invalid');
return Object.freeze({
status: 'stop_requested' as const,
...common,
leaseVersion,
renewedAtMs,
expiresAtMs,
stop: Object.freeze({
reason: stop.reason as RemoteWorkerStopReason,
requestedAtMs: integer('requestedAtMs', stop.requestedAtMs),
}),
});
}
export function createRemoteWorkerLeaseControlResponseBody(
value: RemoteWorkerLeaseControlResult,
): RemoteWorkerLeaseControlResponseBody {
const result = normalizeRemoteWorkerLeaseControlResult(value);
return Object.freeze({
schema: REMOTE_WORKER_LEASE_CONTROL_SCHEMA,
status: result.status,
projectId: result.projectId,
runId: result.runId,
attemptId: result.attemptId,
offerId: result.offerId,
leaseGeneration: result.leaseGeneration,
leaseVersion: result.leaseVersion ?? null,
renewedAtMs: result.renewedAtMs ?? null,
expiresAtMs: result.expiresAtMs ?? null,
stop: result.stop ?? null,
terminalStatus: result.terminalStatus ?? null,
});
}
export function parseRemoteWorkerLeaseControlResponse(
serialized: Uint8Array | string,
): Readonly<RemoteWorkerLeaseControlResult> {
const bytes = typeof serialized === 'string'
? Buffer.from(serialized, 'utf8')
: Buffer.from(serialized);
if (
bytes.byteLength < 2 ||
bytes.byteLength > MAX_REMOTE_WORKER_LEASE_CONTROL_RESPONSE_BYTES
) return invalid('lease control response byte size is invalid');
let parsed: unknown;
try {
parsed = JSON.parse(bytes.toString('utf8')) as unknown;
} catch {
return invalid('lease control response is not valid JSON');
}
const response = object(parsed, 'lease control response');
exactKeys(response, [
'schema', 'status', 'projectId', 'runId', 'attemptId', 'offerId',
'leaseGeneration', 'leaseVersion', 'renewedAtMs', 'expiresAtMs', 'stop',
'terminalStatus',
], 'lease control response');
if (response.schema !== REMOTE_WORKER_LEASE_CONTROL_SCHEMA) {
return invalid('lease control response schema is invalid');
}
return normalizeRemoteWorkerLeaseControlResult({
status: response.status as RemoteWorkerLeaseControlResult['status'],
projectId: response.projectId as string,
runId: response.runId as string,
attemptId: response.attemptId as string,
offerId: response.offerId as string,
leaseGeneration: response.leaseGeneration as number,
...(response.leaseVersion === null
? {}
: { leaseVersion: response.leaseVersion as number }),
...(response.renewedAtMs === null
? {}
: { renewedAtMs: response.renewedAtMs as number }),
...(response.expiresAtMs === null
? {}
: { expiresAtMs: response.expiresAtMs as number }),
...(response.stop === null
? {}
: { stop: response.stop as NonNullable<RemoteWorkerLeaseControlResult['stop']> }),
...(response.terminalStatus === null
? {}
: { terminalStatus: response.terminalStatus as RemoteWorkerTerminalStatus }),
});
}
export function assertRemoteWorkerLeaseControlDuration(value: number): void {
try {
assertRunDispatchLeaseDuration(value);
} catch {
invalid('lease duration is invalid');
}
}
@@ -0,0 +1,666 @@
import { createHash } from 'node:crypto';
import type { WorkerSessionRecord } from '../worker/workerSession';
import { assertWorkerSessionRecord } from '../worker/workerSession';
import { semver } from '../versioning/pinnedSemver';
export const REMOTE_WORKER_EXECUTOR_CAPABILITY = 'remote-worker';
export const MAX_REMOTE_PLACEMENT_VALUES = 16;
export const MAX_REMOTE_PLACEMENT_PREFERENCES = 16;
export const MAX_REMOTE_WORKER_RUNTIMES = 32;
export const MAX_REMOTE_WORKER_LABELS = 32;
export const MAX_REMOTE_WORKER_FEATURES = 32;
export const MAX_REMOTE_WORKER_GPUS = 8;
const CAPABILITY_NAME = /^[a-z0-9][a-z0-9._+-]*$/;
const LABEL_KEY = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/;
export interface RemoteWorkerRuntimeCapability {
readonly name: string;
readonly version: string;
}
export interface RemoteWorkerCapabilities {
readonly architecture: string;
readonly executors: readonly string[];
readonly operatingSystem?: string;
readonly runtimes?: readonly RemoteWorkerRuntimeCapability[];
readonly labels?: Readonly<Record<string, string>>;
readonly capacity?: Readonly<{
readonly cpuCores?: number;
readonly memoryBytes?: number;
readonly diskBytes?: number;
readonly gpu?: readonly Readonly<{
readonly vendor: string;
readonly model?: string;
readonly memoryBytes?: number;
}>[];
}>;
readonly features?: readonly string[];
}
export interface RemoteWorkerRuntimeRequirement {
readonly name: string;
readonly versionRange?: string;
}
export interface RemoteWorkerPlacementSpec {
readonly required?: Readonly<{
readonly architectures?: readonly string[];
readonly operatingSystems?: readonly string[];
readonly executors?: readonly string[];
readonly runtimes?: readonly RemoteWorkerRuntimeRequirement[];
readonly labels?: Readonly<Record<string, string>>;
readonly minMemoryBytes?: number;
readonly minDiskBytes?: number;
readonly gpuVendor?: string;
readonly features?: readonly string[];
}>;
readonly preferred?: readonly Readonly<{
readonly labels: Readonly<Record<string, string>>;
readonly weight: number;
}>[];
}
export type RemoteWorkerPlacementMismatch =
| 'worker_unavailable'
| 'architecture'
| 'operating_system'
| 'executor'
| 'runtime'
| 'label'
| 'memory'
| 'disk'
| 'gpu'
| 'feature';
export interface RemoteWorkerPlacementDecision {
readonly matches: boolean;
readonly score: number;
readonly mismatches: readonly RemoteWorkerPlacementMismatch[];
}
function invalid(message: string): never {
throw new TypeError(`Remote Worker placement value is invalid: ${message}`);
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
invalid(`${label} must be an object`);
}
return value as Record<string, unknown>;
}
function exactKeys(
value: Record<string, unknown>,
required: readonly string[],
optional: readonly string[],
label: string,
): void {
const allowed = new Set([...required, ...optional]);
if (
required.some((key) => !Object.hasOwn(value, key)) ||
Object.keys(value).some((key) => !allowed.has(key))
)
invalid(`${label} shape is invalid`);
}
function boundedString(
value: unknown,
label: string,
maximum: number,
pattern?: RegExp,
): string {
if (
typeof value !== 'string' ||
value.length < 1 ||
value.length > maximum ||
/[\u0000-\u001f\u007f]/.test(value) ||
(pattern !== undefined && !pattern.test(value))
)
invalid(`${label} is invalid`);
return value;
}
function positiveInteger(
value: unknown,
label: string,
maximum = Number.MAX_SAFE_INTEGER,
): number {
if (
!Number.isSafeInteger(value) ||
(value as number) < 1 ||
(value as number) > maximum
) {
invalid(`${label} is invalid`);
}
return value as number;
}
function sortedStrings(
value: unknown,
label: string,
maximum: number,
allowEmpty = true,
): readonly string[] {
if (
!Array.isArray(value) ||
value.length > maximum ||
(!allowEmpty && value.length === 0)
) {
invalid(`${label} is invalid`);
}
const result = value.map((item, index) =>
boundedString(item, `${label}[${index}]`, 64, CAPABILITY_NAME),
);
if (new Set(result).size !== result.length)
invalid(`${label} contains duplicates`);
return Object.freeze(result.sort());
}
function normalizedLabels(
value: unknown,
label: string,
maximum: number,
): Readonly<Record<string, string>> {
const source = object(value, label);
const entries = Object.entries(source);
if (entries.length > maximum) invalid(`${label} exceeds its item budget`);
return Object.freeze(
Object.fromEntries(
entries
.map(
([key, item]) =>
[
boundedString(key, `${label} key`, 128, LABEL_KEY),
boundedString(item, `${label}.${key}`, 256),
] as const,
)
.sort((left, right) => left[0].localeCompare(right[0])),
),
);
}
export function normalizeRemoteWorkerCapabilities(
value: unknown,
): RemoteWorkerCapabilities {
const source = object(value, 'capabilities');
exactKeys(
source,
['architecture', 'executors'],
['capacity', 'features', 'labels', 'operatingSystem', 'runtimes'],
'capabilities',
);
const architecture = boundedString(
source.architecture,
'architecture',
32,
CAPABILITY_NAME,
);
const executors = sortedStrings(source.executors, 'executors', 16, false);
const operatingSystem =
source.operatingSystem === undefined
? undefined
: boundedString(
source.operatingSystem,
'operatingSystem',
32,
CAPABILITY_NAME,
);
let runtimes: readonly RemoteWorkerRuntimeCapability[] | undefined;
if (source.runtimes !== undefined) {
if (
!Array.isArray(source.runtimes) ||
source.runtimes.length > MAX_REMOTE_WORKER_RUNTIMES
)
invalid('runtimes is invalid');
const mapped = source.runtimes.map((item, index) => {
const runtime = object(item, `runtimes[${index}]`);
exactKeys(runtime, ['name', 'version'], [], `runtimes[${index}]`);
const name = boundedString(
runtime.name,
`runtimes[${index}].name`,
64,
CAPABILITY_NAME,
);
const version = boundedString(
runtime.version,
`runtimes[${index}].version`,
64,
);
if (semver().valid(version) === null)
invalid(`runtimes[${index}].version is not semver`);
return Object.freeze({ name, version });
});
if (new Set(mapped.map((item) => item.name)).size !== mapped.length)
invalid('runtimes repeats a runtime name');
runtimes = Object.freeze(
mapped.sort((left, right) => left.name.localeCompare(right.name)),
);
}
const labels =
source.labels === undefined
? undefined
: normalizedLabels(source.labels, 'labels', MAX_REMOTE_WORKER_LABELS);
let capacity: RemoteWorkerCapabilities['capacity'];
if (source.capacity !== undefined) {
const candidate = object(source.capacity, 'capacity');
exactKeys(
candidate,
[],
['cpuCores', 'diskBytes', 'gpu', 'memoryBytes'],
'capacity',
);
let gpu: NonNullable<RemoteWorkerCapabilities['capacity']>['gpu'];
if (candidate.gpu !== undefined) {
if (
!Array.isArray(candidate.gpu) ||
candidate.gpu.length > MAX_REMOTE_WORKER_GPUS
)
invalid('capacity.gpu is invalid');
gpu = Object.freeze(
candidate.gpu.map((item, index) => {
const device = object(item, `capacity.gpu[${index}]`);
exactKeys(
device,
['vendor'],
['memoryBytes', 'model'],
`capacity.gpu[${index}]`,
);
return Object.freeze({
vendor: boundedString(
device.vendor,
`capacity.gpu[${index}].vendor`,
64,
CAPABILITY_NAME,
),
...(device.model === undefined
? {}
: {
model: boundedString(
device.model,
`capacity.gpu[${index}].model`,
128,
),
}),
...(device.memoryBytes === undefined
? {}
: {
memoryBytes: positiveInteger(
device.memoryBytes,
`capacity.gpu[${index}].memoryBytes`,
),
}),
});
}),
);
}
capacity = Object.freeze({
...(candidate.cpuCores === undefined
? {}
: {
cpuCores: positiveInteger(
candidate.cpuCores,
'capacity.cpuCores',
4096,
),
}),
...(candidate.memoryBytes === undefined
? {}
: {
memoryBytes: positiveInteger(
candidate.memoryBytes,
'capacity.memoryBytes',
),
}),
...(candidate.diskBytes === undefined
? {}
: {
diskBytes: positiveInteger(
candidate.diskBytes,
'capacity.diskBytes',
),
}),
...(gpu === undefined ? {} : { gpu }),
});
}
const features =
source.features === undefined
? undefined
: sortedStrings(source.features, 'features', MAX_REMOTE_WORKER_FEATURES);
return Object.freeze({
architecture,
executors,
...(operatingSystem === undefined ? {} : { operatingSystem }),
...(runtimes === undefined ? {} : { runtimes }),
...(labels === undefined ? {} : { labels }),
...(capacity === undefined ? {} : { capacity }),
...(features === undefined ? {} : { features }),
});
}
export function canonicalRemoteWorkerCapabilities(value: unknown): Readonly<{
capabilities: RemoteWorkerCapabilities;
json: string;
hash: string;
}> {
const capabilities = normalizeRemoteWorkerCapabilities(value);
const json = JSON.stringify(capabilities);
return Object.freeze({
capabilities,
json,
hash: createHash('sha256').update(json, 'utf8').digest('hex'),
});
}
export function parseRemoteWorkerCapabilities(
record: WorkerSessionRecord,
): RemoteWorkerCapabilities {
assertWorkerSessionRecord(record);
const canonical = canonicalRemoteWorkerCapabilities(
JSON.parse(record.capabilitiesJson) as unknown,
);
if (
canonical.json !== record.capabilitiesJson ||
canonical.hash !== record.capabilitiesHash
) {
invalid('capabilities snapshot is not canonical');
}
return canonical.capabilities;
}
function optionalStringList(
value: unknown,
label: string,
): readonly string[] | undefined {
return value === undefined
? undefined
: sortedStrings(value, label, MAX_REMOTE_PLACEMENT_VALUES);
}
export function normalizeRemoteWorkerPlacement(
value: unknown,
): RemoteWorkerPlacementSpec {
const source = object(value, 'placement');
exactKeys(source, [], ['preferred', 'required'], 'placement');
let required: RemoteWorkerPlacementSpec['required'];
if (source.required !== undefined) {
const candidate = object(source.required, 'placement.required');
exactKeys(
candidate,
[],
[
'architectures',
'executors',
'features',
'gpuVendor',
'labels',
'minDiskBytes',
'minMemoryBytes',
'operatingSystems',
'runtimes',
],
'placement.required',
);
const architectures = optionalStringList(
candidate.architectures,
'placement.required.architectures',
);
const operatingSystems = optionalStringList(
candidate.operatingSystems,
'placement.required.operatingSystems',
);
const executors = optionalStringList(
candidate.executors,
'placement.required.executors',
);
const features = optionalStringList(
candidate.features,
'placement.required.features',
);
let runtimes: readonly RemoteWorkerRuntimeRequirement[] | undefined;
if (candidate.runtimes !== undefined) {
if (
!Array.isArray(candidate.runtimes) ||
candidate.runtimes.length > MAX_REMOTE_PLACEMENT_VALUES
)
invalid('placement.required.runtimes is invalid');
const mapped = candidate.runtimes.map((item, index) => {
const runtime = object(item, `placement.required.runtimes[${index}]`);
exactKeys(
runtime,
['name'],
['versionRange'],
`placement.required.runtimes[${index}]`,
);
const name = boundedString(
runtime.name,
`placement.required.runtimes[${index}].name`,
64,
CAPABILITY_NAME,
);
const versionRange =
runtime.versionRange === undefined
? undefined
: boundedString(
runtime.versionRange,
`placement.required.runtimes[${index}].versionRange`,
128,
);
if (
versionRange !== undefined &&
semver().validRange(versionRange) === null
)
invalid(
`placement.required.runtimes[${index}].versionRange is not semver`,
);
return Object.freeze({
name,
...(versionRange === undefined ? {} : { versionRange }),
});
});
if (new Set(mapped.map((item) => item.name)).size !== mapped.length)
invalid('placement.required.runtimes repeats a runtime name');
runtimes = Object.freeze(
mapped.sort((left, right) => left.name.localeCompare(right.name)),
);
}
required = Object.freeze({
...(architectures === undefined ? {} : { architectures }),
...(operatingSystems === undefined ? {} : { operatingSystems }),
...(executors === undefined ? {} : { executors }),
...(runtimes === undefined ? {} : { runtimes }),
...(candidate.labels === undefined
? {}
: {
labels: normalizedLabels(
candidate.labels,
'placement.required.labels',
MAX_REMOTE_PLACEMENT_VALUES,
),
}),
...(candidate.minMemoryBytes === undefined
? {}
: {
minMemoryBytes: positiveInteger(
candidate.minMemoryBytes,
'placement.required.minMemoryBytes',
),
}),
...(candidate.minDiskBytes === undefined
? {}
: {
minDiskBytes: positiveInteger(
candidate.minDiskBytes,
'placement.required.minDiskBytes',
),
}),
...(candidate.gpuVendor === undefined
? {}
: {
gpuVendor: boundedString(
candidate.gpuVendor,
'placement.required.gpuVendor',
64,
CAPABILITY_NAME,
),
}),
...(features === undefined ? {} : { features }),
});
}
let preferred: RemoteWorkerPlacementSpec['preferred'];
if (source.preferred !== undefined) {
if (
!Array.isArray(source.preferred) ||
source.preferred.length > MAX_REMOTE_PLACEMENT_PREFERENCES
)
invalid('placement.preferred is invalid');
preferred = Object.freeze(
source.preferred.map((item, index) => {
const preference = object(item, `placement.preferred[${index}]`);
exactKeys(
preference,
['labels', 'weight'],
[],
`placement.preferred[${index}]`,
);
const labels = normalizedLabels(
preference.labels,
`placement.preferred[${index}].labels`,
MAX_REMOTE_PLACEMENT_VALUES,
);
if (Object.keys(labels).length === 0)
invalid(`placement.preferred[${index}].labels is empty`);
return Object.freeze({
labels,
weight: positiveInteger(
preference.weight,
`placement.preferred[${index}].weight`,
100,
),
});
}),
);
}
return Object.freeze({
...(required === undefined ? {} : { required }),
...(preferred === undefined ? {} : { preferred }),
});
}
export function effectiveRemoteWorkerPlacement(
value: unknown,
): RemoteWorkerPlacementSpec {
const placement = normalizeRemoteWorkerPlacement(value ?? {});
const executors = placement.required?.executors;
if (
executors !== undefined &&
!executors.includes(REMOTE_WORKER_EXECUTOR_CAPABILITY)
) {
invalid(`placement must require ${REMOTE_WORKER_EXECUTOR_CAPABILITY}`);
}
return normalizeRemoteWorkerPlacement({
...placement,
required: {
...placement.required,
executors: executors ?? [REMOTE_WORKER_EXECUTOR_CAPABILITY],
},
});
}
function containsLabels(
actual: Readonly<Record<string, string>> | undefined,
expected: Readonly<Record<string, string>>,
): boolean {
return Object.entries(expected).every(
([key, value]) => actual?.[key] === value,
);
}
export function evaluateRemoteWorkerPlacement(
worker: WorkerSessionRecord,
placementValue: unknown,
observedAtMs: number,
): RemoteWorkerPlacementDecision {
assertWorkerSessionRecord(worker);
if (!Number.isSafeInteger(observedAtMs) || observedAtMs < 0)
invalid('observedAtMs is invalid');
const capabilities = parseRemoteWorkerCapabilities(worker);
const placement = effectiveRemoteWorkerPlacement(placementValue);
const required = placement.required ?? {};
const mismatches: RemoteWorkerPlacementMismatch[] = [];
if (
worker.status !== 'online' ||
worker.availableSlots < 1 ||
worker.leaseExpiresAtMs <= observedAtMs
)
mismatches.push('worker_unavailable');
if (
required.architectures?.length &&
!required.architectures.includes(capabilities.architecture)
)
mismatches.push('architecture');
if (
required.operatingSystems?.length &&
(!capabilities.operatingSystem ||
!required.operatingSystems.includes(capabilities.operatingSystem))
)
mismatches.push('operating_system');
if (
required.executors?.some(
(executor) => !capabilities.executors.includes(executor),
)
)
mismatches.push('executor');
if (
required.runtimes?.some(
(requirement) =>
!(capabilities.runtimes ?? []).some(
(runtime) =>
runtime.name === requirement.name &&
(requirement.versionRange === undefined ||
semver().satisfies(runtime.version, requirement.versionRange, {
includePrerelease: true,
})),
),
)
)
mismatches.push('runtime');
if (required.labels && !containsLabels(capabilities.labels, required.labels))
mismatches.push('label');
if (
required.minMemoryBytes !== undefined &&
(capabilities.capacity?.memoryBytes ?? 0) < required.minMemoryBytes
)
mismatches.push('memory');
if (
required.minDiskBytes !== undefined &&
(capabilities.capacity?.diskBytes ?? 0) < required.minDiskBytes
)
mismatches.push('disk');
if (
required.gpuVendor !== undefined &&
!(capabilities.capacity?.gpu ?? []).some(
(gpu) => gpu.vendor === required.gpuVendor,
)
)
mismatches.push('gpu');
if (
required.features?.some(
(feature) => !(capabilities.features ?? []).includes(feature),
)
)
mismatches.push('feature');
const score = (placement.preferred ?? []).reduce(
(total, preference) =>
total +
(containsLabels(capabilities.labels, preference.labels)
? preference.weight
: 0),
0,
);
return Object.freeze({
matches: mismatches.length === 0,
score,
mismatches: Object.freeze(mismatches),
});
}