@corca-org/ceal-host-darwin-arm64 1.0.10 → 1.0.11
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/bin/ceal-host +0 -0
- package/libexec/ceal-host/claude-hook.mjs +562 -204
- package/libexec/ceal-host/claude-invocation-authentication.mjs +584 -198
- package/libexec/ceal-host/claude-producer.mjs +618 -198
- package/libexec/ceal-host/codex-hook.mjs +1069 -575
- package/libexec/ceal-host/codex-invocation-authentication.mjs +766 -373
- package/libexec/ceal-host/codex-producer.mjs +803 -376
- package/libexec/ceal-host/control.mjs +1586 -777
- package/libexec/ceal-host/relay.mjs +1160 -563
- package/package.json +1 -1
- package/share/ceal-host/bundle-manifest.json +12 -12
- package/share/ceal-host/bundle-manifest.sigstore.json +1 -1
|
@@ -61,6 +61,46 @@ var client_wire_contract_default = {
|
|
|
61
61
|
schema_version: "ceal.command_request.v1",
|
|
62
62
|
absolute_transport_max_bytes: 65536
|
|
63
63
|
},
|
|
64
|
+
command_resolution: {
|
|
65
|
+
schema_version: "ceal.gateway_command_resolution.v1",
|
|
66
|
+
authority: {
|
|
67
|
+
anonymous: {
|
|
68
|
+
state: "anonymous",
|
|
69
|
+
fields: [
|
|
70
|
+
"state"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
authenticated: {
|
|
74
|
+
state: "authenticated",
|
|
75
|
+
fields: [
|
|
76
|
+
"state",
|
|
77
|
+
"principal_kind",
|
|
78
|
+
"organization_ref",
|
|
79
|
+
"instance_ref",
|
|
80
|
+
"access_group_ref",
|
|
81
|
+
"principal_ref",
|
|
82
|
+
"authority_revision_ref"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
semantic_options: {
|
|
87
|
+
inspection: {
|
|
88
|
+
values: [
|
|
89
|
+
"default",
|
|
90
|
+
"full"
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
cache_mode: {
|
|
94
|
+
values: [
|
|
95
|
+
"default",
|
|
96
|
+
"bypass"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
compact: {
|
|
100
|
+
type: "boolean"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
64
104
|
command_response: {
|
|
65
105
|
schema_version: "ceal.command_response.v1",
|
|
66
106
|
descriptor_field: "verified_artifact",
|
|
@@ -74,12 +114,15 @@ var client_wire_contract_default = {
|
|
|
74
114
|
],
|
|
75
115
|
bare_document_fields: [
|
|
76
116
|
"invocation",
|
|
77
|
-
"required_action_sequence",
|
|
78
117
|
"entrypoints",
|
|
79
118
|
"common_concepts",
|
|
80
119
|
"operations",
|
|
81
120
|
"catalog_revision"
|
|
82
121
|
],
|
|
122
|
+
bare_document_optional_fields: [
|
|
123
|
+
"required_action_sequence",
|
|
124
|
+
"withheld"
|
|
125
|
+
],
|
|
83
126
|
prompt_field: "prompt",
|
|
84
127
|
prompt_schema_version: "ceal.prompt.v1",
|
|
85
128
|
focused_prompt_document_fields: [
|
|
@@ -92,7 +135,7 @@ var client_wire_contract_default = {
|
|
|
92
135
|
"prompt"
|
|
93
136
|
],
|
|
94
137
|
discovery_command: "discovery",
|
|
95
|
-
discovery_manifest_schema_version: "ceal.discovery_manifest.
|
|
138
|
+
discovery_manifest_schema_version: "ceal.discovery_manifest.v2",
|
|
96
139
|
discovery_manifest_document_fields: [
|
|
97
140
|
"discovery_manifest"
|
|
98
141
|
],
|
|
@@ -100,11 +143,29 @@ var client_wire_contract_default = {
|
|
|
100
143
|
bare_document_max_bytes: 65536,
|
|
101
144
|
complete_max_bytes: 66019,
|
|
102
145
|
absolute_transport_max_bytes: 4194304
|
|
146
|
+
},
|
|
147
|
+
resource_stream: {
|
|
148
|
+
path: "/api/ceal/v2/resources",
|
|
149
|
+
request_schema_version: "ceal.resource_stream_request.v1",
|
|
150
|
+
ref_pattern: "^resource:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
151
|
+
media_type_pattern: "^[a-z0-9][a-z0-9!#$&^_.+-]{0,63}/[a-z0-9][a-z0-9!#$&^_.+-]{0,63}$",
|
|
152
|
+
filename_max_bytes: 512,
|
|
153
|
+
max_bytes: 9007199254740991,
|
|
154
|
+
deadline_ms: 6e5,
|
|
155
|
+
fetch_argv: [
|
|
156
|
+
"resource",
|
|
157
|
+
"fetch"
|
|
158
|
+
],
|
|
159
|
+
ref_flag: "--ref"
|
|
103
160
|
}
|
|
104
161
|
};
|
|
105
162
|
|
|
106
163
|
// packages/ceal-client-protocol/src/client-wire-contract.ts
|
|
107
164
|
var CEAL_CLIENT_WIRE_CONTRACT = Object.freeze(client_wire_contract_default);
|
|
165
|
+
var commandResolution = CEAL_CLIENT_WIRE_CONTRACT.command_resolution;
|
|
166
|
+
var commandResolutionAuthority = commandResolution.authority;
|
|
167
|
+
var commandResolutionSemanticOptions = commandResolution.semantic_options;
|
|
168
|
+
var CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION = commandResolution.schema_version;
|
|
108
169
|
var artifact = CEAL_CLIENT_WIRE_CONTRACT.artifact_descriptor;
|
|
109
170
|
var request = CEAL_CLIENT_WIRE_CONTRACT.command_request;
|
|
110
171
|
var response = CEAL_CLIENT_WIRE_CONTRACT.command_response;
|
|
@@ -125,6 +186,7 @@ var CEAL_COMMAND_REQUEST_MAX_BYTES = request.absolute_transport_max_bytes;
|
|
|
125
186
|
var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_MAX_BYTES = response.bare_document_max_bytes;
|
|
126
187
|
var CEAL_COMMAND_RESPONSE_PROMPT_FIELD = response.prompt_field;
|
|
127
188
|
var CEAL_COMMAND_RESPONSE_PROMPT_SCHEMA_VERSION = response.prompt_schema_version;
|
|
189
|
+
var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_OPTIONAL_FIELDS = Object.freeze([...response.bare_document_optional_fields]);
|
|
128
190
|
var CEAL_COMMAND_RESPONSE_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.bare_document_fields, response.prompt_field]);
|
|
129
191
|
var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_document_fields]);
|
|
130
192
|
var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_CONTINUATION_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_continuation_document_fields]);
|
|
@@ -138,9 +200,50 @@ function vocabulary(words) {
|
|
|
138
200
|
return Object.freeze(words);
|
|
139
201
|
}
|
|
140
202
|
|
|
203
|
+
// packages/ceal-client-protocol/src/gateway-response-types.ts
|
|
204
|
+
var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
|
|
205
|
+
var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
|
|
206
|
+
var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
|
|
207
|
+
var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
|
|
208
|
+
var CEAL_GATEWAY_READ_REUSE_DISPOSITIONS = vocabulary(["response_cache_hit", "corpus_hit", "corpus_hit_after_revalidation", "corpus_partial_provider_supplement", "provider_direct", "provider_continuation", "reuse_unavailable"]);
|
|
209
|
+
var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
|
|
210
|
+
var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
|
|
211
|
+
var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
|
|
212
|
+
var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
|
|
213
|
+
var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "agent_lease_admission", "provider_authenticated_event"]);
|
|
214
|
+
var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
|
|
215
|
+
var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
|
|
216
|
+
var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
|
|
217
|
+
var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
|
|
218
|
+
var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
|
|
219
|
+
var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verified", "not_dispatched"]);
|
|
220
|
+
var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
|
|
221
|
+
var CEAL_PROTOCOL_VERSION = "1.4.0";
|
|
222
|
+
var CEAL_GATEWAY_ANNOUNCEMENT_SCOPE_STATEMENTS = Object.freeze({
|
|
223
|
+
github_app_installation_repositories: "Repositories in the installed GitHub App installation.",
|
|
224
|
+
slack_public_app_member_channels_only: "Public channels where the installed Slack app is a member, plus direct and multi-person direct messages the requester is a member of, as Slack reports that membership; private channels the administrator has not approved are not declared by this connector.",
|
|
225
|
+
notion_connected_logical_area: "Connected Notion logical area under provider-enforced sharing; descendant inventory is not declared.",
|
|
226
|
+
google_workspace_calendar_read_only: "Approved Calendar availability and event reads only; Calendar mutation is not declared.",
|
|
227
|
+
google_workspace_admitted_drive_target_metadata: "Metadata search for files in the shared drives and directly shared files the Connector reaches and the Organization admits as Targets; file-content read and mutation are not declared.",
|
|
228
|
+
google_workspace_admitted_drive_target_sheet_ranges: "Bounded values reads from Google Sheets in the shared drives and directly shared files the Connector reaches and the Organization admits as Targets; file mutation is not declared.",
|
|
229
|
+
google_workspace_admitted_drive_target_sheet_tabs: "Bounded tab metadata and grid extents from Google Sheets in the shared drives and directly shared files the Connector reaches and the Organization admits as Targets; cell contents and file mutation are not declared.",
|
|
230
|
+
google_workspace_admitted_drive_target_editable_sheet_ranges: "Bounded values updates and clears in editable Google Sheets in the shared drives and directly shared files the Connector reaches and the Organization admits as Targets; Docs, Slides, and other Drive file mutation are not declared."
|
|
231
|
+
});
|
|
232
|
+
var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
|
|
233
|
+
var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
|
|
234
|
+
var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
|
|
235
|
+
var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
|
|
236
|
+
var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
|
|
237
|
+
"connector_ref",
|
|
238
|
+
"connector_revision",
|
|
239
|
+
"policy_decision_revision"
|
|
240
|
+
]);
|
|
241
|
+
|
|
141
242
|
// packages/ceal-client-protocol/src/operation-contract-types.ts
|
|
142
243
|
var CEAL_JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
|
|
143
244
|
var CEAL_OPERATION_ERROR_SCHEMA_VERSION = "ceal.operation_error.v1";
|
|
245
|
+
var CEAL_OPERATION_WITHHELD_CAUSES = vocabulary(["provider_permission", "access_level", "personal_authorization_required"]);
|
|
246
|
+
var CEAL_RETRY_AFTER_MAX_MS = 60 * 60 * 1e3;
|
|
144
247
|
var CEAL_JSON_SCHEMA_TYPES = vocabulary(["object", "array", "string", "integer", "number", "boolean", "null"]);
|
|
145
248
|
var OBSERVATION_POSTURES = vocabulary(["direct_readback", "approved_no_readback", "operator_mediated", "excluded"]);
|
|
146
249
|
var EFFECT_DOMAINS = vocabulary(["none", "gateway_state", "provider", "external_world"]);
|
|
@@ -176,27 +279,6 @@ var CEAL_OPERATION_APPROVAL_DISPOSITIONS = vocabulary([
|
|
|
176
279
|
"interactive_unavailable"
|
|
177
280
|
]);
|
|
178
281
|
|
|
179
|
-
// packages/ceal-client-protocol/src/gateway-response-types.ts
|
|
180
|
-
var CEAL_CAPABILITY_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
|
|
181
|
-
var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
|
|
182
|
-
var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
|
|
183
|
-
var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
|
|
184
|
-
var CEAL_GATEWAY_READ_REUSE_DISPOSITIONS = vocabulary(["response_cache_hit", "corpus_hit", "corpus_hit_after_revalidation", "corpus_partial_provider_supplement", "provider_direct", "provider_continuation", "reuse_unavailable"]);
|
|
185
|
-
var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
|
|
186
|
-
var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
|
|
187
|
-
var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
|
|
188
|
-
var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
|
|
189
|
-
var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "agent_lease_admission", "provider_authenticated_event"]);
|
|
190
|
-
var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
|
|
191
|
-
var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
|
|
192
|
-
var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
|
|
193
|
-
var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
|
|
194
|
-
var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
|
|
195
|
-
var CEAL_PROTOCOL_VERSION = "1.4.0";
|
|
196
|
-
var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
|
|
197
|
-
var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
|
|
198
|
-
var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
|
|
199
|
-
|
|
200
282
|
// packages/ceal-client-protocol/src/gateway-validation-primitives.ts
|
|
201
283
|
var CealProtocolValidationError = class extends Error {
|
|
202
284
|
name = "CealProtocolValidationError";
|
|
@@ -213,6 +295,7 @@ var SAFE_CONNECTOR_KIND = new RegExp(`^[a-z][a-z0-9-]{0,${MAX_SAFE_TOKEN_LENGTH
|
|
|
213
295
|
var FORBIDDEN_SECRET_KEY = /^(?:[a-z0-9_]*(?:token|secret|password|credential(?:s)?|private_?key)|api_?key|authorization|bearer|raw_?provider_?payload|provider_?payload)$/iu;
|
|
214
296
|
var FORBIDDEN_AUTHORITY_KEY = /^(?:actor_?ref|owner_?ref|registration_?ref|runner_?ref|auth_?decision|policy_?decision|host_?decision)$/iu;
|
|
215
297
|
var TEXT_ENCODER = new TextEncoder();
|
|
298
|
+
var CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS = 128;
|
|
216
299
|
var CEAL_WIRE_SAFE_TEXT_MAX_BYTES = 8 * 1024;
|
|
217
300
|
function assertSafeJsonValue(value, options, depth = 0, count = { value: 0 }) {
|
|
218
301
|
count.value += 1;
|
|
@@ -232,12 +315,12 @@ function assertSafeJsonString(value, options) {
|
|
|
232
315
|
if (byteLength(value) > CEAL_WIRE_SAFE_TEXT_MAX_BYTES || firstDisallowedControl(value, "multi_line") !== null) invalidByContext(options);
|
|
233
316
|
}
|
|
234
317
|
function assertSafeJsonArray(value, options, depth, count) {
|
|
235
|
-
if (value.length >
|
|
318
|
+
if (value.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
236
319
|
for (const item of value) assertSafeJsonValue(item, options, depth + 1, count);
|
|
237
320
|
}
|
|
238
321
|
function assertSafeJsonRecord(record6, options, depth, count) {
|
|
239
322
|
const entries = Object.entries(record6);
|
|
240
|
-
if (entries.length >
|
|
323
|
+
if (entries.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
241
324
|
const sourceUrlColumn = options.allowHttpsUrl ? compactSourceUrlColumn(record6.fields) : null;
|
|
242
325
|
for (const [key, child] of entries) {
|
|
243
326
|
if (key === "credential_material_included" && child !== false) invalidByContext(options);
|
|
@@ -263,9 +346,9 @@ function compactSourceUrlColumn(fields) {
|
|
|
263
346
|
return index < 0 ? null : index;
|
|
264
347
|
}
|
|
265
348
|
function assertSafeJsonCompactRows(value, sourceUrlColumn, options, depth, count) {
|
|
266
|
-
if (!Array.isArray(value) || value.length >
|
|
349
|
+
if (!Array.isArray(value) || value.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
267
350
|
for (const row of value) {
|
|
268
|
-
if (!Array.isArray(row) || row.length >
|
|
351
|
+
if (!Array.isArray(row) || row.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
269
352
|
for (const [index, cell] of row.entries()) {
|
|
270
353
|
if (index === sourceUrlColumn && (cell === null || isSafeExternalHttpsUrl(cell))) continue;
|
|
271
354
|
assertSafeJsonValue(cell, options, depth + 2, count);
|
|
@@ -298,6 +381,7 @@ function isSafeResultContent(value, key) {
|
|
|
298
381
|
const maximum = key === "text" ? 8192 : 1024;
|
|
299
382
|
return typeof value === "string" && byteLength(value) <= maximum && firstDisallowedControl(value, "multi_line") === null;
|
|
300
383
|
}
|
|
384
|
+
var CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES = 512;
|
|
301
385
|
function normalizeCealSingleLineText(value) {
|
|
302
386
|
return value.split("").map((character) => hasControlCharacter(character) ? " " : character).join("").trim();
|
|
303
387
|
}
|
|
@@ -406,6 +490,28 @@ function cut(text) {
|
|
|
406
490
|
return text.length <= REFUSAL_VALUE_MAX_CHARS ? text : `${text.slice(0, REFUSAL_VALUE_MAX_CHARS)}...(+${text.length - REFUSAL_VALUE_MAX_CHARS} chars)`;
|
|
407
491
|
}
|
|
408
492
|
|
|
493
|
+
// packages/ceal-client-protocol/src/operation-access-class.ts
|
|
494
|
+
var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
|
|
495
|
+
|
|
496
|
+
// packages/ceal-client-protocol/src/domain-vocabulary.ts
|
|
497
|
+
var CEAL_INTEGRATION_KINDS = vocabulary(["github", "google-workspace", "notion", "slack"]);
|
|
498
|
+
var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
|
|
499
|
+
var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
|
|
500
|
+
var CEAL_PROVIDER_BOUNDARY_KINDS = vocabulary([
|
|
501
|
+
"github_account",
|
|
502
|
+
"github_organization",
|
|
503
|
+
"google_workspace_domain",
|
|
504
|
+
"notion_workspace",
|
|
505
|
+
"slack_workspace"
|
|
506
|
+
]);
|
|
507
|
+
var CEAL_PROVIDER_BOUNDARY_EVIDENCE = vocabulary(["provider_readback", "operator_stated"]);
|
|
508
|
+
var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
|
|
509
|
+
var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
|
|
510
|
+
var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
|
|
511
|
+
var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
|
|
512
|
+
var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
|
|
513
|
+
var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
|
|
514
|
+
|
|
409
515
|
// packages/ceal-client-protocol/src/safe-json-budget.ts
|
|
410
516
|
var SAFE_JSON_MIN_BYTES_PER_NODE = 4;
|
|
411
517
|
function safeJsonNodeBudgetForBytes(maxBytes) {
|
|
@@ -435,7 +541,9 @@ var CEAL_OPERATION_JSON_SCHEMA_BUDGET = Object.freeze({
|
|
|
435
541
|
});
|
|
436
542
|
|
|
437
543
|
// packages/ceal-client-protocol/src/operation-validation-primitives.ts
|
|
438
|
-
var ISO_TIMESTAMP =
|
|
544
|
+
var ISO_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?([Zz]|([+-])(\d{2}):(\d{2}))$/u;
|
|
545
|
+
var CEAL_CALENDAR_DATE_PATTERN_SOURCE = "^\\d{4}-\\d{2}-\\d{2}$";
|
|
546
|
+
var CEAL_CALENDAR_DATE = new RegExp(CEAL_CALENDAR_DATE_PATTERN_SOURCE, "u");
|
|
439
547
|
function isOperationRecord(value) {
|
|
440
548
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
441
549
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -447,8 +555,56 @@ function requireOperationRecord(value, message) {
|
|
|
447
555
|
function requireClosedValue(value, members, message) {
|
|
448
556
|
if (typeof value !== "string" || !members.includes(value)) throw new TypeError(message);
|
|
449
557
|
}
|
|
558
|
+
function isCealIsoTimestamp(value) {
|
|
559
|
+
return typeof value === "string" && parseCealIsoTimestamp(value) !== null;
|
|
560
|
+
}
|
|
561
|
+
function parseCealIsoTimestamp(value) {
|
|
562
|
+
const fields = parseCealIsoTimestampFields(value);
|
|
563
|
+
if (fields === null || !validCalendarDate(fields) || !validClockTime(fields) || !validUtcOffset(fields)) return null;
|
|
564
|
+
const epochMilliseconds = cealFieldsEpochMilliseconds(fields);
|
|
565
|
+
if (!Number.isFinite(epochMilliseconds)) return null;
|
|
566
|
+
return {
|
|
567
|
+
epochNanoseconds: BigInt(epochMilliseconds) * 1000000n + BigInt(fields.fraction.padEnd(9, "0") || "0")
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function parseCealIsoTimestampFields(value) {
|
|
571
|
+
const match = ISO_TIMESTAMP.exec(value);
|
|
572
|
+
if (match === null) return null;
|
|
573
|
+
return {
|
|
574
|
+
year: Number(match[1]),
|
|
575
|
+
month: Number(match[2]),
|
|
576
|
+
day: Number(match[3]),
|
|
577
|
+
hour: Number(match[4]),
|
|
578
|
+
minute: Number(match[5]),
|
|
579
|
+
second: Number(match[6]),
|
|
580
|
+
fraction: match[7] ?? "",
|
|
581
|
+
offsetSign: match[9] === void 0 ? 0 : match[9] === "+" ? 1 : -1,
|
|
582
|
+
offsetHour: Number(match[10] ?? 0),
|
|
583
|
+
offsetMinute: Number(match[11] ?? 0)
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
function validCalendarDate({ year, month, day }) {
|
|
587
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth(year, month);
|
|
588
|
+
}
|
|
589
|
+
function validClockTime({ hour, minute, second }) {
|
|
590
|
+
return hour <= 23 && minute <= 59 && second <= 59;
|
|
591
|
+
}
|
|
592
|
+
function validUtcOffset({ offsetHour, offsetMinute }) {
|
|
593
|
+
return offsetHour <= 23 && offsetMinute <= 59;
|
|
594
|
+
}
|
|
595
|
+
function cealFieldsEpochMilliseconds(fields) {
|
|
596
|
+
const shiftedYear = fields.year < 100 ? fields.year + 400 : fields.year;
|
|
597
|
+
const cycleMilliseconds = fields.year < 100 ? (400 * 365 + 97) * 24 * 60 * 60 * 1e3 : 0;
|
|
598
|
+
const localMilliseconds = Date.UTC(shiftedYear, fields.month - 1, fields.day, fields.hour, fields.minute, fields.second, 0) - cycleMilliseconds;
|
|
599
|
+
const offsetMilliseconds = fields.offsetSign * (fields.offsetHour * 60 + fields.offsetMinute) * 6e4;
|
|
600
|
+
return localMilliseconds - offsetMilliseconds;
|
|
601
|
+
}
|
|
602
|
+
function daysInMonth(year, month) {
|
|
603
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
604
|
+
return [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1] ?? 0;
|
|
605
|
+
}
|
|
450
606
|
function requireIsoTimestamp(value, message) {
|
|
451
|
-
if (
|
|
607
|
+
if (!isCealIsoTimestamp(value)) throw new TypeError(message);
|
|
452
608
|
}
|
|
453
609
|
|
|
454
610
|
// packages/ceal-client-protocol/src/operation-json-schema.ts
|
|
@@ -470,7 +626,7 @@ var CLIENT_AUDIENCE_SET = new Set(CLIENT_AUDIENCES);
|
|
|
470
626
|
// packages/ceal-client-protocol/src/schema-literals.ts
|
|
471
627
|
var CEAL_SCHEMA_LITERALS = [
|
|
472
628
|
"ceal.activation_transport.v1",
|
|
473
|
-
|
|
629
|
+
CEAL_OPERATION_ACCESS_SCHEMA_VERSION,
|
|
474
630
|
CEAL_OPERATION_CATALOG_SCHEMA_VERSION,
|
|
475
631
|
"ceal.client_refresh_request.v2",
|
|
476
632
|
"ceal.client_refresh_result.v2",
|
|
@@ -493,7 +649,6 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
493
649
|
"ceal.enrollment_exchange.v1",
|
|
494
650
|
"ceal.enrollment_result.v1",
|
|
495
651
|
"ceal.first_activation_causal_join.v1",
|
|
496
|
-
"ceal.gateway_aggregate_authorization_snapshot.v2",
|
|
497
652
|
"ceal.gateway_announcement_policy.v1",
|
|
498
653
|
"ceal.gateway_audit_call_detail.v1",
|
|
499
654
|
"ceal.gateway_audit_event.v1",
|
|
@@ -501,7 +656,7 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
501
656
|
"ceal.gateway_authorization_snapshot.v1",
|
|
502
657
|
"ceal.gateway_cache_origin.v1",
|
|
503
658
|
"ceal.gateway_call_result.v1",
|
|
504
|
-
|
|
659
|
+
CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION,
|
|
505
660
|
"ceal.gateway_connector_route_failure.v1",
|
|
506
661
|
"ceal.gateway_discovery.v3",
|
|
507
662
|
"ceal.gateway_handshake.v1",
|
|
@@ -536,7 +691,7 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
536
691
|
"ceal.operation_receipt.v1",
|
|
537
692
|
"ceal.operation_result.v1",
|
|
538
693
|
"ceal.operation_result.compact.v1",
|
|
539
|
-
"ceal.local_result_materialization.
|
|
694
|
+
"ceal.local_result_materialization.v2",
|
|
540
695
|
"ceal.local_result_root.v1",
|
|
541
696
|
"ceal.owner_bootstrap_exchange_request.v1",
|
|
542
697
|
"ceal.owner_bootstrap_exchange_result.v1",
|
|
@@ -546,7 +701,8 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
546
701
|
"ceal.request_cancellation.v1",
|
|
547
702
|
"ceal.request_graph_revision.v1",
|
|
548
703
|
"ceal.request_occurrence_correlation.v1",
|
|
549
|
-
"ceal.request_projection.v1"
|
|
704
|
+
"ceal.request_projection.v1",
|
|
705
|
+
"ceal.write_safety_finding.v1"
|
|
550
706
|
];
|
|
551
707
|
var CEAL_SCHEMA_LITERAL_SET = new Set(CEAL_SCHEMA_LITERALS);
|
|
552
708
|
|
|
@@ -561,21 +717,14 @@ var CEAL_BOUNDED_SERIES_CATALOG = Object.freeze({
|
|
|
561
717
|
thread_run_events: (root) => join(root, "run-events.jsonl"),
|
|
562
718
|
bounded_store_sidecar: (root) => `${root}.jsonl`,
|
|
563
719
|
gateway_audit_archive: (root) => `${root}.jsonl`,
|
|
564
|
-
gateway_capability_audit: (root) => join(root, "capability-audit.jsonl"),
|
|
565
|
-
gateway_retrieval_waste_partition: (root) => `${root}.jsonl`,
|
|
566
|
-
gateway_access_policy_publication: (root) => join(root, "access-policy-publications.jsonl"),
|
|
567
720
|
gateway_access_group_connector_control_audit: (root) => `${root}.audit.jsonl`,
|
|
568
721
|
gateway_access_group_connector_scope_audit: (root) => join(root, "profile-connector-scope-audit.jsonl"),
|
|
569
722
|
gateway_access_group_connector_activity_scope_audit: (root) => join(root, "profile-connector-activity-scope-audit.jsonl"),
|
|
723
|
+
gateway_access_group_connector_calendar_scope_audit: (root) => join(root, "profile-connector-calendar-scope-audit.jsonl"),
|
|
570
724
|
gateway_access_group_connector_readiness_audit: (root) => join(root, "profile-connector-readiness.audit.jsonl"),
|
|
571
725
|
gateway_organization_audit_ledger: (root) => join(root, "organization-ledger.jsonl"),
|
|
572
726
|
gateway_personal_client_audit: (root) => join(root, "audit.jsonl"),
|
|
573
|
-
gateway_personal_client_access_audit: (root) => join(root, "access-audit.jsonl")
|
|
574
|
-
gateway_admin_enrollment: (root) => join(root, "enrollments.jsonl"),
|
|
575
|
-
gateway_admin_login: (root) => join(root, "logins.jsonl"),
|
|
576
|
-
gateway_admin_family: (root) => join(root, "families.jsonl"),
|
|
577
|
-
gateway_admin_refresh_token: (root) => join(root, "refresh-tokens.jsonl"),
|
|
578
|
-
gateway_admin_access_token: (root) => join(root, "access-tokens.jsonl")
|
|
727
|
+
gateway_personal_client_access_audit: (root) => join(root, "access-audit.jsonl")
|
|
579
728
|
});
|
|
580
729
|
|
|
581
730
|
// packages/ceal-client-protocol/src/gateway-installation-activation.ts
|
|
@@ -583,8 +732,21 @@ var CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_DATA_CHARACTERS = 32;
|
|
|
583
732
|
var CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_CHECK_CHARACTERS = 8;
|
|
584
733
|
var CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_COMPACT_CHARACTERS = CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_DATA_CHARACTERS + CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_CHECK_CHARACTERS;
|
|
585
734
|
|
|
735
|
+
// packages/ceal-client-protocol/src/gateway-entrypoint-commands.ts
|
|
736
|
+
var GATEWAY_ENTRYPOINT_COMMANDS = Object.freeze([
|
|
737
|
+
Object.freeze({ id: "controlPlane", path: Object.freeze(["control-plane"]) }),
|
|
738
|
+
Object.freeze({ id: "originShow", path: Object.freeze(["origin", "show"]) }),
|
|
739
|
+
Object.freeze({ id: "credentialsImport", path: Object.freeze(["credentials", "import"]) }),
|
|
740
|
+
Object.freeze({ id: "initialize", path: Object.freeze(["init"]) })
|
|
741
|
+
]);
|
|
742
|
+
|
|
586
743
|
// packages/ceal-client-protocol/src/host-registration.ts
|
|
587
744
|
var CEAL_HOST_REGISTRATION_RESULT_KEYS = Object.freeze(["credential_generation", "host_access_token", "host_installation_ref", "host_source_ref", "ok", "registration_attempt_ref", "schema_version", "status"]);
|
|
745
|
+
var CEAL_LOWERCASE_UUID_PATTERN = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
746
|
+
var CEAL_HOST_REGISTRATION_ATTEMPT_REF_PATTERN = new RegExp(`^host-registration-attempt:${CEAL_LOWERCASE_UUID_PATTERN}$`, "u");
|
|
747
|
+
var CEAL_HOST_INSTALLATION_REF_PATTERN = new RegExp(`^host-installation:${CEAL_LOWERCASE_UUID_PATTERN}$`, "u");
|
|
748
|
+
var CEAL_HOST_SOURCE_REF_PATTERN = new RegExp(`^host-source:${CEAL_LOWERCASE_UUID_PATTERN}$`, "u");
|
|
749
|
+
var CEAL_HOST_ACCESS_TOKEN_PATTERN = /^ceal_host_[A-Za-z0-9_-]{43}$/u;
|
|
588
750
|
|
|
589
751
|
// packages/ceal-client-protocol/src/agent-session-handoff.ts
|
|
590
752
|
var CEAL_AGENT_ADMISSION_PHASES = vocabulary([
|
|
@@ -616,12 +778,19 @@ var CEAL_AGENT_SERVICE_SESSION_RECORD_KEYS = vocabulary([
|
|
|
616
778
|
var CEAL_AGENT_SESSION_COMMIT_KEYS = vocabulary(["attempt_ref", "expected_session_revision", "session_revision"]);
|
|
617
779
|
|
|
618
780
|
// packages/ceal-client-protocol/src/operation-next-actions.ts
|
|
781
|
+
var CEAL_REPEAT_REQUEST_NEXT_ACTION = "repeat_request";
|
|
782
|
+
var CEAL_INSPECT_RECEIPT_NEXT_ACTION = "inspect_receipt";
|
|
619
783
|
var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
620
784
|
"select_did_you_mean",
|
|
621
785
|
"choose_new_output_path",
|
|
622
786
|
"choose_new_idempotency_key",
|
|
623
787
|
"restart_search",
|
|
624
788
|
"repeat_original_arguments",
|
|
789
|
+
// The occurrence this call addressed is settled and cannot move again -- a
|
|
790
|
+
// denied effect approval, an expired continuation -- so the caller runs the
|
|
791
|
+
// same call again as a new request. The refusal names the re-request; this
|
|
792
|
+
// word is the re-request.
|
|
793
|
+
CEAL_REPEAT_REQUEST_NEXT_ACTION,
|
|
625
794
|
"choose_new_request_identity",
|
|
626
795
|
// A refusal that names the argument (invalid_argument with `property`) tells the caller exactly what to change.
|
|
627
796
|
"correct_argument",
|
|
@@ -640,10 +809,35 @@ var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
|
640
809
|
"use_original_input",
|
|
641
810
|
// The same mismatch on the Request binding rather than the input: the
|
|
642
811
|
// occurrence was admitted under one binding and the replay presents another.
|
|
643
|
-
"use_original_request_binding"
|
|
812
|
+
"use_original_request_binding",
|
|
813
|
+
// The client's own filesystem write was refused -- EACCES, EPERM, or a
|
|
814
|
+
// read-only mount -- which is how an agent sandbox fences writes outside its
|
|
815
|
+
// workspace. The caller reruns the same command with unsandboxed or escalated
|
|
816
|
+
// execution; re-adopting the session cannot help, because the sandbox refuses
|
|
817
|
+
// that write too.
|
|
818
|
+
"rerun_outside_agent_sandbox",
|
|
819
|
+
// The refusal names the one Operation to read next in `describe_command`:
|
|
820
|
+
// an absent Operation id with a sole catalog-ranked candidate, or a known
|
|
821
|
+
// Operation invoked without its input. Describing only reads the verified
|
|
822
|
+
// contract and dispatches nothing, and the caller then retries with the
|
|
823
|
+
// named Operation or input, so it is guidance the scorer counts.
|
|
824
|
+
"describe_operation",
|
|
825
|
+
// The refusal names the caller's own personal connect Operation in
|
|
826
|
+
// `required_action_sequence`: the Operation asked for, or the credential
|
|
827
|
+
// the call ran on, needs a personal connection the caller does not hold
|
|
828
|
+
// (never connected, revoked, or expired). The caller runs that exact
|
|
829
|
+
// Operation, completes the consent it hands back, and retries the same call.
|
|
830
|
+
"connect_personal_account",
|
|
831
|
+
// The provider or Gateway named a bounded wait; the caller waits that long
|
|
832
|
+
// and submits the same read again.
|
|
833
|
+
"retry_after"
|
|
644
834
|
];
|
|
645
835
|
var CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS = Object.freeze(CALLER_RECOVERABLE_NEXT_ACTION_VALUES);
|
|
646
836
|
var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
837
|
+
// Only an administrator can take the named step -- approving a place for an
|
|
838
|
+
// Operation, granting a withheld capability -- so a non-administrator asks
|
|
839
|
+
// one. The refusal names the administrator step; this word is the ask.
|
|
840
|
+
"ask_admin",
|
|
647
841
|
"await_pending_approval",
|
|
648
842
|
"check_gateway",
|
|
649
843
|
"inspect_context_store",
|
|
@@ -658,9 +852,13 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
|
658
852
|
"inspect_local_command",
|
|
659
853
|
"inspect_local_output",
|
|
660
854
|
"inspect_local_route",
|
|
661
|
-
|
|
855
|
+
CEAL_INSPECT_RECEIPT_NEXT_ACTION,
|
|
662
856
|
"inspect_request",
|
|
663
857
|
"none",
|
|
858
|
+
// The provider refused because of its own configuration -- an API not
|
|
859
|
+
// enabled, an app scope missing -- which an operator changes in that
|
|
860
|
+
// provider's console. No caller edit and no retry fixes it.
|
|
861
|
+
"operator_action",
|
|
664
862
|
"recover",
|
|
665
863
|
"reconcile",
|
|
666
864
|
// Reconciliation of one named occurrence, not of the deployment: a legacy
|
|
@@ -668,6 +866,10 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
|
668
866
|
// no edit to the call changes that. `reconcile` is the deployment-wide word.
|
|
669
867
|
"reconcile_occurrence",
|
|
670
868
|
"reduce_request_size",
|
|
869
|
+
// The Host's invocation context for this turn is stale or expired at the
|
|
870
|
+
// Gateway. The Host's next turn rebinds it and the same call then succeeds;
|
|
871
|
+
// nothing in the call changes, so it is not a remedy the scorer may count.
|
|
872
|
+
"refresh_host_invocation_context",
|
|
671
873
|
"repair_or_adopt_session",
|
|
672
874
|
"retry",
|
|
673
875
|
// The owner -- a correlation hook, an approval hook -- is not serving right
|
|
@@ -681,10 +883,23 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
|
681
883
|
var CEAL_NON_REMEDY_NEXT_ACTIONS = Object.freeze(NON_REMEDY_NEXT_ACTION_VALUES);
|
|
682
884
|
var DECLARED = /* @__PURE__ */ new Set([...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS, ...CEAL_NON_REMEDY_NEXT_ACTIONS]);
|
|
683
885
|
var RECOVERABLE = new Set(CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS);
|
|
886
|
+
var CEAL_OPERATION_WITHHELD_NEXT_ACTIONS = Object.freeze({
|
|
887
|
+
provider_permission: "ask_admin",
|
|
888
|
+
access_level: "ask_admin",
|
|
889
|
+
personal_authorization_required: "connect_personal_account"
|
|
890
|
+
});
|
|
684
891
|
|
|
685
892
|
// packages/ceal-client-protocol/src/operation-contract-artifact.ts
|
|
686
893
|
var CATALOG_REVISION = new RegExp(CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_PATTERN, "u");
|
|
687
894
|
var SHA256 = new RegExp(CEAL_VERIFIED_ARTIFACT_DIGEST_PATTERN, "u");
|
|
895
|
+
var CEAL_OPERATION_INVOKE_COMMAND = "invoke";
|
|
896
|
+
var CEAL_OPERATION_INPUT_FLAG = "--input";
|
|
897
|
+
var CEAL_OPERATION_ID_PLACEHOLDER = "<operation-id>";
|
|
898
|
+
var CEAL_OPERATION_INPUT_PLACEHOLDER = "<json-object>";
|
|
899
|
+
var CEAL_MODEL_INVOCATION_ARGUMENTS = [CEAL_OPERATION_INVOKE_COMMAND, CEAL_OPERATION_ID_PLACEHOLDER, CEAL_OPERATION_INPUT_FLAG, shellSingleQuoted(CEAL_OPERATION_INPUT_PLACEHOLDER)].join(" ");
|
|
900
|
+
function shellSingleQuoted(value) {
|
|
901
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
902
|
+
}
|
|
688
903
|
var CEAL_OPERATION_CONTRACT_REQUIRED_FIELDS = Object.freeze(["input_schema", "result_schema"]);
|
|
689
904
|
var CEAL_OPERATION_CONTRACT_OPTIONAL_FIELDS = Object.freeze(["summary", "input_resolution"]);
|
|
690
905
|
var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
|
|
@@ -713,9 +928,178 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
|
|
|
713
928
|
}
|
|
714
929
|
});
|
|
715
930
|
|
|
931
|
+
// packages/ceal-client-protocol/src/bare-document.ts
|
|
932
|
+
var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_fields;
|
|
933
|
+
var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
|
|
934
|
+
|
|
935
|
+
// packages/ceal-client-protocol/src/leased-consumer-vocabulary.ts
|
|
936
|
+
var CEAL_TERMINAL_DISPOSITIONS = vocabulary(["completed", "failed", "cancelled"]);
|
|
937
|
+
var CEAL_LEASE_DISPOSITIONS = vocabulary([...CEAL_TERMINAL_DISPOSITIONS, "deferred"]);
|
|
938
|
+
var CEAL_AUTOMATIC_FEEDBACK_INTENTS = vocabulary(["initial"]);
|
|
939
|
+
var CEAL_LEASED_MESSAGE_DELIVERY_OPERATION_IDS = vocabulary(["message.send", "message.update", "message.delete"]);
|
|
940
|
+
var CEAL_REPOSITORY_VISIBILITIES = vocabulary(["public", "private", "internal"]);
|
|
941
|
+
var CEAL_RESULT_DELIVERY_OFFERS = vocabulary(["pending", "offered", "transport_lost"]);
|
|
942
|
+
var CEAL_RESULT_DELIVERY_STATES = vocabulary(["unavailable", ...CEAL_RESULT_DELIVERY_OFFERS]);
|
|
943
|
+
var CEAL_PROVIDER_OUTCOMES = vocabulary(["not_attempted", "outcome_unknown", "verified"]);
|
|
944
|
+
var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
945
|
+
"provider_page_budget",
|
|
946
|
+
"call_deadline",
|
|
947
|
+
"related_item_budget",
|
|
948
|
+
"provider_cursor_unavailable",
|
|
949
|
+
"malformed_provider_page",
|
|
950
|
+
"visibility_incomplete",
|
|
951
|
+
"unsupported_event_kind"
|
|
952
|
+
]);
|
|
953
|
+
var CEAL_ACTIVITY_CONNECTOR_KINDS = vocabulary(["slack", "github", "google-calendar", "google-drive", "notion"]);
|
|
954
|
+
var CEAL_ACTIVITY_COVERAGE_STATUSES = vocabulary(["complete_for_target", "continuation_required", "blocked"]);
|
|
955
|
+
var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
956
|
+
"slack.message",
|
|
957
|
+
"slack.reply",
|
|
958
|
+
"slack.direct_message",
|
|
959
|
+
"github.commit.authored",
|
|
960
|
+
"github.issue.opened",
|
|
961
|
+
"github.pull_request.opened",
|
|
962
|
+
"github.review.submitted",
|
|
963
|
+
"github.issue_comment.created",
|
|
964
|
+
"github.review_comment.created",
|
|
965
|
+
"calendar.organized",
|
|
966
|
+
"calendar.accepted_invite",
|
|
967
|
+
"calendar.recorded",
|
|
968
|
+
"drive.file.created",
|
|
969
|
+
"drive.file.modified",
|
|
970
|
+
"drive.file.viewed",
|
|
971
|
+
"notion.page.created",
|
|
972
|
+
"notion.page.edited"
|
|
973
|
+
]);
|
|
974
|
+
var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
975
|
+
"message_created",
|
|
976
|
+
"commit_authored",
|
|
977
|
+
"resource_created",
|
|
978
|
+
"review_submitted",
|
|
979
|
+
"comment_created",
|
|
980
|
+
"event_start",
|
|
981
|
+
"all_day_start",
|
|
982
|
+
"file_created",
|
|
983
|
+
"file_modified",
|
|
984
|
+
"file_modified_by_me",
|
|
985
|
+
"file_viewed_by_me",
|
|
986
|
+
"page_created",
|
|
987
|
+
"page_last_edited",
|
|
988
|
+
"resource_modified"
|
|
989
|
+
]);
|
|
990
|
+
var CEAL_ACTIVITY_V3_KINDS = vocabulary(["authored", "edited", "messaged", "responded"]);
|
|
991
|
+
var CEAL_RESOURCE_TIME_BASES = vocabulary(["created", "modified", "viewed"]);
|
|
992
|
+
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
993
|
+
var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
|
|
994
|
+
var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "kinds", "container_ref", "addressed_to_me", "unread"]);
|
|
995
|
+
var CEAL_ARTIFACT_STAGE_TERMINALS = vocabulary(["chunk_accepted", "artifact_ready", "idempotency_replayed"]);
|
|
996
|
+
var CEAL_READ_ITEM_KINDS = vocabulary(["conversation", "identity", "usergroup", "message", "file", "document"]);
|
|
997
|
+
var CEAL_LEASED_MESSAGE_SEARCH_COMPLETENESS = vocabulary(["complete", "continuation_available", "truncated"]);
|
|
998
|
+
var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "object"]);
|
|
999
|
+
var CEAL_WRITE_OBJECT_KINDS = vocabulary(["document", "comment", "cell_range"]);
|
|
1000
|
+
var CEAL_CONVERSATION_KINDS = vocabulary(["channel", "dm", "group"]);
|
|
1001
|
+
var CEAL_FILE_TYPE_FAMILIES = vocabulary(["all", "image", "pdf"]);
|
|
1002
|
+
var CEAL_GITHUB_ISSUE_STATES = vocabulary(["open", "closed", "all"]);
|
|
1003
|
+
var CEAL_NOTION_UPDATABLE_BLOCK_TYPES = vocabulary([
|
|
1004
|
+
"paragraph",
|
|
1005
|
+
"heading_1",
|
|
1006
|
+
"heading_2",
|
|
1007
|
+
"heading_3",
|
|
1008
|
+
"bulleted_list_item",
|
|
1009
|
+
"numbered_list_item",
|
|
1010
|
+
"to_do",
|
|
1011
|
+
"code"
|
|
1012
|
+
]);
|
|
1013
|
+
var CEAL_NOTION_NEUTRAL_TYPES = vocabulary([
|
|
1014
|
+
"text",
|
|
1015
|
+
"number",
|
|
1016
|
+
"boolean",
|
|
1017
|
+
"date",
|
|
1018
|
+
"select",
|
|
1019
|
+
"multi_select",
|
|
1020
|
+
"people",
|
|
1021
|
+
"url",
|
|
1022
|
+
"email",
|
|
1023
|
+
"phone",
|
|
1024
|
+
"unsupported"
|
|
1025
|
+
]);
|
|
1026
|
+
var CEAL_NOTION_IDENTITY_GAP_REASONS = vocabulary([
|
|
1027
|
+
"identity_unavailable",
|
|
1028
|
+
"identity_stale",
|
|
1029
|
+
"identity_ambiguous",
|
|
1030
|
+
"identity_revoked",
|
|
1031
|
+
"identity_unlinked"
|
|
1032
|
+
]);
|
|
1033
|
+
var CEAL_NOTION_STRING_VALUE_TYPES = vocabulary(["text", "select", "url", "email", "phone"]);
|
|
1034
|
+
var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "unsupported", "download_failed", "permission_denied"]);
|
|
1035
|
+
var CEAL_PLAN_ITEM_STATUSES = vocabulary(["pending", "active", "completed"]);
|
|
1036
|
+
var CEAL_FINAL_PRESENTATION_INTENTS = vocabulary(["final", "stop", "transient_notice"]);
|
|
1037
|
+
var CEAL_PRESENTATION_INTENTS = vocabulary(["progress", ...CEAL_FINAL_PRESENTATION_INTENTS]);
|
|
1038
|
+
|
|
1039
|
+
// packages/ceal-client-protocol/src/resource-row.ts
|
|
1040
|
+
var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
1041
|
+
schema_version: { required: true, timestamp: false },
|
|
1042
|
+
ref: { required: true, timestamp: false },
|
|
1043
|
+
next: { required: true, timestamp: false },
|
|
1044
|
+
kind: { required: true, timestamp: false },
|
|
1045
|
+
title: { required: true, timestamp: false },
|
|
1046
|
+
container_label: { required: true, timestamp: false },
|
|
1047
|
+
container_ref: { required: false, timestamp: false },
|
|
1048
|
+
source: { required: true, timestamp: false },
|
|
1049
|
+
owner: { required: false, timestamp: false },
|
|
1050
|
+
counterparts: { required: false, timestamp: false },
|
|
1051
|
+
created_at: { required: false, timestamp: true },
|
|
1052
|
+
created_at_local: { required: false, timestamp: true },
|
|
1053
|
+
modified_at: { required: false, timestamp: true },
|
|
1054
|
+
modified_at_local: { required: false, timestamp: true },
|
|
1055
|
+
viewed_by_me_at: { required: false, timestamp: true },
|
|
1056
|
+
visibility: { required: false, timestamp: false },
|
|
1057
|
+
start: { required: false, timestamp: false },
|
|
1058
|
+
end: { required: false, timestamp: false },
|
|
1059
|
+
status: { required: false, timestamp: false },
|
|
1060
|
+
snippet: { required: false, timestamp: false },
|
|
1061
|
+
state: { required: true, timestamp: false },
|
|
1062
|
+
timestamp_basis: { required: true, timestamp: false }
|
|
1063
|
+
});
|
|
1064
|
+
var RESOURCE_ROW_REQUIRED_KEYS = Object.entries(RESOURCE_ROW_KEY_RULES).flatMap(([key, rule]) => rule.required ? [key] : []);
|
|
1065
|
+
var RESOURCE_ROW_KEYS = new Set(Object.keys(RESOURCE_ROW_KEY_RULES));
|
|
1066
|
+
var RESOURCE_ROW_TIMESTAMP_KEYS = Object.entries(RESOURCE_ROW_KEY_RULES).flatMap(([key, rule]) => rule.timestamp ? [key] : []);
|
|
1067
|
+
|
|
1068
|
+
// packages/ceal-client-protocol/src/resource-read-result.ts
|
|
1069
|
+
var CEAL_RESOURCE_GET_COVERAGE_NEXT_ACTIONS = vocabulary(["reduce_request_size", "read_resource_again"]);
|
|
1070
|
+
|
|
1071
|
+
// packages/ceal-client-protocol/src/excluded-target-coverage-contract.ts
|
|
1072
|
+
var CEAL_EXCLUDED_TARGET_REASONS = vocabulary(["audience_narrower_than_place", "visibility_unknown"]);
|
|
1073
|
+
|
|
1074
|
+
// packages/ceal-client-protocol/src/excluded-target-coverage.ts
|
|
1075
|
+
var CEAL_EXCLUDED_TARGET_COVERAGE_MAX_ENTRIES = 64;
|
|
1076
|
+
function cealExcludedTargetCoverageJsonSchema() {
|
|
1077
|
+
const target = {
|
|
1078
|
+
target_ref: { type: "string", minLength: 8, maxLength: 134, pattern: "^target:[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$" },
|
|
1079
|
+
connector_kind: { type: "string", minLength: 1, maxLength: 64, pattern: "^[a-z][a-z0-9-]{0,63}$" },
|
|
1080
|
+
label: { type: "string", minLength: 1, maxLength: CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES },
|
|
1081
|
+
reason: { type: "string", enum: [...CEAL_EXCLUDED_TARGET_REASONS] }
|
|
1082
|
+
};
|
|
1083
|
+
return {
|
|
1084
|
+
type: "array",
|
|
1085
|
+
minItems: 1,
|
|
1086
|
+
maxItems: CEAL_EXCLUDED_TARGET_COVERAGE_MAX_ENTRIES,
|
|
1087
|
+
items: { type: "object", additionalProperties: false, maxProperties: 4, properties: target, required: ["target_ref", "reason"] }
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// packages/ceal-client-protocol/src/write-safety-finding.ts
|
|
1092
|
+
var CEAL_WRITE_SAFETY_FINDING_STATUSES = vocabulary(["warning", "unchecked"]);
|
|
1093
|
+
var CEAL_WRITE_SAFETY_AUDIENCES = vocabulary(["workspace", "person", "members", "unknown"]);
|
|
1094
|
+
var CEAL_WRITE_SAFETY_REFERENCE_REASONS = vocabulary(["not_published_to_audience", "audience_access_unknown"]);
|
|
1095
|
+
var CEAL_WRITE_SAFETY_UNCHECKED_REASONS = vocabulary(["authority_unavailable"]);
|
|
1096
|
+
|
|
716
1097
|
// packages/ceal-client-protocol/src/operation-occurrence.ts
|
|
717
1098
|
var MAX_RECEIPT_REVISION = Number.MAX_SAFE_INTEGER - 1;
|
|
718
1099
|
|
|
1100
|
+
// packages/ceal-client-protocol/src/leased-artifact-contract.ts
|
|
1101
|
+
var CEAL_LEASED_CONSUMER_ARTIFACT_CHUNK_MAX_BYTES = 12 * 1024;
|
|
1102
|
+
|
|
719
1103
|
// packages/ceal-client-protocol/src/leased-resource-stream.ts
|
|
720
1104
|
var CEAL_LEASED_CONSUMER_ATTACHMENT_STREAM_MAGIC = new Uint8Array([67, 69, 65, 76, 82, 83, 50, 0]);
|
|
721
1105
|
var CEAL_LEASED_CONSUMER_ATTACHMENT_STREAM_RECORD_PREFIX_BYTES = 8;
|
|
@@ -727,6 +1111,7 @@ var CEAL_ATTACHMENT_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_
|
|
|
727
1111
|
// packages/ceal-client-protocol/src/request-validation-primitives.ts
|
|
728
1112
|
import { createHash } from "node:crypto";
|
|
729
1113
|
var DIGEST = /^[a-f0-9]{64}$/u;
|
|
1114
|
+
var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
|
|
730
1115
|
function isCealDigest(value) {
|
|
731
1116
|
return typeof value === "string" && DIGEST.test(value);
|
|
732
1117
|
}
|
|
@@ -753,7 +1138,7 @@ var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
|
|
|
753
1138
|
var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority", "prompt_ref", "host_session_ref", "host_turn_ref", "host_source_ref"]);
|
|
754
1139
|
var CEAL_REQUEST_JSON_SCHEMA = {
|
|
755
1140
|
type: "object",
|
|
756
|
-
maxProperties:
|
|
1141
|
+
maxProperties: 10,
|
|
757
1142
|
additionalProperties: false,
|
|
758
1143
|
properties: {
|
|
759
1144
|
schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
|
|
@@ -776,7 +1161,8 @@ var CEAL_REQUEST_JSON_SCHEMA = {
|
|
|
776
1161
|
host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
777
1162
|
host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
778
1163
|
host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
779
|
-
host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
1164
|
+
host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1165
|
+
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." }
|
|
780
1166
|
},
|
|
781
1167
|
required: CEAL_REQUEST_KEYS
|
|
782
1168
|
};
|
|
@@ -826,21 +1212,26 @@ var REQUEST_NODE_STANDING_OCCURRENCE_SCHEMA = {
|
|
|
826
1212
|
},
|
|
827
1213
|
required: ["schema_version", "node_ref", "operation_id", "correlation_ref", "occurrence_ref", "graph_revision", "graph_revision_digest", "admitted_input_sha256", "reason"]
|
|
828
1214
|
};
|
|
1215
|
+
var REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES = {
|
|
1216
|
+
schema_version: { type: "string", const: CEAL_OPERATION_ERROR_SCHEMA_VERSION, maxLength: 64 },
|
|
1217
|
+
code: REQUEST_ACTION_SAFE_REF_SCHEMA,
|
|
1218
|
+
message: { type: "string", minLength: 1, maxLength: 2048 },
|
|
1219
|
+
property: { type: "string", minLength: 1, maxLength: 256 },
|
|
1220
|
+
next_action: REQUEST_ACTION_SAFE_REF_SCHEMA,
|
|
1221
|
+
retryable: { type: "boolean" },
|
|
1222
|
+
source: { type: "string", enum: ["provider", "gateway"] },
|
|
1223
|
+
retry_after_ms: { type: "integer", minimum: 0, maximum: CEAL_RETRY_AFTER_MAX_MS },
|
|
1224
|
+
excluded_targets: cealExcludedTargetCoverageJsonSchema(),
|
|
1225
|
+
repaired_input: {},
|
|
1226
|
+
unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
|
|
1227
|
+
provider_handoff: { type: "string", const: "not_offered", maxLength: 32 },
|
|
1228
|
+
cause: { type: "string", enum: [...CEAL_OPERATION_WITHHELD_CAUSES] }
|
|
1229
|
+
};
|
|
829
1230
|
var REQUEST_EXECUTION_PREPARE_REFUSAL_SCHEMA = {
|
|
830
1231
|
type: "object",
|
|
831
1232
|
additionalProperties: false,
|
|
832
|
-
maxProperties:
|
|
833
|
-
properties:
|
|
834
|
-
schema_version: { type: "string", const: CEAL_OPERATION_ERROR_SCHEMA_VERSION, maxLength: 64 },
|
|
835
|
-
code: REQUEST_ACTION_SAFE_REF_SCHEMA,
|
|
836
|
-
message: { type: "string", minLength: 1, maxLength: 2048 },
|
|
837
|
-
property: { type: "string", minLength: 1, maxLength: 256 },
|
|
838
|
-
next_action: REQUEST_ACTION_SAFE_REF_SCHEMA,
|
|
839
|
-
retryable: { type: "boolean" },
|
|
840
|
-
repaired_input: {},
|
|
841
|
-
unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
|
|
842
|
-
provider_handoff: { type: "string", const: "not_offered", maxLength: 32 }
|
|
843
|
-
},
|
|
1233
|
+
maxProperties: Object.keys(REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES).length,
|
|
1234
|
+
properties: REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES,
|
|
844
1235
|
required: ["schema_version", "code", "message", "next_action", "retryable"]
|
|
845
1236
|
};
|
|
846
1237
|
var CEAL_REQUEST_EXECUTION_ORDERS = vocabulary(["graph_dependency", "caller"]);
|
|
@@ -1023,6 +1414,7 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
|
|
|
1023
1414
|
// packages/ceal-client-protocol/src/host-invocation-authentication.ts
|
|
1024
1415
|
var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = "ceal.host_invocation_authentication_result.v1";
|
|
1025
1416
|
var CEAL_HOST_INVOCATION_CONTEXT_PATH = "/api/ceal/v2/host-invocation-context";
|
|
1417
|
+
var CEAL_HOST_INGRESS_PATH = "/api/ceal/v2/host-ingress";
|
|
1026
1418
|
var CEAL_HOST_CLIENT_NONCE_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
|
|
1027
1419
|
var CEAL_HOST_CONTEXT_TOKEN_PATTERN = /^ceal_context_[A-Za-z0-9_-]{43}$/u;
|
|
1028
1420
|
function decodeCealHostInvocationAuthenticationResult(value, now = /* @__PURE__ */ new Date()) {
|
|
@@ -1041,16 +1433,14 @@ function exactKeys(value, expected) {
|
|
|
1041
1433
|
}
|
|
1042
1434
|
|
|
1043
1435
|
// packages/ceal-client-protocol/src/client-result-materialization.ts
|
|
1044
|
-
var
|
|
1045
|
-
var
|
|
1436
|
+
var CEAL_MODEL_VISIBLE_COMMAND_OUTPUT_MAX_BYTES = 32 * 1024;
|
|
1437
|
+
var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "read_argv"]);
|
|
1438
|
+
var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "local_result_root"]);
|
|
1046
1439
|
|
|
1047
1440
|
// packages/ceal-client-protocol/src/session-start-affordance.ts
|
|
1048
1441
|
var CEAL_SESSION_ENTRY_INVOCATION = "ceal";
|
|
1049
1442
|
var CEAL_LINK_RESOURCE_PRECEDENCE = `When the user asks to use Ceal or provides a link to information that may be available through installed organizational capabilities, run bare \`${CEAL_SESSION_ENTRY_INVOCATION}\`. The Host supplies the current user prompt without exposing it to the client command, and the Gateway returns the applicable entry in that one response. Treat a supplied link as a candidate Ceal resource rather than a public web-search target. Reach for a generic web fetch or web search only when the Gateway offered nothing.`;
|
|
1050
1443
|
|
|
1051
|
-
// packages/ceal-client-protocol/src/discovery-manifest.ts
|
|
1052
|
-
var CEAL_DISCOVERY_MANIFEST_CONNECTOR_KINDS = vocabulary(["github", "google-workspace", "notion", "slack"]);
|
|
1053
|
-
|
|
1054
1444
|
// packages/ceal-client-protocol/src/conversation-contracts.ts
|
|
1055
1445
|
var CEAL_APPROVAL_POLICY_KINDS = vocabulary(["requester_only", "named_users", "role_based"]);
|
|
1056
1446
|
var CEAL_APPROVAL_TARGET_KINDS = vocabulary([
|
|
@@ -1093,38 +1483,18 @@ var CEAL_GATEWAY_ANNOUNCEMENT_POLICY_NON_CLAIMS = vocabulary([
|
|
|
1093
1483
|
|
|
1094
1484
|
// packages/ceal-client-protocol/src/gateway-discovery-response-validation.ts
|
|
1095
1485
|
var ANNOUNCEMENT_POLICY_CAPABILITY_BINDINGS = Object.freeze({
|
|
1096
|
-
"
|
|
1097
|
-
"
|
|
1098
|
-
"github.issue.get": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1099
|
-
"github.pull_request.get": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1486
|
+
"repository.get": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1487
|
+
"resource.search": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1100
1488
|
"github.workflow_run.get": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1101
1489
|
"message.search": [{ effect: "read", scopeStatementKind: "slack_public_app_member_channels_only", providerAuthorityKind: "slack_app" }],
|
|
1102
|
-
"message.get": [{ effect: "read", scopeStatementKind: "slack_public_app_member_channels_only", providerAuthorityKind: "slack_app" }],
|
|
1103
|
-
"resource.resolve": [
|
|
1104
|
-
{ effect: "read", scopeStatementKind: "slack_public_app_member_channels_only", providerAuthorityKind: "slack_app" },
|
|
1105
|
-
{ effect: "read", scopeStatementKind: "notion_connected_logical_area", providerAuthorityKind: "notion_integration" }
|
|
1106
|
-
],
|
|
1107
|
-
"conversation.thread.get": [{ effect: "read", scopeStatementKind: "slack_public_app_member_channels_only", providerAuthorityKind: "slack_app" }],
|
|
1108
1490
|
"notion.search": [{ effect: "read", scopeStatementKind: "notion_connected_logical_area", providerAuthorityKind: "notion_integration" }],
|
|
1109
|
-
"
|
|
1110
|
-
"
|
|
1111
|
-
"
|
|
1112
|
-
"
|
|
1113
|
-
"
|
|
1114
|
-
"
|
|
1115
|
-
"
|
|
1116
|
-
"sheets.values.update": [{ effect: "write", scopeStatementKind: "google_workspace_ceal_drive_or_direct_share_editable_sheet_ranges", providerAuthorityKind: "google_service_account" }],
|
|
1117
|
-
"sheets.values.clear": [{ effect: "write", scopeStatementKind: "google_workspace_ceal_drive_or_direct_share_editable_sheet_clear_ranges", providerAuthorityKind: "google_service_account" }]
|
|
1118
|
-
});
|
|
1119
|
-
var ANNOUNCEMENT_SCOPE_STATEMENTS = Object.freeze({
|
|
1120
|
-
github_app_installation_repositories: "Repositories in the installed GitHub App installation.",
|
|
1121
|
-
slack_public_app_member_channels_only: "Public channels where the installed Slack app is a member; private channels, direct messages, multi-person direct messages, and requester membership are not declared by this connector.",
|
|
1122
|
-
notion_connected_logical_area: "Connected Notion logical area under provider-enforced sharing; descendant inventory is not declared.",
|
|
1123
|
-
google_workspace_calendar_read_only: "Approved Calendar availability and event reads only; Calendar mutation is not declared.",
|
|
1124
|
-
google_workspace_ceal_drive_or_direct_share_metadata: "Metadata search for files in the organization shared drive named Ceal Drive and files directly shared with the provider application; file-content read and mutation are not declared.",
|
|
1125
|
-
google_workspace_ceal_drive_or_direct_share_sheet_ranges: "Bounded values reads from governed Google Sheets in the organization shared drive named Ceal Drive and directly shared files; file mutation is not declared.",
|
|
1126
|
-
google_workspace_ceal_drive_or_direct_share_editable_sheet_ranges: "Bounded values updates in governed editable Google Sheets in the organization shared drive named Ceal Drive and directly shared files; Docs, Slides, and other Drive file mutation are not declared.",
|
|
1127
|
-
google_workspace_ceal_drive_or_direct_share_editable_sheet_clear_ranges: "Bounded values clears in governed editable Google Sheets in the organization shared drive named Ceal Drive and directly shared files; Docs, Slides, and other Drive file mutation are not declared."
|
|
1491
|
+
"resource.get": [{ effect: "read", scopeStatementKind: "notion_connected_logical_area", providerAuthorityKind: "notion_integration" }],
|
|
1492
|
+
"availability.get": [{ effect: "read", scopeStatementKind: "google_workspace_calendar_read_only", providerAuthorityKind: "google_service_account" }],
|
|
1493
|
+
"event.search": [{ effect: "read", scopeStatementKind: "google_workspace_calendar_read_only", providerAuthorityKind: "google_service_account" }],
|
|
1494
|
+
"file.search": [{ effect: "read", scopeStatementKind: "google_workspace_admitted_drive_target_metadata", providerAuthorityKind: "google_service_account" }],
|
|
1495
|
+
"cell_range.get": [{ effect: "read", scopeStatementKind: "google_workspace_admitted_drive_target_sheet_ranges", providerAuthorityKind: "google_service_account" }],
|
|
1496
|
+
"sheet.list": [{ effect: "read", scopeStatementKind: "google_workspace_admitted_drive_target_sheet_tabs", providerAuthorityKind: "google_service_account" }],
|
|
1497
|
+
"cell_range.update": [{ effect: "write", scopeStatementKind: "google_workspace_admitted_drive_target_editable_sheet_ranges", providerAuthorityKind: "google_service_account" }]
|
|
1128
1498
|
});
|
|
1129
1499
|
|
|
1130
1500
|
// packages/ceal-client-protocol/src/gateway-write-identity.ts
|
|
@@ -1181,113 +1551,29 @@ var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "
|
|
|
1181
1551
|
// packages/ceal-client-protocol/src/actor-kind.ts
|
|
1182
1552
|
var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
|
|
1183
1553
|
|
|
1184
|
-
// packages/ceal-client-protocol/src/
|
|
1185
|
-
var
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
var CEAL_PROVIDER_OUTCOMES = vocabulary(["not_attempted", "outcome_unknown", "verified"]);
|
|
1193
|
-
var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
1194
|
-
"provider_page_budget",
|
|
1195
|
-
"related_item_budget",
|
|
1196
|
-
"provider_cursor_unavailable",
|
|
1197
|
-
"malformed_provider_page",
|
|
1198
|
-
"visibility_incomplete",
|
|
1199
|
-
"unsupported_event_kind"
|
|
1200
|
-
]);
|
|
1201
|
-
var CEAL_ACTIVITY_COVERAGE_STATUSES = vocabulary(["complete_for_target", "continuation_required", "blocked"]);
|
|
1202
|
-
var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
1203
|
-
"slack.message",
|
|
1204
|
-
"slack.reply",
|
|
1205
|
-
"github.commit.authored",
|
|
1206
|
-
"github.issue.opened",
|
|
1207
|
-
"github.pull_request.opened",
|
|
1208
|
-
"github.review.submitted",
|
|
1209
|
-
"github.issue_comment.created",
|
|
1210
|
-
"github.review_comment.created",
|
|
1211
|
-
"calendar.organized",
|
|
1212
|
-
"calendar.accepted_invite",
|
|
1213
|
-
"calendar.recorded"
|
|
1214
|
-
]);
|
|
1215
|
-
var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
1216
|
-
"message_created",
|
|
1217
|
-
"commit_authored",
|
|
1218
|
-
"resource_created",
|
|
1219
|
-
"review_submitted",
|
|
1220
|
-
"comment_created",
|
|
1221
|
-
"event_start",
|
|
1222
|
-
"all_day_start"
|
|
1554
|
+
// packages/ceal-client-protocol/src/personal-client-vocabulary.ts
|
|
1555
|
+
var CEAL_PERSONAL_CLIENT_BINDING_KEYS = vocabulary([
|
|
1556
|
+
"access_group_ref",
|
|
1557
|
+
"membership_ref",
|
|
1558
|
+
"registration_ref",
|
|
1559
|
+
"client_ref",
|
|
1560
|
+
"subject_ref",
|
|
1561
|
+
"instance_ref"
|
|
1223
1562
|
]);
|
|
1224
|
-
var
|
|
1225
|
-
var
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
"
|
|
1234
|
-
"heading_1",
|
|
1235
|
-
"heading_2",
|
|
1236
|
-
"heading_3",
|
|
1237
|
-
"bulleted_list_item",
|
|
1238
|
-
"numbered_list_item",
|
|
1239
|
-
"to_do",
|
|
1240
|
-
"code"
|
|
1241
|
-
]);
|
|
1242
|
-
var CEAL_NOTION_NEUTRAL_TYPES = vocabulary([
|
|
1243
|
-
"text",
|
|
1244
|
-
"number",
|
|
1245
|
-
"boolean",
|
|
1246
|
-
"date",
|
|
1247
|
-
"select",
|
|
1248
|
-
"multi_select",
|
|
1249
|
-
"people",
|
|
1250
|
-
"url",
|
|
1251
|
-
"email",
|
|
1252
|
-
"phone",
|
|
1253
|
-
"unsupported"
|
|
1254
|
-
]);
|
|
1255
|
-
var CEAL_NOTION_IDENTITY_GAP_REASONS = vocabulary([
|
|
1256
|
-
"identity_unavailable",
|
|
1257
|
-
"identity_stale",
|
|
1258
|
-
"identity_ambiguous",
|
|
1259
|
-
"identity_revoked",
|
|
1260
|
-
"identity_unlinked"
|
|
1261
|
-
]);
|
|
1262
|
-
var CEAL_NOTION_STRING_VALUE_TYPES = vocabulary(["text", "select", "url", "email", "phone"]);
|
|
1263
|
-
var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "unsupported", "download_failed", "permission_denied"]);
|
|
1264
|
-
var CEAL_PLAN_ITEM_STATUSES = vocabulary(["pending", "active", "completed"]);
|
|
1265
|
-
var CEAL_FINAL_PRESENTATION_INTENTS = vocabulary(["final", "stop", "transient_notice"]);
|
|
1266
|
-
var CEAL_PRESENTATION_INTENTS = vocabulary(["progress", ...CEAL_FINAL_PRESENTATION_INTENTS]);
|
|
1267
|
-
|
|
1268
|
-
// packages/ceal-client-protocol/src/personal-client-vocabulary.ts
|
|
1269
|
-
var CEAL_PERSONAL_CLIENT_BINDING_KEYS = vocabulary([
|
|
1270
|
-
"access_group_ref",
|
|
1271
|
-
"membership_ref",
|
|
1272
|
-
"registration_ref",
|
|
1273
|
-
"client_ref",
|
|
1274
|
-
"subject_ref",
|
|
1275
|
-
"instance_ref"
|
|
1276
|
-
]);
|
|
1277
|
-
var CEAL_ENROLLMENT_FAILURE_CODES = vocabulary(["enrollment_invalid", "enrollment_expired", "enrollment_used"]);
|
|
1278
|
-
var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
1279
|
-
"refresh_invalid",
|
|
1280
|
-
"refresh_expired",
|
|
1281
|
-
"refresh_inactive",
|
|
1282
|
-
"refresh_replayed",
|
|
1283
|
-
"refresh_revoked",
|
|
1284
|
-
"authority_replaced",
|
|
1285
|
-
"refresh_recovery_unavailable",
|
|
1286
|
-
"refresh_recovery_expired"
|
|
1563
|
+
var CEAL_ENROLLMENT_FAILURE_CODES = vocabulary(["enrollment_invalid", "enrollment_expired", "enrollment_used"]);
|
|
1564
|
+
var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
1565
|
+
"refresh_invalid",
|
|
1566
|
+
"refresh_expired",
|
|
1567
|
+
"refresh_inactive",
|
|
1568
|
+
"refresh_replayed",
|
|
1569
|
+
"refresh_revoked",
|
|
1570
|
+
"authority_replaced",
|
|
1571
|
+
"refresh_recovery_unavailable",
|
|
1572
|
+
"refresh_recovery_expired"
|
|
1287
1573
|
]);
|
|
1288
1574
|
|
|
1289
1575
|
// packages/ceal-client-protocol/src/device-enrollment.ts
|
|
1290
|
-
var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired"]);
|
|
1576
|
+
var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
|
|
1291
1577
|
var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
|
|
1292
1578
|
|
|
1293
1579
|
// packages/ceal-client-protocol/src/personal-client-session.ts
|
|
@@ -1299,7 +1585,6 @@ var MAX_ARGUMENT_BYTES = 16 * 1024;
|
|
|
1299
1585
|
var HANDSHAKE_IDENTITY_KEYS = Object.freeze(["membership_ref", "registration_ref", "client_ref", "subject_ref", "instance_ref"]);
|
|
1300
1586
|
var AUDIT_IDENTITY_KEYS = Object.freeze(["event_ref", "membership_ref", "registration_ref", "client_ref", "subject_ref", "instance_ref"]);
|
|
1301
1587
|
var FAILURE_RESPONSE_KEYS = Object.freeze(["error", "ok", "proof_ref_or_unavailable", "protocol_version", "request_id"]);
|
|
1302
|
-
var MAX_RECOVERY_RETRY_AFTER_MS = 60 * 60 * 1e3;
|
|
1303
1588
|
|
|
1304
1589
|
// host/invocation-authentication-client-command.ts
|
|
1305
1590
|
var MAX_DETAIL_CHARACTERS = 240;
|
|
@@ -1327,10 +1612,56 @@ function errorCode(error) {
|
|
|
1327
1612
|
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
1328
1613
|
}
|
|
1329
1614
|
|
|
1330
|
-
// host/
|
|
1331
|
-
import {
|
|
1332
|
-
import {
|
|
1333
|
-
|
|
1615
|
+
// host/service-log.ts
|
|
1616
|
+
import { copyFile, open, stat, truncate } from "node:fs/promises";
|
|
1617
|
+
import { isAbsolute } from "node:path";
|
|
1618
|
+
var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
|
|
1619
|
+
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
1620
|
+
var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
|
|
1621
|
+
function hostServiceLogPreviousPath(path) {
|
|
1622
|
+
return `${path}.1`;
|
|
1623
|
+
}
|
|
1624
|
+
async function rotateHostServiceLog(path, maxBytes = HOST_SERVICE_LOG_MAX_BYTES) {
|
|
1625
|
+
let size;
|
|
1626
|
+
try {
|
|
1627
|
+
size = (await stat(path)).size;
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
if (isNotFound(error)) return false;
|
|
1630
|
+
throw error;
|
|
1631
|
+
}
|
|
1632
|
+
if (size <= maxBytes) return false;
|
|
1633
|
+
await copyFile(path, hostServiceLogPreviousPath(path));
|
|
1634
|
+
await truncate(path, 0);
|
|
1635
|
+
return true;
|
|
1636
|
+
}
|
|
1637
|
+
function boundHostServiceLog(environment = process.env) {
|
|
1638
|
+
const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
|
|
1639
|
+
if (path === void 0 || !isAbsolute(path)) return () => void 0;
|
|
1640
|
+
let reported = false;
|
|
1641
|
+
const check = () => {
|
|
1642
|
+
rotateHostServiceLog(path).catch((error) => {
|
|
1643
|
+
if (reported) return;
|
|
1644
|
+
reported = true;
|
|
1645
|
+
process.stderr.write(`[host-service-log] rotation_failed code=${errorCode2(error)}
|
|
1646
|
+
`);
|
|
1647
|
+
});
|
|
1648
|
+
};
|
|
1649
|
+
check();
|
|
1650
|
+
const timer = setInterval(check, HOST_SERVICE_LOG_CHECK_INTERVAL_MS);
|
|
1651
|
+
timer.unref();
|
|
1652
|
+
return () => clearInterval(timer);
|
|
1653
|
+
}
|
|
1654
|
+
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
1655
|
+
function isNotFound(error) {
|
|
1656
|
+
return errorCode2(error) === "ENOENT";
|
|
1657
|
+
}
|
|
1658
|
+
function errorCode2(error) {
|
|
1659
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "unknown";
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
// host/runtime-source-paths.ts
|
|
1663
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1664
|
+
import { posix as posix2, win32 as win322 } from "node:path";
|
|
1334
1665
|
|
|
1335
1666
|
// host/absolute-path.ts
|
|
1336
1667
|
import { posix, win32 } from "node:path";
|
|
@@ -1339,6 +1670,37 @@ function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allow
|
|
|
1339
1670
|
return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
|
|
1340
1671
|
}
|
|
1341
1672
|
|
|
1673
|
+
// host/runtime-source-paths.ts
|
|
1674
|
+
var HOST_SOURCE_ID = /^host-source:([0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12})$/u;
|
|
1675
|
+
function hostSourceUuid(sourceRef) {
|
|
1676
|
+
return HOST_SOURCE_ID.exec(sourceRef)?.[1] ?? null;
|
|
1677
|
+
}
|
|
1678
|
+
function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
|
|
1679
|
+
const sourceId = hostSourceUuid(sourceRef);
|
|
1680
|
+
if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
|
|
1681
|
+
const path = platform === "win32" ? win322 : posix2;
|
|
1682
|
+
const sourcesRoot = path.join(stateRoot, "sources");
|
|
1683
|
+
const sourceRoot = path.join(sourcesRoot, sourceId);
|
|
1684
|
+
const runtimeRoot = path.join(stateRoot, "run");
|
|
1685
|
+
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash2("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
|
|
1686
|
+
assertHostRuntimeIpcLength(endpoint, platform);
|
|
1687
|
+
return { sourceId, sourcesRoot, sourceRoot, relayRoot: path.join(sourceRoot, "relay"), codexOutbox: path.join(sourceRoot, "producers", "codex", "outbox"), claudeOutbox: path.join(sourceRoot, "producers", "claude", "outbox"), endpoint, runtimeRoot };
|
|
1688
|
+
}
|
|
1689
|
+
function assertHostRuntimeIpcLength(endpoint, platform) {
|
|
1690
|
+
if (platform === "win32") return;
|
|
1691
|
+
const maximum = platform === "linux" ? 107 : 103;
|
|
1692
|
+
if (Buffer.byteLength(endpoint, "utf8") > maximum) throw Object.assign(new TypeError(`ipc_endpoint_too_long: Host IPC path exceeds ${maximum} bytes; use a shorter Host state root.`), { code: "ipc_endpoint_too_long" });
|
|
1693
|
+
}
|
|
1694
|
+
function assertHostRelaySourcePaths(stateRoot, config, platform = process.platform) {
|
|
1695
|
+
const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
|
|
1696
|
+
if (config.state_root !== paths.relayRoot || config.ipc_endpoint !== paths.endpoint) throw new TypeError("Host Relay state and IPC must belong to its configured source.");
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
// host/relay/src/host-relay-runtime.ts
|
|
1700
|
+
import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
|
|
1701
|
+
import { lstat as lstat4, open as open5 } from "node:fs/promises";
|
|
1702
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
1703
|
+
|
|
1342
1704
|
// host/abortable-wait.ts
|
|
1343
1705
|
function waitForAbort(signal, milliseconds) {
|
|
1344
1706
|
if (signal.aborted) return Promise.resolve();
|
|
@@ -1423,7 +1785,7 @@ function skipWhitespace(text, start) {
|
|
|
1423
1785
|
}
|
|
1424
1786
|
|
|
1425
1787
|
// host/relay/src/producer-frame.ts
|
|
1426
|
-
import { createHash as
|
|
1788
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
1427
1789
|
var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
|
|
1428
1790
|
var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
|
|
1429
1791
|
var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
@@ -1469,7 +1831,7 @@ function decodeHostProducerFrame(input) {
|
|
|
1469
1831
|
return value;
|
|
1470
1832
|
}
|
|
1471
1833
|
function hostProducerFrameHash(frame) {
|
|
1472
|
-
return
|
|
1834
|
+
return createHash3("sha256").update(JSON.stringify(frame), "utf8").digest("hex");
|
|
1473
1835
|
}
|
|
1474
1836
|
function hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef) {
|
|
1475
1837
|
if (!opaqueHostSourceRef(hostSourceRef)) throw new TypeError("Gateway host source reference is invalid.");
|
|
@@ -1590,20 +1952,20 @@ function exactOptional(value, required, optional) {
|
|
|
1590
1952
|
|
|
1591
1953
|
// host/relay/src/relay-admission-store.ts
|
|
1592
1954
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
1593
|
-
import { join as
|
|
1955
|
+
import { join as join4 } from "node:path";
|
|
1594
1956
|
|
|
1595
1957
|
// host/relay/src/durable-atomic-replacement.ts
|
|
1596
1958
|
import { O_RDONLY } from "node:constants";
|
|
1597
|
-
import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
1959
|
+
import { lstat, mkdir, open as open2, rename, unlink } from "node:fs/promises";
|
|
1598
1960
|
import { dirname } from "node:path";
|
|
1599
1961
|
var DEFAULT_PORT = {
|
|
1600
1962
|
platform: process.platform,
|
|
1601
1963
|
mkdir: (path, options) => mkdir(path, options),
|
|
1602
1964
|
lstat: (path) => lstat(path),
|
|
1603
|
-
open_file: (path) =>
|
|
1965
|
+
open_file: (path) => open2(path, "wx", 384),
|
|
1604
1966
|
rename,
|
|
1605
1967
|
unlink,
|
|
1606
|
-
open_directory: (path) =>
|
|
1968
|
+
open_directory: (path) => open2(path, O_RDONLY),
|
|
1607
1969
|
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
1608
1970
|
};
|
|
1609
1971
|
async function durableAtomicReplace(target, data, port = DEFAULT_PORT) {
|
|
@@ -1663,7 +2025,7 @@ async function removeTemporary(path, port, failures) {
|
|
|
1663
2025
|
try {
|
|
1664
2026
|
await port.unlink(path);
|
|
1665
2027
|
} catch (error) {
|
|
1666
|
-
if (
|
|
2028
|
+
if (errorCode3(error) !== "ENOENT") failures.push(error);
|
|
1667
2029
|
}
|
|
1668
2030
|
}
|
|
1669
2031
|
function throwFailures(failures) {
|
|
@@ -1671,16 +2033,255 @@ function throwFailures(failures) {
|
|
|
1671
2033
|
if (failures.length > 1) throw new AggregateError(failures, "Host Relay durable replacement and cleanup failed.");
|
|
1672
2034
|
}
|
|
1673
2035
|
function directorySyncUnsupported(error) {
|
|
1674
|
-
return ["EINVAL", "ENOSYS", "ENOTSUP"].includes(
|
|
2036
|
+
return ["EINVAL", "ENOSYS", "ENOTSUP"].includes(errorCode3(error) ?? "");
|
|
1675
2037
|
}
|
|
1676
|
-
function
|
|
2038
|
+
function errorCode3(error) {
|
|
1677
2039
|
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
1678
2040
|
}
|
|
1679
2041
|
|
|
2042
|
+
// host/relay/src/relay-request-binding-store.ts
|
|
2043
|
+
import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
|
|
2044
|
+
import { open as open3 } from "node:fs/promises";
|
|
2045
|
+
import { join as join2 } from "node:path";
|
|
2046
|
+
var HOST_RELAY_REQUEST_BINDING_SCHEMA = "ceal.host_relay_request_binding.v1";
|
|
2047
|
+
var STATE_SCHEMA = "ceal.host_relay_request_binding_state.v1";
|
|
2048
|
+
var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2049
|
+
var MAX_BINDINGS = 8192;
|
|
2050
|
+
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
2051
|
+
var HostRelayBindingError = class extends TypeError {
|
|
2052
|
+
name = "HostRelayBindingError";
|
|
2053
|
+
code;
|
|
2054
|
+
constructor(code, message) {
|
|
2055
|
+
super(message);
|
|
2056
|
+
this.code = code;
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
var RelayRequestBindingStore = class {
|
|
2060
|
+
tail = Promise.resolve();
|
|
2061
|
+
bindingWaiters = /* @__PURE__ */ new Set();
|
|
2062
|
+
root;
|
|
2063
|
+
constructor(root) {
|
|
2064
|
+
if (typeof root !== "string" || root.length === 0) throw new TypeError("Host Relay binding root is invalid.");
|
|
2065
|
+
this.root = root;
|
|
2066
|
+
}
|
|
2067
|
+
bind(input, now = /* @__PURE__ */ new Date()) {
|
|
2068
|
+
const run = this.tail.then(() => this.bindLocked(input, now));
|
|
2069
|
+
this.tail = run.then(() => void 0, () => void 0);
|
|
2070
|
+
return run;
|
|
2071
|
+
}
|
|
2072
|
+
query(input) {
|
|
2073
|
+
const run = this.tail.then(() => this.queryLocked(input));
|
|
2074
|
+
this.tail = run.then(() => void 0, () => void 0);
|
|
2075
|
+
return run;
|
|
2076
|
+
}
|
|
2077
|
+
waitForBinding(input, signal) {
|
|
2078
|
+
validateQuery(input);
|
|
2079
|
+
if (signal.aborted) return Promise.reject(new Error("Host Relay binding wait cancelled."));
|
|
2080
|
+
return new Promise((resolve, reject) => {
|
|
2081
|
+
let settled = false;
|
|
2082
|
+
const finish = (result2, error) => {
|
|
2083
|
+
if (settled) return;
|
|
2084
|
+
settled = true;
|
|
2085
|
+
this.bindingWaiters.delete(waiter);
|
|
2086
|
+
signal.removeEventListener("abort", waiter.abort);
|
|
2087
|
+
if (error !== void 0) reject(error);
|
|
2088
|
+
else if (result2?.status === "ready") resolve(result2);
|
|
2089
|
+
};
|
|
2090
|
+
const abort = () => finish(void 0, new Error("Host Relay binding wait cancelled."));
|
|
2091
|
+
const waiter = { query: input, resolve: (result2) => finish(result2), abort };
|
|
2092
|
+
this.bindingWaiters.add(waiter);
|
|
2093
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
2094
|
+
void this.query(input).then((result2) => {
|
|
2095
|
+
if (result2.status === "ready") finish(result2);
|
|
2096
|
+
}, (error) => finish(void 0, error));
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
async bindLocked(input, now) {
|
|
2100
|
+
const candidate = bindingFromGateway(input, now);
|
|
2101
|
+
const state = await this.read();
|
|
2102
|
+
const existing = state.bindings.find((item) => sameCanonicalIdentity(item, candidate));
|
|
2103
|
+
if (existing !== void 0) {
|
|
2104
|
+
if (!sameBinding(existing, candidate)) throw new HostRelayBindingError("canonical_turn_rebound", `Host Relay canonical turn is already bound to another Request: ${refusalFacts([{ name: "candidate_request_ref", value: candidate.request_ref, expected: existing.request_ref }, { name: "candidate_prompt_ref", value: candidate.prompt_ref, expected: existing.prompt_ref }, { name: "candidate_context_expires_at", value: candidate.context_expires_at, expected: String(existing.context_expires_at) }, { name: "candidate_local_turn_aliases", value: candidate.local_turn_aliases, expected: JSON.stringify(existing.local_turn_aliases) }])}.`);
|
|
2105
|
+
return existing;
|
|
2106
|
+
}
|
|
2107
|
+
if (state.bindings.length >= MAX_BINDINGS) throw new HostRelayBindingError("binding_capacity_exhausted", `Host Relay request binding capacity is exhausted: ${refusalFact("binding_count", state.bindings.length, `below ${MAX_BINDINGS}`)}.`);
|
|
2108
|
+
const next = { schema_version: STATE_SCHEMA, bindings: [...state.bindings, candidate] };
|
|
2109
|
+
await this.write(next);
|
|
2110
|
+
this.resolveBindingWaiters(candidate);
|
|
2111
|
+
return structuredClone(candidate);
|
|
2112
|
+
}
|
|
2113
|
+
resolveBindingWaiters(binding) {
|
|
2114
|
+
for (const waiter of this.bindingWaiters) {
|
|
2115
|
+
if (relayRequestBindingMatchesQuery(waiter.query, binding)) waiter.resolve({ status: "ready", binding: structuredClone(binding) });
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
async queryLocked(input) {
|
|
2119
|
+
validateQuery(input);
|
|
2120
|
+
const state = await this.read();
|
|
2121
|
+
const matches = state.bindings.filter((binding2) => relayRequestBindingMatchesQuery(input, binding2));
|
|
2122
|
+
if (matches.length === 0) return { status: "no_binding" };
|
|
2123
|
+
const binding = matches[0];
|
|
2124
|
+
if (binding === void 0) throw new TypeError("Host Relay binding query result is invalid.");
|
|
2125
|
+
return { status: "ready", binding: structuredClone(binding) };
|
|
2126
|
+
}
|
|
2127
|
+
async read() {
|
|
2128
|
+
let handle;
|
|
2129
|
+
try {
|
|
2130
|
+
handle = await open3(join2(this.root, "request-bindings.json"), O_RDONLY2 | O_NOFOLLOW);
|
|
2131
|
+
const stats = await handle.stat();
|
|
2132
|
+
if (!stats.isFile() || !ownerPrivate2(stats)) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFacts([{ name: "state_is_file", value: stats.isFile(), expected: "true" }, { name: "state_uid", value: stats.uid, expected: String(process.geteuid?.() ?? stats.uid) }, { name: "state_mode", value: (stats.mode & 511).toString(8), expected: "no group or other permission bits" }])}.`);
|
|
2133
|
+
if (stats.size > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFact("state_bytes", stats.size, `at most ${MAX_STATE_BYTES}`)}.`);
|
|
2134
|
+
return decodeState(JSON.parse(await handle.readFile("utf8")));
|
|
2135
|
+
} catch (error) {
|
|
2136
|
+
if (errorCode4(error) === "ENOENT") return { schema_version: STATE_SCHEMA, bindings: [] };
|
|
2137
|
+
throw error;
|
|
2138
|
+
} finally {
|
|
2139
|
+
await handle?.close();
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
async write(state) {
|
|
2143
|
+
const encoded = JSON.stringify(state);
|
|
2144
|
+
if (Buffer.byteLength(encoded, "utf8") > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is too large: ${refusalFact("state_bytes", Buffer.byteLength(encoded, "utf8"), `at most ${MAX_STATE_BYTES}`)}.`);
|
|
2145
|
+
await durableAtomicReplace(join2(this.root, "request-bindings.json"), encoded);
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
function relayRequestWorkContextFromBinding(binding) {
|
|
2149
|
+
return createRelayRequestWorkContext({ request_ref: binding.request_ref, host_binding: {
|
|
2150
|
+
host_source_ref: binding.host_source_ref,
|
|
2151
|
+
host_session_ref: binding.host_session_ref,
|
|
2152
|
+
host_turn_ref: binding.host_turn_ref,
|
|
2153
|
+
host_input_ref: binding.host_input_ref,
|
|
2154
|
+
prompt_ref: binding.prompt_ref
|
|
2155
|
+
} });
|
|
2156
|
+
}
|
|
2157
|
+
function createRelayRequestWorkContext(input) {
|
|
2158
|
+
if (!record2(input) || !exactKeys3(input, ["request_ref", "host_binding"]) || !safeRef2(input.request_ref) || !record2(input.host_binding)) {
|
|
2159
|
+
throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
|
|
2160
|
+
}
|
|
2161
|
+
const hostBinding = input.host_binding;
|
|
2162
|
+
if (!exactKeys3(hostBinding, ["host_source_ref", "host_session_ref", "host_turn_ref", "host_input_ref", "prompt_ref"]) || ![hostBinding.host_source_ref, hostBinding.host_session_ref, hostBinding.host_turn_ref, hostBinding.host_input_ref, hostBinding.prompt_ref].every(safeRef2)) {
|
|
2163
|
+
throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
|
|
2164
|
+
}
|
|
2165
|
+
const hostSourceRef = requiredRef(hostBinding.host_source_ref);
|
|
2166
|
+
const hostSessionRef = requiredRef(hostBinding.host_session_ref);
|
|
2167
|
+
const hostTurnRef = requiredRef(hostBinding.host_turn_ref);
|
|
2168
|
+
const hostInputRef = requiredRef(hostBinding.host_input_ref);
|
|
2169
|
+
const promptRef = requiredRef(hostBinding.prompt_ref);
|
|
2170
|
+
return Object.freeze({ request_ref: input.request_ref, host_binding: Object.freeze({
|
|
2171
|
+
host_source_ref: hostSourceRef,
|
|
2172
|
+
host_session_ref: hostSessionRef,
|
|
2173
|
+
host_turn_ref: hostTurnRef,
|
|
2174
|
+
host_input_ref: hostInputRef,
|
|
2175
|
+
prompt_ref: promptRef
|
|
2176
|
+
}) });
|
|
2177
|
+
}
|
|
2178
|
+
function bindingFromGateway(input, now) {
|
|
2179
|
+
const payload = input.frame.payload;
|
|
2180
|
+
const candidate = {
|
|
2181
|
+
schema_version: HOST_RELAY_REQUEST_BINDING_SCHEMA,
|
|
2182
|
+
producer_id: input.frame.producer_id,
|
|
2183
|
+
producer_installation_ref: input.frame.producer_installation_ref,
|
|
2184
|
+
local_session_ref: requiredRef(payload.local_session_ref),
|
|
2185
|
+
local_turn_ref: requiredRef(payload.local_turn_ref),
|
|
2186
|
+
local_turn_aliases: optionalRefs2(payload.local_turn_aliases),
|
|
2187
|
+
prompt_ref: requiredRef(input.prompt_ref),
|
|
2188
|
+
request_ref: requiredRef(input.request_ref),
|
|
2189
|
+
bound_at: timestamp2(now.toISOString()),
|
|
2190
|
+
...payload.context_expires_at === void 0 ? {} : { context_expires_at: timestamp2(payload.context_expires_at) },
|
|
2191
|
+
host_source_ref: requiredRef(input.host_binding.host_source_ref),
|
|
2192
|
+
host_session_ref: requiredRef(input.host_binding.host_session_ref),
|
|
2193
|
+
host_turn_ref: requiredRef(input.host_binding.host_turn_ref),
|
|
2194
|
+
host_input_ref: requiredRef(input.host_binding.host_input_ref)
|
|
2195
|
+
};
|
|
2196
|
+
if (input.host_binding.prompt_ref !== input.prompt_ref) throw new HostRelayBindingError("context_identity_mismatch", "Host Relay binding prompt reference does not match the acknowledged submission.");
|
|
2197
|
+
validateBinding(candidate);
|
|
2198
|
+
return candidate;
|
|
2199
|
+
}
|
|
2200
|
+
function decodeState(value) {
|
|
2201
|
+
if (!record2(value) || !exactKeys3(value, ["schema_version", "bindings"])) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("state_keys", record2(value) ? Object.keys(value) : [], "exactly schema_version and bindings")}.`);
|
|
2202
|
+
if (value.schema_version !== STATE_SCHEMA) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("schema_version", value.schema_version, STATE_SCHEMA)}.`);
|
|
2203
|
+
if (!Array.isArray(value.bindings)) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("bindings", value.bindings, "an array")}.`);
|
|
2204
|
+
const bindings = value.bindings.map((item) => {
|
|
2205
|
+
validateBinding(item);
|
|
2206
|
+
return item;
|
|
2207
|
+
});
|
|
2208
|
+
const keys = bindings.map(bindingKey);
|
|
2209
|
+
if (new Set(keys).size !== keys.length) throw new TypeError("Host Relay binding state contains duplicate canonical turns.");
|
|
2210
|
+
return { schema_version: STATE_SCHEMA, bindings };
|
|
2211
|
+
}
|
|
2212
|
+
function validateBinding(value) {
|
|
2213
|
+
if (!record2(value) || !exactOptionalKeys(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "local_turn_aliases", "prompt_ref", "request_ref", "bound_at", "host_source_ref", "host_session_ref", "host_turn_ref", "host_input_ref"], ["context_expires_at"]) || !validBindingIdentity(value) || !validBindingRequest(value)) throw new TypeError("Host Relay request binding is invalid.");
|
|
2214
|
+
}
|
|
2215
|
+
function validBindingIdentity(value) {
|
|
2216
|
+
return value.schema_version === HOST_RELAY_REQUEST_BINDING_SCHEMA && producer(value.producer_id) && [value.producer_installation_ref, value.local_session_ref, value.local_turn_ref].every(safeRef2) && refList(value.local_turn_aliases);
|
|
2217
|
+
}
|
|
2218
|
+
function validBindingRequest(value) {
|
|
2219
|
+
const tuple = [value.host_source_ref, value.host_session_ref, value.host_turn_ref, value.host_input_ref];
|
|
2220
|
+
return safeRef2(value.prompt_ref) && safeRef2(value.request_ref) && validTimestamp(value.bound_at) && (value.context_expires_at === void 0 || validTimestamp(value.context_expires_at)) && tuple.every(safeRef2);
|
|
2221
|
+
}
|
|
2222
|
+
function validateQuery(value) {
|
|
2223
|
+
if (!record2(value) || !exactKeys3(value, ["producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref"]) || !producer(value.producer_id) || !safeRef2(value.producer_installation_ref) || !safeRef2(value.local_session_ref) || !safeRef2(value.local_turn_ref)) throw new TypeError("Host Relay binding query is invalid.");
|
|
2224
|
+
}
|
|
2225
|
+
function relayRequestBindingMatchesQuery(query, binding) {
|
|
2226
|
+
return query.producer_id === binding.producer_id && query.local_session_ref === binding.local_session_ref && (query.local_turn_ref === binding.local_turn_ref || binding.local_turn_aliases.includes(query.local_turn_ref)) && query.producer_installation_ref === binding.producer_installation_ref;
|
|
2227
|
+
}
|
|
2228
|
+
function sameCanonicalIdentity(left, right) {
|
|
2229
|
+
return bindingKey(left) === bindingKey(right);
|
|
2230
|
+
}
|
|
2231
|
+
function sameBinding(left, right) {
|
|
2232
|
+
return left.request_ref === right.request_ref && left.prompt_ref === right.prompt_ref && left.context_expires_at === right.context_expires_at && left.host_source_ref === right.host_source_ref && left.host_session_ref === right.host_session_ref && left.host_turn_ref === right.host_turn_ref && left.host_input_ref === right.host_input_ref && JSON.stringify(left.local_turn_aliases) === JSON.stringify(right.local_turn_aliases);
|
|
2233
|
+
}
|
|
2234
|
+
function bindingKey(value) {
|
|
2235
|
+
return `${value.producer_id}\0${value.producer_installation_ref}\0${value.local_session_ref}\0${value.local_turn_ref}`;
|
|
2236
|
+
}
|
|
2237
|
+
function requiredRef(value) {
|
|
2238
|
+
if (!safeRef2(value)) throw new TypeError("Host Relay binding reference is invalid.");
|
|
2239
|
+
return value;
|
|
2240
|
+
}
|
|
2241
|
+
function optionalRefs2(value) {
|
|
2242
|
+
if (value === void 0) return [];
|
|
2243
|
+
if (!refList(value)) throw new TypeError("Host Relay binding aliases are invalid.");
|
|
2244
|
+
return [...value];
|
|
2245
|
+
}
|
|
2246
|
+
function timestamp2(value) {
|
|
2247
|
+
if (!validTimestamp(value)) throw new TypeError("Host Relay binding timestamp is invalid.");
|
|
2248
|
+
return value;
|
|
2249
|
+
}
|
|
2250
|
+
function producer(value) {
|
|
2251
|
+
return value === "codex" || value === "claude";
|
|
2252
|
+
}
|
|
2253
|
+
function safeRef2(value) {
|
|
2254
|
+
return typeof value === "string" && SAFE_REF3.test(value);
|
|
2255
|
+
}
|
|
2256
|
+
function refList(value) {
|
|
2257
|
+
return Array.isArray(value) && value.length <= 16 && value.every(safeRef2) && new Set(value).size === value.length;
|
|
2258
|
+
}
|
|
2259
|
+
function validTimestamp(value) {
|
|
2260
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
2261
|
+
}
|
|
2262
|
+
function record2(value) {
|
|
2263
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2264
|
+
}
|
|
2265
|
+
function exactKeys3(value, keys) {
|
|
2266
|
+
return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
2267
|
+
}
|
|
2268
|
+
function exactOptionalKeys(value, required, optional) {
|
|
2269
|
+
const keys = Object.keys(value);
|
|
2270
|
+
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2271
|
+
}
|
|
2272
|
+
function errorCode4(error) {
|
|
2273
|
+
return record2(error) && typeof error.code === "string" ? error.code : null;
|
|
2274
|
+
}
|
|
2275
|
+
function ownerPrivate2(stats) {
|
|
2276
|
+
if (process.platform === "win32") return true;
|
|
2277
|
+
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
2278
|
+
return stats.uid === uid && (stats.mode & 63) === 0;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
1680
2281
|
// host/relay/src/relay-quarantine-store.ts
|
|
1681
|
-
import { createHash as
|
|
2282
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
1682
2283
|
import { readFile } from "node:fs/promises";
|
|
1683
|
-
import { join as
|
|
2284
|
+
import { join as join3 } from "node:path";
|
|
1684
2285
|
var RELAY_QUARANTINE_SCHEMA = "ceal.host_relay_quarantine_state.v1";
|
|
1685
2286
|
var RELAY_QUARANTINE_MAX_RECORDS = 512;
|
|
1686
2287
|
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
@@ -1721,27 +2322,27 @@ var RelayQuarantineStore = class {
|
|
|
1721
2322
|
const raw = Buffer.from(rawFrame).subarray(0, MAX_CAPTURE_BYTES);
|
|
1722
2323
|
const state = await this.readState();
|
|
1723
2324
|
const record6 = {
|
|
1724
|
-
quarantine_ref: `quarantine:${
|
|
2325
|
+
quarantine_ref: `quarantine:${createHash4("sha256").update(`${producerId}\0${reason}\0${capturedAt}\0`).update(raw).digest("hex")}`,
|
|
1725
2326
|
producer_id: producerId,
|
|
1726
2327
|
reason,
|
|
1727
2328
|
captured_at: capturedAt,
|
|
1728
|
-
frame_sha256:
|
|
2329
|
+
frame_sha256: createHash4("sha256").update(raw).digest("hex"),
|
|
1729
2330
|
charged_bytes: raw.byteLength,
|
|
1730
2331
|
raw_frame_base64: raw.toString("base64")
|
|
1731
2332
|
};
|
|
1732
2333
|
const records = state.producers[producerId];
|
|
1733
2334
|
records.push(record6);
|
|
1734
2335
|
while (records.length > this.maxRecords || totalBytes(records) > this.maxBytes) records.shift();
|
|
1735
|
-
await durableAtomicReplace(
|
|
2336
|
+
await durableAtomicReplace(join3(this.root, "quarantine.json"), JSON.stringify(state));
|
|
1736
2337
|
});
|
|
1737
2338
|
this.tail = run.then(() => void 0, () => void 0);
|
|
1738
2339
|
await run;
|
|
1739
2340
|
}
|
|
1740
2341
|
async readState() {
|
|
1741
2342
|
try {
|
|
1742
|
-
return
|
|
2343
|
+
return decodeState2(JSON.parse(await readFile(join3(this.root, "quarantine.json"), "utf8")));
|
|
1743
2344
|
} catch (error) {
|
|
1744
|
-
if (
|
|
2345
|
+
if (errorCode5(error) === "ENOENT") return emptyState();
|
|
1745
2346
|
throw error;
|
|
1746
2347
|
}
|
|
1747
2348
|
}
|
|
@@ -1752,9 +2353,9 @@ function emptyState() {
|
|
|
1752
2353
|
function totalBytes(records) {
|
|
1753
2354
|
return records.reduce((total, record6) => total + record6.charged_bytes, 0);
|
|
1754
2355
|
}
|
|
1755
|
-
function
|
|
1756
|
-
if (!
|
|
1757
|
-
if (!
|
|
2356
|
+
function decodeState2(value) {
|
|
2357
|
+
if (!record3(value) || value.schema_version !== RELAY_QUARANTINE_SCHEMA) throw new TypeError(`Host Relay quarantine state is invalid: ${refusalFact("schema_version", record3(value) ? value.schema_version : void 0, RELAY_QUARANTINE_SCHEMA)}.`);
|
|
2358
|
+
if (!record3(value.producers)) throw new TypeError(`Host Relay quarantine state is invalid: ${refusalFact("producer_ids", record3(value.producers) ? Object.keys(value.producers) : [], `the ${PRODUCERS2.length} quarantine producer buckets`)}.`);
|
|
1758
2359
|
const state = emptyState();
|
|
1759
2360
|
for (const producerId of PRODUCERS2) {
|
|
1760
2361
|
const records = value.producers[producerId];
|
|
@@ -1764,7 +2365,7 @@ function decodeState(value) {
|
|
|
1764
2365
|
return state;
|
|
1765
2366
|
}
|
|
1766
2367
|
function validRecord(value) {
|
|
1767
|
-
if (!
|
|
2368
|
+
if (!record3(value) || typeof value.quarantine_ref !== "string" || !quarantineProducer(value.producer_id) || typeof value.reason !== "string" || !Number.isFinite(Date.parse(String(value.captured_at))) || typeof value.frame_sha256 !== "string" || typeof value.charged_bytes !== "number" || !Number.isSafeInteger(value.charged_bytes) || value.charged_bytes < 0 || typeof value.raw_frame_base64 !== "string") return false;
|
|
1768
2369
|
return true;
|
|
1769
2370
|
}
|
|
1770
2371
|
function quarantineProducer(value) {
|
|
@@ -1773,16 +2374,16 @@ function quarantineProducer(value) {
|
|
|
1773
2374
|
function inferProducer(rawFrame) {
|
|
1774
2375
|
try {
|
|
1775
2376
|
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.from(rawFrame).subarray(4)));
|
|
1776
|
-
return
|
|
2377
|
+
return record3(value) && (value.producer_id === "codex" || value.producer_id === "claude") ? value.producer_id : "unknown";
|
|
1777
2378
|
} catch {
|
|
1778
2379
|
return "unknown";
|
|
1779
2380
|
}
|
|
1780
2381
|
}
|
|
1781
|
-
function
|
|
2382
|
+
function record3(value) {
|
|
1782
2383
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1783
2384
|
}
|
|
1784
|
-
function
|
|
1785
|
-
return
|
|
2385
|
+
function errorCode5(error) {
|
|
2386
|
+
return record3(error) && typeof error.code === "string" ? error.code : null;
|
|
1786
2387
|
}
|
|
1787
2388
|
|
|
1788
2389
|
// host/relay/src/relay-admission-store.ts
|
|
@@ -1792,11 +2393,12 @@ var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
|
1792
2393
|
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
1793
2394
|
var RelayAdmissionStore = class {
|
|
1794
2395
|
tail = Promise.resolve();
|
|
2396
|
+
drainWaiters = /* @__PURE__ */ new Set();
|
|
1795
2397
|
root;
|
|
1796
2398
|
quarantine;
|
|
1797
2399
|
limits;
|
|
1798
2400
|
faultHooks;
|
|
1799
|
-
constructor(root, limits = DEFAULT_LIMITS, faultHooks = {}, quarantine = new RelayQuarantineStore(
|
|
2401
|
+
constructor(root, limits = DEFAULT_LIMITS, faultHooks = {}, quarantine = new RelayQuarantineStore(join4(root, "quarantine"))) {
|
|
1800
2402
|
this.root = root;
|
|
1801
2403
|
this.quarantine = quarantine;
|
|
1802
2404
|
this.limits = limits;
|
|
@@ -1811,10 +2413,36 @@ var RelayAdmissionStore = class {
|
|
|
1811
2413
|
await this.tail;
|
|
1812
2414
|
return this.read();
|
|
1813
2415
|
}
|
|
2416
|
+
async hasPendingRequestBinding(query) {
|
|
2417
|
+
const state = await this.read();
|
|
2418
|
+
return state.entries.some((entry2) => pendingFrameMatchesBindingQuery(entry2, query));
|
|
2419
|
+
}
|
|
1814
2420
|
async drainRound(deliver, options) {
|
|
1815
2421
|
const run = this.tail.then(() => this.drainLocked(deliver, options));
|
|
1816
2422
|
this.tail = run.then(() => void 0, () => void 0);
|
|
1817
|
-
|
|
2423
|
+
const result2 = await run;
|
|
2424
|
+
if (result2.delivered.length > 0 || result2.refused.length > 0 || result2.retained.length > 0) this.resolveDrainWaiters();
|
|
2425
|
+
return result2;
|
|
2426
|
+
}
|
|
2427
|
+
waitForDrainChange(signal) {
|
|
2428
|
+
if (signal.aborted) return Promise.reject(new Error("Host Relay admission wait cancelled."));
|
|
2429
|
+
return new Promise((resolve, reject) => {
|
|
2430
|
+
let settled = false;
|
|
2431
|
+
const finish = (error) => {
|
|
2432
|
+
if (settled) return;
|
|
2433
|
+
settled = true;
|
|
2434
|
+
this.drainWaiters.delete(waiter);
|
|
2435
|
+
signal.removeEventListener("abort", waiter.abort);
|
|
2436
|
+
if (error !== void 0) reject(error);
|
|
2437
|
+
else resolve();
|
|
2438
|
+
};
|
|
2439
|
+
const waiter = { resolve: () => finish(), abort: () => finish(new Error("Host Relay admission wait cancelled.")) };
|
|
2440
|
+
this.drainWaiters.add(waiter);
|
|
2441
|
+
signal.addEventListener("abort", waiter.abort, { once: true });
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
resolveDrainWaiters() {
|
|
2445
|
+
for (const waiter of this.drainWaiters) waiter.resolve();
|
|
1818
2446
|
}
|
|
1819
2447
|
async admitLocked(frame) {
|
|
1820
2448
|
const state = await this.read();
|
|
@@ -1871,308 +2499,109 @@ var RelayAdmissionStore = class {
|
|
|
1871
2499
|
if (!installation2) throw new TypeError("Host Relay delivery installation is unavailable.");
|
|
1872
2500
|
if (outcome.status === "refused" || exhausted) {
|
|
1873
2501
|
await this.quarantine.retainFrame(head.frame, "gateway_refused");
|
|
1874
|
-
installation2.last_refusal = { sequence: head.sequence, submission_ref: input.submission_ref, ...refusalCause(outcome, head), refused_at_ms: options.now_ms };
|
|
1875
|
-
refused.push(input.submission_ref);
|
|
1876
|
-
} else {
|
|
1877
|
-
await options.after_gateway_accept_before_delivery_fsync?.({ fault_point: "after_gateway_accept_before_delivery_fsync", delivery: structuredClone(input) });
|
|
1878
|
-
delivered.push(input.submission_ref);
|
|
1879
|
-
}
|
|
1880
|
-
state.entries.splice(state.entries.indexOf(head), 1);
|
|
1881
|
-
installation2.delivered_through_sequence = Math.max(installation2.delivered_through_sequence, head.sequence);
|
|
1882
|
-
await this.write(state);
|
|
1883
|
-
}
|
|
1884
|
-
return { delivered, retained, refused };
|
|
1885
|
-
}
|
|
1886
|
-
async read() {
|
|
1887
|
-
try {
|
|
1888
|
-
return decodeState2(JSON.parse(await readFile2(join3(this.root, "admission.json"), "utf8")));
|
|
1889
|
-
} catch (error) {
|
|
1890
|
-
if (errorCode4(error) === "ENOENT") return { schema_version: "ceal.host_relay_admission_state.v1", installations: {}, entries: [] };
|
|
1891
|
-
throw error;
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
async write(state) {
|
|
1895
|
-
await durableAtomicReplace(join3(this.root, "admission.json"), JSON.stringify(state));
|
|
1896
|
-
}
|
|
1897
|
-
};
|
|
1898
|
-
function installationKey(frame) {
|
|
1899
|
-
return `${frame.producer_id}\0${frame.producer_installation_ref}`;
|
|
1900
|
-
}
|
|
1901
|
-
function replayCode(existing, hash, sequence, installation2) {
|
|
1902
|
-
if (existing !== void 0) return existing.hash === hash ? "already_admitted" : "frame_identity_conflict";
|
|
1903
|
-
return sequence <= installation2.delivered_through_sequence ? "already_admitted" : null;
|
|
1904
|
-
}
|
|
1905
|
-
function bytes(entries) {
|
|
1906
|
-
return entries.reduce((total, entry2) => total + entry2.charged_bytes, 0);
|
|
1907
|
-
}
|
|
1908
|
-
function retryExhausted(outcome, entry2) {
|
|
1909
|
-
return outcome.status === "retryable" && entry2.retry_attempts >= MAX_RETRYABLE_ATTEMPTS;
|
|
1910
|
-
}
|
|
1911
|
-
function refusalCause(outcome, entry2) {
|
|
1912
|
-
if (outcome.status === "refused") return { code: outcome.code, message: outcome.message };
|
|
1913
|
-
const reason = outcome.status === "retryable" ? outcome.reason ?? "no reason reported" : "no reason reported";
|
|
1914
|
-
return { code: "retry_exhausted", message: `Gateway delivery stayed retryable through ${entry2.retry_attempts} attempts: ${reason}` };
|
|
1915
|
-
}
|
|
1916
|
-
function scheduleRetry(entry2, nowMs, reason) {
|
|
1917
|
-
if (reason === void 0) delete entry2.retry_reason;
|
|
1918
|
-
else entry2.retry_reason = reason;
|
|
1919
|
-
entry2.retry_attempts += 1;
|
|
1920
|
-
entry2.retry_at_ms = nowMs + 1e3 * (RETRY_SECONDS[Math.min(entry2.retry_attempts - 1, RETRY_SECONDS.length - 1)] ?? 60);
|
|
1921
|
-
}
|
|
1922
|
-
function result(code, state) {
|
|
1923
|
-
return { code, admitted_through_sequence: state.admitted_through_sequence, delivered_through_sequence: state.delivered_through_sequence };
|
|
1924
|
-
}
|
|
1925
|
-
function errorCode4(error) {
|
|
1926
|
-
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
1927
|
-
}
|
|
1928
|
-
function decodeState2(value) {
|
|
1929
|
-
if (!stateRecord(value)) throw new TypeError("Host Relay admission state is invalid.");
|
|
1930
|
-
const installations = {};
|
|
1931
|
-
for (const [key, item] of Object.entries(value.installations)) {
|
|
1932
|
-
if (!installation(item)) throw new TypeError("Host Relay installation state is invalid.");
|
|
1933
|
-
installations[key] = item;
|
|
1934
|
-
}
|
|
1935
|
-
const entries = [];
|
|
1936
|
-
for (const item of value.entries) {
|
|
1937
|
-
if (!entry(item)) throw new TypeError("Host Relay entry state is invalid.");
|
|
1938
|
-
entries.push(item);
|
|
1939
|
-
}
|
|
1940
|
-
return { schema_version: "ceal.host_relay_admission_state.v1", installations, entries };
|
|
1941
|
-
}
|
|
1942
|
-
function stateRecord(value) {
|
|
1943
|
-
return object(value) && value.schema_version === "ceal.host_relay_admission_state.v1" && object(value.installations) && Array.isArray(value.entries);
|
|
1944
|
-
}
|
|
1945
|
-
function installation(value) {
|
|
1946
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && "epoch" in value && "admitted_through_sequence" in value && "delivered_through_sequence" in value && [value.epoch, value.admitted_through_sequence, value.delivered_through_sequence].every(Number.isSafeInteger) && (!("last_refusal" in value) || refusalRecord(value.last_refusal));
|
|
1947
|
-
}
|
|
1948
|
-
function refusalRecord(value) {
|
|
1949
|
-
return object(value) && Number.isSafeInteger(value.sequence) && typeof value.submission_ref === "string" && typeof value.code === "string" && typeof value.message === "string" && Number.isSafeInteger(value.refused_at_ms);
|
|
1950
|
-
}
|
|
1951
|
-
function entry(value) {
|
|
1952
|
-
return object(value) && producer(value.producer_id) && typeof value.producer_installation_ref === "string" && Number.isSafeInteger(value.sequence) && typeof value.hash === "string" && Number.isSafeInteger(value.charged_bytes) && object(value.frame) && Number.isSafeInteger(value.retry_attempts) && Number.isSafeInteger(value.retry_at_ms);
|
|
1953
|
-
}
|
|
1954
|
-
function producer(value) {
|
|
1955
|
-
return value === "codex" || value === "claude";
|
|
1956
|
-
}
|
|
1957
|
-
function object(value) {
|
|
1958
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
// host/relay/src/relay-gateway-bridge.ts
|
|
1962
|
-
import { readFile as readFile3 } from "node:fs/promises";
|
|
1963
|
-
import { join as join5 } from "node:path";
|
|
1964
|
-
|
|
1965
|
-
// host/relay/src/relay-request-binding-store.ts
|
|
1966
|
-
import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
|
|
1967
|
-
import { open as open2 } from "node:fs/promises";
|
|
1968
|
-
import { join as join4 } from "node:path";
|
|
1969
|
-
var HOST_RELAY_REQUEST_BINDING_SCHEMA = "ceal.host_relay_request_binding.v1";
|
|
1970
|
-
var STATE_SCHEMA = "ceal.host_relay_request_binding_state.v1";
|
|
1971
|
-
var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
1972
|
-
var MAX_BINDINGS = 8192;
|
|
1973
|
-
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
1974
|
-
var HostRelayBindingError = class extends TypeError {
|
|
1975
|
-
name = "HostRelayBindingError";
|
|
1976
|
-
code;
|
|
1977
|
-
constructor(code, message) {
|
|
1978
|
-
super(message);
|
|
1979
|
-
this.code = code;
|
|
1980
|
-
}
|
|
1981
|
-
};
|
|
1982
|
-
var RelayRequestBindingStore = class {
|
|
1983
|
-
tail = Promise.resolve();
|
|
1984
|
-
root;
|
|
1985
|
-
constructor(root) {
|
|
1986
|
-
if (typeof root !== "string" || root.length === 0) throw new TypeError("Host Relay binding root is invalid.");
|
|
1987
|
-
this.root = root;
|
|
1988
|
-
}
|
|
1989
|
-
bind(input, now = /* @__PURE__ */ new Date()) {
|
|
1990
|
-
const run = this.tail.then(() => this.bindLocked(input, now));
|
|
1991
|
-
this.tail = run.then(() => void 0, () => void 0);
|
|
1992
|
-
return run;
|
|
1993
|
-
}
|
|
1994
|
-
query(input) {
|
|
1995
|
-
const run = this.tail.then(() => this.queryLocked(input));
|
|
1996
|
-
this.tail = run.then(() => void 0, () => void 0);
|
|
1997
|
-
return run;
|
|
1998
|
-
}
|
|
1999
|
-
async bindLocked(input, now) {
|
|
2000
|
-
const candidate = bindingFromGateway(input, now);
|
|
2001
|
-
const state = await this.read();
|
|
2002
|
-
const existing = state.bindings.find((item) => sameCanonicalIdentity(item, candidate));
|
|
2003
|
-
if (existing !== void 0) {
|
|
2004
|
-
if (!sameBinding(existing, candidate)) throw new HostRelayBindingError("canonical_turn_rebound", `Host Relay canonical turn is already bound to another Request: ${refusalFacts([{ name: "candidate_request_ref", value: candidate.request_ref, expected: existing.request_ref }, { name: "candidate_prompt_ref", value: candidate.prompt_ref, expected: existing.prompt_ref }, { name: "candidate_context_expires_at", value: candidate.context_expires_at, expected: String(existing.context_expires_at) }, { name: "candidate_local_turn_aliases", value: candidate.local_turn_aliases, expected: JSON.stringify(existing.local_turn_aliases) }])}.`);
|
|
2005
|
-
return existing;
|
|
2006
|
-
}
|
|
2007
|
-
if (state.bindings.length >= MAX_BINDINGS) throw new HostRelayBindingError("binding_capacity_exhausted", `Host Relay request binding capacity is exhausted: ${refusalFact("binding_count", state.bindings.length, `below ${MAX_BINDINGS}`)}.`);
|
|
2008
|
-
const next = { schema_version: STATE_SCHEMA, bindings: [...state.bindings, candidate] };
|
|
2009
|
-
await this.write(next);
|
|
2010
|
-
return structuredClone(candidate);
|
|
2011
|
-
}
|
|
2012
|
-
async queryLocked(input) {
|
|
2013
|
-
validateQuery(input);
|
|
2014
|
-
const state = await this.read();
|
|
2015
|
-
const matches = state.bindings.filter((binding2) => queryMatches(input, binding2));
|
|
2016
|
-
if (matches.length === 0) return { status: "no_binding" };
|
|
2017
|
-
const binding = matches[0];
|
|
2018
|
-
if (binding === void 0) throw new TypeError("Host Relay binding query result is invalid.");
|
|
2019
|
-
return { status: "ready", binding: structuredClone(binding) };
|
|
2502
|
+
installation2.last_refusal = { sequence: head.sequence, submission_ref: input.submission_ref, ...refusalCause(outcome, head), refused_at_ms: options.now_ms };
|
|
2503
|
+
refused.push(input.submission_ref);
|
|
2504
|
+
} else {
|
|
2505
|
+
await options.after_gateway_accept_before_delivery_fsync?.({ fault_point: "after_gateway_accept_before_delivery_fsync", delivery: structuredClone(input) });
|
|
2506
|
+
delivered.push(input.submission_ref);
|
|
2507
|
+
}
|
|
2508
|
+
state.entries.splice(state.entries.indexOf(head), 1);
|
|
2509
|
+
installation2.delivered_through_sequence = Math.max(installation2.delivered_through_sequence, head.sequence);
|
|
2510
|
+
await this.write(state);
|
|
2511
|
+
}
|
|
2512
|
+
return { delivered, retained, refused };
|
|
2020
2513
|
}
|
|
2021
2514
|
async read() {
|
|
2022
|
-
let handle;
|
|
2023
2515
|
try {
|
|
2024
|
-
|
|
2025
|
-
const stats = await handle.stat();
|
|
2026
|
-
if (!stats.isFile() || !ownerPrivate2(stats)) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFacts([{ name: "state_is_file", value: stats.isFile(), expected: "true" }, { name: "state_uid", value: stats.uid, expected: String(process.geteuid?.() ?? stats.uid) }, { name: "state_mode", value: (stats.mode & 511).toString(8), expected: "no group or other permission bits" }])}.`);
|
|
2027
|
-
if (stats.size > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFact("state_bytes", stats.size, `at most ${MAX_STATE_BYTES}`)}.`);
|
|
2028
|
-
return decodeState3(JSON.parse(await handle.readFile("utf8")));
|
|
2516
|
+
return decodeState3(JSON.parse(await readFile2(join4(this.root, "admission.json"), "utf8")));
|
|
2029
2517
|
} catch (error) {
|
|
2030
|
-
if (
|
|
2518
|
+
if (errorCode6(error) === "ENOENT") return { schema_version: "ceal.host_relay_admission_state.v1", installations: {}, entries: [] };
|
|
2031
2519
|
throw error;
|
|
2032
|
-
} finally {
|
|
2033
|
-
await handle?.close();
|
|
2034
2520
|
}
|
|
2035
2521
|
}
|
|
2036
2522
|
async write(state) {
|
|
2037
|
-
|
|
2038
|
-
if (Buffer.byteLength(encoded, "utf8") > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is too large: ${refusalFact("state_bytes", Buffer.byteLength(encoded, "utf8"), `at most ${MAX_STATE_BYTES}`)}.`);
|
|
2039
|
-
await durableAtomicReplace(join4(this.root, "request-bindings.json"), encoded);
|
|
2523
|
+
await durableAtomicReplace(join4(this.root, "admission.json"), JSON.stringify(state));
|
|
2040
2524
|
}
|
|
2041
2525
|
};
|
|
2042
|
-
function
|
|
2043
|
-
return
|
|
2044
|
-
host_source_ref: binding.host_source_ref,
|
|
2045
|
-
host_session_ref: binding.host_session_ref,
|
|
2046
|
-
host_turn_ref: binding.host_turn_ref,
|
|
2047
|
-
host_input_ref: binding.host_input_ref,
|
|
2048
|
-
prompt_ref: binding.prompt_ref
|
|
2049
|
-
} });
|
|
2050
|
-
}
|
|
2051
|
-
function createRelayRequestWorkContext(input) {
|
|
2052
|
-
if (!record3(input) || !exactKeys3(input, ["request_ref", "host_binding"]) || !safeRef2(input.request_ref) || !record3(input.host_binding)) {
|
|
2053
|
-
throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
|
|
2054
|
-
}
|
|
2055
|
-
const hostBinding = input.host_binding;
|
|
2056
|
-
if (!exactKeys3(hostBinding, ["host_source_ref", "host_session_ref", "host_turn_ref", "host_input_ref", "prompt_ref"]) || ![hostBinding.host_source_ref, hostBinding.host_session_ref, hostBinding.host_turn_ref, hostBinding.host_input_ref, hostBinding.prompt_ref].every(safeRef2)) {
|
|
2057
|
-
throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
|
|
2058
|
-
}
|
|
2059
|
-
const hostSourceRef = requiredRef(hostBinding.host_source_ref);
|
|
2060
|
-
const hostSessionRef = requiredRef(hostBinding.host_session_ref);
|
|
2061
|
-
const hostTurnRef = requiredRef(hostBinding.host_turn_ref);
|
|
2062
|
-
const hostInputRef = requiredRef(hostBinding.host_input_ref);
|
|
2063
|
-
const promptRef = requiredRef(hostBinding.prompt_ref);
|
|
2064
|
-
return Object.freeze({ request_ref: input.request_ref, host_binding: Object.freeze({
|
|
2065
|
-
host_source_ref: hostSourceRef,
|
|
2066
|
-
host_session_ref: hostSessionRef,
|
|
2067
|
-
host_turn_ref: hostTurnRef,
|
|
2068
|
-
host_input_ref: hostInputRef,
|
|
2069
|
-
prompt_ref: promptRef
|
|
2070
|
-
}) });
|
|
2071
|
-
}
|
|
2072
|
-
function bindingFromGateway(input, now) {
|
|
2073
|
-
const payload = input.frame.payload;
|
|
2074
|
-
const candidate = {
|
|
2075
|
-
schema_version: HOST_RELAY_REQUEST_BINDING_SCHEMA,
|
|
2076
|
-
producer_id: input.frame.producer_id,
|
|
2077
|
-
producer_installation_ref: input.frame.producer_installation_ref,
|
|
2078
|
-
local_session_ref: requiredRef(payload.local_session_ref),
|
|
2079
|
-
local_turn_ref: requiredRef(payload.local_turn_ref),
|
|
2080
|
-
local_turn_aliases: optionalRefs2(payload.local_turn_aliases),
|
|
2081
|
-
prompt_ref: requiredRef(input.prompt_ref),
|
|
2082
|
-
request_ref: requiredRef(input.request_ref),
|
|
2083
|
-
bound_at: timestamp2(now.toISOString()),
|
|
2084
|
-
...payload.context_expires_at === void 0 ? {} : { context_expires_at: timestamp2(payload.context_expires_at) },
|
|
2085
|
-
host_source_ref: requiredRef(input.host_binding.host_source_ref),
|
|
2086
|
-
host_session_ref: requiredRef(input.host_binding.host_session_ref),
|
|
2087
|
-
host_turn_ref: requiredRef(input.host_binding.host_turn_ref),
|
|
2088
|
-
host_input_ref: requiredRef(input.host_binding.host_input_ref)
|
|
2089
|
-
};
|
|
2090
|
-
if (input.host_binding.prompt_ref !== input.prompt_ref) throw new HostRelayBindingError("context_identity_mismatch", "Host Relay binding prompt reference does not match the acknowledged submission.");
|
|
2091
|
-
validateBinding(candidate);
|
|
2092
|
-
return candidate;
|
|
2526
|
+
function installationKey(frame) {
|
|
2527
|
+
return `${frame.producer_id}\0${frame.producer_installation_ref}`;
|
|
2093
2528
|
}
|
|
2094
|
-
function
|
|
2095
|
-
if (
|
|
2096
|
-
|
|
2097
|
-
if (
|
|
2098
|
-
const
|
|
2099
|
-
|
|
2100
|
-
|
|
2529
|
+
function pendingFrameMatchesBindingQuery(entry2, query) {
|
|
2530
|
+
if (entry2.frame.frame_kind !== "user_input") return false;
|
|
2531
|
+
const payload = entry2.frame.payload;
|
|
2532
|
+
if (typeof payload.local_session_ref !== "string" || typeof payload.local_turn_ref !== "string") return false;
|
|
2533
|
+
const aliases = Array.isArray(payload.local_turn_aliases) ? payload.local_turn_aliases.filter((value) => typeof value === "string") : [];
|
|
2534
|
+
return relayRequestBindingMatchesQuery(query, {
|
|
2535
|
+
producer_id: entry2.frame.producer_id,
|
|
2536
|
+
producer_installation_ref: entry2.frame.producer_installation_ref,
|
|
2537
|
+
local_session_ref: payload.local_session_ref,
|
|
2538
|
+
local_turn_ref: payload.local_turn_ref,
|
|
2539
|
+
local_turn_aliases: aliases
|
|
2101
2540
|
});
|
|
2102
|
-
const keys = bindings.map(bindingKey);
|
|
2103
|
-
if (new Set(keys).size !== keys.length) throw new TypeError("Host Relay binding state contains duplicate canonical turns.");
|
|
2104
|
-
return { schema_version: STATE_SCHEMA, bindings };
|
|
2105
2541
|
}
|
|
2106
|
-
function
|
|
2107
|
-
if (
|
|
2542
|
+
function replayCode(existing, hash, sequence, installation2) {
|
|
2543
|
+
if (existing !== void 0) return existing.hash === hash ? "already_admitted" : "frame_identity_conflict";
|
|
2544
|
+
return sequence <= installation2.delivered_through_sequence ? "already_admitted" : null;
|
|
2108
2545
|
}
|
|
2109
|
-
function
|
|
2110
|
-
return
|
|
2546
|
+
function bytes(entries) {
|
|
2547
|
+
return entries.reduce((total, entry2) => total + entry2.charged_bytes, 0);
|
|
2111
2548
|
}
|
|
2112
|
-
function
|
|
2113
|
-
|
|
2114
|
-
return safeRef2(value.prompt_ref) && safeRef2(value.request_ref) && validTimestamp(value.bound_at) && (value.context_expires_at === void 0 || validTimestamp(value.context_expires_at)) && tuple.every(safeRef2);
|
|
2549
|
+
function retryExhausted(outcome, entry2) {
|
|
2550
|
+
return outcome.status === "retryable" && entry2.retry_attempts >= MAX_RETRYABLE_ATTEMPTS;
|
|
2115
2551
|
}
|
|
2116
|
-
function
|
|
2117
|
-
if (
|
|
2552
|
+
function refusalCause(outcome, entry2) {
|
|
2553
|
+
if (outcome.status === "refused") return { code: outcome.code, message: outcome.message };
|
|
2554
|
+
const reason = outcome.status === "retryable" ? outcome.reason ?? "no reason reported" : "no reason reported";
|
|
2555
|
+
return { code: "retry_exhausted", message: `Gateway delivery stayed retryable through ${entry2.retry_attempts} attempts: ${reason}` };
|
|
2118
2556
|
}
|
|
2119
|
-
function
|
|
2120
|
-
|
|
2557
|
+
function scheduleRetry(entry2, nowMs, reason) {
|
|
2558
|
+
if (reason === void 0) delete entry2.retry_reason;
|
|
2559
|
+
else entry2.retry_reason = reason;
|
|
2560
|
+
entry2.retry_attempts += 1;
|
|
2561
|
+
entry2.retry_at_ms = nowMs + 1e3 * (RETRY_SECONDS[Math.min(entry2.retry_attempts - 1, RETRY_SECONDS.length - 1)] ?? 60);
|
|
2121
2562
|
}
|
|
2122
|
-
function
|
|
2123
|
-
return
|
|
2563
|
+
function result(code, state) {
|
|
2564
|
+
return { code, admitted_through_sequence: state.admitted_through_sequence, delivered_through_sequence: state.delivered_through_sequence };
|
|
2124
2565
|
}
|
|
2125
|
-
function
|
|
2126
|
-
return
|
|
2566
|
+
function errorCode6(error) {
|
|
2567
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
2127
2568
|
}
|
|
2128
|
-
function
|
|
2129
|
-
|
|
2569
|
+
function decodeState3(value) {
|
|
2570
|
+
if (!stateRecord(value)) throw new TypeError("Host Relay admission state is invalid.");
|
|
2571
|
+
const installations = {};
|
|
2572
|
+
for (const [key, item] of Object.entries(value.installations)) {
|
|
2573
|
+
if (!installation(item)) throw new TypeError("Host Relay installation state is invalid.");
|
|
2574
|
+
installations[key] = item;
|
|
2575
|
+
}
|
|
2576
|
+
const entries = [];
|
|
2577
|
+
for (const item of value.entries) {
|
|
2578
|
+
if (!entry(item)) throw new TypeError("Host Relay entry state is invalid.");
|
|
2579
|
+
entries.push(item);
|
|
2580
|
+
}
|
|
2581
|
+
return { schema_version: "ceal.host_relay_admission_state.v1", installations, entries };
|
|
2130
2582
|
}
|
|
2131
|
-
function
|
|
2132
|
-
|
|
2133
|
-
return value;
|
|
2583
|
+
function stateRecord(value) {
|
|
2584
|
+
return object(value) && value.schema_version === "ceal.host_relay_admission_state.v1" && object(value.installations) && Array.isArray(value.entries);
|
|
2134
2585
|
}
|
|
2135
|
-
function
|
|
2136
|
-
|
|
2137
|
-
if (!refList(value)) throw new TypeError("Host Relay binding aliases are invalid.");
|
|
2138
|
-
return [...value];
|
|
2586
|
+
function installation(value) {
|
|
2587
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && "epoch" in value && "admitted_through_sequence" in value && "delivered_through_sequence" in value && [value.epoch, value.admitted_through_sequence, value.delivered_through_sequence].every(Number.isSafeInteger) && (!("last_refusal" in value) || refusalRecord(value.last_refusal));
|
|
2139
2588
|
}
|
|
2140
|
-
function
|
|
2141
|
-
|
|
2142
|
-
|
|
2589
|
+
function refusalRecord(value) {
|
|
2590
|
+
return object(value) && Number.isSafeInteger(value.sequence) && typeof value.submission_ref === "string" && typeof value.code === "string" && typeof value.message === "string" && Number.isSafeInteger(value.refused_at_ms);
|
|
2591
|
+
}
|
|
2592
|
+
function entry(value) {
|
|
2593
|
+
return object(value) && producer2(value.producer_id) && typeof value.producer_installation_ref === "string" && Number.isSafeInteger(value.sequence) && typeof value.hash === "string" && Number.isSafeInteger(value.charged_bytes) && object(value.frame) && Number.isSafeInteger(value.retry_attempts) && Number.isSafeInteger(value.retry_at_ms);
|
|
2143
2594
|
}
|
|
2144
2595
|
function producer2(value) {
|
|
2145
2596
|
return value === "codex" || value === "claude";
|
|
2146
2597
|
}
|
|
2147
|
-
function
|
|
2148
|
-
return typeof value === "
|
|
2149
|
-
}
|
|
2150
|
-
function refList(value) {
|
|
2151
|
-
return Array.isArray(value) && value.length <= 16 && value.every(safeRef2) && new Set(value).size === value.length;
|
|
2152
|
-
}
|
|
2153
|
-
function validTimestamp(value) {
|
|
2154
|
-
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
2155
|
-
}
|
|
2156
|
-
function record3(value) {
|
|
2157
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2158
|
-
}
|
|
2159
|
-
function exactKeys3(value, keys) {
|
|
2160
|
-
return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
2161
|
-
}
|
|
2162
|
-
function exactOptionalKeys(value, required, optional) {
|
|
2163
|
-
const keys = Object.keys(value);
|
|
2164
|
-
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2165
|
-
}
|
|
2166
|
-
function errorCode5(error) {
|
|
2167
|
-
return record3(error) && typeof error.code === "string" ? error.code : null;
|
|
2168
|
-
}
|
|
2169
|
-
function ownerPrivate2(stats) {
|
|
2170
|
-
if (process.platform === "win32") return true;
|
|
2171
|
-
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
2172
|
-
return stats.uid === uid && (stats.mode & 63) === 0;
|
|
2598
|
+
function object(value) {
|
|
2599
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2173
2600
|
}
|
|
2174
2601
|
|
|
2175
2602
|
// host/relay/src/relay-gateway-bridge.ts
|
|
2603
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
2604
|
+
import { join as join5 } from "node:path";
|
|
2176
2605
|
var STATE_SCHEMA2 = "ceal.host_relay_gateway_state.v1";
|
|
2177
2606
|
var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2178
2607
|
var HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS = ["created", "adopted", "replayed", "approval_approved", "approval_denied", "approval_replayed"];
|
|
@@ -2292,7 +2721,7 @@ var RelayGatewayBridge = class {
|
|
|
2292
2721
|
if (state.host_source_ref !== this.hostSourceRef) throw new TypeError(`Host Relay Gateway source binding changed: ${refusalFacts([{ name: "stored_host_source_ref", value: state.host_source_ref }, { name: "configured_host_source_ref", value: this.hostSourceRef }])}.`);
|
|
2293
2722
|
return state;
|
|
2294
2723
|
} catch (error) {
|
|
2295
|
-
if (
|
|
2724
|
+
if (errorCode7(error) === "ENOENT") return { schema_version: STATE_SCHEMA2, host_source_ref: this.hostSourceRef, sessions: {}, turns: {} };
|
|
2296
2725
|
throw error;
|
|
2297
2726
|
}
|
|
2298
2727
|
}
|
|
@@ -2303,7 +2732,7 @@ var RelayGatewayBridge = class {
|
|
|
2303
2732
|
var RETRYABLE_HTTP_STATUSES = /* @__PURE__ */ new Set([401, 403, 408, 429]);
|
|
2304
2733
|
function submissionRefusal(error) {
|
|
2305
2734
|
const status = isRecord(error) && typeof error.status === "number" ? error.status : void 0;
|
|
2306
|
-
const code =
|
|
2735
|
+
const code = errorCode7(error) ?? "gateway_unavailable";
|
|
2307
2736
|
const definitive = code === "payload_rejected" || status !== void 0 && status >= 400 && status < 500 && !RETRYABLE_HTTP_STATUSES.has(status);
|
|
2308
2737
|
return { code, message: errorMessage(error), definitive };
|
|
2309
2738
|
}
|
|
@@ -2384,7 +2813,7 @@ function isRecord(value) {
|
|
|
2384
2813
|
function validOptionalTimeout(value) {
|
|
2385
2814
|
return value === void 0 || Number.isSafeInteger(value) && Number(value) >= 1 && Number(value) <= 6e4;
|
|
2386
2815
|
}
|
|
2387
|
-
function
|
|
2816
|
+
function errorCode7(error) {
|
|
2388
2817
|
return isRecord(error) && typeof error.code === "string" ? error.code : null;
|
|
2389
2818
|
}
|
|
2390
2819
|
function decodeState4(value) {
|
|
@@ -2428,7 +2857,7 @@ function isInputRefusal(value) {
|
|
|
2428
2857
|
|
|
2429
2858
|
// host/relay/src/relay-gateway-http.ts
|
|
2430
2859
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
|
|
2431
|
-
import { lstat as lstat2, open as
|
|
2860
|
+
import { lstat as lstat2, open as open4 } from "node:fs/promises";
|
|
2432
2861
|
import { dirname as dirname2 } from "node:path";
|
|
2433
2862
|
|
|
2434
2863
|
// host/gateway-http.ts
|
|
@@ -2478,6 +2907,7 @@ async function submitHostIngress(options) {
|
|
|
2478
2907
|
}
|
|
2479
2908
|
|
|
2480
2909
|
// host/relay/src/relay-gateway-http.ts
|
|
2910
|
+
var HOST_REGISTRATION_STATE_SCHEMA = "ceal.host_registration_state.v1";
|
|
2481
2911
|
var HOST_REGISTRATION_STATE_KEYS = Object.freeze([
|
|
2482
2912
|
"credential_generation",
|
|
2483
2913
|
"gateway_request_origin",
|
|
@@ -2488,19 +2918,30 @@ var HOST_REGISTRATION_STATE_KEYS = Object.freeze([
|
|
|
2488
2918
|
"schema_version",
|
|
2489
2919
|
"status"
|
|
2490
2920
|
]);
|
|
2491
|
-
var HOST_INGRESS_PATH = "/api/ceal/v2/host-ingress";
|
|
2492
2921
|
var HOST_REGISTRATION_STATE_MAX_BYTES = 65536;
|
|
2493
2922
|
var HostRelayGatewayError = class extends Error {
|
|
2494
2923
|
name = "HostRelayGatewayError";
|
|
2495
2924
|
code;
|
|
2496
2925
|
/** HTTP status of a Gateway refusal, when one answered. */
|
|
2497
2926
|
status;
|
|
2498
|
-
|
|
2927
|
+
/** The Gateway's own refusal word, when it named one; a bounded token, never its message. */
|
|
2928
|
+
error_code;
|
|
2929
|
+
constructor(code, message, status, errorCode9) {
|
|
2499
2930
|
super(message);
|
|
2500
2931
|
this.code = code;
|
|
2501
2932
|
if (status !== void 0) this.status = status;
|
|
2933
|
+
if (errorCode9 !== void 0) this.error_code = errorCode9;
|
|
2502
2934
|
}
|
|
2503
2935
|
};
|
|
2936
|
+
var GATEWAY_REFUSAL_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
2937
|
+
async function gatewayRefusalCode(response2) {
|
|
2938
|
+
try {
|
|
2939
|
+
const body = await response2.json();
|
|
2940
|
+
return isCealJsonRecord(body) && typeof body.error_code === "string" && GATEWAY_REFUSAL_CODE_PATTERN.test(body.error_code) ? body.error_code : void 0;
|
|
2941
|
+
} catch {
|
|
2942
|
+
return void 0;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2504
2945
|
function refusalMessage(status, body) {
|
|
2505
2946
|
const record6 = isCealJsonRecord(body) ? body : {};
|
|
2506
2947
|
const code = typeof record6.error_code === "string" ? record6.error_code : "unknown";
|
|
@@ -2546,7 +2987,7 @@ function createHostRelayGatewayHttpPort(options) {
|
|
|
2546
2987
|
} catch {
|
|
2547
2988
|
throw new HostRelayGatewayError("gateway_unavailable", "Gateway Host invocation context is unavailable.");
|
|
2548
2989
|
}
|
|
2549
|
-
if (!response2.ok) throw new HostRelayGatewayError(response2.status === 401 ? "credential" : "gateway_refused", `Gateway Host invocation context was refused (${response2.status}).`, response2.status);
|
|
2990
|
+
if (!response2.ok) throw new HostRelayGatewayError(response2.status === 401 ? "credential" : "gateway_refused", `Gateway Host invocation context was refused (${response2.status}).`, response2.status, await gatewayRefusalCode(response2));
|
|
2550
2991
|
let value;
|
|
2551
2992
|
try {
|
|
2552
2993
|
value = await response2.json();
|
|
@@ -2562,7 +3003,7 @@ function createHostRelayGatewayHttpPort(options) {
|
|
|
2562
3003
|
});
|
|
2563
3004
|
}
|
|
2564
3005
|
function hostRelayGatewayEndpoint(value) {
|
|
2565
|
-
return hostRelayGatewayRoute(value,
|
|
3006
|
+
return hostRelayGatewayRoute(value, CEAL_HOST_INGRESS_PATH);
|
|
2566
3007
|
}
|
|
2567
3008
|
function hostRelayGatewayRoute(value, path) {
|
|
2568
3009
|
let parsed;
|
|
@@ -2602,7 +3043,7 @@ async function readHostRegistrationAuthority(path, expectedHostSourceRef) {
|
|
|
2602
3043
|
let handle;
|
|
2603
3044
|
try {
|
|
2604
3045
|
await assertOwnerPrivateDirectory(dirname2(path));
|
|
2605
|
-
handle = await
|
|
3046
|
+
handle = await open4(path, O_RDONLY3 | O_NOFOLLOW2);
|
|
2606
3047
|
const stats = await handle.stat();
|
|
2607
3048
|
if (!stats.isFile() || !ownerPrivate3(stats)) throw new HostRelayGatewayError("credential", "Host Relay credential file is not owner-private.");
|
|
2608
3049
|
const raw = await handle.readFile("utf8");
|
|
@@ -2632,13 +3073,13 @@ function decodeHostRegistrationAuthority(value, expectedHostSourceRef) {
|
|
|
2632
3073
|
return { gateway_request_origin: value.gateway_request_origin, host_access_token: value.host_access_token };
|
|
2633
3074
|
}
|
|
2634
3075
|
function validRegistrationIdentity(value, expectedHostSourceRef) {
|
|
2635
|
-
return value.schema_version ===
|
|
3076
|
+
return value.schema_version === HOST_REGISTRATION_STATE_SCHEMA && value.status === "active" && value.host_source_ref === expectedHostSourceRef && typeof value.registration_attempt_ref === "string" && CEAL_HOST_REGISTRATION_ATTEMPT_REF_PATTERN.test(value.registration_attempt_ref) && typeof value.host_installation_ref === "string" && CEAL_HOST_INSTALLATION_REF_PATTERN.test(value.host_installation_ref) && typeof value.host_source_ref === "string" && hostSourceUuid(value.host_source_ref) !== null;
|
|
2636
3077
|
}
|
|
2637
3078
|
function validRegistrationCredential(value) {
|
|
2638
3079
|
return Number.isSafeInteger(value.credential_generation) && Number(value.credential_generation) > 0 && validHostAccessToken(value.host_access_token);
|
|
2639
3080
|
}
|
|
2640
3081
|
function validHostAccessToken(value) {
|
|
2641
|
-
return typeof value === "string" &&
|
|
3082
|
+
return typeof value === "string" && CEAL_HOST_ACCESS_TOKEN_PATTERN.test(value);
|
|
2642
3083
|
}
|
|
2643
3084
|
function validGatewayOrigin(value) {
|
|
2644
3085
|
try {
|
|
@@ -2671,7 +3112,7 @@ import { chmod, lstat as lstat3, unlink as unlink2 } from "node:fs/promises";
|
|
|
2671
3112
|
import { createConnection, createServer } from "node:net";
|
|
2672
3113
|
|
|
2673
3114
|
// host/relay/src/current-prompt-custody.ts
|
|
2674
|
-
import { createHash as
|
|
3115
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
2675
3116
|
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
2676
3117
|
var MAX_CURRENT_PROMPTS = 1024;
|
|
2677
3118
|
var HostCurrentPromptCustody = class {
|
|
@@ -2685,7 +3126,7 @@ var HostCurrentPromptCustody = class {
|
|
|
2685
3126
|
const bytes2 = Buffer.byteLength(prompt, "utf8");
|
|
2686
3127
|
const expiry = Date.parse(expiresAt);
|
|
2687
3128
|
if (bytes2 === 0 || bytes2 > HOST_CURRENT_PROMPT_MAX_BYTES || !Number.isFinite(expiry) || expiry <= this.#now()) throw new TypeError("current_prompt_unavailable");
|
|
2688
|
-
if (hostProducerPromptRef(
|
|
3129
|
+
if (hostProducerPromptRef(createHash5("sha256").update(prompt, "utf8").digest("hex")) !== identity.prompt_ref) throw new TypeError("current_prompt_unavailable");
|
|
2689
3130
|
this.#purge();
|
|
2690
3131
|
const key = promptKey(identity);
|
|
2691
3132
|
if (this.#consumed.has(key)) return;
|
|
@@ -2738,11 +3179,46 @@ var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_co
|
|
|
2738
3179
|
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
2739
3180
|
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
2740
3181
|
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
3182
|
+
var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
3183
|
+
"context_stale",
|
|
3184
|
+
"context_expired",
|
|
3185
|
+
"host_source_refused",
|
|
3186
|
+
"host_credential_refused",
|
|
3187
|
+
"gateway_unavailable",
|
|
3188
|
+
"gateway_refused",
|
|
3189
|
+
"gateway_response_invalid",
|
|
3190
|
+
"deadline",
|
|
3191
|
+
"cancelled",
|
|
3192
|
+
"mint_failed",
|
|
3193
|
+
"relay_failed"
|
|
3194
|
+
]);
|
|
3195
|
+
var HostRelayInvocationMintRefusal = class extends Error {
|
|
3196
|
+
reason;
|
|
3197
|
+
constructor(reason) {
|
|
3198
|
+
super(reason);
|
|
3199
|
+
this.name = "HostRelayInvocationMintRefusal";
|
|
3200
|
+
this.reason = reason;
|
|
3201
|
+
}
|
|
3202
|
+
};
|
|
3203
|
+
var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
|
|
3204
|
+
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
3205
|
+
"permission_denied",
|
|
3206
|
+
"socket_missing",
|
|
3207
|
+
"connection_refused",
|
|
3208
|
+
"closed_early",
|
|
3209
|
+
"response_invalid",
|
|
3210
|
+
"timed_out",
|
|
3211
|
+
"cancelled",
|
|
3212
|
+
"unavailable"
|
|
3213
|
+
]);
|
|
2741
3214
|
var HostRelayIpcError = class extends Error {
|
|
2742
3215
|
code;
|
|
2743
|
-
|
|
3216
|
+
/** Set only when the client-side socket exchange itself failed. */
|
|
3217
|
+
failure;
|
|
3218
|
+
constructor(code, message = code, failure) {
|
|
2744
3219
|
super(message);
|
|
2745
3220
|
this.code = code;
|
|
3221
|
+
this.failure = failure;
|
|
2746
3222
|
}
|
|
2747
3223
|
};
|
|
2748
3224
|
async function startHostRelayIpcServer(options) {
|
|
@@ -2751,7 +3227,7 @@ async function startHostRelayIpcServer(options) {
|
|
|
2751
3227
|
const sockets = /* @__PURE__ */ new Set();
|
|
2752
3228
|
const quarantine = options.quarantine ?? options.admission.quarantine;
|
|
2753
3229
|
const currentPrompts = options.currentPrompts ?? new HostCurrentPromptCustody();
|
|
2754
|
-
const server = createServer((socket) => serveSocket(socket, options
|
|
3230
|
+
const server = createServer((socket) => serveSocket(socket, options, currentPrompts, quarantine, active, sockets));
|
|
2755
3231
|
await listen(server, options.endpoint);
|
|
2756
3232
|
if (process.platform !== "win32") await chmod(options.endpoint, 384);
|
|
2757
3233
|
return { endpoint: options.endpoint, close: () => closeServer(server, sockets, options.endpoint) };
|
|
@@ -2763,12 +3239,12 @@ function encodeHostRelayIpcResponse(response2) {
|
|
|
2763
3239
|
body.copy(result2, HOST_RELAY_IPC_LENGTH_BYTES);
|
|
2764
3240
|
return result2;
|
|
2765
3241
|
}
|
|
2766
|
-
function serveSocket(socket,
|
|
3242
|
+
function serveSocket(socket, options, currentPrompts, quarantine, active, sockets) {
|
|
2767
3243
|
const state = { buffer: Buffer.alloc(0), closed: false, tail: Promise.resolve(), abort: new AbortController() };
|
|
2768
3244
|
sockets.add(socket);
|
|
2769
3245
|
socket.on("data", (chunk) => {
|
|
2770
3246
|
state.buffer = Buffer.concat([state.buffer, Buffer.from(chunk)]);
|
|
2771
|
-
state.tail = state.tail.then(() => drainFrames(socket, state,
|
|
3247
|
+
state.tail = state.tail.then(() => drainFrames(socket, state, options, currentPrompts, quarantine, active)).catch(() => {
|
|
2772
3248
|
writeResponse(socket, errorResponse("relay_unavailable"));
|
|
2773
3249
|
socket.end();
|
|
2774
3250
|
});
|
|
@@ -2786,11 +3262,11 @@ function serveSocket(socket, admission, bindings, currentPrompts, mintInvocation
|
|
|
2786
3262
|
else socket.end();
|
|
2787
3263
|
});
|
|
2788
3264
|
}
|
|
2789
|
-
async function drainFrames(socket, state,
|
|
2790
|
-
while (!state.closed && state.buffer.length >= HOST_RELAY_IPC_LENGTH_BYTES && await processBufferedRequest(socket, state,
|
|
3265
|
+
async function drainFrames(socket, state, options, currentPrompts, quarantine, active) {
|
|
3266
|
+
while (!state.closed && state.buffer.length >= HOST_RELAY_IPC_LENGTH_BYTES && await processBufferedRequest(socket, state, options, currentPrompts, quarantine, active)) {
|
|
2791
3267
|
}
|
|
2792
3268
|
}
|
|
2793
|
-
async function processBufferedRequest(socket, state,
|
|
3269
|
+
async function processBufferedRequest(socket, state, options, currentPrompts, quarantine, active) {
|
|
2794
3270
|
const length = state.buffer.readUInt32BE(0);
|
|
2795
3271
|
const promptWire = currentPromptWire(state.buffer);
|
|
2796
3272
|
const maximum = promptWire ? HOST_CURRENT_PROMPT_MAX_BYTES + 4096 : HOST_PRODUCER_MAX_JSON_BYTES;
|
|
@@ -2819,8 +3295,13 @@ async function processBufferedRequest(socket, state, admission, bindings, curren
|
|
|
2819
3295
|
}
|
|
2820
3296
|
return true;
|
|
2821
3297
|
}
|
|
2822
|
-
|
|
2823
|
-
|
|
3298
|
+
const answered = await processHostQuery(socket, request2, options, currentPrompts, state.abort.signal);
|
|
3299
|
+
if (answered === "closed") {
|
|
3300
|
+
socket.end();
|
|
3301
|
+
return false;
|
|
3302
|
+
}
|
|
3303
|
+
if (answered === "answered") return true;
|
|
3304
|
+
await processProducerRequest(socket, state, request2, encoded, options.admission, quarantine, active);
|
|
2824
3305
|
return true;
|
|
2825
3306
|
}
|
|
2826
3307
|
async function refuseInvalidBufferedRequest(socket, rawBuffer, length, promptWire, quarantine) {
|
|
@@ -2852,16 +3333,29 @@ async function processProducerRequest(socket, state, request2, encoded, admissio
|
|
|
2852
3333
|
}
|
|
2853
3334
|
writeResponse(socket, admissionResponse(await admission.admit(request2)));
|
|
2854
3335
|
}
|
|
2855
|
-
async function processHostQuery(socket, request2,
|
|
3336
|
+
async function processHostQuery(socket, request2, options, currentPrompts, signal) {
|
|
2856
3337
|
if (request2.schema_version === HOST_RELAY_BINDING_QUERY_SCHEMA) {
|
|
2857
|
-
|
|
2858
|
-
|
|
3338
|
+
let result2;
|
|
3339
|
+
try {
|
|
3340
|
+
result2 = await options.bindings.query(bindingQueryFromRequest(request2));
|
|
3341
|
+
} catch {
|
|
3342
|
+
return "closed";
|
|
3343
|
+
}
|
|
3344
|
+
writeBindingResponse(socket, result2);
|
|
3345
|
+
return "answered";
|
|
2859
3346
|
}
|
|
2860
3347
|
if (request2.schema_version === HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) {
|
|
2861
|
-
|
|
2862
|
-
|
|
3348
|
+
let response2;
|
|
3349
|
+
const resolve = (invocationSignal) => resolveInvocationContext(request2, options.admission, options.bindings, currentPrompts, options.mintInvocationContext, invocationSignal);
|
|
3350
|
+
try {
|
|
3351
|
+
response2 = options.invocationMintTimeoutMs === void 0 && options.scheduleInvocationMintDeadline === void 0 && options.observeInvocationMint === void 0 ? await resolve(signal) : await observeInvocationMint(resolve, signal, options.invocationMintTimeoutMs, options.scheduleInvocationMintDeadline, options.observeInvocationMint);
|
|
3352
|
+
} catch {
|
|
3353
|
+
response2 = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: "relay_failed" };
|
|
3354
|
+
}
|
|
3355
|
+
writeInvocationContextResponse(socket, response2);
|
|
3356
|
+
return "answered";
|
|
2863
3357
|
}
|
|
2864
|
-
return
|
|
3358
|
+
return "not_a_query";
|
|
2865
3359
|
}
|
|
2866
3360
|
function claimProducer(socket, state, frame, active) {
|
|
2867
3361
|
const key = producerKey(frame);
|
|
@@ -2895,14 +3389,93 @@ function writeInvocationContextResponse(socket, response2) {
|
|
|
2895
3389
|
function writeCurrentPromptResponse(socket, status) {
|
|
2896
3390
|
if (!socket.destroyed) socket.write(encodeLengthPrefixed({ schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status }));
|
|
2897
3391
|
}
|
|
2898
|
-
async function resolveInvocationContext(request2, bindings, currentPrompts, mint, signal) {
|
|
3392
|
+
async function resolveInvocationContext(request2, admission, bindings, currentPrompts, mint, signal) {
|
|
2899
3393
|
if (mint === void 0) throw new HostRelayIpcError("relay_unavailable");
|
|
2900
|
-
const
|
|
2901
|
-
|
|
3394
|
+
const query = bindingQueryFromRequest(request2);
|
|
3395
|
+
const binding = await waitForRequestBinding(query, admission, bindings, signal);
|
|
3396
|
+
if (binding.status !== "ready") return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "no_binding" };
|
|
2902
3397
|
const currentPrompt = currentPrompts.take({ producer_id: request2.producer_id, producer_installation_ref: request2.producer_installation_ref, local_session_ref: request2.local_session_ref, local_turn_ref: request2.local_turn_ref, prompt_ref: binding.binding.prompt_ref });
|
|
2903
|
-
|
|
3398
|
+
let result2;
|
|
3399
|
+
try {
|
|
3400
|
+
result2 = await mint({ client_nonce: request2.client_nonce, context_expires_at: binding.binding.context_expires_at ?? binding.binding.bound_at, request_work_context: relayRequestWorkContextFromBinding(binding.binding), ...currentPrompt === void 0 ? {} : { current_prompt: currentPrompt } }, signal);
|
|
3401
|
+
} catch (error) {
|
|
3402
|
+
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: error instanceof HostRelayInvocationMintRefusal ? error.reason : "mint_failed" };
|
|
3403
|
+
}
|
|
2904
3404
|
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "ready", result: result2 };
|
|
2905
3405
|
}
|
|
3406
|
+
async function waitForRequestBinding(query, admission, bindings, signal) {
|
|
3407
|
+
let binding = await bindings.query(query);
|
|
3408
|
+
if (binding.status === "ready") return binding;
|
|
3409
|
+
while (await admission.hasPendingRequestBinding(query)) {
|
|
3410
|
+
const cycle = new AbortController();
|
|
3411
|
+
const cancelCycle = () => cycle.abort(signal.reason);
|
|
3412
|
+
if (signal.aborted) cancelCycle();
|
|
3413
|
+
else signal.addEventListener("abort", cancelCycle, { once: true });
|
|
3414
|
+
try {
|
|
3415
|
+
const changed = Promise.race([
|
|
3416
|
+
bindings.waitForBinding(query, cycle.signal).then((result3) => ({ kind: "bound", result: result3 })),
|
|
3417
|
+
admission.waitForDrainChange(cycle.signal).then(() => ({ kind: "drained" }))
|
|
3418
|
+
]).then((result3) => result3, (error) => ({ kind: "cancelled", error }));
|
|
3419
|
+
binding = await bindings.query(query);
|
|
3420
|
+
if (binding.status === "ready") return binding;
|
|
3421
|
+
if (!await admission.hasPendingRequestBinding(query)) return await bindings.query(query);
|
|
3422
|
+
const result2 = await changed;
|
|
3423
|
+
if (result2.kind === "cancelled") throw result2.error;
|
|
3424
|
+
} finally {
|
|
3425
|
+
signal.removeEventListener("abort", cancelCycle);
|
|
3426
|
+
cycle.abort("wait_cycle_complete");
|
|
3427
|
+
}
|
|
3428
|
+
binding = await bindings.query(query);
|
|
3429
|
+
if (binding.status === "ready") return binding;
|
|
3430
|
+
}
|
|
3431
|
+
return await bindings.query(query);
|
|
3432
|
+
}
|
|
3433
|
+
async function observeInvocationMint(resolve, callerSignal, timeoutMs, scheduleDeadline, observe) {
|
|
3434
|
+
const started = process.hrtime.bigint();
|
|
3435
|
+
const controller2 = new AbortController();
|
|
3436
|
+
let cancellation = null;
|
|
3437
|
+
let outcome = "failed";
|
|
3438
|
+
let reason;
|
|
3439
|
+
const cancel = (cause) => {
|
|
3440
|
+
if (cancellation === null) {
|
|
3441
|
+
cancellation = cause;
|
|
3442
|
+
controller2.abort(cause);
|
|
3443
|
+
}
|
|
3444
|
+
};
|
|
3445
|
+
const callerCancelled = () => cancel("caller_cancelled");
|
|
3446
|
+
const cancelled = new Promise((_resolve, reject) => controller2.signal.addEventListener("abort", () => reject(new Error("invocation mint cancelled")), { once: true }));
|
|
3447
|
+
const clearDeadline = timeoutMs === void 0 ? () => void 0 : (scheduleDeadline ?? scheduleInvocationMintDeadline)(() => cancel("deadline"), timeoutMs);
|
|
3448
|
+
if (callerSignal.aborted) callerCancelled();
|
|
3449
|
+
else callerSignal.addEventListener("abort", callerCancelled, { once: true });
|
|
3450
|
+
try {
|
|
3451
|
+
const response2 = await Promise.race([resolve(controller2.signal), cancelled]);
|
|
3452
|
+
({ outcome, reason } = invocationMintObservationForResponse(response2));
|
|
3453
|
+
return response2;
|
|
3454
|
+
} catch {
|
|
3455
|
+
({ outcome, reason } = invocationMintObservationForCancellation(cancellation));
|
|
3456
|
+
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: reason ?? "relay_failed" };
|
|
3457
|
+
} finally {
|
|
3458
|
+
clearDeadline();
|
|
3459
|
+
callerSignal.removeEventListener("abort", callerCancelled);
|
|
3460
|
+
observe?.({ outcome, elapsed_ms: Number((process.hrtime.bigint() - started) / 1000000n), ...reason === void 0 ? {} : { reason } });
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
function invocationMintObservationForResponse(response2) {
|
|
3464
|
+
if (response2.status === "no_binding") return { outcome: "no_binding" };
|
|
3465
|
+
if (response2.status === "ready") return { outcome: "completed" };
|
|
3466
|
+
if (response2.reason === "deadline") return { outcome: "deadline", reason: "deadline" };
|
|
3467
|
+
if (response2.reason === "cancelled") return { outcome: "caller_cancelled", reason: "cancelled" };
|
|
3468
|
+
return { outcome: "failed", reason: response2.reason };
|
|
3469
|
+
}
|
|
3470
|
+
function invocationMintObservationForCancellation(cancellation) {
|
|
3471
|
+
if (cancellation === "deadline") return { outcome: "deadline", reason: "deadline" };
|
|
3472
|
+
if (cancellation === "caller_cancelled") return { outcome: "caller_cancelled", reason: "cancelled" };
|
|
3473
|
+
return { outcome: "failed", reason: "relay_failed" };
|
|
3474
|
+
}
|
|
3475
|
+
function scheduleInvocationMintDeadline(expire, milliseconds) {
|
|
3476
|
+
const timeout = setTimeout(expire, milliseconds);
|
|
3477
|
+
return () => clearTimeout(timeout);
|
|
3478
|
+
}
|
|
2906
3479
|
function admissionResponse(result2) {
|
|
2907
3480
|
return { schema_version: HOST_RELAY_IPC_RESPONSE_SCHEMA, ...result2 };
|
|
2908
3481
|
}
|
|
@@ -2988,7 +3561,7 @@ async function assertEndpointVacant(endpoint) {
|
|
|
2988
3561
|
await lstat3(endpoint);
|
|
2989
3562
|
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is already present.");
|
|
2990
3563
|
} catch (error) {
|
|
2991
|
-
if (
|
|
3564
|
+
if (errorCode8(error) !== "ENOENT") throw error;
|
|
2992
3565
|
}
|
|
2993
3566
|
}
|
|
2994
3567
|
function listen(server, endpoint) {
|
|
@@ -3005,7 +3578,7 @@ async function closeServer(server, sockets, endpoint) {
|
|
|
3005
3578
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
3006
3579
|
if (process.platform !== "win32") await unlink2(endpoint).catch(() => void 0);
|
|
3007
3580
|
}
|
|
3008
|
-
function
|
|
3581
|
+
function errorCode8(error) {
|
|
3009
3582
|
return record4(error) && typeof error.code === "string" ? error.code : null;
|
|
3010
3583
|
}
|
|
3011
3584
|
|
|
@@ -3027,7 +3600,7 @@ async function readHostRelayConfig(path) {
|
|
|
3027
3600
|
let handle;
|
|
3028
3601
|
try {
|
|
3029
3602
|
await assertOwnerPrivateDirectory2(dirname3(path));
|
|
3030
|
-
handle = await
|
|
3603
|
+
handle = await open5(path, O_RDONLY4 | O_NOFOLLOW3);
|
|
3031
3604
|
const stats = await handle.stat();
|
|
3032
3605
|
if (!stats.isFile() || !ownerPrivate4(stats)) throw new HostRelayConfigError("config_not_private", "Host Relay config file is not owner-private.");
|
|
3033
3606
|
if (stats.size < HOST_RELAY_CONFIG_MIN_BYTES || stats.size > HOST_RELAY_CONFIG_MAX_BYTES) throw new HostRelayConfigError("invalid_config", `invalid_config: Host Relay config size is invalid: ${refusalFact("config_bytes", stats.size, `between ${HOST_RELAY_CONFIG_MIN_BYTES} and ${HOST_RELAY_CONFIG_MAX_BYTES}`)}.`);
|
|
@@ -3039,7 +3612,9 @@ async function readHostRelayConfig(path) {
|
|
|
3039
3612
|
} catch {
|
|
3040
3613
|
throw new HostRelayConfigError("invalid_json", "Host Relay config is not JSON.");
|
|
3041
3614
|
}
|
|
3042
|
-
|
|
3615
|
+
const config = decodeHostRelayConfig(value);
|
|
3616
|
+
validateHostRelaySourcePaths(config);
|
|
3617
|
+
return config;
|
|
3043
3618
|
} catch (error) {
|
|
3044
3619
|
if (error instanceof HostRelayConfigError) throw error;
|
|
3045
3620
|
throw new HostRelayConfigError("config_unavailable", "Host Relay config is unavailable.");
|
|
@@ -3047,6 +3622,13 @@ async function readHostRelayConfig(path) {
|
|
|
3047
3622
|
await handle?.close();
|
|
3048
3623
|
}
|
|
3049
3624
|
}
|
|
3625
|
+
function validateHostRelaySourcePaths(config) {
|
|
3626
|
+
try {
|
|
3627
|
+
assertHostRelaySourcePaths(dirname3(config.registration_state_file), config);
|
|
3628
|
+
} catch (error) {
|
|
3629
|
+
throw new HostRelayConfigError("invalid_config", error instanceof Error ? error.message : "Host Relay source tuple is invalid.");
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3050
3632
|
function decodeHostRelayConfig(value) {
|
|
3051
3633
|
const required = ["schema_version", "registration_state_file", "host_source_ref", "state_root", "ipc_endpoint"];
|
|
3052
3634
|
const optional = ["drain_interval_ms", "gateway_timeout_ms"];
|
|
@@ -3069,6 +3651,8 @@ function validConfigIdentity(value) {
|
|
|
3069
3651
|
function validConfigRuntime(value) {
|
|
3070
3652
|
return isAbsoluteNormalizedNonRootPath(value.state_root, process.platform, true) && isAbsoluteNormalizedNonRootPath(value.ipc_endpoint, process.platform, true) && boundedInteger(value.drain_interval_ms, 50, 3e4, true) && boundedInteger(value.gateway_timeout_ms, 100, 6e4, true);
|
|
3071
3653
|
}
|
|
3654
|
+
var HOST_RELAY_INVOCATION_MINT_LOG_TAG = "[host-relay-invocation-mint]";
|
|
3655
|
+
var HOST_RELAY_INVOCATION_MINT_LINE = new RegExp(`^${HOST_RELAY_INVOCATION_MINT_LOG_TAG.replace(/[[\]]/gu, "\\$&")} outcome=([a-z_]+)(?: reason=([a-z][a-z0-9_]{0,63}))? elapsed_ms=[0-9]+$`, "u");
|
|
3072
3656
|
async function serveHostRelay(config, signal, dependencies = {}) {
|
|
3073
3657
|
const admission = new RelayAdmissionStore(join6(config.state_root, "admission"));
|
|
3074
3658
|
const bindings = new RelayRequestBindingStore(join6(config.state_root, "bindings"));
|
|
@@ -3078,7 +3662,7 @@ async function serveHostRelay(config, signal, dependencies = {}) {
|
|
|
3078
3662
|
} });
|
|
3079
3663
|
let server;
|
|
3080
3664
|
try {
|
|
3081
|
-
server = await (
|
|
3665
|
+
server = await startHostRelayRuntimeIpc(config, admission, bindings, gateway, dependencies);
|
|
3082
3666
|
while (!signal.aborted) {
|
|
3083
3667
|
const round = await admission.drainRound((input) => bridge.deliver(input), { now_ms: (dependencies.now ?? Date.now)(), host_source_ref: config.host_source_ref });
|
|
3084
3668
|
if (round.delivered.length === 0 && round.refused.length === 0) await (dependencies.wait ?? ((milliseconds, waitSignal) => waitForAbort(waitSignal, milliseconds)))(config.drain_interval_ms, signal);
|
|
@@ -3087,41 +3671,52 @@ async function serveHostRelay(config, signal, dependencies = {}) {
|
|
|
3087
3671
|
await server?.close();
|
|
3088
3672
|
}
|
|
3089
3673
|
}
|
|
3090
|
-
async function
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
else callerSignal.addEventListener("abort", callerCancelled, { once: true });
|
|
3103
|
-
const cancelled = new Promise((_resolve, reject) => controller2.signal.addEventListener("abort", () => reject(new Error("invocation mint cancelled")), { once: true }));
|
|
3674
|
+
async function startHostRelayRuntimeIpc(config, admission, bindings, gateway, dependencies) {
|
|
3675
|
+
return (dependencies.startIpc ?? startHostRelayIpcServer)({
|
|
3676
|
+
endpoint: config.ipc_endpoint,
|
|
3677
|
+
admission,
|
|
3678
|
+
bindings,
|
|
3679
|
+
mintInvocationContext: (input, invocationSignal) => mintInvocationContext(gateway, input, invocationSignal),
|
|
3680
|
+
invocationMintTimeoutMs: config.gateway_timeout_ms,
|
|
3681
|
+
scheduleInvocationMintDeadline: dependencies.scheduleInvocationMintDeadline ?? scheduleInvocationMintDeadline2,
|
|
3682
|
+
observeInvocationMint: dependencies.observeInvocationMint ?? reportInvocationMint
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3685
|
+
async function mintInvocationContext(gateway, input, signal) {
|
|
3104
3686
|
try {
|
|
3105
|
-
|
|
3106
|
-
outcome = "completed";
|
|
3107
|
-
return result2;
|
|
3687
|
+
return await gateway.mintInvocationContext(input, signal);
|
|
3108
3688
|
} catch (error) {
|
|
3109
|
-
|
|
3110
|
-
throw error;
|
|
3111
|
-
}
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3689
|
+
const cancellation = signal.reason === "deadline" ? "deadline" : signal.reason === "caller_cancelled" ? "caller_cancelled" : null;
|
|
3690
|
+
throw new HostRelayInvocationMintRefusal(invocationMintRefusalReason(error, cancellation));
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
function invocationMintRefusalReason(error, cancellation) {
|
|
3694
|
+
if (cancellation === "deadline") return "deadline";
|
|
3695
|
+
if (cancellation === "caller_cancelled") return "cancelled";
|
|
3696
|
+
if (!(error instanceof HostRelayGatewayError)) return "mint_failed";
|
|
3697
|
+
if (error.code === "credential") return "host_credential_refused";
|
|
3698
|
+
if (error.code === "gateway_unavailable") return "gateway_unavailable";
|
|
3699
|
+
if (error.code === "invalid_response") return "gateway_response_invalid";
|
|
3700
|
+
if (error.code !== "gateway_refused") return "mint_failed";
|
|
3701
|
+
return (error.error_code === void 0 ? void 0 : GATEWAY_MINT_REFUSAL_REASONS.get(error.error_code)) ?? "gateway_refused";
|
|
3702
|
+
}
|
|
3703
|
+
var GATEWAY_MINT_REFUSAL_REASONS = /* @__PURE__ */ new Map([
|
|
3704
|
+
["request_context_refused", "context_stale"],
|
|
3705
|
+
["context_expired", "context_expired"],
|
|
3706
|
+
["host_source_refused", "host_source_refused"],
|
|
3707
|
+
["host_invocation_context_unavailable", "gateway_unavailable"]
|
|
3708
|
+
]);
|
|
3709
|
+
function scheduleInvocationMintDeadline2(expire, milliseconds) {
|
|
3118
3710
|
const timeout = setTimeout(expire, milliseconds);
|
|
3119
3711
|
return () => clearTimeout(timeout);
|
|
3120
3712
|
}
|
|
3121
3713
|
function reportInvocationMint(observation) {
|
|
3122
|
-
process.stderr.write(
|
|
3714
|
+
process.stderr.write(`${formatHostRelayInvocationMintLine(observation)}
|
|
3123
3715
|
`);
|
|
3124
3716
|
}
|
|
3717
|
+
function formatHostRelayInvocationMintLine(observation) {
|
|
3718
|
+
return `${HOST_RELAY_INVOCATION_MINT_LOG_TAG} outcome=${observation.outcome}${observation.reason === void 0 ? "" : ` reason=${observation.reason}`} elapsed_ms=${observation.elapsed_ms}`;
|
|
3719
|
+
}
|
|
3125
3720
|
async function runHostRelayCli(args, signal, dependencies = {}) {
|
|
3126
3721
|
try {
|
|
3127
3722
|
const configPath = parseServeArgs(args);
|
|
@@ -3175,6 +3770,7 @@ var controller = new AbortController();
|
|
|
3175
3770
|
var stop = () => controller.abort();
|
|
3176
3771
|
process.once("SIGINT", stop);
|
|
3177
3772
|
process.once("SIGTERM", stop);
|
|
3773
|
+
var stopLogBound = boundHostServiceLog();
|
|
3178
3774
|
void runHostRelayCli(process.argv.slice(2), controller.signal).then((result2) => {
|
|
3179
3775
|
process.stdout.write(`${JSON.stringify({ schema_version: "ceal.host_relay_command.v1", ...result2 })}
|
|
3180
3776
|
`);
|
|
@@ -3184,6 +3780,7 @@ void runHostRelayCli(process.argv.slice(2), controller.signal).then((result2) =>
|
|
|
3184
3780
|
`);
|
|
3185
3781
|
process.exitCode = 1;
|
|
3186
3782
|
}).finally(() => {
|
|
3783
|
+
stopLogBound();
|
|
3187
3784
|
process.removeListener("SIGINT", stop);
|
|
3188
3785
|
process.removeListener("SIGTERM", stop);
|
|
3189
3786
|
});
|