@fluid-app/fluid-cli-genealogy 0.1.1 → 0.1.3
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/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/api.ts","../src/index.ts"],"sourcesContent":["// Calls to the company genealogy API (`/api/company/v2026-10/genealogy`).\n\n/** The slice of the CLI's API client these calls use. */\nexport interface GenealogyClient {\n get<T>(endpoint: string, params?: Record<string, unknown>): Promise<T>;\n post<T>(\n endpoint: string,\n body?: unknown,\n options?: { headers?: Record<string, string> },\n ): Promise<T>;\n patch<T>(endpoint: string, body?: unknown): Promise<T>;\n delete<T>(\n endpoint: string,\n options?: { params?: Record<string, unknown> },\n ): Promise<T>;\n}\n\nconst BASE = \"/api/company/v2026-10/genealogy\";\n\n/** The tree `fluid genealogy` reads when --tree is not given, matching the\n * Rails genealogy CLI. */\nexport const DEFAULT_TREE = \"placement\";\n\n/** Deeper reads (descendants, subtree, level, level counts) admit relative\n * depths 1-7; the API refuses larger values rather than clamping them. */\nexport const MAX_DEPTH = 7;\n\n/** Node pages hold at most 1,000 rows; history-style pages (placements,\n * holding tank, move requests and jobs) at most 100. */\nconst NODE_PAGE_MAX = 1000;\nconst HISTORY_PAGE_MAX = 100;\n\n/** An unbounded read stops here instead of returning a partial list. */\nconst MAX_PAGES = 200;\n\n/** Fan-out reads (each child when ranking legs, each relation kind) run\n * this many at a time. */\nconst CONCURRENCY = 5;\n\nconst UUID = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;\n\nexport class GenealogyError extends Error {\n /** The API's failure type, when the error restates an API failure. */\n readonly type?: string;\n /** The API's HTTP status, when the error restates an API failure. */\n readonly status?: number;\n\n constructor(\n message: string,\n options: { type?: string; status?: number } = {},\n ) {\n super(message);\n this.name = \"GenealogyError\";\n if (options.type !== undefined) this.type = options.type;\n if (options.status !== undefined) this.status = options.status;\n }\n}\n\nexport interface GenealogyNode {\n id: string;\n tree_id: string;\n member_id: string;\n parent_node_id: string | null;\n leg: string | null;\n position: number;\n pinned: boolean;\n relative_depth?: number;\n}\n\n/** nodes/{id} without as_of also carries structural counts. */\nexport interface NodeRelationship {\n id: string;\n tree_id: string;\n member_id: string;\n parent_node_id: string | null;\n leg: string | null;\n effective_at?: string;\n ended_at?: string | null;\n children_count?: number;\n downline_count?: number;\n counts_as_of?: string;\n}\n\nexport interface Tree {\n id: string;\n key: string;\n name?: string;\n kind?: string;\n role?: string | null;\n leg_names?: string[];\n [field: string]: unknown;\n}\n\ninterface Pagination {\n next_cursor?: string | null;\n}\n\ninterface PageWire {\n meta?: { pagination?: Pagination; traversal?: { depth_truncated?: boolean } };\n}\n\ninterface NodeResponse<N> {\n node?: N | null;\n}\n\nexport function isUuid(value: string): boolean {\n return UUID.test(value);\n}\n\nfunction path(...segments: string[]): string {\n return `${BASE}/${segments.map((s) => encodeURIComponent(s)).join(\"/\")}`;\n}\n\nfunction requireUuid(label: string, value: string): string {\n const trimmed = value.trim();\n if (!isUuid(trimmed)) {\n throw new GenealogyError(`${label} \"${value}\" isn't a UUID.`);\n }\n return trimmed;\n}\n\n/** Parse a CLI number option; undefined stays undefined. */\nexport function toPositiveInt(\n name: string,\n raw: string | number | undefined,\n max?: number,\n): number | undefined {\n if (raw === undefined) return undefined;\n const value = typeof raw === \"number\" ? raw : Number(raw);\n if (!Number.isInteger(value) || value < 1 || (max != null && value > max)) {\n const range = max != null ? ` from 1 to ${max}` : \"\";\n throw new GenealogyError(`${name} must be a whole number${range}.`);\n }\n return value;\n}\n\nfunction requireDepth(raw: string | number | undefined): number {\n const depth = toPositiveInt(\"--depth\", raw);\n if (depth === undefined) {\n throw new GenealogyError(\n `--depth is required: a relative depth from 1 to ${MAX_DEPTH}.`,\n );\n }\n return depth;\n}\n\nfunction checkAsOf(asOf: string | undefined): string | undefined {\n if (asOf === undefined) return undefined;\n if (Number.isNaN(Date.parse(asOf))) {\n throw new GenealogyError(\n `--as-of \"${asOf}\" isn't an ISO 8601 timestamp, e.g. 2026-09-15T00:00:00Z.`,\n );\n }\n return asOf;\n}\n\n// --- Trees and member seats ------------------------------------------------\n\nexport async function listTrees(client: GenealogyClient): Promise<Tree[]> {\n const data = await client.get<{ trees?: Tree[] | null }>(path(\"trees\"));\n return data.trees ?? [];\n}\n\n/** A tree's identifiers, without its configuration. */\nexport interface TreeKey {\n id: string;\n key: string;\n name: string;\n}\n\n/** List the company's tree ids, keys and names. Unlike `listTrees`, this\n * needs only members.view, not developer.view. */\nexport async function listTreeKeys(\n client: GenealogyClient,\n): Promise<TreeKey[]> {\n const data = await client.get<{ trees?: TreeKey[] | null }>(\n path(\"tree-keys\"),\n );\n return data.trees ?? [];\n}\n\nconst isForbidden = (err: unknown) =>\n (err as { status?: unknown }).status === 403;\n\nconst isNotFound = (err: unknown) =>\n (err as { status?: unknown }).status === 404;\n\n/** Tree keys need members.view; a caller with only developer.view reads\n * them from the tree definitions instead. An API that predates the\n * tree-keys endpoint answers 404, and also falls back. */\nasync function lookUpTreeKeys(client: GenealogyClient): Promise<TreeKey[]> {\n try {\n return await listTreeKeys(client);\n } catch (err) {\n if (!isForbidden(err) && !isNotFound(err)) throw err;\n }\n try {\n const trees = await listTrees(client);\n return trees.map(({ id, key, name }) => ({ id, key, name: name ?? key }));\n } catch (err) {\n if (!isForbidden(err)) throw err;\n throw new GenealogyError(\n \"Looking up a tree by key needs members.view or developer.view. Pass --tree <tree-uuid> instead.\",\n );\n }\n}\n\n/** Resolve a tree key (e.g. \"placement\") or UUID to a tree UUID. Tree paths\n * only take UUIDs, so a key is looked up in the company's tree keys. */\nexport async function resolveTreeId(\n client: GenealogyClient,\n keyOrId: string,\n): Promise<string> {\n const trimmed = keyOrId.trim();\n if (isUuid(trimmed)) return trimmed;\n const trees = await lookUpTreeKeys(client);\n const match = trees.find((tree) => tree.key === trimmed);\n if (!match) {\n const keys = trees.map((tree) => tree.key).join(\", \") || \"none\";\n throw new GenealogyError(\n `No tree with key \"${trimmed}\" on this company. Trees: ${keys}.`,\n );\n }\n return match.id;\n}\n\n// --- Tree definitions (create, update, delete) -----------------------------\n\nexport const TREE_KINDS = [\"unilevel\", \"binary\", \"matrix\"] as const;\nexport const TREE_ROLES = [\"enrollment\", \"sponsor\"] as const;\nexport const TREE_AUTHORITIES = [\n \"authoritative\",\n \"replica\",\n \"unconfigured\",\n] as const;\nexport const MOVEMENT_APPROVALS = [\"immediate\", \"require_approval\"] as const;\nexport const VOLUME_ON_MOVE = [\n \"follows_structure\",\n \"stays_at_earning\",\n] as const;\n/** A tree's default rule: the placement rules plus the two role-tree rules. */\nexport const TREE_PLACEMENT_RULES = [\n \"exact\",\n \"first_empty\",\n \"left\",\n \"right\",\n \"inside\",\n \"outside\",\n \"balanced\",\n \"weak_leg\",\n \"strong_leg\",\n \"enroller_preference\",\n \"defer\",\n \"under_enroller\",\n \"mirror_enrollment\",\n] as const;\n\n/** Writable tree fields, as CLI flag values (strings until validated).\n * For a clearable field, the word `none` sends null. */\nexport interface TreeFieldsInput {\n key?: string;\n name?: string;\n kind?: string;\n role?: string;\n legNames?: string;\n placementRule?: string;\n movementApproval?: string;\n holdingTankDays?: string;\n maxDepth?: string;\n maxMovementLevels?: string;\n movementWindowDays?: string;\n volumeOnMove?: string;\n volumeKey?: string;\n allowMultiplePositions?: string;\n authority?: string;\n owningCompanyIntegrationId?: string;\n upstreamTreeRef?: string;\n externalIntegrationMarketId?: string;\n}\n\nconst NONE = \"none\";\n\nfunction oneOf(\n flag: string,\n value: string,\n allowed: readonly string[],\n): string {\n if (!allowed.includes(value)) {\n throw new GenealogyError(`${flag} must be one of ${allowed.join(\", \")}.`);\n }\n return value;\n}\n\nfunction wholeNumber(flag: string, raw: string, min: number): number {\n const value = Number(raw);\n if (raw.trim() === \"\" || !Number.isInteger(value) || value < min) {\n throw new GenealogyError(`${flag} must be a whole number, ${min} or more.`);\n }\n return value;\n}\n\nfunction bool(flag: string, raw: string): boolean {\n if (raw === \"true\") return true;\n if (raw === \"false\") return false;\n throw new GenealogyError(`${flag} must be true or false.`);\n}\n\nfunction legList(raw: string): string[] {\n const legs = raw.split(\",\").map((leg) => leg.trim());\n if (legs.some((leg) => leg === \"\") || new Set(legs).size !== legs.length) {\n throw new GenealogyError(\n \"--leg-names must be distinct, nonblank names separated by commas, e.g. left,right.\",\n );\n }\n return legs;\n}\n\n/** Build the `tree` object a create or update sends, validating each flag.\n * Only the flags given are sent. */\nexport function treeFields(input: TreeFieldsInput): Record<string, unknown> {\n const fields: Record<string, unknown> = {};\n const text = (flag: string, name: string, value: string | undefined) => {\n if (value === undefined) return;\n if (value.trim() === \"\")\n throw new GenealogyError(`${flag} can't be blank.`);\n fields[name] = value;\n };\n const clearable = (\n name: string,\n value: string | undefined,\n parse: (raw: string) => unknown,\n ) => {\n if (value === undefined) return;\n fields[name] = value === NONE ? null : parse(value);\n };\n\n text(\"--key\", \"key\", input.key);\n text(\"--name\", \"name\", input.name);\n if (input.kind !== undefined) {\n fields[\"kind\"] = oneOf(\"--kind\", input.kind, TREE_KINDS);\n }\n clearable(\"role\", input.role, (raw) => oneOf(\"--role\", raw, TREE_ROLES));\n if (input.legNames !== undefined) {\n fields[\"leg_names\"] =\n input.legNames === NONE ? [] : legList(input.legNames);\n }\n if (input.placementRule !== undefined) {\n fields[\"default_placement_rule\"] = oneOf(\n \"--placement-rule\",\n input.placementRule,\n TREE_PLACEMENT_RULES,\n );\n }\n if (input.movementApproval !== undefined) {\n fields[\"movement_approval\"] = oneOf(\n \"--movement-approval\",\n input.movementApproval,\n MOVEMENT_APPROVALS,\n );\n }\n if (input.maxDepth !== undefined) {\n fields[\"max_depth\"] = wholeNumber(\"--max-depth\", input.maxDepth, 1);\n }\n const counts: Array<[keyof TreeFieldsInput, string, string]> = [\n [\"holdingTankDays\", \"--holding-tank-days\", \"holding_tank_days\"],\n [\"movementWindowDays\", \"--movement-window-days\", \"movement_window_days\"],\n [\"maxMovementLevels\", \"--max-movement-levels\", \"max_movement_levels\"],\n ];\n for (const [prop, flag, name] of counts) {\n const raw = input[prop];\n if (raw !== undefined) fields[name] = wholeNumber(flag, raw, 0);\n }\n clearable(\"volume_on_move\", input.volumeOnMove, (raw) =>\n oneOf(\"--volume-on-move\", raw, VOLUME_ON_MOVE),\n );\n clearable(\"volume_key\", input.volumeKey, (raw) => raw);\n if (input.allowMultiplePositions !== undefined) {\n fields[\"allow_multiple_positions\"] = bool(\n \"--allow-multiple-positions\",\n input.allowMultiplePositions,\n );\n }\n if (input.authority !== undefined) {\n fields[\"authority\"] = oneOf(\n \"--authority\",\n input.authority,\n TREE_AUTHORITIES,\n );\n }\n clearable(\n \"owning_company_integration_id\",\n input.owningCompanyIntegrationId,\n (raw) => wholeNumber(\"--owning-company-integration-id\", raw, 1),\n );\n clearable(\"upstream_tree_ref\", input.upstreamTreeRef, (raw) => raw);\n clearable(\n \"external_integration_market_id\",\n input.externalIntegrationMarketId,\n (raw) => raw,\n );\n return fields;\n}\n\n/** The body for POST /trees. Checks what the API would refuse anyway, so a\n * mistake is named before anything is sent. */\nexport function createTreeBody(input: TreeFieldsInput): {\n tree: Record<string, unknown>;\n} {\n const fields = treeFields(input);\n const missing = (\n [\n [\"key\", \"--key\"],\n [\"name\", \"--name\"],\n [\"kind\", \"--kind\"],\n [\"authority\", \"--authority\"],\n ] as const\n )\n .filter(([name]) => fields[name] === undefined)\n .map(([, flag]) => flag);\n if (missing.length > 0) {\n throw new GenealogyError(\n `Creating a tree needs ${missing.join(\", \")}. --authority declares who owns the tree: authoritative (Fluid), replica (an integration's copy) or unconfigured (not yet decided; it takes no placements).`,\n );\n }\n const kind = fields[\"kind\"];\n const legs = fields[\"leg_names\"] as string[] | undefined;\n if (kind === \"unilevel\" && legs && legs.length > 0) {\n throw new GenealogyError(\"A unilevel tree has no legs; drop --leg-names.\");\n }\n if (kind === \"binary\" && legs && legs.length !== 2) {\n throw new GenealogyError(\n \"A binary tree has exactly two legs, e.g. --leg-names left,right (the default).\",\n );\n }\n if (kind === \"matrix\" && (!legs || legs.length < 2)) {\n throw new GenealogyError(\n \"A matrix tree needs --leg-names with two or more legs; their count is its width.\",\n );\n }\n if (kind !== \"unilevel\" && fields[\"volume_on_move\"] == null) {\n throw new GenealogyError(\n `A ${String(kind)} tree needs --volume-on-move ${VOLUME_ON_MOVE.join(\" or \")}.`,\n );\n }\n if (\n fields[\"authority\"] === \"replica\" &&\n fields[\"owning_company_integration_id\"] == null\n ) {\n throw new GenealogyError(\n \"A replica tree needs --owning-company-integration-id.\",\n );\n }\n return { tree: fields };\n}\n\n/** Readable restatements of the registry's write failures. */\nfunction treeWriteError(\n err: unknown,\n tree: string,\n method: TreeWrite[\"method\"],\n): unknown {\n const status = (err as { status?: unknown }).status;\n const body = (err as { body?: Record<string, unknown> | null }).body;\n const nested = body?.[\"error\"];\n const type =\n nested && typeof nested === \"object\"\n ? (nested as { details?: { type?: unknown } }).details?.type\n : undefined;\n if (typeof status !== \"number\" || typeof type !== \"string\") return err;\n const messages: Record<string, string> = {\n duplicate_key: `Another tree on this company already has the key of \"${tree}\". Tree keys are unique per company; pick another --key.`,\n role_taken:\n \"This company already has a tree with that role. Each role (enrollment, sponsor) has one tree.\",\n stale_tree: `Tree \"${tree}\" changed since its lock_version was read. Read it again with \"fluid genealogy trees\" and retry, or omit --lock-version to use the current one.`,\n tree_has_nodes:\n method === \"DELETE\"\n ? `Tree \"${tree}\" has seats, and delete refuses a tree with nodes. Remove its seats first.`\n : `Tree \"${tree}\" has seats, so only its --name can change; other fields need a structural migration.`,\n reserved_role:\n \"The default enrollment and placement trees can't be deleted or have their role changed.\",\n reserved_key:\n 'The keys \"enrollment\" and \"placement\" belong to the default role trees.',\n };\n const message = messages[type];\n return message ? new GenealogyError(message, { type, status }) : err;\n}\n\n/** A tree's full definition by key or UUID, from the tree list. */\nexport async function findTree(\n client: GenealogyClient,\n keyOrId: string,\n): Promise<Tree> {\n const wanted = keyOrId.trim();\n const trees = await listTrees(client);\n const match = trees.find((tree) =>\n isUuid(wanted) ? tree.id === wanted : tree.key === wanted,\n );\n if (!match) {\n const keys = trees.map((tree) => tree.key).join(\", \") || \"none\";\n throw new GenealogyError(\n `No tree \"${wanted}\" on this company. Trees: ${keys}.`,\n );\n }\n return match;\n}\n\n/** A write the CLI is about to send, for --dry-run and confirmation. */\nexport interface TreeWrite {\n method: \"POST\" | \"PATCH\" | \"DELETE\";\n path: string;\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport function planCreateTree(input: TreeFieldsInput): TreeWrite {\n return { method: \"POST\", path: path(\"trees\"), body: createTreeBody(input) };\n}\n\n/** Plan a PATCH: only the given fields, at the tree's lock_version (read\n * now unless --lock-version pins it). */\nexport async function planUpdateTree(\n client: GenealogyClient,\n keyOrId: string,\n input: TreeFieldsInput,\n lockVersion?: string,\n): Promise<TreeWrite> {\n const fields = treeFields(input);\n if (Object.keys(fields).length === 0) {\n throw new GenealogyError(\n \"Pass at least one field to change, e.g. --name or --movement-approval.\",\n );\n }\n const { id, version } = await treeVersion(client, keyOrId, lockVersion);\n return {\n method: \"PATCH\",\n path: path(\"trees\", id),\n body: { tree: { ...fields, lock_version: version } },\n };\n}\n\nexport async function planDeleteTree(\n client: GenealogyClient,\n keyOrId: string,\n lockVersion?: string,\n): Promise<TreeWrite> {\n const { id, version } = await treeVersion(client, keyOrId, lockVersion);\n return {\n method: \"DELETE\",\n path: path(\"trees\", id),\n params: { lock_version: version },\n };\n}\n\nasync function treeVersion(\n client: GenealogyClient,\n keyOrId: string,\n lockVersion: string | undefined,\n): Promise<{ id: string; version: number }> {\n const pinned =\n lockVersion === undefined\n ? undefined\n : wholeNumber(\"--lock-version\", lockVersion, 0);\n if (pinned !== undefined && isUuid(keyOrId.trim())) {\n return { id: keyOrId.trim(), version: pinned };\n }\n const tree = await findTree(client, keyOrId);\n const current = tree[\"lock_version\"];\n if (pinned === undefined && typeof current !== \"number\") {\n throw new GenealogyError(\n `Tree \"${keyOrId}\" was listed without a lock_version; pass --lock-version.`,\n );\n }\n return { id: tree.id, version: pinned ?? (current as number) };\n}\n\n/** Send a planned tree write. Returns the written tree, or a deletion\n * receipt. */\nexport async function sendTreeWrite(\n client: GenealogyClient,\n write: TreeWrite,\n label: string,\n): Promise<unknown> {\n try {\n if (write.method === \"POST\") {\n return withoutMeta(await client.post(write.path, write.body));\n }\n if (write.method === \"PATCH\") {\n return withoutMeta(await client.patch(write.path, write.body));\n }\n await client.delete(write.path, { params: write.params });\n return { deleted: true, tree: label, path: write.path };\n } catch (err) {\n throw treeWriteError(err, label, write.method);\n }\n}\n\n/** The tally a placement-tree mirror preview returns. */\nexport interface MirrorTally {\n tree_id: string;\n dry_run: boolean;\n counts: Record<string, number>;\n failures: Array<{ member_id: string; type: string; description: string }>;\n}\n\n/** A background placement mirror run and its progress. */\nexport interface MirrorRun {\n id: string;\n tree_id: string;\n status: MirrorRunStatus;\n counts: Record<string, number>;\n failures: Array<{ member_id: string; type: string; description: string }>;\n failure_type: string | null;\n failure_description: string | null;\n windows: number;\n attempts: number;\n requested_by_id: string;\n created_at: string;\n started_at: string | null;\n finished_at: string | null;\n}\n\nexport const MIRROR_RUN_STATUSES = [\n \"queued\",\n \"running\",\n \"succeeded\",\n \"failed\",\n \"halted\",\n] as const;\nexport type MirrorRunStatus = (typeof MIRROR_RUN_STATUSES)[number];\n\n/** Whether a run has finished, one way or another. */\nexport function mirrorRunFinished(run: MirrorRun): boolean {\n return [\"succeeded\", \"failed\", \"halted\"].includes(run.status);\n}\n\n/** Preview the mirror of the enrollment tree into the placement tree:\n * what would be seated and re-parented. Writes nothing. */\nexport async function previewMirror(\n client: GenealogyClient,\n treeId: string,\n): Promise<{ mirror: MirrorTally | null }> {\n const data = await client.post<{ mirror?: MirrorTally | null }>(\n path(\"trees\", treeId, \"mirror-enrollment\"),\n { mirror: { dry_run: true } },\n );\n return { mirror: data.mirror ?? null };\n}\n\n/** Queue a background run that mirrors the enrollment tree into the\n * placement tree, or get the run already active (`started: false`). */\nexport async function startMirror(\n client: GenealogyClient,\n treeId: string,\n): Promise<{ mirror_run: MirrorRun | null; started: boolean }> {\n const data = await client.post<{\n mirror_run?: MirrorRun | null;\n started?: boolean;\n }>(path(\"trees\", treeId, \"mirror-enrollment\"), {\n mirror: { dry_run: false },\n });\n return {\n mirror_run: data.mirror_run ?? null,\n started: data.started === true,\n };\n}\n\nexport async function getMirrorRun(\n client: GenealogyClient,\n id: string,\n): Promise<MirrorRun | null> {\n const data = await client.get<{ mirror_run?: MirrorRun | null }>(\n path(\"mirror-runs\", requireUuid(\"Mirror run\", id)),\n );\n return data.mirror_run ?? null;\n}\n\n/** The company's most recent mirror runs, newest first. */\nexport async function listMirrorRuns(\n client: GenealogyClient,\n options: { status?: string; limit?: number } = {},\n): Promise<{ mirror_runs: MirrorRun[] }> {\n if (\n options.status !== undefined &&\n !(MIRROR_RUN_STATUSES as readonly string[]).includes(options.status)\n ) {\n throw new GenealogyError(\n `--status must be one of ${MIRROR_RUN_STATUSES.join(\", \")}`,\n );\n }\n const params: Record<string, unknown> = {};\n if (options.status !== undefined) params[\"status\"] = options.status;\n if (options.limit !== undefined) params[\"limit\"] = options.limit;\n const data = await client.get<{ mirror_runs?: MirrorRun[] }>(\n path(\"mirror-runs\"),\n params,\n );\n return { mirror_runs: data.mirror_runs ?? [] };\n}\n\n/** Poll a run until it finishes, reporting each reading. */\nexport async function waitForMirrorRun(\n client: GenealogyClient,\n run: MirrorRun,\n options: {\n intervalMs: number;\n onProgress?: (run: MirrorRun) => void;\n sleep?: (ms: number) => Promise<void>;\n },\n): Promise<MirrorRun> {\n const sleep =\n options.sleep ??\n ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n let current = run;\n options.onProgress?.(current);\n while (!mirrorRunFinished(current)) {\n await sleep(options.intervalMs);\n const next = await getMirrorRun(client, current.id);\n if (!next) throw new GenealogyError(`Mirror run ${current.id} is gone`);\n current = next;\n options.onProgress?.(current);\n }\n return current;\n}\n\n/** Find a member's one seat in a tree. Seats are listed across every tree,\n * so the tree is filtered here; a member with several positions in the\n * tree must be addressed by node id. */\nexport async function resolveMemberNode(\n client: GenealogyClient,\n memberId: string,\n tree: string = DEFAULT_TREE,\n): Promise<string> {\n const member = requireUuid(\"Member\", memberId);\n const treeId = await resolveTreeId(client, tree);\n const { items } = await collectPages<GenealogyNode>(\n client,\n path(\"members\", member, \"nodes\"),\n {},\n \"nodes\",\n { maxPageSize: NODE_PAGE_MAX },\n );\n const seats = items.filter((node) => node.tree_id === treeId);\n if (seats.length === 0) {\n throw new GenealogyError(`Member ${member} has no seat in tree \"${tree}\".`);\n }\n if (seats.length > 1) {\n const listed = seats\n .map((seat) => `${seat.id} (position ${seat.position})`)\n .join(\", \");\n throw new GenealogyError(\n `Member ${member} has ${seats.length} seats in tree \"${tree}\": ${listed}. Pass the node id instead.`,\n );\n }\n return (seats[0] as GenealogyNode).id;\n}\n\n/** A command's subject: a node UUID, or --member resolved in --tree. */\nexport async function resolveSubject(\n client: GenealogyClient,\n node: string | undefined,\n member: string | undefined,\n tree: string = DEFAULT_TREE,\n): Promise<string> {\n if (node && member) {\n throw new GenealogyError(\"Pass a node id or --member, not both.\");\n }\n if (node) return requireUuid(\"Node\", node);\n if (member) return resolveMemberNode(client, member, tree);\n throw new GenealogyError(\"Pass a node id or --member <member-id>.\");\n}\n\n// --- Paging ----------------------------------------------------------------\n\nexport interface PageOptions {\n /** Total rows to return across pages. */\n limit?: number;\n /** Resume from a next_cursor printed by an earlier read. */\n cursor?: string;\n /** Rows per request; defaults to the endpoint's maximum. */\n pageSize?: number;\n}\n\nexport interface Collected<T, P> {\n items: T[];\n /** Set when more rows exist past --limit; pass it back as --cursor. */\n nextCursor: string | null;\n pages: P[];\n}\n\n/** Follow next_cursor until the list ends or --limit rows are collected.\n * A capped read reports the cursor to resume from; an uncapped read that\n * runs past MAX_PAGES fails rather than return part of the list. */\nexport async function collectPages<T, P extends PageWire = PageWire>(\n client: GenealogyClient,\n endpoint: string,\n params: Record<string, unknown>,\n itemsKey: string,\n options: PageOptions & { maxPageSize: number },\n): Promise<Collected<T, P>> {\n const pageSize =\n toPositiveInt(\"--page-size\", options.pageSize, options.maxPageSize) ??\n options.maxPageSize;\n const limit = toPositiveInt(\"--limit\", options.limit);\n const items: T[] = [];\n const pages: P[] = [];\n let cursor = options.cursor;\n for (let page = 1; page <= MAX_PAGES; page += 1) {\n const remaining = limit === undefined ? pageSize : limit - items.length;\n const data = await client.get<P>(endpoint, {\n ...params,\n limit: Math.min(pageSize, remaining),\n ...(cursor ? { cursor } : {}),\n });\n pages.push(data);\n const rows = (data as Record<string, unknown>)[itemsKey];\n if (Array.isArray(rows)) items.push(...(rows as T[]));\n const next = data.meta?.pagination?.next_cursor ?? null;\n if (!next) return { items, nextCursor: null, pages };\n if (limit !== undefined && items.length >= limit) {\n return { items, nextCursor: next, pages };\n }\n cursor = next;\n }\n throw new GenealogyError(\n `More than ${MAX_PAGES} pages; refusing to return a partial list. Pass --limit and resume with --cursor.`,\n );\n}\n\n// --- Node reads ------------------------------------------------------------\n\n/** Paged node reads and the query selectors each one accepts. The API\n * refuses selectors it does not list, so only these are sent. */\nconst NODE_PAGES = {\n children: { asOf: false, depth: false, idsOnly: true },\n siblings: { asOf: false, depth: false, idsOnly: true },\n frontline: { asOf: false, depth: false, idsOnly: true },\n \"personally-enrolled\": { asOf: false, depth: false, idsOnly: true },\n ancestors: { asOf: true, depth: false, idsOnly: false },\n descendants: { asOf: true, depth: true, idsOnly: true },\n subtree: { asOf: true, depth: true, idsOnly: true },\n level: { asOf: true, depth: true, idsOnly: true },\n} as const;\n\nexport type NodePageOperation = keyof typeof NODE_PAGES;\n\nexport interface NodePageOptions extends PageOptions {\n depth?: string | number;\n asOf?: string;\n idsOnly?: boolean;\n}\n\nexport interface NodePageResult {\n nodes?: GenealogyNode[];\n ids?: string[];\n next_cursor: string | null;\n /** frontline / personally-enrolled: the role-tree seat whose children\n * are listed. */\n node?: GenealogyNode | null;\n /** Deeper reads: rows exist beyond the selected depth. */\n depth_truncated?: boolean;\n}\n\nexport async function readNodePage(\n client: GenealogyClient,\n nodeId: string,\n operation: NodePageOperation,\n options: NodePageOptions = {},\n): Promise<NodePageResult> {\n const accepts = NODE_PAGES[operation];\n if (options.asOf !== undefined && !accepts.asOf) {\n throw new GenealogyError(`--as-of is not accepted by ${operation}.`);\n }\n if (options.idsOnly && !accepts.idsOnly) {\n throw new GenealogyError(`--ids-only is not accepted by ${operation}.`);\n }\n const params: Record<string, unknown> = {};\n if (accepts.depth) params[\"depth\"] = requireDepth(options.depth);\n const asOf = checkAsOf(options.asOf);\n if (asOf) params[\"as_of\"] = asOf;\n if (options.idsOnly) params[\"ids_only\"] = true;\n\n const itemsKey = options.idsOnly ? \"ids\" : \"nodes\";\n const { items, nextCursor, pages } = await collectPages<\n unknown,\n PageWire & { node?: GenealogyNode | null }\n >(client, path(\"nodes\", nodeId, operation), params, itemsKey, {\n ...options,\n maxPageSize: NODE_PAGE_MAX,\n });\n\n const result: NodePageResult = { [itemsKey]: items, next_cursor: nextCursor };\n if (operation === \"frontline\" || operation === \"personally-enrolled\") {\n result.node = pages[0]?.node ?? null;\n }\n if (accepts.depth) {\n result.depth_truncated = pages.some(\n (page) => page.meta?.traversal?.depth_truncated === true,\n );\n }\n return result;\n}\n\n/** Grandchildren are the nodes exactly two levels down. */\nexport function readGrandchildren(\n client: GenealogyClient,\n nodeId: string,\n options: Omit<NodePageOptions, \"depth\"> = {},\n): Promise<NodePageResult> {\n return readNodePage(client, nodeId, \"level\", { ...options, depth: 2 });\n}\n\n/** One node; current reads include children_count and downline_count,\n * as_of reads return the recorded placement without counts. */\nexport async function readNode(\n client: GenealogyClient,\n nodeId: string,\n asOf?: string,\n): Promise<{ node: NodeRelationship | null }> {\n const at = checkAsOf(asOf);\n const data = await client.get<NodeResponse<NodeRelationship>>(\n path(\"nodes\", nodeId),\n at ? { as_of: at } : undefined,\n );\n return { node: data.node ?? null };\n}\n\nexport interface NodeCounts {\n node_id: string;\n children_count: number;\n downline_count: number;\n counts_as_of: string | null;\n}\n\n/** Structural counts. They include retained seats of discarded members,\n * so they can exceed what the page reads list. */\nexport async function readCounts(\n client: GenealogyClient,\n nodeId: string,\n): Promise<NodeCounts> {\n const { node } = await readNode(client, nodeId);\n if (\n !node ||\n typeof node.children_count !== \"number\" ||\n typeof node.downline_count !== \"number\"\n ) {\n throw new GenealogyError(`The read of node ${nodeId} returned no counts.`);\n }\n return {\n node_id: node.id,\n children_count: node.children_count,\n downline_count: node.downline_count,\n counts_as_of: node.counts_as_of ?? null,\n };\n}\n\nexport type RelativeOperation = \"parent\" | \"root\" | \"sponsor\" | \"enroller\";\n\n/** parent and root take as_of; sponsor and enroller take no selectors.\n * The node is null for a root (and for a role-tree root). */\nexport async function readRelative(\n client: GenealogyClient,\n nodeId: string,\n operation: RelativeOperation,\n asOf?: string,\n): Promise<{ node: GenealogyNode | null }> {\n if (\n asOf !== undefined &&\n (operation === \"sponsor\" || operation === \"enroller\")\n ) {\n throw new GenealogyError(`--as-of is not accepted by ${operation}.`);\n }\n const at = checkAsOf(asOf);\n const data = await client.get<NodeResponse<GenealogyNode>>(\n path(\"nodes\", nodeId, operation),\n at ? { as_of: at } : undefined,\n );\n return { node: data.node ?? null };\n}\n\nexport const RELATION_KINDS = [\n \"ancestor_of\",\n \"descendant_of\",\n \"parent_of\",\n \"child_of\",\n \"sibling_of\",\n \"root_of\",\n \"indirect_of\",\n \"in_subtree_of\",\n \"within_downline\",\n] as const;\n\nexport type RelationKind = (typeof RELATION_KINDS)[number];\n\n/** Answer how node relates to other: one kind, or every kind when none is\n * given. For within_downline, other is the downline's root. */\nexport async function readRelation(\n client: GenealogyClient,\n nodeId: string,\n otherId: string,\n kind?: string,\n): Promise<{\n node_id: string;\n other: string;\n relations: Partial<Record<RelationKind, boolean>>;\n}> {\n const other = requireUuid(\"Other node\", otherId);\n if (\n kind !== undefined &&\n !(RELATION_KINDS as readonly string[]).includes(kind)\n ) {\n throw new GenealogyError(\n `--kind must be one of ${RELATION_KINDS.join(\", \")}.`,\n );\n }\n const kinds = kind ? [kind as RelationKind] : [...RELATION_KINDS];\n const answers = await mapLimit(kinds, CONCURRENCY, async (k) => {\n const data = await client.get<{\n relation?: { kind: RelationKind; answer: boolean };\n }>(path(\"nodes\", nodeId, \"relation\"), { other, kind: k });\n return [k, data.relation?.answer ?? false] as const;\n });\n return { node_id: nodeId, other, relations: Object.fromEntries(answers) };\n}\n\nexport async function readLevelCounts(\n client: GenealogyClient,\n nodeId: string,\n depth: string | number | undefined,\n): Promise<{\n node_id: string;\n levels: Array<{ distance: number; count: number }>;\n counts_as_of: string | null;\n depth_truncated: boolean;\n}> {\n const data = await client.get<{\n levels?: Array<{ distance: number; count: number }>;\n meta?: { traversal?: { counts_as_of?: string; depth_truncated?: boolean } };\n }>(path(\"nodes\", nodeId, \"level-counts\"), { depth: requireDepth(depth) });\n return {\n node_id: nodeId,\n levels: data.levels ?? [],\n counts_as_of: data.meta?.traversal?.counts_as_of ?? null,\n depth_truncated: data.meta?.traversal?.depth_truncated ?? false,\n };\n}\n\n// --- History, holding tank, move receipts ----------------------------------\n\nexport async function readPlacements(\n client: GenealogyClient,\n nodeId: string,\n options: PageOptions = {},\n): Promise<{ placements: unknown[]; next_cursor: string | null }> {\n const { items, nextCursor } = await collectPages(\n client,\n path(\"nodes\", nodeId, \"placements\"),\n {},\n \"placements\",\n { ...options, maxPageSize: HISTORY_PAGE_MAX },\n );\n return { placements: items, next_cursor: nextCursor };\n}\n\nexport async function readHoldingTank(\n client: GenealogyClient,\n treeId: string,\n options: PageOptions = {},\n): Promise<{ nodes: unknown[]; next_cursor: string | null }> {\n const { items, nextCursor } = await collectPages(\n client,\n path(\"trees\", treeId, \"holding-tank\"),\n {},\n \"nodes\",\n { ...options, maxPageSize: HISTORY_PAGE_MAX },\n );\n return { nodes: items, next_cursor: nextCursor };\n}\n\nexport async function listMoveRequests(\n client: GenealogyClient,\n treeId: string,\n status?: string,\n options: PageOptions = {},\n): Promise<{ move_requests: unknown[]; next_cursor: string | null }> {\n const { items, nextCursor } = await collectPages(\n client,\n path(\"trees\", treeId, \"move-requests\"),\n status ? { status } : {},\n \"move_requests\",\n { ...options, maxPageSize: HISTORY_PAGE_MAX },\n );\n return { move_requests: items, next_cursor: nextCursor };\n}\n\nexport async function listMoveJobs(\n client: GenealogyClient,\n treeId: string,\n status?: string,\n options: PageOptions = {},\n): Promise<{ move_jobs: unknown[]; next_cursor: string | null }> {\n const { items, nextCursor } = await collectPages(\n client,\n path(\"trees\", treeId, \"move-jobs\"),\n status ? { status } : {},\n \"move_jobs\",\n { ...options, maxPageSize: HISTORY_PAGE_MAX },\n );\n return { move_jobs: items, next_cursor: nextCursor };\n}\n\nexport async function getMoveRequest(\n client: GenealogyClient,\n id: string,\n): Promise<unknown> {\n const data = await client.get<{ move_request?: unknown }>(\n path(\"move-requests\", requireUuid(\"Move request\", id)),\n );\n return data.move_request ?? null;\n}\n\nexport async function getMoveJob(\n client: GenealogyClient,\n id: string,\n): Promise<unknown> {\n const data = await client.get<{ move_job?: unknown }>(\n path(\"move-jobs\", requireUuid(\"Move job\", id)),\n );\n return data.move_job ?? null;\n}\n\n// --- Legs (headcount) ------------------------------------------------------\n\nexport interface LegEntry {\n leg: string | null;\n node_id: string | null;\n member_id: string | null;\n downline_count: number;\n /** The child's own seat plus its downline_count; 0 for an empty leg. */\n seats: number;\n counts_as_of: string | null;\n /** The one direct child the children read left out, sized from the\n * node's counts; its node and member are not known. */\n unlisted?: true;\n}\n\nexport const LEG_MEASURE = \"headcount\";\nexport const LEG_COUNTED = \"seats: child plus downline_count\";\nconst LEG_NOTE =\n \"Headcount only; the genealogy API has no leg volume. Counts include retained seats of discarded members.\";\n\n/** Direct children the children read leaves out: seats of discarded\n * members, which the node's structural counts still include. */\nexport interface UnlistedLegs {\n children: number;\n /** Seats under them, from the node's downline_count; at least children. */\n seats: number;\n /** True when legs includes them: a single unlisted child is exactly\n * `seats`, so it is ranked like any other leg. */\n ranked: boolean;\n /** Named legs that are empty but cannot be told apart from the legs the\n * unlisted children hold. */\n unknown_empty_legs: number;\n}\n\nexport interface LegsReport {\n measure: typeof LEG_MEASURE;\n counted: typeof LEG_COUNTED;\n node_id: string;\n tree_id: string | null;\n legs: LegEntry[];\n /** Set when some direct children could not be listed. */\n unlisted?: UnlistedLegs;\n /** False when the tree's leg names could not be read (it needs\n * developer.view), so empty named legs are not known. */\n leg_names_known: boolean;\n note: string;\n}\n\n/** Largest first; ties keep the tree's leg order, then node id. */\nexport function rankLegs(\n entries: LegEntry[],\n legNames: string[] = [],\n): LegEntry[] {\n const order = (leg: string | null): number => {\n const index = leg === null ? -1 : legNames.indexOf(leg);\n return index === -1 ? legNames.length : index;\n };\n return [...entries].sort(\n (a, b) =>\n b.seats - a.seats ||\n order(a.leg) - order(b.leg) ||\n (a.node_id ?? \"\").localeCompare(b.node_id ?? \"\"),\n );\n}\n\n/** The genealogy API has no per-leg count endpoint, so `legs` reads each\n * child's counts separately. Above this many children it refuses rather\n * than silently making one request per child. */\nexport const DEFAULT_LEG_CHILD_LIMIT = 200;\n\nexport interface LegsOptions {\n /** Most direct children to count; more is an error. */\n maxChildren?: number;\n}\n\n/** Each direct child as a leg with its headcount, largest first. Named legs\n * of a binary or matrix tree with no child are listed with 0 seats. */\nexport async function readLegs(\n client: GenealogyClient,\n nodeId: string,\n options: LegsOptions = {},\n): Promise<LegsReport> {\n const maxChildren = options.maxChildren ?? DEFAULT_LEG_CHILD_LIMIT;\n const [trees, settled] = await Promise.all([\n // Leg names only add empty named legs. Listing trees needs\n // developer.view, so without it the occupied legs are still ranked.\n listTrees(client).catch((err: unknown) => {\n if ((err as { status?: unknown }).status === 403) return null;\n throw err;\n }),\n readSettledLegs(client, nodeId, maxChildren),\n ]);\n const { anchor, counted } = settled;\n const treeId = anchor?.tree_id ?? null;\n const legNames = trees?.find((tree) => tree.id === treeId)?.leg_names ?? [];\n const occupied = new Set(counted.map((entry) => entry.leg));\n const free = legNames.filter((leg) => !occupied.has(leg));\n\n const hidden = hiddenChildren(anchor, counted);\n let unlisted: UnlistedLegs | undefined;\n const entries = [...counted];\n if (hidden) {\n // A binary or matrix leg holds one seat, so the unlisted children hold\n // free legs; with as many free legs as unlisted children, none is empty.\n const unknownEmpty = Math.max(0, free.length - hidden.children);\n const ranked = hidden.children === 1;\n if (ranked) {\n entries.push({\n leg: free.length === 1 ? (free[0] ?? null) : null,\n node_id: null,\n member_id: null,\n downline_count: hidden.seats - 1,\n seats: hidden.seats,\n counts_as_of: anchor?.counts_as_of ?? null,\n unlisted: true,\n });\n }\n unlisted = { ...hidden, ranked, unknown_empty_legs: unknownEmpty };\n } else {\n for (const leg of free) {\n entries.push({\n leg,\n node_id: null,\n member_id: null,\n downline_count: 0,\n seats: 0,\n counts_as_of: null,\n });\n }\n }\n\n return {\n measure: LEG_MEASURE,\n counted: LEG_COUNTED,\n node_id: nodeId,\n tree_id: treeId,\n legs: rankLegs(entries, legNames),\n ...(unlisted ? { unlisted } : {}),\n leg_names_known: trees !== null,\n note: [\n LEG_NOTE,\n unlisted ? unlistedNote(unlisted) : null,\n trees === null\n ? \"Empty named legs are not listed: reading leg names needs developer.view.\"\n : null,\n ]\n .filter(Boolean)\n .join(\" \"),\n };\n}\n\nfunction unlistedNote(unlisted: UnlistedLegs): string {\n const { children, seats } = unlisted;\n const what = `${children} direct ${children === 1 ? \"child holds\" : \"children hold\"} a discarded member's seat, which the children read leaves out`;\n if (unlisted.ranked) {\n return `${what}; it is listed with unlisted: true and its ${seats} ${seats === 1 ? \"seat\" : \"seats\"} from the node's counts.${unlisted.unknown_empty_legs > 0 ? \" Empty named legs are not listed, since one of them is the leg it holds.\" : \"\"}`;\n }\n return `${what}; their ${seats} seats are not ranked, and empty named legs are not listed because one may hold them.`;\n}\n\n/** Reads that bracket the leg reads and disagree mean the tree changed\n * meanwhile; read again this many times. */\nconst LEG_READ_ATTEMPTS = 3;\n\ninterface SettledLegs {\n anchor: NodeRelationship | null;\n counted: LegEntry[];\n}\n\n/** The node's children and each child's counts, read twice between reads\n * of the node's counts, accepted only when every read agrees. The API has\n * no read that returns them together, so agreement is what shows that no\n * child moved in, out or between legs while they were read. */\nasync function readSettledLegs(\n client: GenealogyClient,\n nodeId: string,\n maxChildren: number,\n): Promise<SettledLegs> {\n for (let attempt = 1; ; attempt += 1) {\n const { node: before } = await readNode(client, nodeId);\n const first = await readLegPass(client, nodeId, maxChildren);\n const second = await readLegPass(client, nodeId, maxChildren);\n const { node: after } = await readNode(client, nodeId);\n if (sameCounts(before, after) && legKey(first) === legKey(second)) {\n return { anchor: after, counted: second };\n }\n if (attempt >= LEG_READ_ATTEMPTS) {\n throw new GenealogyError(\n `Node ${nodeId}'s legs changed during each of ${LEG_READ_ATTEMPTS} reads, so they could not be ranked. Try again.`,\n );\n }\n }\n}\n\n/** One read of the children and of each child's counts. */\nasync function readLegPass(\n client: GenealogyClient,\n nodeId: string,\n maxChildren: number,\n): Promise<LegEntry[]> {\n const page = await collectPages<GenealogyNode>(\n client,\n path(\"nodes\", nodeId, \"children\"),\n {},\n \"nodes\",\n {\n // One extra row tells us the cap was exceeded without paging further.\n limit: maxChildren + 1,\n maxPageSize: NODE_PAGE_MAX,\n },\n );\n if (page.items.length > maxChildren) {\n throw new GenealogyError(\n `Node ${nodeId} has more than ${maxChildren} direct children, and ranking legs reads each child's counts with its own request. Pass --limit <n> to count up to n children.`,\n );\n }\n return mapLimit(page.items, CONCURRENCY, (child) =>\n readCounts(client, child.id).then(\n (counts): LegEntry => ({\n leg: child.leg,\n node_id: child.id,\n member_id: child.member_id,\n downline_count: counts.downline_count,\n seats: counts.downline_count + 1,\n counts_as_of: counts.counts_as_of,\n }),\n ),\n );\n}\n\nfunction sameCounts(\n a: NodeRelationship | null | undefined,\n b: NodeRelationship | null | undefined,\n): boolean {\n return (\n a?.children_count === b?.children_count &&\n a?.downline_count === b?.downline_count\n );\n}\n\n/** Who holds which leg and how many seats, in a stable order. */\nfunction legKey(entries: LegEntry[]): string {\n return entries\n .map((entry) => `${entry.node_id}:${entry.leg}:${entry.seats}`)\n .sort()\n .join(\"|\");\n}\n\n/** The node's structural counts include seats of discarded members, which\n * the children read omits; the difference is what was left out. */\nfunction hiddenChildren(\n anchor: NodeRelationship | null | undefined,\n counted: LegEntry[],\n): { children: number; seats: number } | undefined {\n if (typeof anchor?.children_count !== \"number\") return undefined;\n const children = anchor.children_count - counted.length;\n if (children <= 0) return undefined;\n const rankedSeats = counted.reduce((sum, entry) => sum + entry.seats, 0);\n const seats =\n typeof anchor.downline_count === \"number\"\n ? Math.max(children, anchor.downline_count - rankedSeats)\n : children;\n return { children, seats };\n}\n\nexport interface LegPick {\n measure: typeof LEG_MEASURE;\n counted: typeof LEG_COUNTED;\n node_id: string;\n /** Null when there are no legs, or when unlisted seats could change it. */\n leg: LegEntry | null;\n /** Other legs with the same headcount, if any. */\n tied_with: LegEntry[];\n /** Set when some legs could not be listed. */\n unlisted?: UnlistedLegs;\n /** With no certain pick: the pick among the listed legs. */\n best_listed?: LegEntry | null;\n note: string;\n}\n\n/** The strong leg is the largest by headcount, the weak leg the smallest.\n * With unlisted children a leg is named only when they cannot change the\n * pick: one unlisted child is ranked with its exact size; with more, only\n * a strong leg that outnumbers all their seats together is certain. A\n * weak pick is withheld while an empty leg cannot be named, including\n * when the tree's leg names could not be read. */\nexport function pickLeg(report: LegsReport, which: \"strong\" | \"weak\"): LegPick {\n const legs = which === \"strong\" ? report.legs : [...report.legs].reverse();\n const top = legs[0] ?? null;\n const base = {\n measure: report.measure,\n counted: report.counted,\n node_id: report.node_id,\n };\n const tiedWith = (leg: LegEntry) =>\n legs.slice(1).filter((other) => other.seats === leg.seats);\n const { unlisted } = report;\n // An empty leg has no seats, so it never changes the strong leg; it is the\n // weak leg whenever there is one, so a weak pick needs every leg known.\n const certain =\n which === \"strong\"\n ? !unlisted ||\n unlisted.ranked ||\n (top !== null && top.seats > unlisted.seats)\n : report.leg_names_known &&\n (!unlisted || (unlisted.ranked && unlisted.unknown_empty_legs === 0));\n if (certain) {\n return {\n ...base,\n leg: top,\n tied_with: top ? tiedWith(top) : [],\n ...(unlisted ? { unlisted } : {}),\n note: top ? report.note : \"This node has no legs.\",\n };\n }\n const reason = !report.leg_names_known\n ? \"the tree's leg names could not be read, so an empty named leg may be smaller\"\n : unlisted?.ranked\n ? \"an empty named leg is smaller, but it cannot be told from the leg the unlisted child holds\"\n : \"it may be one that is not listed\";\n return {\n ...base,\n leg: null,\n tied_with: [],\n ...(unlisted ? { unlisted } : {}),\n best_listed: top,\n note: `${report.note} The ${which} leg cannot be told: ${reason}.${top ? \" best_listed is the pick among the listed legs.\" : \"\"}`,\n };\n}\n\n// --- Downline tree text ----------------------------------------------------\n\n/** Read the subtree to depth (1-7) for `tree`; every page is followed. */\nexport async function readTree(\n client: GenealogyClient,\n nodeId: string,\n depth: string | number | undefined,\n): Promise<{ nodes: GenealogyNode[]; depthTruncated: boolean; depth: number }> {\n const bounded = toPositiveInt(\"--depth\", depth ?? 3, MAX_DEPTH) as number;\n const result = await readNodePage(client, nodeId, \"subtree\", {\n depth: bounded,\n });\n return {\n nodes: result.nodes ?? [],\n depthTruncated: result.depth_truncated ?? false,\n depth: bounded,\n };\n}\n\nfunction label(node: GenealogyNode): string {\n const leg = node.leg ? ` [${node.leg}]` : \"\";\n return `member ${node.member_id}${leg} (node ${node.id})`;\n}\n\n/** Indent a subtree page (anchor at relative depth 0) as a text tree. */\nexport function renderTree(\n nodes: GenealogyNode[],\n depthTruncated: boolean,\n depth: number,\n): string {\n const anchor = nodes.find((node) => node.relative_depth === 0) ?? nodes[0];\n if (!anchor) return \"(no nodes)\\n\";\n const children = new Map<string, GenealogyNode[]>();\n for (const node of nodes) {\n if (node === anchor || !node.parent_node_id) continue;\n const siblings = children.get(node.parent_node_id) ?? [];\n siblings.push(node);\n children.set(node.parent_node_id, siblings);\n }\n const lines = [label(anchor)];\n const walk = (parentId: string, prefix: string): void => {\n const kids = children.get(parentId) ?? [];\n kids.forEach((kid, index) => {\n const last = index === kids.length - 1;\n lines.push(`${prefix}${last ? \"└── \" : \"├── \"}${label(kid)}`);\n walk(kid.id, `${prefix}${last ? \" \" : \"│ \"}`);\n });\n };\n walk(anchor.id, \"\");\n if (depthTruncated) {\n lines.push(`… more seats below depth ${depth} (depth_truncated)`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\n// --- Writes ----------------------------------------------------------------\n\nexport const PLACEMENT_RULES = [\n \"exact\",\n \"first_empty\",\n \"left\",\n \"right\",\n \"inside\",\n \"outside\",\n \"balanced\",\n \"weak_leg\",\n \"strong_leg\",\n \"enroller_preference\",\n \"defer\",\n] as const;\n\nexport interface PlacementInput {\n /** Parent seat, or where a rule's search starts; null places a root. */\n parent?: string | null;\n leg?: string;\n rule?: string;\n reason?: string;\n}\n\nfunction placementFields(input: PlacementInput): Record<string, unknown> {\n if (\n input.rule !== undefined &&\n !(PLACEMENT_RULES as readonly string[]).includes(input.rule)\n ) {\n throw new GenealogyError(\n `--rule must be one of ${PLACEMENT_RULES.join(\", \")}.`,\n );\n }\n const fields: Record<string, unknown> = {};\n if (input.parent !== undefined) {\n fields[\"parent_node_id\"] =\n input.parent === null ? null : requireUuid(\"Parent node\", input.parent);\n }\n if (input.leg !== undefined) fields[\"leg\"] = input.leg;\n if (input.rule !== undefined) fields[\"rule\"] = input.rule;\n if (input.reason !== undefined) fields[\"reason\"] = input.reason;\n return fields;\n}\n\nfunction withoutMeta(data: unknown): unknown {\n if (data && typeof data === \"object\") {\n const { meta: _meta, ...rest } = data as Record<string, unknown>;\n return rest;\n }\n return data;\n}\n\n/** Place a seat waiting in the holding tank. */\nexport async function placeHeldSeat(\n client: GenealogyClient,\n nodeId: string,\n input: PlacementInput,\n): Promise<unknown> {\n if (input.rule === \"defer\") {\n throw new GenealogyError(\"A held seat can't be deferred again.\");\n }\n if (input.parent === undefined) {\n throw new GenealogyError(\"Pass --parent <node> (or --root) for the seat.\");\n }\n const data = await client.post(path(\"nodes\", nodeId, \"place\"), {\n placement: placementFields(input),\n });\n return withoutMeta(data);\n}\n\n/** Give a member a new seat in a binary or matrix tree, or hold it in the\n * holding tank with the rule defer. */\nexport async function placeMember(\n client: GenealogyClient,\n treeId: string,\n memberId: string,\n input: PlacementInput & { position?: number },\n): Promise<unknown> {\n const member = requireUuid(\"Member\", memberId);\n const position = input.position;\n if (\n position !== undefined &&\n !(Number.isInteger(position) && position >= 0)\n ) {\n throw new GenealogyError(\"--position must be a whole number, 0 or more.\");\n }\n if (input.rule === \"defer\") {\n if (\n input.parent !== undefined ||\n input.leg !== undefined ||\n input.reason !== undefined\n ) {\n throw new GenealogyError(\n \"The rule defer takes no --parent, --root, --leg or --reason; give the reason when the held seat is placed.\",\n );\n }\n } else if (input.parent === undefined) {\n throw new GenealogyError(\n \"Pass --parent <node> (or --root), or --rule defer to hold the seat.\",\n );\n }\n const data = await client.post(path(\"trees\", treeId, \"nodes\"), {\n node: {\n member_id: member,\n ...placementFields(input),\n ...(position !== undefined ? { position } : {}),\n },\n });\n return withoutMeta(data);\n}\n\nexport interface MoveInput {\n /** New parent node, or null for a root move. */\n parent: string | null;\n leg?: string;\n reason?: string;\n}\n\nfunction moveBody(input: MoveInput): { move: Record<string, unknown> } {\n return {\n move: {\n parent_node_id:\n input.parent === null ? null : requireUuid(\"Parent node\", input.parent),\n ...(input.leg !== undefined ? { leg: input.leg } : {}),\n ...(input.reason !== undefined ? { reason: input.reason } : {}),\n },\n };\n}\n\n/** Preview a move; writes and enqueues nothing. */\nexport async function validateMove(\n client: GenealogyClient,\n nodeId: string,\n input: MoveInput,\n): Promise<unknown> {\n const data = await client.post<{ validation?: unknown }>(\n path(\"nodes\", nodeId, \"move\", \"validate\"),\n moveBody(input),\n );\n return { validation: data.validation ?? null };\n}\n\n/** Request a move. It applies now (status 200), or returns a move request\n * awaiting approval or a deferred move job (status 202). */\nexport async function requestMove(\n client: GenealogyClient,\n nodeId: string,\n input: MoveInput,\n idempotencyKey?: string,\n): Promise<unknown> {\n const data = await client.post(\n path(\"nodes\", nodeId, \"move\"),\n moveBody(input),\n idempotencyKey\n ? { headers: { \"Idempotency-Key\": idempotencyKey } }\n : undefined,\n );\n return withoutMeta(data);\n}\n\nexport async function approveMoveRequest(\n client: GenealogyClient,\n id: string,\n reason?: string,\n): Promise<unknown> {\n const data = await client.post(\n path(\"move-requests\", requireUuid(\"Move request\", id), \"approve\"),\n reason !== undefined ? { decision: { reason } } : undefined,\n );\n return withoutMeta(data);\n}\n\nexport async function rejectMoveRequest(\n client: GenealogyClient,\n id: string,\n reason: string | undefined,\n): Promise<unknown> {\n if (!reason || !/\\S/.test(reason)) {\n throw new GenealogyError(\"Rejecting a move request needs --reason <text>.\");\n }\n const data = await client.post(\n path(\"move-requests\", requireUuid(\"Move request\", id), \"reject\"),\n { decision: { reason } },\n );\n return withoutMeta(data);\n}\n\n// --- Helpers ---------------------------------------------------------------\n\n/** Map with at most `limit` calls in flight, keeping input order. */\nexport async function mapLimit<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = [];\n let next = 0;\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const index = next;\n next += 1;\n results[index] = await fn(items[index] as T);\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, worker),\n );\n return results;\n}\n","import type { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport type { CommandContext, FluidPlugin } from \"@fluid-app/fluid-cli\";\nimport { createDomainCommand } from \"@fluid-app/fluid-cli\";\n\nimport {\n approveMoveRequest,\n DEFAULT_LEG_CHILD_LIMIT,\n DEFAULT_TREE,\n GenealogyError,\n getMoveJob,\n getMoveRequest,\n getMirrorRun,\n listMirrorRuns,\n listMoveJobs,\n listMoveRequests,\n listTrees,\n MAX_DEPTH,\n mirrorRunFinished,\n previewMirror,\n startMirror,\n waitForMirrorRun,\n type MirrorRun,\n MOVEMENT_APPROVALS,\n planCreateTree,\n planDeleteTree,\n planUpdateTree,\n sendTreeWrite,\n TREE_AUTHORITIES,\n TREE_KINDS,\n TREE_PLACEMENT_RULES,\n TREE_ROLES,\n VOLUME_ON_MOVE,\n type TreeFieldsInput,\n type TreeWrite,\n pickLeg,\n placeHeldSeat,\n placeMember,\n readCounts,\n readGrandchildren,\n readHoldingTank,\n readLegs,\n readLevelCounts,\n readNode,\n readNodePage,\n readPlacements,\n readRelation,\n readRelative,\n readTree,\n rejectMoveRequest,\n renderTree,\n requestMove,\n resolveSubject,\n resolveTreeId,\n toPositiveInt,\n validateMove,\n type GenealogyClient,\n type LegsOptions,\n type NodePageOperation,\n type PageOptions,\n type RelativeOperation,\n} from \"./api.js\";\n\nasync function client(ctx: CommandContext): Promise<GenealogyClient> {\n return (await ctx.getClient()) as unknown as GenealogyClient;\n}\n\n/**\n * Placements and moves change the live tree and need an explicit yes.\n * Scripts and agents pass `--yes`; an interactive terminal is asked. With\n * neither, nothing runs.\n */\nexport async function confirmChange(\n question: string,\n yes: boolean,\n interactive: boolean = Boolean(process.stdin.isTTY),\n): Promise<void> {\n if (yes) return;\n if (!interactive) {\n throw new GenealogyError(`${question} Pass --yes to confirm.`);\n }\n const answer = await prompts({\n type: \"confirm\",\n name: \"ok\",\n message: question,\n initial: false,\n });\n if (!answer.ok) throw new GenealogyError(\"Cancelled.\");\n}\n\n/** Print a failure as JSON on stderr and exit non-zero, so scripts and\n * agents can read it the same way as a success. Genealogy errors carry\n * `error.message` and a machine-readable `error.details.type`. */\nfunction fail(err: unknown): void {\n const status =\n typeof (err as { status?: unknown }).status === \"number\"\n ? (err as { status: number }).status\n : undefined;\n const body = (err as { body?: Record<string, unknown> | null }).body;\n const nested = body?.[\"error\"] as\n | { message?: unknown; details?: { type?: unknown } }\n | string\n | undefined;\n const candidates = [\n body?.[\"error_message\"],\n typeof nested === \"object\" ? nested?.message : nested,\n body?.[\"message\"],\n ];\n const fromBody = candidates.find((c): c is string => typeof c === \"string\");\n // A GenealogyError restating an API failure carries the readable message.\n const restated = err instanceof GenealogyError;\n const message = restated\n ? err.message\n : (fromBody ?? (err instanceof Error ? err.message : String(err)));\n const details =\n typeof nested === \"object\" && nested?.details\n ? (nested.details as Record<string, unknown>)\n : undefined;\n const type =\n restated && err.type\n ? err.type\n : typeof details?.[\"type\"] === \"string\"\n ? details[\"type\"]\n : undefined;\n // Validation failures (\"Invalid parameters\") explain themselves only in\n // their details, e.g. { node: [\"a placement rule chooses the leg ...\"] }.\n const { type: _type, ...reasons } = details ?? {};\n process.stderr.write(\n JSON.stringify({\n error: message,\n ...(type ? { type } : {}),\n ...(status ? { status } : {}),\n ...(Object.keys(reasons).length > 0 ? { details: reasons } : {}),\n }) + \"\\n\",\n );\n process.exitCode = 1;\n}\n\n/** One stderr line per reading while --wait follows a mirror run. */\nfunction reportMirrorProgress(mirrorRun: MirrorRun): void {\n const counts = Object.entries(mirrorRun.counts)\n .map(([outcome, n]) => `${outcome} ${n}`)\n .join(\", \");\n const state = mirrorRunFinished(mirrorRun) ? \"finished\" : \"progress\";\n process.stderr.write(\n `mirror run ${mirrorRun.id} ${state}: ${mirrorRun.status}, window ${mirrorRun.windows}${counts ? `, ${counts}` : \"\"}\\n`,\n );\n}\n\nfunction run(action: () => Promise<void>): Promise<void> {\n return action().catch(fail);\n}\n\ninterface SubjectOpts {\n member?: string;\n tree: string;\n}\n\ninterface PagingOpts {\n limit?: string;\n cursor?: string;\n pageSize?: string;\n}\n\nfunction paging(opts: PagingOpts): PageOptions {\n return {\n limit: toPositiveInt(\"--limit\", opts.limit),\n pageSize: toPositiveInt(\"--page-size\", opts.pageSize),\n cursor: opts.cursor,\n };\n}\n\n/** A command whose subject is `[node]` or `--member` in `--tree`. */\nfunction subjectCommand(parent: Command, name: string, description: string) {\n return parent\n .command(`${name} [node]`)\n .description(description)\n .option(\n \"--member <member-id>\",\n \"Use this member's seat instead of a node id\",\n )\n .option(\n \"--tree <key-or-id>\",\n \"Tree for --member (key or UUID)\",\n DEFAULT_TREE,\n );\n}\n\nfunction withPaging(command: Command, idsOnly = true): Command {\n command\n .option(\"--limit <n>\", \"Most rows to return across pages\")\n .option(\"--cursor <cursor>\", \"Resume from a next_cursor\")\n .option(\"--page-size <n>\", \"Rows per request\");\n if (idsOnly) command.option(\"--ids-only\", \"Return node ids only\", false);\n return command;\n}\n\n/** Every writable tree field as a flag. Clearable fields take `none`. */\nfunction withTreeFields(command: Command): Command {\n return command\n .option(\"--key <key>\", \"Tree key, unique per company\")\n .option(\"--name <name>\", \"Display name\")\n .option(\"--kind <kind>\", `Tree kind: ${TREE_KINDS.join(\", \")}`)\n .option(\n \"--role <role>\",\n `Default-tree role: ${TREE_ROLES.join(\", \")}, or none`,\n )\n .option(\n \"--leg-names <legs>\",\n \"Comma-separated legs: two for binary (default left,right), two or more for matrix\",\n )\n .option(\n \"--placement-rule <rule>\",\n `Default placement rule: ${TREE_PLACEMENT_RULES.join(\", \")}. Binary and matrix trees accept exact, first_empty, left, right or defer; unilevel trees exact, under_enroller or mirror_enrollment`,\n )\n .option(\n \"--movement-approval <mode>\",\n `Who approves member-requested moves: ${MOVEMENT_APPROVALS.join(\", \")}`,\n )\n .option(\n \"--holding-tank-days <n>\",\n \"Days a deferred seat may wait in the holding tank (0 turns it off)\",\n )\n .option(\"--max-depth <n>\", \"Deepest level the tree allows\")\n .option(\n \"--max-movement-levels <n>\",\n \"How many levels a member move may cross (0 = no limit)\",\n )\n .option(\n \"--movement-window-days <n>\",\n \"Days after placement during which a seat may move (0 = no window)\",\n )\n .option(\n \"--volume-on-move <mode>\",\n `Where volume goes when a seat moves: ${VOLUME_ON_MOVE.join(\", \")}, or none; required for binary and matrix`,\n )\n .option(\"--volume-key <key>\", \"Volume key the tree ranks by, or none\")\n .option(\n \"--allow-multiple-positions <bool>\",\n \"true lets a member hold several seats (matrix trees)\",\n )\n .option(\n \"--authority <authority>\",\n `Ownership: ${TREE_AUTHORITIES.join(\", \")}. Only authoritative trees take placements`,\n )\n .option(\n \"--owning-company-integration-id <id>\",\n \"Integration that owns a replica tree, or none\",\n )\n .option(\n \"--upstream-tree-ref <ref>\",\n \"The back office's id for a replica tree, or none\",\n )\n .option(\n \"--external-integration-market-id <id>\",\n \"The back office market a replica tree belongs to, or none\",\n )\n .option(\"--dry-run\", \"Print the request and send nothing\", false)\n .option(\"--yes\", \"Confirm without a prompt\", false);\n}\n\n/** `trees create | update <tree> | delete <tree>`. Writes show the request\n * and ask first unless --yes; --dry-run prints it and sends nothing. */\nfunction registerTreeWrites(trees: Command, ctx: CommandContext): void {\n const send = async (\n api: () => Promise<GenealogyClient>,\n write: TreeWrite,\n label: string,\n opts: { dryRun: boolean; yes: boolean },\n verb: string,\n ) => {\n if (opts.dryRun) {\n ctx.output({ dry_run: true, request: write });\n return;\n }\n const shown = write.body ?? { params: write.params };\n await confirmChange(\n `${verb} tree ${label} with ${write.method} ${write.path} ${JSON.stringify(shown)}?`,\n opts.yes,\n );\n ctx.output(await sendTreeWrite(await api(), write, label));\n };\n\n const permission =\n \"Needs an admin login with developer.update; a company API token works if its role grants it.\";\n\n withTreeFields(\n trees\n .command(\"create\")\n .description(\n `Add a tree definition. --key, --name, --kind and --authority are required. ${permission}`,\n ),\n ).action((opts: TreeFieldsInput & { dryRun: boolean; yes: boolean }) =>\n run(async () => {\n const write = planCreateTree(opts);\n await send(() => client(ctx), write, String(opts.key), opts, \"Create\");\n }),\n );\n\n withTreeFields(\n trees\n .command(\"update <tree>\")\n .description(\n `Change a tree definition by key or UUID; sends only the flags given. An occupied tree can only be renamed. ${permission}`,\n ),\n )\n .option(\n \"--lock-version <n>\",\n \"Update only if the tree is still at this version (default: the current one)\",\n )\n .action(\n (\n tree: string,\n opts: TreeFieldsInput & {\n lockVersion?: string;\n dryRun: boolean;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const write = await planUpdateTree(api, tree, opts, opts.lockVersion);\n await send(async () => api, write, tree, opts, \"Update\");\n }),\n );\n\n trees\n .command(\"mirror [tree]\")\n .description(\n \"Mirror the enrollment tree into the placement tree: seat each enrolled member without a placement seat under their enroller's seat (roots first) and re-parent unpinned seats; pinned seats stay. Idempotent. A real run is queued in the background and printed; --wait follows it to the end. Needs an admin login with members.manage, GENEALOGY_TREE_WRITES, authoritative default trees and the mirror_enrollment rule.\",\n )\n .option(\n \"--dry-run\",\n \"Report at once what would change and write nothing\",\n false,\n )\n .option(\n \"--wait\",\n \"Follow the run until it finishes; exit non-zero if it fails or halts\",\n false,\n )\n .option(\"--interval <seconds>\", \"Seconds between readings with --wait\", \"2\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n tree: string | undefined,\n opts: {\n dryRun: boolean;\n wait: boolean;\n interval: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const label = tree ?? DEFAULT_TREE;\n const treeId = await resolveTreeId(api, label);\n if (opts.dryRun) {\n ctx.output(await previewMirror(api, treeId));\n return;\n }\n const intervalMs =\n (toPositiveInt(\"--interval\", opts.interval) ?? 2) * 1000;\n await confirmChange(\n `Mirror the enrollment tree into tree ${label}?`,\n opts.yes,\n );\n const started = await startMirror(api, treeId);\n if (!opts.wait || !started.mirror_run) {\n ctx.output(started);\n return;\n }\n const finished = await waitForMirrorRun(api, started.mirror_run, {\n intervalMs,\n onProgress: reportMirrorProgress,\n });\n ctx.output({ mirror_run: finished, started: started.started });\n if (finished.status !== \"succeeded\") {\n throw new GenealogyError(\n finished.failure_description ?? `Mirror run ${finished.status}`,\n finished.failure_type ? { type: finished.failure_type } : {},\n );\n }\n }),\n );\n\n const mirrorRuns = trees\n .command(\"mirror-runs\")\n .description(\"Read background placement mirror runs\");\n mirrorRuns\n .command(\"list\")\n .description(\"List the company's most recent mirror runs, newest first\")\n .option(\"--status <status>\", \"queued, running, succeeded, failed or halted\")\n .option(\"--limit <n>\", \"How many runs, 1 to 100 (default 25)\")\n .action((opts: { status?: string; limit?: string }) =>\n run(async () => {\n ctx.output(\n await listMirrorRuns(await client(ctx), {\n status: opts.status,\n limit: toPositiveInt(\"--limit\", opts.limit),\n }),\n );\n }),\n );\n mirrorRuns\n .command(\"show <id>\")\n .description(\"Read a mirror run's progress\")\n .action((id: string) =>\n run(async () => {\n ctx.output({ mirror_run: await getMirrorRun(await client(ctx), id) });\n }),\n );\n\n trees\n .command(\"delete <tree>\")\n .description(\n `Delete a custom tree by key or UUID. It fails with tree_has_nodes while the tree holds any seat, and the default enrollment and placement trees can't be deleted. ${permission}`,\n )\n .option(\n \"--lock-version <n>\",\n \"Delete only if the tree is still at this version (default: the current one)\",\n )\n .option(\"--dry-run\", \"Print the request and send nothing\", false)\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n tree: string,\n opts: { lockVersion?: string; dryRun: boolean; yes: boolean },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const write = await planDeleteTree(api, tree, opts.lockVersion);\n await send(async () => api, write, tree, opts, \"Delete\");\n }),\n );\n}\n\nexport function registerGenealogyCommands(\n parent: Command,\n ctx: CommandContext,\n): void {\n const subject = async (\n api: GenealogyClient,\n node: string | undefined,\n opts: SubjectOpts,\n ) => resolveSubject(api, node, opts.member, opts.tree);\n\n const trees = parent\n .command(\"trees\")\n .description(\n \"List the company's genealogy tree definitions, or create, update or delete one (needs developer.view; writes need developer.update)\",\n )\n .action(() =>\n run(async () =>\n ctx.output({ trees: await listTrees(await client(ctx)) }),\n ),\n );\n registerTreeWrites(trees, ctx);\n\n subjectCommand(parent, \"show\", \"Read a node, with child and downline counts\")\n .option(\"--as-of <time>\", \"Read the recorded placement at this time\")\n .action((node: string | undefined, opts: SubjectOpts & { asOf?: string }) =>\n run(async () => {\n const api = await client(ctx);\n ctx.output(\n await readNode(api, await subject(api, node, opts), opts.asOf),\n );\n }),\n );\n\n subjectCommand(\n parent,\n \"counts\",\n \"Children and downline counts (retained seats, including discarded members)\",\n ).action((node: string | undefined, opts: SubjectOpts) =>\n run(async () => {\n const api = await client(ctx);\n ctx.output(await readCounts(api, await subject(api, node, opts)));\n }),\n );\n\n const relatives: Array<[RelativeOperation, string, boolean]> = [\n [\"parent\", \"Read the parent node (null for a root)\", true],\n [\"root\", \"Read the root of the node's tree\", true],\n [\"sponsor\", \"Read the sponsor: the parent in the sponsor-role tree\", false],\n [\n \"enroller\",\n \"Read the enroller: the parent in the enrollment-role tree\",\n false,\n ],\n ];\n for (const [operation, description, asOf] of relatives) {\n const command = subjectCommand(parent, operation, description);\n if (asOf) command.option(\"--as-of <time>\", \"Read as of this time\");\n command.action(\n (node: string | undefined, opts: SubjectOpts & { asOf?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(await readRelative(api, id, operation, opts.asOf));\n }),\n );\n }\n\n const pages: Array<[NodePageOperation, string, \"depth\" | \"asOf\" | null]> = [\n [\"ancestors\", \"List ancestors, root first\", \"asOf\"],\n [\"children\", \"List direct children\", null],\n [\"siblings\", \"List siblings (excluding the node)\", null],\n [\"frontline\", \"List direct children in the sponsor-role tree\", null],\n [\n \"personally-enrolled\",\n \"List direct children in the enrollment-role tree\",\n null,\n ],\n [\"descendants\", \"List descendants to --depth\", \"depth\"],\n [\"subtree\", \"List the node and its descendants to --depth\", \"depth\"],\n [\"level\", \"List the nodes exactly --depth levels down\", \"depth\"],\n ];\n for (const [operation, description, extra] of pages) {\n const command = withPaging(\n subjectCommand(parent, operation, description),\n operation !== \"ancestors\",\n );\n if (extra === \"depth\") {\n command.option(\"--depth <n>\", `Relative depth, 1-${MAX_DEPTH}`);\n }\n if (extra) command.option(\"--as-of <time>\", \"Read as of this time\");\n command.action(\n (\n node: string | undefined,\n opts: SubjectOpts &\n PagingOpts & { depth?: string; asOf?: string; idsOnly?: boolean },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(\n await readNodePage(api, id, operation, {\n ...paging(opts),\n depth: opts.depth,\n asOf: opts.asOf,\n idsOnly: opts.idsOnly,\n }),\n );\n }),\n );\n }\n\n withPaging(\n subjectCommand(parent, \"grandchildren\", \"List the nodes two levels down\"),\n )\n .option(\"--as-of <time>\", \"Read as of this time\")\n .action(\n (\n node: string | undefined,\n opts: SubjectOpts & PagingOpts & { asOf?: string; idsOnly?: boolean },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(\n await readGrandchildren(api, id, {\n ...paging(opts),\n asOf: opts.asOf,\n idsOnly: opts.idsOnly,\n }),\n );\n }),\n );\n\n subjectCommand(parent, \"level-counts\", \"Count seats at each level to --depth\")\n .option(\"--depth <n>\", `Relative depth, 1-${MAX_DEPTH}`)\n .action(\n (node: string | undefined, opts: SubjectOpts & { depth?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(await readLevelCounts(api, id, opts.depth));\n }),\n );\n\n parent\n .command(\"relation <node> <other>\")\n .description(\n \"How <node> relates to <other>: one --kind, or every kind (for within_downline, <other> is the root)\",\n )\n .option(\n \"--kind <kind>\",\n \"ancestor_of, descendant_of, parent_of, child_of, sibling_of, root_of, indirect_of, in_subtree_of or within_downline\",\n )\n .action((node: string, other: string, opts: { kind?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const id = await resolveSubject(api, node, undefined);\n ctx.output(await readRelation(api, id, other, opts.kind));\n }),\n );\n\n withPaging(\n subjectCommand(\n parent,\n \"placements\",\n \"List recorded placements, newest first\",\n ),\n false,\n ).action((node: string | undefined, opts: SubjectOpts & PagingOpts) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(await readPlacements(api, id, paging(opts)));\n }),\n );\n\n withPaging(\n parent\n .command(\"holding-tank\")\n .description(\n \"List seats waiting in a tree's holding tank, soonest due first\",\n )\n .option(\"--tree <key-or-id>\", \"Tree (key or UUID)\", DEFAULT_TREE),\n false,\n ).action((opts: PagingOpts & { tree: string }) =>\n run(async () => {\n const api = await client(ctx);\n const treeId = await resolveTreeId(api, opts.tree);\n ctx.output(await readHoldingTank(api, treeId, paging(opts)));\n }),\n );\n\n // Legs are ranked by headcount only: the API has no leg volume. Each\n // child's counts is its own request, so the child count is capped.\n const legLimit = (command: Command) =>\n command.option(\n \"--limit <n>\",\n `Most direct children to count, one request each (default ${DEFAULT_LEG_CHILD_LIMIT}); more is an error`,\n );\n const legOptions = (opts: { limit?: string }): LegsOptions => ({\n maxChildren: toPositiveInt(\"--limit\", opts.limit),\n });\n legLimit(\n subjectCommand(\n parent,\n \"legs\",\n \"Rank the node's legs by headcount (child plus its downline), largest first\",\n ),\n ).action((node: string | undefined, opts: SubjectOpts & { limit?: string }) =>\n run(async () => {\n const api = await client(ctx);\n ctx.output(\n await readLegs(api, await subject(api, node, opts), legOptions(opts)),\n );\n }),\n );\n for (const which of [\"strong\", \"weak\"] as const) {\n legLimit(\n subjectCommand(\n parent,\n `${which}-leg`,\n `The ${which === \"strong\" ? \"largest\" : \"smallest\"} leg by headcount`,\n ),\n ).action(\n (node: string | undefined, opts: SubjectOpts & { limit?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const report = await readLegs(\n api,\n await subject(api, node, opts),\n legOptions(opts),\n );\n ctx.output(pickLeg(report, which));\n }),\n );\n }\n\n subjectCommand(parent, \"tree\", \"Print the downline as an indented text tree\")\n .option(\"--depth <n>\", `Levels to show, 1-${MAX_DEPTH}`, \"3\")\n .action((node: string | undefined, opts: SubjectOpts & { depth: string }) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n const read = await readTree(api, id, opts.depth);\n process.stdout.write(\n renderTree(read.nodes, read.depthTruncated, read.depth),\n );\n }),\n );\n\n subjectCommand(\n parent,\n \"place\",\n \"Place a held seat, or with --member give that member a new seat in --tree (changes the live tree)\",\n )\n .option(\"--parent <node>\", \"Parent seat, or where a rule's search starts\")\n .option(\"--root\", \"Place as a root seat\", false)\n .option(\n \"--leg <leg>\",\n \"Leg under the parent, e.g. left or right; with neither --leg nor --rule the tree's default rule applies\",\n )\n .option(\n \"--rule <rule>\",\n \"Placement rule, e.g. first_empty, left, right, defer\",\n )\n .option(\n \"--position <n>\",\n \"Seat position (matrix trees with multiple positions)\",\n )\n .option(\"--reason <text>\", \"Reason recorded on the placement\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n node: string | undefined,\n opts: SubjectOpts & {\n parent?: string;\n root: boolean;\n leg?: string;\n rule?: string;\n position?: string;\n reason?: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n if (opts.parent && opts.root) {\n throw new GenealogyError(\"Pass --parent or --root, not both.\");\n }\n if (node && opts.member) {\n throw new GenealogyError(\n \"Pass a held node id or --member, not both.\",\n );\n }\n const api = await client(ctx);\n const input = {\n parent: opts.root ? null : opts.parent,\n leg: opts.leg,\n rule: opts.rule,\n reason: opts.reason,\n };\n if (opts.member) {\n const treeId = await resolveTreeId(api, opts.tree);\n await confirmChange(\n `Place member ${opts.member} in tree ${opts.tree}?`,\n opts.yes,\n );\n ctx.output(\n await placeMember(api, treeId, opts.member, {\n ...input,\n position:\n opts.position === undefined\n ? undefined\n : Number(opts.position),\n }),\n );\n return;\n }\n if (opts.position !== undefined) {\n // A held seat keeps the position it was held at.\n throw new GenealogyError(\n \"--position applies only with --member; a held seat keeps its stored position.\",\n );\n }\n const id = await resolveSubject(api, node, undefined);\n await confirmChange(`Place held seat ${id}?`, opts.yes);\n ctx.output(await placeHeldSeat(api, id, input));\n }),\n );\n\n subjectCommand(\n parent,\n \"move\",\n \"Move a node under a new parent (changes the live tree); --validate previews only\",\n )\n .option(\"--to <node>\", \"New parent node\")\n .option(\n \"--to-member <member-id>\",\n \"New parent: this member's seat in --tree\",\n )\n .option(\"--to-root\", \"Make the node a root\", false)\n .option(\"--leg <leg>\", \"Leg under the new parent (binary and matrix trees)\")\n .option(\"--reason <text>\", \"Reason recorded on the move\")\n .option(\"--validate\", \"Preview the move without changing anything\", false)\n .option(\"--dry-run\", \"Same as --validate\", false)\n .option(\"--idempotency-key <key>\", \"Make a retried request safe to repeat\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n node: string | undefined,\n opts: SubjectOpts & {\n to?: string;\n toMember?: string;\n toRoot: boolean;\n leg?: string;\n reason?: string;\n validate: boolean;\n dryRun: boolean;\n idempotencyKey?: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const destinations = [\n opts.to,\n opts.toMember,\n opts.toRoot || undefined,\n ];\n if (destinations.filter((d) => d !== undefined).length !== 1) {\n throw new GenealogyError(\n \"Pass exactly one of --to <node>, --to-member <member-id> or --to-root.\",\n );\n }\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n const parentId = opts.toRoot\n ? null\n : await resolveSubject(api, opts.to, opts.toMember, opts.tree);\n const input = {\n parent: parentId,\n leg: opts.leg,\n reason: opts.reason,\n };\n if (opts.validate || opts.dryRun) {\n ctx.output(await validateMove(api, id, input));\n return;\n }\n await confirmChange(\n `Move ${id} ${parentId ? `under ${parentId}` : \"to a root\"}?`,\n opts.yes,\n );\n ctx.output(await requestMove(api, id, input, opts.idempotencyKey));\n }),\n );\n\n const requests = parent\n .command(\"move-requests\")\n .description(\"List, read, approve or reject move requests\");\n withPaging(\n requests\n .command(\"list\")\n .description(\"List a tree's move requests\")\n .option(\"--tree <key-or-id>\", \"Tree (key or UUID)\", DEFAULT_TREE)\n .option(\n \"--status <status>\",\n \"pending, approved, rejected, withdrawn, superseded or expired\",\n ),\n false,\n ).action((opts: PagingOpts & { tree: string; status?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const treeId = await resolveTreeId(api, opts.tree);\n ctx.output(\n await listMoveRequests(api, treeId, opts.status, paging(opts)),\n );\n }),\n );\n requests\n .command(\"show <id>\")\n .description(\"Read a move request\")\n .action((id: string) =>\n run(async () => {\n ctx.output({\n move_request: await getMoveRequest(await client(ctx), id),\n });\n }),\n );\n requests\n .command(\"approve <id>\")\n .description(\"Approve a move request (changes the live tree)\")\n .option(\"--reason <text>\", \"Reason recorded on the decision\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((id: string, opts: { reason?: string; yes: boolean }) =>\n run(async () => {\n await confirmChange(`Approve move request ${id}?`, opts.yes);\n ctx.output(\n await approveMoveRequest(await client(ctx), id, opts.reason),\n );\n }),\n );\n requests\n .command(\"reject <id>\")\n .description(\"Reject a move request with a reason\")\n .requiredOption(\"--reason <text>\", \"Why the request is rejected\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((id: string, opts: { reason: string; yes: boolean }) =>\n run(async () => {\n await confirmChange(`Reject move request ${id}?`, opts.yes);\n ctx.output(await rejectMoveRequest(await client(ctx), id, opts.reason));\n }),\n );\n\n const jobs = parent\n .command(\"move-jobs\")\n .description(\"Read deferred move jobs\");\n jobs\n .command(\"show <id>\")\n .description(\"Read a move job's progress\")\n .action((id: string) =>\n run(async () => {\n ctx.output({ move_job: await getMoveJob(await client(ctx), id) });\n }),\n );\n withPaging(\n jobs\n .command(\"list\")\n .description(\"List a tree's move jobs\")\n .option(\"--tree <key-or-id>\", \"Tree (key or UUID)\", DEFAULT_TREE)\n .option(\n \"--status <status>\",\n \"pending, running, applied, failed or superseded\",\n ),\n false,\n ).action((opts: PagingOpts & { tree: string; status?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const treeId = await resolveTreeId(api, opts.tree);\n ctx.output(await listMoveJobs(api, treeId, opts.status, paging(opts)));\n }),\n );\n}\n\nconst plugin: FluidPlugin = {\n name: \"@fluid-app/fluid-cli-genealogy\",\n version: \"0.1.0\",\n register(ctx) {\n ctx.program.addCommand(\n createDomainCommand(\n \"genealogy\",\n \"Read genealogy trees, rank legs, and place or move members\",\n registerGenealogyCommands,\n ),\n );\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/api.ts","../src/index.ts"],"sourcesContent":["// Calls to the company genealogy API (`/api/company/v2026-10/genealogy`).\n\n/** The slice of the CLI's API client these calls use. */\nexport interface GenealogyClient {\n get<T>(endpoint: string, params?: Record<string, unknown>): Promise<T>;\n post<T>(\n endpoint: string,\n body?: unknown,\n options?: { headers?: Record<string, string> },\n ): Promise<T>;\n patch<T>(endpoint: string, body?: unknown): Promise<T>;\n delete<T>(\n endpoint: string,\n options?: { params?: Record<string, unknown> },\n ): Promise<T>;\n}\n\nconst BASE = \"/api/company/v2026-10/genealogy\";\n\n/** The tree `fluid genealogy` reads when --tree is not given, matching the\n * Rails genealogy CLI. */\nexport const DEFAULT_TREE = \"placement\";\n\n/** Deeper reads (descendants, subtree, level, level counts) admit relative\n * depths 1-7; the API refuses larger values rather than clamping them. */\nexport const MAX_DEPTH = 7;\n\n/** Node pages hold at most 1,000 rows; history-style pages (placements,\n * holding tank, move requests and jobs) at most 100. */\nconst NODE_PAGE_MAX = 1000;\nconst HISTORY_PAGE_MAX = 100;\n\n/** An unbounded read stops here instead of returning a partial list. */\nconst MAX_PAGES = 200;\n\n/** Fan-out reads (each child when ranking legs, each relation kind) run\n * this many at a time. */\nconst CONCURRENCY = 5;\n\nconst UUID = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;\n\nexport class GenealogyError extends Error {\n /** The API's failure type, when the error restates an API failure. */\n readonly type?: string;\n /** The API's HTTP status, when the error restates an API failure. */\n readonly status?: number;\n\n constructor(\n message: string,\n options: { type?: string; status?: number } = {},\n ) {\n super(message);\n this.name = \"GenealogyError\";\n if (options.type !== undefined) this.type = options.type;\n if (options.status !== undefined) this.status = options.status;\n }\n}\n\nexport interface GenealogyNode {\n id: string;\n tree_id: string;\n member_id: string;\n parent_node_id: string | null;\n leg: string | null;\n position: number;\n pinned: boolean;\n relative_depth?: number;\n}\n\n/** nodes/{id} without as_of also carries structural counts. */\nexport interface NodeRelationship {\n id: string;\n tree_id: string;\n member_id: string;\n parent_node_id: string | null;\n leg: string | null;\n effective_at?: string;\n ended_at?: string | null;\n children_count?: number;\n downline_count?: number;\n counts_as_of?: string;\n}\n\nexport interface Tree {\n id: string;\n key: string;\n name?: string;\n kind?: string;\n role?: string | null;\n leg_names?: string[];\n [field: string]: unknown;\n}\n\ninterface Pagination {\n next_cursor?: string | null;\n}\n\ninterface PageWire {\n meta?: { pagination?: Pagination; traversal?: { depth_truncated?: boolean } };\n}\n\ninterface NodeResponse<N> {\n node?: N | null;\n}\n\nexport function isUuid(value: string): boolean {\n return UUID.test(value);\n}\n\nfunction path(...segments: string[]): string {\n return `${BASE}/${segments.map((s) => encodeURIComponent(s)).join(\"/\")}`;\n}\n\nfunction requireUuid(label: string, value: string): string {\n const trimmed = value.trim();\n if (!isUuid(trimmed)) {\n throw new GenealogyError(`${label} \"${value}\" isn't a UUID.`);\n }\n return trimmed;\n}\n\n/** Parse a CLI number option; undefined stays undefined. */\nexport function toPositiveInt(\n name: string,\n raw: string | number | undefined,\n max?: number,\n): number | undefined {\n if (raw === undefined) return undefined;\n const value = typeof raw === \"number\" ? raw : Number(raw);\n if (!Number.isInteger(value) || value < 1 || (max != null && value > max)) {\n const range = max != null ? ` from 1 to ${max}` : \"\";\n throw new GenealogyError(`${name} must be a whole number${range}.`);\n }\n return value;\n}\n\nfunction requireDepth(raw: string | number | undefined): number {\n const depth = toPositiveInt(\"--depth\", raw);\n if (depth === undefined) {\n throw new GenealogyError(\n `--depth is required: a relative depth from 1 to ${MAX_DEPTH}.`,\n );\n }\n return depth;\n}\n\nfunction checkAsOf(asOf: string | undefined): string | undefined {\n if (asOf === undefined) return undefined;\n if (Number.isNaN(Date.parse(asOf))) {\n throw new GenealogyError(\n `--as-of \"${asOf}\" isn't an ISO 8601 timestamp, e.g. 2026-09-15T00:00:00Z.`,\n );\n }\n return asOf;\n}\n\n// Trees and member seats\n\nexport async function listTrees(client: GenealogyClient): Promise<Tree[]> {\n const data = await client.get<{ trees?: Tree[] | null }>(path(\"trees\"));\n return data.trees ?? [];\n}\n\n/** A tree's identifiers, without its configuration. */\nexport interface TreeKey {\n id: string;\n key: string;\n name: string;\n}\n\n/** List the company's tree ids, keys and names. Unlike `listTrees`, this\n * needs only members.view, not developer.view. */\nexport async function listTreeKeys(\n client: GenealogyClient,\n): Promise<TreeKey[]> {\n const data = await client.get<{ trees?: TreeKey[] | null }>(\n path(\"tree-keys\"),\n );\n return data.trees ?? [];\n}\n\nconst isForbidden = (err: unknown) =>\n (err as { status?: unknown }).status === 403;\n\nconst isNotFound = (err: unknown) =>\n (err as { status?: unknown }).status === 404;\n\n/** Tree keys need members.view; a caller with only developer.view reads\n * them from the tree definitions instead. An API that predates the\n * tree-keys endpoint answers 404, and also falls back. */\nasync function lookUpTreeKeys(client: GenealogyClient): Promise<TreeKey[]> {\n try {\n return await listTreeKeys(client);\n } catch (err) {\n if (!isForbidden(err) && !isNotFound(err)) throw err;\n }\n try {\n const trees = await listTrees(client);\n return trees.map(({ id, key, name }) => ({ id, key, name: name ?? key }));\n } catch (err) {\n if (!isForbidden(err)) throw err;\n throw new GenealogyError(\n \"Looking up a tree by key needs members.view or developer.view. Pass --tree <tree-uuid> instead.\",\n );\n }\n}\n\n/** Resolve a tree key (e.g. \"placement\") or UUID to a tree UUID. Tree paths\n * only take UUIDs, so a key is looked up in the company's tree keys. */\nexport async function resolveTreeId(\n client: GenealogyClient,\n keyOrId: string,\n): Promise<string> {\n const trimmed = keyOrId.trim();\n if (isUuid(trimmed)) return trimmed;\n const trees = await lookUpTreeKeys(client);\n const match = trees.find((tree) => tree.key === trimmed);\n if (!match) {\n const keys = trees.map((tree) => tree.key).join(\", \") || \"none\";\n throw new GenealogyError(\n `No tree with key \"${trimmed}\" on this company. Trees: ${keys}.`,\n );\n }\n return match.id;\n}\n\n// Tree definitions (create, update, delete)\n\nexport const TREE_KINDS = [\"unilevel\", \"binary\", \"matrix\"] as const;\nexport const TREE_ROLES = [\"enrollment\", \"sponsor\"] as const;\nexport const TREE_AUTHORITIES = [\n \"authoritative\",\n \"replica\",\n \"unconfigured\",\n] as const;\nexport const MOVEMENT_APPROVALS = [\"immediate\", \"require_approval\"] as const;\nexport const VOLUME_ON_MOVE = [\n \"follows_structure\",\n \"stays_at_earning\",\n] as const;\n/** A tree's default rule: the placement rules plus the two role-tree rules. */\nexport const TREE_PLACEMENT_RULES = [\n \"exact\",\n \"first_empty\",\n \"left\",\n \"right\",\n \"inside\",\n \"outside\",\n \"balanced\",\n \"weak_leg\",\n \"strong_leg\",\n \"enroller_preference\",\n \"defer\",\n \"under_enroller\",\n \"mirror_enrollment\",\n] as const;\n\n/** Writable tree fields, as CLI flag values (strings until validated).\n * For a clearable field, the word `none` sends null. */\nexport interface TreeFieldsInput {\n key?: string;\n name?: string;\n kind?: string;\n role?: string;\n legNames?: string;\n placementRule?: string;\n movementApproval?: string;\n holdingTankDays?: string;\n maxDepth?: string;\n maxMovementLevels?: string;\n movementWindowDays?: string;\n volumeOnMove?: string;\n volumeKey?: string;\n allowMultiplePositions?: string;\n authority?: string;\n owningCompanyIntegrationId?: string;\n upstreamTreeRef?: string;\n externalIntegrationMarketId?: string;\n}\n\nconst NONE = \"none\";\n\nfunction oneOf(\n flag: string,\n value: string,\n allowed: readonly string[],\n): string {\n if (!allowed.includes(value)) {\n throw new GenealogyError(`${flag} must be one of ${allowed.join(\", \")}.`);\n }\n return value;\n}\n\nfunction wholeNumber(flag: string, raw: string, min: number): number {\n const value = Number(raw);\n if (raw.trim() === \"\" || !Number.isInteger(value) || value < min) {\n throw new GenealogyError(`${flag} must be a whole number, ${min} or more.`);\n }\n return value;\n}\n\nfunction bool(flag: string, raw: string): boolean {\n if (raw === \"true\") return true;\n if (raw === \"false\") return false;\n throw new GenealogyError(`${flag} must be true or false.`);\n}\n\nfunction legList(raw: string): string[] {\n const legs = raw.split(\",\").map((leg) => leg.trim());\n if (legs.some((leg) => leg === \"\") || new Set(legs).size !== legs.length) {\n throw new GenealogyError(\n \"--leg-names must be distinct, nonblank names separated by commas, e.g. left,right.\",\n );\n }\n return legs;\n}\n\n/** Build the `tree` object a create or update sends, validating each flag.\n * Only the flags given are sent. */\nexport function treeFields(input: TreeFieldsInput): Record<string, unknown> {\n const fields: Record<string, unknown> = {};\n const text = (flag: string, name: string, value: string | undefined) => {\n if (value === undefined) return;\n if (value.trim() === \"\")\n throw new GenealogyError(`${flag} can't be blank.`);\n fields[name] = value;\n };\n const clearable = (\n name: string,\n value: string | undefined,\n parse: (raw: string) => unknown,\n ) => {\n if (value === undefined) return;\n fields[name] = value === NONE ? null : parse(value);\n };\n\n text(\"--key\", \"key\", input.key);\n text(\"--name\", \"name\", input.name);\n if (input.kind !== undefined) {\n fields[\"kind\"] = oneOf(\"--kind\", input.kind, TREE_KINDS);\n }\n clearable(\"role\", input.role, (raw) => oneOf(\"--role\", raw, TREE_ROLES));\n if (input.legNames !== undefined) {\n fields[\"leg_names\"] =\n input.legNames === NONE ? [] : legList(input.legNames);\n }\n if (input.placementRule !== undefined) {\n fields[\"default_placement_rule\"] = oneOf(\n \"--placement-rule\",\n input.placementRule,\n TREE_PLACEMENT_RULES,\n );\n }\n if (input.movementApproval !== undefined) {\n fields[\"movement_approval\"] = oneOf(\n \"--movement-approval\",\n input.movementApproval,\n MOVEMENT_APPROVALS,\n );\n }\n if (input.maxDepth !== undefined) {\n fields[\"max_depth\"] = wholeNumber(\"--max-depth\", input.maxDepth, 1);\n }\n const counts: Array<[keyof TreeFieldsInput, string, string]> = [\n [\"holdingTankDays\", \"--holding-tank-days\", \"holding_tank_days\"],\n [\"movementWindowDays\", \"--movement-window-days\", \"movement_window_days\"],\n [\"maxMovementLevels\", \"--max-movement-levels\", \"max_movement_levels\"],\n ];\n for (const [prop, flag, name] of counts) {\n const raw = input[prop];\n if (raw !== undefined) fields[name] = wholeNumber(flag, raw, 0);\n }\n clearable(\"volume_on_move\", input.volumeOnMove, (raw) =>\n oneOf(\"--volume-on-move\", raw, VOLUME_ON_MOVE),\n );\n clearable(\"volume_key\", input.volumeKey, (raw) => raw);\n if (input.allowMultiplePositions !== undefined) {\n fields[\"allow_multiple_positions\"] = bool(\n \"--allow-multiple-positions\",\n input.allowMultiplePositions,\n );\n }\n if (input.authority !== undefined) {\n fields[\"authority\"] = oneOf(\n \"--authority\",\n input.authority,\n TREE_AUTHORITIES,\n );\n }\n clearable(\n \"owning_company_integration_id\",\n input.owningCompanyIntegrationId,\n (raw) => wholeNumber(\"--owning-company-integration-id\", raw, 1),\n );\n clearable(\"upstream_tree_ref\", input.upstreamTreeRef, (raw) => raw);\n clearable(\n \"external_integration_market_id\",\n input.externalIntegrationMarketId,\n (raw) => raw,\n );\n return fields;\n}\n\n/** The body for POST /trees. Checks what the API would refuse anyway, so a\n * mistake is named before anything is sent. */\nexport function createTreeBody(input: TreeFieldsInput): {\n tree: Record<string, unknown>;\n} {\n const fields = treeFields(input);\n const missing = (\n [\n [\"key\", \"--key\"],\n [\"name\", \"--name\"],\n [\"kind\", \"--kind\"],\n [\"authority\", \"--authority\"],\n ] as const\n )\n .filter(([name]) => fields[name] === undefined)\n .map(([, flag]) => flag);\n if (missing.length > 0) {\n throw new GenealogyError(\n `Creating a tree needs ${missing.join(\", \")}. --authority declares who owns the tree: authoritative (Fluid), replica (an integration's copy) or unconfigured (not yet decided; it takes no placements).`,\n );\n }\n const kind = fields[\"kind\"];\n const legs = fields[\"leg_names\"] as string[] | undefined;\n if (kind === \"unilevel\" && legs && legs.length > 0) {\n throw new GenealogyError(\"A unilevel tree has no legs; drop --leg-names.\");\n }\n if (kind === \"binary\" && legs && legs.length !== 2) {\n throw new GenealogyError(\n \"A binary tree has exactly two legs, e.g. --leg-names left,right (the default).\",\n );\n }\n if (kind === \"matrix\" && (!legs || legs.length < 2)) {\n throw new GenealogyError(\n \"A matrix tree needs --leg-names with two or more legs; their count is its width.\",\n );\n }\n if (kind !== \"unilevel\" && fields[\"volume_on_move\"] == null) {\n throw new GenealogyError(\n `A ${String(kind)} tree needs --volume-on-move ${VOLUME_ON_MOVE.join(\" or \")}.`,\n );\n }\n if (\n fields[\"authority\"] === \"replica\" &&\n fields[\"owning_company_integration_id\"] == null\n ) {\n throw new GenealogyError(\n \"A replica tree needs --owning-company-integration-id.\",\n );\n }\n return { tree: fields };\n}\n\n/** Readable restatements of the registry's write failures. */\nfunction treeWriteError(\n err: unknown,\n tree: string,\n method: TreeWrite[\"method\"],\n): unknown {\n const status = (err as { status?: unknown }).status;\n const body = (err as { body?: Record<string, unknown> | null }).body;\n const nested = body?.[\"error\"];\n const type =\n nested && typeof nested === \"object\"\n ? (nested as { details?: { type?: unknown } }).details?.type\n : undefined;\n if (typeof status !== \"number\" || typeof type !== \"string\") return err;\n const messages: Record<string, string> = {\n duplicate_key: `Another tree on this company already has the key of \"${tree}\". Tree keys are unique per company; pick another --key.`,\n role_taken:\n \"This company already has a tree with that role. Each role (enrollment, sponsor) has one tree.\",\n stale_tree: `Tree \"${tree}\" changed since its lock_version was read. Read it again with \"fluid genealogy trees\" and retry, or omit --lock-version to use the current one.`,\n tree_has_nodes:\n method === \"DELETE\"\n ? `Tree \"${tree}\" has seats, and delete refuses a tree with nodes. Remove its seats first.`\n : `Tree \"${tree}\" has seats, so only its --name can change; other fields need a structural migration.`,\n reserved_role:\n \"The default enrollment and placement trees can't be deleted or have their role changed.\",\n reserved_key:\n 'The keys \"enrollment\" and \"placement\" belong to the default role trees.',\n };\n const message = messages[type];\n return message ? new GenealogyError(message, { type, status }) : err;\n}\n\n/** A tree's full definition by key or UUID, from the tree list. */\nexport async function findTree(\n client: GenealogyClient,\n keyOrId: string,\n): Promise<Tree> {\n const wanted = keyOrId.trim();\n const trees = await listTrees(client);\n const match = trees.find((tree) =>\n isUuid(wanted) ? tree.id === wanted : tree.key === wanted,\n );\n if (!match) {\n const keys = trees.map((tree) => tree.key).join(\", \") || \"none\";\n throw new GenealogyError(\n `No tree \"${wanted}\" on this company. Trees: ${keys}.`,\n );\n }\n return match;\n}\n\n/** A write the CLI is about to send, for --dry-run and confirmation. */\nexport interface TreeWrite {\n method: \"POST\" | \"PATCH\" | \"DELETE\";\n path: string;\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport function planCreateTree(input: TreeFieldsInput): TreeWrite {\n return { method: \"POST\", path: path(\"trees\"), body: createTreeBody(input) };\n}\n\n/** Plan a PATCH: only the given fields, at the tree's lock_version (read\n * now unless --lock-version pins it). */\nexport async function planUpdateTree(\n client: GenealogyClient,\n keyOrId: string,\n input: TreeFieldsInput,\n lockVersion?: string,\n): Promise<TreeWrite> {\n const fields = treeFields(input);\n if (Object.keys(fields).length === 0) {\n throw new GenealogyError(\n \"Pass at least one field to change, e.g. --name or --movement-approval.\",\n );\n }\n const { id, version } = await treeVersion(client, keyOrId, lockVersion);\n return {\n method: \"PATCH\",\n path: path(\"trees\", id),\n body: { tree: { ...fields, lock_version: version } },\n };\n}\n\nexport async function planDeleteTree(\n client: GenealogyClient,\n keyOrId: string,\n lockVersion?: string,\n): Promise<TreeWrite> {\n const { id, version } = await treeVersion(client, keyOrId, lockVersion);\n return {\n method: \"DELETE\",\n path: path(\"trees\", id),\n params: { lock_version: version },\n };\n}\n\nasync function treeVersion(\n client: GenealogyClient,\n keyOrId: string,\n lockVersion: string | undefined,\n): Promise<{ id: string; version: number }> {\n const pinned =\n lockVersion === undefined\n ? undefined\n : wholeNumber(\"--lock-version\", lockVersion, 0);\n if (pinned !== undefined && isUuid(keyOrId.trim())) {\n return { id: keyOrId.trim(), version: pinned };\n }\n const tree = await findTree(client, keyOrId);\n const current = tree[\"lock_version\"];\n if (pinned === undefined && typeof current !== \"number\") {\n throw new GenealogyError(\n `Tree \"${keyOrId}\" was listed without a lock_version; pass --lock-version.`,\n );\n }\n return { id: tree.id, version: pinned ?? (current as number) };\n}\n\n/** Send a planned tree write. Returns the written tree, or a deletion\n * receipt. */\nexport async function sendTreeWrite(\n client: GenealogyClient,\n write: TreeWrite,\n label: string,\n): Promise<unknown> {\n try {\n if (write.method === \"POST\") {\n return withoutMeta(await client.post(write.path, write.body));\n }\n if (write.method === \"PATCH\") {\n return withoutMeta(await client.patch(write.path, write.body));\n }\n await client.delete(write.path, { params: write.params });\n return { deleted: true, tree: label, path: write.path };\n } catch (err) {\n throw treeWriteError(err, label, write.method);\n }\n}\n\n/** The tally a placement-tree mirror preview returns. */\nexport interface MirrorTally {\n tree_id: string;\n dry_run: boolean;\n counts: Record<string, number>;\n failures: Array<{ member_id: string; type: string; description: string }>;\n}\n\n/** A background placement mirror run and its progress. */\nexport interface MirrorRun {\n id: string;\n tree_id: string;\n status: MirrorRunStatus;\n counts: Record<string, number>;\n failures: Array<{ member_id: string; type: string; description: string }>;\n failure_type: string | null;\n failure_description: string | null;\n windows: number;\n attempts: number;\n requested_by_id: string;\n created_at: string;\n started_at: string | null;\n finished_at: string | null;\n}\n\nexport const MIRROR_RUN_STATUSES = [\n \"queued\",\n \"running\",\n \"succeeded\",\n \"failed\",\n \"halted\",\n] as const;\nexport type MirrorRunStatus = (typeof MIRROR_RUN_STATUSES)[number];\n\n/** Whether a run has finished, one way or another. */\nexport function mirrorRunFinished(run: MirrorRun): boolean {\n return [\"succeeded\", \"failed\", \"halted\"].includes(run.status);\n}\n\n/** Preview the mirror of the enrollment tree into the placement tree:\n * what would be seated and re-parented. Writes nothing. */\nexport async function previewMirror(\n client: GenealogyClient,\n treeId: string,\n): Promise<{ mirror: MirrorTally | null }> {\n const data = await client.post<{ mirror?: MirrorTally | null }>(\n path(\"trees\", treeId, \"mirror-enrollment\"),\n { mirror: { dry_run: true } },\n );\n return { mirror: data.mirror ?? null };\n}\n\n/** Queue a background run that mirrors the enrollment tree into the\n * placement tree, or get the run already active (`started: false`). */\nexport async function startMirror(\n client: GenealogyClient,\n treeId: string,\n): Promise<{ mirror_run: MirrorRun | null; started: boolean }> {\n const data = await client.post<{\n mirror_run?: MirrorRun | null;\n started?: boolean;\n }>(path(\"trees\", treeId, \"mirror-enrollment\"), {\n mirror: { dry_run: false },\n });\n return {\n mirror_run: data.mirror_run ?? null,\n started: data.started === true,\n };\n}\n\nexport async function getMirrorRun(\n client: GenealogyClient,\n id: string,\n): Promise<MirrorRun | null> {\n const data = await client.get<{ mirror_run?: MirrorRun | null }>(\n path(\"mirror-runs\", requireUuid(\"Mirror run\", id)),\n );\n return data.mirror_run ?? null;\n}\n\n/** The company's most recent mirror runs, newest first. */\nexport async function listMirrorRuns(\n client: GenealogyClient,\n options: { status?: string; limit?: number } = {},\n): Promise<{ mirror_runs: MirrorRun[] }> {\n if (\n options.status !== undefined &&\n !(MIRROR_RUN_STATUSES as readonly string[]).includes(options.status)\n ) {\n throw new GenealogyError(\n `--status must be one of ${MIRROR_RUN_STATUSES.join(\", \")}`,\n );\n }\n const params: Record<string, unknown> = {};\n if (options.status !== undefined) params[\"status\"] = options.status;\n if (options.limit !== undefined) params[\"limit\"] = options.limit;\n const data = await client.get<{ mirror_runs?: MirrorRun[] }>(\n path(\"mirror-runs\"),\n params,\n );\n return { mirror_runs: data.mirror_runs ?? [] };\n}\n\n/** Poll a run until it finishes, reporting each reading. */\nexport async function waitForMirrorRun(\n client: GenealogyClient,\n run: MirrorRun,\n options: {\n intervalMs: number;\n onProgress?: (run: MirrorRun) => void;\n sleep?: (ms: number) => Promise<void>;\n },\n): Promise<MirrorRun> {\n const sleep =\n options.sleep ??\n ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n let current = run;\n options.onProgress?.(current);\n while (!mirrorRunFinished(current)) {\n await sleep(options.intervalMs);\n const next = await getMirrorRun(client, current.id);\n if (!next) throw new GenealogyError(`Mirror run ${current.id} is gone`);\n current = next;\n options.onProgress?.(current);\n }\n return current;\n}\n\n/** Find a member's one seat in a tree. Seats are listed across every tree,\n * so the tree is filtered here; a member with several positions in the\n * tree must be addressed by node id. */\nexport async function resolveMemberNode(\n client: GenealogyClient,\n memberId: string,\n tree: string = DEFAULT_TREE,\n): Promise<string> {\n const member = requireUuid(\"Member\", memberId);\n const treeId = await resolveTreeId(client, tree);\n const { items } = await collectPages<GenealogyNode>(\n client,\n path(\"members\", member, \"nodes\"),\n {},\n \"nodes\",\n { maxPageSize: NODE_PAGE_MAX },\n );\n const seats = items.filter((node) => node.tree_id === treeId);\n if (seats.length === 0) {\n throw new GenealogyError(`Member ${member} has no seat in tree \"${tree}\".`);\n }\n if (seats.length > 1) {\n const listed = seats\n .map((seat) => `${seat.id} (position ${seat.position})`)\n .join(\", \");\n throw new GenealogyError(\n `Member ${member} has ${seats.length} seats in tree \"${tree}\": ${listed}. Pass the node id instead.`,\n );\n }\n return (seats[0] as GenealogyNode).id;\n}\n\n/** A command's subject: a node UUID, or --member resolved in --tree. */\nexport async function resolveSubject(\n client: GenealogyClient,\n node: string | undefined,\n member: string | undefined,\n tree: string = DEFAULT_TREE,\n): Promise<string> {\n if (node && member) {\n throw new GenealogyError(\"Pass a node id or --member, not both.\");\n }\n if (node) return requireUuid(\"Node\", node);\n if (member) return resolveMemberNode(client, member, tree);\n throw new GenealogyError(\"Pass a node id or --member <member-id>.\");\n}\n\n// Paging\n\nexport interface PageOptions {\n /** Total rows to return across pages. */\n limit?: number;\n /** Resume from a next_cursor printed by an earlier read. */\n cursor?: string;\n /** Rows per request; defaults to the endpoint's maximum. */\n pageSize?: number;\n}\n\nexport interface Collected<T, P> {\n items: T[];\n /** Set when more rows exist past --limit; pass it back as --cursor. */\n nextCursor: string | null;\n pages: P[];\n}\n\n/** Follow next_cursor until the list ends or --limit rows are collected.\n * A capped read reports the cursor to resume from; an uncapped read that\n * runs past MAX_PAGES fails rather than return part of the list. */\nexport async function collectPages<T, P extends PageWire = PageWire>(\n client: GenealogyClient,\n endpoint: string,\n params: Record<string, unknown>,\n itemsKey: string,\n options: PageOptions & { maxPageSize: number },\n): Promise<Collected<T, P>> {\n const pageSize =\n toPositiveInt(\"--page-size\", options.pageSize, options.maxPageSize) ??\n options.maxPageSize;\n const limit = toPositiveInt(\"--limit\", options.limit);\n const items: T[] = [];\n const pages: P[] = [];\n let cursor = options.cursor;\n for (let page = 1; page <= MAX_PAGES; page += 1) {\n const remaining = limit === undefined ? pageSize : limit - items.length;\n const data = await client.get<P>(endpoint, {\n ...params,\n limit: Math.min(pageSize, remaining),\n ...(cursor ? { cursor } : {}),\n });\n pages.push(data);\n const rows = (data as Record<string, unknown>)[itemsKey];\n if (Array.isArray(rows)) items.push(...(rows as T[]));\n const next = data.meta?.pagination?.next_cursor ?? null;\n if (!next) return { items, nextCursor: null, pages };\n if (limit !== undefined && items.length >= limit) {\n return { items, nextCursor: next, pages };\n }\n cursor = next;\n }\n throw new GenealogyError(\n `More than ${MAX_PAGES} pages; refusing to return a partial list. Pass --limit and resume with --cursor.`,\n );\n}\n\n// Node reads\n\n/** Paged node reads and the query selectors each one accepts. The API\n * refuses selectors it does not list, so only these are sent. */\nconst NODE_PAGES = {\n children: { asOf: false, depth: false, idsOnly: true },\n siblings: { asOf: false, depth: false, idsOnly: true },\n frontline: { asOf: false, depth: false, idsOnly: true },\n \"personally-enrolled\": { asOf: false, depth: false, idsOnly: true },\n ancestors: { asOf: true, depth: false, idsOnly: false },\n descendants: { asOf: true, depth: true, idsOnly: true },\n subtree: { asOf: true, depth: true, idsOnly: true },\n level: { asOf: true, depth: true, idsOnly: true },\n} as const;\n\nexport type NodePageOperation = keyof typeof NODE_PAGES;\n\nexport interface NodePageOptions extends PageOptions {\n depth?: string | number;\n asOf?: string;\n idsOnly?: boolean;\n}\n\nexport interface NodePageResult {\n nodes?: GenealogyNode[];\n ids?: string[];\n next_cursor: string | null;\n /** frontline / personally-enrolled: the role-tree seat whose children\n * are listed. */\n node?: GenealogyNode | null;\n /** Deeper reads: rows exist beyond the selected depth. */\n depth_truncated?: boolean;\n}\n\nexport async function readNodePage(\n client: GenealogyClient,\n nodeId: string,\n operation: NodePageOperation,\n options: NodePageOptions = {},\n): Promise<NodePageResult> {\n const accepts = NODE_PAGES[operation];\n if (options.asOf !== undefined && !accepts.asOf) {\n throw new GenealogyError(`--as-of is not accepted by ${operation}.`);\n }\n if (options.idsOnly && !accepts.idsOnly) {\n throw new GenealogyError(`--ids-only is not accepted by ${operation}.`);\n }\n const params: Record<string, unknown> = {};\n if (accepts.depth) params[\"depth\"] = requireDepth(options.depth);\n const asOf = checkAsOf(options.asOf);\n if (asOf) params[\"as_of\"] = asOf;\n if (options.idsOnly) params[\"ids_only\"] = true;\n\n const itemsKey = options.idsOnly ? \"ids\" : \"nodes\";\n const { items, nextCursor, pages } = await collectPages<\n unknown,\n PageWire & { node?: GenealogyNode | null }\n >(client, path(\"nodes\", nodeId, operation), params, itemsKey, {\n ...options,\n maxPageSize: NODE_PAGE_MAX,\n });\n\n const result: NodePageResult = { [itemsKey]: items, next_cursor: nextCursor };\n if (operation === \"frontline\" || operation === \"personally-enrolled\") {\n result.node = pages[0]?.node ?? null;\n }\n if (accepts.depth) {\n result.depth_truncated = pages.some(\n (page) => page.meta?.traversal?.depth_truncated === true,\n );\n }\n return result;\n}\n\n/** Grandchildren are the nodes exactly two levels down. */\nexport function readGrandchildren(\n client: GenealogyClient,\n nodeId: string,\n options: Omit<NodePageOptions, \"depth\"> = {},\n): Promise<NodePageResult> {\n return readNodePage(client, nodeId, \"level\", { ...options, depth: 2 });\n}\n\n/** One node; current reads include children_count and downline_count,\n * as_of reads return the recorded placement without counts. */\nexport async function readNode(\n client: GenealogyClient,\n nodeId: string,\n asOf?: string,\n): Promise<{ node: NodeRelationship | null }> {\n const at = checkAsOf(asOf);\n const data = await client.get<NodeResponse<NodeRelationship>>(\n path(\"nodes\", nodeId),\n at ? { as_of: at } : undefined,\n );\n return { node: data.node ?? null };\n}\n\nexport interface NodeCounts {\n node_id: string;\n children_count: number;\n downline_count: number;\n counts_as_of: string | null;\n}\n\n/** Structural counts. They include retained seats of discarded members,\n * so they can exceed what the page reads list. */\nexport async function readCounts(\n client: GenealogyClient,\n nodeId: string,\n): Promise<NodeCounts> {\n const { node } = await readNode(client, nodeId);\n if (\n !node ||\n typeof node.children_count !== \"number\" ||\n typeof node.downline_count !== \"number\"\n ) {\n throw new GenealogyError(`The read of node ${nodeId} returned no counts.`);\n }\n return {\n node_id: node.id,\n children_count: node.children_count,\n downline_count: node.downline_count,\n counts_as_of: node.counts_as_of ?? null,\n };\n}\n\nexport type RelativeOperation = \"parent\" | \"root\" | \"sponsor\" | \"enroller\";\n\n/** parent and root take as_of; sponsor and enroller take no selectors.\n * The node is null for a root (and for a role-tree root). */\nexport async function readRelative(\n client: GenealogyClient,\n nodeId: string,\n operation: RelativeOperation,\n asOf?: string,\n): Promise<{ node: GenealogyNode | null }> {\n if (\n asOf !== undefined &&\n (operation === \"sponsor\" || operation === \"enroller\")\n ) {\n throw new GenealogyError(`--as-of is not accepted by ${operation}.`);\n }\n const at = checkAsOf(asOf);\n const data = await client.get<NodeResponse<GenealogyNode>>(\n path(\"nodes\", nodeId, operation),\n at ? { as_of: at } : undefined,\n );\n return { node: data.node ?? null };\n}\n\nexport const RELATION_KINDS = [\n \"ancestor_of\",\n \"descendant_of\",\n \"parent_of\",\n \"child_of\",\n \"sibling_of\",\n \"root_of\",\n \"indirect_of\",\n \"in_subtree_of\",\n \"within_downline\",\n] as const;\n\nexport type RelationKind = (typeof RELATION_KINDS)[number];\n\n/** Answer how node relates to other: one kind, or every kind when none is\n * given. For within_downline, other is the downline's root. */\nexport async function readRelation(\n client: GenealogyClient,\n nodeId: string,\n otherId: string,\n kind?: string,\n): Promise<{\n node_id: string;\n other: string;\n relations: Partial<Record<RelationKind, boolean>>;\n}> {\n const other = requireUuid(\"Other node\", otherId);\n if (\n kind !== undefined &&\n !(RELATION_KINDS as readonly string[]).includes(kind)\n ) {\n throw new GenealogyError(\n `--kind must be one of ${RELATION_KINDS.join(\", \")}.`,\n );\n }\n const kinds = kind ? [kind as RelationKind] : [...RELATION_KINDS];\n const answers = await mapLimit(kinds, CONCURRENCY, async (k) => {\n const data = await client.get<{\n relation?: { kind: RelationKind; answer: boolean };\n }>(path(\"nodes\", nodeId, \"relation\"), { other, kind: k });\n return [k, data.relation?.answer ?? false] as const;\n });\n return { node_id: nodeId, other, relations: Object.fromEntries(answers) };\n}\n\nexport async function readLevelCounts(\n client: GenealogyClient,\n nodeId: string,\n depth: string | number | undefined,\n): Promise<{\n node_id: string;\n levels: Array<{ distance: number; count: number }>;\n counts_as_of: string | null;\n depth_truncated: boolean;\n}> {\n const data = await client.get<{\n levels?: Array<{ distance: number; count: number }>;\n meta?: { traversal?: { counts_as_of?: string; depth_truncated?: boolean } };\n }>(path(\"nodes\", nodeId, \"level-counts\"), { depth: requireDepth(depth) });\n return {\n node_id: nodeId,\n levels: data.levels ?? [],\n counts_as_of: data.meta?.traversal?.counts_as_of ?? null,\n depth_truncated: data.meta?.traversal?.depth_truncated ?? false,\n };\n}\n\n// History, holding tank, move receipts\n\nexport async function readPlacements(\n client: GenealogyClient,\n nodeId: string,\n options: PageOptions = {},\n): Promise<{ placements: unknown[]; next_cursor: string | null }> {\n const { items, nextCursor } = await collectPages(\n client,\n path(\"nodes\", nodeId, \"placements\"),\n {},\n \"placements\",\n { ...options, maxPageSize: HISTORY_PAGE_MAX },\n );\n return { placements: items, next_cursor: nextCursor };\n}\n\nexport async function readHoldingTank(\n client: GenealogyClient,\n treeId: string,\n options: PageOptions = {},\n): Promise<{ nodes: unknown[]; next_cursor: string | null }> {\n const { items, nextCursor } = await collectPages(\n client,\n path(\"trees\", treeId, \"holding-tank\"),\n {},\n \"nodes\",\n { ...options, maxPageSize: HISTORY_PAGE_MAX },\n );\n return { nodes: items, next_cursor: nextCursor };\n}\n\nexport async function listMoveRequests(\n client: GenealogyClient,\n treeId: string,\n status?: string,\n options: PageOptions = {},\n): Promise<{ move_requests: unknown[]; next_cursor: string | null }> {\n const { items, nextCursor } = await collectPages(\n client,\n path(\"trees\", treeId, \"move-requests\"),\n status ? { status } : {},\n \"move_requests\",\n { ...options, maxPageSize: HISTORY_PAGE_MAX },\n );\n return { move_requests: items, next_cursor: nextCursor };\n}\n\nexport async function listMoveJobs(\n client: GenealogyClient,\n treeId: string,\n status?: string,\n options: PageOptions = {},\n): Promise<{ move_jobs: unknown[]; next_cursor: string | null }> {\n const { items, nextCursor } = await collectPages(\n client,\n path(\"trees\", treeId, \"move-jobs\"),\n status ? { status } : {},\n \"move_jobs\",\n { ...options, maxPageSize: HISTORY_PAGE_MAX },\n );\n return { move_jobs: items, next_cursor: nextCursor };\n}\n\nexport async function getMoveRequest(\n client: GenealogyClient,\n id: string,\n): Promise<unknown> {\n const data = await client.get<{ move_request?: unknown }>(\n path(\"move-requests\", requireUuid(\"Move request\", id)),\n );\n return data.move_request ?? null;\n}\n\nexport async function getMoveJob(\n client: GenealogyClient,\n id: string,\n): Promise<unknown> {\n const data = await client.get<{ move_job?: unknown }>(\n path(\"move-jobs\", requireUuid(\"Move job\", id)),\n );\n return data.move_job ?? null;\n}\n\n// Legs (headcount)\n\nexport interface LegEntry {\n leg: string | null;\n node_id: string | null;\n member_id: string | null;\n downline_count: number;\n /** The child's own seat plus its downline_count; 0 for an empty leg. */\n seats: number;\n counts_as_of: string | null;\n /** The one direct child the children read left out, sized from the\n * node's counts; its node and member are not known. */\n unlisted?: true;\n}\n\nexport const LEG_MEASURE = \"headcount\";\nexport const LEG_COUNTED = \"seats: child plus downline_count\";\nconst LEG_NOTE =\n \"Headcount only; the genealogy API has no leg volume. Counts include retained seats of discarded members.\";\n\n/** Direct children the children read leaves out: seats of discarded\n * members, which the node's structural counts still include. */\nexport interface UnlistedLegs {\n children: number;\n /** Seats under them, from the node's downline_count; at least children. */\n seats: number;\n /** True when legs includes them: a single unlisted child is exactly\n * `seats`, so it is ranked like any other leg. */\n ranked: boolean;\n /** Named legs that are empty but cannot be told apart from the legs the\n * unlisted children hold. */\n unknown_empty_legs: number;\n}\n\nexport interface LegsReport {\n measure: typeof LEG_MEASURE;\n counted: typeof LEG_COUNTED;\n node_id: string;\n tree_id: string | null;\n legs: LegEntry[];\n /** Set when some direct children could not be listed. */\n unlisted?: UnlistedLegs;\n /** False when the tree's leg names could not be read (it needs\n * developer.view), so empty named legs are not known. */\n leg_names_known: boolean;\n note: string;\n}\n\n/** Largest first; ties keep the tree's leg order, then node id. */\nexport function rankLegs(\n entries: LegEntry[],\n legNames: string[] = [],\n): LegEntry[] {\n const order = (leg: string | null): number => {\n const index = leg === null ? -1 : legNames.indexOf(leg);\n return index === -1 ? legNames.length : index;\n };\n return [...entries].sort(\n (a, b) =>\n b.seats - a.seats ||\n order(a.leg) - order(b.leg) ||\n (a.node_id ?? \"\").localeCompare(b.node_id ?? \"\"),\n );\n}\n\n/** The genealogy API has no per-leg count endpoint, so `legs` reads each\n * child's counts separately. Above this many children it refuses rather\n * than silently making one request per child. */\nexport const DEFAULT_LEG_CHILD_LIMIT = 200;\n\nexport interface LegsOptions {\n /** Most direct children to count; more is an error. */\n maxChildren?: number;\n}\n\n/** Each direct child as a leg with its headcount, largest first. Named legs\n * of a binary or matrix tree with no child are listed with 0 seats. */\nexport async function readLegs(\n client: GenealogyClient,\n nodeId: string,\n options: LegsOptions = {},\n): Promise<LegsReport> {\n const maxChildren = options.maxChildren ?? DEFAULT_LEG_CHILD_LIMIT;\n const [trees, settled] = await Promise.all([\n // Leg names only add empty named legs. Listing trees needs\n // developer.view, so without it the occupied legs are still ranked.\n listTrees(client).catch((err: unknown) => {\n if ((err as { status?: unknown }).status === 403) return null;\n throw err;\n }),\n readSettledLegs(client, nodeId, maxChildren),\n ]);\n const { anchor, counted } = settled;\n const treeId = anchor?.tree_id ?? null;\n const legNames = trees?.find((tree) => tree.id === treeId)?.leg_names ?? [];\n const occupied = new Set(counted.map((entry) => entry.leg));\n const free = legNames.filter((leg) => !occupied.has(leg));\n\n const hidden = hiddenChildren(anchor, counted);\n let unlisted: UnlistedLegs | undefined;\n const entries = [...counted];\n if (hidden) {\n // A binary or matrix leg holds one seat, so the unlisted children hold\n // free legs; with as many free legs as unlisted children, none is empty.\n const unknownEmpty = Math.max(0, free.length - hidden.children);\n const ranked = hidden.children === 1;\n if (ranked) {\n entries.push({\n leg: free.length === 1 ? (free[0] ?? null) : null,\n node_id: null,\n member_id: null,\n downline_count: hidden.seats - 1,\n seats: hidden.seats,\n counts_as_of: anchor?.counts_as_of ?? null,\n unlisted: true,\n });\n }\n unlisted = { ...hidden, ranked, unknown_empty_legs: unknownEmpty };\n } else {\n for (const leg of free) {\n entries.push({\n leg,\n node_id: null,\n member_id: null,\n downline_count: 0,\n seats: 0,\n counts_as_of: null,\n });\n }\n }\n\n return {\n measure: LEG_MEASURE,\n counted: LEG_COUNTED,\n node_id: nodeId,\n tree_id: treeId,\n legs: rankLegs(entries, legNames),\n ...(unlisted ? { unlisted } : {}),\n leg_names_known: trees !== null,\n note: [\n LEG_NOTE,\n unlisted ? unlistedNote(unlisted) : null,\n trees === null\n ? \"Empty named legs are not listed: reading leg names needs developer.view.\"\n : null,\n ]\n .filter(Boolean)\n .join(\" \"),\n };\n}\n\nfunction unlistedNote(unlisted: UnlistedLegs): string {\n const { children, seats } = unlisted;\n const what = `${children} direct ${children === 1 ? \"child holds\" : \"children hold\"} a discarded member's seat, which the children read leaves out`;\n if (unlisted.ranked) {\n return `${what}; it is listed with unlisted: true and its ${seats} ${seats === 1 ? \"seat\" : \"seats\"} from the node's counts.${unlisted.unknown_empty_legs > 0 ? \" Empty named legs are not listed, since one of them is the leg it holds.\" : \"\"}`;\n }\n return `${what}; their ${seats} seats are not ranked, and empty named legs are not listed because one may hold them.`;\n}\n\n/** Reads that bracket the leg reads and disagree mean the tree changed\n * meanwhile; read again this many times. */\nconst LEG_READ_ATTEMPTS = 3;\n\ninterface SettledLegs {\n anchor: NodeRelationship | null;\n counted: LegEntry[];\n}\n\n/** The node's children and each child's counts, read twice between reads\n * of the node's counts, accepted only when every read agrees. The API has\n * no read that returns them together, so agreement is what shows that no\n * child moved in, out or between legs while they were read. */\nasync function readSettledLegs(\n client: GenealogyClient,\n nodeId: string,\n maxChildren: number,\n): Promise<SettledLegs> {\n for (let attempt = 1; ; attempt += 1) {\n const { node: before } = await readNode(client, nodeId);\n const first = await readLegPass(client, nodeId, maxChildren);\n const second = await readLegPass(client, nodeId, maxChildren);\n const { node: after } = await readNode(client, nodeId);\n if (sameCounts(before, after) && legKey(first) === legKey(second)) {\n return { anchor: after, counted: second };\n }\n if (attempt >= LEG_READ_ATTEMPTS) {\n throw new GenealogyError(\n `Node ${nodeId}'s legs changed during each of ${LEG_READ_ATTEMPTS} reads, so they could not be ranked. Try again.`,\n );\n }\n }\n}\n\n/** One read of the children and of each child's counts. */\nasync function readLegPass(\n client: GenealogyClient,\n nodeId: string,\n maxChildren: number,\n): Promise<LegEntry[]> {\n const page = await collectPages<GenealogyNode>(\n client,\n path(\"nodes\", nodeId, \"children\"),\n {},\n \"nodes\",\n {\n // One extra row tells us the cap was exceeded without paging further.\n limit: maxChildren + 1,\n maxPageSize: NODE_PAGE_MAX,\n },\n );\n if (page.items.length > maxChildren) {\n throw new GenealogyError(\n `Node ${nodeId} has more than ${maxChildren} direct children, and ranking legs reads each child's counts with its own request. Pass --limit <n> to count up to n children.`,\n );\n }\n return mapLimit(page.items, CONCURRENCY, (child) =>\n readCounts(client, child.id).then(\n (counts): LegEntry => ({\n leg: child.leg,\n node_id: child.id,\n member_id: child.member_id,\n downline_count: counts.downline_count,\n seats: counts.downline_count + 1,\n counts_as_of: counts.counts_as_of,\n }),\n ),\n );\n}\n\nfunction sameCounts(\n a: NodeRelationship | null | undefined,\n b: NodeRelationship | null | undefined,\n): boolean {\n return (\n a?.children_count === b?.children_count &&\n a?.downline_count === b?.downline_count\n );\n}\n\n/** Who holds which leg and how many seats, in a stable order. */\nfunction legKey(entries: LegEntry[]): string {\n return entries\n .map((entry) => `${entry.node_id}:${entry.leg}:${entry.seats}`)\n .sort()\n .join(\"|\");\n}\n\n/** The node's structural counts include seats of discarded members, which\n * the children read omits; the difference is what was left out. */\nfunction hiddenChildren(\n anchor: NodeRelationship | null | undefined,\n counted: LegEntry[],\n): { children: number; seats: number } | undefined {\n if (typeof anchor?.children_count !== \"number\") return undefined;\n const children = anchor.children_count - counted.length;\n if (children <= 0) return undefined;\n const rankedSeats = counted.reduce((sum, entry) => sum + entry.seats, 0);\n const seats =\n typeof anchor.downline_count === \"number\"\n ? Math.max(children, anchor.downline_count - rankedSeats)\n : children;\n return { children, seats };\n}\n\nexport interface LegPick {\n measure: typeof LEG_MEASURE;\n counted: typeof LEG_COUNTED;\n node_id: string;\n /** Null when there are no legs, or when unlisted seats could change it. */\n leg: LegEntry | null;\n /** Other legs with the same headcount, if any. */\n tied_with: LegEntry[];\n /** Set when some legs could not be listed. */\n unlisted?: UnlistedLegs;\n /** With no certain pick: the pick among the listed legs. */\n best_listed?: LegEntry | null;\n note: string;\n}\n\n/** The strong leg is the largest by headcount, the weak leg the smallest.\n * With unlisted children a leg is named only when they cannot change the\n * pick: one unlisted child is ranked with its exact size; with more, only\n * a strong leg that outnumbers all their seats together is certain. A\n * weak pick is withheld while an empty leg cannot be named, including\n * when the tree's leg names could not be read. */\nexport function pickLeg(report: LegsReport, which: \"strong\" | \"weak\"): LegPick {\n const legs = which === \"strong\" ? report.legs : [...report.legs].reverse();\n const top = legs[0] ?? null;\n const base = {\n measure: report.measure,\n counted: report.counted,\n node_id: report.node_id,\n };\n const tiedWith = (leg: LegEntry) =>\n legs.slice(1).filter((other) => other.seats === leg.seats);\n const { unlisted } = report;\n // An empty leg has no seats, so it never changes the strong leg; it is the\n // weak leg whenever there is one, so a weak pick needs every leg known.\n const certain =\n which === \"strong\"\n ? !unlisted ||\n unlisted.ranked ||\n (top !== null && top.seats > unlisted.seats)\n : report.leg_names_known &&\n (!unlisted || (unlisted.ranked && unlisted.unknown_empty_legs === 0));\n if (certain) {\n return {\n ...base,\n leg: top,\n tied_with: top ? tiedWith(top) : [],\n ...(unlisted ? { unlisted } : {}),\n note: top ? report.note : \"This node has no legs.\",\n };\n }\n const reason = !report.leg_names_known\n ? \"the tree's leg names could not be read, so an empty named leg may be smaller\"\n : unlisted?.ranked\n ? \"an empty named leg is smaller, but it cannot be told from the leg the unlisted child holds\"\n : \"it may be one that is not listed\";\n return {\n ...base,\n leg: null,\n tied_with: [],\n ...(unlisted ? { unlisted } : {}),\n best_listed: top,\n note: `${report.note} The ${which} leg cannot be told: ${reason}.${top ? \" best_listed is the pick among the listed legs.\" : \"\"}`,\n };\n}\n\n// Downline tree text\n\n/** Read the subtree to depth (1-7) for `tree`; every page is followed. */\nexport async function readTree(\n client: GenealogyClient,\n nodeId: string,\n depth: string | number | undefined,\n): Promise<{ nodes: GenealogyNode[]; depthTruncated: boolean; depth: number }> {\n const bounded = toPositiveInt(\"--depth\", depth ?? 3, MAX_DEPTH) as number;\n const result = await readNodePage(client, nodeId, \"subtree\", {\n depth: bounded,\n });\n return {\n nodes: result.nodes ?? [],\n depthTruncated: result.depth_truncated ?? false,\n depth: bounded,\n };\n}\n\nfunction label(node: GenealogyNode): string {\n const leg = node.leg ? ` [${node.leg}]` : \"\";\n return `member ${node.member_id}${leg} (node ${node.id})`;\n}\n\n/** Indent a subtree page (anchor at relative depth 0) as a text tree. */\nexport function renderTree(\n nodes: GenealogyNode[],\n depthTruncated: boolean,\n depth: number,\n): string {\n const anchor = nodes.find((node) => node.relative_depth === 0) ?? nodes[0];\n if (!anchor) return \"(no nodes)\\n\";\n const children = new Map<string, GenealogyNode[]>();\n for (const node of nodes) {\n if (node === anchor || !node.parent_node_id) continue;\n const siblings = children.get(node.parent_node_id) ?? [];\n siblings.push(node);\n children.set(node.parent_node_id, siblings);\n }\n const lines = [label(anchor)];\n const walk = (parentId: string, prefix: string): void => {\n const kids = children.get(parentId) ?? [];\n kids.forEach((kid, index) => {\n const last = index === kids.length - 1;\n lines.push(`${prefix}${last ? \"└── \" : \"├── \"}${label(kid)}`);\n walk(kid.id, `${prefix}${last ? \" \" : \"│ \"}`);\n });\n };\n walk(anchor.id, \"\");\n if (depthTruncated) {\n lines.push(`… more seats below depth ${depth} (depth_truncated)`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\n// Writes\n\nexport const PLACEMENT_RULES = [\n \"exact\",\n \"first_empty\",\n \"left\",\n \"right\",\n \"inside\",\n \"outside\",\n \"balanced\",\n \"weak_leg\",\n \"strong_leg\",\n \"enroller_preference\",\n \"defer\",\n] as const;\n\nexport interface PlacementInput {\n /** Parent seat, or where a rule's search starts; null places a root. */\n parent?: string | null;\n leg?: string;\n rule?: string;\n reason?: string;\n}\n\nfunction placementFields(input: PlacementInput): Record<string, unknown> {\n if (\n input.rule !== undefined &&\n !(PLACEMENT_RULES as readonly string[]).includes(input.rule)\n ) {\n throw new GenealogyError(\n `--rule must be one of ${PLACEMENT_RULES.join(\", \")}.`,\n );\n }\n const fields: Record<string, unknown> = {};\n if (input.parent !== undefined) {\n fields[\"parent_node_id\"] =\n input.parent === null ? null : requireUuid(\"Parent node\", input.parent);\n }\n if (input.leg !== undefined) fields[\"leg\"] = input.leg;\n if (input.rule !== undefined) fields[\"rule\"] = input.rule;\n if (input.reason !== undefined) fields[\"reason\"] = input.reason;\n return fields;\n}\n\nfunction withoutMeta(data: unknown): unknown {\n if (data && typeof data === \"object\") {\n const { meta: _meta, ...rest } = data as Record<string, unknown>;\n return rest;\n }\n return data;\n}\n\n/** Place a seat waiting in the holding tank. */\nexport async function placeHeldSeat(\n client: GenealogyClient,\n nodeId: string,\n input: PlacementInput,\n): Promise<unknown> {\n if (input.rule === \"defer\") {\n throw new GenealogyError(\"A held seat can't be deferred again.\");\n }\n if (input.parent === undefined) {\n throw new GenealogyError(\"Pass --parent <node> (or --root) for the seat.\");\n }\n const data = await client.post(path(\"nodes\", nodeId, \"place\"), {\n placement: placementFields(input),\n });\n return withoutMeta(data);\n}\n\n/** Give a member a new seat in a binary or matrix tree, or hold it in the\n * holding tank with the rule defer. */\nexport async function placeMember(\n client: GenealogyClient,\n treeId: string,\n memberId: string,\n input: PlacementInput & { position?: number },\n): Promise<unknown> {\n const member = requireUuid(\"Member\", memberId);\n const position = input.position;\n if (\n position !== undefined &&\n !(Number.isInteger(position) && position >= 0)\n ) {\n throw new GenealogyError(\"--position must be a whole number, 0 or more.\");\n }\n if (input.rule === \"defer\") {\n if (\n input.parent !== undefined ||\n input.leg !== undefined ||\n input.reason !== undefined\n ) {\n throw new GenealogyError(\n \"The rule defer takes no --parent, --root, --leg or --reason; give the reason when the held seat is placed.\",\n );\n }\n } else if (input.parent === undefined) {\n throw new GenealogyError(\n \"Pass --parent <node> (or --root), or --rule defer to hold the seat.\",\n );\n }\n const data = await client.post(path(\"trees\", treeId, \"nodes\"), {\n node: {\n member_id: member,\n ...placementFields(input),\n ...(position !== undefined ? { position } : {}),\n },\n });\n return withoutMeta(data);\n}\n\nexport interface MoveInput {\n /** New parent node, or null for a root move. */\n parent: string | null;\n leg?: string;\n reason?: string;\n}\n\nfunction moveBody(input: MoveInput): { move: Record<string, unknown> } {\n return {\n move: {\n parent_node_id:\n input.parent === null ? null : requireUuid(\"Parent node\", input.parent),\n ...(input.leg !== undefined ? { leg: input.leg } : {}),\n ...(input.reason !== undefined ? { reason: input.reason } : {}),\n },\n };\n}\n\n/** Preview a move; writes and enqueues nothing. */\nexport async function validateMove(\n client: GenealogyClient,\n nodeId: string,\n input: MoveInput,\n): Promise<unknown> {\n const data = await client.post<{ validation?: unknown }>(\n path(\"nodes\", nodeId, \"move\", \"validate\"),\n moveBody(input),\n );\n return { validation: data.validation ?? null };\n}\n\n/** Request a move. It applies now (status 200), or returns a move request\n * awaiting approval or a deferred move job (status 202). */\nexport async function requestMove(\n client: GenealogyClient,\n nodeId: string,\n input: MoveInput,\n idempotencyKey?: string,\n): Promise<unknown> {\n const data = await client.post(\n path(\"nodes\", nodeId, \"move\"),\n moveBody(input),\n idempotencyKey\n ? { headers: { \"Idempotency-Key\": idempotencyKey } }\n : undefined,\n );\n return withoutMeta(data);\n}\n\nexport async function approveMoveRequest(\n client: GenealogyClient,\n id: string,\n reason?: string,\n): Promise<unknown> {\n const data = await client.post(\n path(\"move-requests\", requireUuid(\"Move request\", id), \"approve\"),\n reason !== undefined ? { decision: { reason } } : undefined,\n );\n return withoutMeta(data);\n}\n\nexport async function rejectMoveRequest(\n client: GenealogyClient,\n id: string,\n reason: string | undefined,\n): Promise<unknown> {\n if (!reason || !/\\S/.test(reason)) {\n throw new GenealogyError(\"Rejecting a move request needs --reason <text>.\");\n }\n const data = await client.post(\n path(\"move-requests\", requireUuid(\"Move request\", id), \"reject\"),\n { decision: { reason } },\n );\n return withoutMeta(data);\n}\n\n// Helpers\n\n/** Map with at most `limit` calls in flight, keeping input order. */\nexport async function mapLimit<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = [];\n let next = 0;\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const index = next;\n next += 1;\n results[index] = await fn(items[index] as T);\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, worker),\n );\n return results;\n}\n","import type { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport type { CommandContext, FluidPlugin } from \"@fluid-app/fluid-cli\";\nimport { createDomainCommand } from \"@fluid-app/fluid-cli\";\n\nimport {\n approveMoveRequest,\n DEFAULT_LEG_CHILD_LIMIT,\n DEFAULT_TREE,\n GenealogyError,\n getMoveJob,\n getMoveRequest,\n getMirrorRun,\n listMirrorRuns,\n listMoveJobs,\n listMoveRequests,\n listTrees,\n MAX_DEPTH,\n mirrorRunFinished,\n previewMirror,\n startMirror,\n waitForMirrorRun,\n type MirrorRun,\n MOVEMENT_APPROVALS,\n planCreateTree,\n planDeleteTree,\n planUpdateTree,\n sendTreeWrite,\n TREE_AUTHORITIES,\n TREE_KINDS,\n TREE_PLACEMENT_RULES,\n TREE_ROLES,\n VOLUME_ON_MOVE,\n type TreeFieldsInput,\n type TreeWrite,\n pickLeg,\n placeHeldSeat,\n placeMember,\n readCounts,\n readGrandchildren,\n readHoldingTank,\n readLegs,\n readLevelCounts,\n readNode,\n readNodePage,\n readPlacements,\n readRelation,\n readRelative,\n readTree,\n rejectMoveRequest,\n renderTree,\n requestMove,\n resolveSubject,\n resolveTreeId,\n toPositiveInt,\n validateMove,\n type GenealogyClient,\n type LegsOptions,\n type NodePageOperation,\n type PageOptions,\n type RelativeOperation,\n} from \"./api.js\";\n\nasync function client(ctx: CommandContext): Promise<GenealogyClient> {\n return (await ctx.getClient()) as unknown as GenealogyClient;\n}\n\n/**\n * Placements and moves change the live tree and need an explicit yes.\n * Scripts and agents pass `--yes`; an interactive terminal is asked. With\n * neither, nothing runs.\n */\nexport async function confirmChange(\n question: string,\n yes: boolean,\n interactive: boolean = Boolean(process.stdin.isTTY),\n): Promise<void> {\n if (yes) return;\n if (!interactive) {\n throw new GenealogyError(`${question} Pass --yes to confirm.`);\n }\n const answer = await prompts({\n type: \"confirm\",\n name: \"ok\",\n message: question,\n initial: false,\n });\n if (!answer.ok) throw new GenealogyError(\"Cancelled.\");\n}\n\n/** Print a failure as JSON on stderr and exit non-zero, so scripts and\n * agents can read it the same way as a success. Genealogy errors carry\n * `error.message` and a machine-readable `error.details.type`. */\nfunction fail(err: unknown): void {\n const status =\n typeof (err as { status?: unknown }).status === \"number\"\n ? (err as { status: number }).status\n : undefined;\n const body = (err as { body?: Record<string, unknown> | null }).body;\n const nested = body?.[\"error\"] as\n | { message?: unknown; details?: { type?: unknown } }\n | string\n | undefined;\n const candidates = [\n body?.[\"error_message\"],\n typeof nested === \"object\" ? nested?.message : nested,\n body?.[\"message\"],\n ];\n const fromBody = candidates.find((c): c is string => typeof c === \"string\");\n // A GenealogyError restating an API failure carries the readable message.\n const restated = err instanceof GenealogyError;\n const message = restated\n ? err.message\n : (fromBody ?? (err instanceof Error ? err.message : String(err)));\n const details =\n typeof nested === \"object\" && nested?.details\n ? (nested.details as Record<string, unknown>)\n : undefined;\n const type =\n restated && err.type\n ? err.type\n : typeof details?.[\"type\"] === \"string\"\n ? details[\"type\"]\n : undefined;\n // Validation failures (\"Invalid parameters\") explain themselves only in\n // their details, e.g. { node: [\"a placement rule chooses the leg ...\"] }.\n const { type: _type, ...reasons } = details ?? {};\n process.stderr.write(\n JSON.stringify({\n error: message,\n ...(type ? { type } : {}),\n ...(status ? { status } : {}),\n ...(Object.keys(reasons).length > 0 ? { details: reasons } : {}),\n }) + \"\\n\",\n );\n process.exitCode = 1;\n}\n\n/** One stderr line per reading while --wait follows a mirror run. */\nfunction reportMirrorProgress(mirrorRun: MirrorRun): void {\n const counts = Object.entries(mirrorRun.counts)\n .map(([outcome, n]) => `${outcome} ${n}`)\n .join(\", \");\n const state = mirrorRunFinished(mirrorRun) ? \"finished\" : \"progress\";\n process.stderr.write(\n `mirror run ${mirrorRun.id} ${state}: ${mirrorRun.status}, window ${mirrorRun.windows}${counts ? `, ${counts}` : \"\"}\\n`,\n );\n}\n\nfunction run(action: () => Promise<void>): Promise<void> {\n return action().catch(fail);\n}\n\ninterface SubjectOpts {\n member?: string;\n tree: string;\n}\n\ninterface PagingOpts {\n limit?: string;\n cursor?: string;\n pageSize?: string;\n}\n\nfunction paging(opts: PagingOpts): PageOptions {\n return {\n limit: toPositiveInt(\"--limit\", opts.limit),\n pageSize: toPositiveInt(\"--page-size\", opts.pageSize),\n cursor: opts.cursor,\n };\n}\n\n/** A command whose subject is `[node]` or `--member` in `--tree`. */\nfunction subjectCommand(parent: Command, name: string, description: string) {\n return parent\n .command(`${name} [node]`)\n .description(description)\n .option(\n \"--member <member-id>\",\n \"Use this member's seat instead of a node id\",\n )\n .option(\n \"--tree <key-or-id>\",\n \"Tree for --member (key or UUID)\",\n DEFAULT_TREE,\n );\n}\n\nfunction withPaging(command: Command, idsOnly = true): Command {\n command\n .option(\"--limit <n>\", \"Most rows to return across pages\")\n .option(\"--cursor <cursor>\", \"Resume from a next_cursor\")\n .option(\"--page-size <n>\", \"Rows per request\");\n if (idsOnly) command.option(\"--ids-only\", \"Return node ids only\", false);\n return command;\n}\n\n/** Every writable tree field as a flag. Clearable fields take `none`. */\nfunction withTreeFields(command: Command): Command {\n return command\n .option(\"--key <key>\", \"Tree key, unique per company\")\n .option(\"--name <name>\", \"Display name\")\n .option(\"--kind <kind>\", `Tree kind: ${TREE_KINDS.join(\", \")}`)\n .option(\n \"--role <role>\",\n `Default-tree role: ${TREE_ROLES.join(\", \")}, or none`,\n )\n .option(\n \"--leg-names <legs>\",\n \"Comma-separated legs: two for binary (default left,right), two or more for matrix\",\n )\n .option(\n \"--placement-rule <rule>\",\n `Default placement rule: ${TREE_PLACEMENT_RULES.join(\", \")}. Binary and matrix trees accept exact, first_empty, left, right or defer; unilevel trees exact, under_enroller or mirror_enrollment`,\n )\n .option(\n \"--movement-approval <mode>\",\n `Who approves member-requested moves: ${MOVEMENT_APPROVALS.join(\", \")}`,\n )\n .option(\n \"--holding-tank-days <n>\",\n \"Days a deferred seat may wait in the holding tank (0 turns it off)\",\n )\n .option(\"--max-depth <n>\", \"Deepest level the tree allows\")\n .option(\n \"--max-movement-levels <n>\",\n \"How many levels a member move may cross (0 = no limit)\",\n )\n .option(\n \"--movement-window-days <n>\",\n \"Days after placement during which a seat may move (0 = no window)\",\n )\n .option(\n \"--volume-on-move <mode>\",\n `Where volume goes when a seat moves: ${VOLUME_ON_MOVE.join(\", \")}, or none; required for binary and matrix`,\n )\n .option(\"--volume-key <key>\", \"Volume key the tree ranks by, or none\")\n .option(\n \"--allow-multiple-positions <bool>\",\n \"true lets a member hold several seats (matrix trees)\",\n )\n .option(\n \"--authority <authority>\",\n `Ownership: ${TREE_AUTHORITIES.join(\", \")}. Only authoritative trees take placements`,\n )\n .option(\n \"--owning-company-integration-id <id>\",\n \"Integration that owns a replica tree, or none\",\n )\n .option(\n \"--upstream-tree-ref <ref>\",\n \"The back office's id for a replica tree, or none\",\n )\n .option(\n \"--external-integration-market-id <id>\",\n \"The back office market a replica tree belongs to, or none\",\n )\n .option(\"--dry-run\", \"Print the request and send nothing\", false)\n .option(\"--yes\", \"Confirm without a prompt\", false);\n}\n\n/** `trees create | update <tree> | delete <tree>`. Writes show the request\n * and ask first unless --yes; --dry-run prints it and sends nothing. */\nfunction registerTreeWrites(trees: Command, ctx: CommandContext): void {\n const send = async (\n api: () => Promise<GenealogyClient>,\n write: TreeWrite,\n label: string,\n opts: { dryRun: boolean; yes: boolean },\n verb: string,\n ) => {\n if (opts.dryRun) {\n ctx.output({ dry_run: true, request: write });\n return;\n }\n const shown = write.body ?? { params: write.params };\n await confirmChange(\n `${verb} tree ${label} with ${write.method} ${write.path} ${JSON.stringify(shown)}?`,\n opts.yes,\n );\n ctx.output(await sendTreeWrite(await api(), write, label));\n };\n\n const permission =\n \"Needs an admin login with developer.update; a company API token works if its role grants it.\";\n\n withTreeFields(\n trees\n .command(\"create\")\n .description(\n `Add a tree definition. --key, --name, --kind and --authority are required. ${permission}`,\n ),\n ).action((opts: TreeFieldsInput & { dryRun: boolean; yes: boolean }) =>\n run(async () => {\n const write = planCreateTree(opts);\n await send(() => client(ctx), write, String(opts.key), opts, \"Create\");\n }),\n );\n\n withTreeFields(\n trees\n .command(\"update <tree>\")\n .description(\n `Change a tree definition by key or UUID; sends only the flags given. An occupied tree can only be renamed. ${permission}`,\n ),\n )\n .option(\n \"--lock-version <n>\",\n \"Update only if the tree is still at this version (default: the current one)\",\n )\n .action(\n (\n tree: string,\n opts: TreeFieldsInput & {\n lockVersion?: string;\n dryRun: boolean;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const write = await planUpdateTree(api, tree, opts, opts.lockVersion);\n await send(async () => api, write, tree, opts, \"Update\");\n }),\n );\n\n trees\n .command(\"mirror [tree]\")\n .description(\n \"Mirror the enrollment tree into the placement tree: seat each enrolled member without a placement seat under their enroller's seat (roots first) and re-parent unpinned seats; pinned seats stay. Idempotent. A real run is queued in the background and printed; --wait follows it to the end. Needs an admin login with members.manage, GENEALOGY_TREE_WRITES, authoritative default trees and the mirror_enrollment rule.\",\n )\n .option(\n \"--dry-run\",\n \"Report at once what would change and write nothing\",\n false,\n )\n .option(\n \"--wait\",\n \"Follow the run until it finishes; exit non-zero if it fails or halts\",\n false,\n )\n .option(\"--interval <seconds>\", \"Seconds between readings with --wait\", \"2\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n tree: string | undefined,\n opts: {\n dryRun: boolean;\n wait: boolean;\n interval: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const label = tree ?? DEFAULT_TREE;\n const treeId = await resolveTreeId(api, label);\n if (opts.dryRun) {\n ctx.output(await previewMirror(api, treeId));\n return;\n }\n const intervalMs =\n (toPositiveInt(\"--interval\", opts.interval) ?? 2) * 1000;\n await confirmChange(\n `Mirror the enrollment tree into tree ${label}?`,\n opts.yes,\n );\n const started = await startMirror(api, treeId);\n if (!opts.wait || !started.mirror_run) {\n ctx.output(started);\n return;\n }\n const finished = await waitForMirrorRun(api, started.mirror_run, {\n intervalMs,\n onProgress: reportMirrorProgress,\n });\n ctx.output({ mirror_run: finished, started: started.started });\n if (finished.status !== \"succeeded\") {\n throw new GenealogyError(\n finished.failure_description ?? `Mirror run ${finished.status}`,\n finished.failure_type ? { type: finished.failure_type } : {},\n );\n }\n }),\n );\n\n const mirrorRuns = trees\n .command(\"mirror-runs\")\n .description(\"Read background placement mirror runs\");\n mirrorRuns\n .command(\"list\")\n .description(\"List the company's most recent mirror runs, newest first\")\n .option(\"--status <status>\", \"queued, running, succeeded, failed or halted\")\n .option(\"--limit <n>\", \"How many runs, 1 to 100 (default 25)\")\n .action((opts: { status?: string; limit?: string }) =>\n run(async () => {\n ctx.output(\n await listMirrorRuns(await client(ctx), {\n status: opts.status,\n limit: toPositiveInt(\"--limit\", opts.limit),\n }),\n );\n }),\n );\n mirrorRuns\n .command(\"show <id>\")\n .description(\"Read a mirror run's progress\")\n .action((id: string) =>\n run(async () => {\n ctx.output({ mirror_run: await getMirrorRun(await client(ctx), id) });\n }),\n );\n\n trees\n .command(\"delete <tree>\")\n .description(\n `Delete a custom tree by key or UUID. It fails with tree_has_nodes while the tree holds any seat, and the default enrollment and placement trees can't be deleted. ${permission}`,\n )\n .option(\n \"--lock-version <n>\",\n \"Delete only if the tree is still at this version (default: the current one)\",\n )\n .option(\"--dry-run\", \"Print the request and send nothing\", false)\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n tree: string,\n opts: { lockVersion?: string; dryRun: boolean; yes: boolean },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const write = await planDeleteTree(api, tree, opts.lockVersion);\n await send(async () => api, write, tree, opts, \"Delete\");\n }),\n );\n}\n\nexport function registerGenealogyCommands(\n parent: Command,\n ctx: CommandContext,\n): void {\n const subject = async (\n api: GenealogyClient,\n node: string | undefined,\n opts: SubjectOpts,\n ) => resolveSubject(api, node, opts.member, opts.tree);\n\n const trees = parent\n .command(\"trees\")\n .description(\n \"List the company's genealogy tree definitions, or create, update or delete one (needs developer.view; writes need developer.update)\",\n )\n .action(() =>\n run(async () =>\n ctx.output({ trees: await listTrees(await client(ctx)) }),\n ),\n );\n registerTreeWrites(trees, ctx);\n\n subjectCommand(parent, \"show\", \"Read a node, with child and downline counts\")\n .option(\"--as-of <time>\", \"Read the recorded placement at this time\")\n .action((node: string | undefined, opts: SubjectOpts & { asOf?: string }) =>\n run(async () => {\n const api = await client(ctx);\n ctx.output(\n await readNode(api, await subject(api, node, opts), opts.asOf),\n );\n }),\n );\n\n subjectCommand(\n parent,\n \"counts\",\n \"Children and downline counts (retained seats, including discarded members)\",\n ).action((node: string | undefined, opts: SubjectOpts) =>\n run(async () => {\n const api = await client(ctx);\n ctx.output(await readCounts(api, await subject(api, node, opts)));\n }),\n );\n\n const relatives: Array<[RelativeOperation, string, boolean]> = [\n [\"parent\", \"Read the parent node (null for a root)\", true],\n [\"root\", \"Read the root of the node's tree\", true],\n [\"sponsor\", \"Read the sponsor: the parent in the sponsor-role tree\", false],\n [\n \"enroller\",\n \"Read the enroller: the parent in the enrollment-role tree\",\n false,\n ],\n ];\n for (const [operation, description, asOf] of relatives) {\n const command = subjectCommand(parent, operation, description);\n if (asOf) command.option(\"--as-of <time>\", \"Read as of this time\");\n command.action(\n (node: string | undefined, opts: SubjectOpts & { asOf?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(await readRelative(api, id, operation, opts.asOf));\n }),\n );\n }\n\n const pages: Array<[NodePageOperation, string, \"depth\" | \"asOf\" | null]> = [\n [\"ancestors\", \"List ancestors, root first\", \"asOf\"],\n [\"children\", \"List direct children\", null],\n [\"siblings\", \"List siblings (excluding the node)\", null],\n [\"frontline\", \"List direct children in the sponsor-role tree\", null],\n [\n \"personally-enrolled\",\n \"List direct children in the enrollment-role tree\",\n null,\n ],\n [\"descendants\", \"List descendants to --depth\", \"depth\"],\n [\"subtree\", \"List the node and its descendants to --depth\", \"depth\"],\n [\"level\", \"List the nodes exactly --depth levels down\", \"depth\"],\n ];\n for (const [operation, description, extra] of pages) {\n const command = withPaging(\n subjectCommand(parent, operation, description),\n operation !== \"ancestors\",\n );\n if (extra === \"depth\") {\n command.option(\"--depth <n>\", `Relative depth, 1-${MAX_DEPTH}`);\n }\n if (extra) command.option(\"--as-of <time>\", \"Read as of this time\");\n command.action(\n (\n node: string | undefined,\n opts: SubjectOpts &\n PagingOpts & { depth?: string; asOf?: string; idsOnly?: boolean },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(\n await readNodePage(api, id, operation, {\n ...paging(opts),\n depth: opts.depth,\n asOf: opts.asOf,\n idsOnly: opts.idsOnly,\n }),\n );\n }),\n );\n }\n\n withPaging(\n subjectCommand(parent, \"grandchildren\", \"List the nodes two levels down\"),\n )\n .option(\"--as-of <time>\", \"Read as of this time\")\n .action(\n (\n node: string | undefined,\n opts: SubjectOpts & PagingOpts & { asOf?: string; idsOnly?: boolean },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(\n await readGrandchildren(api, id, {\n ...paging(opts),\n asOf: opts.asOf,\n idsOnly: opts.idsOnly,\n }),\n );\n }),\n );\n\n subjectCommand(parent, \"level-counts\", \"Count seats at each level to --depth\")\n .option(\"--depth <n>\", `Relative depth, 1-${MAX_DEPTH}`)\n .action(\n (node: string | undefined, opts: SubjectOpts & { depth?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(await readLevelCounts(api, id, opts.depth));\n }),\n );\n\n parent\n .command(\"relation <node> <other>\")\n .description(\n \"How <node> relates to <other>: one --kind, or every kind (for within_downline, <other> is the root)\",\n )\n .option(\n \"--kind <kind>\",\n \"ancestor_of, descendant_of, parent_of, child_of, sibling_of, root_of, indirect_of, in_subtree_of or within_downline\",\n )\n .action((node: string, other: string, opts: { kind?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const id = await resolveSubject(api, node, undefined);\n ctx.output(await readRelation(api, id, other, opts.kind));\n }),\n );\n\n withPaging(\n subjectCommand(\n parent,\n \"placements\",\n \"List recorded placements, newest first\",\n ),\n false,\n ).action((node: string | undefined, opts: SubjectOpts & PagingOpts) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n ctx.output(await readPlacements(api, id, paging(opts)));\n }),\n );\n\n withPaging(\n parent\n .command(\"holding-tank\")\n .description(\n \"List seats waiting in a tree's holding tank, soonest due first\",\n )\n .option(\"--tree <key-or-id>\", \"Tree (key or UUID)\", DEFAULT_TREE),\n false,\n ).action((opts: PagingOpts & { tree: string }) =>\n run(async () => {\n const api = await client(ctx);\n const treeId = await resolveTreeId(api, opts.tree);\n ctx.output(await readHoldingTank(api, treeId, paging(opts)));\n }),\n );\n\n // Legs are ranked by headcount only: the API has no leg volume. Each\n // child's counts is its own request, so the child count is capped.\n const legLimit = (command: Command) =>\n command.option(\n \"--limit <n>\",\n `Most direct children to count, one request each (default ${DEFAULT_LEG_CHILD_LIMIT}); more is an error`,\n );\n const legOptions = (opts: { limit?: string }): LegsOptions => ({\n maxChildren: toPositiveInt(\"--limit\", opts.limit),\n });\n legLimit(\n subjectCommand(\n parent,\n \"legs\",\n \"Rank the node's legs by headcount (child plus its downline), largest first\",\n ),\n ).action((node: string | undefined, opts: SubjectOpts & { limit?: string }) =>\n run(async () => {\n const api = await client(ctx);\n ctx.output(\n await readLegs(api, await subject(api, node, opts), legOptions(opts)),\n );\n }),\n );\n for (const which of [\"strong\", \"weak\"] as const) {\n legLimit(\n subjectCommand(\n parent,\n `${which}-leg`,\n `The ${which === \"strong\" ? \"largest\" : \"smallest\"} leg by headcount`,\n ),\n ).action(\n (node: string | undefined, opts: SubjectOpts & { limit?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const report = await readLegs(\n api,\n await subject(api, node, opts),\n legOptions(opts),\n );\n ctx.output(pickLeg(report, which));\n }),\n );\n }\n\n subjectCommand(parent, \"tree\", \"Print the downline as an indented text tree\")\n .option(\"--depth <n>\", `Levels to show, 1-${MAX_DEPTH}`, \"3\")\n .action((node: string | undefined, opts: SubjectOpts & { depth: string }) =>\n run(async () => {\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n const read = await readTree(api, id, opts.depth);\n process.stdout.write(\n renderTree(read.nodes, read.depthTruncated, read.depth),\n );\n }),\n );\n\n subjectCommand(\n parent,\n \"place\",\n \"Place a held seat, or with --member give that member a new seat in --tree (changes the live tree)\",\n )\n .option(\"--parent <node>\", \"Parent seat, or where a rule's search starts\")\n .option(\"--root\", \"Place as a root seat\", false)\n .option(\n \"--leg <leg>\",\n \"Leg under the parent, e.g. left or right; with neither --leg nor --rule the tree's default rule applies\",\n )\n .option(\n \"--rule <rule>\",\n \"Placement rule, e.g. first_empty, left, right, defer\",\n )\n .option(\n \"--position <n>\",\n \"Seat position (matrix trees with multiple positions)\",\n )\n .option(\"--reason <text>\", \"Reason recorded on the placement\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n node: string | undefined,\n opts: SubjectOpts & {\n parent?: string;\n root: boolean;\n leg?: string;\n rule?: string;\n position?: string;\n reason?: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n if (opts.parent && opts.root) {\n throw new GenealogyError(\"Pass --parent or --root, not both.\");\n }\n if (node && opts.member) {\n throw new GenealogyError(\n \"Pass a held node id or --member, not both.\",\n );\n }\n const api = await client(ctx);\n const input = {\n parent: opts.root ? null : opts.parent,\n leg: opts.leg,\n rule: opts.rule,\n reason: opts.reason,\n };\n if (opts.member) {\n const treeId = await resolveTreeId(api, opts.tree);\n await confirmChange(\n `Place member ${opts.member} in tree ${opts.tree}?`,\n opts.yes,\n );\n ctx.output(\n await placeMember(api, treeId, opts.member, {\n ...input,\n position:\n opts.position === undefined\n ? undefined\n : Number(opts.position),\n }),\n );\n return;\n }\n if (opts.position !== undefined) {\n // A held seat keeps the position it was held at.\n throw new GenealogyError(\n \"--position applies only with --member; a held seat keeps its stored position.\",\n );\n }\n const id = await resolveSubject(api, node, undefined);\n await confirmChange(`Place held seat ${id}?`, opts.yes);\n ctx.output(await placeHeldSeat(api, id, input));\n }),\n );\n\n subjectCommand(\n parent,\n \"move\",\n \"Move a node under a new parent (changes the live tree); --validate previews only\",\n )\n .option(\"--to <node>\", \"New parent node\")\n .option(\n \"--to-member <member-id>\",\n \"New parent: this member's seat in --tree\",\n )\n .option(\"--to-root\", \"Make the node a root\", false)\n .option(\"--leg <leg>\", \"Leg under the new parent (binary and matrix trees)\")\n .option(\"--reason <text>\", \"Reason recorded on the move\")\n .option(\"--validate\", \"Preview the move without changing anything\", false)\n .option(\"--dry-run\", \"Same as --validate\", false)\n .option(\"--idempotency-key <key>\", \"Make a retried request safe to repeat\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n node: string | undefined,\n opts: SubjectOpts & {\n to?: string;\n toMember?: string;\n toRoot: boolean;\n leg?: string;\n reason?: string;\n validate: boolean;\n dryRun: boolean;\n idempotencyKey?: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const destinations = [\n opts.to,\n opts.toMember,\n opts.toRoot || undefined,\n ];\n if (destinations.filter((d) => d !== undefined).length !== 1) {\n throw new GenealogyError(\n \"Pass exactly one of --to <node>, --to-member <member-id> or --to-root.\",\n );\n }\n const api = await client(ctx);\n const id = await subject(api, node, opts);\n const parentId = opts.toRoot\n ? null\n : await resolveSubject(api, opts.to, opts.toMember, opts.tree);\n const input = {\n parent: parentId,\n leg: opts.leg,\n reason: opts.reason,\n };\n if (opts.validate || opts.dryRun) {\n ctx.output(await validateMove(api, id, input));\n return;\n }\n await confirmChange(\n `Move ${id} ${parentId ? `under ${parentId}` : \"to a root\"}?`,\n opts.yes,\n );\n ctx.output(await requestMove(api, id, input, opts.idempotencyKey));\n }),\n );\n\n const requests = parent\n .command(\"move-requests\")\n .description(\"List, read, approve or reject move requests\");\n withPaging(\n requests\n .command(\"list\")\n .description(\"List a tree's move requests\")\n .option(\"--tree <key-or-id>\", \"Tree (key or UUID)\", DEFAULT_TREE)\n .option(\n \"--status <status>\",\n \"pending, approved, rejected, withdrawn, superseded or expired\",\n ),\n false,\n ).action((opts: PagingOpts & { tree: string; status?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const treeId = await resolveTreeId(api, opts.tree);\n ctx.output(\n await listMoveRequests(api, treeId, opts.status, paging(opts)),\n );\n }),\n );\n requests\n .command(\"show <id>\")\n .description(\"Read a move request\")\n .action((id: string) =>\n run(async () => {\n ctx.output({\n move_request: await getMoveRequest(await client(ctx), id),\n });\n }),\n );\n requests\n .command(\"approve <id>\")\n .description(\"Approve a move request (changes the live tree)\")\n .option(\"--reason <text>\", \"Reason recorded on the decision\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((id: string, opts: { reason?: string; yes: boolean }) =>\n run(async () => {\n await confirmChange(`Approve move request ${id}?`, opts.yes);\n ctx.output(\n await approveMoveRequest(await client(ctx), id, opts.reason),\n );\n }),\n );\n requests\n .command(\"reject <id>\")\n .description(\"Reject a move request with a reason\")\n .requiredOption(\"--reason <text>\", \"Why the request is rejected\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((id: string, opts: { reason: string; yes: boolean }) =>\n run(async () => {\n await confirmChange(`Reject move request ${id}?`, opts.yes);\n ctx.output(await rejectMoveRequest(await client(ctx), id, opts.reason));\n }),\n );\n\n const jobs = parent\n .command(\"move-jobs\")\n .description(\"Read deferred move jobs\");\n jobs\n .command(\"show <id>\")\n .description(\"Read a move job's progress\")\n .action((id: string) =>\n run(async () => {\n ctx.output({ move_job: await getMoveJob(await client(ctx), id) });\n }),\n );\n withPaging(\n jobs\n .command(\"list\")\n .description(\"List a tree's move jobs\")\n .option(\"--tree <key-or-id>\", \"Tree (key or UUID)\", DEFAULT_TREE)\n .option(\n \"--status <status>\",\n \"pending, running, applied, failed or superseded\",\n ),\n false,\n ).action((opts: PagingOpts & { tree: string; status?: string }) =>\n run(async () => {\n const api = await client(ctx);\n const treeId = await resolveTreeId(api, opts.tree);\n ctx.output(await listMoveJobs(api, treeId, opts.status, paging(opts)));\n }),\n );\n}\n\nconst plugin: FluidPlugin = {\n name: \"@fluid-app/fluid-cli-genealogy\",\n version: \"0.1.0\",\n register(ctx) {\n ctx.program.addCommand(\n createDomainCommand(\n \"genealogy\",\n \"Read genealogy trees, rank legs, and place or move members\",\n registerGenealogyCommands,\n ),\n );\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-app/fluid-cli-genealogy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Fluid CLI plugin for genealogy trees — parents, children, legs, placements and moves",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@fluid-app/fluid-cli": "0.1.
|
|
24
|
+
"@fluid-app/fluid-cli": "0.1.52",
|
|
25
25
|
"commander": "^12.0.0",
|
|
26
26
|
"prompts": "^2.4.2"
|
|
27
27
|
},
|