@fluid-app/fluid-cli-genealogy 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -2
- package/dist/index.d.mts +15 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1242 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +81 -3
package/README.md
CHANGED
|
@@ -1,3 +1,132 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fluid-app/fluid-cli-genealogy
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Fluid CLI plugin for genealogy trees: parents, children, legs, placements and
|
|
4
|
+
moves, over the company genealogy API (v2026-10).
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install -g @fluid-app/fluid-cli @fluid-app/fluid-cli-genealogy
|
|
8
|
+
fluid login
|
|
9
|
+
fluid genealogy trees
|
|
10
|
+
fluid genealogy show --member <member-id> # seat in the placement tree, with counts
|
|
11
|
+
fluid genealogy ancestors <node> --as-of 2026-09-01T00:00:00Z
|
|
12
|
+
fluid genealogy children <node> --limit 50 # prints next_cursor if more remain
|
|
13
|
+
fluid genealogy grandchildren <node>
|
|
14
|
+
fluid genealogy legs <node> # legs ranked by headcount
|
|
15
|
+
fluid genealogy strong-leg <node>
|
|
16
|
+
fluid genealogy tree <node> --depth 3 # indented text downline
|
|
17
|
+
fluid genealogy move <node> --to <parent> --dry-run # preview only
|
|
18
|
+
fluid genealogy move <node> --to <parent> --yes
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
A node is a member's seat in one tree. Commands take a node UUID, or
|
|
22
|
+
`--member <member-id>` with `--tree <key-or-id>` (default `placement`) to use
|
|
23
|
+
that member's seat. A member with no seat, or several, in the tree is an error.
|
|
24
|
+
|
|
25
|
+
Reads: `show`, `counts`, `parent`, `root`, `ancestors`, `sponsor`, `enroller`,
|
|
26
|
+
`children`, `siblings`, `frontline`, `personally-enrolled`, `grandchildren`,
|
|
27
|
+
`level --depth N`, `descendants --depth N`, `subtree --depth N`,
|
|
28
|
+
`level-counts --depth N`, `relation <node> <other> [--kind]`, `placements`,
|
|
29
|
+
`holding-tank`, `trees`. Deeper reads take depths 1-7. Lists follow every page;
|
|
30
|
+
`--limit` caps the total and the output's `next_cursor` resumes with
|
|
31
|
+
`--cursor`.
|
|
32
|
+
|
|
33
|
+
Legs are measured by headcount only: each leg counts the child's seat plus its
|
|
34
|
+
`downline_count`. The API has no leg volume. Counts include retained seats of
|
|
35
|
+
discarded members. The API has no per-leg count endpoint, so `legs`,
|
|
36
|
+
`strong-leg` and `weak-leg` read each direct child's counts with its own
|
|
37
|
+
request. They refuse a node with more than 200 direct children; pass
|
|
38
|
+
`--limit <n>` to count up to `n`. The children read leaves out a direct child
|
|
39
|
+
whose member was discarded, while the node's own counts keep it. There is no
|
|
40
|
+
single read of a node's children with their counts, so `legs` reads the node's
|
|
41
|
+
counts, then the children and each child's counts twice, then the node's counts
|
|
42
|
+
again, and uses them only when every read agrees; otherwise it reads again (up
|
|
43
|
+
to three times). When the node has more children than were listed, the output
|
|
44
|
+
carries `unlisted` (`children`, `seats`, `ranked`, `unknown_empty_legs`). A
|
|
45
|
+
single unlisted child is ranked with its exact size and `unlisted: true`, in the
|
|
46
|
+
named leg left free when there is only one. Empty named legs are not listed
|
|
47
|
+
when they cannot be told from an unlisted child's leg. `strong-leg` and
|
|
48
|
+
`weak-leg` name a leg only when the unlisted children cannot change it; a
|
|
49
|
+
strong leg that has more seats than all of them together still holds.
|
|
50
|
+
Otherwise they return `leg: null` with `best_listed`, the pick among the listed
|
|
51
|
+
legs.
|
|
52
|
+
|
|
53
|
+
Reads need a company admin with `members.view` (or the legacy `users.view`);
|
|
54
|
+
a company API token (`C-...`) with that permission works too. Naming a tree
|
|
55
|
+
by key, including the default `placement`, looks it up with
|
|
56
|
+
`GET /api/company/v2026-10/genealogy/tree-keys`, which lists only each tree's
|
|
57
|
+
id, key and name and needs the same `members.view`; without it, the key is
|
|
58
|
+
looked up in the tree definitions, which need `developer.view`. An API
|
|
59
|
+
without the `tree-keys` endpoint (it answers 404) falls back the same way. Only `fluid genealogy
|
|
60
|
+
trees`, which prints the full tree definitions, needs `developer.view`, because
|
|
61
|
+
those definitions carry company configuration. `legs` reads a tree's leg names
|
|
62
|
+
from those definitions too: without `developer.view` it still ranks the
|
|
63
|
+
occupied legs but cannot list empty named legs, so `weak-leg` returns
|
|
64
|
+
`leg: null` with `best_listed` (an unseen empty leg may be the weak one) and
|
|
65
|
+
the report carries `leg_names_known: false`.
|
|
66
|
+
|
|
67
|
+
Writes: `place`, `move`, `move-requests approve|reject`. They need an admin
|
|
68
|
+
user's login: run `fluid login` as a company admin with `members.manage` (for
|
|
69
|
+
`place` and `move`) or `team_moves.approve` (for `move-requests
|
|
70
|
+
approve|reject`). A company API token (`C-...`) is refused with 403 "A company
|
|
71
|
+
admin identity is required ...". Writes change the live
|
|
72
|
+
tree, so they ask for confirmation unless you pass `--yes`. `move --validate`
|
|
73
|
+
(or `--dry-run`) previews without writing. `move-requests list|show` and
|
|
74
|
+
`move-jobs list|show` read move receipts.
|
|
75
|
+
|
|
76
|
+
Tree definitions: `trees create`, `trees update <tree>` and `trees delete
|
|
77
|
+
<tree>` (a key or UUID) write the company's tree registry. They need an admin
|
|
78
|
+
login with `developer.update` (a company API token works only if its role
|
|
79
|
+
grants it). Every writable field is a flag: `--key`, `--name`, `--kind
|
|
80
|
+
unilevel|binary|matrix`, `--role`, `--leg-names left,right`,
|
|
81
|
+
`--placement-rule`, `--movement-approval immediate|require_approval`,
|
|
82
|
+
`--holding-tank-days`, `--max-depth`, `--max-movement-levels`,
|
|
83
|
+
`--movement-window-days`, `--volume-on-move`, `--volume-key`,
|
|
84
|
+
`--allow-multiple-positions true|false`, `--authority
|
|
85
|
+
authoritative|replica|unconfigured`, `--owning-company-integration-id`,
|
|
86
|
+
`--upstream-tree-ref` and `--external-integration-market-id`; `none` clears a
|
|
87
|
+
nullable field. Create needs `--key`, `--name`, `--kind` and `--authority`;
|
|
88
|
+
binary and matrix trees also need `--volume-on-move`. Binary and matrix trees accept the default rules `exact`, `first_empty`,
|
|
89
|
+
`left`, `right` and `defer`; unilevel trees `exact`, `under_enroller` and
|
|
90
|
+
`mirror_enrollment` (other rules are refused with a typed error). `place
|
|
91
|
+
--parent` with neither `--leg` nor `--rule` uses the tree's default rule. Only an authoritative
|
|
92
|
+
tree takes placements, and placing in a binary or matrix tree also needs the
|
|
93
|
+
company's `GENEALOGY_LEGGED_TREES` flag. Update sends only the flags given, at
|
|
94
|
+
the tree's current `lock_version` unless `--lock-version` pins one; an
|
|
95
|
+
occupied tree can only be renamed. Delete fails with `tree_has_nodes` while
|
|
96
|
+
the tree holds any seat, and the default `enrollment` and `placement` trees
|
|
97
|
+
can't be deleted. Each write shows the request and asks first unless `--yes`;
|
|
98
|
+
`--dry-run` prints it and sends nothing.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
fluid genealogy trees create --key team-binary --name "Team Binary" --kind binary \
|
|
102
|
+
--authority authoritative --volume-on-move follows_structure --dry-run
|
|
103
|
+
fluid genealogy trees update team-binary --movement-approval require_approval --yes
|
|
104
|
+
fluid genealogy trees delete team-binary --yes
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`trees mirror [tree]` (default `placement`) backfills the placement tree
|
|
108
|
+
from the enrollment tree for a company whose placement tree uses
|
|
109
|
+
`mirror_enrollment` and has no legacy placement tree: every enrolled member
|
|
110
|
+
without a placement seat is seated under their enroller's seat, roots
|
|
111
|
+
first, and unpinned seats that no longer match are re-parented. Pinned
|
|
112
|
+
seats stay. It is idempotent and needs an admin login with
|
|
113
|
+
`members.manage`. Member writes keep the trees mirrored afterwards.
|
|
114
|
+
|
|
115
|
+
`--dry-run` reports the counts at once and writes nothing (up to 5,000
|
|
116
|
+
enrollment seats). A real run asks first unless `--yes`, then queues a
|
|
117
|
+
background run with no seat limit and prints it (`mirror_run`, plus
|
|
118
|
+
`started: false` when the company already had a run queued or running;
|
|
119
|
+
there is one at a time). `--wait` follows the run, printing a progress line
|
|
120
|
+
per reading on stderr (every `--interval` seconds, default 2), then prints
|
|
121
|
+
the finished run and exits non-zero if it `failed` or `halted`. A halted
|
|
122
|
+
run kept the batches it wrote; run the mirror again to finish.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
fluid genealogy trees mirror --dry-run
|
|
126
|
+
fluid genealogy trees mirror --yes --wait
|
|
127
|
+
fluid genealogy trees mirror-runs list --status running
|
|
128
|
+
fluid genealogy trees mirror-runs show <run-id>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Every command except `tree` prints JSON. Failures print a JSON `error` (with the
|
|
132
|
+
API's `type` when there is one) on stderr and exit non-zero.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CommandContext, FluidPlugin } from "@fluid-app/fluid-cli";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Placements and moves change the live tree and need an explicit yes.
|
|
7
|
+
* Scripts and agents pass `--yes`; an interactive terminal is asked. With
|
|
8
|
+
* neither, nothing runs.
|
|
9
|
+
*/
|
|
10
|
+
declare function confirmChange(question: string, yes: boolean, interactive?: boolean): Promise<void>;
|
|
11
|
+
declare function registerGenealogyCommands(parent: Command, ctx: CommandContext): void;
|
|
12
|
+
declare const plugin: FluidPlugin;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { confirmChange, plugin as default, registerGenealogyCommands };
|
|
15
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;AAwEA;;;iBAAsB,aAAA,CACpB,QAAA,UACA,GAAA,WACA,WAAA,aACC,OAAA;AAAA,iBAyWa,yBAAA,CACd,MAAA,EAAQ,OAAA,EACR,GAAA,EAAK,cAAA;AAAA,cA+dD,MAAA,EAAQ,WAAA"}
|