@unisonlabs/sixminute-admin 0.1.608-next → 0.1.610-next
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 +8 -1
- package/dist/sixminute-admin.mjs +143 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,10 +58,17 @@ sixminute-admin research export --env production --tenant tenant_... --from 2026
|
|
|
58
58
|
sixminute-admin activity-type list --env production --tenant tenant_...
|
|
59
59
|
sixminute-admin member list --env production --tenant tenant_...
|
|
60
60
|
sixminute-admin matters import --env production --tenant tenant_... --from-file matters.json
|
|
61
|
+
sixminute-admin matter list --env production --tenant tenant_... --all
|
|
62
|
+
sixminute-admin matter edit --env production --tenant tenant_... --matter matter_... --name "Example Holding: lease review"
|
|
63
|
+
sixminute-admin matter archive --env production --tenant tenant_... --matter matter_...
|
|
64
|
+
sixminute-admin client edit --env production --tenant tenant_... --client client_... --external-ref C-101
|
|
61
65
|
sixminute-admin connector status --env production --firm tenant_...
|
|
62
66
|
sixminute-admin audit --env production --limit 20
|
|
63
67
|
```
|
|
64
68
|
|
|
65
69
|
`sixminute-admin --help` lists every command, with the warnings on the destructive ones.
|
|
66
|
-
|
|
70
|
+
Creating, editing and archiving clients and matters, and `matters import`, work only on a firm
|
|
71
|
+
marked internal (our own). Archive is the delete: the row and its entries stay, and an archived
|
|
72
|
+
matter leaves the pickers; nothing deletes a client or a matter. A client is archived only once
|
|
73
|
+
none of its matters is open: archive the matters first.
|
|
67
74
|
`sixminute-admin --version` prints the version and the API commit it was built from.
|
package/dist/sixminute-admin.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { homedir } from "node:os";
|
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
22
|
|
|
23
23
|
// tools/admin/src/build-info.ts
|
|
24
|
-
var BUILT = { version: "0.1.
|
|
24
|
+
var BUILT = { version: "0.1.610-next", commit: "7e589dae1055faaac0407d389ef7f333e3b84aec", surface: "e738440f007ffca1" };
|
|
25
25
|
|
|
26
26
|
// tools/admin/src/run.ts
|
|
27
27
|
import { rmSync as rmSync3 } from "node:fs";
|
|
@@ -6712,12 +6712,31 @@ is required; without one every command answers 403.
|
|
|
6712
6712
|
|
|
6713
6713
|
sixminute-admin client create --env <env> --tenant tenant_... --name "Example Holding" [--external-ref C-100] [--id client_...] [--json]
|
|
6714
6714
|
Creates one client. A name already in use is refused.
|
|
6715
|
-
sixminute-admin client list --env <env> --tenant tenant_... [--json]
|
|
6716
|
-
Every client of the firm with its id, which matter create takes
|
|
6715
|
+
sixminute-admin client list --env <env> --tenant tenant_... [--all] [--json]
|
|
6716
|
+
Every client of the firm with its id, which matter create takes, and
|
|
6717
|
+
whether it is active or archived. Archived clients are left out
|
|
6718
|
+
unless --all.
|
|
6719
|
+
sixminute-admin client edit --env <env> --tenant tenant_... --client client_... [--name "..."] [--external-ref R] [--active true|false] [--json]
|
|
6720
|
+
Changes only the fields given; at least one is required. A name
|
|
6721
|
+
already in use is refused.
|
|
6722
|
+
sixminute-admin client archive --env <env> --tenant tenant_... --client client_... [--json]
|
|
6723
|
+
sixminute-admin client unarchive --env <env> --tenant tenant_... --client client_... [--json]
|
|
6724
|
+
Sets the client inactive, or active again.
|
|
6717
6725
|
sixminute-admin matter create --env <env> --tenant tenant_... --client client_... --name "Example Holding: general advice" \\
|
|
6718
6726
|
[--number 2026-0001] [--description "..."] [--external-ref R] [--billable true|false] [--temporary true|false] [--id matter_...] [--json]
|
|
6719
6727
|
Creates one matter under a client of the firm. --number is the firm's
|
|
6720
6728
|
matter number, unique within the client. An unknown client is refused.
|
|
6729
|
+
sixminute-admin matter list --env <env> --tenant tenant_... [--client client_...] [--all] [--json]
|
|
6730
|
+
Every matter of the firm, or of one client: id, number, active or
|
|
6731
|
+
archived, client and name. Archived matters are left out unless --all.
|
|
6732
|
+
sixminute-admin matter edit --env <env> --tenant tenant_... --matter matter_... \\
|
|
6733
|
+
[--name "..."] [--number N] [--description "..."] [--external-ref R] [--active true|false] [--json]
|
|
6734
|
+
Changes only the fields given; at least one is required. A matter
|
|
6735
|
+
belongs to one client for life, so there is no --client.
|
|
6736
|
+
sixminute-admin matter archive --env <env> --tenant tenant_... --matter matter_... [--json]
|
|
6737
|
+
sixminute-admin matter unarchive --env <env> --tenant tenant_... --matter matter_... [--json]
|
|
6738
|
+
Sets the matter inactive, or active again. A temporary matter with
|
|
6739
|
+
entries on it is refused: move the entries first.
|
|
6721
6740
|
sixminute-admin matters import --env <env> --tenant tenant_... --from-file tools/admin/examples/matters-import.json [--json]
|
|
6722
6741
|
Clients with their matters, from a document: the file IS the body of
|
|
6723
6742
|
POST /v1/matters/import, checked against its schema before anything is
|
|
@@ -6725,10 +6744,14 @@ is required; without one every command answers 403.
|
|
|
6725
6744
|
missing rows are created and known rows corrected, so running the same
|
|
6726
6745
|
file again changes nothing. Prints how many were created, updated and
|
|
6727
6746
|
left alone.
|
|
6728
|
-
client
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
and nothing is written.
|
|
6747
|
+
The client and matter create, edit, archive and unarchive commands and
|
|
6748
|
+
matters import WORK ONLY ON AN INTERNAL FIRM, one marked with tenant
|
|
6749
|
+
internal set: a real firm's clients and matters come from its practice
|
|
6750
|
+
system. Any other firm is refused, exit 2, and nothing is written.
|
|
6751
|
+
Archive is the delete: it keeps the row and every entry on it, and an
|
|
6752
|
+
archived matter leaves the pickers. client archive refuses while the
|
|
6753
|
+
client has an open matter, and prints the matter archive line for each
|
|
6754
|
+
one. Nothing here deletes a client or a matter.
|
|
6732
6755
|
|
|
6733
6756
|
sixminute-admin member list --env <env> --tenant tenant_... [--json]
|
|
6734
6757
|
The firm's members and its pending invitations.
|
|
@@ -6800,7 +6823,8 @@ var BOOLEAN_FLAGS = new Set([
|
|
|
6800
6823
|
"required",
|
|
6801
6824
|
"proposed",
|
|
6802
6825
|
"confirm",
|
|
6803
|
-
"password-stdin"
|
|
6826
|
+
"password-stdin",
|
|
6827
|
+
"all"
|
|
6804
6828
|
]);
|
|
6805
6829
|
function parseFlags(argv, offset) {
|
|
6806
6830
|
const values = new Map;
|
|
@@ -6971,8 +6995,8 @@ async function runAdmin(argv, deps) {
|
|
|
6971
6995
|
return runMember(run2, subcommand);
|
|
6972
6996
|
if (group === "client")
|
|
6973
6997
|
return runClient(run2, subcommand);
|
|
6974
|
-
if (group === "matter"
|
|
6975
|
-
return
|
|
6998
|
+
if (group === "matter")
|
|
6999
|
+
return runMatter(run2, subcommand);
|
|
6976
7000
|
if (group === "matters" && subcommand === "import")
|
|
6977
7001
|
return runMattersImport(run2);
|
|
6978
7002
|
if (group === "tier" && subcommand === "set")
|
|
@@ -7457,7 +7481,34 @@ async function readMatterSets(firm, matterId) {
|
|
|
7457
7481
|
since = page.next_cursor;
|
|
7458
7482
|
}
|
|
7459
7483
|
}
|
|
7460
|
-
var
|
|
7484
|
+
var PULL_PAGE = 2000;
|
|
7485
|
+
async function pullAll(run2, tenant, path, row) {
|
|
7486
|
+
const rows = [];
|
|
7487
|
+
let since = null;
|
|
7488
|
+
let head = null;
|
|
7489
|
+
for (;; ) {
|
|
7490
|
+
const query = since === null ? `?limit=${PULL_PAGE}` : `?limit=${PULL_PAGE}&since=${enc(since)}`;
|
|
7491
|
+
const answer = await call(run2.api, "GET", tenantPath(tenant, `${path}${query}`));
|
|
7492
|
+
head ??= actingHead(tenant, answer);
|
|
7493
|
+
const page = pullResultSchema(row).parse(expectOk(answer));
|
|
7494
|
+
rows.push(...page.rows);
|
|
7495
|
+
if (page.next_cursor === null)
|
|
7496
|
+
return { rows, head };
|
|
7497
|
+
since = page.next_cursor;
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7500
|
+
var stateOf = (row) => row.active ? "active" : "archived";
|
|
7501
|
+
function countLine(noun, shown, hidden) {
|
|
7502
|
+
return `${shown} ${noun}(s)${hidden === 0 ? "" : `; ${hidden} archived not shown, --all lists them`}`;
|
|
7503
|
+
}
|
|
7504
|
+
function patchOf(fields, schema, flagNames) {
|
|
7505
|
+
const body = definedFields(fields);
|
|
7506
|
+
if (Object.keys(body).length === 0)
|
|
7507
|
+
throw new AdminUsageError(`nothing to change: give at least one of ${flagNames}`);
|
|
7508
|
+
checkBody(schema, body);
|
|
7509
|
+
return body;
|
|
7510
|
+
}
|
|
7511
|
+
var clientLine = (c) => `client ${c.id} ${stateOf(c)} ${c.name}${c.external_ref === null ? "" : ` ref ${c.external_ref}`}`;
|
|
7461
7512
|
async function runClient(run2, subcommand) {
|
|
7462
7513
|
const { flags } = run2;
|
|
7463
7514
|
const tenant = required(flags, "tenant", "the firm the client belongs to");
|
|
@@ -7478,50 +7529,95 @@ async function runClient(run2, subcommand) {
|
|
|
7478
7529
|
]);
|
|
7479
7530
|
}
|
|
7480
7531
|
if (subcommand === "list") {
|
|
7481
|
-
const
|
|
7482
|
-
|
|
7483
|
-
let head = null;
|
|
7484
|
-
for (;; ) {
|
|
7485
|
-
const query = since === null ? `?limit=${CLIENT_PAGE}` : `?limit=${CLIENT_PAGE}&since=${enc(since)}`;
|
|
7486
|
-
const answer = await call(run2.api, "GET", tenantPath(tenant, `/clients${query}`));
|
|
7487
|
-
head ??= actingHead(tenant, answer);
|
|
7488
|
-
const page = pullResultSchema(ClientSchema).parse(expectOk(answer));
|
|
7489
|
-
clients.push(...page.rows);
|
|
7490
|
-
if (page.next_cursor === null)
|
|
7491
|
-
break;
|
|
7492
|
-
since = page.next_cursor;
|
|
7493
|
-
}
|
|
7532
|
+
const { rows, head } = await pullAll(run2, tenant, "/clients", ClientSchema);
|
|
7533
|
+
const clients = switched(flags, "all") ? rows : rows.filter((c) => c.active);
|
|
7494
7534
|
return render(run2, { clients }, [
|
|
7495
7535
|
head,
|
|
7496
|
-
|
|
7497
|
-
...clients.map((c) => ` ${c.id} ${c.name}${c.external_ref === null ? "" : ` ref ${c.external_ref}`}
|
|
7536
|
+
countLine("client", clients.length, rows.length - clients.length),
|
|
7537
|
+
...clients.map((c) => ` ${c.id} ${stateOf(c)} ${c.name}${c.external_ref === null ? "" : ` ref ${c.external_ref}`}`)
|
|
7498
7538
|
]);
|
|
7499
7539
|
}
|
|
7540
|
+
if (subcommand === "edit" || subcommand === "archive" || subcommand === "unarchive") {
|
|
7541
|
+
const id = required(flags, "client", "which client_... to change (client list --all prints them)");
|
|
7542
|
+
const body = subcommand === "edit" ? patchOf({ name: optional(flags, "name"), external_ref: optional(flags, "external-ref"), active: booleanOf(flags, "active") }, ClientPatchSchema, "--name, --external-ref, --active") : { active: subcommand === "unarchive" };
|
|
7543
|
+
if (body["active"] === false) {
|
|
7544
|
+
const { rows } = await pullAll(run2, tenant, "/matters", MatterSchema);
|
|
7545
|
+
const open = rows.filter((m) => m.client_id === id && m.active);
|
|
7546
|
+
if (open.length > 0) {
|
|
7547
|
+
throw new AdminUsageError(`${id} still has ${open.length} open matter(s), which stay pickable while they are open; archive them first:
|
|
7548
|
+
` + open.map((m) => ` sixminute-admin matter archive --env ${run2.environment.name} --tenant ${tenant} --matter ${m.id} (${m.name})`).join(`
|
|
7549
|
+
`));
|
|
7550
|
+
}
|
|
7551
|
+
}
|
|
7552
|
+
const answer = await call(run2.api, "PATCH", tenantPath(tenant, `/clients/${enc(id)}`), body);
|
|
7553
|
+
if (answer.status !== 200)
|
|
7554
|
+
return refused(run2, answer, [actingHead(tenant, answer)]);
|
|
7555
|
+
const client = ClientSchema.parse(answer.body);
|
|
7556
|
+
return render(run2, { client }, [actingHead(tenant, answer), clientLine(client)]);
|
|
7557
|
+
}
|
|
7500
7558
|
throw new AdminUsageError(`unknown command: client ${subcommand ?? ""}`.trim());
|
|
7501
7559
|
}
|
|
7502
|
-
|
|
7560
|
+
var matterLines = (m) => [
|
|
7561
|
+
`matter ${m.id} ${m.code ?? "no number"} ${stateOf(m)} ${m.name}`,
|
|
7562
|
+
`client ${m.client_id}`
|
|
7563
|
+
];
|
|
7564
|
+
async function runMatter(run2, subcommand) {
|
|
7503
7565
|
const { flags } = run2;
|
|
7504
7566
|
const tenant = required(flags, "tenant", "the firm the matter belongs to");
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7567
|
+
if (subcommand === "create") {
|
|
7568
|
+
const body = definedFields({
|
|
7569
|
+
id: optional(flags, "id"),
|
|
7570
|
+
client_id: required(flags, "client", "the client_... the matter is under (client list prints them)"),
|
|
7571
|
+
name: required(flags, "name", "what the matter is called"),
|
|
7572
|
+
code: optional(flags, "number"),
|
|
7573
|
+
description: optional(flags, "description"),
|
|
7574
|
+
external_ref: optional(flags, "external-ref"),
|
|
7575
|
+
billable: booleanOf(flags, "billable"),
|
|
7576
|
+
temporary: booleanOf(flags, "temporary")
|
|
7577
|
+
});
|
|
7578
|
+
checkBody(MatterInputSchema, body);
|
|
7579
|
+
const answer = await call(run2.api, "POST", tenantPath(tenant, "/matters"), body);
|
|
7580
|
+
if (answer.status !== 201)
|
|
7581
|
+
return refused(run2, answer, [actingHead(tenant, answer)]);
|
|
7582
|
+
const matter = MatterSchema.parse(answer.body);
|
|
7583
|
+
return render(run2, { matter }, [
|
|
7584
|
+
actingHead(tenant, answer),
|
|
7585
|
+
`matter ${matter.id} ${matter.code ?? "no number"} ${matter.name}`,
|
|
7586
|
+
`client ${matter.client_id}`
|
|
7587
|
+
]);
|
|
7588
|
+
}
|
|
7589
|
+
if (subcommand === "list") {
|
|
7590
|
+
const client = optional(flags, "client");
|
|
7591
|
+
if (client !== undefined)
|
|
7592
|
+
checkBody(MatterSchema.pick({ client_id: true }), { client_id: client });
|
|
7593
|
+
const { rows, head } = await pullAll(run2, tenant, "/matters", MatterSchema);
|
|
7594
|
+
const ofClient = client === undefined ? rows : rows.filter((m) => m.client_id === client);
|
|
7595
|
+
const matters = switched(flags, "all") ? ofClient : ofClient.filter((m) => m.active);
|
|
7596
|
+
return render(run2, { matters }, [
|
|
7597
|
+
head,
|
|
7598
|
+
countLine("matter", matters.length, ofClient.length - matters.length),
|
|
7599
|
+
...matters.map((m) => ` ${m.id} ${m.code ?? "no number"} ${stateOf(m)} ${m.client_id} ${m.name}`)
|
|
7600
|
+
]);
|
|
7601
|
+
}
|
|
7602
|
+
if (subcommand === "edit" || subcommand === "archive" || subcommand === "unarchive") {
|
|
7603
|
+
const id = required(flags, "matter", "which matter_... to change (matter list --all prints them)");
|
|
7604
|
+
if (subcommand === "edit" && flags.values.has("client")) {
|
|
7605
|
+
throw new AdminUsageError("--client is not taken on matter edit: a matter belongs to one client for life; create a new matter under the other client");
|
|
7606
|
+
}
|
|
7607
|
+
const body = subcommand === "edit" ? patchOf({
|
|
7608
|
+
name: optional(flags, "name"),
|
|
7609
|
+
code: optional(flags, "number"),
|
|
7610
|
+
description: optional(flags, "description"),
|
|
7611
|
+
external_ref: optional(flags, "external-ref"),
|
|
7612
|
+
active: booleanOf(flags, "active")
|
|
7613
|
+
}, MatterPatchSchema, "--name, --number, --description, --external-ref, --active") : { active: subcommand === "unarchive" };
|
|
7614
|
+
const answer = await call(run2.api, "PATCH", tenantPath(tenant, `/matters/${enc(id)}`), body);
|
|
7615
|
+
if (answer.status !== 200)
|
|
7616
|
+
return refused(run2, answer, [actingHead(tenant, answer)]);
|
|
7617
|
+
const matter = MatterSchema.parse(answer.body);
|
|
7618
|
+
return render(run2, { matter }, [actingHead(tenant, answer), ...matterLines(matter)]);
|
|
7619
|
+
}
|
|
7620
|
+
throw new AdminUsageError(`unknown command: matter ${subcommand ?? ""}`.trim());
|
|
7525
7621
|
}
|
|
7526
7622
|
async function runMattersImport(run2) {
|
|
7527
7623
|
const { flags } = run2;
|
package/package.json
CHANGED