@deepraxis/cli 0.1.0-dev.ce6682e → 0.1.0-dev.d0f5f8e

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.
Files changed (3) hide show
  1. package/COMMANDS.md +11 -4
  2. package/dist/praxis.js +524 -160
  3. package/package.json +1 -1
package/COMMANDS.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Generated by `bun run commands`. DO NOT EDIT.
4
4
 
5
- 365 HTTP operations; 367 commands including aliases.
5
+ 372 HTTP operations; 374 commands including aliases.
6
6
 
7
7
  Environment: `PRAXIS_ENV=develop|production` (dev package defaults to `develop`; stable package defaults to `production`). No `--env` option.
8
8
 
@@ -21,7 +21,7 @@ Handwritten commands: `login`, `logout`, `whoami`, `org list`, `org switch <orga
21
21
  | `praxis agent archive <agent_id>` | `agent.agents.archive` | POST `/agent/v1/agents/{agent_id}/archive` | |
22
22
  | `praxis agent computer archive <computer_id>` | `agent.computers.archive` | POST `/agent/v1/computers/{computer_id}/archive` | |
23
23
  | `praxis agent computer attachment create` | `agent.computers.attachments.create` | POST `/agent/v1/computers/{computer_id}/attachments` | `--computer`, `--name` |
24
- | `praxis agent computer connect <computer_id>` | `agent.computers.connect` | POST `/agent/v1/computers/{computer_id}/connect` | |
24
+ | `praxis agent computer connect <computer_id>` | `agent.computers.connect` | POST `/agent/v1/computers/{computer_id}/connect` | `--need` |
25
25
  | `praxis agent computer create` | `agent.computers.create` | POST `/agent/v1/computers` | `--name`, `--environment-id` |
26
26
  | `praxis agent computer delete <computer_id>` | `agent.computers.delete` | DELETE `/agent/v1/computers/{computer_id}` | |
27
27
  | `praxis agent computer file get-content` | `agent.computers.files.retrieveContent` | GET `/agent/v1/computers/{computer_id}/files/content` | `--computer`, `--path` |
@@ -31,7 +31,7 @@ Handwritten commands: `login`, `logout`, `whoami`, `org list`, `org switch <orga
31
31
  | `praxis agent computer lease renew create` | `agent.computers.leases.renew.create` | POST `/agent/v1/computers/{computer_id}/leases/{lease_id}/renew` | `--computer`, `--lease` |
32
32
  | `praxis agent computer list` | `agent.computers.list` | GET `/agent/v1/computers` | `--param-limit`, `--page` |
33
33
  | `praxis agent computer update <computer_id>` | `agent.computers.update` | POST `/agent/v1/computers/{computer_id}` | `--name` |
34
- | `praxis agent create` | `agent.agents.create` | POST `/agent/v1/agents` | `--name`, `--description`, `--system`, `--kind`, `--computer-id`, `--subagent-model` |
34
+ | `praxis agent create` | `agent.agents.create` | POST `/agent/v1/agents` | `--name`, `--description`, `--system`, `--kind`, `--computer-id`, `--subagent-model`, `--preset` |
35
35
  | `praxis agent deployment archive <deployment_id>` | `agent.deployments.archive` | POST `/agent/v1/deployments/{deployment_id}/archive` | |
36
36
  | `praxis agent deployment create` | `agent.deployments.create` | POST `/agent/v1/deployments` | `--environment-id`, `--name`, `--description`, `--vault-ids` |
37
37
  | `praxis agent deployment get <deployment_id>` | `agent.deployments.retrieve` | GET `/agent/v1/deployments/{deployment_id}` | |
@@ -69,6 +69,7 @@ Handwritten commands: `login`, `logout`, `whoami`, `org list`, `org switch <orga
69
69
  | `praxis agent session compact <session_id>` | `agent.sessions.compact` | POST `/agent/v1/sessions/{session_id}/compact` | `--instructions` |
70
70
  | `praxis agent session compaction list` | `agent.sessions.compactions.list` | GET `/agent/v1/sessions/{session_id}/compactions` | `--session` |
71
71
  | `praxis agent session create` | `agent.sessions.create` | POST `/agent/v1/sessions` | `--environment-id`, `--title`, `--vault-ids`, `--cwd`, `--computer-id`, `--time-zone`, `--model` |
72
+ | `praxis agent session delete <session_id>` | `agent.sessions.delete` | DELETE `/agent/v1/sessions/{session_id}` | |
72
73
  | `praxis agent session event list` | `agent.sessions.events.list` | GET `/agent/v1/sessions/{session_id}/events` | `--session`, `--created-at-gt`, `--created-at-gte`, `--created-at-lt`, `--created-at-lte`, `--order`, `--types`, `--param-limit`, `--page` |
73
74
  | `praxis agent session event send <session_id>` | `agent.sessions.events.send` | POST `/agent/v1/sessions/{session_id}/events` | |
74
75
  | `praxis agent session event stream` | `agent.sessions.events.stream` | GET `/agent/v1/sessions/{session_id}/events/stream` | `--session`, `--event-deltas` |
@@ -90,7 +91,8 @@ Handwritten commands: `login`, `logout`, `whoami`, `org list`, `org switch <orga
90
91
  | `praxis agent session thread stream <thread_id>` | `agent.sessions.threads.stream` | GET `/agent/v1/sessions/{session_id}/threads/{thread_id}/stream` | `--session`, `--query-session-id`, `--event-deltas` |
91
92
  | `praxis agent session update <session_id>` | `agent.sessions.update` | POST `/agent/v1/sessions/{session_id}` | `--title`, `--vault-ids`, `--model` |
92
93
  | `praxis agent toolset list` | `agent.toolsets.list` | GET `/agent/v1/toolsets` | |
93
- | `praxis agent update <agent_id>` | `agent.agents.update` | POST `/agent/v1/agents/{agent_id}` | `--description`, `--name`, `--system`, `--param-version`, `--kind`, `--computer-id`, `--subagent-model` |
94
+ | `praxis agent unarchive create` | `agent.agents.unarchive.create` | POST `/agent/v1/agents/{agent_id}/unarchive` | `--agent` |
95
+ | `praxis agent update <agent_id>` | `agent.agents.update` | POST `/agent/v1/agents/{agent_id}` | `--description`, `--name`, `--system`, `--param-version`, `--kind`, `--computer-id`, `--subagent-model`, `--preset` |
94
96
  | `praxis agent version list` | `agent.agents.versions.list` | GET `/agent/v1/agents/{agent_id}/versions` | `--agent`, `--param-limit`, `--page` |
95
97
  | `praxis application asset upload` | `application.assets.upload.create` | POST `/application/v1/assets/upload` | `--jwt` |
96
98
  | `praxis application subdomain get` | `application.subdomain.retrieve` | GET `/application/v1/subdomain` | |
@@ -113,6 +115,7 @@ Handwritten commands: `login`, `logout`, `whoami`, `org list`, `org switch <orga
113
115
  | `praxis application worker secret list` | `application.workers.secrets.list` | GET `/application/v1/workers/{worker_id}/secrets` | `--worker` |
114
116
  | `praxis application worker secret update <name>` | `application.workers.secrets.update` | PUT `/application/v1/workers/{worker_id}/secrets/{name}` | `--worker` |
115
117
  | `praxis application worker update <worker_id>` | `application.workers.update` | PATCH `/application/v1/workers/{worker_id}` | `--name`, `--tags` |
118
+ | `praxis application worker version asset get-manifest` | `application.workers.versions.assets.retrieveManifest` | GET `/application/v1/workers/{worker_id}/versions/{version_id}/assets/manifest` | `--worker`, `--param-version` |
116
119
  | `praxis application worker version create` | `application.workers.versions.create` | POST `/application/v1/workers/{worker_id}/versions` | `--worker`, `--deploy`, `--main-module`, `--compatibility-date`, `--compatibility-flags` |
117
120
  | `praxis application worker version get <version_id>` | `application.workers.versions.retrieve` | GET `/application/v1/workers/{worker_id}/versions/{version_id}` | `--worker` |
118
121
  | `praxis application worker version list` | `application.workers.versions.list` | GET `/application/v1/workers/{worker_id}/versions` | `--worker`, `--param-cursor`, `--param-limit` |
@@ -229,6 +232,10 @@ Handwritten commands: `login`, `logout`, `whoami`, `org list`, `org switch <orga
229
232
  | `praxis file get <file_id>` | `file.files.retrieve` | GET `/file/v1/files/{file_id}` | |
230
233
  | `praxis file get-content <file_id>` | `file.files.retrieveContent` | GET `/file/v1/files/{file_id}/content` | |
231
234
  | `praxis file list` | `file.files.list` | GET `/file/v1/files` | `--purpose`, `--param-limit`, `--order`, `--after`, `--scope-id`, `--before` |
235
+ | `praxis file upload cancel <upload_id>` | `file.uploads.cancel` | POST `/file/v1/uploads/{upload_id}/cancel` | |
236
+ | `praxis file upload complete <upload_id>` | `file.uploads.complete` | POST `/file/v1/uploads/{upload_id}/complete` | `--part-ids`, `--md5` |
237
+ | `praxis file upload create` | `file.uploads.create` | POST `/file/v1/uploads` | `--bytes`, `--filename`, `--mime-type`, `--purpose`, `--part-size` |
238
+ | `praxis file upload part create` | `file.uploads.parts.create` | POST `/file/v1/uploads/{upload_id}/parts` | `--upload`, `--part-number`, `--param-data` |
232
239
  | `praxis knowledge vector-store create` | `knowledge.vectorStores.create` | POST `/knowledge/v1/vector_stores` | `--name`, `--file-ids`, `--description` |
233
240
  | `praxis knowledge vector-store delete <vector_store_id>` | `knowledge.vectorStores.delete` | DELETE `/knowledge/v1/vector_stores/{vector_store_id}` | |
234
241
  | `praxis knowledge vector-store event list` | `knowledge.vectorStores.events.list` | GET `/knowledge/v1/vector_stores/{vector_store_id}/events` | `--vector-store`, `--after-seq`, `--param-limit` |
