@runuai/host 0.9.149 → 0.9.151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/db/migrations/0027_cloud_credential_foundation.sql +51 -0
- package/db/migrations/0028_leased_engine_runtimes.sql +11 -0
- package/db/migrations/meta/_journal.json +14 -0
- package/db/schema.ts +188 -0
- package/lib/agent-cli.ts +11 -13
- package/lib/agent.ts +177 -7
- package/lib/agents/durable-proc.ts +56 -44
- package/lib/agents/registry.ts +19 -3
- package/lib/agents/session-state.ts +23 -10
- package/lib/agents/transport.ts +101 -69
- package/lib/apple-one-off.ts +277 -0
- package/lib/apple-runtime-recycle.ts +4 -3
- package/lib/attachments.ts +19 -17
- package/lib/capture-flow.ts +319 -0
- package/lib/cloud-credential-cleanup-adapter.ts +123 -0
- package/lib/cloud-credential-host.ts +512 -0
- package/lib/cloud-credential-leases.ts +2544 -0
- package/lib/cloud-credential-runtime.ts +1035 -0
- package/lib/codex-auth.ts +21 -1
- package/lib/container-runtime.ts +76 -0
- package/lib/credential-cleanup-inventory.ts +405 -0
- package/lib/credential-consumers/engine-anchor.ts +1876 -0
- package/lib/credential-consumers/engines.ts +773 -0
- package/lib/credential-consumers/github.ts +594 -0
- package/lib/credential-consumers/mcp.ts +384 -0
- package/lib/credential-consumers/task-files.ts +127 -0
- package/lib/credential-store-pickup.ts +15 -2
- package/lib/credential-store.ts +361 -33
- package/lib/credentials-activation-authority.ts +546 -0
- package/lib/credentials-purge.ts +1006 -0
- package/lib/db.ts +11 -0
- package/lib/docker-exec.ts +3 -1
- package/lib/engine-accounts.ts +42 -32
- package/lib/engine-connect.ts +47 -2
- package/lib/engine-credential-env.ts +50 -0
- package/lib/engine-login.ts +83 -1
- package/lib/env-file.ts +62 -8
- package/lib/env.ts +5 -0
- package/lib/fs-aliases.ts +49 -0
- package/lib/git-clone-credentials.ts +334 -0
- package/lib/git-diff.ts +219 -177
- package/lib/git-identity.ts +59 -14
- package/lib/github-origin.ts +28 -0
- package/lib/github-tokens.ts +56 -21
- package/lib/host-env.ts +17 -4
- package/lib/launch-mode.ts +25 -0
- package/lib/legacy-runtime-retirement.ts +388 -0
- package/lib/machine-exec.ts +14 -3
- package/lib/machine-provider-aws.ts +50 -8
- package/lib/machine-provider-local.ts +4 -0
- package/lib/machine-provider.ts +4 -0
- package/lib/managed-operation-drain.ts +8 -0
- package/lib/managed-runtime.ts +371 -7
- package/lib/mcp-capture.ts +860 -0
- package/lib/mcp-connections.ts +8 -1
- package/lib/mcp-gateway.ts +168 -10
- package/lib/obs.ts +2 -6
- package/lib/orchestrator.ts +1847 -236
- package/lib/person-capture.ts +327 -0
- package/lib/project-env-legacy-tags.ts +130 -0
- package/lib/project-env-task-replica.ts +1752 -0
- package/lib/runner-boot.ts +10 -0
- package/lib/runtime-activation-tail.ts +50 -3
- package/lib/runtime-archive.ts +68 -1
- package/lib/runtime-reconnect.ts +86 -0
- package/lib/runtime-state.ts +429 -13
- package/lib/safe-http.ts +364 -0
- package/lib/secrets-proxy/task-trust.ts +34 -3
- package/lib/sidecar-resume.ts +18 -5
- package/lib/skills.ts +47 -8
- package/lib/standard-image.ts +157 -103
- package/lib/stopped-task-status-queue.ts +68 -10
- package/lib/task-credential-cleanup.ts +525 -0
- package/lib/task-diff-sandbox.ts +464 -0
- package/lib/task-diff.ts +66 -163
- package/lib/task-env-source.ts +68 -14
- package/lib/task-environment/anchor.ts +31 -0
- package/lib/task-environment/apple-container.ts +69 -28
- package/lib/task-environment/docker.ts +101 -20
- package/lib/task-environment/machine-environment.ts +61 -0
- package/lib/task-environment/machine-task-up.ts +459 -96
- package/lib/task-environment/machine.ts +101 -11
- package/lib/task-environment/self.ts +108 -50
- package/lib/task-environment/types.ts +89 -5
- package/lib/task-environment/workspace-files.ts +623 -25
- package/lib/task-environment/workspace-runtime.fixture.ts +50 -0
- package/lib/task-gateway-recovery.ts +4 -1
- package/lib/task-gateway-state.ts +15 -0
- package/lib/task-machine-swap-safety.ts +729 -0
- package/lib/token-broker-adopt.ts +344 -0
- package/lib/token-broker-lambda.ts +56 -2
- package/lib/token-broker-local.ts +28 -4
- package/lib/token-broker-revision.ts +310 -0
- package/lib/token-broker-ssm.ts +368 -28
- package/lib/token-lanes/openai-oauth.ts +7 -3
- package/lib/token-lanes/xai-oauth.ts +7 -3
- package/lib/transcript.ts +9 -5
- package/package.json +6 -2
- package/scripts/agent/task-up.sh +242 -100
- package/src/cloud-credentials.ts +2399 -0
- package/src/event-outbox.ts +8 -6
- package/src/index.ts +1386 -875
- package/src/main.ts +1062 -140
- package/src/outbound-frame.ts +31 -0
- package/src/project-env-policy.ts +125 -10
- package/src/protocol.ts +699 -23
- package/src/ui/server.ts +48 -10
package/src/protocol.ts
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseCaptureFlowWire,
|
|
3
|
+
parseCaptureResultWire,
|
|
4
|
+
type CaptureFlowWire,
|
|
5
|
+
type CaptureResultWire,
|
|
6
|
+
} from "./cloud-credentials";
|
|
7
|
+
import type {
|
|
8
|
+
GithubCaptureFrame,
|
|
9
|
+
GithubRevokeAckFrame,
|
|
10
|
+
GithubRevokeFrame,
|
|
11
|
+
McpCaptureAckFrame,
|
|
12
|
+
McpCaptureCancelFrame,
|
|
13
|
+
McpCaptureCompleteFrame,
|
|
14
|
+
McpCaptureProbeFrame,
|
|
15
|
+
PersonCaptureAckFrame,
|
|
16
|
+
SshMintFrame,
|
|
17
|
+
CredentialChangedFrame,
|
|
18
|
+
CredentialRefreshFrame,
|
|
19
|
+
CredentialRefreshResultFrame,
|
|
20
|
+
CredentialsActivateFrame,
|
|
21
|
+
CredentialsActivateResultFrame,
|
|
22
|
+
CredentialsActivatedFrame,
|
|
23
|
+
LeaseGrantV1Frame,
|
|
24
|
+
LeaseRequestV1Frame,
|
|
25
|
+
LeaseStaleFrame,
|
|
26
|
+
LeaseUnavailableFrame,
|
|
27
|
+
} from "./cloud-credentials";
|
|
28
|
+
|
|
1
29
|
export enum HostErrorCode {
|
|
2
30
|
TaskNotFound = "task_not_found",
|
|
3
31
|
ProjectNotFound = "project_not_found",
|
|
@@ -238,6 +266,14 @@ export type ProjectEnvDirective =
|
|
|
238
266
|
| { kind: "omit" }
|
|
239
267
|
| { kind: "unsupported"; reason: ProjectEnvUnsupportedReason };
|
|
240
268
|
|
|
269
|
+
/** The shape of a proxied key's placeholder (`@placeholder`): a prefix the
|
|
270
|
+
* tools that check a key's format expect, then random characters, to this
|
|
271
|
+
* total length. A shape only, never a literal. */
|
|
272
|
+
export interface ProjectEnvPlaceholderShape {
|
|
273
|
+
prefix: string;
|
|
274
|
+
totalLength: number;
|
|
275
|
+
}
|
|
276
|
+
|
|
241
277
|
/** Cloud-safe declaration captured from a trusted `.env.schema` base.
|
|
242
278
|
* `placeholder` is a shape only: the repository's literal never crosses the
|
|
243
279
|
* bridge and is never used as a task placeholder. */
|
|
@@ -247,10 +283,7 @@ export interface ProjectEnvSchemaDeclaration {
|
|
|
247
283
|
sensitive: boolean;
|
|
248
284
|
type?: string;
|
|
249
285
|
directive: ProjectEnvDirective;
|
|
250
|
-
placeholder?:
|
|
251
|
-
prefix: string;
|
|
252
|
-
totalLength: number;
|
|
253
|
-
};
|
|
286
|
+
placeholder?: ProjectEnvPlaceholderShape;
|
|
254
287
|
}
|
|
255
288
|
|
|
256
289
|
/** A schema condition that cannot be represented on the declaration itself.
|
|
@@ -289,7 +322,17 @@ export type ProjectEnvSchemaReport = ProjectEnvSchemaObservation;
|
|
|
289
322
|
|
|
290
323
|
/** The most permissive policy the owner has approved for one stored value. */
|
|
291
324
|
export type ProjectEnvBinding =
|
|
292
|
-
| {
|
|
325
|
+
| {
|
|
326
|
+
kind: "proxy";
|
|
327
|
+
domains: string[];
|
|
328
|
+
/**
|
|
329
|
+
* ADR-154 §9: the owner declared this key proxied in Settings, which
|
|
330
|
+
* the repository's schema can only narrow, with the placeholder shape
|
|
331
|
+
* Settings gives it (null: the default). Valid only under policy epoch
|
|
332
|
+
* 2 or later; an earlier epoch reads it as a binding it cannot accept.
|
|
333
|
+
*/
|
|
334
|
+
declared?: { placeholder: ProjectEnvPlaceholderShape | null };
|
|
335
|
+
}
|
|
293
336
|
| { kind: "passthrough" }
|
|
294
337
|
| { kind: "omit" }
|
|
295
338
|
| { kind: "withhold"; reason: ProjectEnvWithholdReason };
|
|
@@ -484,10 +527,15 @@ export function isProjectEnvSchemaDeclaration(
|
|
|
484
527
|
}
|
|
485
528
|
if (object.placeholder === undefined) return true;
|
|
486
529
|
if (object.directive.kind !== "proxy") return false;
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
530
|
+
return isProjectEnvPlaceholderShape(object.placeholder);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/** A placeholder shape: a prefix of the allowed characters, and room for at
|
|
534
|
+
* least 32 random characters after it, within the length bound. */
|
|
535
|
+
export function isProjectEnvPlaceholderShape(
|
|
536
|
+
value: unknown,
|
|
537
|
+
): value is ProjectEnvPlaceholderShape {
|
|
538
|
+
const placeholder = exactWireObject(value, ["prefix", "totalLength"]);
|
|
491
539
|
return (
|
|
492
540
|
placeholder !== null &&
|
|
493
541
|
typeof placeholder.prefix === "string" &&
|
|
@@ -638,15 +686,24 @@ export function isProjectEnvBinding(
|
|
|
638
686
|
return exactWireObject(value, ["kind"]) !== null;
|
|
639
687
|
}
|
|
640
688
|
if (kind === "proxy") {
|
|
641
|
-
const object = exactWireObject(value, ["kind", "domains"]);
|
|
642
|
-
|
|
643
|
-
object
|
|
644
|
-
isSortedUniqueStringArray(
|
|
689
|
+
const object = exactWireObject(value, ["kind", "domains"], ["declared"]);
|
|
690
|
+
if (
|
|
691
|
+
object === null ||
|
|
692
|
+
!isSortedUniqueStringArray(
|
|
645
693
|
object.domains,
|
|
646
694
|
MAX_PROJECT_ENV_PROXY_DOMAINS,
|
|
647
695
|
isProjectEnvDomain,
|
|
648
|
-
)
|
|
649
|
-
object.domains.length
|
|
696
|
+
) ||
|
|
697
|
+
object.domains.length === 0
|
|
698
|
+
) {
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
if (object.declared === undefined) return true;
|
|
702
|
+
const declared = exactWireObject(object.declared, ["placeholder"]);
|
|
703
|
+
return (
|
|
704
|
+
declared !== null &&
|
|
705
|
+
(declared.placeholder === null ||
|
|
706
|
+
isProjectEnvPlaceholderShape(declared.placeholder))
|
|
650
707
|
);
|
|
651
708
|
}
|
|
652
709
|
if (kind === "withhold") {
|
|
@@ -656,6 +713,15 @@ export function isProjectEnvBinding(
|
|
|
656
713
|
return false;
|
|
657
714
|
}
|
|
658
715
|
|
|
716
|
+
/** A binding the owner declared in Settings (ADR-154 §9). */
|
|
717
|
+
export function isDeclaredProjectEnvBinding(
|
|
718
|
+
binding: ProjectEnvBinding | null,
|
|
719
|
+
): binding is Extract<ProjectEnvBinding, { kind: "proxy" }> & {
|
|
720
|
+
declared: { placeholder: ProjectEnvPlaceholderShape | null };
|
|
721
|
+
} {
|
|
722
|
+
return binding?.kind === "proxy" && binding.declared !== undefined;
|
|
723
|
+
}
|
|
724
|
+
|
|
659
725
|
/** Where a key's live value is (ADR-153 §4, "Value references"): in the
|
|
660
726
|
* record's own row (owned runners), in a staged parameter named by its
|
|
661
727
|
* staging id, or in the key's legacy parameter at one exact SSM version. */
|
|
@@ -1095,6 +1161,38 @@ export type ProjectEnvStagingAckFrame =
|
|
|
1095
1161
|
error: string;
|
|
1096
1162
|
};
|
|
1097
1163
|
|
|
1164
|
+
/** ADR-154 §9: the box tags pinned legacy parameters. Advertised by the
|
|
1165
|
+
* sign-in box only; the cloud sends `env.legacy.tag` to nothing else. */
|
|
1166
|
+
export const PROJECT_ENV_LEGACY_TAGS_PROTOCOL_FEATURE = "project-env-legacy-tags-v1";
|
|
1167
|
+
export const MAX_PROJECT_ENV_LEGACY_TAG_KEYS = 25;
|
|
1168
|
+
|
|
1169
|
+
/** ADR-154 §9: a v1 lease reads `/uai/projects/*` only where the parameter's
|
|
1170
|
+
* `org` tag is the task's workspace, and ADR-153's backfill pinned legacy
|
|
1171
|
+
* parameters (`<prefix>/projects/<projectId>/env/<KEY>`) without checking
|
|
1172
|
+
* their tags. The write-only sign-in box puts the project's `org` and
|
|
1173
|
+
* `project` tags on each named one and reads them back. Metadata only:
|
|
1174
|
+
* nothing reads or writes a value, and the cloud checks each pin itself,
|
|
1175
|
+
* before and after, from its own metadata reads. */
|
|
1176
|
+
export interface ProjectEnvLegacyTagFrame {
|
|
1177
|
+
kind: "env.legacy.tag";
|
|
1178
|
+
opId: HostOpId;
|
|
1179
|
+
orgId: string;
|
|
1180
|
+
projectId: string;
|
|
1181
|
+
keys: string[];
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/** `tagged`: both tags read back as asked; `missing`: no such parameter;
|
|
1185
|
+
* `refused`: AWS will not tag it (too many tags), so the value is withheld;
|
|
1186
|
+
* `retry`: anything else, asked again later. */
|
|
1187
|
+
export type ProjectEnvLegacyTagOutcome = "tagged" | "missing" | "refused" | "retry";
|
|
1188
|
+
|
|
1189
|
+
export interface ProjectEnvLegacyTagAckFrame {
|
|
1190
|
+
kind: "env.legacy.tag.ack";
|
|
1191
|
+
opId: HostOpId;
|
|
1192
|
+
/** One per key of the request, in its order. */
|
|
1193
|
+
results: Array<{ key: string; outcome: ProjectEnvLegacyTagOutcome }>;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1098
1196
|
/** Uai Cloud is authoritative for task-machine records. A replica may read
|
|
1099
1197
|
* only immutable staged values or one pinned legacy SSM version; `row` is an
|
|
1100
1198
|
* owned-runner reference and is never meaningful on a sidecar. */
|
|
@@ -1118,6 +1216,85 @@ export type ProjectEnvReplicaBindingState =
|
|
|
1118
1216
|
| ProjectEnvReplicaTombstoneState
|
|
1119
1217
|
| ProjectEnvReplicaLiveState;
|
|
1120
1218
|
|
|
1219
|
+
/**
|
|
1220
|
+
* ADR-154's task-keyed replica identity. The authenticated bridge supplies
|
|
1221
|
+
* the runner identity; these fields bind work to one task run, one runner
|
|
1222
|
+
* process and one ordered stream on that runner. A reconnect under the same
|
|
1223
|
+
* boot id may resume the stream. A new boot id or run epoch may not.
|
|
1224
|
+
*/
|
|
1225
|
+
export interface ProjectEnvReplicaV1Identity {
|
|
1226
|
+
taskId: string;
|
|
1227
|
+
runEpoch: number;
|
|
1228
|
+
runnerBootId: HostOpId;
|
|
1229
|
+
streamId: HostOpId;
|
|
1230
|
+
/** Null only on a live apply and its acknowledgement. */
|
|
1231
|
+
attemptId: HostOpId | null;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
type ProjectEnvReplicaV1AttemptIdentity = Omit<
|
|
1235
|
+
ProjectEnvReplicaV1Identity,
|
|
1236
|
+
"attemptId"
|
|
1237
|
+
> & {
|
|
1238
|
+
attemptId: HostOpId;
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1241
|
+
type ProjectEnvReplicaV1ApplyIdentity = ProjectEnvReplicaV1Identity;
|
|
1242
|
+
|
|
1243
|
+
/** Exact identity equality used when correlating an ack with its request. */
|
|
1244
|
+
export function sameProjectEnvReplicaV1Identity(
|
|
1245
|
+
left: ProjectEnvReplicaV1Identity,
|
|
1246
|
+
right: ProjectEnvReplicaV1Identity,
|
|
1247
|
+
): boolean {
|
|
1248
|
+
return (
|
|
1249
|
+
left.taskId === right.taskId &&
|
|
1250
|
+
left.runEpoch === right.runEpoch &&
|
|
1251
|
+
left.runnerBootId === right.runnerBootId &&
|
|
1252
|
+
left.streamId === right.streamId &&
|
|
1253
|
+
left.attemptId === right.attemptId
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Versioned in parallel with the legacy machine-scoped family so a cloud-first
|
|
1259
|
+
* rollout can continue serving old sidecars unchanged.
|
|
1260
|
+
*/
|
|
1261
|
+
export type ProjectEnvReplicaV1BeginFrame =
|
|
1262
|
+
ProjectEnvReplicaV1AttemptIdentity & {
|
|
1263
|
+
kind: "env.replica.v1.begin";
|
|
1264
|
+
opId: HostOpId;
|
|
1265
|
+
/** Canonical: sorted and unique. The empty set is valid. */
|
|
1266
|
+
projectIds: string[];
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
export type ProjectEnvReplicaV1PageFrame =
|
|
1270
|
+
ProjectEnvReplicaV1AttemptIdentity & {
|
|
1271
|
+
kind: "env.replica.v1.page";
|
|
1272
|
+
opId: HostOpId;
|
|
1273
|
+
projectId: string;
|
|
1274
|
+
after: string | null;
|
|
1275
|
+
recordVersion: number;
|
|
1276
|
+
records: ProjectEnvReplicaBindingState[];
|
|
1277
|
+
complete: boolean;
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
export type ProjectEnvReplicaV1ApplyFrame =
|
|
1281
|
+
ProjectEnvReplicaV1ApplyIdentity & {
|
|
1282
|
+
kind: "env.replica.v1.apply";
|
|
1283
|
+
opId: HostOpId;
|
|
1284
|
+
projectId: string;
|
|
1285
|
+
recordVersion: number;
|
|
1286
|
+
state: ProjectEnvReplicaBindingState;
|
|
1287
|
+
delivery: ProjectEnvDeliveryCursor;
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
export type ProjectEnvReplicaV1CommitFrame =
|
|
1291
|
+
ProjectEnvReplicaV1AttemptIdentity & {
|
|
1292
|
+
kind: "env.replica.v1.commit";
|
|
1293
|
+
opId: HostOpId;
|
|
1294
|
+
highWater: ProjectEnvDeliveryCursor;
|
|
1295
|
+
versions: ProjectEnvRecordVersion[];
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1121
1298
|
/** Close the sidecar's serving gate and start a whole-task replica snapshot.
|
|
1122
1299
|
* Project ids are canonical (sorted, unique); the empty set is valid. */
|
|
1123
1300
|
export interface ProjectEnvReplicaBeginFrame {
|
|
@@ -1282,6 +1459,91 @@ export type ProjectEnvReplicaAckFrame =
|
|
|
1282
1459
|
}
|
|
1283
1460
|
| ProjectEnvReplicaErrorAckFrame;
|
|
1284
1461
|
|
|
1462
|
+
type ProjectEnvReplicaV1AckCommon = ProjectEnvReplicaV1Identity & {
|
|
1463
|
+
kind: "env.replica.v1.ack";
|
|
1464
|
+
opId: HostOpId;
|
|
1465
|
+
policyEpochs: number[];
|
|
1466
|
+
appliedThrough: ProjectEnvDeliveryCursor;
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1469
|
+
type ProjectEnvReplicaV1ErrorAckFrame = ProjectEnvReplicaV1AckCommon &
|
|
1470
|
+
(
|
|
1471
|
+
| {
|
|
1472
|
+
ok: false;
|
|
1473
|
+
phase: "applied";
|
|
1474
|
+
attemptId: HostOpId | null;
|
|
1475
|
+
code: ProjectEnvWithholdReason;
|
|
1476
|
+
error: string;
|
|
1477
|
+
}
|
|
1478
|
+
| {
|
|
1479
|
+
ok: false;
|
|
1480
|
+
phase: "begun" | "page" | "complete";
|
|
1481
|
+
attemptId: HostOpId;
|
|
1482
|
+
code: ProjectEnvWithholdReason;
|
|
1483
|
+
error: string;
|
|
1484
|
+
}
|
|
1485
|
+
);
|
|
1486
|
+
|
|
1487
|
+
/** Every acknowledgement echoes the complete task-replica identity. */
|
|
1488
|
+
export type ProjectEnvReplicaV1AckFrame =
|
|
1489
|
+
| (ProjectEnvReplicaV1AckCommon & {
|
|
1490
|
+
ok: true;
|
|
1491
|
+
phase: "begun";
|
|
1492
|
+
attemptId: HostOpId;
|
|
1493
|
+
projectId?: never;
|
|
1494
|
+
after?: never;
|
|
1495
|
+
recordVersion?: never;
|
|
1496
|
+
results?: never;
|
|
1497
|
+
complete?: never;
|
|
1498
|
+
result?: never;
|
|
1499
|
+
highWater?: never;
|
|
1500
|
+
code?: never;
|
|
1501
|
+
error?: never;
|
|
1502
|
+
})
|
|
1503
|
+
| (ProjectEnvReplicaV1AckCommon & {
|
|
1504
|
+
ok: true;
|
|
1505
|
+
phase: "page";
|
|
1506
|
+
attemptId: HostOpId;
|
|
1507
|
+
projectId: string;
|
|
1508
|
+
after: string | null;
|
|
1509
|
+
recordVersion: number;
|
|
1510
|
+
results: ProjectEnvReplicaResult[];
|
|
1511
|
+
complete: boolean;
|
|
1512
|
+
result?: never;
|
|
1513
|
+
highWater?: never;
|
|
1514
|
+
code?: never;
|
|
1515
|
+
error?: never;
|
|
1516
|
+
})
|
|
1517
|
+
| (ProjectEnvReplicaV1AckCommon & {
|
|
1518
|
+
ok: true;
|
|
1519
|
+
phase: "applied";
|
|
1520
|
+
attemptId: HostOpId | null;
|
|
1521
|
+
projectId: string;
|
|
1522
|
+
recordVersion: number;
|
|
1523
|
+
result: ProjectEnvReplicaResult;
|
|
1524
|
+
after?: never;
|
|
1525
|
+
results?: never;
|
|
1526
|
+
complete?: never;
|
|
1527
|
+
highWater?: never;
|
|
1528
|
+
code?: never;
|
|
1529
|
+
error?: never;
|
|
1530
|
+
})
|
|
1531
|
+
| (ProjectEnvReplicaV1AckCommon & {
|
|
1532
|
+
ok: true;
|
|
1533
|
+
phase: "complete";
|
|
1534
|
+
attemptId: HostOpId;
|
|
1535
|
+
highWater: ProjectEnvDeliveryCursor;
|
|
1536
|
+
projectId?: never;
|
|
1537
|
+
after?: never;
|
|
1538
|
+
recordVersion?: never;
|
|
1539
|
+
results?: never;
|
|
1540
|
+
complete?: never;
|
|
1541
|
+
result?: never;
|
|
1542
|
+
code?: never;
|
|
1543
|
+
error?: never;
|
|
1544
|
+
})
|
|
1545
|
+
| ProjectEnvReplicaV1ErrorAckFrame;
|
|
1546
|
+
|
|
1285
1547
|
type ProjectEnvAckBase = {
|
|
1286
1548
|
kind: "env.var.ack";
|
|
1287
1549
|
opId: HostOpId;
|
|
@@ -1873,6 +2135,52 @@ export function isProjectEnvStagingAckFrame(
|
|
|
1873
2135
|
return false;
|
|
1874
2136
|
}
|
|
1875
2137
|
|
|
2138
|
+
function isProjectEnvLegacyTagKeys(value: unknown): value is string[] {
|
|
2139
|
+
return (
|
|
2140
|
+
Array.isArray(value) &&
|
|
2141
|
+
value.length >= 1 &&
|
|
2142
|
+
value.length <= MAX_PROJECT_ENV_LEGACY_TAG_KEYS &&
|
|
2143
|
+
value.every((key) => isProjectEnvKey(key) && PROJECT_ENV_STORE_PATH_SEGMENT_RE.test(key)) &&
|
|
2144
|
+
new Set(value).size === value.length
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
export function isProjectEnvLegacyTagFrame(
|
|
2149
|
+
value: unknown,
|
|
2150
|
+
): value is ProjectEnvLegacyTagFrame {
|
|
2151
|
+
const frame = exactWireObject(value, ["kind", "opId", "orgId", "projectId", "keys"]);
|
|
2152
|
+
return Boolean(
|
|
2153
|
+
frame &&
|
|
2154
|
+
frame.kind === "env.legacy.tag" &&
|
|
2155
|
+
isHostOpId(frame.opId) &&
|
|
2156
|
+
isProjectEnvOrgId(frame.orgId) &&
|
|
2157
|
+
PROJECT_ENV_STORE_PATH_SEGMENT_RE.test(frame.orgId) &&
|
|
2158
|
+
isProjectEnvProjectId(frame.projectId) &&
|
|
2159
|
+
PROJECT_ENV_STORE_PATH_SEGMENT_RE.test(frame.projectId) &&
|
|
2160
|
+
isProjectEnvLegacyTagKeys(frame.keys),
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
function isProjectEnvLegacyTagOutcome(value: unknown): value is ProjectEnvLegacyTagOutcome {
|
|
2165
|
+
return value === "tagged" || value === "missing" || value === "refused" || value === "retry";
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
export function isProjectEnvLegacyTagAckFrame(
|
|
2169
|
+
value: unknown,
|
|
2170
|
+
): value is ProjectEnvLegacyTagAckFrame {
|
|
2171
|
+
const frame = exactWireObject(value, ["kind", "opId", "results"]);
|
|
2172
|
+
if (!frame || frame.kind !== "env.legacy.tag.ack" || !isHostOpId(frame.opId)) return false;
|
|
2173
|
+
const results = frame.results;
|
|
2174
|
+
if (!Array.isArray(results) || results.length > MAX_PROJECT_ENV_LEGACY_TAG_KEYS) return false;
|
|
2175
|
+
const keys: unknown[] = [];
|
|
2176
|
+
for (const item of results) {
|
|
2177
|
+
const result = exactWireObject(item, ["key", "outcome"]);
|
|
2178
|
+
if (!result || !isProjectEnvLegacyTagOutcome(result.outcome)) return false;
|
|
2179
|
+
keys.push(result.key);
|
|
2180
|
+
}
|
|
2181
|
+
return keys.length === 0 || isProjectEnvLegacyTagKeys(keys);
|
|
2182
|
+
}
|
|
2183
|
+
|
|
1876
2184
|
export function isProjectEnvReplicaBeginFrame(
|
|
1877
2185
|
value: unknown,
|
|
1878
2186
|
): value is ProjectEnvReplicaBeginFrame {
|
|
@@ -1977,6 +2285,137 @@ export function isProjectEnvReplicaCommitFrame(
|
|
|
1977
2285
|
);
|
|
1978
2286
|
}
|
|
1979
2287
|
|
|
2288
|
+
function isProjectEnvReplicaV1Identity(
|
|
2289
|
+
frame: WireObject,
|
|
2290
|
+
allowLive: boolean,
|
|
2291
|
+
): boolean {
|
|
2292
|
+
return (
|
|
2293
|
+
isHostOpId(frame.taskId) &&
|
|
2294
|
+
isPositiveSafeInteger(frame.runEpoch) &&
|
|
2295
|
+
isHostOpId(frame.runnerBootId) &&
|
|
2296
|
+
isHostOpId(frame.streamId) &&
|
|
2297
|
+
(isHostOpId(frame.attemptId) || (allowLive && frame.attemptId === null))
|
|
2298
|
+
);
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
export function isProjectEnvReplicaV1BeginFrame(
|
|
2302
|
+
value: unknown,
|
|
2303
|
+
): value is ProjectEnvReplicaV1BeginFrame {
|
|
2304
|
+
const frame = exactWireObject(value, [
|
|
2305
|
+
"kind",
|
|
2306
|
+
"opId",
|
|
2307
|
+
"taskId",
|
|
2308
|
+
"runEpoch",
|
|
2309
|
+
"runnerBootId",
|
|
2310
|
+
"streamId",
|
|
2311
|
+
"attemptId",
|
|
2312
|
+
"projectIds",
|
|
2313
|
+
]);
|
|
2314
|
+
return Boolean(
|
|
2315
|
+
frame &&
|
|
2316
|
+
frame.kind === "env.replica.v1.begin" &&
|
|
2317
|
+
isHostOpId(frame.opId) &&
|
|
2318
|
+
isProjectEnvReplicaV1Identity(frame, false) &&
|
|
2319
|
+
isProjectEnvProjectIds(frame.projectIds),
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
export function isProjectEnvReplicaV1PageFrame(
|
|
2324
|
+
value: unknown,
|
|
2325
|
+
): value is ProjectEnvReplicaV1PageFrame {
|
|
2326
|
+
const frame = exactWireObject(value, [
|
|
2327
|
+
"kind",
|
|
2328
|
+
"opId",
|
|
2329
|
+
"taskId",
|
|
2330
|
+
"runEpoch",
|
|
2331
|
+
"runnerBootId",
|
|
2332
|
+
"streamId",
|
|
2333
|
+
"attemptId",
|
|
2334
|
+
"projectId",
|
|
2335
|
+
"after",
|
|
2336
|
+
"recordVersion",
|
|
2337
|
+
"records",
|
|
2338
|
+
"complete",
|
|
2339
|
+
]);
|
|
2340
|
+
if (
|
|
2341
|
+
frame === null ||
|
|
2342
|
+
frame.kind !== "env.replica.v1.page" ||
|
|
2343
|
+
!isHostOpId(frame.opId) ||
|
|
2344
|
+
!isProjectEnvReplicaV1Identity(frame, false) ||
|
|
2345
|
+
!isProjectEnvProjectId(frame.projectId) ||
|
|
2346
|
+
!(frame.after === null || isProjectEnvKey(frame.after)) ||
|
|
2347
|
+
!isNonNegativeSafeInteger(frame.recordVersion) ||
|
|
2348
|
+
!isProjectEnvReplicaBindingStates(frame.records) ||
|
|
2349
|
+
typeof frame.complete !== "boolean"
|
|
2350
|
+
) {
|
|
2351
|
+
return false;
|
|
2352
|
+
}
|
|
2353
|
+
const records = frame.records as ProjectEnvReplicaBindingState[];
|
|
2354
|
+
if (records.length === 0) {
|
|
2355
|
+
if (!(frame.complete && frame.after === null)) return false;
|
|
2356
|
+
} else if (
|
|
2357
|
+
frame.after !== null &&
|
|
2358
|
+
records[0]!.key <= frame.after
|
|
2359
|
+
) {
|
|
2360
|
+
return false;
|
|
2361
|
+
}
|
|
2362
|
+
return isProjectEnvPageWithinBound(value);
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
export function isProjectEnvReplicaV1ApplyFrame(
|
|
2366
|
+
value: unknown,
|
|
2367
|
+
): value is ProjectEnvReplicaV1ApplyFrame {
|
|
2368
|
+
const frame = exactWireObject(value, [
|
|
2369
|
+
"kind",
|
|
2370
|
+
"opId",
|
|
2371
|
+
"taskId",
|
|
2372
|
+
"runEpoch",
|
|
2373
|
+
"runnerBootId",
|
|
2374
|
+
"streamId",
|
|
2375
|
+
"attemptId",
|
|
2376
|
+
"projectId",
|
|
2377
|
+
"recordVersion",
|
|
2378
|
+
"state",
|
|
2379
|
+
"delivery",
|
|
2380
|
+
]);
|
|
2381
|
+
return Boolean(
|
|
2382
|
+
frame &&
|
|
2383
|
+
frame.kind === "env.replica.v1.apply" &&
|
|
2384
|
+
isHostOpId(frame.opId) &&
|
|
2385
|
+
isProjectEnvReplicaV1Identity(frame, true) &&
|
|
2386
|
+
isProjectEnvProjectId(frame.projectId) &&
|
|
2387
|
+
isPositiveSafeInteger(frame.recordVersion) &&
|
|
2388
|
+
isProjectEnvReplicaBindingState(frame.state) &&
|
|
2389
|
+
isProjectEnvMutationDelivery(frame.delivery) &&
|
|
2390
|
+
(frame.delivery as ProjectEnvDeliveryCursor).streamId === frame.streamId,
|
|
2391
|
+
);
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
export function isProjectEnvReplicaV1CommitFrame(
|
|
2395
|
+
value: unknown,
|
|
2396
|
+
): value is ProjectEnvReplicaV1CommitFrame {
|
|
2397
|
+
const frame = exactWireObject(value, [
|
|
2398
|
+
"kind",
|
|
2399
|
+
"opId",
|
|
2400
|
+
"taskId",
|
|
2401
|
+
"runEpoch",
|
|
2402
|
+
"runnerBootId",
|
|
2403
|
+
"streamId",
|
|
2404
|
+
"attemptId",
|
|
2405
|
+
"highWater",
|
|
2406
|
+
"versions",
|
|
2407
|
+
]);
|
|
2408
|
+
return Boolean(
|
|
2409
|
+
frame &&
|
|
2410
|
+
frame.kind === "env.replica.v1.commit" &&
|
|
2411
|
+
isHostOpId(frame.opId) &&
|
|
2412
|
+
isProjectEnvReplicaV1Identity(frame, false) &&
|
|
2413
|
+
isProjectEnvDeliveryCursor(frame.highWater) &&
|
|
2414
|
+
(frame.highWater as ProjectEnvDeliveryCursor).streamId === frame.streamId &&
|
|
2415
|
+
isProjectEnvRecordVersions(frame.versions),
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2418
|
+
|
|
1980
2419
|
function hasSortedBindingStates(value: ProjectEnvBindingState[]): boolean {
|
|
1981
2420
|
return value.every(
|
|
1982
2421
|
(state, index) => index === 0 || value[index - 1]!.key < state.key,
|
|
@@ -2314,6 +2753,155 @@ export function isProjectEnvReplicaAckFrame(
|
|
|
2314
2753
|
}
|
|
2315
2754
|
return false;
|
|
2316
2755
|
}
|
|
2756
|
+
|
|
2757
|
+
function isProjectEnvReplicaV1AckIdentity(
|
|
2758
|
+
frame: WireObject,
|
|
2759
|
+
allowLive: boolean,
|
|
2760
|
+
): boolean {
|
|
2761
|
+
return (
|
|
2762
|
+
isHostOpId(frame.opId) &&
|
|
2763
|
+
isProjectEnvReplicaV1Identity(frame, allowLive) &&
|
|
2764
|
+
isProjectEnvPolicyEpochs(frame.policyEpochs) &&
|
|
2765
|
+
isProjectEnvDeliveryCursor(frame.appliedThrough) &&
|
|
2766
|
+
(frame.appliedThrough as ProjectEnvDeliveryCursor).streamId ===
|
|
2767
|
+
frame.streamId
|
|
2768
|
+
);
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
export function isProjectEnvReplicaV1AckFrame(
|
|
2772
|
+
value: unknown,
|
|
2773
|
+
): value is ProjectEnvReplicaV1AckFrame {
|
|
2774
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2775
|
+
const candidate = value as WireObject;
|
|
2776
|
+
|
|
2777
|
+
if (candidate.ok === false) {
|
|
2778
|
+
const frame = exactWireObject(value, [
|
|
2779
|
+
"kind",
|
|
2780
|
+
"opId",
|
|
2781
|
+
"ok",
|
|
2782
|
+
"phase",
|
|
2783
|
+
"taskId",
|
|
2784
|
+
"runEpoch",
|
|
2785
|
+
"runnerBootId",
|
|
2786
|
+
"streamId",
|
|
2787
|
+
"attemptId",
|
|
2788
|
+
"code",
|
|
2789
|
+
"error",
|
|
2790
|
+
"policyEpochs",
|
|
2791
|
+
"appliedThrough",
|
|
2792
|
+
]);
|
|
2793
|
+
if (
|
|
2794
|
+
frame === null ||
|
|
2795
|
+
frame.kind !== "env.replica.v1.ack" ||
|
|
2796
|
+
!(
|
|
2797
|
+
frame.phase === "begun" ||
|
|
2798
|
+
frame.phase === "page" ||
|
|
2799
|
+
frame.phase === "applied" ||
|
|
2800
|
+
frame.phase === "complete"
|
|
2801
|
+
) ||
|
|
2802
|
+
!isProjectEnvReplicaV1AckIdentity(frame, frame.phase === "applied") ||
|
|
2803
|
+
!isProjectEnvReason(frame.code) ||
|
|
2804
|
+
!isBoundedMessage(frame.error)
|
|
2805
|
+
) {
|
|
2806
|
+
return false;
|
|
2807
|
+
}
|
|
2808
|
+
return true;
|
|
2809
|
+
}
|
|
2810
|
+
if (candidate.ok !== true) return false;
|
|
2811
|
+
|
|
2812
|
+
const identityKeys = [
|
|
2813
|
+
"kind",
|
|
2814
|
+
"opId",
|
|
2815
|
+
"ok",
|
|
2816
|
+
"phase",
|
|
2817
|
+
"taskId",
|
|
2818
|
+
"runEpoch",
|
|
2819
|
+
"runnerBootId",
|
|
2820
|
+
"streamId",
|
|
2821
|
+
"attemptId",
|
|
2822
|
+
];
|
|
2823
|
+
const tailKeys = ["policyEpochs", "appliedThrough"];
|
|
2824
|
+
|
|
2825
|
+
if (candidate.phase === "begun") {
|
|
2826
|
+
const frame = exactWireObject(value, [...identityKeys, ...tailKeys]);
|
|
2827
|
+
return Boolean(
|
|
2828
|
+
frame &&
|
|
2829
|
+
frame.kind === "env.replica.v1.ack" &&
|
|
2830
|
+
isProjectEnvReplicaV1AckIdentity(frame, false),
|
|
2831
|
+
);
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
if (candidate.phase === "page") {
|
|
2835
|
+
const frame = exactWireObject(value, [
|
|
2836
|
+
...identityKeys,
|
|
2837
|
+
"projectId",
|
|
2838
|
+
"after",
|
|
2839
|
+
"recordVersion",
|
|
2840
|
+
"results",
|
|
2841
|
+
"complete",
|
|
2842
|
+
...tailKeys,
|
|
2843
|
+
]);
|
|
2844
|
+
if (
|
|
2845
|
+
frame === null ||
|
|
2846
|
+
frame.kind !== "env.replica.v1.ack" ||
|
|
2847
|
+
!isProjectEnvReplicaV1AckIdentity(frame, false) ||
|
|
2848
|
+
!isProjectEnvProjectId(frame.projectId) ||
|
|
2849
|
+
!(frame.after === null || isProjectEnvKey(frame.after)) ||
|
|
2850
|
+
!isNonNegativeSafeInteger(frame.recordVersion) ||
|
|
2851
|
+
!isProjectEnvReplicaResults(frame.results) ||
|
|
2852
|
+
typeof frame.complete !== "boolean"
|
|
2853
|
+
) {
|
|
2854
|
+
return false;
|
|
2855
|
+
}
|
|
2856
|
+
const results = frame.results as ProjectEnvReplicaResult[];
|
|
2857
|
+
if (results.length === 0) {
|
|
2858
|
+
if (!(frame.complete && frame.after === null)) return false;
|
|
2859
|
+
} else if (
|
|
2860
|
+
frame.after !== null &&
|
|
2861
|
+
results[0]!.state.key <= frame.after
|
|
2862
|
+
) {
|
|
2863
|
+
return false;
|
|
2864
|
+
}
|
|
2865
|
+
return isProjectEnvPageWithinBound(value);
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
if (candidate.phase === "applied") {
|
|
2869
|
+
const frame = exactWireObject(value, [
|
|
2870
|
+
...identityKeys,
|
|
2871
|
+
"projectId",
|
|
2872
|
+
"recordVersion",
|
|
2873
|
+
"result",
|
|
2874
|
+
...tailKeys,
|
|
2875
|
+
]);
|
|
2876
|
+
return Boolean(
|
|
2877
|
+
frame &&
|
|
2878
|
+
frame.kind === "env.replica.v1.ack" &&
|
|
2879
|
+
isProjectEnvReplicaV1AckIdentity(frame, true) &&
|
|
2880
|
+
isProjectEnvProjectId(frame.projectId) &&
|
|
2881
|
+
isPositiveSafeInteger(frame.recordVersion) &&
|
|
2882
|
+
isProjectEnvReplicaResult(frame.result),
|
|
2883
|
+
);
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
if (candidate.phase === "complete") {
|
|
2887
|
+
const frame = exactWireObject(value, [
|
|
2888
|
+
...identityKeys,
|
|
2889
|
+
"highWater",
|
|
2890
|
+
...tailKeys,
|
|
2891
|
+
]);
|
|
2892
|
+
return Boolean(
|
|
2893
|
+
frame &&
|
|
2894
|
+
frame.kind === "env.replica.v1.ack" &&
|
|
2895
|
+
isProjectEnvReplicaV1AckIdentity(frame, false) &&
|
|
2896
|
+
isProjectEnvDeliveryCursor(frame.highWater) &&
|
|
2897
|
+
(frame.highWater as ProjectEnvDeliveryCursor).streamId ===
|
|
2898
|
+
frame.streamId &&
|
|
2899
|
+
(frame.appliedThrough as ProjectEnvDeliveryCursor).sequence ===
|
|
2900
|
+
(frame.highWater as ProjectEnvDeliveryCursor).sequence,
|
|
2901
|
+
);
|
|
2902
|
+
}
|
|
2903
|
+
return false;
|
|
2904
|
+
}
|
|
2317
2905
|
/** The owner-level account scope; every pre-scope account lives here. */
|
|
2318
2906
|
export const HOST_ACCOUNT_SCOPE = "host";
|
|
2319
2907
|
const ORG_ACCOUNT_SCOPE_RE = /^org:[A-Za-z0-9_-]{1,128}$/;
|
|
@@ -2561,6 +3149,9 @@ export interface EngineLoginStartFrame {
|
|
|
2561
3149
|
* host). Only sent to hosts advertising `host-engine-account-scopes-v1`;
|
|
2562
3150
|
* absent = the host scope. An org scope requires a label. */
|
|
2563
3151
|
scope?: string;
|
|
3152
|
+
/** ADR-154: write the capture as this revision of a cloud credential.
|
|
3153
|
+
* Only sent to a sign-in box advertising `signin-box-capture-flows-v1`. */
|
|
3154
|
+
flow?: CaptureFlowWire;
|
|
2564
3155
|
}
|
|
2565
3156
|
|
|
2566
3157
|
/** The only generic one-time response channel. Long-lived tokens must never
|
|
@@ -2625,6 +3216,9 @@ export type EngineLoginEventFrame =
|
|
|
2625
3216
|
/** `succeeded` of a LABELED capture: the id of the account it created,
|
|
2626
3217
|
* so the client can rename/order it right away. */
|
|
2627
3218
|
accountId?: string;
|
|
3219
|
+
/** `succeeded` of an ADR-154 capture flow: the revision written, by
|
|
3220
|
+
* name and version. Never material. */
|
|
3221
|
+
capture?: CaptureResultWire;
|
|
2628
3222
|
});
|
|
2629
3223
|
|
|
2630
3224
|
/** Rolling-safe, secret-free engine state. v1 intentionally makes no claim
|
|
@@ -2686,6 +3280,8 @@ export interface EngineConnectStartFrame {
|
|
|
2686
3280
|
label?: string;
|
|
2687
3281
|
/** ADR-131 D3: see EngineLoginStartFrame.scope. */
|
|
2688
3282
|
scope?: string;
|
|
3283
|
+
/** ADR-154: see EngineLoginStartFrame.flow. */
|
|
3284
|
+
flow?: CaptureFlowWire;
|
|
2689
3285
|
}
|
|
2690
3286
|
|
|
2691
3287
|
export interface EngineConnectStopFrame {
|
|
@@ -2706,6 +3302,8 @@ export interface EngineConnectEventFrame {
|
|
|
2706
3302
|
/** Manage-v1: `done` of a LABELED connect — the id of the account it
|
|
2707
3303
|
* created, so the client can name/order it right away. */
|
|
2708
3304
|
accountId?: string;
|
|
3305
|
+
/** ADR-154: `done` of a capture flow, see EngineLoginEventFrame. */
|
|
3306
|
+
capture?: CaptureResultWire;
|
|
2709
3307
|
}
|
|
2710
3308
|
|
|
2711
3309
|
export function isEngineConnectKind(
|
|
@@ -2727,14 +3325,15 @@ export function isEngineConnectStartFrame(
|
|
|
2727
3325
|
const frame = exactWireObject(
|
|
2728
3326
|
value,
|
|
2729
3327
|
["kind", "opId", "engine"],
|
|
2730
|
-
["secret", "label", "scope"],
|
|
3328
|
+
["secret", "label", "scope", "flow"],
|
|
2731
3329
|
);
|
|
2732
3330
|
if (
|
|
2733
3331
|
!frame ||
|
|
2734
3332
|
frame.kind !== "engine.connect.start" ||
|
|
2735
3333
|
!isHostOpId(frame.opId) ||
|
|
2736
3334
|
!isEngineConnectKind(frame.engine) ||
|
|
2737
|
-
(frame.scope !== undefined && !isEngineAccountScope(frame.scope))
|
|
3335
|
+
(frame.scope !== undefined && !isEngineAccountScope(frame.scope)) ||
|
|
3336
|
+
(frame.flow !== undefined && !isEngineCaptureFlow(frame.flow, frame.engine))
|
|
2738
3337
|
) {
|
|
2739
3338
|
return false;
|
|
2740
3339
|
}
|
|
@@ -2765,7 +3364,7 @@ export function isEngineConnectEventFrame(
|
|
|
2765
3364
|
const frame = exactWireObject(
|
|
2766
3365
|
value,
|
|
2767
3366
|
["kind", "opId", "engine", "phase"],
|
|
2768
|
-
["line", "ok", "message", "accountId"],
|
|
3367
|
+
["line", "ok", "message", "accountId", "capture"],
|
|
2769
3368
|
);
|
|
2770
3369
|
if (
|
|
2771
3370
|
!frame ||
|
|
@@ -2775,6 +3374,14 @@ export function isEngineConnectEventFrame(
|
|
|
2775
3374
|
) {
|
|
2776
3375
|
return false;
|
|
2777
3376
|
}
|
|
3377
|
+
if (
|
|
3378
|
+
Object.hasOwn(frame, "capture") &&
|
|
3379
|
+
(frame.phase !== "done" ||
|
|
3380
|
+
frame.ok !== true ||
|
|
3381
|
+
parseCaptureResultWire(frame.capture) === null)
|
|
3382
|
+
) {
|
|
3383
|
+
return false;
|
|
3384
|
+
}
|
|
2778
3385
|
if (
|
|
2779
3386
|
Object.hasOwn(frame, "accountId") &&
|
|
2780
3387
|
(frame.phase !== "done" ||
|
|
@@ -2992,7 +3599,7 @@ export function isEngineLoginStartFrame(
|
|
|
2992
3599
|
const frame = exactWireObject(
|
|
2993
3600
|
value,
|
|
2994
3601
|
["kind", "opId", "engine"],
|
|
2995
|
-
["label", "scope"],
|
|
3602
|
+
["label", "scope", "flow"],
|
|
2996
3603
|
);
|
|
2997
3604
|
return Boolean(
|
|
2998
3605
|
frame &&
|
|
@@ -3000,7 +3607,18 @@ export function isEngineLoginStartFrame(
|
|
|
3000
3607
|
isHostOpId(frame.opId) &&
|
|
3001
3608
|
isEngineLoginKind(frame.engine) &&
|
|
3002
3609
|
(frame.label === undefined || isEngineAccountLabel(frame.label)) &&
|
|
3003
|
-
(frame.scope === undefined || isEngineAccountScope(frame.scope))
|
|
3610
|
+
(frame.scope === undefined || isEngineAccountScope(frame.scope)) &&
|
|
3611
|
+
(frame.flow === undefined || isEngineCaptureFlow(frame.flow, frame.engine)),
|
|
3612
|
+
);
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
/** ADR-154: a capture flow for an AI account of exactly this engine. */
|
|
3616
|
+
function isEngineCaptureFlow(value: unknown, engine: unknown): boolean {
|
|
3617
|
+
const flow = parseCaptureFlowWire(value);
|
|
3618
|
+
return (
|
|
3619
|
+
flow !== null &&
|
|
3620
|
+
flow.location.class === "engine" &&
|
|
3621
|
+
flow.location.engineKind === engine
|
|
3004
3622
|
);
|
|
3005
3623
|
}
|
|
3006
3624
|
|
|
@@ -3070,7 +3688,7 @@ export function isEngineLoginEventFrame(
|
|
|
3070
3688
|
const frame = exactWireObject(
|
|
3071
3689
|
value,
|
|
3072
3690
|
["kind", "opId", "engine", "phase"],
|
|
3073
|
-
["url", "errorCode", "message", "code", "accountId"],
|
|
3691
|
+
["url", "errorCode", "message", "code", "accountId", "capture"],
|
|
3074
3692
|
);
|
|
3075
3693
|
if (
|
|
3076
3694
|
!frame ||
|
|
@@ -3097,6 +3715,12 @@ export function isEngineLoginEventFrame(
|
|
|
3097
3715
|
) {
|
|
3098
3716
|
return false;
|
|
3099
3717
|
}
|
|
3718
|
+
if (
|
|
3719
|
+
Object.hasOwn(frame, "capture") &&
|
|
3720
|
+
(frame.phase !== "succeeded" || parseCaptureResultWire(frame.capture) === null)
|
|
3721
|
+
) {
|
|
3722
|
+
return false;
|
|
3723
|
+
}
|
|
3100
3724
|
if (frame.phase === "authorize") {
|
|
3101
3725
|
return (
|
|
3102
3726
|
isHttpsUrl(frame.url) &&
|
|
@@ -3423,6 +4047,11 @@ export interface TaskCommandTask {
|
|
|
3423
4047
|
engineAccountScope?: string;
|
|
3424
4048
|
/** ADR-062: shared-files mount mode ("off" | "ro" | "rw"; default "ro"). */
|
|
3425
4049
|
sharedFiles?: string;
|
|
4050
|
+
/** ADR-154: the task's run epoch, which the cloud advances at every start,
|
|
4051
|
+
* resume and wake dispatch. With the runner the task is assigned to, it is
|
|
4052
|
+
* the assignment every v1 grant, replica frame and cleanup-inventory entry
|
|
4053
|
+
* is fenced to. Absent from a cloud that predates it. */
|
|
4054
|
+
runEpoch?: number;
|
|
3426
4055
|
}
|
|
3427
4056
|
|
|
3428
4057
|
/**
|
|
@@ -3456,6 +4085,9 @@ export interface TaskLaunchInput {
|
|
|
3456
4085
|
* pressed pause; no veto. */
|
|
3457
4086
|
export interface TaskStopInput {
|
|
3458
4087
|
reason: "idle" | "manual";
|
|
4088
|
+
/** ADR-154 assignment fence. New clouds name the exact run they observed;
|
|
4089
|
+
* absent only for rolling-compatible tasks that have never crossed v1. */
|
|
4090
|
+
runEpoch?: number;
|
|
3459
4091
|
}
|
|
3460
4092
|
|
|
3461
4093
|
export interface TaskStopResult {
|
|
@@ -3864,6 +4496,9 @@ export type CloudToHost =
|
|
|
3864
4496
|
opId: string;
|
|
3865
4497
|
generation?: number;
|
|
3866
4498
|
userId: string;
|
|
4499
|
+
/** ADR-154 §12, sign-in box only: list with this GitHub revision of
|
|
4500
|
+
* the person's, read from the store, instead of a held token. */
|
|
4501
|
+
parameter?: string;
|
|
3867
4502
|
}
|
|
3868
4503
|
// Renew one installation's App ∩ user repository allowlist without sending
|
|
3869
4504
|
// the user's token to the cloud. Kept separate from installation listing so
|
|
@@ -3874,6 +4509,8 @@ export type CloudToHost =
|
|
|
3874
4509
|
generation?: number;
|
|
3875
4510
|
userId: string;
|
|
3876
4511
|
installationId: number;
|
|
4512
|
+
/** ADR-154 §12, as on `gh.installations.list`. */
|
|
4513
|
+
parameter?: string;
|
|
3877
4514
|
}
|
|
3878
4515
|
// Per-user SSH key lifecycle (ADR-029). Keys are generated + stored on the
|
|
3879
4516
|
// host; only the public key crosses the bridge (ssh.key.ack). `get` fetches
|
|
@@ -3899,10 +4536,17 @@ export type CloudToHost =
|
|
|
3899
4536
|
| ProjectEnvReconciledFrame
|
|
3900
4537
|
| ProjectEnvDeliverySkipFrame
|
|
3901
4538
|
| ProjectEnvStagingPutFrame
|
|
4539
|
+
| ProjectEnvLegacyTagFrame
|
|
3902
4540
|
| ProjectEnvReplicaBeginFrame
|
|
3903
4541
|
| ProjectEnvReplicaPageFrame
|
|
3904
4542
|
| ProjectEnvReplicaApplyFrame
|
|
3905
4543
|
| ProjectEnvReplicaCommitFrame
|
|
4544
|
+
// ADR-154: task-keyed v1 replicas coexist with the exact legacy family
|
|
4545
|
+
// until the fleet audit removes pre-v1 sidecars.
|
|
4546
|
+
| ProjectEnvReplicaV1BeginFrame
|
|
4547
|
+
| ProjectEnvReplicaV1PageFrame
|
|
4548
|
+
| ProjectEnvReplicaV1ApplyFrame
|
|
4549
|
+
| ProjectEnvReplicaV1CommitFrame
|
|
3906
4550
|
// User-authed MCP connections (ADR-057). Ops run entirely host-side —
|
|
3907
4551
|
// discovery, DCR, PKCE, token exchange, encrypted storage. Secrets in a
|
|
3908
4552
|
// probe (headerValue, clientSecret) are write-only; nothing secret ever
|
|
@@ -3938,7 +4582,21 @@ export type CloudToHost =
|
|
|
3938
4582
|
| { kind: "event.ack"; seq: number }
|
|
3939
4583
|
// ADR-133 leases: the cloud's answer to a sidecar's `lease.request`.
|
|
3940
4584
|
| { kind: "lease.grant"; opId: string; lease: TaskLease }
|
|
3941
|
-
| { kind: "lease.deny"; opId: string; error: string }
|
|
4585
|
+
| { kind: "lease.deny"; opId: string; error: string }
|
|
4586
|
+
// ADR-154: v1 leases and the frames that keep them current. Exact shapes
|
|
4587
|
+
// and parsers live in ./cloud-credentials.
|
|
4588
|
+
| LeaseGrantV1Frame
|
|
4589
|
+
| LeaseUnavailableFrame
|
|
4590
|
+
| LeaseStaleFrame
|
|
4591
|
+
| CredentialChangedFrame
|
|
4592
|
+
| CredentialRefreshResultFrame
|
|
4593
|
+
| CredentialsActivateResultFrame
|
|
4594
|
+
| McpCaptureProbeFrame
|
|
4595
|
+
| McpCaptureCompleteFrame
|
|
4596
|
+
| McpCaptureCancelFrame
|
|
4597
|
+
| GithubCaptureFrame
|
|
4598
|
+
| SshMintFrame
|
|
4599
|
+
| GithubRevokeFrame;
|
|
3942
4600
|
|
|
3943
4601
|
/** ADR-141: provenance of a channelDeliver. */
|
|
3944
4602
|
export type DeliveryOrigin = "human" | "agent" | "system";
|
|
@@ -4034,10 +4692,26 @@ export type HostToCloud =
|
|
|
4034
4692
|
// no reason, the cloud records none, and that host's operator Stop keeps the
|
|
4035
4693
|
// pre-ADR-144 behaviour rather than mislabelling its quarantines as manual
|
|
4036
4694
|
// and blocking the recovery path they exist to enable.
|
|
4037
|
-
| {
|
|
4695
|
+
| {
|
|
4696
|
+
kind: "task.status";
|
|
4697
|
+
taskId: string;
|
|
4698
|
+
status: string;
|
|
4699
|
+
reason?: string;
|
|
4700
|
+
/** ADR-154 assignment fence; absent on rolling/pre-v1 hosts. */
|
|
4701
|
+
runEpoch?: number;
|
|
4702
|
+
}
|
|
4038
4703
|
| { kind: "ping"; ts: number }
|
|
4039
4704
|
// ADR-133: a sidecar asks for (or refreshes) its task-scoped AWS lease.
|
|
4705
|
+
// ADR-154: a v1 runner also names the run epoch it works under, which the
|
|
4706
|
+
// cloud must still find current.
|
|
4040
4707
|
| { kind: "lease.request"; opId: string; taskId: string }
|
|
4708
|
+
| LeaseRequestV1Frame
|
|
4709
|
+
| CredentialRefreshFrame
|
|
4710
|
+
| CredentialsActivateFrame
|
|
4711
|
+
| CredentialsActivatedFrame
|
|
4712
|
+
| McpCaptureAckFrame
|
|
4713
|
+
| PersonCaptureAckFrame
|
|
4714
|
+
| GithubRevokeAckFrame
|
|
4041
4715
|
| {
|
|
4042
4716
|
kind: "tunnel.ack";
|
|
4043
4717
|
tunnelId: string;
|
|
@@ -4101,7 +4775,9 @@ export type HostToCloud =
|
|
|
4101
4775
|
| ProjectEnvReconcileAckFrame
|
|
4102
4776
|
| ProjectEnvDeliveryAckFrame
|
|
4103
4777
|
| ProjectEnvStagingAckFrame
|
|
4778
|
+
| ProjectEnvLegacyTagAckFrame
|
|
4104
4779
|
| ProjectEnvReplicaAckFrame
|
|
4780
|
+
| ProjectEnvReplicaV1AckFrame
|
|
4105
4781
|
// Ack for mcp.op (ADR-057). `connected` = usable now; `auth_required` =
|
|
4106
4782
|
// open authorizeUrl in the user's browser and wait for the callback;
|
|
4107
4783
|
// `disconnected` = secrets gone. No secret material ever crosses back.
|