@unisonlabs/sixminute-admin 0.1.608-next → 0.1.613-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 +156 -48
- 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.613-next", commit: "15939286f244793828ccd5f14795a6302a06a018", surface: "e738440f007ffca1" };
|
|
25
25
|
|
|
26
26
|
// tools/admin/src/run.ts
|
|
27
27
|
import { rmSync as rmSync3 } from "node:fs";
|
|
@@ -4893,6 +4893,18 @@ var AuthEmailVerifyResponseSchema = exports_external.object({
|
|
|
4893
4893
|
last_name: exports_external.string().nullable().default(null)
|
|
4894
4894
|
})
|
|
4895
4895
|
});
|
|
4896
|
+
var AuthOrganizationSelectionRequestSchema = exports_external.object({
|
|
4897
|
+
pending_authentication_token: exports_external.string().min(1).max(1024),
|
|
4898
|
+
organizations: exports_external.array(exports_external.object({ id: exports_external.string().min(1).max(128) })).min(1).max(100)
|
|
4899
|
+
});
|
|
4900
|
+
var WorkosOrganizationSelectionErrorSchema = exports_external.object({
|
|
4901
|
+
code: exports_external.literal("organization_selection_required"),
|
|
4902
|
+
pending_authentication_token: exports_external.string().min(1),
|
|
4903
|
+
organizations: exports_external.array(exports_external.unknown()).transform((offered) => offered.flatMap((org) => {
|
|
4904
|
+
const parsed = exports_external.object({ id: exports_external.string().min(1) }).safeParse(org);
|
|
4905
|
+
return parsed.success ? [{ id: parsed.data.id }] : [];
|
|
4906
|
+
})).pipe(exports_external.array(exports_external.object({ id: exports_external.string() })).min(1))
|
|
4907
|
+
}).passthrough();
|
|
4896
4908
|
var NewsletterSourceSchema = exports_external.string().regex(/^(footer|subscribed|update:[a-z0-9-]+)$/);
|
|
4897
4909
|
var NewsletterSubscribeRequestSchema = exports_external.object({
|
|
4898
4910
|
email: AuthEmailSendRequestSchema.shape.email,
|
|
@@ -5776,7 +5788,7 @@ var LocalSettingsPatchSchema = exports_external.object({
|
|
|
5776
5788
|
}).optional()
|
|
5777
5789
|
});
|
|
5778
5790
|
var LocalFirmStateSchema = exports_external.enum(["none", "member"]);
|
|
5779
|
-
var LocalAuthRefusalSchema = exports_external.enum(["no-firm"]);
|
|
5791
|
+
var LocalAuthRefusalSchema = exports_external.enum(["no-firm", "choose-firm"]);
|
|
5780
5792
|
var LocalSessionStateSchema = exports_external.object({
|
|
5781
5793
|
signed_in: exports_external.boolean(),
|
|
5782
5794
|
user: exports_external.object({ email: exports_external.string(), name: exports_external.string() }).nullable(),
|
|
@@ -6712,12 +6724,31 @@ is required; without one every command answers 403.
|
|
|
6712
6724
|
|
|
6713
6725
|
sixminute-admin client create --env <env> --tenant tenant_... --name "Example Holding" [--external-ref C-100] [--id client_...] [--json]
|
|
6714
6726
|
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
|
|
6727
|
+
sixminute-admin client list --env <env> --tenant tenant_... [--all] [--json]
|
|
6728
|
+
Every client of the firm with its id, which matter create takes, and
|
|
6729
|
+
whether it is active or archived. Archived clients are left out
|
|
6730
|
+
unless --all.
|
|
6731
|
+
sixminute-admin client edit --env <env> --tenant tenant_... --client client_... [--name "..."] [--external-ref R] [--active true|false] [--json]
|
|
6732
|
+
Changes only the fields given; at least one is required. A name
|
|
6733
|
+
already in use is refused.
|
|
6734
|
+
sixminute-admin client archive --env <env> --tenant tenant_... --client client_... [--json]
|
|
6735
|
+
sixminute-admin client unarchive --env <env> --tenant tenant_... --client client_... [--json]
|
|
6736
|
+
Sets the client inactive, or active again.
|
|
6717
6737
|
sixminute-admin matter create --env <env> --tenant tenant_... --client client_... --name "Example Holding: general advice" \\
|
|
6718
6738
|
[--number 2026-0001] [--description "..."] [--external-ref R] [--billable true|false] [--temporary true|false] [--id matter_...] [--json]
|
|
6719
6739
|
Creates one matter under a client of the firm. --number is the firm's
|
|
6720
6740
|
matter number, unique within the client. An unknown client is refused.
|
|
6741
|
+
sixminute-admin matter list --env <env> --tenant tenant_... [--client client_...] [--all] [--json]
|
|
6742
|
+
Every matter of the firm, or of one client: id, number, active or
|
|
6743
|
+
archived, client and name. Archived matters are left out unless --all.
|
|
6744
|
+
sixminute-admin matter edit --env <env> --tenant tenant_... --matter matter_... \\
|
|
6745
|
+
[--name "..."] [--number N] [--description "..."] [--external-ref R] [--active true|false] [--json]
|
|
6746
|
+
Changes only the fields given; at least one is required. A matter
|
|
6747
|
+
belongs to one client for life, so there is no --client.
|
|
6748
|
+
sixminute-admin matter archive --env <env> --tenant tenant_... --matter matter_... [--json]
|
|
6749
|
+
sixminute-admin matter unarchive --env <env> --tenant tenant_... --matter matter_... [--json]
|
|
6750
|
+
Sets the matter inactive, or active again. A temporary matter with
|
|
6751
|
+
entries on it is refused: move the entries first.
|
|
6721
6752
|
sixminute-admin matters import --env <env> --tenant tenant_... --from-file tools/admin/examples/matters-import.json [--json]
|
|
6722
6753
|
Clients with their matters, from a document: the file IS the body of
|
|
6723
6754
|
POST /v1/matters/import, checked against its schema before anything is
|
|
@@ -6725,10 +6756,14 @@ is required; without one every command answers 403.
|
|
|
6725
6756
|
missing rows are created and known rows corrected, so running the same
|
|
6726
6757
|
file again changes nothing. Prints how many were created, updated and
|
|
6727
6758
|
left alone.
|
|
6728
|
-
client
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
and nothing is written.
|
|
6759
|
+
The client and matter create, edit, archive and unarchive commands and
|
|
6760
|
+
matters import WORK ONLY ON AN INTERNAL FIRM, one marked with tenant
|
|
6761
|
+
internal set: a real firm's clients and matters come from its practice
|
|
6762
|
+
system. Any other firm is refused, exit 2, and nothing is written.
|
|
6763
|
+
Archive is the delete: it keeps the row and every entry on it, and an
|
|
6764
|
+
archived matter leaves the pickers. client archive refuses while the
|
|
6765
|
+
client has an open matter, and prints the matter archive line for each
|
|
6766
|
+
one. Nothing here deletes a client or a matter.
|
|
6732
6767
|
|
|
6733
6768
|
sixminute-admin member list --env <env> --tenant tenant_... [--json]
|
|
6734
6769
|
The firm's members and its pending invitations.
|
|
@@ -6800,7 +6835,8 @@ var BOOLEAN_FLAGS = new Set([
|
|
|
6800
6835
|
"required",
|
|
6801
6836
|
"proposed",
|
|
6802
6837
|
"confirm",
|
|
6803
|
-
"password-stdin"
|
|
6838
|
+
"password-stdin",
|
|
6839
|
+
"all"
|
|
6804
6840
|
]);
|
|
6805
6841
|
function parseFlags(argv, offset) {
|
|
6806
6842
|
const values = new Map;
|
|
@@ -6971,8 +7007,8 @@ async function runAdmin(argv, deps) {
|
|
|
6971
7007
|
return runMember(run2, subcommand);
|
|
6972
7008
|
if (group === "client")
|
|
6973
7009
|
return runClient(run2, subcommand);
|
|
6974
|
-
if (group === "matter"
|
|
6975
|
-
return
|
|
7010
|
+
if (group === "matter")
|
|
7011
|
+
return runMatter(run2, subcommand);
|
|
6976
7012
|
if (group === "matters" && subcommand === "import")
|
|
6977
7013
|
return runMattersImport(run2);
|
|
6978
7014
|
if (group === "tier" && subcommand === "set")
|
|
@@ -7457,7 +7493,34 @@ async function readMatterSets(firm, matterId) {
|
|
|
7457
7493
|
since = page.next_cursor;
|
|
7458
7494
|
}
|
|
7459
7495
|
}
|
|
7460
|
-
var
|
|
7496
|
+
var PULL_PAGE = 2000;
|
|
7497
|
+
async function pullAll(run2, tenant, path, row) {
|
|
7498
|
+
const rows = [];
|
|
7499
|
+
let since = null;
|
|
7500
|
+
let head = null;
|
|
7501
|
+
for (;; ) {
|
|
7502
|
+
const query = since === null ? `?limit=${PULL_PAGE}` : `?limit=${PULL_PAGE}&since=${enc(since)}`;
|
|
7503
|
+
const answer = await call(run2.api, "GET", tenantPath(tenant, `${path}${query}`));
|
|
7504
|
+
head ??= actingHead(tenant, answer);
|
|
7505
|
+
const page = pullResultSchema(row).parse(expectOk(answer));
|
|
7506
|
+
rows.push(...page.rows);
|
|
7507
|
+
if (page.next_cursor === null)
|
|
7508
|
+
return { rows, head };
|
|
7509
|
+
since = page.next_cursor;
|
|
7510
|
+
}
|
|
7511
|
+
}
|
|
7512
|
+
var stateOf = (row) => row.active ? "active" : "archived";
|
|
7513
|
+
function countLine(noun, shown, hidden) {
|
|
7514
|
+
return `${shown} ${noun}(s)${hidden === 0 ? "" : `; ${hidden} archived not shown, --all lists them`}`;
|
|
7515
|
+
}
|
|
7516
|
+
function patchOf(fields, schema, flagNames) {
|
|
7517
|
+
const body = definedFields(fields);
|
|
7518
|
+
if (Object.keys(body).length === 0)
|
|
7519
|
+
throw new AdminUsageError(`nothing to change: give at least one of ${flagNames}`);
|
|
7520
|
+
checkBody(schema, body);
|
|
7521
|
+
return body;
|
|
7522
|
+
}
|
|
7523
|
+
var clientLine = (c) => `client ${c.id} ${stateOf(c)} ${c.name}${c.external_ref === null ? "" : ` ref ${c.external_ref}`}`;
|
|
7461
7524
|
async function runClient(run2, subcommand) {
|
|
7462
7525
|
const { flags } = run2;
|
|
7463
7526
|
const tenant = required(flags, "tenant", "the firm the client belongs to");
|
|
@@ -7478,50 +7541,95 @@ async function runClient(run2, subcommand) {
|
|
|
7478
7541
|
]);
|
|
7479
7542
|
}
|
|
7480
7543
|
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
|
-
}
|
|
7544
|
+
const { rows, head } = await pullAll(run2, tenant, "/clients", ClientSchema);
|
|
7545
|
+
const clients = switched(flags, "all") ? rows : rows.filter((c) => c.active);
|
|
7494
7546
|
return render(run2, { clients }, [
|
|
7495
7547
|
head,
|
|
7496
|
-
|
|
7497
|
-
...clients.map((c) => ` ${c.id} ${c.name}${c.external_ref === null ? "" : ` ref ${c.external_ref}`}
|
|
7548
|
+
countLine("client", clients.length, rows.length - clients.length),
|
|
7549
|
+
...clients.map((c) => ` ${c.id} ${stateOf(c)} ${c.name}${c.external_ref === null ? "" : ` ref ${c.external_ref}`}`)
|
|
7498
7550
|
]);
|
|
7499
7551
|
}
|
|
7552
|
+
if (subcommand === "edit" || subcommand === "archive" || subcommand === "unarchive") {
|
|
7553
|
+
const id = required(flags, "client", "which client_... to change (client list --all prints them)");
|
|
7554
|
+
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" };
|
|
7555
|
+
if (body["active"] === false) {
|
|
7556
|
+
const { rows } = await pullAll(run2, tenant, "/matters", MatterSchema);
|
|
7557
|
+
const open = rows.filter((m) => m.client_id === id && m.active);
|
|
7558
|
+
if (open.length > 0) {
|
|
7559
|
+
throw new AdminUsageError(`${id} still has ${open.length} open matter(s), which stay pickable while they are open; archive them first:
|
|
7560
|
+
` + open.map((m) => ` sixminute-admin matter archive --env ${run2.environment.name} --tenant ${tenant} --matter ${m.id} (${m.name})`).join(`
|
|
7561
|
+
`));
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
const answer = await call(run2.api, "PATCH", tenantPath(tenant, `/clients/${enc(id)}`), body);
|
|
7565
|
+
if (answer.status !== 200)
|
|
7566
|
+
return refused(run2, answer, [actingHead(tenant, answer)]);
|
|
7567
|
+
const client = ClientSchema.parse(answer.body);
|
|
7568
|
+
return render(run2, { client }, [actingHead(tenant, answer), clientLine(client)]);
|
|
7569
|
+
}
|
|
7500
7570
|
throw new AdminUsageError(`unknown command: client ${subcommand ?? ""}`.trim());
|
|
7501
7571
|
}
|
|
7502
|
-
|
|
7572
|
+
var matterLines = (m) => [
|
|
7573
|
+
`matter ${m.id} ${m.code ?? "no number"} ${stateOf(m)} ${m.name}`,
|
|
7574
|
+
`client ${m.client_id}`
|
|
7575
|
+
];
|
|
7576
|
+
async function runMatter(run2, subcommand) {
|
|
7503
7577
|
const { flags } = run2;
|
|
7504
7578
|
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
|
-
|
|
7579
|
+
if (subcommand === "create") {
|
|
7580
|
+
const body = definedFields({
|
|
7581
|
+
id: optional(flags, "id"),
|
|
7582
|
+
client_id: required(flags, "client", "the client_... the matter is under (client list prints them)"),
|
|
7583
|
+
name: required(flags, "name", "what the matter is called"),
|
|
7584
|
+
code: optional(flags, "number"),
|
|
7585
|
+
description: optional(flags, "description"),
|
|
7586
|
+
external_ref: optional(flags, "external-ref"),
|
|
7587
|
+
billable: booleanOf(flags, "billable"),
|
|
7588
|
+
temporary: booleanOf(flags, "temporary")
|
|
7589
|
+
});
|
|
7590
|
+
checkBody(MatterInputSchema, body);
|
|
7591
|
+
const answer = await call(run2.api, "POST", tenantPath(tenant, "/matters"), body);
|
|
7592
|
+
if (answer.status !== 201)
|
|
7593
|
+
return refused(run2, answer, [actingHead(tenant, answer)]);
|
|
7594
|
+
const matter = MatterSchema.parse(answer.body);
|
|
7595
|
+
return render(run2, { matter }, [
|
|
7596
|
+
actingHead(tenant, answer),
|
|
7597
|
+
`matter ${matter.id} ${matter.code ?? "no number"} ${matter.name}`,
|
|
7598
|
+
`client ${matter.client_id}`
|
|
7599
|
+
]);
|
|
7600
|
+
}
|
|
7601
|
+
if (subcommand === "list") {
|
|
7602
|
+
const client = optional(flags, "client");
|
|
7603
|
+
if (client !== undefined)
|
|
7604
|
+
checkBody(MatterSchema.pick({ client_id: true }), { client_id: client });
|
|
7605
|
+
const { rows, head } = await pullAll(run2, tenant, "/matters", MatterSchema);
|
|
7606
|
+
const ofClient = client === undefined ? rows : rows.filter((m) => m.client_id === client);
|
|
7607
|
+
const matters = switched(flags, "all") ? ofClient : ofClient.filter((m) => m.active);
|
|
7608
|
+
return render(run2, { matters }, [
|
|
7609
|
+
head,
|
|
7610
|
+
countLine("matter", matters.length, ofClient.length - matters.length),
|
|
7611
|
+
...matters.map((m) => ` ${m.id} ${m.code ?? "no number"} ${stateOf(m)} ${m.client_id} ${m.name}`)
|
|
7612
|
+
]);
|
|
7613
|
+
}
|
|
7614
|
+
if (subcommand === "edit" || subcommand === "archive" || subcommand === "unarchive") {
|
|
7615
|
+
const id = required(flags, "matter", "which matter_... to change (matter list --all prints them)");
|
|
7616
|
+
if (subcommand === "edit" && flags.values.has("client")) {
|
|
7617
|
+
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");
|
|
7618
|
+
}
|
|
7619
|
+
const body = subcommand === "edit" ? patchOf({
|
|
7620
|
+
name: optional(flags, "name"),
|
|
7621
|
+
code: optional(flags, "number"),
|
|
7622
|
+
description: optional(flags, "description"),
|
|
7623
|
+
external_ref: optional(flags, "external-ref"),
|
|
7624
|
+
active: booleanOf(flags, "active")
|
|
7625
|
+
}, MatterPatchSchema, "--name, --number, --description, --external-ref, --active") : { active: subcommand === "unarchive" };
|
|
7626
|
+
const answer = await call(run2.api, "PATCH", tenantPath(tenant, `/matters/${enc(id)}`), body);
|
|
7627
|
+
if (answer.status !== 200)
|
|
7628
|
+
return refused(run2, answer, [actingHead(tenant, answer)]);
|
|
7629
|
+
const matter = MatterSchema.parse(answer.body);
|
|
7630
|
+
return render(run2, { matter }, [actingHead(tenant, answer), ...matterLines(matter)]);
|
|
7631
|
+
}
|
|
7632
|
+
throw new AdminUsageError(`unknown command: matter ${subcommand ?? ""}`.trim());
|
|
7525
7633
|
}
|
|
7526
7634
|
async function runMattersImport(run2) {
|
|
7527
7635
|
const { flags } = run2;
|
package/package.json
CHANGED