@sealant/api-contracts-next 0.39.0-next.694 → 0.39.0-next.696
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/dist/capture-owner-map.d.ts +66 -0
- package/dist/capture-owner-map.js +104 -0
- package/dist/core-api/control-plane.d.ts +11 -1
- package/dist/core-api/runs.d.ts +1 -1
- package/dist/core-api/runs.js +2 -1
- package/dist/core-api/workspaces.d.ts +48 -0
- package/dist/core-api/workspaces.js +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The owner map of a capture workspace (sealantd ADR-0015 "Per-person saved directories", Mend's
|
|
3
|
+
* ADR 0016 decision 8): who owns what the daemon's restore writes, and so whether its executor is a
|
|
4
|
+
* per-person one. The ONE definition of its shape, its bounds and its wire encoding, shared by the
|
|
5
|
+
* SDK (client-side refusal with the server's wording), the control-plane validators (every parse:
|
|
6
|
+
* create, worker, restart) and the runtime adapters (the boot env).
|
|
7
|
+
*
|
|
8
|
+
* The daemon reads it from its boot environment as `SEALANT_CAPTURE_OWNER_MAP`, JSON
|
|
9
|
+
* `{"gid":…,"worktree":…,"people":{"<id>":uid,…}}`, parsed strictly (unknown fields refused):
|
|
10
|
+
*
|
|
11
|
+
* - each listed person's saved directory (`<harness home>/people/<id>/`) is restored owned by their
|
|
12
|
+
* uid and the group, the directory itself 0710, its `conversations/` group-readable and -writable;
|
|
13
|
+
* - the worktree and its git directory are given to the group (owned by `worktreeUid`, setgid, the
|
|
14
|
+
* owner's bits copied to the group);
|
|
15
|
+
* - a root daemon whose map names at least one person does not set no-new-privileges, so every
|
|
16
|
+
* person's passwordless `sudo` works. A map naming nobody keeps it.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately dependency-free, like `workspace-environment.ts`: data plus pure functions.
|
|
19
|
+
*/
|
|
20
|
+
/** The daemon's name for the owner map in its boot environment. */
|
|
21
|
+
export declare const CAPTURE_OWNER_MAP_ENV = "SEALANT_CAPTURE_OWNER_MAP";
|
|
22
|
+
/** The shared group every person is in: Mend's `mend`, the reserved range's first id. */
|
|
23
|
+
export declare const CAPTURE_OWNER_MAP_GID = 40000;
|
|
24
|
+
/**
|
|
25
|
+
* The uids people may have: the reserved range (40000–49999) less its first id, the group's. The
|
|
26
|
+
* managed images leave the whole range free (their probe checks it).
|
|
27
|
+
*/
|
|
28
|
+
export declare const CAPTURE_OWNER_MAP_UID_RANGE: {
|
|
29
|
+
readonly first: 40001;
|
|
30
|
+
readonly last: 49999;
|
|
31
|
+
};
|
|
32
|
+
/** At most this many people in one map. */
|
|
33
|
+
export declare const CAPTURE_OWNER_MAP_MAX_PEOPLE = 256;
|
|
34
|
+
/**
|
|
35
|
+
* A person's id: the name of their saved directory under `people/` (Mend's account id). One plain
|
|
36
|
+
* path component of at most 128 characters that starts with a letter, a digit or `_` (so never `.`
|
|
37
|
+
* or `..`).
|
|
38
|
+
*/
|
|
39
|
+
export declare const CAPTURE_OWNER_MAP_ID_PATTERN: RegExp;
|
|
40
|
+
/** One person in an owner map. */
|
|
41
|
+
export interface CaptureOwnerMapPerson {
|
|
42
|
+
/** The name of their saved directory under `<harness home>/people/` (Mend's account id). */
|
|
43
|
+
readonly id: string;
|
|
44
|
+
/** Their Linux uid, in {@link CAPTURE_OWNER_MAP_UID_RANGE}. */
|
|
45
|
+
readonly uid: number;
|
|
46
|
+
}
|
|
47
|
+
/** Who owns what a capture restore writes. */
|
|
48
|
+
export interface CaptureOwnerMap {
|
|
49
|
+
/** The shared group every person is in: {@link CAPTURE_OWNER_MAP_GID}. */
|
|
50
|
+
readonly gid: number;
|
|
51
|
+
/** The change's owner: owns the worktree root and its git directory. */
|
|
52
|
+
readonly worktreeUid: number;
|
|
53
|
+
/** Each current member. A removed member is left out: their directory is restored root's. */
|
|
54
|
+
readonly people: ReadonlyArray<CaptureOwnerMapPerson>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* What is wrong with a map, in the words the control plane answers with; empty when nothing is.
|
|
58
|
+
* Checks the gid, every uid's range, every id's form, and that no id and no uid appears twice (one
|
|
59
|
+
* person's saved directory is never handed to another).
|
|
60
|
+
*/
|
|
61
|
+
export declare const captureOwnerMapProblems: (map: CaptureOwnerMap) => readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* The map as the daemon parses it (`SEALANT_CAPTURE_OWNER_MAP`): compact JSON, `people` an object
|
|
64
|
+
* keyed by id in code-point order (the daemon's own order), always present.
|
|
65
|
+
*/
|
|
66
|
+
export declare const encodeCaptureOwnerMap: (map: CaptureOwnerMap) => string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The owner map of a capture workspace (sealantd ADR-0015 "Per-person saved directories", Mend's
|
|
3
|
+
* ADR 0016 decision 8): who owns what the daemon's restore writes, and so whether its executor is a
|
|
4
|
+
* per-person one. The ONE definition of its shape, its bounds and its wire encoding, shared by the
|
|
5
|
+
* SDK (client-side refusal with the server's wording), the control-plane validators (every parse:
|
|
6
|
+
* create, worker, restart) and the runtime adapters (the boot env).
|
|
7
|
+
*
|
|
8
|
+
* The daemon reads it from its boot environment as `SEALANT_CAPTURE_OWNER_MAP`, JSON
|
|
9
|
+
* `{"gid":…,"worktree":…,"people":{"<id>":uid,…}}`, parsed strictly (unknown fields refused):
|
|
10
|
+
*
|
|
11
|
+
* - each listed person's saved directory (`<harness home>/people/<id>/`) is restored owned by their
|
|
12
|
+
* uid and the group, the directory itself 0710, its `conversations/` group-readable and -writable;
|
|
13
|
+
* - the worktree and its git directory are given to the group (owned by `worktreeUid`, setgid, the
|
|
14
|
+
* owner's bits copied to the group);
|
|
15
|
+
* - a root daemon whose map names at least one person does not set no-new-privileges, so every
|
|
16
|
+
* person's passwordless `sudo` works. A map naming nobody keeps it.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately dependency-free, like `workspace-environment.ts`: data plus pure functions.
|
|
19
|
+
*/
|
|
20
|
+
/** The daemon's name for the owner map in its boot environment. */
|
|
21
|
+
export const CAPTURE_OWNER_MAP_ENV = "SEALANT_CAPTURE_OWNER_MAP";
|
|
22
|
+
/** The shared group every person is in: Mend's `mend`, the reserved range's first id. */
|
|
23
|
+
export const CAPTURE_OWNER_MAP_GID = 40_000;
|
|
24
|
+
/**
|
|
25
|
+
* The uids people may have: the reserved range (40000–49999) less its first id, the group's. The
|
|
26
|
+
* managed images leave the whole range free (their probe checks it).
|
|
27
|
+
*/
|
|
28
|
+
export const CAPTURE_OWNER_MAP_UID_RANGE = { first: 40_001, last: 49_999 };
|
|
29
|
+
/** At most this many people in one map. */
|
|
30
|
+
export const CAPTURE_OWNER_MAP_MAX_PEOPLE = 256;
|
|
31
|
+
/**
|
|
32
|
+
* A person's id: the name of their saved directory under `people/` (Mend's account id). One plain
|
|
33
|
+
* path component of at most 128 characters that starts with a letter, a digit or `_` (so never `.`
|
|
34
|
+
* or `..`).
|
|
35
|
+
*/
|
|
36
|
+
export const CAPTURE_OWNER_MAP_ID_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/u;
|
|
37
|
+
const isUid = (value) => Number.isInteger(value) &&
|
|
38
|
+
value >= CAPTURE_OWNER_MAP_UID_RANGE.first &&
|
|
39
|
+
value <= CAPTURE_OWNER_MAP_UID_RANGE.last;
|
|
40
|
+
const UID_RANGE_TEXT = `${CAPTURE_OWNER_MAP_UID_RANGE.first}–${CAPTURE_OWNER_MAP_UID_RANGE.last}`;
|
|
41
|
+
/**
|
|
42
|
+
* What is wrong with a map, in the words the control plane answers with; empty when nothing is.
|
|
43
|
+
* Checks the gid, every uid's range, every id's form, and that no id and no uid appears twice (one
|
|
44
|
+
* person's saved directory is never handed to another).
|
|
45
|
+
*/
|
|
46
|
+
export const captureOwnerMapProblems = (map) => {
|
|
47
|
+
// A plain JavaScript caller may hand anything: refused in words, never a TypeError.
|
|
48
|
+
const shape = map;
|
|
49
|
+
if (typeof shape !== "object" || shape === null) {
|
|
50
|
+
return ["ownerMap must be an object { gid, worktreeUid, people }"];
|
|
51
|
+
}
|
|
52
|
+
if (!Array.isArray(map.people)) {
|
|
53
|
+
return ["ownerMap.people must be an array of { id, uid }"];
|
|
54
|
+
}
|
|
55
|
+
const problems = [];
|
|
56
|
+
if (map.gid !== CAPTURE_OWNER_MAP_GID) {
|
|
57
|
+
problems.push(`ownerMap.gid must be ${CAPTURE_OWNER_MAP_GID} (the mend group), not ${map.gid}`);
|
|
58
|
+
}
|
|
59
|
+
if (!isUid(map.worktreeUid)) {
|
|
60
|
+
problems.push(`ownerMap.worktreeUid ${map.worktreeUid} is not a uid in ${UID_RANGE_TEXT}`);
|
|
61
|
+
}
|
|
62
|
+
if (map.people.length > CAPTURE_OWNER_MAP_MAX_PEOPLE) {
|
|
63
|
+
problems.push(`ownerMap.people has ${map.people.length} people; the maximum is ${CAPTURE_OWNER_MAP_MAX_PEOPLE}`);
|
|
64
|
+
return problems;
|
|
65
|
+
}
|
|
66
|
+
const ids = new Set();
|
|
67
|
+
const uids = new Map();
|
|
68
|
+
for (const [index, person] of map.people.entries()) {
|
|
69
|
+
const entry = person;
|
|
70
|
+
if (typeof entry !== "object" || entry === null) {
|
|
71
|
+
problems.push(`ownerMap.people[${index}] must be an object { id, uid }`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (typeof person.id !== "string" || !CAPTURE_OWNER_MAP_ID_PATTERN.test(person.id)) {
|
|
75
|
+
problems.push(`ownerMap.people[${index}].id must be one directory name of letters, digits, '.', '_' or '-' (at most 128, not starting with '.' or '-')`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (ids.has(person.id)) {
|
|
79
|
+
problems.push(`ownerMap.people names ${person.id} twice`);
|
|
80
|
+
}
|
|
81
|
+
ids.add(person.id);
|
|
82
|
+
if (!isUid(person.uid)) {
|
|
83
|
+
problems.push(`ownerMap.people ${person.id} has uid ${person.uid}, not a uid in ${UID_RANGE_TEXT}`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const other = uids.get(person.uid);
|
|
87
|
+
if (other !== undefined && other !== person.id) {
|
|
88
|
+
problems.push(`ownerMap.people gives ${other} and ${person.id} the same uid ${person.uid}`);
|
|
89
|
+
}
|
|
90
|
+
uids.set(person.uid, person.id);
|
|
91
|
+
}
|
|
92
|
+
return problems;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* The map as the daemon parses it (`SEALANT_CAPTURE_OWNER_MAP`): compact JSON, `people` an object
|
|
96
|
+
* keyed by id in code-point order (the daemon's own order), always present.
|
|
97
|
+
*/
|
|
98
|
+
export const encodeCaptureOwnerMap = (map) => {
|
|
99
|
+
// `fromEntries` defines own properties, so an id such as `__proto__` stays a key.
|
|
100
|
+
const people = Object.fromEntries(map.people
|
|
101
|
+
.toSorted((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
|
102
|
+
.map((person) => [person.id, person.uid]));
|
|
103
|
+
return JSON.stringify({ gid: map.gid, worktree: map.worktreeUid, people });
|
|
104
|
+
};
|
|
@@ -462,7 +462,7 @@ export declare const ControlPlaneAPI: HttpApi.HttpApi<"sealantControlPlaneApi",
|
|
|
462
462
|
readonly recordDeletedAt: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
463
463
|
readonly createdAt: import("effect/Schema").String;
|
|
464
464
|
readonly updatedAt: import("effect/Schema").String;
|
|
465
|
-
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<typeof import("./runs.js").RunNotFoundError | typeof import("./runs.js").RunInternalServerError>, never, never> | import("effect/unstable/httpapi/HttpApiEndpoint").HttpApiEndpoint<"getRunChanges", "GET", "/v1/runs/:runId/changes", import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
465
|
+
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<typeof import("./budgets.ts").BudgetExceededError | typeof import("./runs.js").RunNotFoundError | typeof import("./runs.js").RunInternalServerError>, never, never> | import("effect/unstable/httpapi/HttpApiEndpoint").HttpApiEndpoint<"getRunChanges", "GET", "/v1/runs/:runId/changes", import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
466
466
|
readonly runId: import("effect/Schema").String;
|
|
467
467
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
468
468
|
readonly ownerUserId: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
@@ -1026,6 +1026,7 @@ export declare const ControlPlaneAPI: HttpApi.HttpApi<"sealantControlPlaneApi",
|
|
|
1026
1026
|
readonly runningMs: import("effect/Schema").Number;
|
|
1027
1027
|
readonly boundMs: import("effect/Schema").Number;
|
|
1028
1028
|
}>>;
|
|
1029
|
+
readonly ownerMap: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1029
1030
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<typeof import("./workspaces.js").WorkspaceBadRequestError | typeof import("./workspaces.js").WorkspaceNotFoundError | typeof import("./workspaces.js").WorkspaceConflictError | typeof import("./workspaces.js").WorkspaceInternalServerError>, never, never> | import("effect/unstable/httpapi/HttpApiEndpoint").HttpApiEndpoint<"getWorkspace", "GET", "/v1/workspaces/:workspaceId", import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
1030
1031
|
readonly workspaceId: import("effect/Schema").String;
|
|
1031
1032
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
@@ -1164,6 +1165,7 @@ export declare const ControlPlaneAPI: HttpApi.HttpApi<"sealantControlPlaneApi",
|
|
|
1164
1165
|
readonly runningMs: import("effect/Schema").Number;
|
|
1165
1166
|
readonly boundMs: import("effect/Schema").Number;
|
|
1166
1167
|
}>>;
|
|
1168
|
+
readonly ownerMap: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1167
1169
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<typeof import("./workspaces.js").WorkspaceBadRequestError | typeof import("./workspaces.js").WorkspaceNotFoundError | typeof import("./workspaces.js").WorkspaceConflictError | typeof import("./workspaces.js").WorkspaceInternalServerError>, never, never> | import("effect/unstable/httpapi/HttpApiEndpoint").HttpApiEndpoint<"getWorkspaceCreate", "GET", "/v1/workspaces/idempotency-keys/:idempotencyKey", import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
1168
1170
|
readonly idempotencyKey: import("effect/Schema").String;
|
|
1169
1171
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
@@ -1410,6 +1412,14 @@ export declare const ControlPlaneAPI: HttpApi.HttpApi<"sealantControlPlaneApi",
|
|
|
1410
1412
|
readonly workspaceId: import("effect/Schema").String;
|
|
1411
1413
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<never>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<import("effect/Schema").Struct<{
|
|
1412
1414
|
readonly ownerUserId: import("effect/Schema").String;
|
|
1415
|
+
readonly expectedOwnerMap: import("effect/Schema").optional<import("effect/Schema").NullOr<import("effect/Schema").Struct<{
|
|
1416
|
+
readonly gid: import("effect/Schema").Number;
|
|
1417
|
+
readonly worktreeUid: import("effect/Schema").Number;
|
|
1418
|
+
readonly people: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
1419
|
+
readonly id: import("effect/Schema").String;
|
|
1420
|
+
readonly uid: import("effect/Schema").Number;
|
|
1421
|
+
}>>;
|
|
1422
|
+
}>>>;
|
|
1413
1423
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<never>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<import("effect/Schema").Struct<{
|
|
1414
1424
|
readonly worktreeId: import("effect/Schema").String;
|
|
1415
1425
|
readonly epoch: import("effect/Schema").Number;
|
package/dist/core-api/runs.d.ts
CHANGED
|
@@ -380,7 +380,7 @@ export declare const RunsGroup: HttpApiGroup.HttpApiGroup<"runs", HttpApiEndpoin
|
|
|
380
380
|
readonly recordDeletedAt: Schema.optional<Schema.String>;
|
|
381
381
|
readonly createdAt: Schema.String;
|
|
382
382
|
readonly updatedAt: Schema.String;
|
|
383
|
-
}>>, HttpApiEndpoint.Json<typeof RunNotFoundError | typeof RunInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getRunChanges", "GET", "/:runId/changes", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
383
|
+
}>>, HttpApiEndpoint.Json<typeof BudgetExceededError | typeof RunNotFoundError | typeof RunInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getRunChanges", "GET", "/:runId/changes", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
384
384
|
readonly runId: Schema.String;
|
|
385
385
|
}>>, HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
386
386
|
readonly ownerUserId: Schema.optional<Schema.String>;
|
package/dist/core-api/runs.js
CHANGED
|
@@ -258,7 +258,8 @@ export const RunsGroup = HttpApiGroup.make("runs")
|
|
|
258
258
|
params: runIdParams,
|
|
259
259
|
query: runOwnerQuerySchema,
|
|
260
260
|
success: runSchema,
|
|
261
|
-
|
|
261
|
+
// The per-credential request budget answers 429 before any handler runs.
|
|
262
|
+
error: [BudgetExceededError, RunNotFoundError, RunInternalServerError],
|
|
262
263
|
}))
|
|
263
264
|
.add(HttpApiEndpoint.patch("updateRun", "/:runId", {
|
|
264
265
|
params: runIdParams,
|
|
@@ -406,6 +406,12 @@ export declare const workspaceCaptureStatusSchema: Schema.Struct<{
|
|
|
406
406
|
readonly runningMs: Schema.Number;
|
|
407
407
|
readonly boundMs: Schema.Number;
|
|
408
408
|
}>>;
|
|
409
|
+
/**
|
|
410
|
+
* The executor booted under an owner map (`SEALANT_CAPTURE_OWNER_MAP`): its restores give each
|
|
411
|
+
* mapped person's saved directory to them and the worktree to the group. Absent from a control
|
|
412
|
+
* plane that predates it; false from a daemon that predates it.
|
|
413
|
+
*/
|
|
414
|
+
readonly ownerMap: Schema.optional<Schema.Boolean>;
|
|
409
415
|
}>;
|
|
410
416
|
export type WorkspaceCaptureStatus = typeof workspaceCaptureStatusSchema.Type;
|
|
411
417
|
/**
|
|
@@ -417,6 +423,21 @@ export type WorkspaceCaptureStatus = typeof workspaceCaptureStatusSchema.Type;
|
|
|
417
423
|
*/
|
|
418
424
|
export declare const replanWorkspaceCaptureRequestSchema: Schema.Struct<{
|
|
419
425
|
readonly ownerUserId: Schema.String;
|
|
426
|
+
/**
|
|
427
|
+
* The owner map the claim needs (a capture source's `ownerMap`), or `null` for none. The daemon
|
|
428
|
+
* reads its map only at boot, so a re-plan restores under the map the executor was launched
|
|
429
|
+
* with: when this is given and differs from that map (including a standby launched with none),
|
|
430
|
+
* the re-plan is refused with `409` code `owner-map-mismatch` before the daemon is reached.
|
|
431
|
+
* Omitted, nothing is compared.
|
|
432
|
+
*/
|
|
433
|
+
readonly expectedOwnerMap: Schema.optional<Schema.NullOr<Schema.Struct<{
|
|
434
|
+
readonly gid: Schema.Number;
|
|
435
|
+
readonly worktreeUid: Schema.Number;
|
|
436
|
+
readonly people: Schema.$Array<Schema.Struct<{
|
|
437
|
+
readonly id: Schema.String;
|
|
438
|
+
readonly uid: Schema.Number;
|
|
439
|
+
}>>;
|
|
440
|
+
}>>>;
|
|
420
441
|
}>;
|
|
421
442
|
export type ReplanWorkspaceCaptureRequest = typeof replanWorkspaceCaptureRequestSchema.Type;
|
|
422
443
|
export declare const workspaceCaptureReplannedSchema: Schema.Struct<{
|
|
@@ -1705,6 +1726,12 @@ export declare const WorkspacesGroup: HttpApiGroup.HttpApiGroup<"workspaces", Ht
|
|
|
1705
1726
|
readonly runningMs: Schema.Number;
|
|
1706
1727
|
readonly boundMs: Schema.Number;
|
|
1707
1728
|
}>>;
|
|
1729
|
+
/**
|
|
1730
|
+
* The executor booted under an owner map (`SEALANT_CAPTURE_OWNER_MAP`): its restores give each
|
|
1731
|
+
* mapped person's saved directory to them and the worktree to the group. Absent from a control
|
|
1732
|
+
* plane that predates it; false from a daemon that predates it.
|
|
1733
|
+
*/
|
|
1734
|
+
readonly ownerMap: Schema.optional<Schema.Boolean>;
|
|
1708
1735
|
}>>, HttpApiEndpoint.Json<typeof WorkspaceBadRequestError | typeof WorkspaceNotFoundError | typeof WorkspaceConflictError | typeof WorkspaceInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getWorkspace", "GET", "/:workspaceId", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
1709
1736
|
readonly workspaceId: Schema.String;
|
|
1710
1737
|
}>>, HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
@@ -1956,6 +1983,12 @@ export declare const WorkspacesGroup: HttpApiGroup.HttpApiGroup<"workspaces", Ht
|
|
|
1956
1983
|
readonly runningMs: Schema.Number;
|
|
1957
1984
|
readonly boundMs: Schema.Number;
|
|
1958
1985
|
}>>;
|
|
1986
|
+
/**
|
|
1987
|
+
* The executor booted under an owner map (`SEALANT_CAPTURE_OWNER_MAP`): its restores give each
|
|
1988
|
+
* mapped person's saved directory to them and the worktree to the group. Absent from a control
|
|
1989
|
+
* plane that predates it; false from a daemon that predates it.
|
|
1990
|
+
*/
|
|
1991
|
+
readonly ownerMap: Schema.optional<Schema.Boolean>;
|
|
1959
1992
|
}>>, HttpApiEndpoint.Json<typeof WorkspaceBadRequestError | typeof WorkspaceNotFoundError | typeof WorkspaceConflictError | typeof WorkspaceInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getWorkspaceCreate", "GET", "/idempotency-keys/:idempotencyKey", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
1960
1993
|
readonly idempotencyKey: Schema.String;
|
|
1961
1994
|
}>>, HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
@@ -2290,6 +2323,21 @@ export declare const WorkspacesGroup: HttpApiGroup.HttpApiGroup<"workspaces", Ht
|
|
|
2290
2323
|
readonly workspaceId: Schema.String;
|
|
2291
2324
|
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
|
|
2292
2325
|
readonly ownerUserId: Schema.String;
|
|
2326
|
+
/**
|
|
2327
|
+
* The owner map the claim needs (a capture source's `ownerMap`), or `null` for none. The daemon
|
|
2328
|
+
* reads its map only at boot, so a re-plan restores under the map the executor was launched
|
|
2329
|
+
* with: when this is given and differs from that map (including a standby launched with none),
|
|
2330
|
+
* the re-plan is refused with `409` code `owner-map-mismatch` before the daemon is reached.
|
|
2331
|
+
* Omitted, nothing is compared.
|
|
2332
|
+
*/
|
|
2333
|
+
readonly expectedOwnerMap: Schema.optional<Schema.NullOr<Schema.Struct<{
|
|
2334
|
+
readonly gid: Schema.Number;
|
|
2335
|
+
readonly worktreeUid: Schema.Number;
|
|
2336
|
+
readonly people: Schema.$Array<Schema.Struct<{
|
|
2337
|
+
readonly id: Schema.String;
|
|
2338
|
+
readonly uid: Schema.Number;
|
|
2339
|
+
}>>;
|
|
2340
|
+
}>>>;
|
|
2293
2341
|
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
|
|
2294
2342
|
readonly worktreeId: Schema.String;
|
|
2295
2343
|
readonly epoch: Schema.Number;
|
|
@@ -357,6 +357,12 @@ export const workspaceCaptureStatusSchema = Schema.Struct({
|
|
|
357
357
|
* past its bound, and from a daemon or control plane that predates it.
|
|
358
358
|
*/
|
|
359
359
|
overdue: Schema.optional(captureOverdueSchema),
|
|
360
|
+
/**
|
|
361
|
+
* The executor booted under an owner map (`SEALANT_CAPTURE_OWNER_MAP`): its restores give each
|
|
362
|
+
* mapped person's saved directory to them and the worktree to the group. Absent from a control
|
|
363
|
+
* plane that predates it; false from a daemon that predates it.
|
|
364
|
+
*/
|
|
365
|
+
ownerMap: Schema.optional(Schema.Boolean),
|
|
360
366
|
});
|
|
361
367
|
/**
|
|
362
368
|
* Re-plan a capture-sourced workspace (sealantd 0.15 `capture.replan`, the claim hook): the daemon
|
|
@@ -367,6 +373,18 @@ export const workspaceCaptureStatusSchema = Schema.Struct({
|
|
|
367
373
|
*/
|
|
368
374
|
export const replanWorkspaceCaptureRequestSchema = Schema.Struct({
|
|
369
375
|
ownerUserId: NonEmptyString,
|
|
376
|
+
/**
|
|
377
|
+
* The owner map the claim needs (a capture source's `ownerMap`), or `null` for none. The daemon
|
|
378
|
+
* reads its map only at boot, so a re-plan restores under the map the executor was launched
|
|
379
|
+
* with: when this is given and differs from that map (including a standby launched with none),
|
|
380
|
+
* the re-plan is refused with `409` code `owner-map-mismatch` before the daemon is reached.
|
|
381
|
+
* Omitted, nothing is compared.
|
|
382
|
+
*/
|
|
383
|
+
expectedOwnerMap: Schema.optional(Schema.NullOr(Schema.Struct({
|
|
384
|
+
gid: Schema.Number,
|
|
385
|
+
worktreeUid: Schema.Number,
|
|
386
|
+
people: Schema.Array(Schema.Struct({ id: Schema.String, uid: Schema.Number })),
|
|
387
|
+
}))),
|
|
370
388
|
});
|
|
371
389
|
export const workspaceCaptureReplannedSchema = Schema.Struct({
|
|
372
390
|
worktreeId: NonEmptyString,
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sealant/api-contracts-next",
|
|
3
|
-
"version": "0.39.0-next.
|
|
3
|
+
"version": "0.39.0-next.696",
|
|
4
4
|
"description": "Contract-first HTTP API definitions for Sealant services",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./workspace-environment": {
|
|
18
18
|
"types": "./dist/workspace-environment.d.ts",
|
|
19
19
|
"import": "./dist/workspace-environment.js"
|
|
20
|
+
},
|
|
21
|
+
"./capture-owner-map": {
|
|
22
|
+
"types": "./dist/capture-owner-map.d.ts",
|
|
23
|
+
"import": "./dist/capture-owner-map.js"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"files": [
|
|
@@ -28,7 +32,7 @@
|
|
|
28
32
|
"peerDependencies": {
|
|
29
33
|
"effect": ">=4.0.0-beta.85 <5"
|
|
30
34
|
},
|
|
31
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "55326dd1cfb131e4324cecc5cd896f5bee35980c",
|
|
32
36
|
"publishConfig": {
|
|
33
37
|
"access": "public"
|
|
34
38
|
}
|