@ingram-tech/ingram-cloud-sdk 0.1.1 → 0.2.0

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.
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Hand-authored Zod schemas for the `agents` resource — the wire's source of
3
+ * truth, replacing the loose generated `schemas.ts` shapes for this resource.
4
+ *
5
+ * One source, three outputs: the API imports these into its `createRoute`
6
+ * definitions (validation + emitted OpenAPI), and the consumer-facing `IC*`
7
+ * types are `z.infer`red from them here and re-exported by `../responses`. No
8
+ * Zod is pulled into a type-only consumer — `responses.ts` re-exports these as
9
+ * `export type`.
10
+ *
11
+ * `.meta({ id })` names the component so the emitted OpenAPI references it as
12
+ * `#/components/schemas/<id>` rather than inlining it.
13
+ */
14
+ import { z } from "zod";
15
+
16
+ /** A per-smith variable an agent declares; bound at run time. */
17
+ export const AgentVariable = z
18
+ .object({
19
+ name: z.string(),
20
+ default: z.string().nullish(),
21
+ description: z.string().nullish(),
22
+ required: z.boolean().optional(),
23
+ })
24
+ .meta({ id: "AgentVariable" });
25
+
26
+ /** The mutable draft head — what the next publish snapshots. */
27
+ export const AgentDraft = z
28
+ .object({
29
+ instructions: z.string().nullable(),
30
+ model: z.string().nullable(),
31
+ enabled_hosted_tools: z.array(z.string()),
32
+ auto_memory: z.boolean().nullable(),
33
+ variables: z.array(AgentVariable),
34
+ })
35
+ .meta({ id: "AgentDraft" });
36
+
37
+ export const AgentOut = z
38
+ .object({
39
+ id: z.string(),
40
+ /** Immutable, project-unique IaC reconcile key. Null for the default agent. */
41
+ slug: z.string().nullable(),
42
+ /** Free, mutable display label (not unique). */
43
+ name: z.string(),
44
+ /** True for the lazily-created default agent (can't be deleted). */
45
+ is_default: z.boolean(),
46
+ draft: AgentDraft,
47
+ active_version: z.number().int().nullable(),
48
+ rollout_version: z.number().int().nullable(),
49
+ rollout_percent: z.number().int(),
50
+ smith_count: z.number().int().optional(),
51
+ created_at: z.string().nullable(),
52
+ updated_at: z.string().nullable(),
53
+ })
54
+ .meta({ id: "AgentOut" });
55
+
56
+ export const AgentListOut = z
57
+ .object({ data: z.array(AgentOut) })
58
+ .meta({ id: "AgentListOut" });
59
+
60
+ /** A published, immutable version snapshot of an agent. */
61
+ export const AgentVersionOut = z
62
+ .object({
63
+ version: z.number().int(),
64
+ snapshot: z.object({
65
+ instructions: z.string().nullish(),
66
+ model: z.string().nullish(),
67
+ enabled_hosted_tools: z.array(z.string()).optional(),
68
+ auto_memory: z.boolean().nullish(),
69
+ variables: z.array(AgentVariable).optional(),
70
+ }),
71
+ created_by: z.string().nullable(),
72
+ note: z.string().nullable(),
73
+ created_at: z.string().nullable(),
74
+ })
75
+ .meta({ id: "AgentVersionOut" });
76
+
77
+ export const AgentVersionListOut = z
78
+ .object({ data: z.array(AgentVersionOut) })
79
+ .meta({ id: "AgentVersionListOut" });
80
+
81
+ // ── Request bodies ──────────────────────────────────────────────────────────
82
+
83
+ export const AgentIn = z
84
+ .object({
85
+ name: z.string(),
86
+ slug: z.string().nullish(),
87
+ instructions: z.string().nullish(),
88
+ model: z.string().nullish(),
89
+ enabled_hosted_tools: z.array(z.string()).nullish(),
90
+ auto_memory: z.boolean().nullish(),
91
+ variables: z.array(AgentVariable).nullish(),
92
+ })
93
+ .meta({ id: "AgentIn" });
94
+
95
+ export const AgentPatch = z
96
+ .object({
97
+ name: z.string().nullish(),
98
+ instructions: z.string().nullish(),
99
+ model: z.string().nullish(),
100
+ enabled_hosted_tools: z.array(z.string()).nullish(),
101
+ auto_memory: z.boolean().nullish(),
102
+ variables: z.array(AgentVariable).nullish(),
103
+ })
104
+ .meta({ id: "AgentPatch" });
105
+
106
+ export const RolloutIn = z
107
+ .object({
108
+ version: z.number().int(),
109
+ percent: z.number().int().min(0).max(100).default(100),
110
+ })
111
+ .meta({ id: "RolloutIn" });
112
+
113
+ export const PublishIn = z
114
+ .object({ note: z.string().nullish() })
115
+ .meta({ id: "PublishIn" });
116
+
117
+ export const ImportIn = z
118
+ .object({ from_smith: z.string(), name: z.string() })
119
+ .meta({ id: "ImportIn" });
120
+
121
+ export const AttachIn = z
122
+ .object({
123
+ all: z.boolean().default(false),
124
+ smith_ids: z.array(z.string()).nullish(),
125
+ })
126
+ .meta({ id: "AttachIn" });
127
+
128
+ // ── Inferred consumer-facing types (re-exported by ../responses) ─────────────
129
+
130
+ export type ICAgentVariable = z.infer<typeof AgentVariable>;
131
+ export type ICAgent = z.infer<typeof AgentOut>;
132
+ export type ICAgentVersion = z.infer<typeof AgentVersionOut>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Hand-authored Zod schemas for the `approvals` resource — the wire's source of
3
+ * truth, replacing the loose generated `schemas.ts` shapes for this resource.
4
+ *
5
+ * One source, three outputs: the API imports these into its `createRoute`
6
+ * definitions (validation + emitted OpenAPI), and the consumer-facing `IC*`
7
+ * types are `z.infer`red from them here and re-exported by `../responses`. No
8
+ * Zod is pulled into a type-only consumer — `responses.ts` re-exports these as
9
+ * `export type`.
10
+ *
11
+ * `.meta({ id })` names the component so the emitted OpenAPI references it as
12
+ * `#/components/schemas/<id>` rather than inlining it.
13
+ *
14
+ * The approvals route is read-only (list + get); there is no decision/submit
15
+ * body here — approvals are resolved on the standard tool-call channel via the
16
+ * run's `/submit`, not a bespoke endpoint.
17
+ */
18
+ import { z } from "zod";
19
+
20
+ /** A first-class human-in-the-loop decision raised by a paused run. */
21
+ export const ApprovalOut = z
22
+ .object({
23
+ id: z.string(),
24
+ run_id: z.string().nullable(),
25
+ smith_id: z.string().nullable(),
26
+ tool_call_id: z.string().nullable(),
27
+ tool: z.string().nullable(),
28
+ /** The tool-call arguments awaiting a decision; `{}` when none. */
29
+ args: z.record(z.string(), z.unknown()),
30
+ /** pending | approved | rejected. */
31
+ status: z.string(),
32
+ actor: z.string().nullable(),
33
+ reason: z.string().nullable(),
34
+ created_at: z.string().nullable(),
35
+ resolved_at: z.string().nullable(),
36
+ })
37
+ .meta({ id: "ApprovalOut" });
38
+
39
+ export const ApprovalListOut = z
40
+ .object({ data: z.array(ApprovalOut) })
41
+ .meta({ id: "ApprovalListOut" });
42
+
43
+ // ── Inferred consumer-facing types (re-exported by ../responses) ─────────────
44
+
45
+ export type ICApproval = z.infer<typeof ApprovalOut>;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Hand-authored Zod schemas for the `budgets` resource — the wire's source of
3
+ * truth, replacing the loose generated `schemas.ts` shapes for this resource.
4
+ *
5
+ * One source, three outputs: the API imports these into its `createRoute`
6
+ * definitions (validation + emitted OpenAPI), and the consumer-facing `IC*`
7
+ * types are `z.infer`red from them here and re-exported by `../responses`. No
8
+ * Zod is pulled into a type-only consumer — `responses.ts` re-exports these as
9
+ * `export type`.
10
+ *
11
+ * `.meta({ id })` names the component so the emitted OpenAPI references it as
12
+ * `#/components/schemas/<id>` rather than inlining it.
13
+ */
14
+ import { z } from "zod";
15
+
16
+ /** What a budget caps; `smith`/`customer` budgets carry the id in `scope_id`. */
17
+ export const BudgetScope = z.enum(["tenant", "smith", "customer"]);
18
+
19
+ /** Whether crossing the cap warns or blocks new runs. */
20
+ export const BudgetAction = z.enum(["warn", "block"]);
21
+
22
+ /** A monthly spend cap on a scope. */
23
+ export const BudgetOut = z
24
+ .object({
25
+ id: z.string(),
26
+ scope: BudgetScope,
27
+ scope_id: z.string().nullish(),
28
+ period: z.string(),
29
+ limit_usd: z.number(),
30
+ action: BudgetAction,
31
+ created_at: z.string().nullish(),
32
+ })
33
+ .meta({ id: "BudgetOut" });
34
+
35
+ /** A budget plus its month-to-date spend, computed by `/status`. */
36
+ export const BudgetStatusOut = BudgetOut.extend({
37
+ period_start: z.string(),
38
+ period_key: z.string(),
39
+ spent: z.number(),
40
+ pct: z.number(),
41
+ over: z.boolean(),
42
+ }).meta({ id: "BudgetStatusOut" });
43
+
44
+ export const BudgetListOut = z
45
+ .object({ data: z.array(BudgetOut) })
46
+ .meta({ id: "BudgetListOut" });
47
+
48
+ // ── Request bodies ──────────────────────────────────────────────────────────
49
+
50
+ export const BudgetIn = z
51
+ .object({
52
+ scope: z.string(),
53
+ scope_id: z.string().nullish(),
54
+ limit_usd: z.number(),
55
+ action: z.string().default("warn"),
56
+ period: z.string().default("monthly"),
57
+ })
58
+ .meta({ id: "BudgetIn" });
59
+
60
+ export const BudgetPatch = z
61
+ .object({
62
+ limit_usd: z.number().nullish(),
63
+ action: z.string().nullish(),
64
+ })
65
+ .meta({ id: "BudgetPatch" });
66
+
67
+ // ── Inferred consumer-facing types (re-exported by ../responses) ─────────────
68
+
69
+ export type ICBudget = z.infer<typeof BudgetOut>;
70
+ export type ICBudgetStatus = z.infer<typeof BudgetStatusOut>;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Hand-authored Zod schemas for the `catalog` resource — the Ingram-curated MCP
3
+ * integration presets exposed at `GET /v1/catalog`.
4
+ *
5
+ * One source, three outputs: the API imports these into its `createRoute`
6
+ * definitions (validation + emitted OpenAPI), and the consumer-facing `IC*`
7
+ * type is `z.infer`red from them here and re-exported by `../responses`. No Zod
8
+ * is pulled into a type-only consumer — `responses.ts` re-exports these as
9
+ * `export type`.
10
+ *
11
+ * `.meta({ id })` names the component so the emitted OpenAPI references it as
12
+ * `#/components/schemas/<id>` rather than inlining it.
13
+ */
14
+ import { z } from "zod";
15
+
16
+ /**
17
+ * The approval-rule shape a catalog entry carries as its `default_approval_policy`.
18
+ *
19
+ * Defined inline (not imported from `./mcp`) to keep this module independent of
20
+ * the parallel mcp authoring. It mirrors the canonical mcp `ApprovalRule`:
21
+ * a tool-name `match` glob and an optional `require` mode.
22
+ */
23
+ const ApprovalRule = z.object({
24
+ match: z.string(),
25
+ require: z.string().optional(),
26
+ });
27
+
28
+ /** How a catalog integration authenticates; surfaced flattened on the wire. */
29
+ const CatalogAuth = z.object({
30
+ kind: z.string(),
31
+ provider: z.string().nullable(),
32
+ client_mode: z.string(),
33
+ });
34
+
35
+ export const CatalogEntryOut = z
36
+ .object({
37
+ slug: z.string(),
38
+ display_name: z.string(),
39
+ description: z.string(),
40
+ mcp_url: z.string(),
41
+ auth: CatalogAuth,
42
+ scopes: z.array(z.string()),
43
+ /** null = expose all discovered tools; otherwise the default-deny set. */
44
+ default_allowlist: z.array(z.string()).nullable(),
45
+ default_approval_policy: z.array(ApprovalRule),
46
+ logo_url: z.string().nullable(),
47
+ docs_url: z.string().nullable(),
48
+ })
49
+ .meta({ id: "CatalogEntryOut" });
50
+
51
+ export const CatalogListOut = z
52
+ .object({ data: z.array(CatalogEntryOut) })
53
+ .meta({ id: "CatalogListOut" });
54
+
55
+ // ── Inferred consumer-facing type (re-exported by ../responses) ──────────────
56
+
57
+ export type ICCatalogEntry = z.infer<typeof CatalogEntryOut>;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Hand-authored Zod schemas for the `channels` resource — messaging endpoints
3
+ * attached to a smith (Telegram/WhatsApp/Slack/SMS/voice/email).
4
+ *
5
+ * One source, three outputs: the API imports these into its `createRoute`
6
+ * definitions (validation + emitted OpenAPI), and the consumer-facing `IC*`
7
+ * types are `z.infer`red from them here and re-exported by `../responses`. No
8
+ * Zod is pulled into a type-only consumer — `responses.ts` re-exports these as
9
+ * `export type`.
10
+ *
11
+ * `.meta({ id })` names the component so the emitted OpenAPI references it as
12
+ * `#/components/schemas/<id>` rather than inlining it.
13
+ *
14
+ * Two response shapes on purpose: `ChannelOut` is the plain serializer
15
+ * (`api/src/runtime/channels.ts::publicChannel`) returned by GET/list, PATCH,
16
+ * and the email `provision` create. `ChannelCreatedOut` is the richer create
17
+ * response — the deferred-setup gateways (telegram/whatsapp `start_token`,
18
+ * slack `provision`/`oauth_install`) hand back binding affordances on top of
19
+ * the base shape (`start_token` + `deep_link`, slack's `install_url`/`state`,
20
+ * …). The relic `ChannelOut` did NOT document these create-only fields; the
21
+ * handler is the truth, so they live here as optionals.
22
+ */
23
+ import { z } from "zod";
24
+
25
+ export const ChannelOut = z
26
+ .object({
27
+ id: z.string(),
28
+ kind: z.string(),
29
+ address: z.string().nullable(),
30
+ provider: z.string().nullable(),
31
+ provider_metadata: z.record(z.string(), z.unknown()).optional(),
32
+ /** Names of the encrypted secret fields that are set (values never returned). */
33
+ secret_keys: z.array(z.string()).optional(),
34
+ status: z.string(),
35
+ created_at: z.string().nullable(),
36
+ })
37
+ .meta({ id: "ChannelOut" });
38
+
39
+ export const ChannelListOut = z
40
+ .object({ data: z.array(ChannelOut) })
41
+ .meta({ id: "ChannelListOut" });
42
+
43
+ /**
44
+ * The richer create response. Beyond the base `ChannelOut`, the setup gateways
45
+ * attach provider-specific binding affordances:
46
+ * - telegram `start_token`: `start_token`, `deep_link` (t.me link).
47
+ * - whatsapp `start_token`: `start_token`, `prefilled_message`, `deep_link` (wa.me link).
48
+ * - slack `provision`/`oauth_install`: `install_url`, `state`, optional `slack_app_id`.
49
+ * - email `provision`: no extras (plain `ChannelOut`).
50
+ * All are optional — which appear depends on the kind + setup mode.
51
+ */
52
+ export const ChannelCreatedOut = ChannelOut.extend({
53
+ start_token: z.string().optional(),
54
+ deep_link: z.string().optional(),
55
+ prefilled_message: z.string().optional(),
56
+ install_url: z.string().optional(),
57
+ state: z.string().optional(),
58
+ slack_app_id: z.string().optional(),
59
+ }).meta({ id: "ChannelCreatedOut" });
60
+
61
+ // ── Request bodies ──────────────────────────────────────────────────────────
62
+
63
+ export const ChannelIn = z
64
+ .object({
65
+ kind: z.string(),
66
+ address: z.string().nullish(),
67
+ provider: z.string().nullish(),
68
+ provider_metadata: z.record(z.string(), z.unknown()).optional(),
69
+ secrets: z.record(z.string(), z.unknown()).optional(),
70
+ setup: z.record(z.string(), z.unknown()).nullish(),
71
+ })
72
+ .meta({ id: "ChannelIn" });
73
+
74
+ export const ChannelPatch = z
75
+ .object({
76
+ display_name: z.string().nullish(),
77
+ owner_email: z.string().nullish(),
78
+ })
79
+ .meta({ id: "ChannelPatch" });
80
+
81
+ // ── Inferred consumer-facing types (re-exported by ../responses) ─────────────
82
+
83
+ export type ICChannel = z.infer<typeof ChannelOut>;
84
+ export type ICChannelCreated = z.infer<typeof ChannelCreatedOut>;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Hand-authored Zod schemas for the `connections` resource — per-smith OAuth
3
+ * grants under `/v1/smiths/:pid/connections`. The wire's source of truth,
4
+ * replacing the loose generated shapes for this resource.
5
+ *
6
+ * One source, three outputs: the API imports these into its `createRoute`
7
+ * definitions (validation + emitted OpenAPI), and the consumer-facing `IC*`
8
+ * types are `z.infer`red from them here and re-exported by `../responses`. No
9
+ * Zod is pulled into a type-only consumer — `responses.ts` re-exports these as
10
+ * `export type`.
11
+ *
12
+ * SECURITY INVARIANT: `ConnectionOut` NEVER carries the secret credential
13
+ * material — only `id`, `provider`, `scopes`, `status`, `expires_at`,
14
+ * `metadata`, `created_at`. The handler's `toPublic` serializer is validated
15
+ * against this schema.
16
+ *
17
+ * `.meta({ id })` names the component so the emitted OpenAPI references it as
18
+ * `#/components/schemas/<id>` rather than inlining it.
19
+ */
20
+ import { z } from "zod";
21
+
22
+ /** OAuth token material the tenant pushes in; IC stores it encrypted and never
23
+ * echoes it back. Only `kind: "oauth_tokens"` is accepted. */
24
+ export const Credential = z
25
+ .object({
26
+ kind: z.string().optional(),
27
+ access_token: z.string().nullish(),
28
+ refresh_token: z.string().nullish(),
29
+ expires_at: z.string().nullish(),
30
+ token_uri: z.string().nullish(),
31
+ })
32
+ .meta({ id: "Credential" });
33
+
34
+ /** A connection WITHOUT the secret credential material. */
35
+ export const ConnectionOut = z
36
+ .object({
37
+ id: z.string(),
38
+ provider: z.string(),
39
+ scopes: z.array(z.string()),
40
+ status: z.string(),
41
+ expires_at: z.string().nullable(),
42
+ metadata: z.record(z.string(), z.unknown()).optional(),
43
+ created_at: z.string().nullable(),
44
+ })
45
+ .meta({ id: "ConnectionOut" });
46
+
47
+ export const ConnectionListOut = z
48
+ .object({ data: z.array(ConnectionOut) })
49
+ .meta({ id: "ConnectionListOut" });
50
+
51
+ // ── Request bodies ──────────────────────────────────────────────────────────
52
+
53
+ export const ConnectionIn = z
54
+ .object({
55
+ provider: z.string(),
56
+ scopes: z.array(z.string()).optional(),
57
+ credential: Credential,
58
+ metadata: z.record(z.string(), z.unknown()).optional(),
59
+ })
60
+ .meta({ id: "ConnectionIn" });
61
+
62
+ export const ConnectionPatch = z
63
+ .object({
64
+ scopes: z.array(z.string()).nullish(),
65
+ credential: Credential.nullish(),
66
+ status: z.string().nullish(),
67
+ metadata: z.record(z.string(), z.unknown()).nullish(),
68
+ })
69
+ .meta({ id: "ConnectionPatch" });
70
+
71
+ /** The `authorize` 200 body: the minted authorize URL + echoed provider. */
72
+ export const AuthorizeOut = z
73
+ .object({ authorize_url: z.string().nullable(), provider: z.string() })
74
+ .meta({ id: "AuthorizeOut" });
75
+
76
+ /** Body for the OAuth consent broker: `POST …/connections/authorize`. */
77
+ export const AuthorizeIn = z
78
+ .object({
79
+ provider: z.string(),
80
+ return_url: z.string().nullish(),
81
+ mcp_server: z.string().nullish(),
82
+ })
83
+ .meta({ id: "AuthorizeIn" });
84
+
85
+ // ── Inferred consumer-facing types (re-exported by ../responses) ─────────────
86
+
87
+ export type ICConnection = z.infer<typeof ConnectionOut>;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Hand-authored Zod schemas for the `customers` resource — the wire's source of
3
+ * truth, replacing the loose generated shapes for this resource.
4
+ *
5
+ * One source, three outputs: the API imports these into its `createRoute`
6
+ * definitions (validation + emitted OpenAPI), and the consumer-facing `IC*`
7
+ * types are `z.infer`red from them here and re-exported by `../responses`. No
8
+ * Zod is pulled into a type-only consumer — `responses.ts` re-exports these as
9
+ * `export type`.
10
+ *
11
+ * A customer is the *tenant's* billable party for its smiths (never ours); many
12
+ * smiths roll up to one customer, so `smith_count` is a correlated count.
13
+ *
14
+ * `.meta({ id })` names the component so the emitted OpenAPI references it as
15
+ * `#/components/schemas/<id>` rather than inlining it.
16
+ */
17
+ import { z } from "zod";
18
+
19
+ export const CustomerOut = z
20
+ .object({
21
+ id: z.string(),
22
+ name: z.string(),
23
+ /** Opaque external system → id map (e.g. CRM/billing keys). */
24
+ external_ids: z.record(z.string(), z.string()),
25
+ /** Free-form tenant annotations. */
26
+ metadata: z.record(z.string(), z.unknown()),
27
+ /** Active smiths rolling up to this customer; present on single-customer reads. */
28
+ smith_count: z.number().int().optional(),
29
+ created_at: z.string().nullable(),
30
+ })
31
+ .meta({ id: "CustomerOut" });
32
+
33
+ /** Paginated list envelope: keyset cursor over `created_at, id` (desc). */
34
+ export const CustomerListOut = z
35
+ .object({
36
+ data: z.array(CustomerOut),
37
+ next_cursor: z.string().nullable(),
38
+ has_more: z.boolean(),
39
+ })
40
+ .meta({ id: "CustomerListOut" });
41
+
42
+ // ── Request bodies ──────────────────────────────────────────────────────────
43
+
44
+ export const CustomerCreate = z
45
+ .object({
46
+ name: z.string(),
47
+ external_ids: z.record(z.string(), z.string()).optional(),
48
+ metadata: z.record(z.string(), z.unknown()).optional(),
49
+ })
50
+ .meta({ id: "CustomerCreate" });
51
+
52
+ export const CustomerPatch = z
53
+ .object({
54
+ name: z.string().nullish(),
55
+ external_ids: z.record(z.string(), z.string()).nullish(),
56
+ metadata: z.record(z.string(), z.unknown()).nullish(),
57
+ })
58
+ .meta({ id: "CustomerPatch" });
59
+
60
+ // ── Inferred consumer-facing types (re-exported by ../responses) ─────────────
61
+
62
+ export type ICCustomer = z.infer<typeof CustomerOut>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Hand-authored Zod schemas for the `email` channel-config resource — the
3
+ * wire's source of truth for the tenant Cloudflare sending config.
4
+ *
5
+ * One source, three outputs: the API imports these into its `createRoute`
6
+ * definitions (validation + emitted OpenAPI), and the consumer-facing `IC*`
7
+ * type is `z.infer`red from `EmailConfigOut` here and re-exported by
8
+ * `../responses` as `export type` — no Zod is pulled into a type-only consumer.
9
+ *
10
+ * `.meta({ id })` names the component so the emitted OpenAPI references it as
11
+ * `#/components/schemas/<id>` rather than inlining it.
12
+ *
13
+ * The out shape is the union of two builders in `api/src/routes/email.ts`
14
+ * (via `runtime/email.ts`):
15
+ * - GET `/v1/tenant/email` (`configStatus`) — `{ configured: false }`, or
16
+ * `{ configured: true, from_domain, display_name, has_token, inbound_url }`.
17
+ * - PUT `/v1/tenant/email` (`configureEmail`) — `{ configured: true,
18
+ * from_domain, display_name, inbound_url, inbound_secret }`.
19
+ * Hence `configured` is the only required field; everything else is optional,
20
+ * and `inbound_secret` is present only on the PUT response (shown once).
21
+ */
22
+ import { z } from "zod";
23
+
24
+ export const EmailConfigOut = z
25
+ .object({
26
+ /** False when the tenant has no email config; true otherwise. */
27
+ configured: z.boolean(),
28
+ from_domain: z.string().optional(),
29
+ display_name: z.string().nullish(),
30
+ /** Present on GET when configured — the API token is never returned. */
31
+ has_token: z.boolean().optional(),
32
+ inbound_url: z.string().optional(),
33
+ /** Only present on PUT (shown once) — wire it into the inbound worker. */
34
+ inbound_secret: z.string().optional(),
35
+ })
36
+ .meta({ id: "EmailConfigOut" });
37
+
38
+ // ── Request body ─────────────────────────────────────────────────────────────
39
+
40
+ export const EmailConfigIn = z
41
+ .object({
42
+ cloudflare_account_id: z.string(),
43
+ cloudflare_api_token: z.string(),
44
+ from_domain: z.string(),
45
+ display_name: z.string().nullish(),
46
+ inbound_secret: z.string().nullish(),
47
+ })
48
+ .meta({ id: "EmailConfigIn" });
49
+
50
+ // ── Inferred consumer-facing type (re-exported by ../responses) ──────────────
51
+
52
+ export type ICEmailConfig = z.infer<typeof EmailConfigOut>;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Hand-authored Zod schemas — the wire's source of truth, one resource module
3
+ * per file. The API imports these into its `createRoute` definitions (runtime
4
+ * validation + emitted OpenAPI); `../responses` re-exports the `z.infer`red
5
+ * `IC*` types from them.
6
+ *
7
+ * Resources are migrated here off the generated `../schemas.ts` one at a time;
8
+ * when the last one lands, the generated file and the `openapi-zod-client` step
9
+ * are deleted and this becomes the sole `schemas` source.
10
+ */
11
+ export * from "./agents";
12
+ export * from "./approvals";
13
+ export * from "./budgets";
14
+ export * from "./catalog";
15
+ export * from "./channels";
16
+ export * from "./connections";
17
+ export * from "./customers";
18
+ export * from "./email";
19
+ export * from "./mcp";
20
+ export * from "./memories";
21
+ export * from "./observability";
22
+ export * from "./projects";
23
+ export * from "./runs";
24
+ export * from "./schedules";
25
+ export * from "./slack";
26
+ export * from "./smith-revisions";
27
+ export * from "./smiths";
28
+ export * from "./telegram";
29
+ export * from "./tenant";
30
+ export * from "./whatsapp";