@unisonlabs/sixminute-admin 0.1.1262-next → 0.1.1263-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.
@@ -16,12 +16,15 @@ var __export = (target, all) => {
16
16
 
17
17
  // tools/admin/src/main.ts
18
18
  import { spawn } from "node:child_process";
19
- import { readFileSync as readFileSync4 } from "node:fs";
19
+ import { readFileSync as readFileSync5 } from "node:fs";
20
20
  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.1262-next", commit: "8599d1bf28d630046efb94dfd23788d94ac26873", surface: "e4d3796ead6a528e" };
24
+ var BUILT = { version: "0.1.1263-next", commit: "ad5a49aafefa58571d742f90fc5fdb1050d842a8", surface: "301dc344f2374600" };
25
+
26
+ // tools/admin/src/run.ts
27
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
25
28
 
26
29
  // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
27
30
  var exports_external = {};
@@ -4102,6 +4105,53 @@ var OperatorAuditQuerySchema = exports_external.object({
4102
4105
  limit: exports_external.coerce.number().int().min(1).max(1000).default(100)
4103
4106
  });
4104
4107
  var OperatorAuditListSchema = exports_external.object({ actions: exports_external.array(OperatorActionSchema) });
4108
+ // packages/contracts/src/narrative-activities.ts
4109
+ var NARRATIVE_PRIMARY_LANGUAGES = ["nl", "en", "de", "fr", "es", "it", "pt", "pl", "sv", "da"];
4110
+ var NarrativePrimaryLanguageSchema = exports_external.enum(NARRATIVE_PRIMARY_LANGUAGES);
4111
+ var NarrativeActivitySchema = exports_external.object({
4112
+ key: exports_external.string().regex(/^[a-z][a-z0-9_]{0,39}$/, "a key of lowercase letters, digits and underscores, starting with a letter"),
4113
+ when: exports_external.string().trim().min(1).max(800),
4114
+ patterns: exports_external.object(Object.fromEntries(NARRATIVE_PRIMARY_LANGUAGES.map((tag) => [tag, exports_external.string().trim().min(1).max(200).optional()]))).strict().refine((patterns) => Object.values(patterns).some((pattern) => pattern !== undefined), "at least one pattern")
4115
+ });
4116
+ var NarrativeActivitiesSchema = exports_external.array(NarrativeActivitySchema).min(1).max(40).refine((rows) => new Set(rows.map((row) => row.key)).size === rows.length, "every key once");
4117
+ var PUBLIC_MAIL_DOMAINS = new Set([
4118
+ "gmail.com",
4119
+ "googlemail.com",
4120
+ "outlook.com",
4121
+ "hotmail.com",
4122
+ "live.com",
4123
+ "msn.com",
4124
+ "icloud.com",
4125
+ "me.com",
4126
+ "mac.com",
4127
+ "yahoo.com",
4128
+ "aol.com",
4129
+ "mail.com",
4130
+ "proton.me",
4131
+ "protonmail.com",
4132
+ "gmx.com",
4133
+ "gmx.de",
4134
+ "gmx.net",
4135
+ "web.de",
4136
+ "t-online.de",
4137
+ "hotmail.nl",
4138
+ "live.nl",
4139
+ "outlook.nl",
4140
+ "ziggo.nl",
4141
+ "kpnmail.nl",
4142
+ "planet.nl",
4143
+ "home.nl",
4144
+ "xs4all.nl",
4145
+ "telenet.be",
4146
+ "skynet.be",
4147
+ "orange.fr",
4148
+ "free.fr",
4149
+ "wanadoo.fr",
4150
+ "laposte.net",
4151
+ "libero.it",
4152
+ "virgilio.it"
4153
+ ]);
4154
+
4105
4155
  // packages/contracts/src/vocabulary.ts
4106
4156
  var AttributeKeySchema = exports_external.string().regex(/^[a-z][a-z0-9_]{0,39}$/, "an attribute key is a lower-case machine name: a letter, then letters, digits or underscores, at most 40");
4107
4157
  var RESERVED_ATTRIBUTE_KEYS = ["utbms_task", "utbms_activity"];
@@ -4228,8 +4278,20 @@ var ClientSchema = exports_external.object({
4228
4278
  created_at: TimestampSchema,
4229
4279
  updated_at: TimestampSchema
4230
4280
  });
4231
- var NARRATIVE_LANGUAGE_PATTERN = /^[a-z]{2,3}(-[A-Z][a-z]{3})?(-([A-Z]{2}|[0-9]{3}))?$/;
4232
- var NarrativeLanguageSchema = exports_external.string().regex(NARRATIVE_LANGUAGE_PATTERN, "a language tag such as en, nl, de or en-GB: language, then an optional script and region");
4281
+ var NARRATIVE_LANGUAGES = [...NARRATIVE_PRIMARY_LANGUAGES, "en-GB", "pt-PT"];
4282
+ var NarrativeLanguageSchema = exports_external.enum(NARRATIVE_LANGUAGES);
4283
+ var STORED_TAG = /^[a-z]{2,3}(-[A-Z][a-z]{3})?(-([A-Z]{2}|[0-9]{3}))?$/;
4284
+ var heldLanguage = (value) => {
4285
+ const read = NarrativeLanguageSchema.safeParse(value);
4286
+ if (read.success)
4287
+ return read.data;
4288
+ if (typeof value !== "string" || !STORED_TAG.test(value))
4289
+ return;
4290
+ const primary = NarrativeLanguageSchema.safeParse(value.split("-", 1)[0]);
4291
+ return primary.success ? primary.data : undefined;
4292
+ };
4293
+ var StoredNarrativeLanguageSchema = exports_external.preprocess((value) => heldLanguage(value) ?? null, NarrativeLanguageSchema.nullable());
4294
+ var StoredFirmLanguageSchema = exports_external.preprocess((value) => heldLanguage(value), NarrativeLanguageSchema.default("en"));
4233
4295
  var MatterAttributeRequiredSchema = exports_external.record(AttributeKeySchema.refine((key) => !key.includes("__"), {
4234
4296
  message: "an attribute key here carries no double underscore"
4235
4297
  }), exports_external.boolean());
@@ -4279,7 +4341,7 @@ var MatterSchema = exports_external.object({
4279
4341
  billing: MatterBillingSchema,
4280
4342
  attribute_sets: MatterAttributeSetsSchema.default({}),
4281
4343
  attribute_required: MatterAttributeRequiredSchema.default({}),
4282
- narrative_language: NarrativeLanguageSchema.nullable().default(null),
4344
+ narrative_language: StoredNarrativeLanguageSchema,
4283
4345
  sync_source: SyncSourceSchema.default("manual"),
4284
4346
  created_at: TimestampSchema,
4285
4347
  updated_at: TimestampSchema
@@ -5919,7 +5981,7 @@ var FirmSettingsSchema = exports_external.object({
5919
5981
  unit_minutes: UnitMinutesSchema.default(6),
5920
5982
  narrative: exports_external.object({
5921
5983
  max_chars: NarrativeMaxCharsSchema.nullable().default(null),
5922
- language: NarrativeLanguageSchema.default("en"),
5984
+ language: StoredFirmLanguageSchema,
5923
5985
  guidance: NarrativeGuidanceSchema.default(""),
5924
5986
  lowered_to_system: exports_external.object({ kind: ConnectorKindSchema, at: TimestampSchema }).nullable().default(null)
5925
5987
  }).default({}),
@@ -5964,7 +6026,7 @@ var MemberSchema = exports_external.object({
5964
6026
  user_id: UserIdSchema,
5965
6027
  role: RoleSchema,
5966
6028
  active: exports_external.boolean().default(true),
5967
- narrative_language: NarrativeLanguageSchema.nullable().default(null),
6029
+ narrative_language: StoredNarrativeLanguageSchema,
5968
6030
  entry_size: EntrySizeSchema.nullable().default(null),
5969
6031
  created_at: TimestampSchema,
5970
6032
  updated_at: TimestampSchema
@@ -6006,7 +6068,7 @@ var MeSystemLinkSchema = exports_external.object({
6006
6068
  code: exports_external.string().min(1).nullable(),
6007
6069
  status: exports_external.enum(["connected", "needs_reconnect"]).optional(),
6008
6070
  synced: exports_external.boolean().optional(),
6009
- narrative_language: NarrativeLanguageSchema.nullable().optional(),
6071
+ narrative_language: StoredNarrativeLanguageSchema.optional(),
6010
6072
  approval: ApprovalModeSchema.optional()
6011
6073
  });
6012
6074
  var MeSchema = exports_external.object({
@@ -6117,7 +6179,7 @@ var MatterPatchSchema = MatterSchema.omit({
6117
6179
  updated_at: true,
6118
6180
  attribute_required: true,
6119
6181
  firm_internal: true
6120
- }).partial();
6182
+ }).extend({ narrative_language: NarrativeLanguageSchema.nullable() }).partial();
6121
6183
  var FirmInternalPutSchema = exports_external.object({ firm_internal: exports_external.boolean() });
6122
6184
  var AttributeSetPutSchema = AttributeSetSchema.omit({ updated_at: true });
6123
6185
  var AttributeSetsQuerySchema = exports_external.object({ attribute: AttributeKeySchema.optional() });
@@ -6648,6 +6710,17 @@ var AdminSealedBackfillResultSchema = exports_external.object({
6648
6710
  judged_sealed: exports_external.number().int(),
6649
6711
  done: exports_external.boolean()
6650
6712
  });
6713
+ var AdminNarrativeInstructionsSetSchema = exports_external.object({ instructions: NarrativeGuidanceSchema });
6714
+ var AdminNarrativeInstructionsSchema = exports_external.object({
6715
+ tenant: exports_external.object({ id: TenantIdSchema, name: exports_external.string() }),
6716
+ instructions: exports_external.string()
6717
+ });
6718
+ var AdminNarrativeActivitiesSetSchema = exports_external.object({ activities: NarrativeActivitiesSchema });
6719
+ var AdminNarrativeActivitiesSchema = exports_external.object({
6720
+ tenant: exports_external.object({ id: TenantIdSchema, name: exports_external.string() }),
6721
+ language: NarrativeLanguageSchema,
6722
+ activities: exports_external.array(NarrativeActivitySchema)
6723
+ });
6651
6724
  // packages/contracts/src/admin-firm-acting.ts
6652
6725
  var ACTING_AS_HEADER = "X-Six-Minute-Acting-As";
6653
6726
  var TeamApplyRequestSchema = exports_external.object({
@@ -6853,6 +6926,13 @@ var COMPAT_MARKERS = [
6853
6926
  "services/api/test/t42-intervals.test.ts",
6854
6927
  "services/api/test/t53-bundle.test.ts"
6855
6928
  ]),
6929
+ ...markers("frozen-short", [
6930
+ "services/api/src/platform/models/bundle.ts",
6931
+ "services/api/src/platform/models/compat/activity-types.ts",
6932
+ "services/api/src/platform/models/compat/narrative-short.md",
6933
+ "services/api/test/compat-180.test.ts",
6934
+ "services/api/test/t53-bundle.test.ts"
6935
+ ]),
6856
6936
  ...markers("narrative-languages", ["services/api/src/app.ts", "services/api/src/features/update/legacy-languages.ts"]),
6857
6937
  ...markers("pr4", [
6858
6938
  "boundaries.ts",
@@ -8149,16 +8229,16 @@ function syncLines(report) {
8149
8229
 
8150
8230
  // tools/admin/src/sets.ts
8151
8231
  import { readFileSync as readFileSync2 } from "node:fs";
8152
- function readDocument(path, schema, what) {
8232
+ function readDocument(path, schema, what, flag = "from-file") {
8153
8233
  let raw;
8154
8234
  try {
8155
8235
  raw = JSON.parse(readFileSync2(path, "utf8"));
8156
8236
  } catch (err) {
8157
- throw new AdminUsageError(`--from-file ${path} is not readable JSON: ${err.message}`);
8237
+ throw new AdminUsageError(`--${flag} ${path} is not readable JSON: ${err.message}`);
8158
8238
  }
8159
8239
  const parsed = schema.safeParse(raw);
8160
8240
  if (!parsed.success) {
8161
- throw new AdminUsageError(`--from-file ${path} is not ${what}: ` + parsed.error.issues.slice(0, 4).map((i) => `${i.path.join(".") || "(root)"} ${i.message}`).join("; "));
8241
+ throw new AdminUsageError(`--${flag} ${path} is not ${what}: ` + parsed.error.issues.slice(0, 4).map((i) => `${i.path.join(".") || "(root)"} ${i.message}`).join("; "));
8162
8242
  }
8163
8243
  return { raw, parsed: parsed.data };
8164
8244
  }
@@ -8557,6 +8637,23 @@ is required; without one every command answers 403.
8557
8637
  setting deletes at the next hourly purge. Summaries, labels and entries
8558
8638
  are not affected.
8559
8639
 
8640
+ sixminute-admin narrative show --env <env> --tenant tenant_... [--file <path>] [--json]
8641
+ sixminute-admin narrative set --env <env> --tenant tenant_... --file <path> [--json]
8642
+ The firm's narrative instructions: the text every narrative of the firm
8643
+ follows, over every default, at most 1,500 characters. show prints it,
8644
+ or writes it to --file; set replaces it with the text of --file (an
8645
+ empty file clears it).
8646
+
8647
+ sixminute-admin narrative activities show --env <env> --tenant tenant_... [--file <path>] [--json]
8648
+ sixminute-admin narrative activities set --env <env> --tenant tenant_... --file <path> [--json]
8649
+ The firm's narrative activities, by rank: the first row whose "when"
8650
+ the evidence meets opens the line. show prints the ranks and keys, or
8651
+ writes the whole table to --file as a JSON array; set replaces the
8652
+ firm's rows with the array in --file, in its order. Edit by show to a
8653
+ file, edit, set. set is refused, and the rows stay as they were, for an
8654
+ empty table, a repeated key, or a table where no row has a pattern in
8655
+ the firm's default narrative language.
8656
+
8560
8657
  sixminute-admin pipeline shadow-report --env <env> --day YYYY-MM-DD [--json]
8561
8658
  The cloud pipeline's shadow against the device for one local day, for
8562
8659
  every person of an internal firm with a shadow recording: intervals
@@ -8919,6 +9016,7 @@ async function runAdmin(argv, deps) {
8919
9016
  const oneWord = ["login", "logout", "whoami", "doctor", "versions", "audit"].includes(group);
8920
9017
  const vocabulary2 = group === "attribute" || group === "matter" && subcommand === "sets";
8921
9018
  const threeDeep = group === "tenant" && subcommand === "internal";
9019
+ const narrativeTable = group === "narrative" && subcommand === "activities";
8922
9020
  let flagArgv;
8923
9021
  const positionals = [];
8924
9022
  if (oneWord)
@@ -8928,6 +9026,11 @@ async function runAdmin(argv, deps) {
8928
9026
  if (verb !== "set")
8929
9027
  throw new AdminUsageError(`unknown command: tenant internal ${verb ?? ""}`.trim());
8930
9028
  flagArgv = more;
9029
+ } else if (narrativeTable) {
9030
+ const [verb, ...more] = rest;
9031
+ if (verb !== "show" && verb !== "set")
9032
+ throw new AdminUsageError(`unknown command: narrative activities ${verb ?? ""}`.trim());
9033
+ flagArgv = more;
8931
9034
  } else if (vocabulary2) {
8932
9035
  const argvOf = group === "attribute" && subcommand === "set" ? rest.slice(1) : rest;
8933
9036
  const forFlags = [];
@@ -8993,6 +9096,10 @@ async function runAdmin(argv, deps) {
8993
9096
  return runRawArchive(run2, subcommand);
8994
9097
  if (group === "retention" && subcommand === "set")
8995
9098
  return runRetentionSet(run2);
9099
+ if (narrativeTable)
9100
+ return runNarrativeActivities(run2, rest[0] === "set");
9101
+ if (group === "narrative" && (subcommand === "show" || subcommand === "set"))
9102
+ return runNarrativeInstructions(run2, subcommand === "set");
8996
9103
  if (group === "backfill" && subcommand === "sealed")
8997
9104
  return runBackfillSealed(run2);
8998
9105
  if (group === "scrub" && subcommand === "plaintext")
@@ -9182,6 +9289,48 @@ async function runRetentionSet(run2) {
9182
9289
  `moved ${moved}`
9183
9290
  ]);
9184
9291
  }
9292
+ async function runNarrativeInstructions(run2, set) {
9293
+ const tenantId = required(run2.flags, "tenant", "whose narrative instructions");
9294
+ const file = set ? required(run2.flags, "file", "the instructions are the text of a file") : optional(run2.flags, "file");
9295
+ let body;
9296
+ if (set) {
9297
+ try {
9298
+ body = { instructions: readFileSync4(file, "utf8") };
9299
+ } catch (err) {
9300
+ throw new AdminUsageError(`--file ${file} is not readable: ${err.message}`);
9301
+ }
9302
+ }
9303
+ const answer = await call(run2.api, set ? "PUT" : "GET", tenantPath(tenantId, "/narrative-instructions"), body);
9304
+ if (answer.status !== 200)
9305
+ return refused(run2, answer, [actingHead(tenantId, answer)]);
9306
+ const done = AdminNarrativeInstructionsSchema.parse(answer.body);
9307
+ const lead = [actingHead(tenantId, answer), `instructions ${done.instructions.length} characters${set ? ", set" : ""}`];
9308
+ if (!set && file !== undefined) {
9309
+ writeFileSync2(file, done.instructions);
9310
+ return render(run2, { ...done, file }, [...lead, `written ${file}`]);
9311
+ }
9312
+ return render(run2, done, set || done.instructions === "" ? lead : [...lead, "", ...done.instructions.split(`
9313
+ `)]);
9314
+ }
9315
+ async function runNarrativeActivities(run2, set) {
9316
+ const tenantId = required(run2.flags, "tenant", "whose narrative activities");
9317
+ const file = set ? required(run2.flags, "file", "the table is a JSON array of rows, by rank, in a file") : optional(run2.flags, "file");
9318
+ const body = set ? { activities: readDocument(file, NarrativeActivitiesSchema, "a narrative activity table (a JSON array of rows, by rank)", "file").raw } : undefined;
9319
+ const answer = await call(run2.api, set ? "PUT" : "GET", tenantPath(tenantId, "/narrative-activities"), body);
9320
+ if (answer.status !== 200)
9321
+ return refused(run2, answer, [actingHead(tenantId, answer)]);
9322
+ const done = AdminNarrativeActivitiesSchema.parse(answer.body);
9323
+ if (!set && file !== undefined)
9324
+ writeFileSync2(file, `${JSON.stringify(done.activities, null, 2)}
9325
+ `);
9326
+ return render(run2, !set && file !== undefined ? { ...done, file } : done, [
9327
+ actingHead(tenantId, answer),
9328
+ `language ${done.language}`,
9329
+ `activities ${done.activities.length}${set ? ", set" : ""}`,
9330
+ ...done.activities.map((row, i) => ` ${String(i + 1).padStart(2)} ${row.key} ${Object.keys(row.patterns).join(", ")}`),
9331
+ ...!set && file !== undefined ? [`written ${file}`] : []
9332
+ ]);
9333
+ }
9185
9334
  async function runBackfillSealed(run2) {
9186
9335
  const tenantId = required(run2.flags, "tenant", "whose rows to seal");
9187
9336
  const total = { decisions_sealed: 0, intervals_aged: 0, judged_sealed: 0 };
@@ -9813,7 +9962,7 @@ function adminSurfaceOf(document) {
9813
9962
  // tools/admin/src/main.ts
9814
9963
  function sourceBuild() {
9815
9964
  const document = fileURLToPath(new URL("../../../services/api/openapi.json", import.meta.url));
9816
- const surface = adminSurfaceOf(JSON.parse(readFileSync4(document, "utf8")));
9965
+ const surface = adminSurfaceOf(JSON.parse(readFileSync5(document, "utf8")));
9817
9966
  return { version: "0.0.0-dev", commit: "source", surface };
9818
9967
  }
9819
9968
  function openBrowser(url) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unisonlabs/sixminute-admin",
3
- "version": "0.1.1262-next",
3
+ "version": "0.1.1263-next",
4
4
  "description": "Sixminute operator tool: the admin commands of the Sixminute API, run with your own operator sign-in.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {