@deepdockai/cli 1.0.7 → 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 +5 -0
- package/README.md +8 -8
- package/dist/deepdock.js +6 -4
- package/package.json +1 -1
- package/skill/references/cli.md +1 -1
- package/skill/references/clients/deepdock.py +11 -4
- package/skill/references/clients/deepdock.ts +34 -5
- package/skill/references/concepts.md +1 -1
- package/skill/references/errors.md +2 -0
- package/skill/references/idempotency.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
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
|
+
|
|
3
8
|
## 1.0.7
|
|
4
9
|
|
|
5
10
|
- The CLI knows `device_syncing` and `device_outdated` (exit code 3, retry later) instead of falling back on the HTTP status.
|
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`, `device_syncing`, `device_outdated`, `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
|
|
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
|
@@ -10874,6 +10874,7 @@ var EXIT_CODES = {
|
|
|
10874
10874
|
device_offline: 3,
|
|
10875
10875
|
device_syncing: 3,
|
|
10876
10876
|
device_outdated: 3,
|
|
10877
|
+
system_version_unverified: 3,
|
|
10877
10878
|
action_paused: 3,
|
|
10878
10879
|
access_outdated: 3,
|
|
10879
10880
|
access_missing: 3,
|
|
@@ -11426,7 +11427,7 @@ import { spawn } from "node:child_process";
|
|
|
11426
11427
|
import { homedir, hostname } from "node:os";
|
|
11427
11428
|
|
|
11428
11429
|
// src/version.ts
|
|
11429
|
-
var VERSION = "1.0.
|
|
11430
|
+
var VERSION = "1.0.8";
|
|
11430
11431
|
|
|
11431
11432
|
// src/runtime.ts
|
|
11432
11433
|
var DEFAULT_BASE_URL = "https://api.deepdock.ai";
|
|
@@ -12151,9 +12152,10 @@ var EXIT_CODES_HELP = `Exit codes
|
|
|
12151
12152
|
0 done
|
|
12152
12153
|
1 usage: something on the command line to fix (invalid_usage, org_required)
|
|
12153
12154
|
2 the API refused the request; do not repeat it unchanged
|
|
12154
|
-
3 retry later (device_offline, device_syncing, device_outdated,
|
|
12155
|
-
|
|
12156
|
-
|
|
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)
|
|
12157
12159
|
4 the CLI is too old for the API (client_outdated)
|
|
12158
12160
|
5 sign-in needed (sign_in_needed, approval_denied, approval_expired; auth status when nobody is signed in)
|
|
12159
12161
|
|
package/package.json
CHANGED
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`, `device_syncing`, `device_outdated`, `action_paused`, `access_outdated`, `access_missing`, `system_unavailable`, `target_busy`, `temporarily_unavailable`, `internal_error`, `outcome_unknown`, `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", "device_syncing", "device_outdated", "
|
|
130
|
-
"temporarily_unavailable", "internal_error", "action_paused",
|
|
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'
|
|
@@ -246,6 +274,7 @@ const RETRY_SAME_KEY = new Set([
|
|
|
246
274
|
'device_offline',
|
|
247
275
|
'device_syncing',
|
|
248
276
|
'device_outdated',
|
|
277
|
+
'system_version_unverified',
|
|
249
278
|
'target_busy',
|
|
250
279
|
'temporarily_unavailable',
|
|
251
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.
|
|
@@ -56,6 +56,7 @@ dashboard-only.
|
|
|
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
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
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" |
|
|
59
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" |
|
|
60
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" |
|
|
61
62
|
| `target_busy` | 503 | the customer's system is busy or unreachable | yes, with backoff | "the customer's system is busy; retrying" |
|
|
@@ -99,6 +100,7 @@ const REACTION: Record<string, Reaction> = {
|
|
|
99
100
|
device_offline: 'retry-same-key',
|
|
100
101
|
device_syncing: 'retry-same-key',
|
|
101
102
|
device_outdated: 'retry-later',
|
|
103
|
+
system_version_unverified: 'retry-later',
|
|
102
104
|
action_paused: 'wait-paused',
|
|
103
105
|
system_unavailable: 'retry-later',
|
|
104
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, device_syncing, device_outdated, 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)
|