@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 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.6";
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, action_paused, system_unavailable, target_busy,
12153
- temporarily_unavailable, internal_error, connection_lost)
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.6",
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",
@@ -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", "target_busy", "temporarily_unavailable", "internal_error",
130
- "action_paused", "system_unavailable"}
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, systems?: 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: { system: string; version: string | null; status: SystemStatus; access: SystemAccess; reportedAt: string }[]
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)