@oxygen-agent/cli 1.1011.493 → 1.1011.600
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 +1 -1
- package/dist/crm-file-read-commands.d.ts +6 -0
- package/dist/crm-file-read-commands.js +16 -0
- package/dist/feed-read-flags.d.ts +3 -0
- package/dist/feed-read-flags.js +9 -0
- package/dist/functions-commands.js +2 -1
- package/dist/help.js +18 -5
- package/dist/index.js +168 -107
- package/dist/sender-limits-flags.js +6 -4
- package/dist/settings-commands.js +11 -11
- package/dist/whatsapp-account-flags.d.ts +4 -0
- package/dist/whatsapp-account-flags.js +18 -0
- package/node_modules/@oxygen/cli-ugc/dist/commands.js +2 -2
- package/node_modules/@oxygen/shared/dist/billing.d.ts +41 -0
- package/node_modules/@oxygen/shared/dist/billing.js +56 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +251 -21
- package/node_modules/@oxygen/shared/dist/contracts/billing-owner-read.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/contracts/billing-owner-read.js +7 -0
- package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +4 -2
- package/node_modules/@oxygen/shared/dist/contracts/copilot-sessions.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/contracts/copilot-sessions.js +7 -0
- package/node_modules/@oxygen/shared/dist/contracts/crm-file-bytes.d.ts +33 -0
- package/node_modules/@oxygen/shared/dist/contracts/crm-file-bytes.js +12 -0
- package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +6 -3
- package/node_modules/@oxygen/shared/dist/contracts/function-library-read.d.ts +14 -0
- package/node_modules/@oxygen/shared/dist/contracts/function-library-read.js +16 -0
- package/node_modules/@oxygen/shared/dist/contracts/inbox-drafts-read.d.ts +45 -0
- package/node_modules/@oxygen/shared/dist/contracts/inbox-drafts-read.js +21 -0
- package/node_modules/@oxygen/shared/dist/contracts/limits-read.d.ts +29 -0
- package/node_modules/@oxygen/shared/dist/contracts/limits-read.js +121 -0
- package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/pipedrive-catalog.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/contracts/pipedrive-catalog.js +29 -0
- package/node_modules/@oxygen/shared/dist/contracts/publishing-reads.d.ts +181 -0
- package/node_modules/@oxygen/shared/dist/contracts/publishing-reads.js +62 -0
- package/node_modules/@oxygen/shared/dist/contracts/sender-limits.js +7 -5
- package/node_modules/@oxygen/shared/dist/contracts/sequence-list.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/contracts/sequence-list.js +8 -0
- package/node_modules/@oxygen/shared/dist/contracts/stored-read-window.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/contracts/stored-read-window.js +8 -0
- package/node_modules/@oxygen/shared/dist/contracts/table-feed-read.d.ts +67 -0
- package/node_modules/@oxygen/shared/dist/contracts/table-feed-read.js +19 -0
- package/node_modules/@oxygen/shared/dist/contracts/table-row-read.d.ts +30 -0
- package/node_modules/@oxygen/shared/dist/contracts/table-row-read.js +9 -0
- package/node_modules/@oxygen/shared/dist/contracts/whatsapp-reads.d.ts +111 -0
- package/node_modules/@oxygen/shared/dist/contracts/whatsapp-reads.js +33 -0
- package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.d.ts +6 -5
- package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.js +5 -5
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.d.ts +4 -1
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.generated.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.generated.js +26 -0
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.js +2 -32
- package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/feature-gates.js +6 -0
- package/node_modules/@oxygen/shared/dist/first-month-offer-window.d.ts +34 -0
- package/node_modules/@oxygen/shared/dist/first-month-offer-window.js +41 -0
- package/node_modules/@oxygen/shared/dist/first-month-offer.d.ts +42 -0
- package/node_modules/@oxygen/shared/dist/first-month-offer.js +56 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +32 -1
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +68 -4
- package/node_modules/@oxygen/shared/dist/otlp-log-sink.d.ts +6 -2
- package/node_modules/@oxygen/shared/dist/otlp-log-sink.js +6 -1
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +11 -0
- package/node_modules/@oxygen/shared/dist/sql-error.js +13 -2
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +17 -1
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +11 -1
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +29 -3
- package/package.json +1 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/** Customer operations missing from one execution surface, with one field contract. */
|
|
2
2
|
import { arr, bool, enumOf, int, nullable, num, obj, str } from "./schema.js";
|
|
3
3
|
import { DATABASE_ATTACH_CONTRACT, DATABASE_MIGRATE_CONTRACT, DATABASE_QUERY_CONTRACT } from "./database.js";
|
|
4
|
+
import { WHATSAPP_DRAFTS_READ_CONTRACT } from "./whatsapp-reads.js";
|
|
5
|
+
import { BILLING_OWNER_READ_CONTRACT } from "./billing-owner-read.js";
|
|
6
|
+
import { COPILOT_SESSIONS_LIST_CONTRACT } from "./copilot-sessions.js";
|
|
4
7
|
const text = (description) => str(description === undefined ? {} : { description });
|
|
5
8
|
const list = () => arr(str());
|
|
6
9
|
const json = { type: "object", additionalProperties: true };
|
|
@@ -28,7 +31,7 @@ export const CUSTOMER_OPERATIONS = [
|
|
|
28
31
|
operation("approvals.cancel", "/api/cli/collab/requests/{request_id}/cancel", "POST", "approvals cancel", "oxygen_approvals_cancel", "collaboration", "Withdraw an approval request you created; grants no approval.", { note: text() }, [], mcpOnly),
|
|
29
32
|
operation("members.client_role", "/api/cli/collab/members/role", "POST", "approvals client grant", "oxygen_members_client_role_set", "collaboration", "Set or clear a member's restricted client role; workspace admin only.", { email: text(), role: nullable(enumOf(["client"])) }, ["email", "role"], mcpOnly),
|
|
30
33
|
operation("members.workspace_role", "/api/cli/collab/members/workspace-role", "POST", "orgs member-role", "oxygen_members_workspace_role_set", "collaboration", "Change a member's workspace role; admin only and the last administrator is protected.", { email: text(), role: enumOf(["admin", "member"]) }, ["email", "role"], mcpOnly),
|
|
31
|
-
operation("copilot.sessions.list", "/api/cli/copilot/sessions", "GET", "copilot list", "oxygen_copilot_sessions_list", "agents", "List attended Copilot sessions in this workspace.",
|
|
34
|
+
operation("copilot.sessions.list", "/api/cli/copilot/sessions", "GET", "copilot list", "oxygen_copilot_sessions_list", "agents", "List attended Copilot sessions in this workspace.", COPILOT_SESSIONS_LIST_CONTRACT.properties, [], mcpOnly),
|
|
32
35
|
operation("copilot.sessions.update", "/api/cli/copilot/sessions/{session_id}", "PATCH", "copilot policy set", "oxygen_copilot_session_update", "agents", "Set an attended Copilot session's approval and screen-follow policies. Paid auto-approval is explicit standing authorization.", { auto_approve_policy: enumOf(["off", "paid"]), surface_policy: enumOf(["off", "follow"]) }, [], { spends: true }),
|
|
33
36
|
operation("copilot.attachments.list", "/api/cli/copilot/sessions/{session_id}/attachments", "GET", "copilot attachments list", "oxygen_copilot_attachments_list", "agents", "List sent and unsent attachments in one Copilot session.", {}, []),
|
|
34
37
|
operation("copilot.attachments.prepare", "/api/cli/copilot/sessions/{session_id}/attachments", "POST", "copilot attachments prepare", "oxygen_copilot_attachment_prepare", "agents", "Reserve a Copilot attachment and return its scoped upload URL; complete only after uploading the exact bytes.", { file_name: text(), content_type: text(), content_length: int({ minimum: 1 }) }, ["file_name", "content_length"]),
|
|
@@ -87,7 +90,7 @@ export const CUSTOMER_OPERATIONS = [
|
|
|
87
90
|
operation("tables.webhooks.create", "/api/cli/tables/webhooks", "POST", "tables webhook create", "oxygen_table_webhook_create", "tables", "Create an inbound Table webhook or platform LinkedIn feed. Optional auto_run is standing permission with explicit or plan-default delivery ceilings.", { table: text(), name: text(), mode: enumOf(["insert", "upsert"]), upsert_key: text(), items_path: text(), event_id_path: text(), event_type_path: text(), occurred_at_path: text(), auth_mode: enumOf(["none", "secret"]), feed_kind: enumOf(["generic", "unipile"]), unipile_account_id: text(), event_types: list(), mapping: text(), auto_run: json }, ["table"], mcpOnly),
|
|
88
91
|
operation("tables.webhooks.update", "/api/cli/tables/webhooks", "PATCH", "tables webhook update", "oxygen_table_webhook_update", "tables", "Edit a Table webhook's name, state or standing auto_run; auth_mode=secret rotates its secret and returns it once.", { endpoint_id: text(), name: text(), status: enumOf(["active", "disabled"]), auth_mode: enumOf(["none", "secret"]), auto_run: json, clear_auto_run: bool() }, ["endpoint_id"], mcpOnly),
|
|
89
92
|
operation("whatsapp.draft", "/api/cli/whatsapp/draft", "POST", "whatsapp draft", "oxygen_whatsapp_draft", "inbox", "Generate a WhatsApp reply draft in the workspace voice, or approve a reviewed draft for an existing chat. Draft generation uses managed AI credits.", { conversation_id: text(), goal: text(), row_id: text(), object: text(), approved: bool(), draft_id: text(), text: text() }, ["conversation_id"], { ...cliOnly, spends: true }),
|
|
90
|
-
operation("whatsapp.drafts.list", "/api/cli/whatsapp/drafts", "GET", "whatsapp drafts list", "oxygen_whatsapp_drafts_list", "inbox", "Read
|
|
93
|
+
operation("whatsapp.drafts.list", "/api/cli/whatsapp/drafts", "GET", "whatsapp drafts list", "oxygen_whatsapp_drafts_list", "inbox", "Read stored WhatsApp agent drafts; personal composer drafts stay private. Use summary and exact draft_id/value for long content.", WHATSAPP_DRAFTS_READ_CONTRACT.properties, [], cliOnly),
|
|
91
94
|
operation("whatsapp.drafts.update", "/api/cli/whatsapp/drafts/update", "POST", "whatsapp drafts update", "oxygen_whatsapp_draft_update", "inbox", "Edit an open WhatsApp draft before approving or sending it.", { draft_id: text(), text: text() }, ["draft_id", "text"], cliOnly),
|
|
92
95
|
operation("whatsapp.drafts.reject", "/api/cli/whatsapp/drafts/reject", "POST", "whatsapp drafts reject", "oxygen_whatsapp_draft_reject", "inbox", "Reject an open WhatsApp draft without sending it.", { draft_id: text(), reason: text() }, ["draft_id"], cliOnly),
|
|
93
96
|
operation("worker.repair", "/api/cli/worker/repair", "POST", "worker repair", "oxygen_workspace_queue_repair", "workspace", "Repair this workspace's stalled execution queues through the native bounded recovery path.", {}, [], mcpOnly),
|
|
@@ -110,7 +113,7 @@ export const CUSTOMER_OPERATIONS = [
|
|
|
110
113
|
operation("knowledge.files.download", "/api/cli/knowledge/files/download", "GET", "knowledge files download", "oxygen_knowledge_file_download", "knowledge", "Download exact bytes for one entitled Knowledge file or pinned revision.", { id: text(), revision: int({ minimum: 1 }) }, ["id"], { ...mcpOnly, binary: { maxBytes: 50 * 1024 * 1024 } }),
|
|
111
114
|
operation("mailboxes.sequences", "/api/cli/mailboxes/{mailbox_id}/sequences", "GET", "mailboxes sequences", "oxygen_mailbox_sequences", "deliverability", "Read the sequences using one sending mailbox by id or email address."),
|
|
112
115
|
operation("managed_inboxes.registrant", "/api/cli/managed-inboxes/registrant", "GET", "managed-inboxes registrant", "oxygen_managed_inbox_registrant", "deliverability", "Read this workspace's domain-registration contact.", {}, [], mcpOnly),
|
|
113
|
-
operation("orgs.billing_owners", "/api/cli/orgs/billing-owners", "GET", "orgs billing-owners", "oxygen_workspace_billing_owners", "workspace", "Read
|
|
116
|
+
operation("orgs.billing_owners", "/api/cli/orgs/billing-owners", "GET", "orgs billing-owners", "oxygen_workspace_billing_owners", "workspace", "Read the current workspace's billing owner, entitlement and payer candidates, including ineligibility reasons. Optional organization asserts the current workspace. Free.", BILLING_OWNER_READ_CONTRACT.properties, [], mcpOnly),
|
|
114
117
|
operation("search.runs.items", "/api/cli/search/runs/{search_run_id}/items", "GET", "search runs items", "oxygen_search_run_items", "enrichment", "Read durable search ingestion items by run id and optional status.", { status: text(), limit: int({ minimum: 1 }) }, [], mcpOnly),
|
|
115
118
|
operation("senders.checkpoints", "/api/cli/senders/checkpoints", "GET", "senders checkpoints", "oxygen_sender_checkpoints", "sequences", "Read connected sender authentication checkpoints that need human completion.", {}, [], mcpOnly),
|
|
116
119
|
operation("suppressions.domains.list", "/api/cli/suppressions/domains", "GET", "suppressions domains", "oxygen_suppressed_domains_list", "sequences", "Read domain-wide email suppressions with reason, search and pagination.", { reason: text(), search: text(), limit: int({ minimum: 1 }), offset: int({ minimum: 0 }) }, [], mcpOnly),
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const FUNCTION_LIBRARY_FILTER: {
|
|
2
|
+
readonly all: "all";
|
|
3
|
+
readonly managed: "managed";
|
|
4
|
+
readonly favorites: "favorites";
|
|
5
|
+
};
|
|
6
|
+
export declare const FUNCTION_LIBRARY_FILTERS: readonly ["all", "managed", "favorites"];
|
|
7
|
+
export type FunctionLibraryFilter = (typeof FUNCTION_LIBRARY_FILTERS)[number];
|
|
8
|
+
export declare const FUNCTION_LIBRARY_LIST_READ_CONTRACT: import("./schema.js").ContractObject<{
|
|
9
|
+
readonly filter: {
|
|
10
|
+
type: "string";
|
|
11
|
+
enum: readonly ("all" | "favorites" | "managed")[];
|
|
12
|
+
description?: string;
|
|
13
|
+
};
|
|
14
|
+
}, never>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { enumOf, obj } from "./schema.js";
|
|
2
|
+
export const FUNCTION_LIBRARY_FILTER = {
|
|
3
|
+
all: "all",
|
|
4
|
+
managed: "managed",
|
|
5
|
+
favorites: "favorites",
|
|
6
|
+
};
|
|
7
|
+
export const FUNCTION_LIBRARY_FILTERS = [
|
|
8
|
+
FUNCTION_LIBRARY_FILTER.all,
|
|
9
|
+
FUNCTION_LIBRARY_FILTER.managed,
|
|
10
|
+
FUNCTION_LIBRARY_FILTER.favorites,
|
|
11
|
+
];
|
|
12
|
+
export const FUNCTION_LIBRARY_LIST_READ_CONTRACT = obj({
|
|
13
|
+
filter: enumOf(FUNCTION_LIBRARY_FILTERS, {
|
|
14
|
+
description: "list_functions: all (default), favorites, or retired managed (empty).",
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export declare const INBOX_DRAFT_STATUSES: readonly ["queued", "edited", "approved", "sent", "rejected"];
|
|
2
|
+
export declare const INBOX_DRAFT_CHANNELS: readonly ["email", "linkedin"];
|
|
3
|
+
export declare const INBOX_DRAFTS_READ_CONTRACT: import("./schema.js").ContractObject<{
|
|
4
|
+
readonly projection: {
|
|
5
|
+
type: "string";
|
|
6
|
+
enum: readonly ("full" | "summary" | "value")[];
|
|
7
|
+
description?: string;
|
|
8
|
+
};
|
|
9
|
+
readonly path: import("./schema.js").StringProperty;
|
|
10
|
+
readonly value_cursor: import("./schema.js").StringProperty;
|
|
11
|
+
readonly max_chars: {
|
|
12
|
+
type: "integer";
|
|
13
|
+
description?: string;
|
|
14
|
+
minimum?: number;
|
|
15
|
+
maximum?: number;
|
|
16
|
+
"x-clamp"?: true;
|
|
17
|
+
};
|
|
18
|
+
readonly status: import("./schema.js").StringProperty;
|
|
19
|
+
readonly channel: {
|
|
20
|
+
type: "string";
|
|
21
|
+
enum: readonly ("email" | "linkedin")[];
|
|
22
|
+
description?: string;
|
|
23
|
+
};
|
|
24
|
+
readonly sequence_id: import("./schema.js").StringProperty;
|
|
25
|
+
readonly limit: {
|
|
26
|
+
type: "integer";
|
|
27
|
+
description?: string;
|
|
28
|
+
minimum?: number;
|
|
29
|
+
maximum?: number;
|
|
30
|
+
"x-clamp"?: true;
|
|
31
|
+
};
|
|
32
|
+
readonly conversation_id: import("./schema.js").StringProperty;
|
|
33
|
+
readonly offset: {
|
|
34
|
+
type: "integer";
|
|
35
|
+
description?: string;
|
|
36
|
+
minimum?: number;
|
|
37
|
+
maximum?: number;
|
|
38
|
+
"x-clamp"?: true;
|
|
39
|
+
};
|
|
40
|
+
readonly draft_id: import("./schema.js").StringProperty;
|
|
41
|
+
}, never>;
|
|
42
|
+
/** PostgreSQL's UUID input accepts braces and optional hyphens after groups of
|
|
43
|
+
* four hex digits. Validate without normalizing: LinkedIn's text comparison
|
|
44
|
+
* and the native SQL UUID comparison retain their shipped semantics. */
|
|
45
|
+
export declare function isInboxDraftUuid(value: string): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { enumOf, int, obj, str } from "./schema.js";
|
|
2
|
+
import { STORED_READ_WINDOW_FIELDS } from "./stored-read-window.js";
|
|
3
|
+
export const INBOX_DRAFT_STATUSES = ["queued", "edited", "approved", "sent", "rejected"];
|
|
4
|
+
export const INBOX_DRAFT_CHANNELS = ["email", "linkedin"];
|
|
5
|
+
export const INBOX_DRAFTS_READ_CONTRACT = obj({
|
|
6
|
+
status: str({ description: "Comma-separated draft statuses (queued,edited,approved,sent,rejected)." }),
|
|
7
|
+
channel: enumOf(INBOX_DRAFT_CHANNELS, { description: "Filter to one channel (email | linkedin); omit for both." }),
|
|
8
|
+
sequence_id: str({ description: "Filter to one campaign (sequence) id (email drafts only)." }),
|
|
9
|
+
limit: int({ minimum: 1, maximum: 200 }),
|
|
10
|
+
conversation_id: str({ description: "Local email or LinkedIn conversation UUID. Never a provider chat id." }),
|
|
11
|
+
offset: int({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER, description: "Skip this many matching drafts; default 0. Live queue changes may shift offsets." }),
|
|
12
|
+
draft_id: str({ description: "Exact eligible agent draft UUID, with all other queue filters still applied. Use for stable value selection." }),
|
|
13
|
+
...STORED_READ_WINDOW_FIELDS,
|
|
14
|
+
});
|
|
15
|
+
/** PostgreSQL's UUID input accepts braces and optional hyphens after groups of
|
|
16
|
+
* four hex digits. Validate without normalizing: LinkedIn's text comparison
|
|
17
|
+
* and the native SQL UUID comparison retain their shipped semantics. */
|
|
18
|
+
export function isInboxDraftUuid(value) {
|
|
19
|
+
const unwrapped = value.startsWith("{") && value.endsWith("}") ? value.slice(1, -1) : value;
|
|
20
|
+
return /^(?:[0-9a-f]{4}-?){7}[0-9a-f]{4}$/i.test(unwrapped);
|
|
21
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const LIMITS_READ_SECTIONS: readonly ["summary", "capacity", "plan", "pricing", "api", "webhooks", "spend", "sending", "workflow", "all"];
|
|
2
|
+
export type LimitsReadSection = typeof LIMITS_READ_SECTIONS[number];
|
|
3
|
+
export declare const LIMITS_READ_QUERY_CONTRACT: import("./schema.js").ContractObject<{
|
|
4
|
+
readonly section: {
|
|
5
|
+
type: "string";
|
|
6
|
+
enum: readonly ("all" | "api" | "capacity" | "plan" | "pricing" | "sending" | "spend" | "summary" | "webhooks" | "workflow")[];
|
|
7
|
+
description?: string;
|
|
8
|
+
};
|
|
9
|
+
readonly band: {
|
|
10
|
+
type: "string";
|
|
11
|
+
enum: readonly ("199" | "1999" | "49" | "499" | "99" | "999" | "free")[];
|
|
12
|
+
description?: string;
|
|
13
|
+
};
|
|
14
|
+
readonly max_chars: {
|
|
15
|
+
type: "integer";
|
|
16
|
+
description?: string;
|
|
17
|
+
minimum?: number;
|
|
18
|
+
maximum?: number;
|
|
19
|
+
"x-clamp"?: true;
|
|
20
|
+
};
|
|
21
|
+
}, never>;
|
|
22
|
+
export type LimitsReadQuery = {
|
|
23
|
+
section?: LimitsReadSection;
|
|
24
|
+
band?: string;
|
|
25
|
+
max_chars?: number;
|
|
26
|
+
};
|
|
27
|
+
export declare function limitsReadQueryIssue(query: LimitsReadQuery): string | undefined;
|
|
28
|
+
/** Legacy unqualified HTTP/CLI stays full. Native callers always select a section. */
|
|
29
|
+
export declare function projectLimitsRead(data: Record<string, unknown>, query: LimitsReadQuery): Record<string, unknown>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { enumOf, int, obj } from "./schema.js";
|
|
2
|
+
export const LIMITS_READ_SECTIONS = ["summary", "capacity", "plan", "pricing", "api", "webhooks", "spend", "sending", "workflow", "all"];
|
|
3
|
+
export const LIMITS_READ_QUERY_CONTRACT = obj({
|
|
4
|
+
section: enumOf(LIMITS_READ_SECTIONS, { description: "summary by default on MCP; capacity names the largest reconciled Table." }),
|
|
5
|
+
band: enumOf(["free", "49", "99", "199", "499", "999", "1999"], { description: "plan/all only; selecting a band defaults to plan." }),
|
|
6
|
+
max_chars: int({ minimum: 1000, maximum: 10000, description: "Projection character ceiling; omissions are named." }),
|
|
7
|
+
});
|
|
8
|
+
export function limitsReadQueryIssue(query) {
|
|
9
|
+
if (query.band && query.section && query.section !== "plan" && query.section !== "all")
|
|
10
|
+
return "band requires the plan or all section.";
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
14
|
+
const pick = (value, keys) => Object.fromEntries(keys.filter(key => Object.hasOwn(value, key)).map(key => [key, value[key]]));
|
|
15
|
+
const COMMON = ["organization_id", "plan_band", "plan_band_label", "plan_tier", "limits_tier", "source", "entitled", "shared_billing", "web_url", "deepLink"];
|
|
16
|
+
const SECTION_FIELDS = { api: "api_rate_limits", webhooks: "webhook_ingress", spend: "spend_safety_defaults", sending: "sending_limits", workflow: "workflow_limits" };
|
|
17
|
+
function capacityProjection(value) {
|
|
18
|
+
const capacity = record(value), usage = record(capacity.usage), reconciled = usage.row_usage_reconciled === true;
|
|
19
|
+
return {
|
|
20
|
+
...pick(capacity, ["plan_band", "status", "effective_at", "target", "covers", "crm_records_share_table_storage", "platform_guard"]),
|
|
21
|
+
usage_status: capacity.usage_status ?? "temporarily_unavailable",
|
|
22
|
+
row_count_scope: "all_registered_workspace_tables_including_crm_and_archived_tables",
|
|
23
|
+
usage: Object.keys(usage).length ? {
|
|
24
|
+
...usage,
|
|
25
|
+
workspace_rows: reconciled ? usage.workspace_rows ?? null : null,
|
|
26
|
+
largest_table_rows: reconciled ? usage.largest_table_rows ?? null : null,
|
|
27
|
+
largest_table: reconciled ? usage.largest_table ?? null : null,
|
|
28
|
+
} : null,
|
|
29
|
+
note: "Rows are registry counts, available only after every Table is reconciled. Database bytes combine the last measured bytes and estimated subsequent growth; the estimate flag and timestamps name that state. Object storage is separate.",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function pricingBand(value) {
|
|
33
|
+
const row = record(value);
|
|
34
|
+
return { ...pick(row, ["band", "label", "monthly_price_usd", "monthly_credits", "volume_bonus_percent", "usd_per_100_credits", "current"]),
|
|
35
|
+
storage: pick(record(row.storage), ["table_row_limit", "workspace_row_limit", "workspace_database_limit_bytes", "status", "effective_at", "target"]) };
|
|
36
|
+
}
|
|
37
|
+
/** Legacy unqualified HTTP/CLI stays full. Native callers always select a section. */
|
|
38
|
+
export function projectLimitsRead(data, query) {
|
|
39
|
+
if (!query.section && query.max_chars === undefined && !query.band)
|
|
40
|
+
return data;
|
|
41
|
+
const section = query.section ?? (query.band ? "plan" : "all"), maxChars = query.max_chars ?? 10000;
|
|
42
|
+
const prior = record(data.projection);
|
|
43
|
+
const sameSelection = prior.section === section && (prior.band ?? null) === (query.band ?? null);
|
|
44
|
+
if (sameSelection
|
|
45
|
+
&& typeof prior.max_chars === "number" && prior.max_chars <= maxChars
|
|
46
|
+
&& Array.isArray(prior.omitted_fields) && JSON.stringify(data).length <= maxChars)
|
|
47
|
+
return data;
|
|
48
|
+
const bands = Array.isArray(data.plan_bands) ? data.plan_bands : [];
|
|
49
|
+
const current = bands.find(row => record(row).band === data.plan_band);
|
|
50
|
+
let selected = pick(data, COMMON);
|
|
51
|
+
if (sameSelection || section === "all")
|
|
52
|
+
selected = { ...data };
|
|
53
|
+
else if (section === "summary" || section === "capacity") {
|
|
54
|
+
selected.storage_capacity = capacityProjection(data.storage_capacity);
|
|
55
|
+
if (section === "summary" && current)
|
|
56
|
+
selected.current_plan = pricingBand(current);
|
|
57
|
+
}
|
|
58
|
+
else if (section === "plan") {
|
|
59
|
+
const band = query.band ?? String(data.plan_band ?? "");
|
|
60
|
+
selected.selected_band = band;
|
|
61
|
+
selected.plan_bands = bands.filter(row => record(row).band === band);
|
|
62
|
+
}
|
|
63
|
+
else if (section === "pricing") {
|
|
64
|
+
selected.plan_bands = bands.map(pricingBand);
|
|
65
|
+
const schedule = record(data.pricing_schedule);
|
|
66
|
+
selected.pricing_schedule = {
|
|
67
|
+
...pick(schedule, ["effective_at", "status"]),
|
|
68
|
+
changes: Array.isArray(schedule.changes) ? schedule.changes.map(value => pick(record(value), ["key", "value_before", "value_after", "value_in_force"])) : [],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
else
|
|
72
|
+
selected[SECTION_FIELDS[section]] = data[SECTION_FIELDS[section]];
|
|
73
|
+
if (!sameSelection && section === "all" && query.band)
|
|
74
|
+
selected.plan_bands = bands.filter(row => record(row).band === query.band);
|
|
75
|
+
// Copy before pruning: the full HTTP response and caller's source object are
|
|
76
|
+
// never mutated. Each removed field is named; there is no invented cursor.
|
|
77
|
+
const result = JSON.parse(JSON.stringify(selected));
|
|
78
|
+
const omitted = sameSelection && Array.isArray(prior.omitted_fields)
|
|
79
|
+
? prior.omitted_fields.filter((value) => typeof value === "string") : [];
|
|
80
|
+
delete result.projection;
|
|
81
|
+
const receipt = { section, band: query.band ?? null, max_chars: maxChars, omitted_fields: omitted, truncated: omitted.length > 0, available_sections: LIMITS_READ_SECTIONS };
|
|
82
|
+
const bounded = { projection: receipt, ...result };
|
|
83
|
+
const markOmitted = (path) => {
|
|
84
|
+
// A whole omitted parent accounts for its children. Keeping every leaf
|
|
85
|
+
// after removing that parent would make the receipt itself unbounded.
|
|
86
|
+
for (let index = omitted.length - 1; index >= 0; index--)
|
|
87
|
+
if (omitted[index] === path || omitted[index].startsWith(`${path}.`))
|
|
88
|
+
omitted.splice(index, 1);
|
|
89
|
+
if (!omitted.some(parent => path.startsWith(`${parent}.`)))
|
|
90
|
+
omitted.push(path);
|
|
91
|
+
receipt.truncated = true;
|
|
92
|
+
};
|
|
93
|
+
const candidates = [];
|
|
94
|
+
const collect = (value, prefix = "") => {
|
|
95
|
+
for (const [key, child] of Object.entries(value)) {
|
|
96
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
97
|
+
if (!prefix && (COMMON.includes(key) || key === "projection"))
|
|
98
|
+
continue;
|
|
99
|
+
if (typeof child === "string" || Array.isArray(child))
|
|
100
|
+
candidates.push({ parent: value, key, path, size: JSON.stringify(child).length });
|
|
101
|
+
else if (child && typeof child === "object")
|
|
102
|
+
collect(child, path);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
collect(bounded);
|
|
106
|
+
for (const candidate of candidates.sort((a, b) => b.size - a.size || a.path.localeCompare(b.path))) {
|
|
107
|
+
if (JSON.stringify(bounded).length <= maxChars)
|
|
108
|
+
break;
|
|
109
|
+
delete candidate.parent[candidate.key];
|
|
110
|
+
markOmitted(candidate.path);
|
|
111
|
+
}
|
|
112
|
+
// Extremely small windows or future numeric objects may need a whole section
|
|
113
|
+
// omitted. Keep the exact receipt and workspace identity, never a false zero.
|
|
114
|
+
for (const key of Object.keys(bounded).filter(key => key !== "projection" && !COMMON.includes(key)).sort((a, b) => JSON.stringify(bounded[b]).length - JSON.stringify(bounded[a]).length)) {
|
|
115
|
+
if (JSON.stringify(bounded).length <= maxChars)
|
|
116
|
+
break;
|
|
117
|
+
delete bounded[key];
|
|
118
|
+
markOmitted(key);
|
|
119
|
+
}
|
|
120
|
+
return bounded;
|
|
121
|
+
}
|
|
@@ -3,6 +3,6 @@ import { int, obj, str } from "./schema.js";
|
|
|
3
3
|
export const LINKEDIN_SENT_INVITES_MAX_LIMIT = 200;
|
|
4
4
|
export const LINKEDIN_SENT_INVITES_QUERY_CONTRACT = obj({
|
|
5
5
|
account: str({ description: "Sender id, connection id, or Unipile account id." }),
|
|
6
|
-
limit: int({ minimum: 1, description: "
|
|
6
|
+
limit: int({ minimum: 1, description: "Requested page size; v2 defaults 20/caps 100; HTTP caps 200." }),
|
|
7
7
|
cursor: str({ description: "Pagination cursor from a previous response." }),
|
|
8
8
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Infer } from "./schema.js";
|
|
2
|
+
/** The native saved-filter/field read can charge four own-key provider fees. */
|
|
3
|
+
export declare const PIPEDRIVE_CATALOG_QUERY_CONTRACT: import("./schema.js").ContractObject<{
|
|
4
|
+
readonly query: import("./schema.js").StringProperty;
|
|
5
|
+
readonly approved: import("./schema.js").BooleanProperty;
|
|
6
|
+
readonly max_credits: {
|
|
7
|
+
type: "number";
|
|
8
|
+
description?: string;
|
|
9
|
+
minimum?: number;
|
|
10
|
+
maximum?: number;
|
|
11
|
+
"x-clamp"?: true;
|
|
12
|
+
};
|
|
13
|
+
}, never>;
|
|
14
|
+
export type PipedriveCatalogQuery = Infer<typeof PIPEDRIVE_CATALOG_QUERY_CONTRACT>;
|
|
15
|
+
/** Preserve the HTTP route's first-value, exact-true and numeric semantics. */
|
|
16
|
+
export declare function readPipedriveCatalogQuery(params: URLSearchParams): PipedriveCatalogQuery;
|
|
17
|
+
/** Forward only supplied contract fields; never infer approval or a ceiling. */
|
|
18
|
+
export declare function pipedriveCatalogQueryParams(input: PipedriveCatalogQuery): URLSearchParams;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { bool, num, obj, str, validateContract } from "./schema.js";
|
|
2
|
+
/** The native saved-filter/field read can charge four own-key provider fees. */
|
|
3
|
+
export const PIPEDRIVE_CATALOG_QUERY_CONTRACT = obj({
|
|
4
|
+
query: str({ description: "Optional filter-name search." }),
|
|
5
|
+
approved: bool({ description: "Approve this catalog read's BYOK fees." }),
|
|
6
|
+
max_credits: num({ minimum: 0, description: "Total credit ceiling for this read's four provider calls." }),
|
|
7
|
+
});
|
|
8
|
+
/** Preserve the HTTP route's first-value, exact-true and numeric semantics. */
|
|
9
|
+
export function readPipedriveCatalogQuery(params) {
|
|
10
|
+
const input = {
|
|
11
|
+
query: params.get("query")?.trim().slice(0, 200) ?? "",
|
|
12
|
+
approved: params.get("approved") === "true",
|
|
13
|
+
max_credits: Number(params.get("max_credits")),
|
|
14
|
+
};
|
|
15
|
+
const result = validateContract(PIPEDRIVE_CATALOG_QUERY_CONTRACT, input, { unknown: "reject", overMax: "reject" });
|
|
16
|
+
// Malformed/negative caps historically return fee_approval_required when a
|
|
17
|
+
// fee applies, rather than a 400; pre-repricing reads still work without one.
|
|
18
|
+
return result.ok ? result.value : { ...input, max_credits: Number.NaN };
|
|
19
|
+
}
|
|
20
|
+
/** Forward only supplied contract fields; never infer approval or a ceiling. */
|
|
21
|
+
export function pipedriveCatalogQueryParams(input) {
|
|
22
|
+
const params = new URLSearchParams();
|
|
23
|
+
for (const key of Object.keys(PIPEDRIVE_CATALOG_QUERY_CONTRACT.properties)) {
|
|
24
|
+
const value = input[key];
|
|
25
|
+
if (value !== undefined)
|
|
26
|
+
params.set(key, String(value));
|
|
27
|
+
}
|
|
28
|
+
return params;
|
|
29
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { type ContractObject } from "./schema.js";
|
|
2
|
+
export declare const PUBLISHING_POLICY_LIST_LIMIT = 200;
|
|
3
|
+
export declare const PUBLISHING_ACTION_LIST_LIMIT = 500;
|
|
4
|
+
export declare const PUBLISHING_NOTE_LIST_LIMIT = 500;
|
|
5
|
+
export declare const PUBLISHING_REVISION_LIST_LIMIT = 200;
|
|
6
|
+
export declare const PUBLISHING_METRIC_LIST_LIMIT = 400;
|
|
7
|
+
export declare const PUBLISHING_AMPLIFICATION_SCOPE_KINDS: readonly ["all", "tag", "post", "ugc_program", "ugc_peer_program"];
|
|
8
|
+
export declare const publishingReadProjectionContract: ContractObject<{
|
|
9
|
+
readonly projection: {
|
|
10
|
+
readonly type: "string";
|
|
11
|
+
readonly description?: string;
|
|
12
|
+
readonly minLength?: number;
|
|
13
|
+
readonly maxLength?: number;
|
|
14
|
+
readonly enum: readonly ["full", "summary", "value"];
|
|
15
|
+
};
|
|
16
|
+
readonly value_path: import("./schema.js").StringProperty;
|
|
17
|
+
readonly cursor: import("./schema.js").StringProperty;
|
|
18
|
+
}, never>;
|
|
19
|
+
export declare const publishingReadWindowContract: ContractObject<{
|
|
20
|
+
readonly max_chars: {
|
|
21
|
+
type: "integer";
|
|
22
|
+
description?: string;
|
|
23
|
+
minimum?: number;
|
|
24
|
+
maximum?: number;
|
|
25
|
+
"x-clamp"?: true;
|
|
26
|
+
};
|
|
27
|
+
}, never>;
|
|
28
|
+
export declare const publishingAmplificationListQueryContract: ContractObject<{
|
|
29
|
+
readonly projection: {
|
|
30
|
+
readonly type: "string";
|
|
31
|
+
readonly description?: string;
|
|
32
|
+
readonly minLength?: number;
|
|
33
|
+
readonly maxLength?: number;
|
|
34
|
+
readonly enum: readonly ["full", "summary", "value"];
|
|
35
|
+
};
|
|
36
|
+
readonly value_path: import("./schema.js").StringProperty;
|
|
37
|
+
readonly cursor: import("./schema.js").StringProperty;
|
|
38
|
+
readonly max_chars: {
|
|
39
|
+
type: "integer";
|
|
40
|
+
description?: string;
|
|
41
|
+
minimum?: number;
|
|
42
|
+
maximum?: number;
|
|
43
|
+
"x-clamp"?: true;
|
|
44
|
+
};
|
|
45
|
+
readonly enabled: import("./schema.js").BooleanProperty;
|
|
46
|
+
readonly scope_kind: {
|
|
47
|
+
readonly type: "string";
|
|
48
|
+
readonly description?: string;
|
|
49
|
+
readonly minLength?: number;
|
|
50
|
+
readonly maxLength?: number;
|
|
51
|
+
readonly enum: readonly ["all", "tag", "post", "ugc_program", "ugc_peer_program"];
|
|
52
|
+
};
|
|
53
|
+
readonly limit: {
|
|
54
|
+
type: "integer";
|
|
55
|
+
description?: string;
|
|
56
|
+
minimum?: number;
|
|
57
|
+
maximum?: number;
|
|
58
|
+
"x-clamp"?: true;
|
|
59
|
+
};
|
|
60
|
+
}, never>;
|
|
61
|
+
export declare const publishingAmplificationActionsQueryContract: ContractObject<{
|
|
62
|
+
readonly projection: {
|
|
63
|
+
readonly type: "string";
|
|
64
|
+
readonly description?: string;
|
|
65
|
+
readonly minLength?: number;
|
|
66
|
+
readonly maxLength?: number;
|
|
67
|
+
readonly enum: readonly ["full", "summary", "value"];
|
|
68
|
+
};
|
|
69
|
+
readonly value_path: import("./schema.js").StringProperty;
|
|
70
|
+
readonly cursor: import("./schema.js").StringProperty;
|
|
71
|
+
readonly max_chars: {
|
|
72
|
+
type: "integer";
|
|
73
|
+
description?: string;
|
|
74
|
+
minimum?: number;
|
|
75
|
+
maximum?: number;
|
|
76
|
+
"x-clamp"?: true;
|
|
77
|
+
};
|
|
78
|
+
readonly post_id: import("./schema.js").StringProperty;
|
|
79
|
+
readonly limit: {
|
|
80
|
+
type: "integer";
|
|
81
|
+
description?: string;
|
|
82
|
+
minimum?: number;
|
|
83
|
+
maximum?: number;
|
|
84
|
+
"x-clamp"?: true;
|
|
85
|
+
};
|
|
86
|
+
}, never>;
|
|
87
|
+
export declare const publishingNotesQueryContract: ContractObject<{
|
|
88
|
+
readonly projection: {
|
|
89
|
+
readonly type: "string";
|
|
90
|
+
readonly description?: string;
|
|
91
|
+
readonly minLength?: number;
|
|
92
|
+
readonly maxLength?: number;
|
|
93
|
+
readonly enum: readonly ["full", "summary", "value"];
|
|
94
|
+
};
|
|
95
|
+
readonly value_path: import("./schema.js").StringProperty;
|
|
96
|
+
readonly cursor: import("./schema.js").StringProperty;
|
|
97
|
+
readonly max_chars: {
|
|
98
|
+
type: "integer";
|
|
99
|
+
description?: string;
|
|
100
|
+
minimum?: number;
|
|
101
|
+
maximum?: number;
|
|
102
|
+
"x-clamp"?: true;
|
|
103
|
+
};
|
|
104
|
+
readonly resolved: import("./schema.js").BooleanProperty;
|
|
105
|
+
readonly after_comment_id: import("./schema.js").StringProperty;
|
|
106
|
+
readonly comment_id: import("./schema.js").StringProperty;
|
|
107
|
+
readonly limit: {
|
|
108
|
+
type: "integer";
|
|
109
|
+
description?: string;
|
|
110
|
+
minimum?: number;
|
|
111
|
+
maximum?: number;
|
|
112
|
+
"x-clamp"?: true;
|
|
113
|
+
};
|
|
114
|
+
}, never>;
|
|
115
|
+
export declare const publishingRevisionsQueryContract: ContractObject<{
|
|
116
|
+
readonly projection: {
|
|
117
|
+
readonly type: "string";
|
|
118
|
+
readonly description?: string;
|
|
119
|
+
readonly minLength?: number;
|
|
120
|
+
readonly maxLength?: number;
|
|
121
|
+
readonly enum: readonly ["full", "summary", "value"];
|
|
122
|
+
};
|
|
123
|
+
readonly value_path: import("./schema.js").StringProperty;
|
|
124
|
+
readonly cursor: import("./schema.js").StringProperty;
|
|
125
|
+
readonly max_chars: {
|
|
126
|
+
type: "integer";
|
|
127
|
+
description?: string;
|
|
128
|
+
minimum?: number;
|
|
129
|
+
maximum?: number;
|
|
130
|
+
"x-clamp"?: true;
|
|
131
|
+
};
|
|
132
|
+
readonly before_revision: {
|
|
133
|
+
type: "integer";
|
|
134
|
+
description?: string;
|
|
135
|
+
minimum?: number;
|
|
136
|
+
maximum?: number;
|
|
137
|
+
"x-clamp"?: true;
|
|
138
|
+
};
|
|
139
|
+
readonly revision_number: {
|
|
140
|
+
type: "integer";
|
|
141
|
+
description?: string;
|
|
142
|
+
minimum?: number;
|
|
143
|
+
maximum?: number;
|
|
144
|
+
"x-clamp"?: true;
|
|
145
|
+
};
|
|
146
|
+
readonly limit: {
|
|
147
|
+
type: "integer";
|
|
148
|
+
description?: string;
|
|
149
|
+
minimum?: number;
|
|
150
|
+
maximum?: number;
|
|
151
|
+
"x-clamp"?: true;
|
|
152
|
+
};
|
|
153
|
+
}, never>;
|
|
154
|
+
export declare const publishingAnalyticsQueryContract: ContractObject<{
|
|
155
|
+
readonly projection: {
|
|
156
|
+
readonly type: "string";
|
|
157
|
+
readonly description?: string;
|
|
158
|
+
readonly minLength?: number;
|
|
159
|
+
readonly maxLength?: number;
|
|
160
|
+
readonly enum: readonly ["full", "summary", "value"];
|
|
161
|
+
};
|
|
162
|
+
readonly value_path: import("./schema.js").StringProperty;
|
|
163
|
+
readonly cursor: import("./schema.js").StringProperty;
|
|
164
|
+
readonly max_chars: {
|
|
165
|
+
type: "integer";
|
|
166
|
+
description?: string;
|
|
167
|
+
minimum?: number;
|
|
168
|
+
maximum?: number;
|
|
169
|
+
"x-clamp"?: true;
|
|
170
|
+
};
|
|
171
|
+
readonly limit: {
|
|
172
|
+
type: "integer";
|
|
173
|
+
description?: string;
|
|
174
|
+
minimum?: number;
|
|
175
|
+
maximum?: number;
|
|
176
|
+
"x-clamp"?: true;
|
|
177
|
+
};
|
|
178
|
+
}, never>;
|
|
179
|
+
/** Select only the exact leaf contract's query fields. False and zero survive;
|
|
180
|
+
* null/undefined are absent. Other action fields never become query arguments. */
|
|
181
|
+
export declare function publishingReadQueryParams(contract: Pick<ContractObject, "properties">, values: Record<string, unknown>): URLSearchParams;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { bool, int, obj, str } from "./schema.js";
|
|
2
|
+
// These are the existing native store bounds, shared by the serving producer
|
|
3
|
+
// and both agent interfaces. A zero HTTP limit keeps the shipped default.
|
|
4
|
+
export const PUBLISHING_POLICY_LIST_LIMIT = 200;
|
|
5
|
+
export const PUBLISHING_ACTION_LIST_LIMIT = 500;
|
|
6
|
+
export const PUBLISHING_NOTE_LIST_LIMIT = 500;
|
|
7
|
+
export const PUBLISHING_REVISION_LIST_LIMIT = 200;
|
|
8
|
+
export const PUBLISHING_METRIC_LIST_LIMIT = 400;
|
|
9
|
+
export const PUBLISHING_AMPLIFICATION_SCOPE_KINDS = ["all", "tag", "post", "ugc_program", "ugc_peer_program"];
|
|
10
|
+
export const publishingReadProjectionContract = obj({
|
|
11
|
+
projection: { ...str({ description: "full default; summary lists exact child metadata/indices; value streams lossless canonical JSON. Follow next_cursor with the same scope." }), enum: ["full", "summary", "value"] },
|
|
12
|
+
value_path: str({ description: "Projection RFC6901 pointer: empty=root; /revisions/0/content_text, /comments/0/body, /series, /curve, /benchmarks or /accounts. No field is silently trimmed.", maxLength: 4096 }),
|
|
13
|
+
cursor: str({ description: "Signed projection continuation; repeat the same filters, projection and value_path. Changed data/scope/source refuses stale.", maxLength: 4096 }),
|
|
14
|
+
});
|
|
15
|
+
export const publishingReadWindowContract = obj({
|
|
16
|
+
max_chars: int({ minimum: 1_000, maximum: 48_000, description: "Model data window; oversize refuses whole. Widget UI transport is separate. Omit for legacy HTTP/CLI output." }),
|
|
17
|
+
});
|
|
18
|
+
const limit = (maximum, description) => int({ minimum: 0, maximum, clamp: true, description });
|
|
19
|
+
export const publishingAmplificationListQueryContract = obj({
|
|
20
|
+
...publishingReadProjectionContract.properties,
|
|
21
|
+
enabled: bool({ description: "list: true enabled, false disabled; omit for both." }),
|
|
22
|
+
scope_kind: { ...str({ description: "list scope; create: all/tag/post with matching target." }), enum: PUBLISHING_AMPLIFICATION_SCOPE_KINDS },
|
|
23
|
+
limit: limit(PUBLISHING_POLICY_LIST_LIMIT, "list policies, newest first; 0 uses native default 100."),
|
|
24
|
+
...publishingReadWindowContract.properties,
|
|
25
|
+
});
|
|
26
|
+
export const publishingAmplificationActionsQueryContract = obj({
|
|
27
|
+
...publishingReadProjectionContract.properties,
|
|
28
|
+
post_id: str({ description: "actions: scheduled post UUID filter." }),
|
|
29
|
+
limit: limit(PUBLISHING_ACTION_LIST_LIMIT, "actions ledger rows, newest first; 0 uses native default 100."),
|
|
30
|
+
...publishingReadWindowContract.properties,
|
|
31
|
+
});
|
|
32
|
+
export const publishingNotesQueryContract = obj({
|
|
33
|
+
resolved: bool({ description: "list: true resolved, false unresolved; omit for both. resolve: default true." }),
|
|
34
|
+
after_comment_id: str({ description: "list: seek after this private note in native oldest-first order, within this post and resolved filter.", maxLength: 120 }),
|
|
35
|
+
comment_id: str({ description: "list: exact private note on this post; resolve: note to settle/reopen.", maxLength: 120 }),
|
|
36
|
+
...publishingReadProjectionContract.properties,
|
|
37
|
+
limit: limit(PUBLISHING_NOTE_LIST_LIMIT, "list private notes, oldest first; 0 uses native default 200."),
|
|
38
|
+
...publishingReadWindowContract.properties,
|
|
39
|
+
});
|
|
40
|
+
export const publishingRevisionsQueryContract = obj({
|
|
41
|
+
before_revision: int({ minimum: 1, description: "list: read revisions older than this number, newest first; native continuation of a limited history scope." }),
|
|
42
|
+
revision_number: int({ minimum: 1, description: "list: exact historical revision on this post; restore: revision to restore." }),
|
|
43
|
+
...publishingReadProjectionContract.properties,
|
|
44
|
+
limit: limit(PUBLISHING_REVISION_LIST_LIMIT, "list revisions, newest first; 0 uses native default 50."),
|
|
45
|
+
...publishingReadWindowContract.properties,
|
|
46
|
+
});
|
|
47
|
+
export const publishingAnalyticsQueryContract = obj({
|
|
48
|
+
...publishingReadProjectionContract.properties,
|
|
49
|
+
limit: limit(PUBLISHING_METRIC_LIST_LIMIT, "post/account: total metric rows, newest first (not per account); 0 uses 90. summary: top posts, max 50. timeseries ignores it."),
|
|
50
|
+
...publishingReadWindowContract.properties,
|
|
51
|
+
});
|
|
52
|
+
/** Select only the exact leaf contract's query fields. False and zero survive;
|
|
53
|
+
* null/undefined are absent. Other action fields never become query arguments. */
|
|
54
|
+
export function publishingReadQueryParams(contract, values) {
|
|
55
|
+
const params = new URLSearchParams();
|
|
56
|
+
for (const key of Object.keys(contract.properties)) {
|
|
57
|
+
const value = values[key];
|
|
58
|
+
if (value !== undefined && value !== null)
|
|
59
|
+
params.set(key, String(value));
|
|
60
|
+
}
|
|
61
|
+
return params;
|
|
62
|
+
}
|