@consiliency/agent-board-cli 1.9.0 → 1.11.0

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/dist/cli.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Command } from "commander";
2
- import { type EnrollKeystore, type EnrollmentIntent, type PendingEnrollmentStore, type OAuthFetchLike, type FetchLike, type AgentJoinDeployment, type AgentJoinInstructions, type DeviceAuthorizationPrompt } from "@consiliency/agent-board-client";
2
+ import { type EnrollKeystore, type EnrollmentIntent, type FleetOnboardingIntent, type PendingEnrollmentStore, type OAuthFetchLike, type FetchLike, type AgentJoinDeployment, type AgentJoinInstructions, type DeviceAuthorizationPrompt } from "@consiliency/agent-board-client";
3
+ import { DevicePendingEnrollmentStore } from "./enroll_runtime.js";
3
4
  type JsonObject = Record<string, unknown>;
4
5
  export interface CliIo {
5
6
  writeStdout(message: string): void;
@@ -38,6 +39,20 @@ interface ClientLike {
38
39
  grantBoardRole(input: JsonObject): Promise<unknown>;
39
40
  setBoardRoleGate(input: JsonObject): Promise<unknown>;
40
41
  createInviteLink(input: JsonObject): Promise<unknown>;
42
+ createFleetOnboardingInvite?(input: {
43
+ boardId: string;
44
+ machineId: string;
45
+ harnessKind: string;
46
+ harnessLabel: string;
47
+ repoSlug: string;
48
+ repoFingerprint: string;
49
+ repoScopeRole: "worker" | "reviewer";
50
+ agentRole: "phase_worker" | "reviewer";
51
+ expectedAgentHandle: string;
52
+ maxUses?: number;
53
+ expiresAt?: string;
54
+ createdByAgentId?: string;
55
+ }): Promise<unknown>;
41
56
  createInviteLinkWithInstructions(input: JsonObject, deployment: AgentJoinDeployment, board?: {
42
57
  boardSlug?: string;
43
58
  boardName?: string;
@@ -145,10 +160,37 @@ export interface EnrollmentCommandRunner {
145
160
  requestedBoardScopes?: string[];
146
161
  onDeviceAuthorization: (prompt: DeviceAuthorizationPrompt) => void | Promise<void>;
147
162
  }): Promise<unknown>;
163
+ deviceBegin?(input: {
164
+ /** Optional caller-generated ID used to bind a companion non-secret continuation first. */
165
+ enrollmentId?: string;
166
+ slug: string;
167
+ intent: EnrollmentIntent;
168
+ agentKind?: string;
169
+ requestedScopes?: string[];
170
+ requestedBoardScopes?: string[];
171
+ }): Promise<{
172
+ enrollmentId: string;
173
+ prompt: DeviceAuthorizationPrompt;
174
+ }>;
175
+ deviceComplete?(input: {
176
+ enrollmentId: string;
177
+ expectedFleet?: {
178
+ intent: Omit<FleetOnboardingIntent, "inviteCode">;
179
+ keyReference: string;
180
+ slug: string;
181
+ };
182
+ /** Keep the secret continuation for a caller that must finish local materialization first. */
183
+ deletePending?: boolean;
184
+ }): Promise<unknown>;
185
+ deviceDeletePending?(input: {
186
+ enrollmentId: string;
187
+ }): Promise<void>;
148
188
  }
149
189
  export interface DefaultEnrollmentRunnerDeps {
150
190
  /** Pending store bridging begin→complete. Default: FilePendingEnrollmentStore(env). */
151
191
  store?: PendingEnrollmentStore;
192
+ /** Device-code state bridging deviceBegin→deviceComplete across CLI processes. */
193
+ deviceStore?: DevicePendingEnrollmentStore;
152
194
  /** OAuth/enroll fetch. Default: nodeFetchAdapter over globalThis.fetch. */
153
195
  fetch?: OAuthFetchLike;
154
196
  /** Board-native fetch for the post-enroll SessionClient. Default: nodeFetchAdapter. */
@@ -179,6 +221,86 @@ export interface DefaultEnrollmentRunnerDeps {
179
221
  };
180
222
  }
181
223
  export declare function defaultEnrollmentRunner(env?: NodeJS.ProcessEnv, deps?: DefaultEnrollmentRunnerDeps): EnrollmentCommandRunner;
224
+ /**
225
+ * How each field of a `FleetOnboardingIntent` is turned from an argv string into the value
226
+ * that goes on the wire and into the template (Consiliency/message-board#254).
227
+ *
228
+ * This map exists because the previous shape — nine literal `.trim()` calls repeated at two
229
+ * call sites — is the completeness failure CLAUDE.md rule 1 targets: #254 itself enumerated
230
+ * six of the nine, and got one of the six wrong. The map is TOTAL over the intent's string
231
+ * fields by construction, so a field added to `FleetOnboardingIntent` does not compile until
232
+ * someone dispositions it, and both call sites read their verb from here rather than
233
+ * restating it.
234
+ *
235
+ * Three dispositions, because there are three real behaviours and collapsing them is what
236
+ * hid the defect:
237
+ *
238
+ * - `gated` — the library REFUSES a prohibited character in this value rather than
239
+ * rewriting it, because the value is carried byte-exact into a template that runs on
240
+ * ANOTHER machine. A bare `.trim()` here deleted an edge-placed member of the refuse
241
+ * classes before `required()` could see it, so the library's raw-operand ordering (#215)
242
+ * was unreachable from the CLI. The verb is the library's own, imported.
243
+ * - `normalized` — the value is NOT carried verbatim: it is folded, or it is pinned by
244
+ * equality against a value derived from folded atoms. Trimming is right here, and
245
+ * removing it would regress an ordinary trailing space into a hard failure for no
246
+ * byte-exactness gain. Each one's claim is asserted positively in `cli.test.ts`, on the
247
+ * intent the runner RECEIVES — `expectedAgentHandle` and `inviteCode` are copied verbatim
248
+ * into the /enroll body, so "the output is clean" would not be evidence about them.
249
+ * - `vocabulary` — a closed set. A poisoned value cannot match a member, so it is refused
250
+ * by the existing pair check rather than by a presentation class.
251
+ *
252
+ * Each entry also states what a PROHIBITED character in that field does to the /enroll
253
+ * request — `wire`, which is the property `cli.test.ts` measures. It is stated per field
254
+ * rather than as one blanket claim because the blanket claim is false: an invite code is a
255
+ * bearer secret the server matches, so the refuse class is not policed on it.
256
+ *
257
+ * `verbatim` means exactly that — THE REFUSE CLASS IS NOT POLICED ON THIS FIELD — and not
258
+ * "untouched". Edges are still trimmed here as on every field (AC-6), so a prohibited
259
+ * character placed at an edge is erased and only an INTERIOR one survives to the wire. The
260
+ * label is worth stating precisely because the looser reading ("it travels as the operator
261
+ * typed it") stood in this comment and in three places in the plan, and was false at the
262
+ * edges in all four. `cli.test.ts` asserts the arm per PLACEMENT, which is the form that
263
+ * cannot be read two ways.
264
+ *
265
+ * Asserting a field's real arm is what makes an exemption evidence instead of prose.
266
+ */
267
+ type FleetTupleDisposition = {
268
+ readonly kind: "gated";
269
+ readonly flag: string;
270
+ readonly label: string;
271
+ readonly wire: "refused";
272
+ } | {
273
+ readonly kind: "normalized";
274
+ readonly flag: string;
275
+ readonly why: string;
276
+ readonly wire: "clean" | "verbatim";
277
+ readonly normalize: (value: string) => string;
278
+ } | {
279
+ readonly kind: "vocabulary";
280
+ readonly flag?: string;
281
+ readonly why: string;
282
+ readonly wire: "refused";
283
+ };
284
+ /**
285
+ * The string-valued keys of `T` — the operator the totality claim above rests on, and the
286
+ * reason it is named and exported rather than written inline.
287
+ *
288
+ * `NonNullable` is load-bearing. `-?` strips optionality from the PRODUCED property, but
289
+ * the conditional tests the original indexed access, and for `field?: string` that is
290
+ * `string | undefined`, which does not extend `string`. Written without `NonNullable` this
291
+ * operator answers `never` for every optional member — so an optional string field added to
292
+ * the intent would vanish from the disposition map, from the census, and from the wire
293
+ * matrix, WITHOUT a compile error. Fail-open, and invisible until someone adds such a field.
294
+ *
295
+ * Latent rather than live today (`FleetOnboardingIntent` has no optional string members),
296
+ * which is exactly why it is pinned by a type-level probe in `cli.test.ts` — a guard whose
297
+ * failure mode is silence has to be tested against a shape that would trigger it.
298
+ */
299
+ export type StringFieldsOf<T> = {
300
+ [K in keyof T]-?: NonNullable<T[K]> extends string ? K : never;
301
+ }[keyof T];
302
+ type FleetTupleField = StringFieldsOf<FleetOnboardingIntent>;
303
+ export declare const FLEET_TUPLE_DISPOSITION: Readonly<Record<FleetTupleField, FleetTupleDisposition>>;
182
304
  export declare function createProgram(client: ClientLike, io: CliIo, enrollment?: EnrollmentCommandRunner): Command;
183
305
  export declare function runCli(argv: string[], io?: CliIo, client?: ClientLike, enrollment?: EnrollmentCommandRunner): Promise<void>;
184
306
  export {};