package/dist/praxis.js CHANGED
@@ -136,6 +136,27 @@ var operations = [
136
136
  description: `Tool configurations available to the agent. Maximum of 128 tools
137
137
  across all toolsets allowed.`
138
138
  },
139
+ {
140
+ name: "databases",
141
+ type: "array",
142
+ itemType: "object",
143
+ required: false,
144
+ description: "Database allowlist for platform resources accessible through service-agent codemode. Only kind=service accepts nonempty bindings; personal agents return 400. At most 20 bindings per resource type, with no duplicate ids. Newly added bindings apply only to new sessions; disabling or removing a binding immediately revokes access in running sessions. Unbound resources return 404; writes to read_only resources are refused. Other platform APIs return 403, except public API discovery. Invalid bindings and unknown item fields return 400. Omit on create for no bindings."
145
+ },
146
+ {
147
+ name: "vector_stores",
148
+ type: "array",
149
+ itemType: "object",
150
+ required: false,
151
+ description: "Knowledge vector store allowlist for platform resources accessible through service-agent codemode. Only kind=service accepts nonempty bindings; personal agents return 400. At most 20 bindings per resource type, with no duplicate ids. Newly added bindings apply only to new sessions; disabling or removing a binding immediately revokes access in running sessions. Unbound resources return 404; writes to read_only resources are refused. Other platform APIs return 403, except public API discovery. Invalid bindings and unknown item fields return 400. Omit on create for no bindings."
152
+ },
153
+ {
154
+ name: "files",
155
+ type: "array",
156
+ itemType: "object",
157
+ required: false,
158
+ description: "File allowlist for platform resources accessible through service-agent codemode. Only kind=service accepts nonempty bindings; personal agents return 400. At most 20 bindings per resource type, with no duplicate ids. Newly added bindings apply only to new sessions; disabling or removing a binding immediately revokes access in running sessions. Unbound resources return 404; writes to read_only resources are refused. Other platform APIs return 403, except public API discovery. Invalid bindings and unknown item fields return 400. Omit on create for no bindings."
159
+ },
139
160
  {
140
161
  name: "kind",
141
162
  type: "string",
@@ -312,6 +333,27 @@ Obtain this value from a create or retrieve response. Must be at least 1 if
312
333
  specified. When supplied, the request fails if it does not match the server's
313
334
  current version; omit to apply the update unconditionally.`
314
335
  },
336
+ {
337
+ name: "databases",
338
+ type: "array",
339
+ itemType: "object",
340
+ required: false,
341
+ description: "Replace this resource allowlist; omission preserves it, and null or [] clears it. Additions apply only to new sessions; disabling or removing a binding immediately revokes access in running sessions. Only service agents accept nonempty bindings."
342
+ },
343
+ {
344
+ name: "vector_stores",
345
+ type: "array",
346
+ itemType: "object",
347
+ required: false,
348
+ description: "Replace this resource allowlist; omission preserves it, and null or [] clears it. Additions apply only to new sessions; disabling or removing a binding immediately revokes access in running sessions. Only service agents accept nonempty bindings."
349
+ },
350
+ {
351
+ name: "files",
352
+ type: "array",
353
+ itemType: "object",
354
+ required: false,
355
+ description: "Replace this resource allowlist; omission preserves it, and null or [] clears it. Additions apply only to new sessions; disabling or removing a binding immediately revokes access in running sessions. Only service agents accept nonempty bindings."
356
+ },
315
357
  {
316
358
  name: "kind",
317
359
  type: "string",
@@ -804,7 +846,7 @@ Own-face superset: Managed Agents has no persistent Computer attachment director
804
846
 
805
847
  Own-face extension: Managed Agents has no persistent Computer connection. Returns the public SDK sandbox_id, envd_access_token and template_id, plus the machine tier the computer is on now (light or heavy) and the lease that keeps it there (lease_id, lease_expires_at; lease_id is null when no lease exists). Keep the lease while the connection stays open: renew it about every 20 seconds (POST .../leases/{lease_id}/renew) and release it on close (DELETE .../leases/{lease_id}). An unrenewed lease simply expires, and the full machine is reclaimed only after that. Archived computers remain accessible; foreign or absent computers return 404. Never cache credentials.
806
848
 
807
- Connect to a computer with a caller-held lease. Omit the body or send {} to request light: the lightweight shell, or the heavy instance while it is running. Send {"need":"heavy"} to request full Linux for interactive programs; files are synchronized before the heavy connection is returned with tier heavy. Heavy running time counts toward the daily budget in seconds. Renew the lease while using the connection and release it with DELETE when finished. If the daily budget is exhausted, 429 COMPUTER_BUDGET_EXHAUSTED allows you to continue with need light.`,
849
+ Connect to a computer with a caller-held lease. Omit the body or send {} to request light: the lightweight shell, or the heavy instance while it is running. Send {"need":"heavy"} to request full Linux for interactive programs; files are synchronized before the heavy connection is returned with tier heavy. Renew the lease while using the connection and release it with DELETE when finished.`,
808
850
  http: "POST",
809
851
  path: "/agent/v1/computers/{computer_id}/connect",
810
852
  streaming: "no",
@@ -1877,7 +1919,7 @@ owning account only.`
1877
1919
  resource: "agent.sessions",
1878
1920
  method: "create",
1879
1921
  summary: "Create session",
1880
- description: `Create a session: one conversation with an agent, running on a machine. Pass agent as an agent id (latest version) or {id, version, type:"agent"}. Unlike Anthropic Managed Agents, omit environment_id to get a light machine, unless computer_id (explicit, or the agent's default) selects a Computer; pass environment_id for a sandbox recipe. Optional model must be one of the agent's allowed models; optional thinking is {effort: low|medium|high|xhigh|max|none} or null, where null inherits the agent default and then the model default (none requires the model to allow disabling thinking). Selections are saved before initial_events run; invalid ones return 400 invalid_request_error. Inline agent overrides are not supported. Supports vault_ids, up to 50, owned by the current organization and not archived; credential injection is deferred to a later version. Omitted budget defaults to USD 20; null removes the limit, and a limit object uses a positive integer string of USD cents. Up to 500 file resources can be attached. Use initial_events to send a first user.message in this request, or send messages afterwards with sessions.events.send. The returned status can still be idle before initial events run; read agent.message and the subsequent session.status_idle event to confirm completion.`,
1922
+ description: `Create a session: one conversation with an agent. Pass agent as an agent id (latest version) or {id, version, type:"agent"}. Unlike Anthropic Managed Agents, omit environment_id to get a light machine, unless computer_id (explicit, or the agent's default) selects a Computer; pass an environment id for a sandbox recipe. For service agents, omission returns environment_id=light; explicit null allocates no machine, so codemode, bash and other machine tools are unavailable. Optional model must be one of the agent's allowed models; optional thinking is {effort: low|medium|high|xhigh|max|none} or null, where null inherits the agent default and then the model default (none requires the model to allow disabling thinking). Selections are saved before initial_events run; invalid ones return 400 invalid_request_error. Inline agent overrides are not supported. Supports vault_ids, up to 50, owned by the current organization and not archived; credential injection is deferred to a later version. Omitted budget defaults to USD 20; null removes the limit, and a limit object uses a positive integer string of USD cents. Up to 500 file resources can be attached. Use initial_events to send a first user.message in this request, or send messages afterwards with sessions.events.send. The returned status can still be idle before initial events run; read agent.message and the subsequent session.status_idle event to confirm completion.`,
1881
1923
  http: "POST",
1882
1924
  path: "/agent/v1/sessions",
1883
1925
  streaming: "no",
@@ -1897,7 +1939,7 @@ owning account only.`
1897
1939
  name: "environment_id",
1898
1940
  type: "string",
1899
1941
  required: false,
1900
- description: "ID of the `environment` defining the container configuration for\nthis session."
1942
+ description: "Execution environment id from agent.environments.list or agent.environments.create. For a service agent, omission selects the built-in light environment and returns session.environment_id=light; explicit null allocates no machine and makes machine tools, including codemode and bash, unavailable. Personal agents use their selected Computer."
1901
1943
  },
1902
1944
  {
1903
1945
  name: "budget",
@@ -6952,7 +6994,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
6952
6994
  name: "{column}",
6953
6995
  type: "string",
6954
6996
  required: false,
6955
- description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. See the filter operator matrix."
6997
+ description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. Supported operators: eq (equal), neq (not equal; ne is not supported), gt/gte/lt/lte (comparisons), like (case-sensitive text pattern), ilike (case-insensitive text pattern), in (membership), is (null/boolean test), cs (contains), cd (contained by), and ov (array overlap). For like/ilike, * or % matches any sequence and _ matches one character, e.g. title=ilike.*spring*; URL-encode a literal % as %25. Use days=in.(1,2,3), title=is.null, active=is.true or active=is.false; is.not_null and is.unknown are also accepted (unknown means null). Negate a condition with not., e.g. title=not.eq.draft. Multiple filters are ANDed; use or=(title.eq.a,title.eq.b), and=(days.gte.1,days.lte.3), not.or=(...) or not.and=(...), with nested and(...) and or(...) groups as needed. cs/cd accept JSON objects or arrays on JSON fields; cs also accepts a JSON scalar. For array values or multi-link fields, use cs.{a,b}, cd.{a,b} or ov.{a,b}. JSON paths use -> for JSON and ->> for text, e.g. attributes->>has_onsen=eq.true or attributes->tags=cs.[1,2]; chain -> segments and end with ->> for text. Dot paths such as attributes.has_onsen are unsupported. Full-text, regex, range operators and any/all modifiers are unsupported."
6956
6998
  },
6957
6999
  {
6958
7000
  name: "and",
@@ -7000,7 +7042,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7000
7042
  name: "on_conflict",
7001
7043
  type: "string",
7002
7044
  required: false,
7003
- description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7045
+ description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. With resolution=merge-duplicates, a conflicting row keeps columns omitted from the request; supplied values replace the whole field value (including JSON objects), and explicit null clears a nullable field. With resolution=ignore-duplicates, a conflicting row is left unchanged. Without a conflict, both modes insert a new row using field defaults for omitted fields. In a batch, a key present in another row is filled with null when omitted in this row unless missing=default is set; that null can overwrite a value during merge. With missing=default, omitted keys stay omitted for conflict merges (existing values are preserved); new rows use defaults, or null when no default exists. Explicit null never invokes a default. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7004
7046
  },
7005
7047
  {
7006
7048
  name: "columns",
@@ -7014,7 +7056,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7014
7056
  resource: "database.databases.rest",
7015
7057
  method: "retrieve",
7016
7058
  summary: "Select rows",
7017
- description: "Read matching rows with filters, selected columns and ordering. Returns at most 1000 rows per request, including when limit is omitted. Content-Range reports the actual returned interval and the total when count is requested. PATCH and DELETE are not subject to this read cap. Unlike Supabase, casts, aggregates, spread and join modifiers are unsupported. Each table supports up to 1,000,000 rows. Query budget: 8,000 ms with a shared 3,000,000-row safety ceiling. Create indexes on filtered or sorted fields, or narrow the request when it exceeds its budget. Synchronous SQLite aggregates use candidate-row throughput estimates before execution; deployed Workers freeze clocks during synchronous CPU work, so the timer is not a guaranteed server-side interrupt. Count preferences: without filters, exact, planned and estimated all use SQLite's exact b-tree count. With filters, exact counts within budget; planned skips counting and reports an unknown total (Content-Range: 0-24/*); estimated attempts exact counting only when the preflight budget allows it, otherwise reports an unknown total. Preference-Applied echoes the requested count mode. Error table: QUERY_CANCELED / PostgREST 57014 is HTTP 500, retryable=false: Query timed out: index filtered or sorted fields, or narrow the range. DATABASE_INVALID_STATE is HTTP 409 for operations on a deleting database. Cleared or nonexistent databases return DATABASE_NOT_FOUND / HTTP 404. The count is an HTTP Content-Range response header, not a JSON property. Praxis SDK/codemode methods return the body only; a page length is not a total unless you know all matches fit in that page.",
7059
+ description: "Read matching rows with filters, selected columns and ordering. Returns at most 1000 rows per request, including when limit is omitted. Content-Range reports the actual returned interval and the total when count is requested. PATCH and DELETE are not subject to this read cap. Unlike Supabase, casts, aggregates, spread and join modifiers are unsupported. Each table supports up to 1,000,000 rows. Query budget: 8,000 ms with a shared 3,000,000-row safety ceiling. Create indexes on filtered or sorted fields, or narrow the request when it exceeds its budget. Synchronous SQLite aggregates use candidate-row throughput estimates before execution; deployed Workers freeze clocks during synchronous CPU work, so the timer is not a guaranteed server-side interrupt. Count preferences: without filters, exact, planned and estimated all use SQLite's exact b-tree count. With filters, exact counts within budget; planned skips counting and reports an unknown total (Content-Range: 0-24/*); estimated attempts exact counting only when the preflight budget allows it, otherwise reports an unknown total. Preference-Applied echoes the requested count mode. Error table: QUERY_CANCELED / PostgREST 57014 is HTTP 500, retryable=false: Query timed out: index filtered or sorted fields, or narrow the range. DATABASE_INVALID_STATE is HTTP 409 for operations on a deleting database. Cleared or nonexistent databases return DATABASE_NOT_FOUND / HTTP 404. The count is an HTTP Content-Range response header, not a JSON property. Use praxis.database.count in the SDK or codemode to return { count: number | null }; a page length is not a total unless you know all matches fit in that page.",
7018
7060
  http: "GET",
7019
7061
  path: "/database/v1/databases/{database_id}/rest/{table}",
7020
7062
  streaming: "no",
@@ -7040,7 +7082,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7040
7082
  name: "{column}",
7041
7083
  type: "string",
7042
7084
  required: false,
7043
- description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. See the filter operator matrix."
7085
+ description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. Supported operators: eq (equal), neq (not equal; ne is not supported), gt/gte/lt/lte (comparisons), like (case-sensitive text pattern), ilike (case-insensitive text pattern), in (membership), is (null/boolean test), cs (contains), cd (contained by), and ov (array overlap). For like/ilike, * or % matches any sequence and _ matches one character, e.g. title=ilike.*spring*; URL-encode a literal % as %25. Use days=in.(1,2,3), title=is.null, active=is.true or active=is.false; is.not_null and is.unknown are also accepted (unknown means null). Negate a condition with not., e.g. title=not.eq.draft. Multiple filters are ANDed; use or=(title.eq.a,title.eq.b), and=(days.gte.1,days.lte.3), not.or=(...) or not.and=(...), with nested and(...) and or(...) groups as needed. cs/cd accept JSON objects or arrays on JSON fields; cs also accepts a JSON scalar. For array values or multi-link fields, use cs.{a,b}, cd.{a,b} or ov.{a,b}. JSON paths use -> for JSON and ->> for text, e.g. attributes->>has_onsen=eq.true or attributes->tags=cs.[1,2]; chain -> segments and end with ->> for text. Dot paths such as attributes.has_onsen are unsupported. Full-text, regex, range operators and any/all modifiers are unsupported."
7044
7086
  },
7045
7087
  {
7046
7088
  name: "and",
@@ -7088,7 +7130,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7088
7130
  name: "on_conflict",
7089
7131
  type: "string",
7090
7132
  required: false,
7091
- description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7133
+ description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. With resolution=merge-duplicates, a conflicting row keeps columns omitted from the request; supplied values replace the whole field value (including JSON objects), and explicit null clears a nullable field. With resolution=ignore-duplicates, a conflicting row is left unchanged. Without a conflict, both modes insert a new row using field defaults for omitted fields. In a batch, a key present in another row is filled with null when omitted in this row unless missing=default is set; that null can overwrite a value during merge. With missing=default, omitted keys stay omitted for conflict merges (existing values are preserved); new rows use defaults, or null when no default exists. Explicit null never invokes a default. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7092
7134
  },
7093
7135
  {
7094
7136
  name: "columns",
@@ -7102,7 +7144,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7102
7144
  resource: "database.databases.rest",
7103
7145
  method: "head",
7104
7146
  summary: "Count rows",
7105
- description: "Inspect the HTTP row count and range without retrieving row data; send Prefer: count=exact for a filtered count. Returns no body and does not fetch or serialize row data. A known count determines the interval using limit/offset or Range with a default and maximum page size of 1000. Without a count preference, or when the selected count mode cannot supply a total, Content-Range is */*. Singular Accept still validates page cardinality. Unlike Supabase, planned and estimated counts do not use PostgreSQL planner estimates. Count preferences: without filters, exact, planned and estimated all use SQLite's exact b-tree count. With filters, exact counts within budget; planned skips counting and reports an unknown total (Content-Range: 0-24/*); estimated attempts exact counting only when the preflight budget allows it, otherwise reports an unknown total. Preference-Applied echoes the requested count mode. Praxis SDK/codemode methods return only the empty body, not the Content-Range response header; use an HTTP client when you need that header.",
7147
+ description: "Inspect the HTTP row count and range without retrieving row data; send Prefer: count=exact for a filtered count. Returns no body and does not fetch or serialize row data. A known count determines the interval using limit/offset or Range with a default and maximum page size of 1000. Without a count preference, or when the selected count mode cannot supply a total, Content-Range is */*. Singular Accept still validates page cardinality. Unlike Supabase, planned and estimated counts do not use PostgreSQL planner estimates. Count preferences: without filters, exact, planned and estimated all use SQLite's exact b-tree count. With filters, exact counts within budget; planned skips counting and reports an unknown total (Content-Range: 0-24/*); estimated attempts exact counting only when the preflight budget allows it, otherwise reports an unknown total. Preference-Applied echoes the requested count mode. Use praxis.database.count in the SDK or codemode to return { count: number | null } from this header without retrieving rows.",
7106
7148
  http: "HEAD",
7107
7149
  path: "/database/v1/databases/{database_id}/rest/{table}",
7108
7150
  streaming: "no",
@@ -7128,7 +7170,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7128
7170
  name: "{column}",
7129
7171
  type: "string",
7130
7172
  required: false,
7131
- description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. See the filter operator matrix."
7173
+ description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. Supported operators: eq (equal), neq (not equal; ne is not supported), gt/gte/lt/lte (comparisons), like (case-sensitive text pattern), ilike (case-insensitive text pattern), in (membership), is (null/boolean test), cs (contains), cd (contained by), and ov (array overlap). For like/ilike, * or % matches any sequence and _ matches one character, e.g. title=ilike.*spring*; URL-encode a literal % as %25. Use days=in.(1,2,3), title=is.null, active=is.true or active=is.false; is.not_null and is.unknown are also accepted (unknown means null). Negate a condition with not., e.g. title=not.eq.draft. Multiple filters are ANDed; use or=(title.eq.a,title.eq.b), and=(days.gte.1,days.lte.3), not.or=(...) or not.and=(...), with nested and(...) and or(...) groups as needed. cs/cd accept JSON objects or arrays on JSON fields; cs also accepts a JSON scalar. For array values or multi-link fields, use cs.{a,b}, cd.{a,b} or ov.{a,b}. JSON paths use -> for JSON and ->> for text, e.g. attributes->>has_onsen=eq.true or attributes->tags=cs.[1,2]; chain -> segments and end with ->> for text. Dot paths such as attributes.has_onsen are unsupported. Full-text, regex, range operators and any/all modifiers are unsupported."
7132
7174
  },
7133
7175
  {
7134
7176
  name: "and",
@@ -7176,7 +7218,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7176
7218
  name: "on_conflict",
7177
7219
  type: "string",
7178
7220
  required: false,
7179
- description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7221
+ description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. With resolution=merge-duplicates, a conflicting row keeps columns omitted from the request; supplied values replace the whole field value (including JSON objects), and explicit null clears a nullable field. With resolution=ignore-duplicates, a conflicting row is left unchanged. Without a conflict, both modes insert a new row using field defaults for omitted fields. In a batch, a key present in another row is filled with null when omitted in this row unless missing=default is set; that null can overwrite a value during merge. With missing=default, omitted keys stay omitted for conflict merges (existing values are preserved); new rows use defaults, or null when no default exists. Explicit null never invokes a default. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7180
7222
  },
7181
7223
  {
7182
7224
  name: "columns",
@@ -7216,7 +7258,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7216
7258
  name: "{column}",
7217
7259
  type: "string",
7218
7260
  required: false,
7219
- description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. See the filter operator matrix."
7261
+ description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. Supported operators: eq (equal), neq (not equal; ne is not supported), gt/gte/lt/lte (comparisons), like (case-sensitive text pattern), ilike (case-insensitive text pattern), in (membership), is (null/boolean test), cs (contains), cd (contained by), and ov (array overlap). For like/ilike, * or % matches any sequence and _ matches one character, e.g. title=ilike.*spring*; URL-encode a literal % as %25. Use days=in.(1,2,3), title=is.null, active=is.true or active=is.false; is.not_null and is.unknown are also accepted (unknown means null). Negate a condition with not., e.g. title=not.eq.draft. Multiple filters are ANDed; use or=(title.eq.a,title.eq.b), and=(days.gte.1,days.lte.3), not.or=(...) or not.and=(...), with nested and(...) and or(...) groups as needed. cs/cd accept JSON objects or arrays on JSON fields; cs also accepts a JSON scalar. For array values or multi-link fields, use cs.{a,b}, cd.{a,b} or ov.{a,b}. JSON paths use -> for JSON and ->> for text, e.g. attributes->>has_onsen=eq.true or attributes->tags=cs.[1,2]; chain -> segments and end with ->> for text. Dot paths such as attributes.has_onsen are unsupported. Full-text, regex, range operators and any/all modifiers are unsupported."
7220
7262
  },
7221
7263
  {
7222
7264
  name: "and",
@@ -7264,7 +7306,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7264
7306
  name: "on_conflict",
7265
7307
  type: "string",
7266
7308
  required: false,
7267
- description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7309
+ description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. With resolution=merge-duplicates, a conflicting row keeps columns omitted from the request; supplied values replace the whole field value (including JSON objects), and explicit null clears a nullable field. With resolution=ignore-duplicates, a conflicting row is left unchanged. Without a conflict, both modes insert a new row using field defaults for omitted fields. In a batch, a key present in another row is filled with null when omitted in this row unless missing=default is set; that null can overwrite a value during merge. With missing=default, omitted keys stay omitted for conflict merges (existing values are preserved); new rows use defaults, or null when no default exists. Explicit null never invokes a default. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7268
7310
  },
7269
7311
  {
7270
7312
  name: "columns",
@@ -7284,7 +7326,7 @@ Own-face extension. Use a readable session and a run_id from its events. Each su
7284
7326
  summary: "Insert rows",
7285
7327
  description: `Insert rows into an existing table after defining its fields; at most 500 rows per request. Each request is atomic: any failure rolls back the entire batch. There are no transactions across requests.
7286
7328
  Rows are filled to the union of their keys with null; send Prefer: missing=default to use field defaults for missing keys.
7287
- The default is return=minimal: success returns HTTP 201 with an empty body. Send Prefer: return=representation to return the inserted rows. Unlike Supabase, upsert conflicts can target only id or one unique field, not a composite key. Each table supports up to 1,000,000 rows.`,
7329
+ The default is return=minimal: success returns HTTP 201 with an empty body. Send Prefer: return=representation to return the inserted rows. With resolution=merge-duplicates, a conflicting row keeps columns omitted from the request; supplied values replace the whole field value (including JSON objects), and explicit null clears a nullable field. With resolution=ignore-duplicates, a conflicting row is left unchanged. Without a conflict, both modes insert a new row using field defaults for omitted fields. In a batch, a key present in another row is filled with null when omitted in this row unless missing=default is set; that null can overwrite a value during merge. With missing=default, omitted keys stay omitted for conflict merges (existing values are preserved); new rows use defaults, or null when no default exists. Explicit null never invokes a default. Unlike Supabase, upsert conflicts can target only id or one unique field, not a composite key. Each table supports up to 1,000,000 rows.`,
7288
7330
  http: "POST",
7289
7331
  path: "/database/v1/databases/{database_id}/rest/{table}",
7290
7332
  streaming: "no",
@@ -7310,7 +7352,7 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7310
7352
  name: "{column}",
7311
7353
  type: "string",
7312
7354
  required: false,
7313
- description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. See the filter operator matrix."
7355
+ description: "Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. Supported operators: eq (equal), neq (not equal; ne is not supported), gt/gte/lt/lte (comparisons), like (case-sensitive text pattern), ilike (case-insensitive text pattern), in (membership), is (null/boolean test), cs (contains), cd (contained by), and ov (array overlap). For like/ilike, * or % matches any sequence and _ matches one character, e.g. title=ilike.*spring*; URL-encode a literal % as %25. Use days=in.(1,2,3), title=is.null, active=is.true or active=is.false; is.not_null and is.unknown are also accepted (unknown means null). Negate a condition with not., e.g. title=not.eq.draft. Multiple filters are ANDed; use or=(title.eq.a,title.eq.b), and=(days.gte.1,days.lte.3), not.or=(...) or not.and=(...), with nested and(...) and or(...) groups as needed. cs/cd accept JSON objects or arrays on JSON fields; cs also accepts a JSON scalar. For array values or multi-link fields, use cs.{a,b}, cd.{a,b} or ov.{a,b}. JSON paths use -> for JSON and ->> for text, e.g. attributes->>has_onsen=eq.true or attributes->tags=cs.[1,2]; chain -> segments and end with ->> for text. Dot paths such as attributes.has_onsen are unsupported. Full-text, regex, range operators and any/all modifiers are unsupported."
7314
7356
  },
