@mycadet/gateway 0.12.7 → 0.12.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/ces-client/package.json +1 -1
- package/node_modules/@mycadet/ces-client/src/__tests__/ces-client.test.ts +14 -14
- package/node_modules/@mycadet/ces-client/src/rpc-client.ts +1 -1
- package/node_modules/@mycadet/environments/src/seeds.ts +6 -6
- package/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
- package/node_modules/@mycadet/environments/src/types.ts +6 -6
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/gateway-client/src/binding-strength-contract.ts +5 -5
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +1 -1
- package/node_modules/@mycadet/gateway-client/src/inbound-contract.ts +2 -2
- package/node_modules/@mycadet/gateway-client/src/outbound-contract.ts +1 -1
- package/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/openapi.json +17 -17
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +1 -1
- package/src/__tests__/actor-token-revocation.test.ts +0 -1
- package/src/__tests__/{backup-worker-platform-mode.test.ts → backup-worker-cloud-mode.test.ts} +4 -4
- package/src/__tests__/channel-verification-session-proxy.test.ts +0 -1
- package/src/__tests__/{platform-identity.test.ts → cloud-identity.test.ts} +72 -86
- package/src/__tests__/config.test.ts +0 -24
- package/src/__tests__/contacts-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/credential-requests.test.ts +6 -6
- package/src/__tests__/{credential-watcher-managed-bootstrap.test.ts → credential-watcher-cloud-bootstrap.test.ts} +1 -1
- package/src/__tests__/edge-auth.test.ts +18 -18
- package/src/__tests__/edge-guardian-auth.test.ts +10 -10
- package/src/__tests__/guardian-init-lockfile.test.ts +6 -8
- package/src/__tests__/guardian-pin.test.ts +8 -8
- package/src/__tests__/handle-inbound-admission.test.ts +0 -1
- package/src/__tests__/handle-inbound-app-context.test.ts +0 -1
- package/src/__tests__/handle-inbound-invite-intercept.test.ts +0 -1
- package/src/__tests__/handle-inbound-trust-verdict.test.ts +0 -1
- package/src/__tests__/inbound-contact-seed.test.ts +1 -1
- package/src/__tests__/live-voice-websocket.test.ts +12 -12
- package/src/__tests__/load-guards.test.ts +0 -1
- package/src/__tests__/migration-teleport-gcs-proxy.test.ts +0 -1
- package/src/__tests__/oauth-callback.test.ts +0 -1
- package/src/__tests__/remote-feature-flag-sync.test.ts +35 -45
- package/src/__tests__/resolve-assistant.test.ts +0 -1
- package/src/__tests__/route-schema-guard.test.ts +4 -1
- package/src/__tests__/runtime-client.test.ts +1 -2
- package/src/__tests__/runtime-health-proxy.test.ts +0 -1
- package/src/__tests__/runtime-proxy-auth.test.ts +3 -2
- package/src/__tests__/runtime-proxy-device-stamp.test.ts +0 -1
- package/src/__tests__/runtime-proxy-error-body.test.ts +0 -1
- package/src/__tests__/runtime-proxy.test.ts +0 -1
- package/src/__tests__/runtime-stream-test-utils.ts +0 -1
- package/src/__tests__/shared-invite-registration.test.ts +15 -32
- package/src/__tests__/slack-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/slack-display-name.test.ts +0 -1
- package/src/__tests__/slack-normalize.test.ts +0 -1
- package/src/__tests__/slack-reaction-normalize.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-catchup.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-group-dm.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-liveness.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-outbound-root.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-thread-tracking.test.ts +0 -1
- package/src/__tests__/speech-relay-websocket.test.ts +16 -14
- package/src/__tests__/stt-stream-websocket.test.ts +5 -5
- package/src/__tests__/telegram-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/telegram-webhook-drop-visibility.test.ts +0 -1
- package/src/__tests__/telegram-webhook-handler.test.ts +0 -1
- package/src/__tests__/telegram-webhook-manager.test.ts +79 -80
- package/src/__tests__/twilio-media-websocket.test.ts +0 -1
- package/src/__tests__/twilio-webhooks.test.ts +6 -7
- package/src/__tests__/whatsapp-download.test.ts +0 -1
- package/src/__tests__/whatsapp-webhook.test.ts +0 -1
- package/src/attachments/ingest.test.ts +0 -1
- package/src/auth/guardian-bootstrap.ts +16 -15
- package/src/auth/scopes.ts +1 -1
- package/src/auth-fallback-reporter.ts +1 -1
- package/src/backup/backup-worker.ts +2 -2
- package/src/channels/inbound-event.ts +3 -3
- package/src/channels/ingress-inbound-vendors.test.ts +2 -2
- package/src/channels/ingress-inbound.ts +1 -1
- package/src/channels/ingress-verification.test.ts +2 -2
- package/src/channels/ingress-verification.ts +4 -4
- package/src/channels/plugin-ingress-approvals.test.ts +1 -1
- package/src/channels/plugin-ingress-approvals.ts +1 -1
- package/src/channels/plugin-ingress.test.ts +3 -3
- package/src/channels/plugin-ingress.ts +3 -3
- package/src/channels/room-admission.ts +2 -2
- package/src/cli/contacts.ts +3 -1
- package/src/cli/index.ts +1 -1
- package/src/{platform-identity.ts → cloud-identity.ts} +60 -68
- package/src/cloud-url.ts +25 -0
- package/src/{platform-user-id.ts → cloud-user-id.ts} +4 -4
- package/src/config.ts +2 -4
- package/src/credential-reader.ts +2 -2
- package/src/db/assistant-db-proxy.ts +1 -1
- package/src/discord/forward.ts +1 -1
- package/src/email/normalize.ts +4 -4
- package/src/email/register-callback.test.ts +27 -34
- package/src/email/register-callback.ts +21 -26
- package/src/feature-flag-registry.json +5 -5
- package/src/feature-flag-remote-store.ts +2 -2
- package/src/feature-flag-resolver.ts +5 -5
- package/src/http/cadet-signature.ts +1 -1
- package/src/http/edge-forwarded-header.ts +1 -1
- package/src/http/loopback-guard.ts +1 -1
- package/src/http/middleware/auth.ts +22 -22
- package/src/http/router.ts +1 -1
- package/src/http/routes/auto-approve-thresholds-routes.ts +1 -1
- package/src/http/routes/cadet-identity.ts +2 -2
- package/src/http/routes/channel-admission-policy-routes.ts +2 -2
- package/src/http/routes/channel-ingress-routes.ts +3 -3
- package/src/http/routes/channel-ingress.test.ts +13 -2
- package/src/http/routes/channel-permission-overrides-routes.ts +3 -3
- package/src/http/routes/channel-verification-session-proxy.ts +8 -22
- package/src/http/routes/{platform-push-proxy.test.ts → cloud-push-proxy.test.ts} +21 -22
- package/src/http/routes/{platform-push-proxy.ts → cloud-push-proxy.ts} +13 -14
- package/src/http/routes/contacts-control-plane-proxy.ts +3 -3
- package/src/http/routes/contacts-control-plane-routes.ts +5 -5
- package/src/http/routes/email-webhook.test.ts +2 -3
- package/src/http/routes/email-webhook.ts +4 -4
- package/src/http/routes/feature-flags.ts +1 -1
- package/src/http/routes/guardian-channel-create.ts +3 -3
- package/src/http/routes/guardian-pin.ts +9 -9
- package/src/http/routes/inbound-register.ts +2 -2
- package/src/http/routes/ingress-domains.test.ts +129 -0
- package/src/http/routes/ingress-domains.ts +73 -0
- package/src/http/routes/live-voice-websocket.ts +2 -2
- package/src/http/routes/log-export.test.ts +0 -1
- package/src/http/routes/log-tail.test.ts +0 -1
- package/src/http/routes/migration-proxy.ts +1 -1
- package/src/http/routes/plugin-webhook-websocket.test.ts +3 -3
- package/src/http/routes/plugin-webhook.test.ts +9 -8
- package/src/http/routes/plugin-webhook.ts +1 -1
- package/src/http/routes/speech-relay-websocket.ts +8 -11
- package/src/http/routes/stt-stream-websocket.ts +1 -1
- package/src/http/routes/telegram-webhook.test.ts +0 -1
- package/src/http/routes/trust-rules-routes.ts +2 -2
- package/src/http/routes/twilio-voice-verify-callback.ts +3 -3
- package/src/http/routes/twilio-voice-webhook.test.ts +3 -4
- package/src/http/routes/twilio-voice-webhook.ts +3 -3
- package/src/http/routes/whatsapp-webhook.test.ts +0 -1
- package/src/index.ts +57 -56
- package/src/ipc/credential-request-handlers.ts +4 -4
- package/src/ipc/debug-export-handlers.test.ts +0 -1
- package/src/ipc/log-tail-handlers.test.ts +0 -1
- package/src/ipc/risk-classification-handlers.test.ts +4 -4
- package/src/ipc/risk-classification-handlers.ts +2 -2
- package/src/remote-feature-flag-sync.ts +17 -19
- package/src/risk/command-registry/commands/assistant.ts +2 -2
- package/src/risk/skill-risk-classifier.test.ts +24 -24
- package/src/risk/skill-risk-classifier.ts +10 -10
- package/src/runtime/client.ts +17 -17
- package/src/schema.ts +29 -29
- package/src/telegram/send.test.ts +0 -1
- package/src/telegram/webhook-manager.ts +33 -38
- package/src/tunnel/allowed-paths.test.ts +3 -3
- package/src/tunnel/allowed-paths.ts +1 -1
- package/src/tunnel/client.test.ts +20 -21
- package/src/tunnel/client.ts +16 -17
- package/src/verification/shared-invite-registration.ts +22 -17
- package/src/version.ts +1 -1
- package/src/platform-url.ts +0 -24
|
@@ -19,10 +19,10 @@ describe("SkillLoadRiskClassifier", () => {
|
|
|
19
19
|
expect(result.matchType).toBe("registry");
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
test("
|
|
22
|
+
test("scaffold_cloud_skill is always High risk", async () => {
|
|
23
23
|
const classifier = new SkillLoadRiskClassifier();
|
|
24
24
|
const result = await classifier.classify({
|
|
25
|
-
toolName: "
|
|
25
|
+
toolName: "scaffold_cloud_skill",
|
|
26
26
|
});
|
|
27
27
|
expect(result.riskLevel).toBe("high");
|
|
28
28
|
expect(result.reason).toBe(
|
|
@@ -32,10 +32,10 @@ describe("SkillLoadRiskClassifier", () => {
|
|
|
32
32
|
expect(result.matchType).toBe("registry");
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
test("
|
|
35
|
+
test("delete_cloud_skill is always High risk", async () => {
|
|
36
36
|
const classifier = new SkillLoadRiskClassifier();
|
|
37
37
|
const result = await classifier.classify({
|
|
38
|
-
toolName: "
|
|
38
|
+
toolName: "delete_cloud_skill",
|
|
39
39
|
});
|
|
40
40
|
expect(result.riskLevel).toBe("high");
|
|
41
41
|
expect(result.reason).toBe(
|
|
@@ -55,19 +55,19 @@ describe("SkillLoadRiskClassifier", () => {
|
|
|
55
55
|
expect(result.reason).toBe("Skill load (default)");
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
test("
|
|
58
|
+
test("scaffold_cloud_skill with skillSelector is still High risk", async () => {
|
|
59
59
|
const classifier = new SkillLoadRiskClassifier();
|
|
60
60
|
const result = await classifier.classify({
|
|
61
|
-
toolName: "
|
|
61
|
+
toolName: "scaffold_cloud_skill",
|
|
62
62
|
skillSelector: "new-skill",
|
|
63
63
|
});
|
|
64
64
|
expect(result.riskLevel).toBe("high");
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
test("
|
|
67
|
+
test("delete_cloud_skill with skillSelector is still High risk", async () => {
|
|
68
68
|
const classifier = new SkillLoadRiskClassifier();
|
|
69
69
|
const result = await classifier.classify({
|
|
70
|
-
toolName: "
|
|
70
|
+
toolName: "delete_cloud_skill",
|
|
71
71
|
skillSelector: "old-skill",
|
|
72
72
|
});
|
|
73
73
|
expect(result.riskLevel).toBe("high");
|
|
@@ -163,56 +163,56 @@ describe("allowlistOptions", () => {
|
|
|
163
163
|
]);
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
test("
|
|
166
|
+
test("scaffold_cloud_skill produces skill-specific + wildcard options", async () => {
|
|
167
167
|
const classifier = new SkillLoadRiskClassifier();
|
|
168
168
|
const result = await classifier.classify({
|
|
169
|
-
toolName: "
|
|
169
|
+
toolName: "scaffold_cloud_skill",
|
|
170
170
|
skillSelector: "new-skill",
|
|
171
171
|
});
|
|
172
172
|
expect(result.allowlistOptions).toEqual([
|
|
173
173
|
{
|
|
174
174
|
label: "new-skill",
|
|
175
175
|
description: "This skill only",
|
|
176
|
-
pattern: "
|
|
176
|
+
pattern: "scaffold_cloud_skill:new-skill",
|
|
177
177
|
},
|
|
178
178
|
{
|
|
179
|
-
label: "
|
|
179
|
+
label: "scaffold_cloud_skill:*",
|
|
180
180
|
description: "All managed skill scaffolds",
|
|
181
|
-
pattern: "
|
|
181
|
+
pattern: "scaffold_cloud_skill:*",
|
|
182
182
|
},
|
|
183
183
|
]);
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
-
test("
|
|
186
|
+
test("delete_cloud_skill produces skill-specific + wildcard options", async () => {
|
|
187
187
|
const classifier = new SkillLoadRiskClassifier();
|
|
188
188
|
const result = await classifier.classify({
|
|
189
|
-
toolName: "
|
|
189
|
+
toolName: "delete_cloud_skill",
|
|
190
190
|
skillSelector: "old-skill",
|
|
191
191
|
});
|
|
192
192
|
expect(result.allowlistOptions).toEqual([
|
|
193
193
|
{
|
|
194
194
|
label: "old-skill",
|
|
195
195
|
description: "This skill only",
|
|
196
|
-
pattern: "
|
|
196
|
+
pattern: "delete_cloud_skill:old-skill",
|
|
197
197
|
},
|
|
198
198
|
{
|
|
199
|
-
label: "
|
|
199
|
+
label: "delete_cloud_skill:*",
|
|
200
200
|
description: "All managed skill deletes",
|
|
201
|
-
pattern: "
|
|
201
|
+
pattern: "delete_cloud_skill:*",
|
|
202
202
|
},
|
|
203
203
|
]);
|
|
204
204
|
});
|
|
205
205
|
|
|
206
|
-
test("
|
|
206
|
+
test("scaffold_cloud_skill without selector produces only wildcard", async () => {
|
|
207
207
|
const classifier = new SkillLoadRiskClassifier();
|
|
208
208
|
const result = await classifier.classify({
|
|
209
|
-
toolName: "
|
|
209
|
+
toolName: "scaffold_cloud_skill",
|
|
210
210
|
});
|
|
211
211
|
expect(result.allowlistOptions).toEqual([
|
|
212
212
|
{
|
|
213
|
-
label: "
|
|
213
|
+
label: "scaffold_cloud_skill:*",
|
|
214
214
|
description: "All managed skill scaffolds",
|
|
215
|
-
pattern: "
|
|
215
|
+
pattern: "scaffold_cloud_skill:*",
|
|
216
216
|
},
|
|
217
217
|
]);
|
|
218
218
|
});
|
|
@@ -228,8 +228,8 @@ describe("singleton", () => {
|
|
|
228
228
|
test("singleton produces same results as fresh instance", async () => {
|
|
229
229
|
const inputs: SkillClassifierInput[] = [
|
|
230
230
|
{ toolName: "skill_load" },
|
|
231
|
-
{ toolName: "
|
|
232
|
-
{ toolName: "
|
|
231
|
+
{ toolName: "scaffold_cloud_skill" },
|
|
232
|
+
{ toolName: "delete_cloud_skill" },
|
|
233
233
|
];
|
|
234
234
|
|
|
235
235
|
const fresh = new SkillLoadRiskClassifier();
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Implements RiskClassifier<SkillClassifierInput> with constant risk levels
|
|
5
5
|
* for each skill tool type:
|
|
6
6
|
* - skill_load: Low (read-only skill loading)
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
7
|
+
* - scaffold_cloud_skill: High (writes persistent skill source code)
|
|
8
|
+
* - delete_cloud_skill: High (removes persistent skill source code)
|
|
9
9
|
*
|
|
10
10
|
* Gateway adaptation: accepts pre-resolved skill metadata via
|
|
11
11
|
* SkillClassifierInput.resolvedMetadata instead of importing the assistant's
|
|
@@ -50,7 +50,7 @@ export interface ResolvedSkillMetadata {
|
|
|
50
50
|
/** Input to the skill risk classifier. */
|
|
51
51
|
export interface SkillClassifierInput {
|
|
52
52
|
/** Which skill tool is being invoked. */
|
|
53
|
-
toolName: "skill_load" | "
|
|
53
|
+
toolName: "skill_load" | "scaffold_cloud_skill" | "delete_cloud_skill";
|
|
54
54
|
/** Optional skill selector (e.g. skill name or path). */
|
|
55
55
|
skillSelector?: string;
|
|
56
56
|
/**
|
|
@@ -134,12 +134,12 @@ function buildSkillLoadAllowlistOptions(
|
|
|
134
134
|
/**
|
|
135
135
|
* Build allowlist options for scaffold/delete managed skill tools.
|
|
136
136
|
*/
|
|
137
|
-
function
|
|
137
|
+
function buildCloudSkillAllowlistOptions(
|
|
138
138
|
toolName: string,
|
|
139
139
|
skillId?: string,
|
|
140
140
|
): AllowlistOption[] {
|
|
141
141
|
const toolLabel =
|
|
142
|
-
toolName === "
|
|
142
|
+
toolName === "scaffold_cloud_skill" ? "scaffold" : "delete";
|
|
143
143
|
const options: AllowlistOption[] = [];
|
|
144
144
|
if (skillId) {
|
|
145
145
|
options.push({
|
|
@@ -200,25 +200,25 @@ export class SkillLoadRiskClassifier implements RiskClassifier<SkillClassifierIn
|
|
|
200
200
|
};
|
|
201
201
|
break;
|
|
202
202
|
}
|
|
203
|
-
case "
|
|
203
|
+
case "scaffold_cloud_skill":
|
|
204
204
|
assessment = {
|
|
205
205
|
riskLevel: "high",
|
|
206
206
|
reason: "Skill scaffold — writes persistent skill source code",
|
|
207
207
|
scopeOptions: [],
|
|
208
208
|
matchType: "registry",
|
|
209
|
-
allowlistOptions:
|
|
209
|
+
allowlistOptions: buildCloudSkillAllowlistOptions(
|
|
210
210
|
toolName,
|
|
211
211
|
skillSelector,
|
|
212
212
|
),
|
|
213
213
|
};
|
|
214
214
|
break;
|
|
215
|
-
case "
|
|
215
|
+
case "delete_cloud_skill":
|
|
216
216
|
assessment = {
|
|
217
217
|
riskLevel: "high",
|
|
218
218
|
reason: "Skill delete — removes persistent skill source code",
|
|
219
219
|
scopeOptions: [],
|
|
220
220
|
matchType: "registry",
|
|
221
|
-
allowlistOptions:
|
|
221
|
+
allowlistOptions: buildCloudSkillAllowlistOptions(
|
|
222
222
|
toolName,
|
|
223
223
|
skillSelector,
|
|
224
224
|
),
|
|
@@ -227,7 +227,7 @@ export class SkillLoadRiskClassifier implements RiskClassifier<SkillClassifierIn
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// User override is applied after normal classification. This means a user-defined
|
|
230
|
-
// rule CAN lower a security-escalated risk (e.g.,
|
|
230
|
+
// rule CAN lower a security-escalated risk (e.g., scaffold_cloud_skill).
|
|
231
231
|
// This is intentional — user overrides are authoritative for users who explicitly
|
|
232
232
|
// created them. Uses resolved skillId from metadata (when available), and
|
|
233
233
|
// skill_load_dynamic as the tool key for dynamic skills.
|
package/src/runtime/client.ts
CHANGED
|
@@ -397,12 +397,12 @@ function extractPlatformCallbackAssistantId(
|
|
|
397
397
|
|
|
398
398
|
function resolveTunnelBaseWssUrl(
|
|
399
399
|
config: GatewayConfig,
|
|
400
|
-
|
|
400
|
+
cloudAssistantId?: string,
|
|
401
401
|
): string | undefined {
|
|
402
|
-
if (!config.tunnelBaseUrl || !
|
|
402
|
+
if (!config.tunnelBaseUrl || !cloudAssistantId) return undefined;
|
|
403
403
|
|
|
404
404
|
const withPath =
|
|
405
|
-
config.tunnelBaseUrl.replace(/\/+$/, "") + "/" +
|
|
405
|
+
config.tunnelBaseUrl.replace(/\/+$/, "") + "/" + cloudAssistantId;
|
|
406
406
|
const normalizedTunnelUrl = normalizePublicBaseUrl(withPath);
|
|
407
407
|
return normalizedTunnelUrl
|
|
408
408
|
? toWebSocketBaseUrl(normalizedTunnelUrl)
|
|
@@ -418,7 +418,7 @@ function resolveTunnelBaseWssUrl(
|
|
|
418
418
|
* 2. `ingress.publicBaseUrl` from the config file when it is a real public
|
|
419
419
|
* gateway base URL — written by Tunnel after tunnel registration, or set
|
|
420
420
|
* manually for self-hosted.
|
|
421
|
-
* 3. `TUNNEL_BASE_URL` + the
|
|
421
|
+
* 3. `TUNNEL_BASE_URL` + the cloud assistant ID from a configured callback
|
|
422
422
|
* URL or credential cache.
|
|
423
423
|
*
|
|
424
424
|
* Returns `undefined` when no source provides a value — the placeholder
|
|
@@ -428,7 +428,7 @@ function resolveTunnelBaseWssUrl(
|
|
|
428
428
|
export function resolvePublicBaseWssUrl(
|
|
429
429
|
config: GatewayConfig,
|
|
430
430
|
configFile?: ConfigFileCache,
|
|
431
|
-
|
|
431
|
+
cloudAssistantId?: string,
|
|
432
432
|
validatedPublicUrl?: string,
|
|
433
433
|
): string | undefined {
|
|
434
434
|
const raw = configFile?.getString("ingress", "publicBaseUrl");
|
|
@@ -450,7 +450,7 @@ export function resolvePublicBaseWssUrl(
|
|
|
450
450
|
|
|
451
451
|
return resolveTunnelBaseWssUrl(
|
|
452
452
|
config,
|
|
453
|
-
configuredCallbackAssistantId ??
|
|
453
|
+
configuredCallbackAssistantId ?? cloudAssistantId,
|
|
454
454
|
);
|
|
455
455
|
}
|
|
456
456
|
|
|
@@ -462,12 +462,12 @@ export function resolvePublicBaseWssUrl(
|
|
|
462
462
|
* Sources (in priority order):
|
|
463
463
|
* 1. `ingress.publicBaseUrl` from the config file — written by Tunnel after
|
|
464
464
|
* tunnel registration, or set manually for self-hosted.
|
|
465
|
-
* 2. `TUNNEL_BASE_URL` + the
|
|
465
|
+
* 2. `TUNNEL_BASE_URL` + the cloud assistant ID. This mirrors the Twilio
|
|
466
466
|
* resolver's fallback and stays valid even while the tunnel is briefly
|
|
467
467
|
* disconnected (Tunnel clears the config value on every reconnect), so
|
|
468
468
|
* these links no longer break on a tunnel flap.
|
|
469
469
|
*
|
|
470
|
-
* The
|
|
470
|
+
* The cloud assistant ID is read through a lazy getter so callers that
|
|
471
471
|
* resolve from the config value (self-hosted / manual URL) never load
|
|
472
472
|
* identity. A transient credential-store outage must not fail those callers.
|
|
473
473
|
*
|
|
@@ -476,14 +476,14 @@ export function resolvePublicBaseWssUrl(
|
|
|
476
476
|
export async function resolvePublicHttpBaseUrl(
|
|
477
477
|
config: GatewayConfig,
|
|
478
478
|
configFile?: ConfigFileCache,
|
|
479
|
-
|
|
479
|
+
getCloudAssistantId?: () => Promise<string | undefined>,
|
|
480
480
|
): Promise<string | undefined> {
|
|
481
481
|
const resolved =
|
|
482
482
|
normalizeHttpPublicBaseUrl(
|
|
483
483
|
configFile?.getString("ingress", "publicBaseUrl"),
|
|
484
484
|
) ??
|
|
485
485
|
normalizeHttpPublicBaseUrl(
|
|
486
|
-
await resolveTunnelHttpFallback(config,
|
|
486
|
+
await resolveTunnelHttpFallback(config, getCloudAssistantId),
|
|
487
487
|
);
|
|
488
488
|
|
|
489
489
|
// Drop a root-path trailing slash so callers can append `/…` without
|
|
@@ -492,18 +492,18 @@ export async function resolvePublicHttpBaseUrl(
|
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
/**
|
|
495
|
-
* Build the `TUNNEL_BASE_URL/<
|
|
496
|
-
* reads the
|
|
495
|
+
* Build the `TUNNEL_BASE_URL/<cloud-assistant-id>` fallback base URL. Only
|
|
496
|
+
* reads the cloud assistant ID (via the getter) when `tunnelBaseUrl` is
|
|
497
497
|
* configured, so it's skipped entirely on self-hosted deployments.
|
|
498
498
|
*/
|
|
499
499
|
async function resolveTunnelHttpFallback(
|
|
500
500
|
config: GatewayConfig,
|
|
501
|
-
|
|
501
|
+
getCloudAssistantId?: () => Promise<string | undefined>,
|
|
502
502
|
): Promise<string | undefined> {
|
|
503
|
-
if (!config.tunnelBaseUrl || !
|
|
504
|
-
const
|
|
505
|
-
if (!
|
|
506
|
-
return config.tunnelBaseUrl.replace(/\/+$/, "") + "/" +
|
|
503
|
+
if (!config.tunnelBaseUrl || !getCloudAssistantId) return undefined;
|
|
504
|
+
const cloudAssistantId = await getCloudAssistantId();
|
|
505
|
+
if (!cloudAssistantId) return undefined;
|
|
506
|
+
return config.tunnelBaseUrl.replace(/\/+$/, "") + "/" + cloudAssistantId;
|
|
507
507
|
}
|
|
508
508
|
|
|
509
509
|
/**
|
package/src/schema.ts
CHANGED
|
@@ -302,7 +302,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
302
302
|
},
|
|
303
303
|
},
|
|
304
304
|
"405": {
|
|
305
|
-
description: "Method not allowed
|
|
305
|
+
description: "Method not allowed, only POST accepted",
|
|
306
306
|
content: {
|
|
307
307
|
"application/json": {
|
|
308
308
|
schema: { $ref: "#/components/schemas/ErrorResponse" },
|
|
@@ -368,7 +368,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
368
368
|
},
|
|
369
369
|
},
|
|
370
370
|
"405": {
|
|
371
|
-
description: "Method not allowed
|
|
371
|
+
description: "Method not allowed, only POST accepted",
|
|
372
372
|
content: {
|
|
373
373
|
"application/json": {
|
|
374
374
|
schema: { $ref: "#/components/schemas/ErrorResponse" },
|
|
@@ -822,7 +822,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
822
822
|
"application/json": {
|
|
823
823
|
schema: {
|
|
824
824
|
type: "object",
|
|
825
|
-
description: "Mailgun webhook payload
|
|
825
|
+
description: "Mailgun webhook payload as JSON.",
|
|
826
826
|
},
|
|
827
827
|
},
|
|
828
828
|
},
|
|
@@ -945,7 +945,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
945
945
|
get: {
|
|
946
946
|
summary: "Twilio Media Stream WebSocket",
|
|
947
947
|
description:
|
|
948
|
-
"Accepts a WebSocket upgrade from Twilio Media Streams and bidirectionally proxies frames to the assistant runtime's /v1/calls/media-stream endpoint. Handshake metadata
|
|
948
|
+
"Accepts a WebSocket upgrade from Twilio Media Streams and bidirectionally proxies frames to the assistant runtime's /v1/calls/media-stream endpoint. Handshake metadata, the callSessionId and auth token, is carried in URL path segments, e.g. /webhooks/twilio/media-stream/<callSessionId>/<token>, because Twilio Media Streams does not reliably preserve query parameters across the WebSocket upgrade. Legacy query-parameter-based handshake is still supported as a fallback.",
|
|
949
949
|
operationId: "twilioMediaStreamWebsocket",
|
|
950
950
|
parameters: [
|
|
951
951
|
{
|
|
@@ -1003,7 +1003,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1003
1003
|
required: false,
|
|
1004
1004
|
schema: { type: "string" },
|
|
1005
1005
|
description:
|
|
1006
|
-
"Optional STT provider identifier
|
|
1006
|
+
"Optional STT provider identifier, e.g. 'deepgram' or 'google-gemini'. Forwarded as compatibility metadata: the runtime resolves the transcriber from config (`services.stt.roles.dictation`, else `services.stt.provider`), not from this parameter. When supplied and it disagrees with the provider that resolves to, the runtime logs a mismatch warning.",
|
|
1007
1007
|
},
|
|
1008
1008
|
{
|
|
1009
1009
|
name: "mimeType",
|
|
@@ -1011,7 +1011,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1011
1011
|
required: true,
|
|
1012
1012
|
schema: { type: "string" },
|
|
1013
1013
|
description:
|
|
1014
|
-
"MIME type of the audio being streamed
|
|
1014
|
+
"MIME type of the audio being streamed, e.g. 'audio/webm;codecs=opus'.",
|
|
1015
1015
|
},
|
|
1016
1016
|
{
|
|
1017
1017
|
name: "sampleRate",
|
|
@@ -1035,7 +1035,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1035
1035
|
"WebSocket upgrade successful — bidirectional STT audio/transcription frame proxying begins.",
|
|
1036
1036
|
},
|
|
1037
1037
|
"400": {
|
|
1038
|
-
description: "Missing required query parameter
|
|
1038
|
+
description: "Missing required query parameter: mimeType",
|
|
1039
1039
|
content: {
|
|
1040
1040
|
"text/plain": {
|
|
1041
1041
|
schema: { type: "string" },
|
|
@@ -1084,7 +1084,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1084
1084
|
required: true,
|
|
1085
1085
|
schema: { type: "string" },
|
|
1086
1086
|
description:
|
|
1087
|
-
"MIME type of the narration audio being streamed
|
|
1087
|
+
"MIME type of the narration audio being streamed, e.g. 'audio/pcm'.",
|
|
1088
1088
|
},
|
|
1089
1089
|
{
|
|
1090
1090
|
name: "sampleRate",
|
|
@@ -1124,7 +1124,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1124
1124
|
"WebSocket upgrade successful - bidirectional watch stream frame proxying begins.",
|
|
1125
1125
|
},
|
|
1126
1126
|
"400": {
|
|
1127
|
-
description: "Missing required query parameter
|
|
1127
|
+
description: "Missing required query parameter: mimeType",
|
|
1128
1128
|
content: {
|
|
1129
1129
|
"text/plain": {
|
|
1130
1130
|
schema: { type: "string" },
|
|
@@ -1739,7 +1739,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1739
1739
|
"404": { description: "Channel not found" },
|
|
1740
1740
|
"409": {
|
|
1741
1741
|
description:
|
|
1742
|
-
"Invalid state transition
|
|
1742
|
+
"Invalid state transition, e.g. revoking a blocked channel",
|
|
1743
1743
|
},
|
|
1744
1744
|
"503": { description: "Bearer token not configured" },
|
|
1745
1745
|
"502": { description: "Failed to reach assistant runtime" },
|
|
@@ -1751,7 +1751,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1751
1751
|
post: {
|
|
1752
1752
|
summary: "Mark a contact channel as verified by guardian attestation",
|
|
1753
1753
|
description:
|
|
1754
|
-
"Guardian-only endpoint that attests a contact channel as verified without exchanging a challenge code. The caller must be the bound guardian — verified either by JWT actor principal (laptop / docker) or by `X-Cadet-User-Id` matching the stored platform user id (
|
|
1754
|
+
"Guardian-only endpoint that attests a contact channel as verified without exchanging a challenge code. The caller must be the bound guardian — verified either by JWT actor principal (laptop / docker) or by `X-Cadet-User-Id` matching the stored platform user id (cloud-managed). Idempotent: an already-verified channel returns 200 without re-writing. Mutation is gateway-DB primary with a best-effort dual-write to the assistant daemon DB for sync during the gateway-security-migration transition.",
|
|
1755
1755
|
operationId: "contactsChannelVerify",
|
|
1756
1756
|
security: [{ BearerAuth: [] }],
|
|
1757
1757
|
parameters: [
|
|
@@ -1983,7 +1983,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1983
1983
|
required: false,
|
|
1984
1984
|
schema: { type: "string" },
|
|
1985
1985
|
description:
|
|
1986
|
-
"Optional verification purpose
|
|
1986
|
+
"Optional verification purpose, e.g. guardian or trusted-contact.",
|
|
1987
1987
|
},
|
|
1988
1988
|
{
|
|
1989
1989
|
name: "contactChannelId",
|
|
@@ -2126,7 +2126,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2126
2126
|
"Loopback-only endpoint that removes the guardian-init lock file so that /v1/guardian/init can be called again. When bootstrap secrets are configured, callers must provide a valid x-bootstrap-secret header. Used by the desktop app to recover from a lost actor token.",
|
|
2127
2127
|
operationId: "guardianResetBootstrap",
|
|
2128
2128
|
responses: {
|
|
2129
|
-
"200": { description: "Lock file removed
|
|
2129
|
+
"200": { description: "Lock file removed, or already absent" },
|
|
2130
2130
|
"403": {
|
|
2131
2131
|
description:
|
|
2132
2132
|
"Forbidden — non-loopback origin or invalid bootstrap secret",
|
|
@@ -2183,7 +2183,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2183
2183
|
responses: {
|
|
2184
2184
|
"200": { description: "New access token returned" },
|
|
2185
2185
|
"400": {
|
|
2186
|
-
description: "Missing required field
|
|
2186
|
+
description: "Missing required field: refreshToken or deviceId",
|
|
2187
2187
|
},
|
|
2188
2188
|
"401": { description: "Unauthorized — invalid token" },
|
|
2189
2189
|
"403": {
|
|
@@ -2407,7 +2407,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2407
2407
|
operationId: "oauthProvidersList",
|
|
2408
2408
|
parameters: [
|
|
2409
2409
|
{
|
|
2410
|
-
name: "
|
|
2410
|
+
name: "supports_cloud_mode",
|
|
2411
2411
|
in: "query",
|
|
2412
2412
|
required: false,
|
|
2413
2413
|
schema: { type: "boolean" },
|
|
@@ -2509,7 +2509,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2509
2509
|
post: {
|
|
2510
2510
|
summary: "Create OAuth app",
|
|
2511
2511
|
description:
|
|
2512
|
-
"Authenticated gateway endpoint that creates or updates a user-
|
|
2512
|
+
"Authenticated gateway endpoint that creates or updates a user-cloud OAuth app by proxying to the assistant runtime.",
|
|
2513
2513
|
operationId: "oauthAppsCreate",
|
|
2514
2514
|
security: [{ BearerAuth: [] }],
|
|
2515
2515
|
requestBody: {
|
|
@@ -2546,7 +2546,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2546
2546
|
delete: {
|
|
2547
2547
|
summary: "Delete OAuth app",
|
|
2548
2548
|
description:
|
|
2549
|
-
"Authenticated gateway endpoint that deletes a user-
|
|
2549
|
+
"Authenticated gateway endpoint that deletes a user-cloud OAuth app and disconnects its linked accounts by proxying to the assistant runtime.",
|
|
2550
2550
|
operationId: "oauthAppsDelete",
|
|
2551
2551
|
parameters: [
|
|
2552
2552
|
{
|
|
@@ -3314,11 +3314,11 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3314
3314
|
properties: {
|
|
3315
3315
|
startTime: {
|
|
3316
3316
|
type: "number",
|
|
3317
|
-
description: "Start of time range
|
|
3317
|
+
description: "Start of time range, in epoch ms",
|
|
3318
3318
|
},
|
|
3319
3319
|
endTime: {
|
|
3320
3320
|
type: "number",
|
|
3321
|
-
description: "End of time range
|
|
3321
|
+
description: "End of time range, in epoch ms",
|
|
3322
3322
|
},
|
|
3323
3323
|
auditLimit: {
|
|
3324
3324
|
type: "number",
|
|
@@ -3381,7 +3381,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3381
3381
|
enum: ["trace", "debug", "info", "warn", "error", "fatal"],
|
|
3382
3382
|
default: "info",
|
|
3383
3383
|
},
|
|
3384
|
-
description: "Minimum pino level name
|
|
3384
|
+
description: "Minimum pino level name, defaults to info",
|
|
3385
3385
|
},
|
|
3386
3386
|
{
|
|
3387
3387
|
name: "module",
|
|
@@ -3427,7 +3427,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3427
3427
|
get: {
|
|
3428
3428
|
summary: "List trust rules",
|
|
3429
3429
|
description:
|
|
3430
|
-
"Authenticated gateway endpoint that lists trust rules from the SQLite-backed store. By default returns user-relevant rules
|
|
3430
|
+
"Authenticated gateway endpoint that lists trust rules from the SQLite-backed store. By default returns user-relevant rules: user_defined + user-modified defaults. Supports `origin`, `tool`, and `include_deleted` query filters.",
|
|
3431
3431
|
operationId: "trustRulesGet",
|
|
3432
3432
|
security: [{ BearerAuth: [] }],
|
|
3433
3433
|
parameters: [
|
|
@@ -3752,7 +3752,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3752
3752
|
post: {
|
|
3753
3753
|
summary: "Resolve the effective channel-permission threshold",
|
|
3754
3754
|
description:
|
|
3755
|
-
"Authenticated gateway endpoint that resolves the cascade for one coordinate
|
|
3755
|
+
"Authenticated gateway endpoint that resolves the cascade for one coordinate — adapter, optional channelType/channelExternalId, contact-type — and returns the winning cell's threshold and scope, or null when no cell matches so the caller falls through to the global thresholds. Read-only; a POST verb path because the composite query travels in the body, same rationale as /delete.",
|
|
3756
3756
|
operationId: "channelPermissionResolve",
|
|
3757
3757
|
security: [{ BearerAuth: [] }],
|
|
3758
3758
|
requestBody: {
|
|
@@ -3764,7 +3764,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3764
3764
|
},
|
|
3765
3765
|
},
|
|
3766
3766
|
responses: {
|
|
3767
|
-
"200": { description: "Resolution returned
|
|
3767
|
+
"200": { description: "Resolution returned — resolved or null" },
|
|
3768
3768
|
"400": { description: "Invalid request payload or adapter" },
|
|
3769
3769
|
"401": {
|
|
3770
3770
|
description: "Unauthorized — missing or invalid bearer token",
|
|
@@ -3777,7 +3777,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3777
3777
|
post: {
|
|
3778
3778
|
summary: "Delete a channel-permission matrix cell",
|
|
3779
3779
|
description:
|
|
3780
|
-
"Authenticated gateway endpoint that removes one channel-permission cell by its composite key
|
|
3780
|
+
"Authenticated gateway endpoint that removes one channel-permission cell by its composite key, selector × contact-type, letting the next cascade tier up win. A POST verb path because cells have no row id and DELETE request bodies are unreliable through proxies.",
|
|
3781
3781
|
operationId: "channelPermissionOverrideDelete",
|
|
3782
3782
|
security: [{ BearerAuth: [] }],
|
|
3783
3783
|
requestBody: {
|
|
@@ -4152,7 +4152,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
4152
4152
|
post: {
|
|
4153
4153
|
summary: "Kick off an async export-to-GCS job",
|
|
4154
4154
|
description:
|
|
4155
|
-
"Transparent proxy to the assistant daemon's `POST /v1/migrations/export-to-gcs` endpoint. The daemon schedules a background export job that streams the workspace backup to the supplied signed GCS upload URL and returns `202 Accepted` with a `job_id`. Callers poll `GET /v1/migrations/jobs/{jobId}` for progress. Registered explicitly
|
|
4155
|
+
"Transparent proxy to the assistant daemon's `POST /v1/migrations/export-to-gcs` endpoint. The daemon schedules a background export job that streams the workspace backup to the supplied signed GCS upload URL and returns `202 Accepted` with a `job_id`. Callers poll `GET /v1/migrations/jobs/{jobId}` for progress. Registered explicitly, not via the runtime-proxy catch-all, for dedicated auth and timeout handling.",
|
|
4156
4156
|
operationId: "migrationExportToGcs",
|
|
4157
4157
|
security: [{ BearerAuth: [] }],
|
|
4158
4158
|
requestBody: {
|
|
@@ -4191,7 +4191,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
4191
4191
|
post: {
|
|
4192
4192
|
summary: "Kick off an async import-from-GCS job",
|
|
4193
4193
|
description:
|
|
4194
|
-
"Transparent proxy to the assistant daemon's `POST /v1/migrations/import-from-gcs` endpoint. The daemon schedules a background import job that fetches a `.vbundle` archive at the supplied `bundle_url` and streams it through the importer, returning `202 Accepted` with a `job_id`. Callers poll `GET /v1/migrations/jobs/{jobId}` for progress. Registered explicitly
|
|
4194
|
+
"Transparent proxy to the assistant daemon's `POST /v1/migrations/import-from-gcs` endpoint. The daemon schedules a background import job that fetches a `.vbundle` archive at the supplied `bundle_url` and streams it through the importer, returning `202 Accepted` with a `job_id`. Callers poll `GET /v1/migrations/jobs/{jobId}` for progress. Registered explicitly, not via the runtime-proxy catch-all, for dedicated auth and timeout handling.",
|
|
4195
4195
|
operationId: "migrationImportFromGcs",
|
|
4196
4196
|
security: [{ BearerAuth: [] }],
|
|
4197
4197
|
requestBody: {
|
|
@@ -4241,7 +4241,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
4241
4241
|
get: {
|
|
4242
4242
|
summary: "Poll unified migration job status",
|
|
4243
4243
|
description:
|
|
4244
|
-
"Transparent proxy to the assistant daemon's `GET /v1/migrations/jobs/{job_id}` endpoint. Returns the current status of an async export-to-GCS or import-from-GCS job tracked by the daemon's migration job registry. Registered explicitly
|
|
4244
|
+
"Transparent proxy to the assistant daemon's `GET /v1/migrations/jobs/{job_id}` endpoint. Returns the current status of an async export-to-GCS or import-from-GCS job tracked by the daemon's migration job registry. Registered explicitly, not via the runtime-proxy catch-all, for dedicated auth and timeout handling.",
|
|
4245
4245
|
operationId: "migrationJobStatus",
|
|
4246
4246
|
security: [{ BearerAuth: [] }],
|
|
4247
4247
|
parameters: [
|
|
@@ -4346,7 +4346,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
4346
4346
|
post: {
|
|
4347
4347
|
summary: "Auto-verify guardian email",
|
|
4348
4348
|
description:
|
|
4349
|
-
"Called by the
|
|
4349
|
+
"Called by the cloud to auto-verify the guardian's email channel. For BYO providers (resend, mailgun), validates the provider API key. For cloud-managed email (cadet), the route's auth is sufficient. Creates a guardian email channel binding.",
|
|
4350
4350
|
operationId: "inboundRegister",
|
|
4351
4351
|
security: [{ BearerAuth: [] }],
|
|
4352
4352
|
requestBody: {
|
|
@@ -4360,7 +4360,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
4360
4360
|
type: {
|
|
4361
4361
|
type: "string",
|
|
4362
4362
|
description:
|
|
4363
|
-
"Email provider type
|
|
4363
|
+
"Email provider type, e.g. resend, mailgun, cadet",
|
|
4364
4364
|
},
|
|
4365
4365
|
guardian_email: {
|
|
4366
4366
|
type: "string",
|