@deepdockai/cli 1.0.4 → 1.0.6
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/CHANGELOG.md +12 -1
- package/dist/deepdock.js +1 -1
- package/package.json +2 -1
- package/skill/SKILL.md +6 -2
- package/skill/references/auth-and-keys.md +7 -3
- package/skill/references/calling-actions.md +6 -27
- package/skill/references/cli.md +1 -1
- package/skill/references/clients/deepdock.py +5 -2
- package/skill/references/clients/deepdock.ts +51 -22
- package/skill/references/concepts.md +4 -3
- package/skill/references/discovery.md +3 -2
- package/skill/references/errors.md +6 -6
- package/skill/references/example-integration.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.6
|
|
4
|
+
|
|
5
|
+
- The agent skill's TypeScript client types a device's `hostname`, `os` and `clientVersion` as `string | null`, as the API answers them before the device has reported them. The skill names `GET /v1/devices` and `GET /v1/devices/{deviceId}`.
|
|
6
|
+
|
|
7
|
+
## 1.0.5
|
|
8
|
+
|
|
9
|
+
- The agent skill's clients: `systems()` is typed as the list of systems the API answers, and a paired `Device` lists its systems as objects, with `outdated` and a `claimedAt` that can be `null`.
|
|
10
|
+
- The TypeScript client runs with plain `node file.ts` (Node 22.18+, or 22.13+ with `--experimental-strip-types`).
|
|
11
|
+
- Both clients take their base URL from `DEEPDOCK_BASE_URL` when it is set, like the CLI.
|
|
12
|
+
- The agent skill holds nothing specific to one system: the recipe for one system is gone, and the skill points to the system's behaviour notes in its OpenAPI document instead.
|
|
13
|
+
|
|
3
14
|
## 1.0.4
|
|
4
15
|
|
|
5
16
|
- `DEEPDOCK_CONFIG_DIR` moves the settings file (and only it) to a directory of your choice, on every platform; it must be an absolute path. Useful for keeping separate sign-ins apart, for example one per project or account.
|
|
@@ -21,7 +32,7 @@
|
|
|
21
32
|
## 1.0.1
|
|
22
33
|
|
|
23
34
|
- The agent skill: page with `nextCursor` until it is `null` (a short page can carry a cursor), and cover the codes `access_outdated`, `access_missing`, `record_locked`, `record_not_editable`, `unknown_provider`, `payload_too_large` and the system status `unknown`, in the skill and the CLI's exit-code table.
|
|
24
|
-
- The agent skill: one statement on how to fetch the OpenAPI document, and a
|
|
35
|
+
- The agent skill: one statement on how to fetch the OpenAPI document, and a recipe for one system with its action names.
|
|
25
36
|
- The agent skill states the id rule in full: reads and creates answer ids as integers, a create's new id included; every id you send is a string of digits without a leading zero, at most 10 characters, in a query string and a write body alike.
|
|
26
37
|
|
|
27
38
|
## 1.0.0
|
package/dist/deepdock.js
CHANGED
|
@@ -11424,7 +11424,7 @@ import { spawn } from "node:child_process";
|
|
|
11424
11424
|
import { homedir, hostname } from "node:os";
|
|
11425
11425
|
|
|
11426
11426
|
// src/version.ts
|
|
11427
|
-
var VERSION = "1.0.
|
|
11427
|
+
var VERSION = "1.0.6";
|
|
11428
11428
|
|
|
11429
11429
|
// src/runtime.ts
|
|
11430
11430
|
var DEFAULT_BASE_URL = "https://api.deepdock.ai";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepdockai/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Deepdock partner CLI: pair devices, see systems and actions, create the API key your code runs them with.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"lint:fix": "biome lint --write .",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
33
|
"gen:codes": "bun scripts/genCodes.ts",
|
|
34
|
+
"gen:devices": "bun scripts/genDevices.ts",
|
|
34
35
|
"check:public": "bun scripts/checkPublic.ts --pack",
|
|
35
36
|
"test": "bun run build && bun --bun vitest run",
|
|
36
37
|
"test:node": "vitest run",
|
package/skill/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: deepdock-integration
|
|
3
|
-
description: Build, debug or test an integration with Deepdock's partner API, the REST actions under /v1 that read and write records in a customer's on-premise business system
|
|
3
|
+
description: Build, debug or test an integration with Deepdock's partner API, the REST actions under /v1 that read and write records in a customer's on-premise business system through a paired Deepdock device. Use when code calls api.deepdock.ai, handles ddk_ API keys, pairs a device by code, calls a system action, handles error codes such as device_offline, action_paused, idempotency_conflict or outcome_unknown, or runs the deepdock CLI.
|
|
4
4
|
metadata:
|
|
5
5
|
cli: "@deepdockai/cli"
|
|
6
6
|
---
|
|
@@ -58,7 +58,8 @@ and what your code does with the result.
|
|
|
58
58
|
dashboard. The CLI receives the key once, writes `DEEPDOCK_API_KEY=<key>` into that file (it
|
|
59
59
|
must be ignored by git) and keeps no copy. It is displayed to you or to the human by nobody.
|
|
60
60
|
You work only with the key's id, prefix and the file name. Never ask for, read, print or paste
|
|
61
|
-
a key value.
|
|
61
|
+
a key value. If the CLI runs with `DEEPDOCK_BASE_URL` set (a non-production API), add the same
|
|
62
|
+
`DEEPDOCK_BASE_URL=<url>` line to that file ([auth-and-keys.md](references/auth-and-keys.md)).
|
|
62
63
|
6. **Test it.** Run your own code against the demo device the human named, with the key from
|
|
63
64
|
`.env.local`, and read back with the read action. The CLI runs no action. Read
|
|
64
65
|
[references/testing.md](references/testing.md) before any write.
|
|
@@ -67,6 +68,9 @@ and what your code does with the result.
|
|
|
67
68
|
|
|
68
69
|
## Rules that are not in the OpenAPI document
|
|
69
70
|
|
|
71
|
+
- **Read the system's behaviour notes first.** Before the first write, read the system's behaviour
|
|
72
|
+
notes in its OpenAPI document (each action's `description`): what it needs (an author, an open
|
|
73
|
+
case), what it accepts and what cannot be undone. Nothing system-specific lives in this skill.
|
|
70
74
|
- **Discover, never hard-code.** Field names, patterns, enums and lengths come from the live OpenAPI
|
|
71
75
|
document (`deepdock systems openapi <system>`, or `GET /v1/systems/{system}/openapi.json` with
|
|
72
76
|
your key), never from memory or from this skill. The document lists Deepdock's whole catalogue for the system; your organisation's assigned
|
|
@@ -34,6 +34,9 @@ in the dashboard" and stop retrying.
|
|
|
34
34
|
- Read it from the environment or a secret store (`DEEPDOCK_API_KEY` is the convention the
|
|
35
35
|
dashboard's examples use). Never a literal in source, a test fixture, a log or a URL.
|
|
36
36
|
- One key per deployment is enough; there is no per-device key.
|
|
37
|
+
- A key works only against the API it was created on. The CLI uses production unless
|
|
38
|
+
`DEEPDOCK_BASE_URL` is set; when it is (`deepdock auth status --json` shows it as `baseUrl`), put
|
|
39
|
+
the same `DEEPDOCK_BASE_URL=<url>` line next to the key. The bundled clients read it too.
|
|
37
40
|
- Getting a key into the app is the CLI's job: `deepdock keys create <name> --expires <d>
|
|
38
41
|
--write-env <file>` opens a link to the dashboard; a human clicks Approve (or Deny); the CLI
|
|
39
42
|
receives the key once and writes `DEEPDOCK_API_KEY=<key>` into the file, which must be ignored
|
|
@@ -81,9 +84,10 @@ curl -X POST "$BASE/v1/devices" \
|
|
|
81
84
|
```
|
|
82
85
|
|
|
83
86
|
`label` is optional, 1 to 100 characters. The device must be online. Answer: `201` with the device
|
|
84
|
-
(`id`, `label`, `hostname`, `os`, `clientVersion`, `status`, `lastSeenAt`, `claimedAt`, `systems`,
|
|
85
|
-
and `updateAvailable` when it runs an older build)
|
|
86
|
-
|
|
87
|
+
(`id`, `label`, `hostname`, `os`, `clientVersion`, `status`, `lastSeenAt`, `claimedAt`, `outdated`, `systems`,
|
|
88
|
+
and `updateAvailable` when it runs an older build); `hostname`, `os` and `clientVersion` are `null`
|
|
89
|
+
until the device has reported them. Keep `id`: it is the `deviceId` of every later call.
|
|
90
|
+
`GET /v1/devices` lists your devices and `GET /v1/devices/{deviceId}` reads one, in the same shape.
|
|
87
91
|
|
|
88
92
|
4. Release with `DELETE /v1/devices/{deviceId}`; the device then shows a new code.
|
|
89
93
|
|
|
@@ -71,35 +71,14 @@ of a customer, the note id of an update. The pattern is always the same:
|
|
|
71
71
|
|
|
72
72
|
Never invent an id, copy one from another device, or hard-code one from a test run.
|
|
73
73
|
|
|
74
|
-
##
|
|
75
|
-
|
|
76
|
-
The examples above use the placeholder system `example-erp`. For a practice running MEDICAL OFFICE
|
|
77
|
-
the same patterns use real actions (the live OpenAPI document is still the authority for fields):
|
|
78
|
-
|
|
79
|
-
1. `readUsers` lists the users who can author an entry; pass one id as `authorUserId`.
|
|
80
|
-
2. `searchPatients?lastName=...` finds a patient: a prefix match with umlauts spelled out
|
|
81
|
-
(`Müller` and `Mueller` match, `Muller` does not), answering `patientId`, `lastName`,
|
|
82
|
-
`firstName`, `birthDate` (`yyyy-mm-dd`, `null` when the system holds no readable date).
|
|
83
|
-
3. `readCases` lists the patient's cases; a write that needs an open case (`createDiagnosis` and
|
|
84
|
-
the note actions) refuses with `422 no_open_case` when there is none, or takes a `caseId`.
|
|
85
|
-
`readProviders` lists the providers for `providerId` (`422 unknown_provider` otherwise).
|
|
86
|
-
4. Write with `createDiagnosis` (and the other create actions) under a fresh `Idempotency-Key`,
|
|
87
|
-
then read back.
|
|
88
|
-
|
|
89
|
-
Things the schemas show but are easy to get wrong:
|
|
74
|
+
## Easy to get wrong
|
|
90
75
|
|
|
91
76
|
- **Ids come back as integers and go in as strings.** A read and a create answer ids as integers
|
|
92
|
-
(a create's new
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
regret stays until someone removes it in MEDICAL OFFICE itself. Write only what you mean to keep.
|
|
98
|
-
- **Mark every test text `TEST-DEEPDOCK-<id>`** (a diagnosis text, a note), on the demo device
|
|
99
|
-
only, and list what you created ([testing.md](testing.md)).
|
|
100
|
-
- Text fields accept MEDICAL OFFICE's Windows-1252 repertoire only; anything else (a tab or line
|
|
101
|
-
break outside a note) answers `422 invalid_input` before a write. Note actions take a `format` of `text` (default, stored as
|
|
102
|
-
typed) or `markdown`.
|
|
77
|
+
(a create's new id too); only an update echoes the key you sent, as the string you sent.
|
|
78
|
+
`customerId: 1001` in a search result goes back as `customerId=1001` in a read's query string and
|
|
79
|
+
as `"customerId": "1001"` in a write body: digits only, no leading zero, at most 10 characters.
|
|
80
|
+
- **Mark every test text `TEST-DEEPDOCK-<id>`** on the demo device only, and list what you created
|
|
81
|
+
([testing.md](testing.md)).
|
|
103
82
|
- A read action's query string treats `+` as a space: send a literal `+` as `%2B`.
|
|
104
83
|
|
|
105
84
|
## Timeouts and latency
|
package/skill/references/cli.md
CHANGED
|
@@ -84,7 +84,7 @@ A failure is one document on stderr: the API's error object unchanged plus what
|
|
|
84
84
|
| 0 | done | |
|
|
85
85
|
| 1 | something on the command line to fix | `invalid_usage` (a bad flag, an unknown slug, a question with no terminal and no `--yes`, an env file git does not ignore), `org_required` |
|
|
86
86
|
| 2 | the API refused the request; do not repeat it unchanged | `invalid_request`, `not_found`, `device_not_found`, `pairing_code_invalid`, `other_partner_build`, `rate_limited`, `forbidden`, `conflict` and the other refusals (not `access_outdated` and `access_missing`, see exit 3) |
|
|
87
|
-
| 3 | retry later (`access_outdated` and `access_missing`: after the
|
|
87
|
+
| 3 | retry later (`access_outdated` and `access_missing`: after the customer repeats "Erneut einrichten" in the Deepdock client) | `device_offline`, `action_paused`, `access_outdated`, `access_missing`, `system_unavailable`, `target_busy`, `temporarily_unavailable`, `internal_error`, `connection_lost` |
|
|
88
88
|
| 4 | the CLI is too old for the API: run `npx @deepdockai/cli@latest` | `client_outdated` |
|
|
89
89
|
| 5 | sign-in needed: run `deepdock auth login` | `sign_in_needed`, `approval_denied`, `approval_expired` |
|
|
90
90
|
|
|
@@ -7,6 +7,7 @@ Everything action-specific (paths, fields, methods) comes from the live OpenAPI
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import os
|
|
10
11
|
from dataclasses import dataclass, field, replace
|
|
11
12
|
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
|
12
13
|
from urllib.parse import quote
|
|
@@ -43,9 +44,11 @@ class WriteResult:
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
class Deepdock:
|
|
46
|
-
def __init__(self, api_key: str, base: str =
|
|
47
|
+
def __init__(self, api_key: str, base: Optional[str] = None, timeout: float = 30.0,
|
|
47
48
|
client: Optional[httpx.Client] = None):
|
|
48
49
|
# 30 s is above Deepdock's 20 s device wait, so device_offline arrives before a client timeout.
|
|
50
|
+
# Default: DEEPDOCK_BASE_URL from the environment (the CLI's own variable), else production.
|
|
51
|
+
base = base or os.environ.get("DEEPDOCK_BASE_URL") or "https://api.deepdock.ai"
|
|
49
52
|
self.c = client or httpx.Client(base_url=base.rstrip("/"), timeout=timeout)
|
|
50
53
|
self.c.headers["Authorization"] = f"Bearer {api_key}"
|
|
51
54
|
|
|
@@ -98,7 +101,7 @@ class Deepdock:
|
|
|
98
101
|
headers={"Idempotency-Key": idempotency_key})
|
|
99
102
|
return WriteResult(data=data, replayed=headers.get("Idempotent-Replayed") == "true")
|
|
100
103
|
|
|
101
|
-
def systems(self, device_id: str) -> Dict[str, Any]:
|
|
104
|
+
def systems(self, device_id: str) -> List[Dict[str, Any]]:
|
|
102
105
|
"""The device's systems and each action's state. A live call: can answer device_offline."""
|
|
103
106
|
data, _ = self._send("GET", f"/v1/devices/{quote(device_id, safe='')}/systems")
|
|
104
107
|
return data
|
|
@@ -6,16 +6,21 @@
|
|
|
6
6
|
* Everything action-specific (paths, fields, methods) comes from the live OpenAPI document.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
// Plain fields, no constructor parameter properties: Node runs this file with its default type
|
|
10
|
+
// stripping, which refuses syntax that needs a transform.
|
|
9
11
|
export class DeepdockError extends Error {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
) {
|
|
12
|
+
readonly status: number
|
|
13
|
+
readonly code: string
|
|
14
|
+
readonly requestId?: string
|
|
15
|
+
readonly systems?: string[]
|
|
16
|
+
|
|
17
|
+
constructor(status: number, code: string, message: string, requestId?: string, systems?: string[]) {
|
|
17
18
|
super(message)
|
|
18
19
|
this.name = 'DeepdockError'
|
|
20
|
+
this.status = status
|
|
21
|
+
this.code = code
|
|
22
|
+
this.requestId = requestId
|
|
23
|
+
this.systems = systems
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
|
|
@@ -34,46 +39,69 @@ export type Page<Item> = { items: Item[]; nextCursor: string | null }
|
|
|
34
39
|
export type WriteResult<T> = { data: T; replayed: boolean }
|
|
35
40
|
export type WriteMethod = 'POST' | 'PATCH'
|
|
36
41
|
|
|
42
|
+
export type SystemStatus = 'running' | 'stopped' | 'missing' | 'unknown'
|
|
43
|
+
/** The database access the device last reported; `null` while none is known. */
|
|
44
|
+
export type SystemAccess = 'ok' | 'outdated' | 'missing' | 'not_set_up' | null
|
|
45
|
+
|
|
37
46
|
export type Device = {
|
|
38
47
|
id: string
|
|
39
48
|
label: string | null
|
|
40
|
-
hostname: string
|
|
41
|
-
os: string
|
|
42
|
-
clientVersion: string
|
|
49
|
+
hostname: string | null
|
|
50
|
+
os: string | null
|
|
51
|
+
clientVersion: string | null
|
|
43
52
|
status: 'connected' | 'offline' | 'inactive'
|
|
44
53
|
lastSeenAt: string | null
|
|
45
|
-
claimedAt: string
|
|
46
|
-
|
|
54
|
+
claimedAt: string | null
|
|
55
|
+
/** True when an action assigned to you needs a newer client: that action answers device_outdated. */
|
|
56
|
+
outdated: boolean
|
|
47
57
|
updateAvailable?: string
|
|
58
|
+
systems: { system: string; version: string | null; status: SystemStatus; access: SystemAccess; reportedAt: string }[]
|
|
48
59
|
}
|
|
49
60
|
|
|
61
|
+
export type PairBody = { code: string; label?: string }
|
|
62
|
+
|
|
50
63
|
export type ActionState =
|
|
51
|
-
| { state: 'active' }
|
|
52
|
-
| { state: 'paused'; reason:
|
|
64
|
+
| { action: string; state: 'active' }
|
|
65
|
+
| { action: string; state: 'paused'; reason: 'structure_changed' | 'check_failed'; pausedAt: string }
|
|
53
66
|
|
|
54
67
|
export type DeviceSystem = {
|
|
68
|
+
system: string
|
|
55
69
|
label: string | null
|
|
56
|
-
version
|
|
57
|
-
status:
|
|
58
|
-
|
|
70
|
+
version?: string
|
|
71
|
+
status: SystemStatus
|
|
72
|
+
access: SystemAccess
|
|
73
|
+
actions: ActionState[]
|
|
59
74
|
}
|
|
60
75
|
|
|
61
76
|
export type DeepdockOptions = {
|
|
77
|
+
/** Default: `DEEPDOCK_BASE_URL` from the environment (the CLI's own variable), else production. */
|
|
62
78
|
base?: string
|
|
63
79
|
/** Above Deepdock's 20 s device wait, so you receive device_offline instead of timing out first. */
|
|
64
80
|
timeoutMs?: number
|
|
65
81
|
fetch?: typeof fetch
|
|
66
82
|
}
|
|
67
83
|
|
|
84
|
+
// `process` is absent in a browser, and Deno throws on `process.env` without --allow-env; then the
|
|
85
|
+
// base comes from the options or is production.
|
|
86
|
+
const envBase = (): string | undefined => {
|
|
87
|
+
try {
|
|
88
|
+
return process.env.DEEPDOCK_BASE_URL || undefined
|
|
89
|
+
} catch {
|
|
90
|
+
return undefined
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
68
94
|
export class Deepdock {
|
|
95
|
+
private readonly apiKey: string
|
|
69
96
|
private readonly base: string
|
|
70
97
|
private readonly timeoutMs: number
|
|
71
98
|
private readonly fetchImpl: typeof fetch
|
|
72
99
|
|
|
73
100
|
constructor(
|
|
74
|
-
|
|
75
|
-
{ base = 'https://api.deepdock.ai', timeoutMs = 30_000, fetch: fetchImpl = fetch }: DeepdockOptions = {},
|
|
101
|
+
apiKey: string,
|
|
102
|
+
{ base = envBase() ?? 'https://api.deepdock.ai', timeoutMs = 30_000, fetch: fetchImpl = fetch }: DeepdockOptions = {},
|
|
76
103
|
) {
|
|
104
|
+
this.apiKey = apiKey
|
|
77
105
|
this.base = base.replace(/\/$/, '')
|
|
78
106
|
this.timeoutMs = timeoutMs
|
|
79
107
|
this.fetchImpl = fetchImpl
|
|
@@ -167,17 +195,18 @@ export class Deepdock {
|
|
|
167
195
|
}
|
|
168
196
|
|
|
169
197
|
/** The device's systems and each action's state. A live call to the device: can answer device_offline. */
|
|
170
|
-
async systems(deviceId: string): Promise<
|
|
171
|
-
return (await this.send<
|
|
198
|
+
async systems(deviceId: string): Promise<DeviceSystem[]> {
|
|
199
|
+
return (await this.send<DeviceSystem[]>(`/v1/devices/${encodeURIComponent(deviceId)}/systems`, { method: 'GET' })).data
|
|
172
200
|
}
|
|
173
201
|
|
|
174
202
|
/** Claims a device by the pairing code it shows. Keep the returned `id`. */
|
|
175
203
|
async pair(code: string, label?: string): Promise<Device> {
|
|
204
|
+
const body: PairBody = label === undefined ? { code } : { code, label }
|
|
176
205
|
return (
|
|
177
206
|
await this.send<Device>('/v1/devices', {
|
|
178
207
|
method: 'POST',
|
|
179
208
|
headers: { 'Content-Type': 'application/json' },
|
|
180
|
-
body: JSON.stringify(
|
|
209
|
+
body: JSON.stringify(body),
|
|
181
210
|
})
|
|
182
211
|
).data
|
|
183
212
|
}
|
|
@@ -24,7 +24,8 @@ Details: [auth-and-keys.md](auth-and-keys.md).
|
|
|
24
24
|
The Deepdock client installed on the customer's Windows PC next to their system. It connects
|
|
25
25
|
outwards to Deepdock; nothing in the customer's network is opened to the internet. A device is
|
|
26
26
|
identified by a UUID (`id`), carries a `label`, `hostname`, `os`, `clientVersion`, `status`
|
|
27
|
-
(`connected`, `offline` or `inactive`), `lastSeenAt`, `claimedAt`
|
|
27
|
+
(`connected`, `offline` or `inactive`), `lastSeenAt`, `claimedAt` (may be `null`), `outdated` (an
|
|
28
|
+
assigned action needs a newer client: it answers `device_outdated`) and the `systems` it reported.
|
|
28
29
|
|
|
29
30
|
- **Pair**: claim a device for your organisation with the 6-character code it shows while unpaired.
|
|
30
31
|
- **Release**: `DELETE /v1/devices/{deviceId}` gives the device back; it shows a new code.
|
|
@@ -36,7 +37,7 @@ identified by a UUID (`id`), carries a `label`, `hostname`, `os`, `clientVersion
|
|
|
36
37
|
## System
|
|
37
38
|
|
|
38
39
|
A program on the device that Deepdock has made reachable, named by a slug such as `example-erp`. Each
|
|
39
|
-
has a display `label` (may be `null`), a `version` the device reported, a `status`, and actions.
|
|
40
|
+
has a display `label` (may be `null`), a `version` the device reported (may be absent), a `status`, and actions.
|
|
40
41
|
`GET /v1/devices/{deviceId}/systems` is a live call to the device, so it can answer
|
|
41
42
|
`device_offline`.
|
|
42
43
|
|
|
@@ -51,7 +52,7 @@ has a display `label` (may be `null`), a `version` the device reported, a `statu
|
|
|
51
52
|
| system `status` | `stopped` | the program is installed but not running | calls answer `system_unavailable`; the site starts the program |
|
|
52
53
|
| system `status` | `missing` | the program was not found where the device expects it | calls answer `system_unavailable`; contact the site and Deepdock |
|
|
53
54
|
| system `status` | `unknown` | the device could not tell whether the program runs (its probe failed or is unavailable on that PC) | do not read it as stopped; the device decides when a call arrives |
|
|
54
|
-
| system `access` | `ok`, `outdated`, `missing`, `not_set_up`, or `null` | the database access the device last reported (`null`: none known yet) | `outdated` refuses the actions that need the missing right with `access_outdated`, `missing` refuses every action with `access_missing`; the
|
|
55
|
+
| system `access` | `ok`, `outdated`, `missing`, `not_set_up`, or `null` | the database access the device last reported (`null`: none known yet) | `outdated` refuses the actions that need the missing right with `access_outdated`, `missing` refuses every action with `access_missing`; the customer repeats "Erneut einrichten" in the Deepdock client, the incident closes by itself once `access` is `ok`, nothing to resume |
|
|
55
56
|
| action state | `active` | the action can be called | normal |
|
|
56
57
|
| action state | `paused` | the action is paused with a `reason` and `pausedAt` | calls answer `action_paused`; show the feature as paused, poll `GET .../systems` at a low rate |
|
|
57
58
|
| pause `reason` | `structure_changed` | the system changed in a way the action depends on (for example a vendor update); Deepdock reviews and resumes | wait; nothing to do on your side |
|
|
@@ -67,8 +67,9 @@ action exists before offering it in your UI, or to validate input client-side be
|
|
|
67
67
|
curl "$BASE/v1/devices/$DEVICE/systems" -H "Authorization: Bearer $DEEPDOCK_API_KEY"
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
A live call to the device.
|
|
71
|
-
`
|
|
70
|
+
A live call to the device. A list; each entry has `system`, `label`, `version` (may be absent),
|
|
71
|
+
`status`, `access` and `actions`, each with `action` and `state` (`active`, or `paused` with
|
|
72
|
+
`reason` and `pausedAt`). Answers `503 device_offline` when the device is not
|
|
72
73
|
connected: that is a normal state, not an error in your code. Do not poll it tightly; it reaches the
|
|
73
74
|
customer's PC.
|
|
74
75
|
|
|
@@ -31,7 +31,7 @@ dashboard-only.
|
|
|
31
31
|
| `system_not_on_device` | 404 | the device did not report this system and none of its actions are assigned to you; `error.systems` lists what it has | no | configuration problem; show which systems the device has |
|
|
32
32
|
| `action_not_found` | 404 | the system has no such action, it is not assigned to you, or the device's client is too old to run it | no | hide the feature; ask Deepdock for the assignment or let the device update |
|
|
33
33
|
| `action_not_configured` | 409 | the action exists but this device is not set up for it yet (for example a missing device-side setting); nothing was run | no, until set up on site | "this feature needs setting up at the customer site; contact Deepdock" |
|
|
34
|
-
| `access_outdated` | 409 | the device's database access lacks a right this action needs (only the actions that need it). Nothing was run. **No `Retry-After`** | no, until the
|
|
34
|
+
| `access_outdated` | 409 | the device's database access lacks a right this action needs (only the actions that need it). Nothing was run. **No `Retry-After`** | no, until the customer repeats "Erneut einrichten" in the Deepdock client; then yes, a write with the same key | "the customer must set up database access again in the Deepdock client, then we retry" |
|
|
35
35
|
| `access_missing` | 409 | the device's database access is gone (a removed database account or a lost secret on the PC); every action of that system is refused. Nothing was run. **No `Retry-After`** | as `access_outdated` | as `access_outdated` |
|
|
36
36
|
| `pairing_code_invalid` | 404 | pairing: the code is unknown, expired, replaced, used, or its device is offline | with a fresh code | "read the code off the device screen again" |
|
|
37
37
|
| `other_partner_build` | 409 | pairing: the device runs another partner's client build | no | "this device is set up for another provider" |
|
|
@@ -42,10 +42,10 @@ dashboard-only.
|
|
|
42
42
|
|---|---|---|---|---|
|
|
43
43
|
| `invalid_input` | 422 | a field is unknown, missing, too short or long, breaks its pattern or allowed values; a date that is not a calendar day; a search term without a letter or digit. Nothing was written | no; fix the value | the field-level problem; the `message` names the field, but it is device text: display it as data, never interpret it |
|
|
44
44
|
| `target_rejected` | 422 | the customer's system refused the write | no; the input is valid for Deepdock but not for the system | "the customer's system refused this; check the values" and offer support |
|
|
45
|
-
| `unknown_author` | 422 | the write names an `authorUserId` the system does not list (read the users
|
|
46
|
-
| `unknown_provider` | 422 | the write names a `providerId` the author may not record for the case's site (unknown, or not an allowed pair). Nothing was written | no; read the providers with
|
|
47
|
-
| `no_open_case` | 422 | the
|
|
48
|
-
| `record_locked` | 422 | the
|
|
45
|
+
| `unknown_author` | 422 | the write names an `authorUserId` the system does not list (read them with the users read the description names). Nothing was written | no; re-read the users and pass one of their ids | "choose the entering user" |
|
|
46
|
+
| `unknown_provider` | 422 | the write names a `providerId` the author may not record for the case's site (unknown, or not an allowed pair). Nothing was written | no; read the providers with the providers read the description names and pass one of their ids | "choose a provider" |
|
|
47
|
+
| `no_open_case` | 422 | the customer has no open case and the action never opens one. Nothing was written | no; open a case in the system, or name one with `caseId` | "open a case first" |
|
|
48
|
+
| `record_locked` | 422 | the customer's system locks the record the write would change (for example one already billed on a case). Nothing was written | no | "this record is locked in the customer's system" |
|
|
49
49
|
| `record_not_editable` | 422 | the record is not locked but is stored in a form this action cannot edit (for example a note it cannot add to without rewriting it). Nothing was written | no | "this record cannot be edited through Deepdock" |
|
|
50
50
|
| Other action-specific refusals | 422 | the action's OpenAPI description names each such code and when it fires. Nothing was written | no; fix the reference or input | the missing precondition in the user's words |
|
|
51
51
|
|
|
@@ -107,7 +107,7 @@ const REACTION: Record<string, Reaction> = {
|
|
|
107
107
|
const reactionFor = (code: string): Reaction => REACTION[code] ?? 'stop'
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Action-specific 422 codes beyond the ones above (any the OpenAPI description lists) map to `fix-input`. Add them from the document, not from memory. `wait-setup` means a person at the
|
|
110
|
+
Action-specific 422 codes beyond the ones above (any the OpenAPI description lists) map to `fix-input`. Add them from the document, not from memory. `wait-setup` means a person at the customer must repeat the database access setup ("Erneut einrichten") in the Deepdock client first; there is no `Retry-After`, so do not loop. Check `access` on the system in `GET .../systems` (`ok` again), then retry; a write keeps its key. `retry-same-key` means the
|
|
111
111
|
pending write stays persisted and is retried from that state with backoff, also across process
|
|
112
112
|
restarts; a device can be offline for hours, so the retry loop lives in a job or queue, not in the
|
|
113
113
|
request that started the write.
|
|
@@ -20,7 +20,7 @@ Copy the file into the project; do not reach into the skill directory at runtime
|
|
|
20
20
|
```ts
|
|
21
21
|
import { Deepdock, DeepdockError, DeepdockTransportError, attempt, type PendingWrite } from './deepdock'
|
|
22
22
|
|
|
23
|
-
const dd = new Deepdock(process.env.DEEPDOCK_API_KEY!) // 30 s timeout
|
|
23
|
+
const dd = new Deepdock(process.env.DEEPDOCK_API_KEY!) // base from DEEPDOCK_BASE_URL, else production; 30 s timeout
|
|
24
24
|
const page = await dd.read<Note>(deviceId, 'example-erp', 'searchNotes', { text: 'TEST-DEEPDOCK-' }, { limit: 50 })
|
|
25
25
|
for await (const p of dd.readAll<Note>(deviceId, 'example-erp', 'searchNotes', { text: 'TEST-DEEPDOCK-' })) { /* every page */ }
|
|
26
26
|
const { data, replayed } = await dd.write<{ noteId: number }>(deviceId, 'example-erp', 'createNote', 'POST', key, input)
|
|
@@ -109,7 +109,7 @@ export const run = async (store: { save: (row: PendingWrite) => Promise<void> })
|
|
|
109
109
|
## curl, for any other language
|
|
110
110
|
|
|
111
111
|
```bash
|
|
112
|
-
BASE
|
|
112
|
+
BASE=${DEEPDOCK_BASE_URL:-https://api.deepdock.ai}
|
|
113
113
|
AUTH="Authorization: Bearer $DEEPDOCK_API_KEY"
|
|
114
114
|
|
|
115
115
|
# Pair a device (keep data.id)
|