7315
7357
  {
7316
7358
  name: "and",
@@ -7358,7 +7400,7 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7358
7400
  name: "on_conflict",
7359
7401
  type: "string",
7360
7402
  required: false,
7361
- description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7403
+ description: "POST upsert: id or one unique field with resolution=merge-duplicates|ignore-duplicates. With resolution=merge-duplicates, a conflicting row keeps columns omitted from the request; supplied values replace the whole field value (including JSON objects), and explicit null clears a nullable field. With resolution=ignore-duplicates, a conflicting row is left unchanged. Without a conflict, both modes insert a new row using field defaults for omitted fields. In a batch, a key present in another row is filled with null when omitted in this row unless missing=default is set; that null can overwrite a value during merge. With missing=default, omitted keys stay omitted for conflict merges (existing values are preserved); new rows use defaults, or null when no default exists. Explicit null never invokes a default. POST accepts an optional client id matching [A-Za-z0-9_-]{1,64}; omitted ids are generated. PATCH cannot change id."
7362
7404
  },
7363
7405
  {
7364
7406
  name: "columns",
@@ -7410,7 +7452,7 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7410
7452
  resource: "database.databases.tables",
7411
7453
  method: "create",
7412
7454
  summary: "Create table",
7413
- description: "Creates a table together with its initial fields in one call and returns it (HTTP 201) with generated tbl_ and fld_ ids. name must match ^[a-z_][a-z0-9_]{0,62}$ and be unused in this database (duplicate: 409 UNIQUE_VIOLATION). fields is required and may be an empty array; fields can also be added later with databaseAddField. Limits: 50 tables per database, 64 fields per table, and 16 indexed or unique fields per table; exceeding one returns 400 LIMIT_EXCEEDED. Every row also has a system id column, which is not declared as a field. Insert data afterwards with databaseInsert. Unlike Supabase, define schema with this table API and the field APIs; PostgreSQL DDL and SQL migration files cannot be submitted. Each table supports up to 1,000,000 rows.",
7455
+ description: 'Creates a table together with its initial fields in one call and returns it (HTTP 201) with generated tbl_ and fld_ ids. name must match ^[a-z_][a-z0-9_]{0,62}$ and be unused in this database (duplicate: 409 UNIQUE_VIOLATION). fields is required and may be an empty array; fields can also be added later with databaseAddField. Limits: 50 tables per database, 64 fields per table, and 16 indexed or unique fields per table; exceeding one returns 400 LIMIT_EXCEEDED. Fields default to indexed:false. Set indexed:true explicitly for fields used in filters or ordering. Only indexed:true or unique:true consumes one of the 16 indexed-or-unique field slots; a field with both consumes one slot. Unique fields still consume a slot even with indexed:false. Existing fields are unchanged. Minimal field example: {"name":"notes","type":"string","indexed":false}. Every row also has a system id column, which is not declared as a field. Insert data afterwards with databaseInsert. Unlike Supabase, define schema with this table API and the field APIs; PostgreSQL DDL and SQL migration files cannot be submitted. Each table supports up to 1,000,000 rows.',
7414
7456
  http: "POST",
7415
7457
  path: "/database/v1/databases/{database_id}/tables",
7416
7458
  streaming: "no",
@@ -7531,7 +7573,7 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7531
7573
  resource: "database.databases.tables.fields",
7532
7574
  method: "create",
7533
7575
  summary: "Add field",
7534
- description: "Adds one field to an existing table and returns the updated table (HTTP 201). Required inputs are name (lowercase letters, digits and underscores; id and names starting with _ are reserved) and type (string, integer, number, boolean, datetime, json or link); json needs options.schema and link needs options.linked_table_id and options.multiple. Adding a required field to a table that already has rows needs a default, otherwise the call fails with 400 VALIDATION_FAILED; a default is written into every existing row. Scalar fields are indexed by default, while json and link fields cannot be indexed. Limits: 64 fields and 16 indexed or unique fields per table (400 LIMIT_EXCEEDED). Unlike Supabase, schema evolution uses field definitions rather than ALTER TABLE statements or SQL migrations.",
7576
+ description: 'Adds one field to an existing table and returns the updated table (HTTP 201). Required inputs are name (lowercase letters, digits and underscores; id and names starting with _ are reserved) and type (string, integer, number, boolean, datetime, json or link); json needs options.schema and link needs options.linked_table_id and options.multiple. Adding a required field to a table that already has rows needs a default, otherwise the call fails with 400 VALIDATION_FAILED; a default is written into every existing row. Fields default to indexed:false. Set indexed:true explicitly for fields used in filters or ordering. Only indexed:true or unique:true consumes one of the 16 indexed-or-unique field slots; a field with both consumes one slot. Unique fields still consume a slot even with indexed:false. Existing fields are unchanged. Minimal field example: {"name":"notes","type":"string","indexed":false}. JSON and link fields cannot be indexed. Limits: 64 fields and 16 indexed or unique fields per table (400 LIMIT_EXCEEDED). Unlike Supabase, schema evolution uses field definitions rather than ALTER TABLE statements or SQL migrations.',
7535
7577
  http: "POST",
7536
7578
  path: "/database/v1/databases/{database_id}/tables/{table}/fields",
7537
7579
  streaming: "no",
@@ -7701,7 +7743,7 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7701
7743
  resource: "file.files",
7702
7744
  method: "create",
7703
7745
  summary: "Upload file",
7704
- description: 'Upload a file to reuse in conversations or attach to a knowledge base. Unlike OpenAI Files, uploads must be nonempty and at most 104857600 bytes, and only user_data, assistants and vision purposes are accepted. Send file bytes as a multipart file part, not a filename or file ID; in the SDK use a File or Blob, for example new File(["Return within 23 days."], "policy.txt", { type: "text/plain" }). Content determines the MIME type. Supported uploads include PDF, DOCX/XLSX/PPTX, PNG/JPEG/GIF/WebP, MP3/WAV/FLAC/OGG/M4A, MP4/WebM, and UTF-8 JSON/NDJSON/CSV/Markdown/plain text; empty, unsupported, oversized files and unsupported purposes return 400. OpenAI Uploads chunking is not supported. The purpose vision accepts only PNG, JPEG, GIF and WebP, while conversation images require user_data. The returned ID belongs to your tenant; upload alone does not ingest the file into Knowledge. Knowledge attachment has a lower limit of 100000000 bytes. Optionally set expires_after[anchor]=created_at with expires_after[seconds] (3600–2592000), or praxis[expires_in_seconds] (3600–7776000), never both. Without expiry, the file persists until deleted.',
7746
+ description: 'Upload a file to reuse in conversations or attach to a knowledge base. Unlike OpenAI Files, uploads must be nonempty and at most 100000000 bytes, and only user_data, assistants and vision purposes are accepted. Send file bytes as a multipart file part, not a filename or file ID; in the SDK use a File or Blob, for example new File(["Return within 23 days."], "policy.txt", { type: "text/plain" }). Content determines the MIME type. Supported uploads include PDF, DOCX/XLSX/PPTX, PNG/JPEG/GIF/WebP, MP3/WAV/FLAC/OGG/M4A, MP4/WebM, and UTF-8 JSON/NDJSON/CSV/Markdown/plain text; empty, unsupported, oversized files and unsupported purposes return 400. Use Uploads for larger files. The purpose vision accepts only PNG, JPEG, GIF and WebP, while conversation images require user_data. The returned ID belongs to your tenant; upload alone does not ingest the file into Knowledge. Knowledge attachment has a lower limit of 100000000 bytes. Optionally set expires_after[anchor]=created_at with expires_after[seconds] (3600–2592000), or praxis[expires_in_seconds] (3600–7776000), never both. Without expiry, the file persists until deleted.',
7705
7747
  http: "POST",
7706
7748
  path: "/file/v1/files",
7707
7749
  streaming: "no",
@@ -7716,7 +7758,7 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7716
7758
  name: "file",
7717
7759
  type: "string",
7718
7760
  required: true,
7719
- description: "File bytes in one multipart file part, not a filename or file ID. The file must be nonempty and at most 104857600 bytes; its content determines the MIME type."
7761
+ description: "File bytes in one multipart file part, not a filename or file ID. The file must be nonempty and at most 100000000 bytes; its content determines the MIME type."
7720
7762
  },
