@inquir/compute 0.4.1 → 0.4.4

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 CHANGED
@@ -1,256 +1,258 @@
1
- # @inquir/compute
2
-
3
- Workspace-first SDK for defining, discovering, generating, and running Inquir Compute assets.
4
-
5
- ## Install
6
-
7
- Requires Node.js 22 or newer. Version 0.3 groups only long-lived container applications in projects; functions and pipelines remain separate assets.
8
-
9
- ```bash
10
- npm install @inquir/compute zod@^3.23.8
11
- ```
12
-
13
- > Release order: publish this SDK before the CLI that depends on it. CLI 0.4.0
14
- > needs SDK 0.4.0 (`isPromoteAccepted`, `getApplicationRollout`); the next CLI
15
- > needs SDK 0.4.1 (`listAppInstances`, `matchAppInstance`); see
16
- > `packages/cli/README.md`, "Publishing to npm".
17
-
18
- ## What It Includes
19
-
20
- - `defineFunction()` and `definePipeline()` for code-first assets.
21
- - `loadConfig()` for `inquir.config.json`.
22
- - `discoverAssets()` for workspace asset scanning.
23
- - `generateCodegenArtifacts()` / `generateAndWriteCodegenArtifacts()` for typed client + `registry.json`.
24
- - `runLocally()` for local function execution/debug harness.
25
- - HTTP transport helpers (`createClient`, `invokeRemote`, deploy/list helpers).
26
- - Application project helpers (`listProjects`, `createProject`, `getProject`, `addProjectResource`) group long-lived containers: web services, APIs, databases, and workers. Project members use `kind: 'application'`; serverless functions are managed separately. `createApplication` accepts `projectId` (ID or slug); omitting it creates a project named after the application. Archiving a project also archives all its applications, including an application shared with another project. Removing a membership leaves it running. Project lifecycle actions require `projects:write` and `apps:promote`.
27
-
28
- ## Pending synchronous invocations
29
-
30
- When the server admits a finite cold invocation, `invokeRemote()` submits once
31
- and follows only the authenticated invocation's `/resume` endpoint. Hot
32
- invocations keep their existing path. Save each handle from `onPending` if the
33
- caller must survive a restart:
34
-
35
- ```ts
36
- import { invokeRemote, resumeRemoteInvocation, cancelRemoteInvocation,
37
- RemoteInvocationObservationEndedError } from '@inquir/compute';
38
-
39
- try {
40
- const result = await invokeRemote(client, 'my-function', event, {
41
- timeoutMs: 120_000, signal, onPending: saveHandle,
42
- });
43
- } catch (error) {
44
- if (error instanceof RemoteInvocationObservationEndedError) {
45
- saveHandle(error.pending); // PENDING/RECOVERY_HELD is not FAILED.
46
- // Later: await resumeRemoteInvocation(client, error.pending);
47
- // Explicit intent: await cancelRemoteInvocation(client, error.pending);
48
- } else throw error;
49
- }
50
- ```
51
-
52
- Abort/timeout stops observation, not execution. A cancellation receipt records
53
- intent, not a terminal outcome. If the initial POST reply is lost before a handle
54
- arrives, this adapter cannot recover an unknown server ID and never repeats that
55
- POST. Server admission remains independently gated; this client does not enable
56
- gateway, pipeline, durable or streaming finite execution.
57
-
58
- ## Durable source commands
59
-
60
- Source execution uses an explicit operation instead of synchronous writable
61
- `/exec`. Save a lowercase UUID v4 before preparing, and atomically retain every
62
- record supplied by the callback in private storage. Records contain command/env
63
- secrets; they contain a credential hash rather than the credential itself.
64
-
65
- ```ts
66
- import { createClient, prepareSourceCommand, restoreSourceCommand } from '@inquir/compute';
67
-
68
- const client = createClient({ baseUrl: apiUrl, apiKey, workspaceId });
69
- const operation = await prepareSourceCommand(client, {
70
- function: 'my-function', requestId: savedUuid,
71
- input: { command: 'npm install', timeoutMs: 60_000 },
72
- retain: savePrivateRecordAtomically,
73
- });
74
- await operation.submit(); // no automatic POST retry
75
- const state = await operation.status(); // GET only
76
- const publication = await operation.reconcile(); // explicit source publication
77
- // await operation.cancel(); // explicit cancellation intent
78
- const restored = await restoreSourceCommand(client, savedRecord, {
79
- retain: savePrivateRecordAtomically,
80
- }); // no HTTP or submission
81
- ```
82
-
83
- The retained API, credential hash, original workspace selector and resolved
84
- workspace/function IDs must match on restore. Later actions stay pinned to those
85
- IDs even if the client changes. A lost response keeps the same operation UUID;
86
- inspect status or explicitly retry the same action. Poll timeout/abort stops
87
- observation and does not cancel or publish. `EXITED` and `CANCEL_REQUESTED` do not
88
- prove physical removal or capacity release. A GET with `publication: null` does
89
- not prove that an earlier explicit publication never happened.
90
-
91
- Acceptance identity is retained before an action returns and after each poll
92
- response. If storage fails, further HTTP is blocked until the pending record is
93
- saved; `operation.persist()` retries storage without HTTP. Keep the updated
94
- record returned by `record()` for reloads. The server must enable the source
95
- command API after its activation gates; a 503 never falls back to writable exec.
96
-
97
- `result.logFailure` and `sourcePublicationState(outcome) === 'CAPTURE_FAILED'`
98
- mark unavailable log capture, preserving the real termination and exit code.
99
- Blank Base64 fields then mean unavailable capture rather than successful empty
100
- logs; workspace output is absent and cannot be published.
101
-
102
- ## Concepts
103
-
104
- - **Function**: a single compute unit with typed input/output (Zod schemas). Created with `defineFunction()`. Discovered by file pattern, run locally via `runLocally()`, deployed and invoked remotely via the transport helpers.
105
- - **Pipeline**: a declarative DAG of functions created with `definePipeline()`. Included in the codegen registry; orchestration is handled server-side.
106
- - **Client**: the generated `client.ts` (from `generateAndWriteCodegenArtifacts()`). Provides a type-safe `createClient()` factory that wraps `invokeRemote()` with your asset's I/O types.
107
- - **Source of truth**: local files on disk. `registry.json` is derived — rebuild it with `generateAndWriteCodegenArtifacts()` whenever sources change.
108
- - **Local ↔ remote link**: remote functions store `linkedLocalName` (local asset name) and `sourceHash`. Compare hashes to detect drift.
109
-
110
- ## Quick Example
111
-
112
- ```ts
113
- import { defineFunction } from '@inquir/compute';
114
- import { z } from 'zod';
115
-
116
- export const hello = defineFunction(
117
- {
118
- name: 'hello',
119
- input: z.object({ name: z.string().optional() }),
120
- output: z.object({ message: z.string() }),
121
- },
122
- async (input) => ({ message: `Hello, ${input.name ?? 'world'}` })
123
- );
124
- ```
125
-
126
- ## Minimal Workspace Config
127
-
128
- Create `inquir.config.json`:
129
-
130
- ```json
131
- {
132
- "functionsDir": "inquir/functions",
133
- "pipelinesDir": "inquir/pipelines",
134
- "generatedDir": ".inquir/generated"
135
- }
136
- ```
137
-
138
- ## Discovery + Codegen
139
-
140
- ```ts
141
- import { discoverAssets, generateAndWriteCodegenArtifacts, loadConfig } from '@inquir/compute';
142
-
143
- const config = loadConfig(process.cwd());
144
- const registry = await discoverAssets(config);
145
- await generateAndWriteCodegenArtifacts(config.absoluteGeneratedDir, registry);
146
- ```
147
-
148
- This generates:
149
-
150
- - `.inquir/generated/client.ts`
151
- - `.inquir/generated/registry.json`
152
-
153
- `registry.json` includes stable metadata such as `name`, `kind`, `sourceHash`, and `assetRoot`.
154
-
155
- ## Local Run
156
-
157
- ```ts
158
- import { runLocally } from '@inquir/compute';
159
-
160
- const result = await runLocally(
161
- {
162
- kind: 'function',
163
- name: 'hello',
164
- filePath: '/abs/path/inquir/functions/hello/index.ts',
165
- exportName: 'hello',
166
- },
167
- { name: 'World' }
168
- );
169
- ```
170
-
171
- ## Remote Invoke (Transport)
172
-
173
- ```ts
174
- import { createClient, invokeRemote } from '@inquir/compute';
175
-
176
- const client = createClient({ baseUrl: 'http://localhost:3000', apiKey: process.env.INQUIR_API_KEY });
177
- const response = await invokeRemote(client, 'hello', { name: 'World' });
178
- ```
179
-
180
- ## Readonly published-release commands (scoped rollout)
181
-
182
- `prepareReleaseCommand` retains the caller's original UUID, workspace and command
183
- before the first HTTP submission. It uses the dedicated readonly release API;
184
- it does not invoke the writable source-command API or a function handler.
185
-
186
- ```ts
187
- import { randomUUID } from 'node:crypto';
188
- import { prepareReleaseCommand, restoreReleaseCommand } from '@inquir/compute';
189
-
190
- const operation = await prepareReleaseCommand(client, {
191
- functionId,
192
- requestId: randomUUID(),
193
- input: { command: 'cat package.json', timeoutMs: 60_000 },
194
- retain: async record => saveCommandRecord(record), // Durable caller storage.
195
- });
196
- const first = await operation.submit(); // Submit once. Keep the retained record on a lost reply.
197
- const outcome = first.state === 'PENDING' || first.state === 'RECOVERY_HELD'
198
- ? await restoreReleaseCommand(client, operation.record()).observe()
199
- : first;
200
- ```
201
-
202
- Observation resumes the same ID and never repeats the initial POST. An expired
203
- or aborted observer keeps that handle; `operation.cancel()` separately requests
204
- durable cancellation. Cancellation acknowledgement is an intent, while `REMOVED`
205
- is a validated terminal result after physical task/container cleanup. Scope or
206
- artifact uncertainty remains held rather than becoming a fabricated failure.
207
- `CANCELLED_WITHOUT_CONTAINER` separately proves an admitted reservation was
208
- cancelled and physically retired before CREATE; its `result` is `null` because
209
- no shell ran. `CANCELLED_BEFORE_ADMISSION` has no reservation to retire.
210
-
211
- This path is disabled by default. Each exact runtime image, readonly process,
212
- source/input mount, resource and network profile must receive native
213
- qualification before its scoped operator approval and rollout switches enable
214
- admission. The initial source tests do not constitute that qualification.
215
-
216
- ## Application observability
217
-
218
- The SDK exposes resource and HTTP history, persisted runtime/HTTP logs and configurable app alerts. Every helper uses the client's credentials and workspace selector:
219
-
220
- ```ts
221
- import {
222
- createClient, getAppMetrics, getAppHttpMetrics, getAppLogHistory,
223
- createAppAlertRule, updateAppAlertRule, listAppAlertIncidents,
224
- } from '@inquir/compute';
225
-
226
- const client = createClient({
227
- baseUrl: 'https://inquir.org/api',
228
- apiKey: process.env.INQUIR_TOKEN,
229
- workspaceId: process.env.INQUIR_WORKSPACE,
230
- });
231
- const to = new Date().toISOString();
232
- const from = new Date(Date.now() - 60 * 60 * 1000).toISOString();
233
- const resources = await getAppMetrics(client, appId, { from, to });
234
- const http = await getAppHttpMetrics(client, appId, { from, to });
235
- const logs = await getAppLogHistory(client, appId, {
236
- from, to, releaseId, allocationId, allocationFence: 4, level: 'error', limit: 100,
237
- });
238
- if (logs.nextCursor) {
239
- const older = await getAppLogHistory(client, appId, {
240
- from: logs.from, to: logs.to, releaseId, allocationId, allocationFence: 4,
241
- level: 'error', limit: 100, cursor: logs.nextCursor,
242
- });
243
- }
244
-
245
- const { rule } = await createAppAlertRule(client, appId, {
246
- name: 'High RAM', enabled: true, metric: 'memory_percent',
247
- threshold: 85, forSeconds: 120, recoveryThreshold: 65,
248
- recoveryForSeconds: 60, noDataSeconds: 180, noData: 'alert', channels: [],
249
- });
250
- await updateAppAlertRule(client, appId, rule.id, { threshold: 90 });
251
- const { incidents } = await listAppAlertIncidents(client, appId, { limit: 25 });
252
- ```
253
-
254
- `getAppHttpLogHistory`, `listAppAlertRules` and `deleteAppAlertRule` complete the API. Deletion disables the rule and resolves its active incidents; retained history is still available. Omit `channels` in a patch to preserve existing destinations, or pass an empty array to remove them. `webhook`/`slack` channel URLs are write-only; response types expose only channel id, type and configured status. Never log alert request inputs containing a secret URL.
255
-
256
- Resource and HTTP retention is 30 days; runtime logs are retained for 7 days. Runtime history is attributed to the exact release, allocation/fence, runner/epoch and container, including replaced containers. Paging is newest-first and requires an unchanged date window and filters. Resource history retains unavailable values as `null`; use `status`, `latestSampleAt` and `truncated` to detect stale or incomplete results. Network and block I/O rates are measured separately; block I/O is not volume space usage. CPU/RAM alert percentages are relative to enforced limits, and the continuous maximum across current production replicas determines threshold firing.
1
+ # @inquir/compute
2
+
3
+ Workspace-first SDK for defining, discovering, generating, and running Inquir Compute assets.
4
+
5
+ ## Install
6
+
7
+ Requires Node.js 22 or newer. Version 0.3 groups only long-lived container applications in projects; functions and pipelines remain separate assets.
8
+
9
+ ```bash
10
+ npm install @inquir/compute zod@^3.23.8
11
+ ```
12
+
13
+ > Release order: publish this SDK before the CLI that depends on it. CLI 0.4.0
14
+ > needs SDK 0.4.0 (`isPromoteAccepted`, `getApplicationRollout`); CLI 0.4.1
15
+ > needs SDK 0.4.2 (`listAppInstances`, `matchAppInstance`, `createSourceArchive`,
16
+ > `manifestBuildContext`, `getProductionReplacement`); the CLI after 0.4.1 needs SDK 0.4.3
17
+ > (`addApplicationDomain(..., { acknowledgeWildcardRisks })`);
18
+ > see `packages/cli/README.md`, "Publishing to npm".
19
+
20
+ ## What It Includes
21
+
22
+ - `defineFunction()` and `definePipeline()` for code-first assets.
23
+ - `loadConfig()` for `inquir.config.json`.
24
+ - `discoverAssets()` for workspace asset scanning.
25
+ - `generateCodegenArtifacts()` / `generateAndWriteCodegenArtifacts()` for typed client + `registry.json`.
26
+ - `runLocally()` for local function execution/debug harness.
27
+ - HTTP transport helpers (`createClient`, `invokeRemote`, deploy/list helpers).
28
+ - Application project helpers (`listProjects`, `createProject`, `getProject`, `addProjectResource`) group long-lived containers: web services, APIs, databases, and workers. Project members use `kind: 'application'`; serverless functions are managed separately. `createApplication` accepts `projectId` (ID or slug); omitting it creates a project named after the application. Archiving a project also archives all its applications, including an application shared with another project. Removing a membership leaves it running. Project lifecycle actions require `projects:write` and `apps:promote`.
29
+
30
+ ## Pending synchronous invocations
31
+
32
+ When the server admits a finite cold invocation, `invokeRemote()` submits once
33
+ and follows only the authenticated invocation's `/resume` endpoint. Hot
34
+ invocations keep their existing path. Save each handle from `onPending` if the
35
+ caller must survive a restart:
36
+
37
+ ```ts
38
+ import { invokeRemote, resumeRemoteInvocation, cancelRemoteInvocation,
39
+ RemoteInvocationObservationEndedError } from '@inquir/compute';
40
+
41
+ try {
42
+ const result = await invokeRemote(client, 'my-function', event, {
43
+ timeoutMs: 120_000, signal, onPending: saveHandle,
44
+ });
45
+ } catch (error) {
46
+ if (error instanceof RemoteInvocationObservationEndedError) {
47
+ saveHandle(error.pending); // PENDING/RECOVERY_HELD is not FAILED.
48
+ // Later: await resumeRemoteInvocation(client, error.pending);
49
+ // Explicit intent: await cancelRemoteInvocation(client, error.pending);
50
+ } else throw error;
51
+ }
52
+ ```
53
+
54
+ Abort/timeout stops observation, not execution. A cancellation receipt records
55
+ intent, not a terminal outcome. If the initial POST reply is lost before a handle
56
+ arrives, this adapter cannot recover an unknown server ID and never repeats that
57
+ POST. Server admission remains independently gated; this client does not enable
58
+ gateway, pipeline, durable or streaming finite execution.
59
+
60
+ ## Durable source commands
61
+
62
+ Source execution uses an explicit operation instead of synchronous writable
63
+ `/exec`. Save a lowercase UUID v4 before preparing, and atomically retain every
64
+ record supplied by the callback in private storage. Records contain command/env
65
+ secrets; they contain a credential hash rather than the credential itself.
66
+
67
+ ```ts
68
+ import { createClient, prepareSourceCommand, restoreSourceCommand } from '@inquir/compute';
69
+
70
+ const client = createClient({ baseUrl: apiUrl, apiKey, workspaceId });
71
+ const operation = await prepareSourceCommand(client, {
72
+ function: 'my-function', requestId: savedUuid,
73
+ input: { command: 'npm install', timeoutMs: 60_000 },
74
+ retain: savePrivateRecordAtomically,
75
+ });
76
+ await operation.submit(); // no automatic POST retry
77
+ const state = await operation.status(); // GET only
78
+ const publication = await operation.reconcile(); // explicit source publication
79
+ // await operation.cancel(); // explicit cancellation intent
80
+ const restored = await restoreSourceCommand(client, savedRecord, {
81
+ retain: savePrivateRecordAtomically,
82
+ }); // no HTTP or submission
83
+ ```
84
+
85
+ The retained API, credential hash, original workspace selector and resolved
86
+ workspace/function IDs must match on restore. Later actions stay pinned to those
87
+ IDs even if the client changes. A lost response keeps the same operation UUID;
88
+ inspect status or explicitly retry the same action. Poll timeout/abort stops
89
+ observation and does not cancel or publish. `EXITED` and `CANCEL_REQUESTED` do not
90
+ prove physical removal or capacity release. A GET with `publication: null` does
91
+ not prove that an earlier explicit publication never happened.
92
+
93
+ Acceptance identity is retained before an action returns and after each poll
94
+ response. If storage fails, further HTTP is blocked until the pending record is
95
+ saved; `operation.persist()` retries storage without HTTP. Keep the updated
96
+ record returned by `record()` for reloads. The server must enable the source
97
+ command API after its activation gates; a 503 never falls back to writable exec.
98
+
99
+ `result.logFailure` and `sourcePublicationState(outcome) === 'CAPTURE_FAILED'`
100
+ mark unavailable log capture, preserving the real termination and exit code.
101
+ Blank Base64 fields then mean unavailable capture rather than successful empty
102
+ logs; workspace output is absent and cannot be published.
103
+
104
+ ## Concepts
105
+
106
+ - **Function**: a single compute unit with typed input/output (Zod schemas). Created with `defineFunction()`. Discovered by file pattern, run locally via `runLocally()`, deployed and invoked remotely via the transport helpers.
107
+ - **Pipeline**: a declarative DAG of functions created with `definePipeline()`. Included in the codegen registry; orchestration is handled server-side.
108
+ - **Client**: the generated `client.ts` (from `generateAndWriteCodegenArtifacts()`). Provides a type-safe `createClient()` factory that wraps `invokeRemote()` with your asset's I/O types.
109
+ - **Source of truth**: local files on disk. `registry.json` is derived — rebuild it with `generateAndWriteCodegenArtifacts()` whenever sources change.
110
+ - **Local ↔ remote link**: remote functions store `linkedLocalName` (local asset name) and `sourceHash`. Compare hashes to detect drift.
111
+
112
+ ## Quick Example
113
+
114
+ ```ts
115
+ import { defineFunction } from '@inquir/compute';
116
+ import { z } from 'zod';
117
+
118
+ export const hello = defineFunction(
119
+ {
120
+ name: 'hello',
121
+ input: z.object({ name: z.string().optional() }),
122
+ output: z.object({ message: z.string() }),
123
+ },
124
+ async (input) => ({ message: `Hello, ${input.name ?? 'world'}` })
125
+ );
126
+ ```
127
+
128
+ ## Minimal Workspace Config
129
+
130
+ Create `inquir.config.json`:
131
+
132
+ ```json
133
+ {
134
+ "functionsDir": "inquir/functions",
135
+ "pipelinesDir": "inquir/pipelines",
136
+ "generatedDir": ".inquir/generated"
137
+ }
138
+ ```
139
+
140
+ ## Discovery + Codegen
141
+
142
+ ```ts
143
+ import { discoverAssets, generateAndWriteCodegenArtifacts, loadConfig } from '@inquir/compute';
144
+
145
+ const config = loadConfig(process.cwd());
146
+ const registry = await discoverAssets(config);
147
+ await generateAndWriteCodegenArtifacts(config.absoluteGeneratedDir, registry);
148
+ ```
149
+
150
+ This generates:
151
+
152
+ - `.inquir/generated/client.ts`
153
+ - `.inquir/generated/registry.json`
154
+
155
+ `registry.json` includes stable metadata such as `name`, `kind`, `sourceHash`, and `assetRoot`.
156
+
157
+ ## Local Run
158
+
159
+ ```ts
160
+ import { runLocally } from '@inquir/compute';
161
+
162
+ const result = await runLocally(
163
+ {
164
+ kind: 'function',
165
+ name: 'hello',
166
+ filePath: '/abs/path/inquir/functions/hello/index.ts',
167
+ exportName: 'hello',
168
+ },
169
+ { name: 'World' }
170
+ );
171
+ ```
172
+
173
+ ## Remote Invoke (Transport)
174
+
175
+ ```ts
176
+ import { createClient, invokeRemote } from '@inquir/compute';
177
+
178
+ const client = createClient({ baseUrl: 'http://localhost:3000', apiKey: process.env.INQUIR_API_KEY });
179
+ const response = await invokeRemote(client, 'hello', { name: 'World' });
180
+ ```
181
+
182
+ ## Readonly published-release commands (scoped rollout)
183
+
184
+ `prepareReleaseCommand` retains the caller's original UUID, workspace and command
185
+ before the first HTTP submission. It uses the dedicated readonly release API;
186
+ it does not invoke the writable source-command API or a function handler.
187
+
188
+ ```ts
189
+ import { randomUUID } from 'node:crypto';
190
+ import { prepareReleaseCommand, restoreReleaseCommand } from '@inquir/compute';
191
+
192
+ const operation = await prepareReleaseCommand(client, {
193
+ functionId,
194
+ requestId: randomUUID(),
195
+ input: { command: 'cat package.json', timeoutMs: 60_000 },
196
+ retain: async record => saveCommandRecord(record), // Durable caller storage.
197
+ });
198
+ const first = await operation.submit(); // Submit once. Keep the retained record on a lost reply.
199
+ const outcome = first.state === 'PENDING' || first.state === 'RECOVERY_HELD'
200
+ ? await restoreReleaseCommand(client, operation.record()).observe()
201
+ : first;
202
+ ```
203
+
204
+ Observation resumes the same ID and never repeats the initial POST. An expired
205
+ or aborted observer keeps that handle; `operation.cancel()` separately requests
206
+ durable cancellation. Cancellation acknowledgement is an intent, while `REMOVED`
207
+ is a validated terminal result after physical task/container cleanup. Scope or
208
+ artifact uncertainty remains held rather than becoming a fabricated failure.
209
+ `CANCELLED_WITHOUT_CONTAINER` separately proves an admitted reservation was
210
+ cancelled and physically retired before CREATE; its `result` is `null` because
211
+ no shell ran. `CANCELLED_BEFORE_ADMISSION` has no reservation to retire.
212
+
213
+ This path is disabled by default. Each exact runtime image, readonly process,
214
+ source/input mount, resource and network profile must receive native
215
+ qualification before its scoped operator approval and rollout switches enable
216
+ admission. The initial source tests do not constitute that qualification.
217
+
218
+ ## Application observability
219
+
220
+ The SDK exposes resource and HTTP history, persisted runtime/HTTP logs and configurable app alerts. Every helper uses the client's credentials and workspace selector:
221
+
222
+ ```ts
223
+ import {
224
+ createClient, getAppMetrics, getAppHttpMetrics, getAppLogHistory,
225
+ createAppAlertRule, updateAppAlertRule, listAppAlertIncidents,
226
+ } from '@inquir/compute';
227
+
228
+ const client = createClient({
229
+ baseUrl: 'https://inquir.org/api',
230
+ apiKey: process.env.INQUIR_TOKEN,
231
+ workspaceId: process.env.INQUIR_WORKSPACE,
232
+ });
233
+ const to = new Date().toISOString();
234
+ const from = new Date(Date.now() - 60 * 60 * 1000).toISOString();
235
+ const resources = await getAppMetrics(client, appId, { from, to });
236
+ const http = await getAppHttpMetrics(client, appId, { from, to });
237
+ const logs = await getAppLogHistory(client, appId, {
238
+ from, to, releaseId, allocationId, allocationFence: 4, level: 'error', limit: 100,
239
+ });
240
+ if (logs.nextCursor) {
241
+ const older = await getAppLogHistory(client, appId, {
242
+ from: logs.from, to: logs.to, releaseId, allocationId, allocationFence: 4,
243
+ level: 'error', limit: 100, cursor: logs.nextCursor,
244
+ });
245
+ }
246
+
247
+ const { rule } = await createAppAlertRule(client, appId, {
248
+ name: 'High RAM', enabled: true, metric: 'memory_percent',
249
+ threshold: 85, forSeconds: 120, recoveryThreshold: 65,
250
+ recoveryForSeconds: 60, noDataSeconds: 180, noData: 'alert', channels: [],
251
+ });
252
+ await updateAppAlertRule(client, appId, rule.id, { threshold: 90 });
253
+ const { incidents } = await listAppAlertIncidents(client, appId, { limit: 25 });
254
+ ```
255
+
256
+ `getAppHttpLogHistory`, `listAppAlertRules` and `deleteAppAlertRule` complete the API. Deletion disables the rule and resolves its active incidents; retained history is still available. Omit `channels` in a patch to preserve existing destinations, or pass an empty array to remove them. `webhook`/`slack` channel URLs are write-only; response types expose only channel id, type and configured status. Never log alert request inputs containing a secret URL.
257
+
258
+ Resource and HTTP retention is 30 days; runtime logs are retained for 7 days. Runtime history is attributed to the exact release, allocation/fence, runner/epoch and container, including replaced containers. Paging is newest-first and requires an unchanged date window and filters. Resource history retains unavailable values as `null`; use `status`, `latestSampleAt` and `truncated` to detect stale or incomplete results. Network and block I/O rates are measured separately; block I/O is not volume space usage. CPU/RAM alert percentages are relative to enforced limits, and the continuous maximum across current production replicas determines threshold firing.
package/dist/apps.d.ts CHANGED
@@ -394,6 +394,19 @@ export interface CreateReleaseResponse {
394
394
  previewUrl?: string;
395
395
  /** True when the release will take production by itself once healthy. */
396
396
  promoteOnReady?: boolean;
397
+ /**
398
+ * What the release took from inquir.yaml `services.web.env` (key names only):
399
+ * `applied` reached the release, `overridden` lost to an application variable
400
+ * of the same name, `ignored` means the platform has APPS_MANIFEST_ENV off.
401
+ * Absent when the build manifest declares no env, or from an older server.
402
+ */
403
+ manifestEnv?: {
404
+ applied: string[];
405
+ overridden: string[];
406
+ ignored?: true;
407
+ };
408
+ /** Advisory: e.g. a manifest env section that was not applied. */
409
+ warnings?: string[];
397
410
  statusUrl: string;
398
411
  logsUrl: string;
399
412
  }
