@zergai/cyberdeck 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # zerg-cyberdeck
2
+
3
+ > **License notice:** This package is source-available, not open source. Free
4
+ > company use is limited to purpose-bound evaluation. Commercial development,
5
+ > ongoing company engineering, customer work, and other Commercial Use require
6
+ > a separate commercial license. By exercising a permission in the package
7
+ > [license](./LICENSE.md), you accept its conditions; a separate signed agreement
8
+ > controls any commercial rights.
9
+
10
+ `zerg-cyberdeck` installs the `zcd` command-line client for Zerg CyberDeck. It
11
+ manages workspace-scoped clone decks, checks running clone and Zerg services,
12
+ and asks the CyberDeck server to run its deployed SDK/API conformance suites.
13
+
14
+ This beta requires Node.js 22 or later.
15
+
16
+ ## Install
17
+
18
+ The ZergAI organization distribution is `@zergai/cyberdeck@next`. After the
19
+ scoped release is published, use `npm install --global @zergai/cyberdeck@next`.
20
+ It provides the same `zcd` command and license as the legacy package below.
21
+ Install only one distribution globally because both provide `zcd`.
22
+
23
+ Until the first stable release, install the `next` channel:
24
+
25
+ ```bash
26
+ npm install --global zerg-cyberdeck@next
27
+ zcd --version
28
+ ```
29
+
30
+ ## Sign in
31
+
32
+ ```bash
33
+ zcd login
34
+ ```
35
+
36
+ The CLI prints a short user code and opens the CyberDeck authorization page.
37
+ Sign in through the normal browser flow, choose one of your workspaces, and
38
+ approve the device. The resulting 30-day personal token is bound to that
39
+ workspace and stored in a mode-`0600` config file.
40
+
41
+ Useful alternatives:
42
+
43
+ ```bash
44
+ zcd login --url https://your-cyberdeck.example --workspace "Blue Team"
45
+ zcd whoami
46
+ zcd logout
47
+ ```
48
+
49
+ For non-interactive automation, set `ZERGCYBERDECK_TOKEN` to an independently
50
+ issued scoped token. `ZERGCYBERDECK_BASE_URL` changes the server, and
51
+ `ZERGCYBERDECK_CONFIG_DIR` isolates the local config directory.
52
+
53
+ ## Common commands
54
+
55
+ ```bash
56
+ # The server is authoritative for available targets and conformance support.
57
+ zcd catalog
58
+
59
+ # Create a deck and start explicitly selected clones.
60
+ zcd up --clones zlunk,zokta --name "SDK check"
61
+
62
+ # Or create every unique target from the authoritative server catalog. Clone
63
+ # starts are admitted through one durable, bounded server operation.
64
+ zcd up --all --name "Full connector range"
65
+
66
+ zcd ls
67
+ zcd status --deck <deck-id>
68
+ zcd diagnose --deck <deck-id> --tail 100
69
+ zcd check --deck <deck-id>
70
+ zcd seed --deck <deck-id> --reset
71
+
72
+ # Start and poll server-side SDK/API conformance; reports remain local.
73
+ zcd sdk --deck <deck-id> --vendor zlunk
74
+
75
+ zcd down --deck <deck-id> --yes
76
+ ```
77
+
78
+ ## Scenarios and templates
79
+
80
+ The `scenario` command manages the same workspace-scoped, versioned definitions
81
+ as the CyberDeck workbench. Draft updates use optimistic versions, and published
82
+ revisions remain immutable.
83
+
84
+ ```bash
85
+ # Read the runnable scenario library and one canonical YAML draft.
86
+ zcd scenario list --json
87
+ zcd scenario show --id <scenario-id> --json
88
+
89
+ # Create, update, and publish a scenario from a local YAML document.
90
+ zcd scenario create --file scenario.yaml --json
91
+ zcd scenario update --id <scenario-id> --expected-version 1 --file scenario.yaml --json
92
+ zcd scenario publish --id <scenario-id> --expected-version 2 --json
93
+
94
+ # Manage reusable drill and company-template libraries through the same API.
95
+ zcd scenario list --kind drill-template --json
96
+ zcd scenario create --kind company-template --file company-template.yaml --json
97
+
98
+ # Lifecycle-admin operations are explicit and recoverable.
99
+ zcd scenario delete --id <scenario-id> --yes --json
100
+ zcd scenario restore --id <scenario-id> --json
101
+ ```
102
+
103
+ `zcd up` removes duplicate target IDs while preserving first-seen order. It
104
+ preflights the selected clones and submits one idempotent fleet operation
105
+ instead of sending an unbounded burst of individual start requests. A blocked
106
+ control plane leaves the new deck intact for inspection and exits with code 2.
107
+
108
+ `zcd sdk --clones ...` may provision the clones you name. Review the selection
109
+ and the target CyberDeck before running it; clone environments consume compute.
110
+ The CLI does not bundle vendor SDKs or a local conformance runner.
111
+
112
+ ## Automation and clone discovery
113
+
114
+ Inventory commands retain their human-readable output by default and expose a
115
+ stable, versioned form for scripts:
116
+
117
+ ```bash
118
+ zcd catalog --json
119
+ zcd ls --json
120
+ zcd ls --deck <deck-id> --json
121
+ zcd status --deck <deck-id> --json
122
+ zcd diagnose --deck <deck-id> --json
123
+ ```
124
+
125
+ Each JSON document has `"schemaVersion": 1`. Catalog targets may advertise a
126
+ non-secret `managedCredential` descriptor with its purpose, authorization
127
+ scheme/header, and display label. The purpose distinguishes vendor API tokens,
128
+ official SDK tokens, and application-console credentials without exposing the
129
+ credential itself. Older CyberDeck servers that omit this descriptor remain
130
+ supported.
131
+
132
+ Inspect one deployment by a unique vendor name or exact clone UUID:
133
+
134
+ ```bash
135
+ zcd inspect --deck <deck-id> --clone zlunk
136
+ zcd inspect --deck <deck-id> --clone <clone-uuid> --json
137
+ ```
138
+
139
+ The inspection includes normalized runtime URLs and status, published Zerg
140
+ module fields, artifact qualification, and the non-secret credential hint. If
141
+ a deck contains multiple clones of the same vendor, pass a clone UUID.
142
+
143
+ Export the current canonical `GET /__sim/export` snapshot from one running
144
+ clone without handling its private simulation-control credential:
145
+
146
+ ```bash
147
+ # Print a stable, schema-versioned envelope to stdout.
148
+ zcd dump --deck <deck-id> --clone zentinel-one
149
+
150
+ # Or write it to a mode-0600 JSON file.
151
+ zcd dump --deck <deck-id> --clone <clone-uuid> --out ./zentinel-one.json
152
+ ```
153
+
154
+ The response includes the clone and deck identity, capture time, source byte
155
+ count, a canonical SHA-256 data hash, and the clone-native snapshot. The Deck
156
+ server scopes the request to the active workspace, fences the live deployment
157
+ generation, and never returns the runtime URL or simulation-control token.
158
+
159
+ ## Review-gated QA oracles
160
+
161
+ An oracle set preserves the human-authored question and query separately from
162
+ the connector parameters that Codex proposes. Proposed parameters cannot be
163
+ executed until a reviewer approves them, and synthesized answers have a second
164
+ review gate before the baseline can be sealed.
165
+
166
+ ```bash
167
+ # The draft JSON contains one immutable scenario-run or clone-dump subject and
168
+ # 1-100 questions. Each question carries 1-16 authored connector queries.
169
+ zcd oracle create --deck <deck-id> --file ./oracle-draft.json --json
170
+
171
+ # Codex runs in text-only mode and may only propose schema-valid parameters.
172
+ zcd oracle materialize --deck <deck-id> --id <oracle-id> --json
173
+
174
+ # Inspect the current immutable version, then submit a reviewed document with
175
+ # optimistic concurrency. Later execution/answer events use the same command.
176
+ zcd oracle show --deck <deck-id> --id <oracle-id> --json
177
+ zcd oracle transition --deck <deck-id> --id <oracle-id> \
178
+ --expected-version 3 --event approve_parameters --file ./reviewed.json --json
179
+
180
+ zcd oracle list --deck <deck-id> --json
181
+ zcd oracle export --deck <deck-id> --id <oracle-id> --out ./oracle.json
182
+ ```
183
+
184
+ The server stores every version as a content-addressed immutable object and
185
+ keeps only its opaque object reference, byte count, content type, and SHA-256
186
+ digest in the database. `oracle export` writes a mode-`0600` file. The exported
187
+ artifact includes the stable oracle ID/version and public metadata but never an
188
+ object-storage key, connector credential, or simulation-control token.
189
+
190
+ Credential disclosure is a separate, explicit write-authorized POST action:
191
+
192
+ ```bash
193
+ zcd credentials --deck <deck-id> --clone zlunk
194
+ zcd credentials --deck <deck-id> --clone zentinel-one
195
+ zcd credentials --deck <deck-id> --clone <clone-uuid> --json
196
+ ```
197
+
198
+ Successful credential output contains a secret and should not be logged.
199
+ Credential and runtime error payloads are redacted from stderr so a malformed
200
+ or failed server response cannot echo a token through CLI diagnostics.
201
+
202
+ ## Fleet diagnostics and recovery
203
+
204
+ `status` and `diagnose` use the observational diagnostics API. Reading them
205
+ does not register routes, update clone rows, or otherwise reconcile runtime
206
+ state. Every clone receives an ordered result envelope even when an upstream
207
+ provider is unavailable.
208
+
209
+ ```bash
210
+ # Inspect the entire deck or one clone. Vendor names must be unique in a deck;
211
+ # an exact clone UUID is always unambiguous.
212
+ zcd diagnose --deck <deck-id>
213
+ zcd diagnose --deck <deck-id> --clone zlunk --tail 200 --json
214
+
215
+ # Read logs without revealing clone credentials.
216
+ zcd logs --deck <deck-id> --clone zlunk --tail 200
217
+ zcd logs --deck <deck-id> --clone <clone-uuid> \
218
+ --since 2026-07-27T19:00:00.000Z --json
219
+
220
+ # Check admission separately, or start all/failed/one clone.
221
+ zcd preflight --deck <deck-id> --all
222
+ zcd start --deck <deck-id> --failed --wait --json
223
+ zcd start --deck <deck-id> --clone zlunk
224
+
225
+ # Recovery actions are bounded and return a result for every selected clone.
226
+ zcd stop --deck <deck-id> --all --json
227
+ zcd reset --deck <deck-id> --clone zlunk
228
+ zcd restart-runtime --deck <deck-id> --failed --wait
229
+ ```
230
+
231
+ `restart-runtime` performs a controlled stop, waits for the observational
232
+ status to become stopped, and then submits a new durable start operation.
233
+
234
+ Fleet operation history is available independently of the current clone
235
+ projection:
236
+
237
+ ```bash
238
+ zcd operations --deck <deck-id> --limit 20
239
+ zcd operation --deck <deck-id> --run <run-id> --json
240
+ zcd operation --deck <deck-id> --run <run-id> --retry-failed --wait
241
+ ```
242
+
243
+ CLI exit codes are stable for automation:
244
+
245
+ - `0`: the command completed without clone failures.
246
+ - `1`: one or more clone actions or operation items failed.
247
+ - `2`: platform readiness blocked admission; no start operation was created.
248
+
249
+ Run `zcd <command> --help` for all options.
250
+
251
+ ## Licensing
252
+
253
+ This package is source-available, not open source. Purpose-limited evaluation
254
+ and the specified personal, educational, research, and nonprofit uses are
255
+ available under the
256
+ [Zerg Source Available License 1.0](./LICENSE.md). Commercial production,
257
+ commercial development, for-profit internal use, managed services,
258
+ governmental operational use, and competing general-purpose platforms require
259
+ a separate signed license. See
260
+ [commercial licensing](./COMMERCIAL-LICENSING.md) or contact
261
+ licensing@zergai.com.
262
+
263
+ Third-party software remains under its own terms; see
264
+ [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
265
+ # Scenario execution and evidence
266
+
267
+ Use the same run API as the web workbench. A custom scenario must be published;
268
+ the server binds the run to its immutable revision and rechecks clone readiness.
269
+
270
+ ```sh
271
+ zcd scenario run --deck <deck-id> --scenario <catalog-or-definition-id> --json
272
+ zcd scenario run --deck <deck-id> --scenario <id> --capture-evidence --reset-targets --seed repro-1 --json
273
+ zcd scenario runs --deck <deck-id> --json
274
+ zcd scenario run-show --deck <deck-id> --run <run-id> --json
275
+ zcd scenario run-control --deck <deck-id> --run <run-id> --action pause --json
276
+ ```
277
+
278
+ Simulation-only is the default. **`--reset-targets` authorizes clearing existing
279
+ data on all running scenario targets**; capture will refuse without it. Server
280
+ preflight checks storage and compatible deployments before reset. The launch
281
+ response is provisioning, not execution success or evidence readiness. Keep its
282
+ exact run ID/URL; inspect `run-show` for execution, artifact status, expiry, and
283
+ authorized download URLs. Evaluator access remains enforced by the server.
284
+ Control commands request a state change; inspect the run to confirm it happened.
@@ -0,0 +1,14 @@
1
+ # Third-Party Notices for zerg-cyberdeck
2
+
3
+ `zerg-cyberdeck` uses the following direct runtime dependencies. They are not
4
+ licensed under the Zerg Source Available License 1.0 and remain governed by
5
+ their own terms and copyright notices.
6
+
7
+ | Component | Requested version | License | Project |
8
+ | --- | --- | --- | --- |
9
+ | Commander.js (`commander`) | `^12.1.0` | MIT | <https://github.com/tj/commander.js> |
10
+ | Socket.IO client (`socket.io-client`) | `^4.8.1` | MIT | <https://github.com/socketio/socket.io> |
11
+
12
+ The dependency installation may include transitive packages. Their package
13
+ distributions contain the authoritative notices and license texts for those
14
+ components. Nothing in this file changes any third-party license.
@@ -0,0 +1,161 @@
1
+ const CLONE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2
+ export function normalizeCatalogForJson(catalog) {
3
+ return {
4
+ schemaVersion: 1,
5
+ targets: catalog.targets.map(target => ({
6
+ ...target,
7
+ managedCredential: target.managedCredential ?? null,
8
+ })),
9
+ };
10
+ }
11
+ export function deckSummary(deck) {
12
+ return {
13
+ id: deck.id,
14
+ name: deck.name,
15
+ slug: deck.slug,
16
+ status: deck.status ?? null,
17
+ };
18
+ }
19
+ export function cloneSummary(clone) {
20
+ return {
21
+ id: clone.id,
22
+ vendor: clone.vendor,
23
+ name: clone.name,
24
+ status: clone.status,
25
+ zergId: clone.zerg_id,
26
+ serviceUrl: clone.service_url,
27
+ deckRouteSlug: clone.deck_route_slug,
28
+ };
29
+ }
30
+ export function resolveCloneSelector(deck, selector) {
31
+ const value = selector.trim();
32
+ const clones = deck.clones ?? [];
33
+ if (!value) {
34
+ throw new Error('--clone must name a vendor or clone UUID.');
35
+ }
36
+ if (CLONE_UUID.test(value)) {
37
+ const exact = clones.find(clone => clone.id.toLowerCase() === value.toLowerCase());
38
+ if (exact)
39
+ return exact;
40
+ throw new Error(`Clone UUID '${value}' was not found in deck ${deck.id}.`);
41
+ }
42
+ const vendor = value.toLowerCase();
43
+ const matches = clones.filter(clone => clone.vendor.toLowerCase() === vendor);
44
+ if (matches.length === 0) {
45
+ throw new Error(`Clone selector '${value}' did not match a clone in deck ${deck.id}. `
46
+ + `Run \`zcd ls --deck ${deck.id}\` to list clone UUIDs.`);
47
+ }
48
+ if (matches.length > 1) {
49
+ const ids = matches.map(clone => clone.id).join(', ');
50
+ throw new Error(`Clone vendor '${vendor}' is ambiguous in deck ${deck.id}. `
51
+ + `Matching clone IDs: ${ids}. Pass a clone UUID.`);
52
+ }
53
+ return matches[0];
54
+ }
55
+ export function catalogTarget(catalog, vendor) {
56
+ return catalog.targets.find(target => target.id === vendor);
57
+ }
58
+ export function explicitlyUnsupportedCredential(target) {
59
+ return (target !== undefined
60
+ && Object.hasOwn(target, 'managedCredential')
61
+ && target.managedCredential === null);
62
+ }
63
+ const PUBLISHED_CREDENTIAL_FIELDS = new Set([
64
+ 'connector_credential_label',
65
+ 'connector_credential_header',
66
+ 'connector_credential_scheme',
67
+ 'connector_credential',
68
+ ]);
69
+ export function publishedCredentialFromRuntime(vendor, runtime) {
70
+ const candidates = [];
71
+ for (const module of runtime.publishedModules ?? []) {
72
+ const credentialFields = module.fields.filter(field => (PUBLISHED_CREDENTIAL_FIELDS.has(field.name)));
73
+ if (credentialFields.length !== PUBLISHED_CREDENTIAL_FIELDS.size
74
+ || new Set(credentialFields.map(field => field.name)).size
75
+ !== PUBLISHED_CREDENTIAL_FIELDS.size)
76
+ continue;
77
+ const fields = new Map(credentialFields.map(field => [field.name, field]));
78
+ const label = publishedString(fields.get('connector_credential_label'), 'text', 256);
79
+ const header = publishedString(fields.get('connector_credential_header'), 'text', 128);
80
+ const scheme = publishedScheme(fields.get('connector_credential_scheme'));
81
+ const token = publishedString(fields.get('connector_credential'), 'code', 2_048);
82
+ if (!label || !header || scheme === null || !token)
83
+ continue;
84
+ if (!/^[A-Za-z0-9-]+$/.test(header)
85
+ || (scheme !== ''
86
+ && !/^[A-Za-z][A-Za-z0-9._~-]*$/.test(scheme))
87
+ || (scheme === '' && header.toLowerCase() === 'authorization'))
88
+ continue;
89
+ candidates.push({
90
+ label,
91
+ credential: {
92
+ schemaVersion: 1,
93
+ vendor,
94
+ authentication: {
95
+ type: 'token',
96
+ scheme,
97
+ header,
98
+ token,
99
+ },
100
+ },
101
+ });
102
+ }
103
+ return candidates.length === 1 ? candidates[0] : null;
104
+ }
105
+ function publishedScheme(field) {
106
+ if (!field
107
+ || !field.available
108
+ || field.kind !== 'text'
109
+ || typeof field.value !== 'string')
110
+ return null;
111
+ const value = field.value;
112
+ return (value === value.trim()
113
+ && value.length <= 64
114
+ && !/[\u0000-\u001f\u007f]/u.test(value)) ? value : null;
115
+ }
116
+ function publishedString(field, kind, maxLength) {
117
+ if (!field
118
+ || !field.available
119
+ || field.kind !== kind
120
+ || typeof field.value !== 'string')
121
+ return '';
122
+ const value = field.value;
123
+ return (value === value.trim()
124
+ && value
125
+ && value.length <= maxLength
126
+ && !/[\u0000-\u001f\u007f]/u.test(value)) ? value : '';
127
+ }
128
+ function optionalRuntimeValue(value, fallback) {
129
+ return value === undefined ? fallback : value;
130
+ }
131
+ export function inspectClone(deck, clone, runtime, target) {
132
+ return {
133
+ schemaVersion: 1,
134
+ deck: deckSummary(deck),
135
+ clone: cloneSummary(clone),
136
+ managedCredential: target?.managedCredential ?? null,
137
+ runtime: {
138
+ provisioned: runtime.provisioned ?? clone.zerg_id !== null,
139
+ zergId: optionalRuntimeValue(runtime.zergId, clone.zerg_id),
140
+ status: runtime.status ?? clone.status,
141
+ cloneStatus: runtime.cloneStatus ?? clone.status,
142
+ deployPhase: runtime.deployPhase ?? null,
143
+ serviceUrl: optionalRuntimeValue(runtime.serviceUrl, clone.service_url),
144
+ socketUrl: runtime.socketUrl ?? null,
145
+ mirrorUrl: runtime.mirrorUrl ?? null,
146
+ errorMessage: runtime.errorMessage ?? null,
147
+ zergCloudUrl: runtime.zergCloudUrl ?? null,
148
+ zstackRunUrl: runtime.zstackRunUrl ?? null,
149
+ flyAppName: runtime.flyAppName ?? null,
150
+ flyAppUrl: runtime.flyAppUrl ?? null,
151
+ updatedAt: runtime.updatedAt ?? null,
152
+ },
153
+ publications: {
154
+ status: runtime.publicationStatus ?? 'none',
155
+ error: runtime.publicationError ?? null,
156
+ modules: runtime.publishedModules ?? [],
157
+ },
158
+ qualification: runtime.qualification ?? null,
159
+ };
160
+ }
161
+ //# sourceMappingURL=automation.js.map