7721
7763
  {
7722
7764
  name: "purpose",
@@ -7765,7 +7807,7 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7765
7807
  resource: "file.files",
7766
7808
  method: "retrieve",
7767
7809
  summary: "Retrieve file",
7768
- description: "Retrieve metadata for a live file owned by your tenant. Use its file_dp_ ID from upload or list. Missing, deleted, expired and other-tenant files return 404; use the content endpoint to download the bytes.",
7810
+ description: "Retrieve metadata for a live file owned by your tenant. Use its file- ID from upload or list. Missing, deleted, expired and other-tenant files return 404; use the content endpoint to download the bytes.",
7769
7811
  http: "GET",
7770
7812
  path: "/file/v1/files/{file_id}",
7771
7813
  streaming: "no",
@@ -7782,7 +7824,7 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7782
7824
  resource: "file.files",
7783
7825
  method: "retrieveContent",
7784
7826
  summary: "Retrieve file content",
7785
- description: "Download the original bytes of a live file owned by your tenant. Supply its file_dp_ ID from upload or list. A single byte Range can return a partial response; a range outside the file returns 416. Missing, deleted, expired and other-tenant files return 404.",
7827
+ description: "Download the original bytes of a live file owned by your tenant. Supply its file- ID from upload or list. A single byte Range can return a partial response; a range outside the file returns 416. Missing, deleted, expired and other-tenant files return 404.",
7786
7828
  http: "GET",
7787
7829
  path: "/file/v1/files/{file_id}/content",
7788
7830
  streaming: "no",
@@ -7795,6 +7837,152 @@ The default is return=minimal: success returns HTTP 201 with an empty body. Send
7795
7837
  ],
