@deepdockai/cli 1.0.6 → 1.0.8
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 +11 -0
- package/README.md +8 -8
- package/dist/deepdock.js +8 -3
- package/package.json +1 -2
- package/skill/references/cli.md +1 -1
- package/skill/references/clients/deepdock.py +11 -4
- package/skill/references/clients/deepdock.ts +36 -5
- package/skill/references/concepts.md +1 -1
- package/skill/references/errors.md +6 -0
- package/skill/references/idempotency.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.8
|
|
4
|
+
|
|
5
|
+
- The CLI knows `system_version_unverified` (exit code 3, retry later): a write on a customer system whose vendor version Deepdock has not verified yet is refused before anything is sent, and works once Deepdock has verified that version (`reason: unverified_version`). A version the API cannot parse (`reason: unparseable`) does not clear by waiting: contact Deepdock. Reads are unaffected.
|
|
6
|
+
- A device's systems carry `writes` (`verified`, `unknown`, `partial`, or `unverified` with a `reason`) in the agent skill's TypeScript client, and the skill's error, idempotency and concepts references describe the refusal.
|
|
7
|
+
|
|
8
|
+
## 1.0.7
|
|
9
|
+
|
|
10
|
+
- The CLI knows `device_syncing` and `device_outdated` (exit code 3, retry later) instead of falling back on the HTTP status.
|
|
11
|
+
- The agent skill's error reference and both clients' pending-write helpers retry them with the same key.
|
|
12
|
+
- `--help`, the README and the skill's CLI and idempotency references list every exit-3 code, `outcome_unknown` and the access codes included.
|
|
13
|
+
|
|
3
14
|
## 1.0.6
|
|
4
15
|
|
|
5
16
|
- 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}`.
|
package/README.md
CHANGED
|
@@ -154,14 +154,14 @@ your code will receive; `auth`, `org select` and `keys create` build it from nam
|
|
|
154
154
|
failure is one document on stderr, `{ "error": { "code", "message" }, "meta": { "hint" } }`,
|
|
155
155
|
with the API's error object unchanged. Switch on `code` or on the exit code, never on `message`.
|
|
156
156
|
|
|
157
|
-
| Exit | Meaning
|
|
158
|
-
| ---- |
|
|
159
|
-
| 0 | done
|
|
160
|
-
| 1 | something on the command line to fix (`invalid_usage`, `org_required`)
|
|
161
|
-
| 2 | the API refused the request; do not repeat it unchanged (`invalid_request`, `not_found`, `forbidden`, `rate_limited` and the other refusals)
|
|
162
|
-
| 3 | retry later (`device_offline`, `action_paused`, `system_unavailable`, `target_busy`, `temporarily_unavailable`, `internal_error`, `connection_lost`) |
|
|
163
|
-
| 4 | the CLI is too old for the API (`client_outdated`): run `npx @deepdockai/cli@latest`
|
|
164
|
-
| 5 | sign-in needed (`sign_in_needed`, `approval_denied`, `approval_expired`); `auth status` when nobody is signed in
|
|
157
|
+
| Exit | Meaning |
|
|
158
|
+
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
159
|
+
| 0 | done |
|
|
160
|
+
| 1 | something on the command line to fix (`invalid_usage`, `org_required`) |
|
|
161
|
+
| 2 | the API refused the request; do not repeat it unchanged (`invalid_request`, `not_found`, `forbidden`, `rate_limited` and the other refusals) |
|
|
162
|
+
| 3 | retry later (`device_offline`, `device_syncing`, `device_outdated`, `system_version_unverified`, `action_paused`, `access_outdated`, `access_missing`, `system_unavailable`, `target_busy`, `temporarily_unavailable`, `internal_error`, `outcome_unknown`, `connection_lost`) |
|
|
163
|
+
| 4 | the CLI is too old for the API (`client_outdated`): run `npx @deepdockai/cli@latest` |
|
|
164
|
+
| 5 | sign-in needed (`sign_in_needed`, `approval_denied`, `approval_expired`); `auth status` when nobody is signed in |
|
|
165
165
|
|
|
166
166
|
The CLI never retries on its own: exit 3 says retry, you decide when. `deepdock commands --json`
|
|
167
167
|
prints the whole command tree.
|
package/dist/deepdock.js
CHANGED
|
@@ -10872,6 +10872,9 @@ var EXIT_CODES = {
|
|
|
10872
10872
|
record_not_editable: 2,
|
|
10873
10873
|
payload_too_large: 2,
|
|
10874
10874
|
device_offline: 3,
|
|
10875
|
+
device_syncing: 3,
|
|
10876
|
+
device_outdated: 3,
|
|
10877
|
+
system_version_unverified: 3,
|
|
10875
10878
|
action_paused: 3,
|
|
10876
10879
|
access_outdated: 3,
|
|
10877
10880
|
access_missing: 3,
|
|
@@ -11424,7 +11427,7 @@ import { spawn } from "node:child_process";
|
|
|
11424
11427
|
import { homedir, hostname } from "node:os";
|
|
11425
11428
|
|
|
11426
11429
|
// src/version.ts
|
|
11427
|
-
var VERSION = "1.0.
|
|
11430
|
+
var VERSION = "1.0.8";
|
|
11428
11431
|
|
|
11429
11432
|
// src/runtime.ts
|
|
11430
11433
|
var DEFAULT_BASE_URL = "https://api.deepdock.ai";
|
|
@@ -12149,8 +12152,10 @@ var EXIT_CODES_HELP = `Exit codes
|
|
|
12149
12152
|
0 done
|
|
12150
12153
|
1 usage: something on the command line to fix (invalid_usage, org_required)
|
|
12151
12154
|
2 the API refused the request; do not repeat it unchanged
|
|
12152
|
-
3 retry later (device_offline,
|
|
12153
|
-
|
|
12155
|
+
3 retry later (device_offline, device_syncing, device_outdated,
|
|
12156
|
+
system_version_unverified, action_paused, access_outdated, access_missing,
|
|
12157
|
+
system_unavailable, target_busy, temporarily_unavailable, internal_error,
|
|
12158
|
+
outcome_unknown, connection_lost)
|
|
12154
12159
|
4 the CLI is too old for the API (client_outdated)
|
|
12155
12160
|
5 sign-in needed (sign_in_needed, approval_denied, approval_expired; auth status when nobody is signed in)
|
|
12156
12161
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepdockai/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
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",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"lint": "biome lint .",
|
|
31
31
|
"lint:fix": "biome lint --write .",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
|
-
"gen:codes": "bun scripts/genCodes.ts",
|
|
34
33
|
"gen:devices": "bun scripts/genDevices.ts",
|
|
35
34
|
"check:public": "bun scripts/checkPublic.ts --pack",
|
|
36
35
|
"test": "bun run build && bun --bun vitest run",
|
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 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` |
|
|
87
|
+
| 3 | retry later (`access_outdated` and `access_missing`: after the customer repeats "Erneut einrichten" in the Deepdock client) | `device_offline`, `device_syncing`, `device_outdated`, `system_version_unverified`, `action_paused`, `access_outdated`, `access_missing`, `system_unavailable`, `target_busy`, `temporarily_unavailable`, `internal_error`, `outcome_unknown`, `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
|
|
|
@@ -19,12 +19,15 @@ class DeepdockError(Exception):
|
|
|
19
19
|
"""Deepdock answered `{ "error": { "code", ... } }`. Switch on `code`, never on `message`."""
|
|
20
20
|
|
|
21
21
|
def __init__(self, status: int, code: str, message: str, request_id: Optional[str] = None,
|
|
22
|
-
systems: Optional[List[str]] = None
|
|
22
|
+
systems: Optional[List[str]] = None, version: Optional[str] = None,
|
|
23
|
+
reason: Optional[str] = None):
|
|
23
24
|
super().__init__(message)
|
|
24
25
|
self.status = status
|
|
25
26
|
self.code = code
|
|
26
27
|
self.request_id = request_id
|
|
27
28
|
self.systems = systems
|
|
29
|
+
self.version = version # system_version_unverified: the version the system last reported
|
|
30
|
+
self.reason = reason # system_version_unverified: unverified_version or unparseable
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
class DeepdockTransportError(Exception):
|
|
@@ -63,7 +66,8 @@ class Deepdock:
|
|
|
63
66
|
raise DeepdockTransportError(f"non-JSON answer with status {r.status_code}")
|
|
64
67
|
if isinstance(body, dict) and "error" in body:
|
|
65
68
|
e = body["error"]
|
|
66
|
-
raise DeepdockError(r.status_code, e["code"], e["message"], e.get("requestId"), e.get("systems")
|
|
69
|
+
raise DeepdockError(r.status_code, e["code"], e["message"], e.get("requestId"), e.get("systems"),
|
|
70
|
+
e.get("version"), e.get("reason"))
|
|
67
71
|
if not isinstance(body, dict) or "data" not in body:
|
|
68
72
|
raise DeepdockTransportError(f"unexpected body with status {r.status_code}")
|
|
69
73
|
return body["data"], r.headers
|
|
@@ -122,12 +126,15 @@ class Deepdock:
|
|
|
122
126
|
# Pending writes: one attempt against persisted state. Persist the row BEFORE the first send, so a
|
|
123
127
|
# crash between send and answer cannot produce a second record under a new key. A job retries rows
|
|
124
128
|
# left in "sent" with backoff (seconds, then minutes, for hours if the device stays offline).
|
|
129
|
+
# For system_version_unverified and device_outdated, poll the device's `writes` / `outdated` before
|
|
130
|
+
# retrying: they take longer than minutes.
|
|
125
131
|
# --------------------------------------------------------------------------------------------------
|
|
126
132
|
|
|
127
133
|
CONFLICT_RETRIES = 5 # a running write finishes within seconds; beyond this, treat it as unknown
|
|
128
134
|
|
|
129
|
-
RETRY_SAME_KEY = {"device_offline", "
|
|
130
|
-
"
|
|
135
|
+
RETRY_SAME_KEY = {"device_offline", "device_syncing", "device_outdated", "system_version_unverified",
|
|
136
|
+
"target_busy", "temporarily_unavailable", "internal_error", "action_paused",
|
|
137
|
+
"system_unavailable"}
|
|
131
138
|
|
|
132
139
|
|
|
133
140
|
@dataclass
|
|
@@ -13,14 +13,24 @@ export class DeepdockError extends Error {
|
|
|
13
13
|
readonly code: string
|
|
14
14
|
readonly requestId?: string
|
|
15
15
|
readonly systems?: string[]
|
|
16
|
+
/** system_version_unverified: the version the system last reported. */
|
|
17
|
+
readonly version?: string
|
|
18
|
+
/** system_version_unverified: `unverified_version` or `unparseable`. */
|
|
19
|
+
readonly reason?: string
|
|
16
20
|
|
|
17
|
-
constructor(status: number, code: string, message: string, requestId?: string,
|
|
21
|
+
constructor(status: number, code: string, message: string, requestId?: string,
|
|
22
|
+
systems?: string[],
|
|
23
|
+
version?: string,
|
|
24
|
+
reason?: string,
|
|
25
|
+
) {
|
|
18
26
|
super(message)
|
|
19
27
|
this.name = 'DeepdockError'
|
|
20
28
|
this.status = status
|
|
21
29
|
this.code = code
|
|
22
30
|
this.requestId = requestId
|
|
23
31
|
this.systems = systems
|
|
32
|
+
this.version = version
|
|
33
|
+
this.reason = reason
|
|
24
34
|
}
|
|
25
35
|
}
|
|
26
36
|
|
|
@@ -32,7 +42,7 @@ export class DeepdockTransportError extends Error {
|
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
44
|
|
|
35
|
-
type ErrorBody = { code: string; message: string; requestId?: string; systems?: string[] }
|
|
45
|
+
type ErrorBody = { code: string; message: string; requestId?: string; systems?: string[]; version?: string; reason?: string }
|
|
36
46
|
type Envelope<T> = { data: T } | { error: ErrorBody }
|
|
37
47
|
|
|
38
48
|
export type Page<Item> = { items: Item[]; nextCursor: string | null }
|
|
@@ -43,6 +53,15 @@ export type SystemStatus = 'running' | 'stopped' | 'missing' | 'unknown'
|
|
|
43
53
|
/** The database access the device last reported; `null` while none is known. */
|
|
44
54
|
export type SystemAccess = 'ok' | 'outdated' | 'missing' | 'not_set_up' | null
|
|
45
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Whether writes on the system run. `unverified` makes writes answer system_version_unverified:
|
|
58
|
+
* `unverified_version` clears once Deepdock verifies the version; `unparseable` does not clear by
|
|
59
|
+
* waiting (contact Deepdock).
|
|
60
|
+
*/
|
|
61
|
+
export type SystemWrites =
|
|
62
|
+
| { state: 'verified' | 'unknown' | 'partial' }
|
|
63
|
+
| { state: 'unverified'; reason: 'unverified_version' | 'unparseable' }
|
|
64
|
+
|
|
46
65
|
export type Device = {
|
|
47
66
|
id: string
|
|
48
67
|
label: string | null
|
|
@@ -55,7 +74,14 @@ export type Device = {
|
|
|
55
74
|
/** True when an action assigned to you needs a newer client: that action answers device_outdated. */
|
|
56
75
|
outdated: boolean
|
|
57
76
|
updateAvailable?: string
|
|
58
|
-
systems: {
|
|
77
|
+
systems: {
|
|
78
|
+
system: string
|
|
79
|
+
version: string | null
|
|
80
|
+
status: SystemStatus
|
|
81
|
+
access: SystemAccess
|
|
82
|
+
reportedAt: string
|
|
83
|
+
writes: SystemWrites
|
|
84
|
+
}[]
|
|
59
85
|
}
|
|
60
86
|
|
|
61
87
|
export type PairBody = { code: string; label?: string }
|
|
@@ -130,8 +156,8 @@ export class Deepdock {
|
|
|
130
156
|
throw new DeepdockTransportError(`non-JSON answer with status ${res.status}`)
|
|
131
157
|
}
|
|
132
158
|
if (body !== null && typeof body === 'object' && 'error' in body) {
|
|
133
|
-
const { code, message, requestId, systems } = body.error
|
|
134
|
-
throw new DeepdockError(res.status, code, message, requestId, systems)
|
|
159
|
+
const { code, message, requestId, systems, version, reason } = body.error
|
|
160
|
+
throw new DeepdockError(res.status, code, message, requestId, systems, version, reason)
|
|
135
161
|
}
|
|
136
162
|
if (body === null || typeof body !== 'object' || !('data' in body)) {
|
|
137
163
|
throw new DeepdockTransportError(`unexpected body with status ${res.status}`)
|
|
@@ -220,6 +246,8 @@ export class Deepdock {
|
|
|
220
246
|
* Pending writes: one attempt against persisted state. Persist the row BEFORE the first send, so a
|
|
221
247
|
* crash between send and answer cannot produce a second record under a new key. A job retries rows
|
|
222
248
|
* left in `sent` with backoff (seconds, then minutes, for hours if the device stays offline).
|
|
249
|
+
* For system_version_unverified and device_outdated, poll the device's `writes` / `outdated` before
|
|
250
|
+
* retrying: they take longer than minutes.
|
|
223
251
|
* ---------------------------------------------------------------------------------------------- */
|
|
224
252
|
|
|
225
253
|
export type PendingStatus = 'pending' | 'sent' | 'done' | 'unknown' | 'refused'
|
|
@@ -244,6 +272,9 @@ export const CONFLICT_RETRIES = 5
|
|
|
244
272
|
|
|
245
273
|
const RETRY_SAME_KEY = new Set([
|
|
246
274
|
'device_offline',
|
|
275
|
+
'device_syncing',
|
|
276
|
+
'device_outdated',
|
|
277
|
+
'system_version_unverified',
|
|
247
278
|
'target_busy',
|
|
248
279
|
'temporarily_unavailable',
|
|
249
280
|
'internal_error',
|
|
@@ -25,7 +25,7 @@ The Deepdock client installed on the customer's Windows PC next to their system.
|
|
|
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
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
|
+
assigned action needs a newer client: it answers `device_outdated`) and the `systems` it reported (each with a `writes` state; reads always work). `verified`: writes run. `unknown`: no version was reported, and writes go through. `partial`: some writes run and some are refused. `unverified`: a write answers `system_version_unverified`; with `reason: unverified_version` it works once Deepdock has verified that version, with `reason: unparseable` retrying does not help and you contact Deepdock.
|
|
29
29
|
|
|
30
30
|
- **Pair**: claim a device for your organisation with the 6-character code it shows while unpaired.
|
|
31
31
|
- **Release**: `DELETE /v1/devices/{deviceId}` gives the device back; it shows a new code.
|
|
@@ -54,6 +54,9 @@ dashboard-only.
|
|
|
54
54
|
| Code | Status | Meaning | Retry | Show |
|
|
55
55
|
|---|---|---|---|---|
|
|
56
56
|
| `device_offline` | 503 | the device is not connected, is shutting down, or did not answer within 20 s. **A write may still have landed** | yes, with backoff (seconds to minutes); a write only with the same key | "the customer PC is offline; we will retry" |
|
|
57
|
+
| `device_syncing` | 503 | the device has not reported its Deepdock client yet (just paired, or its report did not come through); Deepdock asks it again. Nothing was written | yes, after `Retry-After` (5 s); a write with the same key | "the customer PC is connecting; retrying" |
|
|
58
|
+
| `device_outdated` | 409 | the device's Deepdock client is too old for this call. It updates itself when a newer version is released for your organisation, once nobody is signed in on the PC. Nothing was written | after the update (`outdated` on `GET /v1/devices/{deviceId}` turns `false`); retrying before it gets the same answer. A write keeps its key | "the Deepdock client on the customer PC needs an update" |
|
|
59
|
+
| `system_version_unverified` | 409 | a write on a system whose vendor version Deepdock has not verified yet. Nothing was sent to the device; reads still work. `error.version` is the version the system last reported and `error.reason` is `unverified_version`, or `unparseable` when that version is not dotted digits | `unverified_version`: after Deepdock has verified that version (`writes` on the system in `GET /v1/devices/{deviceId}` turns `verified`); retrying before it gets the same answer. `unparseable`: retrying does not help, contact Deepdock; only a different reported version changes it. A write keeps its key | "the customer's system was updated to a version Deepdock has not yet verified; changes will work once it has" |
|
|
57
60
|
| `action_paused` | 503 | the action is paused on the device (`GET .../systems` shows `reason`); only Deepdock resumes it | later, when `GET .../systems` shows it active; do not loop | "this feature is paused while Deepdock checks the customer system" |
|
|
58
61
|
| `system_unavailable` | 503 | the customer's program is stopped or missing on the device | later; often resolves when the customer restarts the program | "the customer's software is not running" |
|
|
59
62
|
| `target_busy` | 503 | the customer's system is busy or unreachable | yes, with backoff | "the customer's system is busy; retrying" |
|
|
@@ -95,6 +98,9 @@ const REACTION: Record<string, Reaction> = {
|
|
|
95
98
|
access_outdated: 'wait-setup',
|
|
96
99
|
access_missing: 'wait-setup',
|
|
97
100
|
device_offline: 'retry-same-key',
|
|
101
|
+
device_syncing: 'retry-same-key',
|
|
102
|
+
device_outdated: 'retry-later',
|
|
103
|
+
system_version_unverified: 'retry-later',
|
|
98
104
|
action_paused: 'wait-paused',
|
|
99
105
|
system_unavailable: 'retry-later',
|
|
100
106
|
target_busy: 'retry-same-key',
|
|
@@ -70,7 +70,7 @@ input, a status (`pending`, `sent`, `done`, `unknown`, `refused`) and the result
|
|
|
70
70
|
|
|
71
71
|
```
|
|
72
72
|
pending ──send──▶ sent ──2xx (fresh or replayed)──────────────▶ done
|
|
73
|
-
│ ──device_offline, timeout, network error, target_busy, temporarily_unavailable, internal_error ▶ sent (retry later, same key)
|
|
73
|
+
│ ──device_offline, device_syncing, device_outdated, system_version_unverified, timeout, network error, target_busy, temporarily_unavailable, internal_error ▶ sent (retry later, same key)
|
|
74
74
|
│ ──idempotency_conflict, retries left ───▶ sent (retry, same key, same input)
|
|
75
75
|
│ ──idempotency_conflict, retries spent ──▶ unknown (read back before anything else)
|
|
76
76
|
│ ──outcome_unknown ──────────────────────▶ unknown (read back before anything else)
|