@unisonlabs/sixminute-admin 0.1.613-next → 0.1.617-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/dist/sixminute-admin.mjs +169 -27
- package/package.json +1 -1
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.617-next", commit: "9332e005679d8639cc55297ce08e6c881aa3dc74", surface: "1d814431cdc5d665" };
|
|
25
25
|
|
|
26
26
|
// tools/admin/src/run.ts
|
|
27
27
|
import { rmSync as rmSync3 } from "node:fs";
|
|
@@ -4124,12 +4124,14 @@ var AttributeBindingSchema = exports_external.object({
|
|
|
4124
4124
|
field: exports_external.string().min(1)
|
|
4125
4125
|
});
|
|
4126
4126
|
var OptionValueSchema = exports_external.string().transform((v) => v.trim()).pipe(exports_external.string().min(1, "an option value is at least one character").max(64, "an option value is at most 64 characters").regex(/^[^\r\n|]+$/, "an option value carries no newline and no pipe: a time entry is one line, and the pipe " + "is a field delimiter in time-entry formats"));
|
|
4127
|
+
var MinNarrativeCharsSchema = exports_external.number().int().positive();
|
|
4127
4128
|
var OptionSchema = exports_external.object({
|
|
4128
4129
|
value: OptionValueSchema,
|
|
4129
4130
|
label: exports_external.string().min(1),
|
|
4130
4131
|
description: exports_external.string().default(""),
|
|
4131
4132
|
external_ref: exports_external.string().nullable().default(null),
|
|
4132
|
-
active: exports_external.boolean().default(true)
|
|
4133
|
+
active: exports_external.boolean().default(true),
|
|
4134
|
+
min_narrative_chars: MinNarrativeCharsSchema.nullable().default(null)
|
|
4133
4135
|
});
|
|
4134
4136
|
var AttributeSetIdSchema = exports_external.string().regex(new RegExp(`^${ID_PREFIXES.attributeSet}_[a-z0-9][a-z0-9_.:-]*$`, "i"), `expected an ${ID_PREFIXES.attributeSet}_ id`);
|
|
4135
4137
|
var AttributeSetSchema = exports_external.object({
|
|
@@ -4236,6 +4238,10 @@ var ClientSchema = exports_external.object({
|
|
|
4236
4238
|
created_at: TimestampSchema,
|
|
4237
4239
|
updated_at: TimestampSchema
|
|
4238
4240
|
});
|
|
4241
|
+
var NarrativeLanguageSchema = exports_external.enum(["en", "nl"]);
|
|
4242
|
+
var MatterAttributeRequiredSchema = exports_external.record(AttributeKeySchema.refine((key) => !key.includes("__"), {
|
|
4243
|
+
message: "an attribute key here carries no double underscore"
|
|
4244
|
+
}), exports_external.boolean());
|
|
4239
4245
|
var MatterBillingSchema = exports_external.object({
|
|
4240
4246
|
narrative_profile: exports_external.string().min(1).default("house")
|
|
4241
4247
|
}).default({});
|
|
@@ -4258,6 +4264,8 @@ var MatterSchema = exports_external.object({
|
|
|
4258
4264
|
last_activity_at: TimestampSchema.nullable().default(null),
|
|
4259
4265
|
billing: MatterBillingSchema,
|
|
4260
4266
|
attribute_sets: MatterAttributeSetsSchema.default({}),
|
|
4267
|
+
attribute_required: MatterAttributeRequiredSchema.default({}),
|
|
4268
|
+
narrative_language: NarrativeLanguageSchema.nullable().default(null),
|
|
4261
4269
|
sync_source: SyncSourceSchema.default("manual"),
|
|
4262
4270
|
created_at: TimestampSchema,
|
|
4263
4271
|
updated_at: TimestampSchema
|
|
@@ -4403,6 +4411,7 @@ var CapabilitiesSchema = exports_external.object({
|
|
|
4403
4411
|
identity: IdentityModeSchema,
|
|
4404
4412
|
change_feed: exports_external.enum(["modified_since", "full_list"]),
|
|
4405
4413
|
narrative_max_chars: exports_external.number().int().positive().nullable(),
|
|
4414
|
+
note_max_chars: exports_external.number().int().positive().nullable().default(null),
|
|
4406
4415
|
insolvency_groups: exports_external.boolean(),
|
|
4407
4416
|
fields: exports_external.array(FieldDeclarationSchema).superRefine((fields, ctx) => {
|
|
4408
4417
|
const seen = new Map;
|
|
@@ -4468,6 +4477,8 @@ var PmsMatterSchema = exports_external.object({
|
|
|
4468
4477
|
keywords: exports_external.array(exports_external.string()).default([]),
|
|
4469
4478
|
default_timekeeper_external_ref: exports_external.string().nullable().default(null),
|
|
4470
4479
|
field_sets: exports_external.record(exports_external.string(), exports_external.array(exports_external.string().min(1))).default({}),
|
|
4480
|
+
field_required: exports_external.record(exports_external.string(), exports_external.boolean()).default({}),
|
|
4481
|
+
language: NarrativeLanguageSchema.nullable().optional(),
|
|
4471
4482
|
insolvency: exports_external.boolean().default(false),
|
|
4472
4483
|
time_entry_blocked: exports_external.boolean().default(false)
|
|
4473
4484
|
});
|
|
@@ -4483,16 +4494,27 @@ var PmsOptionSchema = exports_external.object({
|
|
|
4483
4494
|
value: exports_external.string().min(1),
|
|
4484
4495
|
label: exports_external.string().min(1),
|
|
4485
4496
|
description: exports_external.string().default(""),
|
|
4486
|
-
active: exports_external.boolean().default(true)
|
|
4497
|
+
active: exports_external.boolean().default(true),
|
|
4498
|
+
min_narrative_chars: MinNarrativeCharsSchema.nullable().default(null)
|
|
4487
4499
|
});
|
|
4488
4500
|
var PmsOptionSetSchema = exports_external.object({
|
|
4489
4501
|
field: exports_external.string().min(1),
|
|
4490
4502
|
external_ref: exports_external.string().min(1).nullable(),
|
|
4491
4503
|
name: exports_external.string().min(1),
|
|
4492
|
-
options: exports_external.array(PmsOptionSchema)
|
|
4504
|
+
options: exports_external.array(PmsOptionSchema),
|
|
4505
|
+
complete: exports_external.boolean().optional()
|
|
4493
4506
|
});
|
|
4507
|
+
var ReviewerSettingSchema = exports_external.discriminatedUnion("mode", [
|
|
4508
|
+
exports_external.object({ mode: exports_external.literal("timekeeper") }),
|
|
4509
|
+
exports_external.object({
|
|
4510
|
+
mode: exports_external.literal("person"),
|
|
4511
|
+
external_ref: exports_external.string().min(1),
|
|
4512
|
+
name: exports_external.string().min(1)
|
|
4513
|
+
})
|
|
4514
|
+
]);
|
|
4494
4515
|
var PmsSettingsSchema = exports_external.object({
|
|
4495
|
-
notation: exports_external.string().nullable().default(null)
|
|
4516
|
+
notation: exports_external.string().nullable().default(null),
|
|
4517
|
+
reviewer: ReviewerSettingSchema.nullable().default(null)
|
|
4496
4518
|
});
|
|
4497
4519
|
var PmsListsSchema = exports_external.object({
|
|
4498
4520
|
clients: exports_external.array(PmsClientSchema).default([]),
|
|
@@ -4500,7 +4522,8 @@ var PmsListsSchema = exports_external.object({
|
|
|
4500
4522
|
timekeepers: exports_external.array(PmsTimekeeperSchema).default([]),
|
|
4501
4523
|
fields: exports_external.record(exports_external.string(), exports_external.array(PmsOptionSetSchema)).default({}),
|
|
4502
4524
|
settings: PmsSettingsSchema.nullable().default(null),
|
|
4503
|
-
cursors: exports_external.record(exports_external.string(), exports_external.string()).default({})
|
|
4525
|
+
cursors: exports_external.record(exports_external.string(), exports_external.string()).default({}),
|
|
4526
|
+
retired_timekeepers: exports_external.array(exports_external.string().min(1)).optional()
|
|
4504
4527
|
});
|
|
4505
4528
|
var MatterPmsRulesSchema = exports_external.object({
|
|
4506
4529
|
insolvency: exports_external.boolean().default(false),
|
|
@@ -4534,13 +4557,17 @@ var PmsEntryStateSchema = exports_external.object({
|
|
|
4534
4557
|
modified_at: TimestampSchema.nullable().default(null),
|
|
4535
4558
|
raw_status: exports_external.string().nullable().default(null)
|
|
4536
4559
|
});
|
|
4560
|
+
var PmsEntryFactSchema = PmsEntryStateSchema.extend({
|
|
4561
|
+
followed_ref: exports_external.string().min(1).optional()
|
|
4562
|
+
});
|
|
4537
4563
|
var DeliveryStateSchema = exports_external.enum(["released", "accepted", "rejected", "edited"]);
|
|
4538
4564
|
var PushRefusalSchema = exports_external.enum([
|
|
4539
4565
|
"matter_not_found",
|
|
4540
4566
|
"matter_closed",
|
|
4541
4567
|
"person_not_found",
|
|
4542
4568
|
"person_inactive",
|
|
4543
|
-
"not_accepted"
|
|
4569
|
+
"not_accepted",
|
|
4570
|
+
"reviewer"
|
|
4544
4571
|
]);
|
|
4545
4572
|
var DeliveryReasonSchema = exports_external.enum([
|
|
4546
4573
|
"deleted_in_pms",
|
|
@@ -4591,6 +4618,13 @@ var ConnectRequestSchema = exports_external.discriminatedUnion("kind", [
|
|
|
4591
4618
|
user: exports_external.string().min(1),
|
|
4592
4619
|
password: exports_external.string().min(1)
|
|
4593
4620
|
}),
|
|
4621
|
+
exports_external.object({
|
|
4622
|
+
kind: exports_external.literal("legalsense"),
|
|
4623
|
+
host: exports_external.string().min(1).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?\.legalsense\.nl$/i, "a LegalSense host is <installation>.legalsense.nl"),
|
|
4624
|
+
username: exports_external.string().min(1),
|
|
4625
|
+
password: exports_external.string().min(1),
|
|
4626
|
+
totp: exports_external.string().regex(/^[0-9]{6}$/, "the code is six digits").optional()
|
|
4627
|
+
}),
|
|
4594
4628
|
exports_external.object({
|
|
4595
4629
|
kind: exports_external.literal("fake"),
|
|
4596
4630
|
seed: exports_external.string().optional()
|
|
@@ -4612,6 +4646,9 @@ var ConnectorSummarySchema = exports_external.object({
|
|
|
4612
4646
|
last_sync_at: TimestampSchema.nullable().default(null),
|
|
4613
4647
|
last_error: exports_external.string().nullable().default(null)
|
|
4614
4648
|
}).strict();
|
|
4649
|
+
var ConnectorSettingsPutSchema = exports_external.object({
|
|
4650
|
+
reviewer: ReviewerSettingSchema.nullable()
|
|
4651
|
+
});
|
|
4615
4652
|
var ConnectResultSchema = exports_external.object({
|
|
4616
4653
|
connector: ConnectorSummarySchema,
|
|
4617
4654
|
team: TeamProposalSchema.nullable()
|
|
@@ -4737,7 +4774,7 @@ var ProblemSchema = exports_external.object({
|
|
|
4737
4774
|
status: exports_external.number().int().min(400).max(599),
|
|
4738
4775
|
detail: exports_external.string().optional(),
|
|
4739
4776
|
instance: exports_external.string().optional(),
|
|
4740
|
-
errors: exports_external.array(exports_external.object({ path: exports_external.string(), message: exports_external.string() })).optional()
|
|
4777
|
+
errors: exports_external.array(exports_external.object({ path: exports_external.string(), message: exports_external.string(), reason: exports_external.string().optional() })).optional()
|
|
4741
4778
|
});
|
|
4742
4779
|
var CursorSchema = exports_external.string().min(1);
|
|
4743
4780
|
var PullQuerySchema = exports_external.object({
|
|
@@ -4756,7 +4793,6 @@ var TenantPolicySchema = exports_external.object({
|
|
|
4756
4793
|
security_tier: exports_external.enum(["standard", "confidential"]).default("standard"),
|
|
4757
4794
|
raw_archive: exports_external.boolean().default(false)
|
|
4758
4795
|
}).default({});
|
|
4759
|
-
var NarrativeLanguageSchema = exports_external.enum(["en", "nl"]);
|
|
4760
4796
|
var NARRATIVE_GUIDANCE_MAX_CHARS = 1500;
|
|
4761
4797
|
var GUIDANCE_CONTROL = /[\u0000-\u0009\u000B-\u001F\u007F-\u009F]/;
|
|
4762
4798
|
var NarrativeMaxCharsSchema = exports_external.number().int().min(40).max(1023);
|
|
@@ -4833,7 +4869,9 @@ var DeviceRegistrationSchema = exports_external.object({
|
|
|
4833
4869
|
});
|
|
4834
4870
|
var MeConnectorSchema = exports_external.object({
|
|
4835
4871
|
kind: ConnectorKindSchema,
|
|
4836
|
-
narrative_max_chars: exports_external.number().int().positive().nullable()
|
|
4872
|
+
narrative_max_chars: exports_external.number().int().positive().nullable(),
|
|
4873
|
+
note_max_chars: exports_external.number().int().positive().nullable().optional(),
|
|
4874
|
+
supplies_languages: exports_external.boolean().optional()
|
|
4837
4875
|
});
|
|
4838
4876
|
var MeSystemLinkSchema = exports_external.object({
|
|
4839
4877
|
system: ConnectorKindSchema,
|
|
@@ -4850,7 +4888,8 @@ var MeSchema = exports_external.object({
|
|
|
4850
4888
|
system_link: MeSystemLinkSchema.nullable().default(null)
|
|
4851
4889
|
}),
|
|
4852
4890
|
permissions: exports_external.array(exports_external.string()).default([]),
|
|
4853
|
-
judgment_pipeline: JudgmentPipelineSchema.default("standard")
|
|
4891
|
+
judgment_pipeline: JudgmentPipelineSchema.default("standard"),
|
|
4892
|
+
legalsense_connector: exports_external.boolean().default(false)
|
|
4854
4893
|
});
|
|
4855
4894
|
var ProvisionResultSchema = MeSchema.extend({
|
|
4856
4895
|
action: exports_external.enum(["ready", "refresh"]).default("ready")
|
|
@@ -4921,11 +4960,21 @@ var ClientInputSchema = ClientSchema.omit({ created_at: true, updated_at: true }
|
|
|
4921
4960
|
id: ClientSchema.shape.id.optional()
|
|
4922
4961
|
});
|
|
4923
4962
|
var ClientPatchSchema = ClientSchema.omit({ id: true, created_at: true, updated_at: true }).partial();
|
|
4924
|
-
var MatterInputSchema = MatterSchema.omit({
|
|
4963
|
+
var MatterInputSchema = MatterSchema.omit({
|
|
4964
|
+
created_at: true,
|
|
4965
|
+
updated_at: true,
|
|
4966
|
+
attribute_required: true,
|
|
4967
|
+
narrative_language: true
|
|
4968
|
+
}).extend({
|
|
4925
4969
|
id: MatterSchema.shape.id.optional(),
|
|
4926
4970
|
opened_at: MatterSchema.shape.opened_at.optional()
|
|
4927
4971
|
});
|
|
4928
|
-
var MatterPatchSchema = MatterSchema.omit({
|
|
4972
|
+
var MatterPatchSchema = MatterSchema.omit({
|
|
4973
|
+
id: true,
|
|
4974
|
+
created_at: true,
|
|
4975
|
+
updated_at: true,
|
|
4976
|
+
attribute_required: true
|
|
4977
|
+
}).partial();
|
|
4929
4978
|
var AttributeSetPutSchema = AttributeSetSchema.omit({ updated_at: true });
|
|
4930
4979
|
var AttributeSetsQuerySchema = exports_external.object({ attribute: AttributeKeySchema.optional() });
|
|
4931
4980
|
var MatterImportSchema = exports_external.object({
|
|
@@ -4941,7 +4990,9 @@ var MatterImportSchema = exports_external.object({
|
|
|
4941
4990
|
updated_at: true,
|
|
4942
4991
|
opened_at: true,
|
|
4943
4992
|
closed_at: true,
|
|
4944
|
-
last_activity_at: true
|
|
4993
|
+
last_activity_at: true,
|
|
4994
|
+
attribute_required: true,
|
|
4995
|
+
narrative_language: true
|
|
4945
4996
|
}).extend({ opened_at: TimestampSchema.optional() })))
|
|
4946
4997
|
})))
|
|
4947
4998
|
});
|
|
@@ -6414,9 +6465,14 @@ function summaryLines(summary) {
|
|
|
6414
6465
|
`team ${summary.team_bound} bound, ${summary.team_unbound} unbound`,
|
|
6415
6466
|
`lists ${counts.matters} matters, ${counts.clients} clients, ` + `${counts.timekeepers} timekeepers, ${counts.options} options`,
|
|
6416
6467
|
`last sync ${summary.last_sync_at ?? "never"}`,
|
|
6417
|
-
`last error ${summary.last_error ?? "none"}
|
|
6468
|
+
`last error ${summary.last_error ?? "none"}`,
|
|
6469
|
+
...summary.capabilities.accepts_by === "approval" ? [`reviewer ${reviewerLine(summary)}`] : []
|
|
6418
6470
|
];
|
|
6419
6471
|
}
|
|
6472
|
+
function reviewerLine(summary) {
|
|
6473
|
+
const reviewer = summary.settings?.reviewer ?? null;
|
|
6474
|
+
return reviewer === null || reviewer.mode === "timekeeper" ? "each lawyer approves their own time" : `${reviewer.name} (${reviewer.external_ref}) approves every entry`;
|
|
6475
|
+
}
|
|
6420
6476
|
function syncLines(report) {
|
|
6421
6477
|
const counted = (label, n) => `${label.padEnd(13)} ${n.created} created, ${n.updated} updated, ${n.unchanged} unchanged`;
|
|
6422
6478
|
const d = report.deliveries;
|
|
@@ -6793,6 +6849,17 @@ is required; without one every command answers 403.
|
|
|
6793
6849
|
argument is a line in a shell history, and it is never printed.
|
|
6794
6850
|
Exits 2 when the system refuses the credentials, naming the step it
|
|
6795
6851
|
refused at, and 2 when the firm is already on another system.
|
|
6852
|
+
sixminute-admin connector connect --env <env> --firm tenant_... --kind legalsense \\
|
|
6853
|
+
--installation <name> --username <u> --password-stdin [--totp <six digits>] [--json]
|
|
6854
|
+
LegalSense takes the installation's NAME and never --host: the host
|
|
6855
|
+
is <name>.legalsense.nl. --username is the seat the firm created for
|
|
6856
|
+
Sixminute, and --totp the six-digit code when that seat has
|
|
6857
|
+
two-factor on. What is kept is the host and the session token
|
|
6858
|
+
LegalSense answers with, sealed under the firm's key; the password
|
|
6859
|
+
and the code are not stored. Sixminute's own integrator pair is the
|
|
6860
|
+
service's (its deploy's secrets), never this tool's: a deploy without
|
|
6861
|
+
it answers that LegalSense is not available. Run it again with the
|
|
6862
|
+
password (and a fresh code) to reconnect a firm that needs it.
|
|
6796
6863
|
|
|
6797
6864
|
sixminute-admin connector team --env <env> --firm tenant_... \\
|
|
6798
6865
|
[--bind <external-ref>=<member-id>] [--unbind <external-ref>] [--confirm] [--json]
|
|
@@ -6805,6 +6872,13 @@ is required; without one every command answers 403.
|
|
|
6805
6872
|
what --bind names is bound.
|
|
6806
6873
|
WITHOUT --confirm it prints what it would send and sends nothing.
|
|
6807
6874
|
|
|
6875
|
+
sixminute-admin connector reviewer --env <env> --firm tenant_... \\
|
|
6876
|
+
(--timekeeper | --person <external-ref>) [--json]
|
|
6877
|
+
Who approves a released entry in a system that takes drafts for
|
|
6878
|
+
approval (LegalSense; any other system is refused): --timekeeper,
|
|
6879
|
+
each lawyer their own time (the default), or --person, one active
|
|
6880
|
+
timekeeper of the team table by their ref. A sync never changes it.
|
|
6881
|
+
|
|
6808
6882
|
sixminute-admin connector status --env <env> --firm tenant_... [--json]
|
|
6809
6883
|
sixminute-admin connector sync --env <env> --firm tenant_... [--json]
|
|
6810
6884
|
sixminute-admin connector disconnect --env <env> --firm tenant_... [--json]
|
|
@@ -6836,7 +6910,8 @@ var BOOLEAN_FLAGS = new Set([
|
|
|
6836
6910
|
"proposed",
|
|
6837
6911
|
"confirm",
|
|
6838
6912
|
"password-stdin",
|
|
6839
|
-
"all"
|
|
6913
|
+
"all",
|
|
6914
|
+
"timekeeper"
|
|
6840
6915
|
]);
|
|
6841
6916
|
function parseFlags(argv, offset) {
|
|
6842
6917
|
const values = new Map;
|
|
@@ -7697,19 +7772,33 @@ async function runMember(run2, subcommand) {
|
|
|
7697
7772
|
}
|
|
7698
7773
|
throw new AdminUsageError(`unknown command: member ${subcommand ?? ""}`.trim());
|
|
7699
7774
|
}
|
|
7775
|
+
async function pipedPassword(run2) {
|
|
7776
|
+
const read = run2.deps.readStdin;
|
|
7777
|
+
if (read === undefined)
|
|
7778
|
+
throw new AdminUsageError("--password-stdin was given but this run has no stdin to read");
|
|
7779
|
+
const password = (await read()).replace(/\r?\n$/, "");
|
|
7780
|
+
if (password === "")
|
|
7781
|
+
throw new AdminUsageError("--password-stdin read nothing from stdin; pipe the password into the command");
|
|
7782
|
+
return password;
|
|
7783
|
+
}
|
|
7784
|
+
function requirePasswordStdin(flags) {
|
|
7785
|
+
if (!switched(flags, "password-stdin")) {
|
|
7786
|
+
throw new AdminUsageError("--password-stdin is required: the password is read from stdin and is never taken as an argument, " + "where it would stay in a shell history.");
|
|
7787
|
+
}
|
|
7788
|
+
}
|
|
7700
7789
|
async function connectBody(run2) {
|
|
7701
|
-
const { flags
|
|
7790
|
+
const { flags } = run2;
|
|
7702
7791
|
const kind = required(flags, "kind", "which practice system this firm uses");
|
|
7703
7792
|
if (kind === "fake") {
|
|
7704
7793
|
const seed = flags.values.get("seed");
|
|
7705
7794
|
return { kind, ...seed === undefined ? {} : { seed } };
|
|
7706
7795
|
}
|
|
7796
|
+
if (kind === "legalsense")
|
|
7797
|
+
return legalsenseBody(run2);
|
|
7707
7798
|
if (kind !== "urios") {
|
|
7708
|
-
throw new AdminUsageError(
|
|
7709
|
-
}
|
|
7710
|
-
if (!switched(flags, "password-stdin")) {
|
|
7711
|
-
throw new AdminUsageError("--password-stdin is required: the password is read from stdin and is never taken as an argument, " + "where it would stay in a shell history.");
|
|
7799
|
+
throw new AdminUsageError("--kind is urios, legalsense, or fake for a rehearsal against the in-memory system, " + `got ${JSON.stringify(kind)}`);
|
|
7712
7800
|
}
|
|
7801
|
+
requirePasswordStdin(flags);
|
|
7713
7802
|
const port = flags.values.get("port") ?? "3306";
|
|
7714
7803
|
if (!/^\d+$/.test(port))
|
|
7715
7804
|
throw new AdminUsageError(`--port is a number, got ${JSON.stringify(port)}`);
|
|
@@ -7719,12 +7808,39 @@ async function connectBody(run2) {
|
|
|
7719
7808
|
database: required(flags, "database", "the schema the views live in"),
|
|
7720
7809
|
user: required(flags, "user", "the account we read and write as")
|
|
7721
7810
|
};
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7811
|
+
return { kind, ...rest, password: await pipedPassword(run2) };
|
|
7812
|
+
}
|
|
7813
|
+
var INSTALLATION_NAME = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
7814
|
+
async function legalsenseBody(run2) {
|
|
7815
|
+
const { flags } = run2;
|
|
7816
|
+
if (flags.values.has("host")) {
|
|
7817
|
+
throw new AdminUsageError("--host is not taken for legalsense: give the installation's name with --installation, and the host " + "is <name>.legalsense.nl.");
|
|
7818
|
+
}
|
|
7819
|
+
const installation = required(flags, "installation", "the firm's LegalSense name, as in <name>.legalsense.nl").trim().toLowerCase();
|
|
7820
|
+
if (!INSTALLATION_NAME.test(installation)) {
|
|
7821
|
+
throw new AdminUsageError(`--installation is the name before .legalsense.nl (letters, digits and hyphens), got ${JSON.stringify(installation)}`);
|
|
7822
|
+
}
|
|
7823
|
+
const username = required(flags, "username", "the LegalSense user the firm created for Sixminute");
|
|
7824
|
+
const totp = flags.values.get("totp");
|
|
7825
|
+
if (totp !== undefined && !/^\d{6}$/.test(totp)) {
|
|
7826
|
+
throw new AdminUsageError("--totp is the six-digit code from the seat's authenticator app");
|
|
7827
|
+
}
|
|
7828
|
+
requirePasswordStdin(flags);
|
|
7829
|
+
return {
|
|
7830
|
+
kind: "legalsense",
|
|
7831
|
+
host: `${installation}.legalsense.nl`,
|
|
7832
|
+
username,
|
|
7833
|
+
password: await pipedPassword(run2),
|
|
7834
|
+
...totp === undefined ? {} : { totp }
|
|
7835
|
+
};
|
|
7836
|
+
}
|
|
7837
|
+
function reviewerChoice(flags) {
|
|
7838
|
+
const person = flags.values.get("person");
|
|
7839
|
+
const own = switched(flags, "timekeeper");
|
|
7840
|
+
if (own === (person !== undefined)) {
|
|
7841
|
+
throw new AdminUsageError("connector reviewer takes one of --timekeeper (each lawyer approves their own time) or " + "--person <external-ref> (one person of the firm's team approves every entry)");
|
|
7842
|
+
}
|
|
7843
|
+
return person === undefined ? { mode: "timekeeper" } : { mode: "person", external_ref: person };
|
|
7728
7844
|
}
|
|
7729
7845
|
async function runConnector(run2, subcommand) {
|
|
7730
7846
|
const { flags, environment } = run2;
|
|
@@ -7774,6 +7890,32 @@ async function runConnector(run2, subcommand) {
|
|
|
7774
7890
|
}
|
|
7775
7891
|
return render(run2, { connector: done.connector }, [...head(answer), ...summaryLines(done.connector), "", ...asked]);
|
|
7776
7892
|
}
|
|
7893
|
+
if (subcommand === "reviewer") {
|
|
7894
|
+
const choice = reviewerChoice(flags);
|
|
7895
|
+
const connector2 = await call(run2.api, "GET", path(""));
|
|
7896
|
+
if (connector2.status !== 200)
|
|
7897
|
+
return refused(run2, connector2, head(connector2));
|
|
7898
|
+
const current = ConnectorSummarySchema.parse(connector2.body);
|
|
7899
|
+
if (current.capabilities.accepts_by !== "approval") {
|
|
7900
|
+
throw new AdminUsageError(`connector reviewer is for a practice system that takes entries as drafts for approval, and ` + `${current.kind} does not: there is no reviewer to choose.`);
|
|
7901
|
+
}
|
|
7902
|
+
let reviewer = { mode: "timekeeper" };
|
|
7903
|
+
if (choice.mode === "person") {
|
|
7904
|
+
const team = await call(run2.api, "GET", path("/team"));
|
|
7905
|
+
if (team.status !== 200)
|
|
7906
|
+
return refused(run2, team, head(team));
|
|
7907
|
+
const row = TeamProposalSchema.parse(team.body).rows.find((r) => r.external_ref === choice.external_ref);
|
|
7908
|
+
if (row === undefined) {
|
|
7909
|
+
throw new AdminUsageError(`this firm's practice system has no employee ${JSON.stringify(choice.external_ref)}; ` + `"sixminute-admin connector status" prints the table with every ref in it`);
|
|
7910
|
+
}
|
|
7911
|
+
reviewer = { mode: "person", external_ref: row.external_ref, name: row.name };
|
|
7912
|
+
}
|
|
7913
|
+
const answer = await call(run2.api, "PUT", path("/settings"), { reviewer });
|
|
7914
|
+
if (answer.status !== 200)
|
|
7915
|
+
return refused(run2, answer, head(answer));
|
|
7916
|
+
const summary = ConnectorSummarySchema.parse(answer.body);
|
|
7917
|
+
return render(run2, { connector: summary }, [...head(answer), ...summaryLines(summary)]);
|
|
7918
|
+
}
|
|
7777
7919
|
if (subcommand === "status") {
|
|
7778
7920
|
const answer = await call(run2.api, "GET", path(""));
|
|
7779
7921
|
if (answer.status !== 200)
|
package/package.json
CHANGED