7796
7838
  query: []
7797
7839
  },
7840
+ {
7841
+ resource: "file.uploads",
7842
+ method: "create",
7843
+ summary: "Create an upload",
7844
+ description: "Start a multipart upload for a file larger than the 100000000-byte direct-upload limit. Supply the exact byte length, filename, MIME type and purpose. Unlike OpenAI, uploads accept at most 2 GiB (2147483648 bytes) and only user_data, assistants and vision purposes. Add parts of at most 64000000 bytes and complete within one hour. Optional part_size enables fixed-size, retryable parts. expires_after applies to the resulting file (3600–7776000 seconds on this own face); without it the file persists until deleted.",
7845
+ http: "POST",
7846
+ path: "/file/v1/uploads",
7847
+ streaming: "no",
7848
+ pagination: false,
7849
+ pathParams: [],
7850
+ query: [],
7851
+ body: {
7852
+ required: true,
7853
+ fields: [
7854
+ {
7855
+ name: "bytes",
7856
+ type: "integer",
7857
+ required: true,
7858
+ description: "Exact complete file length in bytes, from 1 through 2147483648 (2 GiB); the sum of completed parts must match."
7859
+ },
7860
+ {
7861
+ name: "filename",
7862
+ type: "string",
7863
+ required: true,
7864
+ description: "Filename for the resulting file, at most 255 UTF-8 bytes."
7865
+ },
7866
+ {
7867
+ name: "mime_type",
7868
+ type: "string",
7869
+ required: true,
7870
+ description: "Declared MIME type; validated against the assembled content."
7871
+ },
7872
+ {
7873
+ name: "purpose",
7874
+ type: "string",
7875
+ enum: [
7876
+ "user_data",
7877
+ "assistants",
7878
+ "vision"
7879
+ ],
7880
+ required: true,
7881
+ description: "Use user_data for conversation attachments, assistants for assistant files, or vision for PNG, JPEG, GIF and WebP images."
7882
+ },
7883
+ {
7884
+ name: "expires_after",
7885
+ type: "object",
7886
+ required: false,
7887
+ description: "Optional expiry policy for the resulting file; omission retains the file until deletion."
7888
+ },
7889
+ {
7890
+ name: "part_size",
7891
+ type: "integer",
7892
+ required: false,
7893
+ description: "Optional fixed part size in bytes, 5242880–64000000. Requires part_number on every part; retries of identical bytes at the same number return the same part. Omit to use arbitrary-sized parts."
7894
+ }
7895
+ ]
7896
+ }
7897
+ },
7898
+ {
7899
+ resource: "file.uploads",
7900
+ method: "cancel",
7901
+ summary: "Cancel an upload",
7902
+ description: "Cancel an unfinished upload using upload_id from create and release its reserved storage. Further parts and completion are rejected. Cancelling an already cancelled upload returns the same terminal result; a completed upload cannot be cancelled. Delete its resulting file instead.",
7903
+ http: "POST",
7904
+ path: "/file/v1/uploads/{upload_id}/cancel",
7905
+ streaming: "no",
7906
+ pagination: false,
7907
+ pathParams: [
7908
+ {
7909
+ name: "upload_id",
7910
+ sdkName: "upload_id"
7911
+ }
7912
+ ],
7913
+ query: []
7914
+ },
7915
+ {
7916
+ resource: "file.uploads",
7917
+ method: "complete",
7918
+ summary: "Complete an upload",
7919
+ description: "Assemble the uploaded parts into a file. Supply upload_id from create and part_ids from addPart in file byte order; their sizes must total the declared bytes. Optional md5 verifies the assembled content. Retrying identical part_ids and normalized md5 returns the same result; different inputs return 409. Unlike OpenAI, completed can contain a file still processing (status uploaded). Poll files.retrieve using file.id until status processed (own face state active); error, expiry or 404 means processing failed and the file cannot be used.",
7920
+ http: "POST",
7921
+ path: "/file/v1/uploads/{upload_id}/complete",
7922
+ streaming: "no",
7923
+ pagination: false,
7924
+ pathParams: [
7925
+ {
7926
+ name: "upload_id",
7927
+ sdkName: "upload_id"
7928
+ }
7929
+ ],
7930
+ query: [],
7931
+ body: {
7932
+ required: true,
7933
+ fields: [
7934
+ {
7935
+ name: "part_ids",
7936
+ type: "array",
7937
+ itemType: "string",
7938
+ required: true,
7939
+ description: "All part IDs in concatenation order, without duplicates. Fixed-size uploads require increasing part_number order."
7940
+ },
7941
+ {
7942
+ name: "md5",
7943
+ type: "string",
7944
+ required: false,
7945
+ description: "Optional whole-file MD5 as 32 hexadecimal characters; verified after assembly."
7946
+ }
7947
+ ]
7948
+ }
7949
+ },
7950
+ {
7951
+ resource: "file.uploads.parts",
7952
+ method: "create",
7953
+ summary: "Add an upload part",
7954
+ description: "Send one nonempty part as the multipart data field, at most 64000000 bytes. Parts may arrive concurrently in any order. Use upload_id from create. For a fixed-size upload, supply part_number starting at 1 and exactly part_size bytes except for the final remainder; retry the same number with identical bytes to recover its part ID. Without part_size, each request creates a new part and part_number is ignored. Save the returned part IDs for complete.",
7955
+ http: "POST",
7956
+ path: "/file/v1/uploads/{upload_id}/parts",
7957
+ streaming: "no",
7958
+ pagination: false,
7959
+ multipart: true,
7960
+ pathParams: [
7961
+ {
7962
+ name: "upload_id",
7963
+ sdkName: "upload_id"
7964
+ }
7965
+ ],
7966
+ query: [
7967
+ {
7968
+ name: "part_number",
7969
+ type: "integer",
7970
+ required: false,
7971
+ description: "Required when create set part_size; 1 through ceil(bytes / part_size). Ignored for general uploads."
7972
+ }
7973
+ ],
7974
+ body: {
7975
+ required: true,
7976
+ fields: [
7977
+ {
7978
+ name: "data",
7979
+ type: "string",
7980
+ required: true,
7981
+ description: "Raw bytes of this part, nonempty and at most 64000000 bytes."
7982
+ }
7983
+ ]
7984
+ }
7985
+ },
7798
7986
  {
7799
7987
  resource: "knowledge.vectorStores",
7800
7988
  method: "list",
@@ -12137,7 +12325,7 @@ for more details.`
12137
12325
  name: "message",
12138
12326
  type: "string",
12139
12327
  required: true,
12140
- description: "Problem description and original error text; at most 4096 Unicode code points."
12328
+ description: "Problem description, at most 4096 Unicode code points; may be empty only when contexts.praxis.error.message is a non-empty string."
12141
12329
  },
12142
12330
  {
12143
12331
  name: "contact_email",
@@ -14701,6 +14889,232 @@ for more details.`
14701
14889
  }