@@ -582,6 +595,27 @@ export declare function listReleases(client: TransportClient, applicationId: str
582
595
  items: Release[];
583
596
  }>;
584
597
  export declare function getRelease(client: TransportClient, releaseId: string): Promise<Release>;
598
+ /**
599
+ * A stop-first replacement of production (`apps redeploy`, `deploy
600
+ * --promote` of an eligible service): the current replica is stopped before
601
+ * the new release starts, so the new release reads PENDING throughout.
602
+ */
603
+ export interface ProductionReplacementOperation {
604
+ id: string;
605
+ applicationId: string;
606
+ sourceReleaseId: string;
607
+ targetReleaseId: string;
608
+ /** RESERVED, STOPPING_SOURCE, STARTING_TARGET, ..., COMMITTED, ROLLED_BACK, CANCELLED. */
609
+ phase: string;
610
+ targetReplicas?: number;
611
+ createdAt: string;
612
+ phaseStartedAt: string;
613
+ lastError?: string;
614
+ }
615
+ /** The application's active production replacement, if any. */
616
+ export declare function getProductionReplacement(client: TransportClient, applicationId: string): Promise<{
617
+ operation: ProductionReplacementOperation | null;
618
+ }>;
585
619
  export interface PromoteResponse {
586
620
  previousReleaseId?: string;
587
621
  /** Absent for a service with no production hostname (pure TCP, or no suffix). */
@@ -686,7 +720,8 @@ export interface DomainVerificationInstructions {
686
720
  export interface AppDomain {
687
721
  hostname: string;
688
722
  kind: 'production' | 'preview';
689
- url: string;
723
+ /** `https://{hostname}`; absent for a wildcard, which is a pattern, not an address. */
724
+ url?: string;
690
725
  activeReleaseId?: string;
691
726
  /** True for a tenant-bound hostname; false for the platform-generated one. */
692
727
  custom: boolean;
@@ -695,13 +730,25 @@ export interface AppDomain {
695
730
  verifiedAt?: string;
696
731
  /** Present while unverified. */
697
732
  verification?: DomainVerificationInstructions;
733
+ /**
734
+ * `*.example.com`: every one-label host under example.com (advanced, not
735
+ * recommended; the server needs APPS_WILDCARD_DOMAINS=on).
736
+ */
737
+ wildcard?: boolean;
738
+ /** Wildcards only: new certificates approved in the last 7 days, of the weekly limit. */
739
+ certificates?: {
740
+ issuedThisWeek: number;
741
+ weeklyLimit: number;
742
+ };
698
743
  }
699
744
  /**
700
745
  * Bind a custom hostname (always production). It starts unverified: publish
701
746
  * the returned TXT record and point the name at the platform, then call
702
747
  * `verifyApplicationDomain`.
703
748
  */
704
- export declare function addApplicationDomain(client: TransportClient, applicationId: string, hostname: string): Promise<AppDomain & {
749
+ export declare function addApplicationDomain(client: TransportClient, applicationId: string, hostname: string, options?: {
750
+ acknowledgeWildcardRisks?: boolean;
751
+ }): Promise<AppDomain & {
705
752
  verification: DomainVerificationInstructions;
706
753
  }>;
707
754
  export declare function verifyApplicationDomain(client: TransportClient, applicationId: string, hostname: string): Promise<AppDomain>;