14702
14890
  ];
14703
14891
 
14892
+ // ../../packages/sdk/typescript/src/core/errors.ts
14893
+ function problemFrom(status, headers, body, statusText) {
14894
+ const value = body && typeof body === "object" ? body : {};
14895
+ const nested = value.error && typeof value.error === "object" ? value.error : {};
14896
+ const string = (v) => typeof v === "string" && v.trim() ? v : undefined;
14897
+ const fallback = statusText || { 400: "Bad Request", 401: "Unauthorized", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 408: "Request Timeout", 409: "Conflict", 413: "Payload Too Large", 415: "Unsupported Media Type", 422: "Unprocessable Entity", 429: "Too Many Requests", 500: "Internal Server Error", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout" }[status] || `HTTP ${status}`;
14898
+ const detail = string(value.detail) ?? string(nested.message) ?? string(value.message) ?? string(body);
14899
+ const title = string(value.title) ?? fallback;
14900
+ const param = nested.param ?? value.param;
14901
+ return {
14902
+ status,
14903
+ message: detail ?? title,
14904
+ title,
14905
+ detail,
14906
+ code: string(value.code) ?? string(nested.code) ?? string(nested.type) ?? string(value.error_code),
14907
+ type: string(nested.type) ?? string(value.type),
14908
+ param: typeof param === "string" ? param : param === null || nested.param === null ? null : undefined,
14909
+ errors: Array.isArray(value.errors) ? value.errors : [],
14910
+ requestId: headers.get("x-request-id") ?? string(value.requestId) ?? string(value.request_id),
14911
+ raw: body
14912
+ };
14913
+ }
14914
+
14915
+ class PraxisError extends Error {
14916
+ status;
14917
+ code;
14918
+ type;
14919
+ title;
14920
+ detail;
14921
+ errors;
14922
+ requestId;
14923
+ param;
14924
+ raw;
14925
+ constructor(problem = {}, options) {
14926
+ super(problem.message ?? problem.detail ?? problem.title ?? `Praxis request failed${problem.status ? ` (${problem.status})` : ""}`, options);
14927
+ this.name = "PraxisError";
14928
+ this.status = problem.status;
14929
+ this.code = problem.code;
14930
+ this.type = problem.type;
14931
+ this.title = problem.title;
14932
+ this.detail = problem.detail;
14933
+ this.errors = problem.errors ?? [];
14934
+ this.requestId = problem.requestId;
14935
+ this.param = problem.param;
14936
+ this.raw = problem.raw;
14937
+ }
14938
+ static fromResponse(response, body) {
14939
+ return new PraxisError(problemFrom(response.status, response.headers, body, response.statusText));
14940
+ }
14941
+ }
14942
+
14943
+ class PraxisConnectionError extends PraxisError {
14944
+ constructor(cause) {
14945
+ super({ code: "CONNECTION_ERROR", title: "Unable to connect to Praxis" }, { cause });
14946
+ this.name = "PraxisConnectionError";
14947
+ }
14948
+ }
14949
+
14950
+ class PraxisTimeoutError extends PraxisError {
14951
+ constructor(timeoutMs) {
14952
+ super({ code: "TIMEOUT", title: `Praxis request exceeded ${timeoutMs} ms` });
14953
+ this.name = "PraxisTimeoutError";
14954
+ }
14955
+ }
14956
+
14957
+ // ../../packages/sdk/typescript/src/core/retry.ts
14958
+ function canRetry(request) {
14959
+ return request.method !== "POST" || Boolean(request.headers.get("Idempotency-Key")?.trim());
14960
+ }
14961
+ function retryAfterMs(value, now = Date.now()) {
14962
+ if (!value)
14963
+ return;
14964
+ if (/^\d+(?:\.\d+)?$/.test(value.trim()))
14965
+ return Number(value) * 1000;
14966
+ const date = Date.parse(value);
14967
+ return Number.isFinite(date) ? Math.max(0, date - now) : undefined;
14968
+ }
14969
+ function retryDelay(attempt, retryAfter, random = Math.random) {
14970
+ return retryAfterMs(retryAfter) ?? Math.min(30000, 500 * 2 ** attempt) * (0.5 + random() * 0.5);
14971
+ }
14972
+ function abortable(promise, signal) {
14973
+ signal.throwIfAborted();
14974
+ return new Promise((resolve, reject) => {
14975
+ const abort = () => reject(signal.reason);
14976
+ signal.addEventListener("abort", abort, { once: true });
14977
+ promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
14978
+ });
14979
+ }
14980
+ function sleep(ms, signal) {
14981
+ signal.throwIfAborted();
14982
+ return new Promise((resolve, reject) => {
14983
+ const abort = () => {
14984
+ clearTimeout(timer);
14985
+ reject(signal.reason);
14986
+ };
14987
+ const timer = setTimeout(() => {
14988
+ signal.removeEventListener("abort", abort);
14989
+ resolve();
14990
+ }, ms);
14991
+ signal.addEventListener("abort", abort, { once: true });
14992
+ });
14993
+ }
14994
+ async function fetchWithRetry(request, fetcher, options) {
14995
+ const eligible = canRetry(request);
14996
+ const wait = options.wait ?? sleep;
14997
+ for (let attempt = 0;; attempt++) {
14998
+ options.signal.throwIfAborted();
14999
+ let response;
15000
+ try {
15001
+ response = await abortable(fetcher(request.clone()), options.signal);
15002
+ } catch (cause) {
15003
+ options.signal.throwIfAborted();
15004
+ if (!eligible || attempt >= options.maxRetries)
15005
+ throw new PraxisConnectionError(cause);
15006
+ await wait(retryDelay(attempt, null, options.random), options.signal);
15007
+ continue;
15008
+ }
15009
+ const retriable = response.status === 408 || response.status === 429 || response.status >= 500 && response.status <= 599;
15010
+ if (!eligible || !retriable || attempt >= options.maxRetries)
15011
+ return response;
15012
+ const delay = retryDelay(attempt, response.headers.get("Retry-After"), options.random);
15013
+ await response.body?.cancel();
15014
+ await wait(delay, options.signal);
15015
+ }
15016
+ }
15017
+
15018
+ // ../../packages/sdk/typescript/src/helpers/file-upload.ts
15019
+ var MAX_RETRIES = 5;
15020
+ function retryable(error) {
15021
+ return error instanceof PraxisConnectionError || error instanceof PraxisTimeoutError || error instanceof SyntaxError || error instanceof PraxisError && (error.status === 408 || error.status === 429 || error.status !== undefined && error.status >= 500 && error.status <= 599);
15022
+ }
15023
+ async function retry(signal, operation) {
15024
+ for (let used = 0;; used++) {
15025
+ signal.throwIfAborted();
15026
+ try {
15027
+ return await operation();
15028
+ } catch (error) {
15029
+ signal.throwIfAborted();
15030
+ if (used >= MAX_RETRIES || !retryable(error))
15031
+ throw error;
15032
+ await sleep(500 * 2 ** used, signal);
15033
+ }
15034
+ }
15035
+ }
15036
+ async function uploadFile(client, file, options) {
15037
+ options.signal?.throwIfAborted();
15038
+ if (file.size < 1 || file.size > 2 * 1024 ** 3)
15039
+ throw new RangeError("File size must be 1 byte through 2 GiB");
15040
+ const partSize = options.partSize ?? 50 * 1024 ** 2;
15041
+ const concurrency = options.concurrency ?? 3;
15042
+ if (!Number.isSafeInteger(partSize) || partSize < 5 * 1024 ** 2 || partSize > 64000000)
15043
+ throw new RangeError("partSize must be an integer from 5242880 through 64000000");
15044
+ if (!Number.isSafeInteger(concurrency) || concurrency < 1)
15045
+ throw new RangeError("concurrency must be a positive integer");
15046
+ const filename = options.filename ?? (typeof File !== "undefined" && file instanceof File ? file.name : "upload");
15047
+ const work = new AbortController;
15048
+ const abort = () => work.abort(options.signal?.reason);
15049
+ options.signal?.addEventListener("abort", abort, { once: true });
15050
+ const signal = work.signal;
15051
+ const request = { signal, maxRetries: 0, timeoutMs: 120000 };
15052
+ let uploadId;
15053
+ const active = async (initial) => {
15054
+ let result = initial;
15055
+ for (let polls = 0;; polls++) {
15056
+ signal.throwIfAborted();
15057
+ if (result.state === "active")
15058
+ return result;
15059
+ if (result.state !== "processing")
15060
+ throw new PraxisError({ code: "FILE_PROCESSING_FAILED", message: `File ${result.id} is not active (${result.state})` });
15061
+ await sleep(Math.min(5000, 1000 * 2 ** polls), signal);
15062
+ result = await retry(signal, () => client.file.files.retrieve({ path: { file_id: result.id }, ...request }));
15063
+ }
15064
+ };
15065
+ try {
15066
+ signal.throwIfAborted();
15067
+ options.onProgress?.(0);
15068
+ if (file.size <= 32 * 1024 ** 2) {
15069
+ const result2 = await client.file.files.create({ body: { file: new File([file], filename, { type: file.type }), purpose: options.purpose }, ...request });
15070
+ options.onProgress?.(1);
15071
+ return await active(result2);
15072
+ }
15073
+ const upload = await client.file.uploads.create({ body: { bytes: file.size, filename, mime_type: file.type || "application/octet-stream", purpose: options.purpose, part_size: partSize }, ...request });
15074
+ uploadId = upload.id;
15075
+ signal.throwIfAborted();
15076
+ const path = { upload_id: uploadId };
15077
+ const count = Math.ceil(file.size / partSize);
15078
+ const ids = new Array(count);
15079
+ let cursor = 0, confirmed = 0;
15080
+ const worker = async () => {
15081
+ while (cursor < count) {
15082
+ signal.throwIfAborted();
15083
+ const index = cursor++;
15084
+ const chunk = file.slice(index * partSize, Math.min(file.size, (index + 1) * partSize));
15085
+ const part = await retry(signal, () => client.file.uploads.parts.create({ path, query: { part_number: index + 1 }, body: { data: new File([chunk], filename) }, ...request }));
15086
+ ids[index] = part.id;
15087
+ confirmed += chunk.size;
15088
+ options.onProgress?.(confirmed / file.size);
15089
+ }
15090
+ };
15091
+ const workers = Array.from({ length: Math.min(concurrency, count) }, worker);
15092
+ try {
15093
+ await Promise.all(workers);
15094
+ } catch (error) {
15095
+ work.abort();
15096
+ await Promise.allSettled(workers);
15097
+ throw error;
15098
+ }
15099
+ const result = await retry(signal, () => client.file.uploads.complete({ path, body: { part_ids: ids }, ...request }));
15100
+ if (result.error)
15101
+ throw new PraxisError({ code: result.error.code, message: result.error.message });
15102
+ if (result.status !== "completed" || !result.file)
15103
+ throw new PraxisError({ code: "INVALID_RESPONSE", message: "Complete did not return a completed Upload with a file" });
15104
+ return await active(result.file);
15105
+ } catch (error) {
15106
+ work.abort();
15107
+ if (uploadId) {
15108
+ await client.file.uploads.cancel({ path: { upload_id: uploadId }, maxRetries: 0, timeoutMs: 1e4 }).catch(() => {});
15109
+ }
15110
+ if (options.signal?.aborted)
15111
+ throw options.signal.reason;
15112
+ throw error;
15113
+ } finally {
15114
+ options.signal?.removeEventListener("abort", abort);
15115
+ }
15116
+ }
15117
+
14704
15118
  // ../../packages/sdk/typescript/src/core/multipart.ts
14705
15119
  function multipartBodySerializer(body) {
14706
15120
  const data = new FormData;
@@ -14768,9 +15182,9 @@ function createSseClient({
14768
15182
  ...options
14769
15183
  }) {
14770
15184
  let lastEventId;
14771
- const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
15185
+ const sleep2 = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
14772
15186
  const createStream = async function* () {
14773
- let retryDelay = sseDefaultRetryDelay ?? 3000;
15187
+ let retryDelay2 = sseDefaultRetryDelay ?? 3000;
14774
15188
  let attempt = 0;
14775
15189
  const signal = options.signal ?? new AbortController().signal;
14776
15190
  while (true) {
@@ -14834,7 +15248,7 @@ function createSseClient({
14834
15248
  } else if (line.startsWith("retry:")) {
14835
15249
  const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
14836
15250
  if (!Number.isNaN(parsed)) {
14837
- retryDelay = parsed;
15251
+ retryDelay2 = parsed;
14838
15252
  }
14839
15253
  }
14840
15254
  }
@@ -14862,7 +15276,7 @@ function createSseClient({
14862
15276
  data,
14863
15277
  event: eventName,
14864
15278
  id: lastEventId,
14865
- retry: retryDelay
15279
+ retry: retryDelay2
14866
15280
  });
14867
15281
  if (dataLines.length) {
14868
15282
  yield data;
@@ -14879,8 +15293,8 @@ function createSseClient({
14879
15293
  if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {
14880
15294
  break;
14881
15295
  }
14882
- const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000);
14883
- await sleep(backoff);
15296
+ const backoff = Math.min(retryDelay2 * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000);
15297
+ await sleep2(backoff);
14884
15298
  }
14885
15299
  }
14886
15300
  };
@@ -16304,6 +16718,55 @@ var fileRetrieveFileContent = (options) => (options.client ?? client).get({
16304
16718
  url: "/file/v1/files/{file_id}/content",
16305
16719
  ...options
16306
16720
  });
16721
+ var fileCreateUpload = (options) => (options.client ?? client).post({
16722
+ security: [{
16723
+ key: "BearerAuth",
16724
+ scheme: "bearer",
16725
+ type: "http"
16726
+ }],
16727
+ url: "/file/v1/uploads",
16728
+ ...options,
16729
+ headers: {
16730
+ "Content-Type": "application/json",
16731
+ ...options.headers
16732
+ }
16733
+ });
16734
+ var fileAddUploadPart = (options) => (options.client ?? client).post({
16735
+ ...formDataBodySerializer,
16736
+ security: [{
16737
+ key: "BearerAuth",
16738
+ scheme: "bearer",
16739
+ type: "http"
16740
+ }],
16741
+ url: "/file/v1/uploads/{upload_id}/parts",
16742
+ ...options,
16743
+ headers: {
16744
+ "Content-Type": null,
16745
+ ...options.headers
16746
+ }
16747
+ });
16748
+ var fileCompleteUpload = (options) => (options.client ?? client).post({
16749
+ security: [{
16750
+ key: "BearerAuth",
16751
+ scheme: "bearer",
16752
+ type: "http"
16753
+ }],
16754
+ url: "/file/v1/uploads/{upload_id}/complete",
16755
+ ...options,
16756
+ headers: {
16757
+ "Content-Type": "application/json",
16758
+ ...options.headers
16759
+ }
16760
+ });
16761
+ var fileCancelUpload = (options) => (options.client ?? client).post({
16762
+ security: [{
16763
+ key: "BearerAuth",
16764
+ scheme: "bearer",
16765
+ type: "http"
16766
+ }],
16767
+ url: "/file/v1/uploads/{upload_id}/cancel",
16768
+ ...options
16769
+ });
16307
16770
  var healthCheck = (options) => (options?.client ?? client).get({ url: "/meta/v1/health", ...options });
16308
16771
  var metaMethodsSearch = (options) => (options?.client ?? client).get({ url: "/meta/v1/methods", ...options });
16309
16772
  var metaMethodsRetrieve = (options) => (options.client ?? client).get({ url: "/meta/v1/methods/{name}", ...options });
@@ -21013,6 +21476,14 @@ function createFile(transport) {
21013
21476
  list: bind(transport, fileListFiles),
21014
21477
  retrieve: bind(transport, fileRetrieveFile),
21015
21478
  retrieveContent: bind(transport, fileRetrieveFileContent)
21479
+ },
21480
+ uploads: {
21481
+ cancel: bind(transport, fileCancelUpload),
21482
+ complete: bind(transport, fileCompleteUpload),
21483
+ create: bind(transport, fileCreateUpload),
21484
+ parts: {
21485
+ create: bind(transport, (options) => fileAddUploadPart({ ...options, bodySerializer: multipartBodySerializer, headers: { "Content-Type": null, ...options.headers } }))
21486
+ }
21016
21487
  }
21017
21488
  };
21018
21489
  }
@@ -21383,132 +21854,6 @@ var resourceFactories = {
21383
21854
  vault: createVault
21384
21855
  };
21385
21856
 
21386
- // ../../packages/sdk/typescript/src/core/errors.ts
21387
- function problemFrom(status, headers, body, statusText) {
21388
- const value = body && typeof body === "object" ? body : {};
21389
- const nested = value.error && typeof value.error === "object" ? value.error : {};
21390
- const string = (v) => typeof v === "string" && v.trim() ? v : undefined;
21391
- const fallback = statusText || { 400: "Bad Request", 401: "Unauthorized", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 408: "Request Timeout", 409: "Conflict", 413: "Payload Too Large", 415: "Unsupported Media Type", 422: "Unprocessable Entity", 429: "Too Many Requests", 500: "Internal Server Error", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout" }[status] || `HTTP ${status}`;
21392
- const detail = string(value.detail) ?? string(nested.message) ?? string(value.message) ?? string(body);
21393
- const title = string(value.title) ?? fallback;
21394
- const param = nested.param ?? value.param;
21395
- return {
21396
- status,
21397
- message: detail ?? title,
21398
- title,
21399
- detail,
21400
- code: string(value.code) ?? string(nested.code) ?? string(nested.type) ?? string(value.error_code),
21401
- type: string(nested.type) ?? string(value.type),
21402
- param: typeof param === "string" ? param : param === null || nested.param === null ? null : undefined,
21403
- errors: Array.isArray(value.errors) ? value.errors : [],
21404
- requestId: headers.get("x-request-id") ?? string(value.requestId) ?? string(value.request_id),
21405
- raw: body
21406
- };
21407
- }
21408
-
21409
- class PraxisError extends Error {
21410
- status;
21411
- code;
21412
- type;
21413
- title;
21414
- detail;
21415
- errors;
21416
- requestId;
21417
- param;
21418
- raw;
21419
- constructor(problem = {}, options) {
21420
- super(problem.message ?? problem.detail ?? problem.title ?? `Praxis request failed${problem.status ? ` (${problem.status})` : ""}`, options);
21421
- this.name = "PraxisError";
21422
- this.status = problem.status;
21423
- this.code = problem.code;
21424
- this.type = problem.type;
21425
- this.title = problem.title;
21426
- this.detail = problem.detail;
21427
- this.errors = problem.errors ?? [];
21428
- this.requestId = problem.requestId;
21429
- this.param = problem.param;
21430
- this.raw = problem.raw;
21431
- }
21432
- static fromResponse(response, body) {
21433
- return new PraxisError(problemFrom(response.status, response.headers, body, response.statusText));
21434
- }
21435
- }
21436
-
21437
- class PraxisConnectionError extends PraxisError {
21438
- constructor(cause) {
21439
- super({ code: "CONNECTION_ERROR", title: "Unable to connect to Praxis" }, { cause });
21440
- this.name = "PraxisConnectionError";
21441
- }
21442
- }
21443
-
21444
- class PraxisTimeoutError extends PraxisError {
21445
- constructor(timeoutMs) {
21446
- super({ code: "TIMEOUT", title: `Praxis request exceeded ${timeoutMs} ms` });
21447
- this.name = "PraxisTimeoutError";
21448
- }
21449
- }
21450
-
21451
- // ../../packages/sdk/typescript/src/core/retry.ts
21452
- function canRetry(request) {
21453
- return request.method !== "POST" || Boolean(request.headers.get("Idempotency-Key")?.trim());
21454
- }
21455
- function retryAfterMs(value, now = Date.now()) {
21456
- if (!value)
21457
- return;
21458
- if (/^\d+(?:\.\d+)?$/.test(value.trim()))
21459
- return Number(value) * 1000;
21460
- const date = Date.parse(value);
21461
- return Number.isFinite(date) ? Math.max(0, date - now) : undefined;
21462
- }
21463
- function retryDelay(attempt, retryAfter, random = Math.random) {
21464
- return retryAfterMs(retryAfter) ?? Math.min(30000, 500 * 2 ** attempt) * (0.5 + random() * 0.5);
21465
- }
21466
- function abortable(promise, signal) {
21467
- signal.throwIfAborted();
21468
- return new Promise((resolve, reject) => {
21469
- const abort = () => reject(signal.reason);
21470
- signal.addEventListener("abort", abort, { once: true });
21471
- promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
21472
- });
21473
- }
21474
- function sleep(ms, signal) {
21475
- signal.throwIfAborted();
21476
- return new Promise((resolve, reject) => {
21477
- const abort = () => {
21478
- clearTimeout(timer);
21479
- reject(signal.reason);
21480
- };
21481
- const timer = setTimeout(() => {
21482
- signal.removeEventListener("abort", abort);
21483
- resolve();
21484
- }, ms);
21485
- signal.addEventListener("abort", abort, { once: true });
21486
- });
21487
- }
21488
- async function fetchWithRetry(request, fetcher, options) {
21489
- const eligible = canRetry(request);
21490
- const wait = options.wait ?? sleep;
21491
- for (let attempt = 0;; attempt++) {
21492
- options.signal.throwIfAborted();
21493
- let response;
21494
- try {
21495
- response = await abortable(fetcher(request.clone()), options.signal);
21496
- } catch (cause) {
21497
- options.signal.throwIfAborted();
21498
- if (!eligible || attempt >= options.maxRetries)
21499
- throw new PraxisConnectionError(cause);
21500
- await wait(retryDelay(attempt, null, options.random), options.signal);
21501
- continue;
21502
- }
21503
- const retriable = response.status === 408 || response.status === 429 || response.status >= 500 && response.status <= 599;
21504
- if (!eligible || !retriable || attempt >= options.maxRetries)
21505
- return response;
21506
- const delay = retryDelay(attempt, response.headers.get("Retry-After"), options.random);
21507
- await response.body?.cancel();
21508
- await wait(delay, options.signal);
21509
- }
21510
- }
21511
-
21512
21857
  // ../../packages/sdk/typescript/src/core/sse.ts
21513
21858
  async function* parseSSE(body, options = {}) {
21514
21859
  const reader = body.getReader();
@@ -21722,9 +22067,13 @@ class Transport {
21722
22067
  };
21723
22068
  }
21724
22069
  async request(operation, options = {}) {
22070
+ return (await this.requestWithResponse(operation, options)).data;
22071
+ }
22072
+ async requestWithResponse(operation, options = {}) {
21725
22073
  const scope = this.scope(options, false);
21726
22074
  try {
21727
- return (await abortable(operation(scope.options), scope.signal)).data;
22075
+ const result = await abortable(operation(scope.options), scope.signal);
22076
+ return { data: result.data, response: result.response };
21728
22077
  } finally {
21729
22078
  scope.close();
21730
22079
  }
@@ -21929,12 +22278,27 @@ async function publish(client2, input) {
21929
22278
  };
21930
22279
  }
21931
22280
 
22281
+ // ../../packages/sdk/typescript/src/helpers/database-count.ts
22282
+ async function count(transport, input) {
22283
+ const { response } = await transport.requestWithResponse(databaseHead, {
22284
+ path: { database_id: input.database_id, table: input.table },
22285
+ query: input.filters,
22286
+ headers: { Prefer: `count=${input.count ?? "exact"}` }
22287
+ });
22288
+ const total = response.headers.get("Content-Range")?.split("/")[1];
22289
+ return { count: total === undefined || total === "*" ? null : Number(total) };
22290
+ }
22291
+
21932
22292
  // ../../packages/sdk/typescript/src/core/client.ts
21933
22293
  class Praxis {
21934
22294
  constructor(options) {
21935
22295
  const transport = new Transport(options);
21936
22296
  for (const [name, create] of Object.entries(resourceFactories)) {
21937
22297
  const resource = create(transport);
22298
+ if (name === "file")
22299
+ Object.assign(resource, { uploadFile: (file, options2) => uploadFile(this, file, options2) });
22300
+ if (name === "database")
22301
+ Object.assign(resource, { count: (input) => count(transport, input) });
21938
22302
  if (name === "application")
21939
22303
  Object.assign(resource, { publish: (input) => publish(this, input) });
21940
22304
  Object.defineProperty(this, name, { value: resource, enumerable: true });
@@ -23066,7 +23430,7 @@ function plain(value) {
23066
23430
  }
23067
23431
 
23068
23432
  // src/output/human.ts
23069
- var count = (n, noun) => `${n} ${noun}${n === 1 ? "" : "s"}`;
23433
+ var count3 = (n, noun) => `${n} ${noun}${n === 1 ? "" : "s"}`;
23070
23434
  function identity(result, ctx) {
23071
23435
  const op = "op" in result ? result.op : undefined;
23072
23436
  const split = op?.lastIndexOf(".") ?? -1;
@@ -23154,7 +23518,7 @@ function objectView(data, resource, id, ctx, paint) {
23154
23518
  return v.map(scalar).map(plain).join(", ");
23155
23519
  folded = true;
23156
23520
  const preview = v.slice(0, 3).map((item) => record(item).id ?? record(item).name).filter((x) => x !== undefined).map((x) => plain(scalar(x)));
23157
- return `${count(v.length, "item")}${preview.length ? ` (${preview.join(", ")})` : ""}`;
23521
+ return `${count3(v.length, "item")}${preview.length ? ` (${preview.join(", ")})` : ""}`;
23158
23522
  }
23159
23523
  if (v !== null && typeof v === "object") {
23160
23524
  if (depth >= 1) {
@@ -23195,7 +23559,7 @@ function renderHuman(result, ctx) {
23195
23559
  return {
23196
23560
  stdout: result.items.length ? table(result.items, ctx, out) : out.dim(`No ${plain(resource)}${/(s|x|ch|sh)$/.test(resource) ? "es" : "s"}.`) + `
23197
23561
  `,
23198
- stderr: err.dim(`${result.total !== undefined ? `${result.items.length} of ${result.total} items` : count(result.items.length, "item")}${result.nextCursor ? ` · next: ${ctx.nextCommand ?? "praxis --help"}` : ""}`) + `
23562
+ stderr: err.dim(`${result.total !== undefined ? `${result.items.length} of ${result.total} items` : count3(result.items.length, "item")}${result.nextCursor ? ` · next: ${ctx.nextCommand ?? "praxis --help"}` : ""}`) + `
23199
23563
  `
23200
23564
  };
23201
23565
  case "object": {
@@ -23475,7 +23839,7 @@ function startProgress(ctx, write = (text) => writeOutput({ stderr: text })) {
23475
23839
  }
23476
23840
 
23477
23841
  // src/main.ts
23478
- var version = "0.1.0-dev.ce6682e";
23842
+ var version = "0.1.0-dev.d0f5f8e";
23479
23843
  var commands = commandTree(operations);
23480
23844
  var manual = [["login"], ["logout"], ["whoami"], ["org", "list"], ["org", "switch"], ["api"], ["skill"]];
23481
23845
  function route(argv) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepraxis/cli",
3
- "version": "0.1.0-dev.ce6682e",
3
+ "version": "0.1.0-dev.d0f5f8e",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "praxis": "dist/praxis.js"